Jump to content

  • Log In with Google      Sign In   
  • Create Account


Object / entity system confusion


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
4 replies to this topic

#1 Idratex   Members   -  Reputation: 114

Like
1Likes
Like

Posted 10 February 2013 - 08:30 AM

Alright, back when I was younger I used to really enjoy playing an online game called Spybotics: the Nightfall Incident, and I've always really wanted to do my own version / clone. The game involves virtual hacking sessions called 'databattles' in which the player fights other software using the different commands available to them through their programs, which are visually represented as little tiles on a grid. I've always considered this type of game one of the easier ones for me to create, as collision checking (one of my weak points) is pretty trivial, since it only involves checking if the tile you want to move into is already occupied or not. It's also turn-based, so my code doesn't have to be super-efficient either.
 
Anyway, around mid-last year I decided to have a go using my preferred language, C++, and using SFML as a library for graphics and related things.
 
My first approach was a naive one, using the classic, inheritance-heavy approach, and since I didn't really know or care about organisation or architecture my code became a huge mess, and I began to think "surely there is a better way of doing this?!". So somehow I stumbled upon something called entity / component-based architecture, and bought a very good book on game architecture (namely Game Coding Complete: 4th edition). The book made reference to Design Patterns, which I bought as well. However I began to realise that while the methods discussed in GCC 4 were really good and suitable for industry-standard games, it may be a bit 'over-engineering' to go for a fully-fledged actor/component system for such a small and tightly-defined game. By tightly-defined I mean the range of different types of actors wasn't that big; the way I see it, entities can either be Programs, which all have the same types of properties; or Items such as credits, which don't do anything except sit there and perform an action (such as award points) when moved over.
 
Also, GCC4 seems to be focused on making a more general-purpose engine (in addition to a game using it) rather than an engine specific to a certain game. I only really care about doing the latter, and after reading articles like "Make games, not engines" on the Internet I am convinced this is the right way for me to go. So over the past few months I've been going round in circles, constantly conjuring up a 'better way' to go about designing my game. I'm getting nowhere fast and very frustrated in the process.
 
Recently I thought I'd go for some flexibility and trust that the component system is indeed the right way to approach my game. I began constructing it this way, for example:
 
  • Programs, Items, etc are just Entities, which store a list of Components. All Entities have a grid position (x, y), a unique  ID, a name and a description.
  • Programs would have a ProgramComponent and a VisibleComponent, along with either an AIComponent or a PlayerComponent.
  • Items would have an ItemComponent, a VisibleComponent, etc.
 
However the way I was implementing it meant that lots of data was duplicated which shouldn't be. For example, a ProgramComponent contains the Commands (attacks) the program can perform. They have a name, a description, and some way to be executed (via strategy / command pattern, perhaps). So one class of program (called the "Hack") could have a command called "Slice". However if the ProgramComponent stores all of a program's commands, this would mean that every instance of a Hack in one game (there can be multiple instances of one type of program / item) would have a copy of the Slice command when it really doesn't need to since all Hacks behave the same way. Obviously this isn't that much of a problem for this example but it would be worse for programs with lots of commands. It's also mildly annoying anyway since it isn't necessary.
 
The same applies to entities' names and descriptions. All Hacks are called "Hack" and they all have the same description. Maybe storing references to these (via pointers), rather the concrete strings, in each entity would be better but then where would the actual strings go?
 
I suppose I could have a global registry for certain properties or something like that but I'm wondering if there is a less convoluted way to solve this problem.
 
Anyway, my latest idea does away with the components and goes back to inheritance. I have two separate hierarchies, the EntityPrototype hierarchy and that of the Entity itself. Prototypes hold the data that is common to all instances of something (eg a ProgramPrototype would store image data and commands) and differ solely in the values of this data. Entities are the actual playing pieces on the grid and maintain a reference (pointer) back to their associated prototype. Sample code:
 

 

shared_ptr<ProgramPrototype> pProtoHack(new ProgramPrototype(PROG_HACK, 2, 4, 5));        //"Hack" is just a type/class of program
shared_ptr<ProgramPrototype> pProtoSentinel(new ProgramPrototype(PROG_SENTINEL, 2, 4, 256));    //Same with sentinels
    
pProtoHack->addCommand(Command("Slice", "Deletes 2 sectors from target"))->setName("Hack")->setDescription("Basic attack program");
    
pProtoSentinel->addCommand(Command("Cut", "Deletes 2 sectors from target"))->setName("Sentinel")->setDescription("Corporate data defender");
    
shared_ptr<EntityProgram> pHack1((EntityProgram*) (pProtoHack->createInstance(1)));    //Number passed to createInstance() is just an ID for the entity
shared_ptr<EntityProgram> pSent1((EntityProgram*) (pProtoSentinel->createInstance(2)));


pHack1->getImageIndex();    //Because image data is shared, the method retrieves the value from its object's prototype reference

 

 

