|
funnyguts
|
 |
« on: July 19, 2012, 07:20:55 AM » |
|
Lately I've been trying to wrap my head around entity systems as opposed to using classes and objects, and it's not going well. There are two big stumbling blocks that I can't really understand: creating individual entities, and making sure an individual entity has its data changed correctly.
1) How do I actually create unique 'things' without classes and objects? From what I can understand, the way to do entity systems is to just have a bunch of components on their own, that get called for each entity that needs them at a given time. But I'm not sure how I'm supposed to have multiples of the same type of entity. Say I make an RPG where the player has to fight a group of six goblins at once. With OOP I could make a monster class, then add a goblin object, then duplicate that object five times. For DOD/Entity stuff the only solution I can think of is to have a list called goblin with all the information I need for a goblin in it, and copy that. But that seems really unwieldy and I'm not sure what it does differently than an object.
2) How do I make sure I actually manipulate the data of a given object? Using the goblin example again, say the player attacks and damages Goblin 4. What's the right way to make sure that my code actually changes Goblin 4's HP and not another goblin's? In general, I'm not sure how to code entity systems in a way that guarantees that I can change each bit of data in an unique way.
Right now I'm not coding anything that definitely needs this kind of design paradigm, but I figure I should try to understand it before I run into something where it would be really useful to have.
|
|
|
|
|
Logged
|
|
|
|
|
omgnoseat
|
 |
« Reply #1 on: July 19, 2012, 07:43:09 AM » |
|
I have written my own entity-component system, and alot of these questions seem to resolve themselves when you get your hands dirty on them. You can think about them for hours, but sometimes it's just better to have a go at them. 1) How do I actually create unique 'things' without classes and objects? From what I can understand, the way to do entity systems is to just have a bunch of components on their own, that get called for each entity that needs them at a given time. But I'm not sure how I'm supposed to have multiples of the same type of entity. Say I make an RPG where the player has to fight a group of six goblins at once. With OOP I could make a monster class, then add a goblin object, then duplicate that object five times. For DOD/Entity stuff the only solution I can think of is to have a list called goblin with all the information I need for a goblin in it, and copy that. But that seems really unwieldy and I'm not sure what it does differently than an object. What makes a goblin a goblin? Right, the components, and more importanty, the parameters you pass into these components. 2 Entities with a graphicscomponent can look completely different if you pass a different sprite/model into the graphicscomponent. Component system and data-oriented design go hand in hand. Each component is completely the same for each entity, it's the data that you pass into the component what makes them different. So to copy the goblin object, you need to make a template of all it's component and the data that you are going to pass into it. Using an external data source (Lua, xml) for these templates is really handy, since you can make new entities without recompiling. 2) How do I make sure I actually manipulate the data of a given object? Using the goblin example again, say the player attacks and damages Goblin 4. What's the right way to make sure that my code actually changes Goblin 4's HP and not another goblin's? In general, I'm not sure how to code entity systems in a way that guarantees that I can change each bit of data in an unique way.
An entity is basically a bag of a components. Entities don't share components with other entities, they both have their own set, it's what defines them. So when you damage goblin 4, you look up it's health component and subtract the damage. The other goblins won't be affected, since they all have their own instance of a health component.
|
|
|
|
|
Logged
|
|
|
|
|
cskau
|
 |
