Jump to content

  • Log In with Google      Sign In   
  • Create Account


What's the best way to handle game objects and their assets in a statically typed language?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
6 replies to this topic

#1 mk369   Members   -  Reputation: 117

Like
0Likes
Like

Posted 01 May 2014 - 08:50 AM

I'm new to game programming, but not really to programming in general, and I thought it would be interesting to give it a try. At first I considiered using Lua, since it's what I'm most familiar with, but I would prefer to use a static language like C++, C#, or Java for performance and safety. The problem is that I'm not really sure how to manage the different types of game objects in a static language. I can probably think of something, but I thought it would be best to ask first:

 

* How to choose which class to make an instance of based on the object type name in the level layout file?

In Lua, since files can be loaded at runtime, the layout file could just contain the path to the script, which would be loaded at runtime. Even arguemnts to the object's constructor can be handled easily: just store them as strings in the layout file, load and run them as Lua code, and pass the result to the object's constructor.

In a statically typed langauge where all classes are built into the application and have names only known at compile time, the only obvious solution I see is to write a huge, centralized if/elseif statement that creates an instance of the right class based on the name given to it.

* How to load and unload the graphics used by the objects in the layout?

In Lua, I can have the object's script load the graphics and store them in a file-local variable, so the functions in the script have access to them. Since the references to the graphics are stored object scripts, and the object scripts are referenced by the "world" instance, everything will be garbage-collected when the level ends.

In a static language, the only solutions I can think of are to store the graphics in static variables, or load them when instances of the class are created.

With static variables, each class can have a static function, which is somehow called by the level layout loader, that loads the graphics into the static variables. The object's methods could then use these varaibles, simlilar to my Lua solution. The problem is that I'm not sure if static variables would be considered good practice, and second, this would prevent the graphics storage from being freed in a garbage-collected language unless I also add an "unload" method that clears the variables.

The other solution I can think of is to load the graphics inside the objects' constructors. Assuming that all objects are created when the level is loaded, this could be okay, but what if a new type of object is created by an existing object later in its lifetime? This will freeze the game while the new object's graphics are loaded.

* It's necessary for objects to find out about other objects of certain types for them to be able to interact.

In Lua, the object "classes" would just be table values, and they can contain a list of the other classes that they inherit from. This can let the "world" maintain lists of active objects that belong to each class, so it can have a function that takes a "class" table and returns all active instances.

Some static languages have runtime type information, but I'm not sure how well it performs, whether it can provide all the information needed, or if it's considered a good idea to use it heavily for non-debugging purposes like this.

It could also be possible to do something other than directly use classes to represent object types.

So the problem is that my solutions depend on the language having dynamic types and being able to load/unload code at runtime, and I'm really not sure how to make them work in a static language. I would guess that there are plenty of well-known solutions for these problems, which is why I'm asking. Any suggestions?



Sponsor:

#2 Truerror   Members   -  Reputation: 315

Like
1Likes
Like

Posted 01 May 2014 - 10:20 AM

