Sign in to follow this  
Heelp

Code design problems again :(

Recommended Posts

What's up, guys.

 

I wanted to ask you something.

 

Imagine a game like Dota 2, or League of Legends, they have like 100+ heroes, right? I was wondering how is every hero instantiated.

 

1st option: Character shadowFiend = Character( 5, 7, 55, 22, 150, 1300, 20, 35 ).( put 40 arguments in the constructor ).

 

2nd option: Make a new class for every hero, but then if you have 100 heroes, you need 100 classes. What I meant is, how to deal with Characters that are very different, and when I don't want to pass 30 arguments to the constructor?

 

3rd option: Character shadowFiend = Character( something );

shadowFiend.setSpeed( 5 );

shadowFiend.setStrength(7);

shadowFiend.setAgility( 12 );

 

and on and on...

 

What is the way normal people prefer?

 

2nd question:

I have a class Character and every character I create needs animations and shaders and a bunch of other stuff.

 

But the problem is that Blender can't export multiple animations on the same model, that's why I need a new model for every animation, and I store them at my ResourceManager class/object ( it doesn't manage anything, just stores ).

 

And I don't know how to deal with the animations because they are simply too much.

 

Should I create a new variable with a different name for every single animation,( I do this now, really bad )

or should I add them all in a vector.

 

But then if I add 20 animations in a vector, how am I going to remember which one is run, knockdown, attack, die, swim, whatever? Isn't this error-prone?

 

3rd question: I store all game content in one class called ResourceManager. And I need to pass that content somehow to my characters when I instantiate them. Is it ok to make a few vectors of pointers in the resourceManager class, so I can pass just the vectors. For example all the animations for Hero1, are in a vector<Anim*> Hero1Anims. So basically, I group the addresses of all common animations in one vector in order to pass them somewhere? Is this the best way?

 

4th question: My class character works with the same shaders for ALLL characters. Is there a way where I can access the resourceManager, get the needed shaders and just instantiate them in the header file of the Character class, instead of passing the same shaders every time to the Character's constructor. Should I make my resource manager global, can you suggest the easiest way you can think of?

 

 Any help is appreciated, if you don't understand anything, just ask, I'll try to clarify.

 

Thanks for taking the time to read all that messed up stuff. ^_^

Edited by codeBoggs

Share this post


Link to post
Share on other sites
Your constructor can take a single argument that is an object that maps strings to values. This mapping is specified in configuration files, and the constructor will then extract the values it needs ("character_type", "hit_points", "primary_weapon"...) to set up the character.

Share this post


Link to post
Share on other sites

What's the difference between:

1st: Using a global ResourceManager and accessing stuff from everywhere.

2nd: Passing resources as arguments to constructors every single time until i die, and resources are still accessed from everywhere. So it seems the same to me, but people say passing resources to objects' constructors is better than declaring the ResourceManager global. Do you know why?

Share this post


Link to post
Share on other sites
On option 2, if done properly, resources are not accessed from everywhere; they are accessed only from places that have explicitly been given references to them.

Having worked in environments of both types, I can tell you that the difference is enormous. For instance, if I want to use some class in my project for some different executable, option 1 makes it essentially impossible, because everything depends on everything else. Under option 2, the class interface will tell you all the things that it needs access to, so I know to provide those in my new program.

Share this post


Link to post
Share on other sites
they have like 100+ heroes, right? I was wondering how is every hero instantiated

 

In a game nothing of this kind is instantiated as single class, you have always certain types of components on a character like its mesh including animations that are mostyl bone data and transformation data processed in a shader these days, coresponding material and texture classes, a character class containing some information, character controller bound to local input or network messages and a lot more depending on the game.

 

If you are in a set environment like Unity 3D or Unreal Engine then this is stored as asset anywhere in your project where the copnfiguration has been made already and be loaded dynamicaly when needed and in a more custom environment these data is stored in any format the studio decided to use, maybe xml but may also be byte code.

 

We for example have serialized our ingame maps as a bunch of bytes where the game didnt know anything about what maps existed, what type they are and so on until they got loaded from a data package running scripts and doing all these stuff of 'initializing' the map class, where deserialisation works in general like

buffer -> readBytes <- asset
map <- create
map_Property <- buffer_getNBytes
map_Property <- buffer_getNBytes
map_Property <- buffer_getNBytes
map_Property <- buffer_getNBytes
map_setFinal

where our properties were like what object is placed identified by its id and mapped to the local object database where with what transforms and a custom data block for additional properties, heightmap, terrain, terrain types (it was a game where this matters) ...

Edited by Shaarigan

Share this post


Link to post
Share on other sites
Large games are built in terms of systems. A hero is not a single item, not a single class. Instead the data behind a hero is made up of many small objects each working together as systems. Many items can be shown on screen rendering together, so a hero may have references to meshes and animations. They may have items so each item works with an item system, or inventory systems, or weapons and armor systems, often being composed of objects that work with many systems at once.

While many beginning programmers like to make each object responsible for all actions, like giving an object something that renders and calling a render function on every object, that isn't how bigger games are composed. A rendering system handles all the rendering, and while an object may have some type of link to an object that renders, it is not the game object's responsibility to handle any rendering, except perhaps turning the model on and off. Each class should have a single responsibility, and game objects are usually container classes for other building blocks.

In some ways that gets back to your fourth question. You've built something that would apply to all the objects. That is a good starting point for thinking in terms of systems. You find the functionality that is common for all the parts, isolate the functionality into small individual responsibilities, and turn those into classes that implement a single responsibility. Then the object can be reused everywhere, with game objects being composed of many sub-components each with their own responsibility and behavior.

As a beginner you won't be building large systems, or even working with large systems unless you are using an existing engine like Unity or Unreal. Since both are free it might be worthwhile for you to download and try those engines out to learn somewhat about how systems interact.

Share this post


Link to post
Share on other sites

Different ways of constructing complex objects are the focus of the first third of the classic book Design Patterns. Your example 3 is what's known as the 'Builder' pattern. Another popular approach is to load in the data and pass that to a 'Factory Method' which sets up the object based on the data.

 

I don't know how to deal with the animations because they are simply too much

 

Use an enum of animation types (run, knockdown, attack) and those should be the keys in your data structure that map to the animations.

 

So basically, I group the addresses of all common animations in one vector in order to pass them somewhere? Is this the best way?

 

Does it matter if it's the 'best' way? It's probably best to focus on the problems you know you have, rather than worries about problems you might have.

Share this post


Link to post
Share on other sites

Thanks for clearing this up for me, guys. :)

 

Having worked in environments of both types, I can tell you that the difference is enormous.

Ok, I'll pass arguments like crazy. I hope it saves me some headaches in the long run. I really want to use a global, but I won't. :lol:

 

That is a good starting point for thinking in terms of systems. You find the functionality that is common for all the parts, isolate the functionality into small individual responsibilities, and turn those into classes that implement a single responsibility

Now that I think of it, My Character class has a lot of responsibilities. Need to cut some off. Thanks ^_^

 

Use an enum of animation types (run, knockdown, attack) and those should be the keys in your data structure that map to the animations.

Just tried it, works like a charm. But can I use vectors + enum class, instead of map + enum class?

Seems ok, right?

 

EDIT: yo, guys. Another question came up.

 

All my drawable objects have class GameObject as a base class and that class calls the render() function.

 

I have different types of objects, animated enemies, nonanimated floors and walls, but I want to draw them using one 'for' loop only.

 

Sounds easy, just declare the vector like this: vector<GameObject> worldObjects and then push back whatever object you want, like Character, Ghost, Wall, Player, whatever.

 

And it works, everything can be drawed in one loop only. I just loop through the worldObjects vector and call render() on each one of them.

 

But now I have both characters and static objects (wall, floor) in one vector. How can I call updateLogic() for Characters only? (because my walls and floors don't have any logic to them, they don't move ).

Edited by codeBoggs

Share this post


Link to post
Share on other sites

vector<GameObject> worldObjects and then push back whatever object you want, like Character, Ghost, Wall, Player, whatever
This doesn't actually work, the vector has no room for the additional data of a derived class.  vector<GameObject*> would work.

 

But now I have both characters and static objects (wall, floor) in one vector. How can I call updateLogic() for Characters only?
You don't, or rather you can code it with a dynamic_cast test, but you should not.

 

- Make an update vector for update (same problem as above, you can't have a vector with a base class, and store objects of derived classes in it, so you need to store the derived objects elsewhere anyway), separately from rendering, or

- make different vectors for different things (I don't see why 4 for-loops for drawing is so bad that you have to go out of your way to keep it 1 for-loop, and then have huge costs in update loop, and possibly elsewhere), or

- give the walls and floors an empty 'update' method.

Share this post


Link to post
Share on other sites
can I use vectors + enum class, instead of map + enum class?

 

Sure. When you think about it, a vector is much like a map, except you use integer indices as the key. I think a map captures the intent better, but a vector is more convenient in some other ways.

Edited by Kylotan

Share this post


Link to post
Share on other sites
codeBoggs, on 24 Nov 2016 - 4:15 PM, said: vector worldObjects and then push back whatever object you want, like Character, Ghost, Wall, Player, whatever This doesn't actually work, the vector has no room for the additional data of a derived class. vector would work.

Alberth, I was wondering about the same thing, but it really works, I mean it compiles and it renders all objects, I don't know how the vector finds place for all of the objects that are another type. There should be some messed up logic that explains it.

 

EDIT:

Your way works too.

 

std::vector<GameObject*> objects;

objects.push_back( new GameObject( myResources.nonAnimatedModels[ 2 ], myResources.objectShaders ) );

 

I don't why I can't do something like that:

objects.push_back( &GameObject( myResources.nonAnimatedModels[ 2 ], myResources.objectShaders ) );

 

it says, taking an address of temporary. I know that 'new' creates an object on the heap. But why does the second way fail, where does it create the object? On the stack? Then why the objects on heap are not temporary and the objects on the stack are temporary?

 

My guess is that objects on stack are destroyed when function is popped out of the stack, and heap objects stay forever until destroyed. am i right? How should I destroy all the objects when I just store pointers? ( too many questions, i know.. )
 

 

give the walls and floors an empty 'update' method.

This.

 

Guys, now that I started thinking about dividing stuff... I was wondering.. I have a Physics class that has only 2 checkCollision() functions, should I move all objects from a function in my class Physics, i mean, hold pointers to all objects in the Physics class, and just call all their move functions.

Edited by codeBoggs

Share this post


Link to post
Share on other sites

You'll need to look into that vector thing because it's most likely broken in ways you can't see yet.

 

And only having 2 functions in a class is fine. Each class should ideally do one specific thing and do it well, however many functions that requires.

Share this post


Link to post
Share on other sites

Alberth, I was wondering about the same thing, but it really works, I mean it compiles and it renders all objects, I don't know how the vector finds place for all of the objects that are another type. There should be some messed up logic that explains it.

Oh, it compiles alright, it just copies the baseclass into the vector. If you don't actually add additional data, or you don't use that data for rendering, or it uses a baseclass method for rendering, it will draw what you expect.

 

"working" and "correct" are two different things, the latter implies the former (assuming your design is right), the former does not necessarily imply the latter (you can have working incorrect code, as strange as it sounds, usually based on sheer luck (or very bad luck, depending on your view)).

 

 

it says, taking an address of temporary. I know that 'new' creates an object on the heap. But why does the second way fail, where does it create the object? On the stack? Then why the objects on heap are not temporary and the objects on the stack are temporary?

The heap is managed memory, doing new again will give you a new chunk of memory from the heap each time. Stack is just scratch space for values.

 

 

In terms of art photos, a heap would be a wall with framed photos so they are all clearly visible, a stack would be a blackboard for quick draft trials with chalk.

A new framed photo gets a nice new spot on the wall with its own spot light, a new quick draft is done by writing on top of the old draft, mostly (we would wipe the board fully clean each time first, but computers don't care much for looks).

 

 

My guess is that objects on stack are destroyed when function is popped out of the stack

In principle, stack is free scratch space for every single statement. If you compute  "k = i + 1" the computer is free to make a temporary integer value representing "i+1" on the stack, before it is copied to "k" (although modern compilers won't do that). This is what happens with your temp object, it gets created at the stack, used for the computation, and destroyed, so the stack can be used for the next statement (it only does destruction because the C++ language says it must be done).

Note it is fine to take an address of such a temporary object, it will just point at the stack. You should not keep that pointer around after the temporary object is destroyed, but that's your responsibility. C++ gives you all the power you want up to and beyond the edge. Typically you have to limit yourself to what you find manageable.

 

 

heap objects stay forever until destroyed. am i right? How should I destroy all the objects when I just store pointers?

Yep, heap allocations stick around until you explicitly delete them.

 

I don't understand why you think a pointer isn't enough. "new" gives you a pointer to the allocated object, and "delete" takes a pointer.

 

 

Note that I am quite old school with C++, I mostly use plain pointers. Modern C++ uses pointer objects, like shared_ptr. You may want to look into those things as well, they tend to be more forgiving when you make an error (as in, doing something unpredictable less often).

Edited by Alberth

Share this post


Link to post
Share on other sites

Alberth, I got this, finally.. Thanks a lot for the explanation. ^_^

 

Kylotan, what I wanted to do is to update the logic of all entities with only one 'for' loop, and draw them with one 'for' loop, because as I create more different classes/objects, the code gets ugly.

 

But I just found out, by coincidence, :o that there is an 'Update Method' design pattern, that solves my problem. ( if someone is interested: http://gameprogrammingpatterns.com/update-method.html ).

 

Is it okay if I declare a virtual 'update' method in my GameObject base class, and inherit all different types of monsters from there, so they have the update too?

 

The (very cool) guy that wrote the book says composition is a lot better than inheritance, but I need to change too much stuff to switch to composition, is it likely that using inheritance will cause me problems later on? :huh:

Edited by codeBoggs

Share this post


Link to post
Share on other sites

- Make an update vector for update (same problem as above, you can't have a vector with a base class, and store objects of derived classes in it, so you need to store the derived objects elsewhere anyway), separately from rendering, or

 

- make different vectors for different things (I don't see why 4 for-loops for drawing is so bad that you have to go out of your way to keep it 1 for-loop, and then have huge costs in update loop, and possibly elsewhere), or

 

- give the walls and floors an empty 'update' method.

Alberth, I have to ask you yet another question. :lol:

 

I really like the 3rd solution where I just put an empty update method to a inactive object. It's better than making two vectors and transferring objects between them when created/destroyed/went-to-inactive-from-active.

 

But if you scroll down to the bottom of this page : http://gameprogrammingpatterns.com/update-method.html

 

He gives the same options as you gave above, but he says that if I store all objects (inactive and active) in one vector, I will waste too much time looping through the vector, because I would have to check if some flag == true for every object. This sounds really funny to me, worrying about a tiny 'for' loop, but he seems like a professional. Should I care about this?  (I think I'll have no more than 500 objects maximum.)

Edited by codeBoggs

Share this post


Link to post
Share on other sites

There's lots of different advice in this thread already but since we're on the beginner forum(and others might read this) I'll throw two side points out there:

 

  • Despite what people try to push, there isn't a "right" way to do anything, just different ways. Ironically people use "what big games do" as an example a lot of times when often big games make extensive use of things like globals and singletons and all these patterns we fight so hard to get around. Also the "right" way to do things seems to change all the time, OOP used to be pushed hard, then people started pushing ECS.
  • How abstracted and modular things need to be to function decently tends to increase exponentially with the scope of the game. A lot of people here often offer advice on how big games do things, what they fail to point out usually is the coding involved to create these abstracted systems is usually exponentially more the more data the thing has to handle. While you can derive some ideas and advice from how big games do things, don't try to copy them. If you do you're just going to get frustrated and be trying to literally write a game engine capable of coping with a AAA game. Most professional developers don't even do that, there's literally a job for people to just write engines for that these days.

Be mindful of the scale of what you want to make and branch outward and experiment as things get larger.

Share this post


Link to post
Share on other sites

Satharis, very deep advice, thank you. ^_^

 

I tend to agree, because I always subconsciously think of some pattern or approach as the 'right way' ( Composition over inheritance ) or as the 'wrong way', ( Singleton ). But in the end, everything depends on the scale and it's kind of like tools in a toolbox.

 

I think I do this because of the way all the books are written. And, yes, it really is frustrating when I kind of try to overreach.

Edited by codeBoggs

Share this post


Link to post
Share on other sites

Alberth, I got this, finally.. Thanks a lot for the explanation. ^_^

 

Kylotan, what I wanted to do is to update the logic of all entities with only one 'for' loop, and draw them with one 'for' loop, because as I create more different classes/objects, the code gets ugly.

 

But I just found out, by coincidence, :o that there is an 'Update Method' design pattern, that solves my problem. ( if someone is interested: http://gameprogrammingpatterns.com/update-method.html ).

 

Is it okay if I declare a virtual 'update' method in my GameObject base class, and inherit all different types of monsters from there, so they have the update too?

 

The (very cool) guy that wrote the book says composition is a lot better than inheritance, but I need to change too much stuff to switch to composition, is it likely that using inheritance will cause me problems later on? :huh:

 

I think you've misunderstood what the Update Method pattern is trying to convey - the key point there is that behaviour in a game is performed by performing many tiny updates for everything, as opposed to regular software development where the program usually sits idle until you press a key, then it runs a full set of behaviour, before waiting for the next event. But you already knew this is how games work. :)

 

Now, that does tie in to polymorphism and having a single Update method across multiple entities, certainly. And yes, you can have all your entities inherit from that base class. But you probably shouldn't "inherit all different types of monsters" from it, because that will start to get unwieldy later. Try it and see how you go.

 

And no, you don't have to worry about the dormant object problem.

Share this post


Link to post
Share on other sites

but I need to change too much stuff to switch to composition, is it likely that using inheritance will cause me problems later on? :huh:

What you're saying here is "I don't like the structure, but I don't want to fix it, so instead I add more ugliness". Some call it "technical debt" which is an interesting viewpoint to read about some time.

 

 

So let me ask you, when would be the time to fix it, if it is not now? What must happen for you to decide you cannot continue and need to fix it first? (I don't need the answer, but ponder about it.)

 

Stacking more stuff on top of it does not make the underlying mess go away. You'll bump into it each time you make another extension or modification. What's worse, the more stuff you stack onto it, the more stuff you're going to have to rewrite when you eventually decide to fix the underlying structure. Each line of code that you write that uses a bad foundation will be thrown away, and will have to be written again when you change the foundation.

You're digging yourself deeper and deeper into the current situation with each addition.

 

It may look as counter-effective to rewrite what you have now, and for some time it is (ie rewriting existing code doesn't give new features). On the other hand, you have learned now that your current solution has some less nice properties. Those properties will be a continuous and growing nuisance until you fix it by rewriting the structure. The question you have to answer yourself is, can you live with the nuisances, knowing that they will get worse as you bolt more things onto it, knowing that fixing it later will be even more work than now ?

 

Deciding when to rewrite some code is part of learning to program. To make such a judgment, I think it would be good if you experience yourself both sides of that decision. That would put you in the situation where you can make an informed decision. Pick a decision, and live with the consequences.

 

 

Personally, I prefer a clean and proper design much more than getting the job done. I have been programming about 35 years, and I still usually start at the wrong end of the problem, needing a rewrite 2 or 3 times before all the puzzle-pieces fit. I don't think it's a waste of time. Each rewrite gives me a better structure that is more pleasant to work in, and maintain (I have a lot of code at work that is continuously modified with new experiments or ideas that need to be tested.) Rewriting also gives me new and deeper insights in the nature of the problem, making my design fit better to the problem being solved (even after all this time, I still find it amazing that picking the right ideas for the design solves at least half the problem). Last but not least, if, in the last attempt performance problems surfaced, a rewrite gives the opportunity to invent a better design that shifts the performance problems to a less problematic area.

 

 

 

that there is an 'Update Method' design pattern, that solves my problem. ( if someone is interested: http://gameprogrammingpatterns.com/update-method.html )

I quoted an earlier sentence, but it fits better with my reply to your questions, so bear with me.

 

 

I am very much against new programmers using design patterns as guide.

 

You'll probably need a little explanation on that view.

Around 25 years ago, computer science invented object oriented programming. People loved it, and in around 20 or so years, everybody is using it. Then some smart guys found that the same kind of structures could be found to solve certain common problems all over the place. They started to collect them, and organize them. They wrote the famous "gang of four" book "Design patterns" and lots of people were very happy with it. This is fine, programmers got names for commonly used ways of solving certain sub-problems, which helps a lot in discussing alternative approaches to solve whatever they're solving (saying "maybe the visitor pattern would work" communicates a whole set of class and object structure to everybody, in 6 words).

Do note however, we're talking about a small subset of solutions for a small subset of problems. Those problems occur a lot, but it's a subset of all problems that need to be solved.

 

Then someone said "Now wait a minute, if these design patterns happen all over the place, couldn't we design a solution from those patterns?". For experienced programmers, this makes sense imho. You use working common solutions for a large part of the design, so you can spend more time on the remaining more tricky areas with custom solutions. It's sound engineering.

For new programmers however, the situation is different. They don't know what's possible with "custom solutions". They don't know why the common solution is the common solution. They don't know when to stop following a design pattern, and make something better instead. I believe that giving a new programmer a standard set of building blocks saying "this is the universe of solutions, have fun building the world" is a very bad move. It has the big danger of locking a new programmer into the mindset that all problems can be solved by these design patterns, which is simply not true. The set of solutions is much much bigger, there are whole areas where a carefully constructed data structure will make the difference between day and night. A new programmer wil never ever encounter them if he/she sticks with using a design pattern for everything.

I think a new programmer should freely explore the entire world, finding what works for him/her. Inventing weird data structures, and trying how they work, and what their problems are. That's where the novel ideas are, the excellent solutions that we don't know about yet.

 

Once they understand the entire solution space, they can look at design patterns, and judge their value within the universe of all possible solutions.

 

 

 

I really like the 3rd solution where I just put an empty update method to a inactive object. It's better than making two vectors and transferring objects between them when created/destroyed/went-to-inactive-from-active.

I don't quite agree with your reason (you can simply hide such details in an object), but the conclusion seems fine to me. That decision is one step towards a "God" object though, where one object has all knowledge, and can do everything. For this reason I am not much of a fan for high level abstract "Game object" like classes, as they push towards all-knowing hierarchies, but it's a trade-off. You always have pain somewhere, the question is, where is the point where it hurts least.

 

 

 

I will waste too much time looping through the vector, because I would have to check if some flag == true for every object. This sounds really funny to me, worrying about a tiny 'for' loop, but he seems like a professional.

Hmm, I am a professional too, should I now agree with him?  :)

 

 

Many of todays professionals were raised at a time where CPU time was very precious. It made a real difference if you could skip a whole set of tests that you'd know to be false. Obviously, this is still sound logic. Not doing something is always faster than trying it and then deciding each time, there is nothing you have to do. The difference however is that with modern processors, "precious" has shifted from the processor core only (running the test instructions) to the CPU memory caches (L1, L2, and perhaps L3) first, and the processor core second. This makes drawing conclusions on performance at the drawing board a hazardous area to say the least. I have seen one case in real code where testing an always-true value was actually faster than removing the test!

 

So yes, I agree with you, it's not something you should worry about. In fact, I would take it a few steps further even.

 

You seem to be constantly looking for "the best solution for X". In general, that doesn't exist at the point where you are now. Any "best" solution exists only, after you wrote the entire program, and you have analyzed all other possible options (there exist infinitely many, so that could take a while), and you have proven they are all worse than this one best solution.

In other words, it is highly unlikely that you actually have the best solution for anything. You don't even have the complete program yet, so you cannot even truthfully compare it with a second solution in the context of the finished program. As you may now realize, nobody knows what "best" is, and it takes too much time to figure it out. It exists, but we don't have any clue how to find it within our finite life time.

Luckily, this is not a major problem. There is a very large set of "acceptable" solutions, they are less good than "the best", but not that much worse (with varying degrees of worse). In fact, the common solution to this problem is "take something that is 'good enough' " [1] . So instead of asking "is this the best", ask yourself "do I know of any reason why it would not work good enough?". If the answer is "no", just use it. You'll make a mistake a couple of times, but that happens even more often if you look for "the best", since that is practically unobtainable, and in my opinion a not very interesting aim. Cpu cycles are cheap and life is short. Stop worrying about optimal solutions, code nicely working programs, and fix them when they appear broken in some way. This does mean you'll have to decide between ditching your current program and make a new better one from scratch, or live with the broken program, every now and then, but overall, I think it's a fair price to pay for making many nice programs that function in the way you envisioned.

 

 

[1]: I skipped the entire discussion of how we know what "good enough" is there. Sorry for that, but the post is long enough already, maybe another time.

Share this post


Link to post
Share on other sites

re: inheritance vs composition...

 

favoring inheritance can lead to the "diamond problem", which composition can avoid.

 

https://en.wikipedia.org/wiki/Multiple_inheritance

 

inheritance  as a design methodology lends itself well to problems that are inherently hierarchical in nature.   Unfortunately, a number of things in games are not hierarchical in nature.

 

 

 

re: one big list...

 

Many of the data structures in games tend to be lists of things.

 

As performance becomes an issue, its common to use a number of lists of specific types of things, as opposed to one big list for everything.

 

 

 

re: globals vs passing params....

 

by passing params, all the dependencies of the code are explicitly stated in the API, which makes the code more portable.

 

define ownership at the lowest scope level possible, and pass down from there.

 

you may find that you often pass the same params again and again. these are good candidates for a parameter struct - a struct you  define just for passing around those pesky parameters.

 

 

 

re: organizing game code....

 

from a design agnostic point of view, it would seem that games are made up of lists:  meshes, textures, animations, sfx, entities, inventories, item definitions, npc definitions, entire libraies of scripts, maps, dialog tree branches, etc.

 

it also seems that code falls into two broad categories:

 

1. code that works on a single data structure. a library, or self contained class, etc.   what i call worker code.

 

2. code that needs to know about two or more types of data or data structures. IE it must operate on data from two or more units of code from category 1.  it needs to use two or more worker code APIs. i call this controller code.

 

it seems that games (and all software) are made up of low level worker code units, which are used by higher level controller code units.

 

a number of design schools, languages, and syntaxes can be used to implement this is in variety of ways, but at the end of the day you always seem to end up with data structures, worker code, and controller code.

Edited by Norman Barrows

Share this post


Link to post
Share on other sites

you may find that you often pass the same params again and again. these are good candidates for a parameter struct - a struct you define just for passing around those pesky parameters.

 

Nice! Why I haven't thought about that earlier...

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this