« Reply #2 on: July 19, 2012, 08:10:48 AM » |
|
I'm not particularly familiar with the concept of entity systems but your post did make me think how I'd do without objects at all. After all they supposedly did so back in the days of assembly right? (to my understanding games like Prince of Persia)
Now I come to think of the days before I understood OOP; how would I do it? I'd of course have to store multiple hp values so I'd need a hp array and so on:
def game(): mob_hps = [] mob_pos = [] ... def spawn_mob(pos, hp): mob_hps.append(hp) mob_pos.append(pos) ... def attack_mob(pos, dmg): i = mob_pos.index(pos) mob_hps[ i] -= dmg ...
This is of course extremely naive code the way you'd write it back in the day, but also how I guess you'd have to work without objects.
What you have here is a sort of implied object. Instead of making an array of mob objects each containing a mix of different attributes you have a bunch of arrays each containing a single attribute off all entities.
Perhaps it's a help to think of all the data as a big two-dimensional array. You'd have all the different attributes up the y-axis and all the different entities out the x-axis. In this model you'd have objects being the 2d array striped into pieces across the x-axis. The above code would then simply be the striping across the other.
The big difference with doing the above is that you'd have to do a lot of management of the data yourself. Initialising values in all the arrays, keeping them updated and synchronized and finally cleaning them up again..
|
|
|
|
|
Logged
|
|
|
|
|
funnyguts
|
 |
« Reply #3 on: July 19, 2012, 08:17:07 AM » |
|
So I include the components along with the data? So for the goblin I wouldn't only include an integer for its current HP, but I'd also include a bit of code where it calculates the damage, which is called when the player damages it? Or am I getting confused about what counts as a component?
As for the array thing, that's what I had considered doing for data-oriented stuff, but it seemed really awkward and way too easy to cause the wrong bit of data to change.
|
|
|
|
|
Logged
|
|
|
|
|
DeadPixel
|
 |
« Reply #4 on: July 19, 2012, 09:54:06 AM » |
|
There are a few ways to handle the actual entity creation. Some people advocate for a hybrid approach whereas you actually have a class container that bundles the components for each concrete entity type, and that has been what I've used in the past as I don't like the more abstract method of bundling components around something like an array index. I even went so far as to include basic things shared among all entites like transformation info (position, scale, origin, rotation) as properties of the entity itself. Now, you can, if you want, include code in each component that will act on its own data. Just give each component an update method that gets called down from its root container and let each component process it's stuff. For instance the update method in HealthComponent checking if the entity has <= 0 life and firing off a death event if so, etc. Some people prefer a more MVC approach whereas each component is simply a little nugget of inert data. Then for each component type you have a controller class that consumes that data and acts on each instance of it. So you'd have a HealthComponentController that keeps a list of all active HealthComponents and during each frame will pull the current information from each component, work with it, fire off events, and set new values if necessary. This does have some advantages as all HealthComponents for every entity will be updated at the same time, as opposed to a staggered approach otherwise.
|
|
|
|
|
Logged
|
|
|
|
|
omgnoseat
|
 |
« Reply #5 on: July 19, 2012, 11:14:12 AM » |
|
So I include the components along with the data? So for the goblin I wouldn't only include an integer for its current HP, but I'd also include a bit of code where it calculates the damage, which is called when the player damages it? Or am I getting confused about what counts as a component?
As for the array thing, that's what I had considered doing for data-oriented stuff, but it seemed really awkward and way too easy to cause the wrong bit of data to change.
The damage calculation isn't data, that's implementation. You pass health to the health component, and damage to the weapon component (or whatever the enemy uses to inflict damage). Here is basic example, please ignore any spelling mistakes as I wrote this up really quickly. You can also see what I used to communicate between components, they send messages to eachother. class HealthComponent : Component { public int health; public HealthComponent(int maxHealth) { health = maxHealth; addMessageListener(Message.DAMAGE, receiveDamage); }
private void receiveDamage(Message m) { int final_damage = calculateDamageReduction(m.damage); this.health -= final_damage; }
private int calculateDamageReduction(int damage) { if (this.entity.hasComponent<ArmorComponent>()) { damage -= entity.getComponent<ArmorComponent>().armor; }
return damage; } }
class WeaponComponent : Component { public int damage = 0;
public WeaponComponent(int damage) { this.damage = damage; }
public void use(Entity target) { target.sendMessage(new Message(Message.DAMAGE, this.damage)); } }
|
|
|
|
|
Logged
|
|
|
|
|
funnyguts
|
 |