I'm not exactly a pro, I'm still learning the tidbits needed to be a decent game developer, by working on some hobbyist/personal projects. However, I mainly use statically typed languages (C#, and recently I've been dabbling in Java as well) to make those game projects, so I'll try answering some of your questions:

 

  • My latest pet project is a tile-based TBS game. For the game, I made my own map parser which parses a specially structured text file. The text file itself simply contain a kind of 2-dimensional array of letters A, B, and C, which represents a plains tile, a mountain tile, and a water tile. The map is built once. when the parser is called. And it's pretty easy to imagine what the parser does: If the parser reads "A" in the 3rd column of the 2nd row of the text file, for example, it will then instantiate a tile object which will draw the image for plains on a certain position on screen. And so on. While this is for a TBS, I think it's not that hard to repurpose it for a level, though of course, your level file will have a different structure.
  • I really don't have much knowledge about low-level graphics stuff, but most languages have a built-in graphics library, and some (like C#, Java, and C++) have special frameworks designed to make game development easier. In C#, there's XNA, which I use. In XNA, loading a sprite image is as easy as intantiating  a Texture2D object, and drawing them. And no, loading an image in a class constructor isn't really a bad thing, but that depends on your game.
  • Most modern languages have a solid standard library which may include things like lists, dictionaries, etc. For example, in C#, if you have a class named, say, EnemyNinjas, which inherit from GameObject, you can have a list of EnemyNinjas, and make it so the EnemyNinjas constructor add every new EnemyNinjas to the list. Lists in C# don't have a predefined lengh, so you can keep adding to them. Java also has this, but I can't remember what it's called.

I don't know Lua, so I'm sorry if I misunderstood any of your questions. But that being said, it's all possible as long as you don't hardcode things.



#3 Waaayoff   Members   -  Reputation: 771

Like
1Likes
Like

Posted 01 May 2014 - 10:39 AM


* How to choose which class to make an instance of based on the object type name in the level layout file?

 

There are many ways to do this. First you should separate the graphical representation from the actual object. (i.e you could have four race car objects, but only one mesh that is used for all cars.) Your layout file will contain, among other things, the type of object and an identifier for the mesh(es) it uses.

 

Second you need to choose a paradigm to represent your objects, such as inheritance or Component Entity Systems. Simple inheritance would probably be best for now.

 

Now, when you load your files, you would create an object (naturally you would need if statements like you said but as things get more complicated you will look into more sophisticated patterns such as a factory method). Then you would request the mesh for this object by ID and load it if it wasn't loaded before.

 


* How to load and unload the graphics used by the objects in the layout?

 

Just like you request a mesh to be loaded, you request for it to be deleted. Mesh creation/deletion/requests can be handled by one or more classes. But for now a simple ResourceLoader class would suffice. This class would have for every mesh some kind of counter that counts how many objects are using that mesh. Every deletion request would decrement the counter until it's zero. Here you can either completely delete the mesh or keep it in cache. This depends on the type of game you're making.

 


* It's necessary for objects to find out about other objects of certain types for them to be able to interact.

 

No, they don't. Objects should NOT care about other objects. The game logic and game objects should be separated. For example, if you want to do collision between two objects, you would calculate whatever collision response you want in a different class, then tell each object to change their state or position accordingly. Direct communication between game objects should be minimal if not nonexistent.


"Spending your life waiting for the messiah to come save the world is like waiting around for the straight piece to come in Tetris...even if it comes, by that time you've accumulated a mountain of shit so high that you're fucked no matter what you do. "

#4 SeanMiddleditch   Members   -  Reputation: 4029

Like
1Likes
Like

Posted 01 May 2014 - 11:04 AM

* How to choose which class to make an instance of based on the object type name in the level layout file?


Reflection lets you do this easily enough. In languages without reflection, like C++, you can build your own. This can either be a "complete" reflection system (very difficult) or something simpler where game objects/components specifically register factories with a central mananger. Many game engines use simpler macros to accomplish this, e.g.
 
BEGIN_GAME_OBJECT(MyObject)
  GO_PROPERTY(foo...)
  GO_PROPERTY(bar...)
END_GAME_OBJECT(MyObject)

* How to load and unload the graphics used by the objects in the layout?


See the "fly weight" design pattern.

Also, look into reference counting or other shared approaches, e.g. shared_ptr in C++. There's a lot of evil hiding in shared ownership )including Lua's GC approach), but it gets the job done.

Basically, you need a resource manager (or managers) that load up the resources on request and return the existing loaded resources on duplicate request. Game objects can have separate objects that stores shared state, e.g. a MonsterBlueprint object that has all the data for rendering a particular monster and a MonsterInstance object that has a specific position and state for a monster, which references the MonsterBlueprint.

Think of Lua's metatable/prototype approach. Only in C++ or C#, you have to code it up explicitly in each class that uses it.
 

* It's necessary for objects to find out about other objects of certain types for them to be able to interact.


No it isn't. Objects can send out messages which other others simply listen for. Or you can use interfaces. Either are approaches for simulating the duck-typing you'd see in Lua.

A message can be thought of as a way of dynamically invoking a method. In "real" OO languages (of which there are few), there are no methods, just messages. Likewise, an interface is a way to polymorphically interact with other objects like you would in a dynamic language, jus with an explicit contract about the methods that will be available.