Prototypes are responsible for creating the actual Entities they correspond to. It's almost like the clone() method in the actual Prototype design pattern, but my implementation is modified to share some of the data.
 
This works currently. However something tells me I'll regret doing things this way at some point, which is why I'm asking: given everything I've said, what would be a good way to approach the design of my entities / game objects without being too general, which seems to be the case in a typical component based architecture? Or should I just plod on with my homegrown prototype-inspired system and let experience be the best teacher?
 
Also, apologies in advance for any confusion... I can't really explain the mechanics of this game without going into a lot of detail tongue.png


Sponsor:

#2 Kylotan   Moderators   -  Reputation: 3324

Like
0Likes
Like

Posted 10 February 2013 - 09:03 AM

Probably the biggest problem I see among hobbyist game developers these days is that they try and use component-based entities, thinking it'll make their game better, and then they get bogged down in the many different ways in which they could choose to implement such a thing, and then get slowed down by the complexity of writing such generic code everywhere. Personally I think they do more harm than good now. I believe that if you're in a position where you have to ask about your component-based system, you're not ready to use one. But anyway, rant aside.

 

Your situation looks fine. I'd probably implement it a similar way. BUT - you mention 'prototype-inspired' as if it's an alternative to 'component-based' - but it is not. These are unrelated topics.

  • Your component-based system could be prototype-based as well. In Unity3D it's common to create objects from 'prefabs' which are pre-configured GameObjects with their own collection of components. The prefab is the prototype in this case. But everything is still 100% component-based.
  • The prototype pattern imparts shared information to instances via copying. You can also do it via reference, where an object maintains a reference to a more abstract object. eg. You might have an entity called Smaug which holds a reference to a 'Dragon' object. When querying an entity, if a value is not found locally, it can inspect the 'parent' reference to get it. So you basically end up implementing data-inheritance within your program. But this too can still be component-based - an instance of a component might have some 'instance' variables that it stores locally and some 'class' variables which only exist in the parent.
  • One way of representing the 'external object holding shared state' idea is via the Flyweight pattern. This has a spectacularly poor description in the Design Patterns book, focusing on memory use with a particularly invalid example, but it does actually handle data-based inheritance. The downside is that it's not always clear that there is a division between concrete and less-concrete data there, but the concept is the same.

When you think about it, these systems are all equivalent in some sense - generate objects where some of the information is shared with others and some of the information is not. Even a class in C++ and the compilation and execution process uses the flyweight concept, except the shared data is the memory that makes up the executable methods. It's all just data in the end, and there are many ways you can choose to arrange it.



#3 Idratex   Members   -  Reputation: 114

Like
0Likes
Like

Posted 10 February 2013 - 09:26 AM

I didn't mean to suggest that the two concepts are mutually exclusive - having a prototype-based component system did cross my mind - but I wasn't sure if it would be worth it. I suppose it all depends on how specific my intentions are. Funny how you talk about Flyweight, I was just reading that as thinking about all this sharing of data reminded me of it! Thanks for those ideas though, I think it may be the fact that doing something slightly different that isn't spelled out for me just feels weird which keeps me from seriously considering things like that.

 

One reason I like components is that they lend naturally to data-driven design, rather than having everything hard-coded in. But again I'm wondering if it's really necessary for a project like this. It's not like I'm straight out of the C++ tutorials and have just learned about a fancy new way of doing things either - I've read quite a bit on the subject, but unfortunately that seems to be all I ever do :S

 

Anyway, I'll see how it goes. Thanks again for the advice.



#4 AngryPlatypus   Members   -  Reputation: 232

Like
0Likes
Like

Posted 10 February 2013 - 01:53 PM

How many different types of entities do you need for your game? Is there a possibility to reuse the entity system? Do you want to use such an entity system because you simply want to create one (and feel its power ;) )?

 

You can still define properties of your entities in data files and you can also call some script functions without a complicated system. What you cant do is: defining new types of entities in script/data. That would be cool but do you really need that?



#5 Idratex   Members   -  Reputation: 114

Like
0Likes
Like

Posted 10 February 2013 - 02:15 PM

If by types of entities you mean the equivalent of classes eg in which they have the same properties but with different values of these properties, then not a lot. Although it would be nice to be able to add weird and wonderful new entity types I only really need the two mentioned (Program and Item) for now.  I don't want to use an entity system for its own sake; I'm considering using it because it's the Next Big Thing and I can see why it is superior, in many respects, to huge class hierarchies. However the class hierarchy isn't very deep here; as mentioned, different entities are not defined as much by a different class as they are by the data values they have.

 

And yes, components would let me define new entities outside of the program itself which would be nice. I suppose it would benefit me in the long run when compile times grow extremely long.

 

Going ahead with my inheritance heavy approach still feels wrong to me, but components also seem overkill ._.

But what I want to avoid this time round is doing a U-turn and starting from scratch... again. I seem to keep doing this but it obviously doesn't help, which means I have to choose a way of doing this and stick with it.






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