« Reply #6 on: July 19, 2012, 11:23:35 AM » |
|
Okay, I'm starting to understand. Let me try a more complex example and attempt to rework it with entity/DOD stuff. I'll use the actual game I'm currently working on, a baking sim game. Every day, the player must select some goods for the bakers to bake, and the bakers will go off and make them on their own. The flow of action goes something like this:
1) Player selects a bunch of goods from a list so they can be baked. 2) A representation of each good picked in step one is created. If the player had a good selected multiple times, that good will need to be created multiple times. 3) The bakers bake the goods, changing the taste and presentation values of each item. 4.) Some goods can be split into smaller items. A batch of 12 cookies can be sold either as the dozen together in a box, or you can split it up so each cookie is sold individually. If the player chooses to split an item, those goods will need to be copied so each individual part of the batch can be sold separately. 5.) Customers then buy the goods based on the good's popularity and presentation, and for the purposes of the game immediately eat them. They then decide whether or not they like what they just ate, and their like or dislike affects the popularity of that type of good. (If someone likes your chocolate chip cookie, they'll tell their friends how good it was, and they'll be more likely to buy a chocoloate chip cookie from you.)
Here's how I had the code to make it happen using OOP: 1) Make a baked_goods class with a bunch of different variables attached, like price, popularity, and so on. Then have a bunch of baked_goods objects with each variable adjusted as needed. 2) When the player selects the goods, I use the objects I coded in step 1 as templates and copy them into new objects. (This actually takes a decent amount of time for every object to be created, and it's kinda awkward to have the game slowdown for something like this. This is what got me interested in DOD to start with.) 3) Once all goods are created, have the bakers go through and do their thing. Each good in the list is altered one by one, until all of them have been completed. Each good has a series of steps that needs to be done, like prep, mixing, and baking, so the bakers do each step in turn. 4) Goods that need to be split into 'smaller' items, like the individual cookies, are split now using the same basic copying code I mentioned above. 5) Customers go through one by one and buy the first thing they like, and then eat it. Then they judge kinda at random if they like the good or not. Then based on how they liked it, I give either a bonus or a penalty to the popularity of the kind of good they just ate by changing the popularity variable in the template object, so that all future objects take the change with them.
Here's what I think I need to change to make it more DOD-friendly: 1) Remove the baked_goods class and have the objects exist as template lists on their own to be called as necessary. 2) Create a set of components to be used by each good, like a popularityComponent that can implement adjusting popularity for each good. 3) When adding the good to the list of things to be baked, simply append the template list to the containing list. (Does my use of words like 'list' and 'append' give away what language I'm currently coding in?) 4) Have each baker complete a good, then when all goods are completed adjust their taste and presentation scores all at once. 5) Have each good get purchased and eaten, then have all goods have their popularity adjusted at once.
Am I on the right track?
|
|
|
|
|
Logged
|
|
|
|
|
achild
|
 |
« Reply #7 on: July 19, 2012, 01:38:42 PM » |
|
I haven't read the whole thread yet, but this isn't what data oriented design is. Okay, so there is memory organization involved in this topic, but DOD has more to do with designing your code and structures for efficient memory access, to reduce cache misses, etc.
I believe the topic here is in regards to data-driven object creation. In other words it's the opposite of hard coding objects - instead you define them by way of external data.
I hope this isn't considered derailing the thread - just thought it was important to straighten that out.
|
|
|
|
|
Logged
|
|
|
|
|
Gimym TILBERT
|
 |
« Reply #8 on: July 21, 2012, 07:09:05 AM » |
|
Think of dod as a generalized particles system.
|
|
|
|
|
Logged
|
|
|
|
|
funnyguts
|
 |
« Reply #9 on: July 21, 2012, 12:36:03 PM » |
|
I've never worked with a particle system before, so I'm not sure where to start on that.
|
|
|
|
|
Logged
|
|
|
|
|
nikki
|
 |