The point being that object A has absolutely no reason to know _anything_ about object B. Object A can just send out a generic DamageMessage or something like that and object B can decide to listen for it and react to it or not. If a tighter coupling is needed than messages allow (avoid this if you can, but you won't always be able to) then object A just expects object B to provide some interface that it attaches to. There might be 16 different types of objects that implement that interface but object A has no reason to know about all 16 of those; it just knows about the one interface.

Actually getting the interface handle from an object is a bit trickier, and usually resorts back to reflection, RTTI, etc. Just cache the values (have object A request object B's interface _once_ and then remember it, rather than querying it every time it's needed) and performance is unlikely to be a problem, especially at the scale of game you're likely working on.

#5 mk369   Members   -  Reputation: 117

Like
0Likes
Like

Posted 02 May 2014 - 07:58 AM

No, they don't. Objects should NOT care about other objects. The game logic and game objects should be separated. For example, if you want to do collision between two objects, you would calculate whatever collision response you want in a different class, then tell each object to change their state or position accordingly. Direct communication between game objects should be minimal if not nonexistent.

 

 

Then wouldn't the "different class" need to know about the two object types that are interacting instead? And wouldn't the two objects need their internals heavily exposed for the "different class" to be able to change their state?

 

 

Reflection lets you do this easily enough. In languages without reflection, like C++, you can build your own. This can either be a "complete" reflection system (very difficult) or something simpler where game objects/components specifically register factories with a central mananger. Many game engines use simpler macros to accomplish this, e.g.


BEGIN_GAME_OBJECT(MyObject)
GO_PROPERTY(foo...)
GO_PROPERTY(bar...)
END_GAME_OBJECT(MyObject)

 

 

That seems like a great idea, but I'm having an issue trying to implement an example of it:

 

The problem is that I need a global key->value data structure to associate the names with functions that create the object instances. I can make a macro that defines a function which creates new instances of the class, but I can't add it to the data structure since you can't have code outside of a function in C++. If I put it in a function, that would mostly defeat the purpose, since I will need to call those functions for each object type in a central place.

 

 

Also, look into reference counting or other shared approaches, e.g. shared_ptr in C++. There's a lot of evil hiding in shared ownership )including Lua's GC approach), but it gets the job done.

Basically, you need a resource manager (or managers) that load up the resources on request and return the existing loaded resources on duplicate request. Game objects can have separate objects that stores shared state, e.g. a MonsterBlueprint object that has all the data for rendering a particular monster and a MonsterInstance object that has a specific position and state for a monster, which references the MonsterBlueprint.

 

 

I already know that it's a good idea to cache the assets so that they will not be loaded twice, but the problem is that caching still wouldn't prevent assets from being loaded during gameplay. For example, let's say I want a particle effect object. The level layout wouldn't contain them, so their graphics won't be loaded when the level is loaded. Instead, they will be spawned for the first time by another object during gameplay, which means that their graphics need to be loaded.

 

 

The point being that object A has absolutely no reason to know _anything_ about object B. Object A can just send out a generic DamageMessage or something like that and object B can decide to listen for it and react to it or not. If a tighter coupling is needed than messages allow (avoid this if you can, but you won't always be able to) then object A just expects object B to provide some interface that it attaches to. There might be 16 different types of objects that implement that interface but object A has no reason to know about all 16 of those; it just knows about the one interface.

 

 

My idea was that each object could ask the world for all colliding objects (or just all objects) that implement a certain interface/derive from a certain class, and then do stuff with them through the interface. It might not be ideal, but it seems simple and prevents the objects from depending on their internals much more than necessary. If you have better suggestions, fell free to mention them, though.

 

As for the runtime type information, maybe the "register game object type" macro like you suggested above could even help generate it.



#6 SeanMiddleditch   Members   -  Reputation: 4029

Like
0Likes
Like

Posted 02 May 2014 - 07:14 PM

No, they don't. Objects should NOT care about other objects. The game logic and game objects should be separated. For example, if you want to do collision between two objects, you would calculate whatever collision response you want in a different class, then tell each object to change their state or position accordingly. Direct communication between game objects should be minimal if not nonexistent.

 
 
Then wouldn't the "different class" need to know about the two object types that are interacting instead? And wouldn't the two objects need their internals heavily exposed for the "different class" to be able to change their state?


No. By "different class" I just meant a physics system. It communicates with objects the same way every object communicates: with messages.
 
class Message {};

class GameObject {
public:
  virtual HandleMessage(const Message&) = 0;
}

class DamageMessage : public Message {
public:
  int Amount;
};

class CollisionMessage : public Message {
public:
  GameObject* selfObject;
  GameObject* otherObject;
};

class Player : public GameObject {
  int m_Health;
public:
  virtual HandleMessage(const Message& msg) override {
    if (auto damage = dynamic_cast<const DamageMessage*>(&msg)) {
      m_Health -= damage.Amount;
  }
};

class LaserBeam : public GameObject {
public:
  void HandleMessage(const Message& msg) override {
    if (auto collision = dynamic_cast<const CollisionMessage*>(&msg)) {
      collision->otherObject->.HandleMessage(DamageMessage(amount));
    }
  }
};
Player has zero reason to know what objects can do damage to it. LaserBeams have no reason to know if they're damaging a monster, a player, or a breakable wall. If the LaserBeam collides with something (the physics system sends it a CollisionMessage) then it sends a damage message. If the thing colliding with a LaserBeam cares, it can listen for that message. Complete decoupling. You can create a whole new Bullet class and you don't have to modify a single character in the Player class to handle damage from bullets. Likewise you can create a whole new Monster class and LaserBeam or Bullet can (try to) damage them without a single character of LaserBeam or Bullet being edited.

The only thing that LaserBeam has to know is how to generate damage messages and the only thing Player has to know is how to read damage messages. The sophistication by which this is done can vary from dead-simple like the above to very, very complex, but the general idea is the same.
 

The problem is that I need a global key->value data structure to associate the names with functions that create the object instances. I can make a macro that defines a function which creates new instances of the class, but I can't add it to the data structure since you can't have code outside of a function in C++. If I put it in a function, that would mostly defeat the purpose, since I will need to call those functions for each object type in a central place.


A common pattern is to create some global object of a class which has a constructor that does the registration.
 
template <void(*Function)()>
class Register {
public:
  Register(const char* name) { g_GlobalManager->Add(name, Function); }
};

void InitFoo() {
  ...;
};
Register registerFoo<InitFoo>("Foo");
Note that this can cause problems with linking inside of static libraries in some odd cases. That's a complicated topic with a lot of (crappy) work-arounds not worth worrying about unless you have the problem.

Another option is to just have some central RegisterAllTheThings() function that calls each individual RegisterFoo() and RegisterBar(), which avoids all the linking issues, though it can be a pain.

Yet another option is to write a pre-build script that finds all the reflection data that needs to be generated and then creates a source file containing that central registration function, so you don't have to maintain it yourself.
 

I already know that it's a good idea to cache the assets so that they will not be loaded twice, but the problem is that caching still wouldn't prevent assets from being loaded during gameplay. For example, let's say I want a particle effect object. The level layout wouldn't contain them, so their graphics won't be loaded when the level is loaded. Instead, they will be spawned for the first time by another object during gameplay, which means that their graphics need to be loaded.


So load them then. If you're worried about load times, you have two options:

1) Write asynchronous loading ocde. This is relatively complicated, but necessary for higher-end engines.

2) Preprocess your data so that the level _does_ know about all the objects in it and all the resources those objects need, including objects that might be created at runtime. Then you can preload all the data needed for a level.