« Reply #10 on: July 22, 2012, 09:32:10 AM » |
|
yeah i wanted to say think of particle system too
and usually you just have arrays (with numbers for speed, velocity, color, alpha whatnot)
instead of having an array full with objects (particles) (that each have speed, velocity, color, alpha and whatnot)
but i'd like to know more complex examples too.
|
|
|
|
|
Logged
|
|
|
|
|
omgnoseat
|
 |
« Reply #11 on: July 22, 2012, 10:23:30 AM » |
|
Okay, I'm starting to understand. Let me try a more complex example and attempt to rework it with entity/DOD stuff. I'll use the actual game I'm currently working on, a baking sim game. Every day, the player must select some goods for the bakers to bake, and the bakers will go off and make them on their own. The flow of action goes something like this:
1) Player selects a bunch of goods from a list so they can be baked. 2) A representation of each good picked in step one is created. If the player had a good selected multiple times, that good will need to be created multiple times. 3) The bakers bake the goods, changing the taste and presentation values of each item. 4.) Some goods can be split into smaller items. A batch of 12 cookies can be sold either as the dozen together in a box, or you can split it up so each cookie is sold individually. If the player chooses to split an item, those goods will need to be copied so each individual part of the batch can be sold separately. 5.) Customers then buy the goods based on the good's popularity and presentation, and for the purposes of the game immediately eat them. They then decide whether or not they like what they just ate, and their like or dislike affects the popularity of that type of good. (If someone likes your chocolate chip cookie, they'll tell their friends how good it was, and they'll be more likely to buy a chocoloate chip cookie from you.)
Here's how I had the code to make it happen using OOP: 1) Make a baked_goods class with a bunch of different variables attached, like price, popularity, and so on. Then have a bunch of baked_goods objects with each variable adjusted as needed. 2) When the player selects the goods, I use the objects I coded in step 1 as templates and copy them into new objects. (This actually takes a decent amount of time for every object to be created, and it's kinda awkward to have the game slowdown for something like this. This is what got me interested in DOD to start with.) 3) Once all goods are created, have the bakers go through and do their thing. Each good in the list is altered one by one, until all of them have been completed. Each good has a series of steps that needs to be done, like prep, mixing, and baking, so the bakers do each step in turn. 4) Goods that need to be split into 'smaller' items, like the individual cookies, are split now using the same basic copying code I mentioned above. 5) Customers go through one by one and buy the first thing they like, and then eat it. Then they judge kinda at random if they like the good or not. Then based on how they liked it, I give either a bonus or a penalty to the popularity of the kind of good they just ate by changing the popularity variable in the template object, so that all future objects take the change with them.
Here's what I think I need to change to make it more DOD-friendly: 1) Remove the baked_goods class and have the objects exist as template lists on their own to be called as necessary. 2) Create a set of components to be used by each good, like a popularityComponent that can implement adjusting popularity for each good. 3) When adding the good to the list of things to be baked, simply append the template list to the containing list. (Does my use of words like 'list' and 'append' give away what language I'm currently coding in?) 4) Have each baker complete a good, then when all goods are completed adjust their taste and presentation scores all at once. 5) Have each good get purchased and eaten, then have all goods have their popularity adjusted at once.
Am I on the right track?
I think you are trying to run before you can walk. Work on something incredibly simple for an entity system first. Just a controllable walking character. I haven't read the whole thread yet, but this isn't what data oriented design is. Okay, so there is memory organization involved in this topic, but DOD has more to do with designing your code and structures for efficient memory access, to reduce cache misses, etc.
I believe the topic here is in regards to data-driven object creation. In other words it's the opposite of hard coding objects - instead you define them by way of external data.
I hope this isn't considered derailing the thread - just thought it was important to straighten that out.
You are absolutely right, I got the 2 mixed up.
|
|
|
|
|
Logged
|
|
|
|
|
cld
|
 |
« Reply #12 on: July 24, 2012, 11:20:24 AM » |
|
Don't mind me, just throwing my hat in the ring because I'm working on a C/E system at the moment. The easiest way for me to wrap my head around it is treating it like a big database where each row is an entity full of components that describe what it is. The analogy I like to toss around is a card catalog. Say you've got a gigantic card catalog that's completely empty, no cards, no drawers, nothing. Alongside this empty catalog is a librarian, who serves as the Entity Manager. She'll handle adding and removing entities, giving them components, etc. So you say "I'd very much like to add an entity to this system" to the librarian, and she takes an empty drawer and slides it into the first available slot on the catalog. Presto, you've created an entity. But it's a boring entity, lacking definition and such. So you say "Let's make it exist in space somewhere, give it a position component." Then the librarian takes an index card, writes "Position" on it, and places it in the drawer. You then make requests for Size, Color, and whatever else you can think of, and the librarian adds a card for each. Now you want to make some way of making the position of the entity you just made change. Enter the System, or in this case a Move System. In the catalog example we'll assume it's an irritating patron. The Move System asks the librarian (our Entity Manager) for every entity in the database that has a "Position" and "Velocity" component. Fortunately for the librarian there's only one entity in the system so it's an easy request. She returns with the entity and provides the cards for Position and Velocity. After glancing at the cards, the Move System adds the Velocity to the Position values, replacing the old Position with a new one. He returns the cards to the Librarian/Entity Manager and goes back to whatever it was he was doing, while the Librarian returns the entity drawer to the catalog and moves on to the next patron. This eventually becomes a loop in which each System takes a turn requesting data from the Entity Manager, manipulating it in some way, and finally returning it. So in your baking example, here's how I'd sort of add a cookie in my engine: //em is my entity manager cookie = em.createEntity(); em.addComponent(cookie, new Price()); em.addComponent(cookie, new Popularity()); em.addComponent(cookie, new Taste()); //now to set the initial values of cookie, I'd get the components and alter whatever I need to //I cheat and use some functions in the Components to make setting lots of values easier em.getComponent(cookie, Price.class).setPrice(1.00); em.getComponent(cookie, Taste.class).tastesLike("chicken");
You could do this over and over, or make a simple class that does all this for you (another "cheat" I use), or a for loop or something. Then, in your Systems (which are just functions that get called once every cycle in your game loop), you could do something like this: List cookies = em.getAllEntitiesContainingComponent(Taste.class); for(first cookie to last cookie in cookies) { if(em.getComponent(cookie, Taste.class) = "bad" { em.getComponent(cookie, Popularity.class).popular = false; } }
So you might have a Bake System that handles creating the values in the components of each cookie, a Selling System, Rating System, and so on. Does that help at all?
|
|
|
|
|
Logged
|
|
|
|
jonbro
Level 1
|
 |
« Reply #13 on: July 25, 2012, 06:04:09 AM » |
|
Havn't read the whole thread, but I went through this process myself. I agree with the idea that you need to just get your hands dirty. One of the things that helped me a bunch was this article. This had been said elsewhere in the thread, but one of the things that confused me is that DOD and EC are two seperate things that work really well together. You don't need to have one to have the other though. For me, decoupling them, and just addressing EC first really helped a bunch with my thinking about it.
|
|
|
|
|
Logged
|
|
|
|
|
funnyguts
|
 |
« Reply #14 on: July 25, 2012, 09:46:42 AM » |
|
The library analogy helps, but I just tried implementing it and I can't get it to work. Could I see an example of the entity manager itself?
I'm reading over the Game Patterns Programming post, and it's going to take a while to sink in. I probably won't go as far as removing my 'Bjorn' equivalent from the game completely, though.
|
|
|
|
|
Logged
|
|
|
|
|