Or you could do both, of course.
  

My idea was that each object could ask the world for all colliding objects (or just all objects) that implement a certain interface/derive from a certain class, and then do stuff with them through the interface. It might not be ideal, but it seems simple and prevents the objects from depending on their internals much more than necessary. If you have better suggestions, fell free to mention them, though.


The message-based approach above is much more flexible and (can be) more efficient.

And you definitely don't want to be querying the world for all objects having and interface. I have first-hand experience with how poorly that scales in games.

#7 mk369   Members   -  Reputation: 117

Like
0Likes
Like

Posted 03 May 2014 - 01:25 PM

A common pattern is to create some global object of a class which has a constructor that does the registration.

 

 

I already figured that out, and I'll probably do it that way. I even found a good workaround to make sure that the map is constructed before things get added to it, even if the objects are compiled separately and then linked.

 

 

So load them then. If you're worried about load times, you have two options:

1) Write asynchronous loading ocde. This is relatively complicated, but necessary for higher-end engines.

2) Preprocess your data so that the level _does_ know about all the objects in it and all the resources those objects need, including objects that might be created at runtime. Then you can preload all the data needed for a level.

Or you could do both, of course.

 

 

I'm thinking it's best to just have a static method that pre-loads the assets in to the cache or static variables. If an object can spawn other objects, it can call the other object type's "load assets" method from inside its own.

 

 

Player has zero reason to know what objects can do damage to it. LaserBeams have no reason to know if they're damaging a monster, a player, or a breakable wall. If the LaserBeam collides with something (the physics system sends it a CollisionMessage) then it sends a damage message. If the thing colliding with a LaserBeam cares, it can listen for that message. Complete decoupling. You can create a whole new Bullet class and you don't have to modify a single character in the Player class to handle damage from bullets. Likewise you can create a whole new Monster class and LaserBeam or Bullet can (try to) damage them without a single character of LaserBeam or Bullet being edited.

The only thing that LaserBeam has to know is how to generate damage messages and the only thing Player has to know is how to read damage messages. The sophistication by which this is done can vary from dead-simple like the above to very, very complex, but the general idea is the same.

 

 

That looks like an interesting idea. It probably wouldn't work for "non-momentary" interactions, like for example an object that changes the player's state and moves it for a few frames, but in such cases I guess it's fine to use messages to tell when they first touch, and then use an interface reference for the object to control the player.

 

 

The message-based approach above is much more flexible and (can be) more efficient.

And you definitely don't want to be querying the world for all objects having and interface. I have first-hand experience with how poorly that scales in games.

 

 

Since I'm not planning on making anything really complicated, and there wouldn't be many active objects on screen at once, it seems like having objects check for collision only against those with the interfaces they're interested in would be more efficient compared to checking all pairs. Is there still a good reason to think that a "collision manager" that sends messages to colliding objects would be faster?






Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS