Followers 0

# component design with enemies

## 20 posts in this topic

I'm playing around with the component design. This is for just testing/trying out so nothing too serious. My components have events as outputs and functions as inputs. I want to stay very strict to decoupling so in no situation is a component to know about ANY other component inside itself. Instead the games created with this design would have a central place that does all the hooking up of events to functions between all components to create the actual flow of the game.

An easy example would be having a Health component attached to a player game object that has an event called OnHealthUpdated. Then having a HUD component that has a function called HealthUpdated. Create the 2 objects, attach their components and hookup the players OnHealthUpdated to the huds HealthUpdated() function and now everytime the players health components is changed it'll fire this event which will tell the hud component to update the display of health with the value that was passed to the event & function.

That's easy and there are many components like this. However when I get into enemy/player interaction things get a little more fuzzier in my head. My enemy component can do a bounding box check to see what models are all this area. However, to keep to the decoupling I can't within the result query for "player" or anything like that. Remember this is more research of ways than, I need to make a game right now with this.

So I'm looking for ideas around enemy/player interaction that is decoupled in their components. Ideally events need to be fired and functions need to be called from those events. The enemy has to somehow know the player from only events/functions linked.

Each game object is just a generic class that stores a list of components. For some reason I'm hesitant to pass around GameObject pointers to events/function. Not sure if I have to get over that or what. :) In my example above I'm passing around a simple health value (int) and it seems less coupled than passing a GameObject*, but since GameObject* is so generic maybe it's ok? Maybe I should track ID's (long) for each GameObject and that's what gets passed around instead? Then when I create enemy components I can just pass it the player's ID so they know what they are looking for?

Any ideas around this?

2

##### Share on other sites

All you're really doing in this exercise is removing a perfectly reasonable coupling (between related components) and introducing a more abstract and less informative coupling (between components and this GameObject).

Remember that coupling is not inherently evil. Coupling [i]between unrelated subsystems[/i] is what you want to avoid. If it makes sense for several components to know how to interact with each other, let them do so.

1

##### Share on other sites

In the past, I have used a FactionInfoComponent to provide entity identification. This component would respond to information requests, and would hold such information as faction (player, enemy, neutral, etc..) team (Red Team, Blue Team, etc...) and possibly even name. If you send a QueryFaction event to an object and receive no reply, that means it is not an object for which faction info is relevant, and it can be discarded from consideration in your area check. All other objects will return their appropriate faction info, and you can further cull your area list based on the contents of this result list. Doing it this way, you can provide more useful information than just "is this object the player or an enemy", and still keep to your decoupling abstraction.

To further emphasize ApochPiQ's point, it can be a mistake to try to take the decoupling too far. There are cases where a certain amount of coupling makes sense. For instance, say you have something like a CombatAnimationsController component, which listens for certain events (StartMeleeAttack, StartSpellCast, etc...) and maps those events to animations to play in the AnimatedModel component. In this case, it makes sense to assume that if the entity has a CombatAnimationsController then it also has an AnimatedModel, and so you can have the controller call the entity to obtain a direct reference to the AnimatedModel (and log an error if one isn't found) rather than going through the convolutions of handling the interaction by way of the message system. Similarly with the various components that might be involved in combat, such as Health, Buffs and Debuffs, etc...

0

##### Share on other sites

Remember that coupling is not inherently evil. Coupling between unrelated subsystems is what you want to avoid. If it makes sense for several components to know how to interact with each other, let them do so.

I agree if I was making a game today and needed to get it done I would do this. For research though I want to explore a more strict representation of component design for some other benefits it adds that I list below.

For instance, say you have something like a CombatAnimationsController component, which listens for certain events (StartMeleeAttack, StartSpellCast, etc...) and maps those events to animations to play in the AnimatedModel component. In this case, it makes sense to assume that if the entity has a CombatAnimationsController then it also has an AnimatedModel, and so you can have the controller call the entity to obtain a direct reference to the AnimatedModel (and log an error if one isn't found) rather than going through the convolutions of handling the interaction by way of the message system. Similarly with the various components that might be involved in combat, such as Health, Buffs and Debuffs, etc...

A big part of my wanting strict decoupling is to make developing components easier and less error prone (note developing components not hooking components up as that'll be more tedious/work). Working in a completely self contained component is about as easy as you can get it. Not having any other dependent components helps a ton in separating out tasks in a group setting. Unit testing becomes so much easier to make sure the components do what they are supposed to do. I see these as huge pluses.

For research purposes I'm really forcing myself to be ignorant to the fact that hooking these messages up might be tedious, complicated, or slow performance. For right now anyway that's how I'm thinking about it.

A "goal" of mine would be to design, on paper, all the components needed for a very basic (to start with anyway) game. Then ask the community to have 1 person take 1 component and code/test it. These components would be rather small and specific and have no dependencies so it shouldn't take someone long to do this. Then once I have all the components I (game design master) would assemble them together to make the game. I really would love to see the result of such an experiement. This would be parallelism at it's best, and puts more responsibility on the designer than the coders. The designer really needs to know all interactions for everything. Maybe this is more of a technical designer vs game designer, but this would really flesh out the game ahead of time. Not saying changes won't be needed but that's another benefit. Completely isolated components will be far easier/less error prone to change (that's the idea anyway).

Your example I think fits decoupling pretty well actually (meaning doesn't seem like it poses any technical issues like the AI example I gave). Tedious? yes, but imagine you had a flowgraph or GUI for hooking these up. Designers would love that.

GameObject* zombie = new GameObject();

// not functioning code, but OnStateChanged would pass the state name to the function ChangeAnimation
zombie->GetComponent("CombatAnimationsController")->OnStateChanged.Bind(zombie->GetComponent("AnimatedModel"), AnimatedModel::ChangeAnimation);

// as a not so good but to show the point, the order of states in COmbatAnimationsController vs animation names in AnimatedModel should match so when OnStateChanged is triggered and passes 2 (for example) to AnimatedModel, it'll play the "spell_cast" animation because it was defined second. You can get more involved with start, middle, end animations and such, and make the relationship outside of the components themselves, but this just shows a basic example of how you might do that


?

So to the original question if you were faced with the challenge of making decoupled components that are hooked up via events in 1 central init place, how can you see AI interacting with each other and the player working? That's sort of the sticky point right now for me. I'm sure it can be done, but need to keep playing around with different ways but wanted to see if anyone wanted to help take the challenge here. :)

How would an AI "see" the player and then "attack" the player without knowing anything about the components hooked up to the player from within itself. It only knows about the player and it's components in the Init() method.

?

Edited by rpiller
0

##### Share on other sites

You're more than welcome to carry out this experiment if you really want the first-hand experience, but I can already tell you from [i]my[/i] experience that it won't work.

Software does not exist in a vacuum. Neither does any well-designed engineered product. You don't design a car by asking a dozen different manufacturers to invent parts and then try and smash them together. (For humorous contrast, consider some space projects that [i]have[/i] attempted this - and witness the inordinate amounts of pain they incurred by doing so. The infamous Mars rover crash due to Imperial/SI unit mixups is a classic example.)

Good design does not come from parallelism, and farming off the "grunt work" of doing component implementation is not a good way to scale a project. You will personally rapidly become a bottleneck to production, in the best case; in the worst, you will spend so much time trying to fix the hodgepodge of implementations you get back that you'll never make forward progress, and the whole thing will fall apart.

Now, if you really want to know this for yourself, by all means do the experiment - just be prepared for it to go badly. I'd much rather you learn this the hard way than just take my word for it and turn it into yet another religious belief - software development has too much of that as it is :-)

1

##### Share on other sites

@ApochPiQ I can for sure see the issues you listed happening. I have no idea of the details of how others have implemented something like this but sometimes small changes or a different approach to a similar thing can yeild different results. I'm optimisitc, right now anyway :)

I'll have to think of a small enough game to try this in yet big enough to get a good test. Then find people who care enough to try to implement the components :)

Still looking for decopled AI interactions. If they even exist I guess :)

0

##### Share on other sites

I'll have to think of a small enough game to try this in yet big enough to get a good test. Then find people who care enough to try to implement the components

I've worked on open-source projects before, and have tried to start a few myself that failed to gain traction. It takes quite a bit to get folks fired up about a project enough that they contribute anything meaningful, and getting people onboard usually requires that you do quite a lot of work upfront to convince them that it's actually viable, and not just somebody's pipe dream. So just showing up with a bunch of design notes on components to be written, without anything else, is a good way to become familiar with the sound of crickets in an empty room.
0

##### Share on other sites

@FLeBlanc Yeah, I've been through all that before too :). Good times huh. I actually found the lack of a design was the biggest problem. They were asking people to stick around and just "play" around with the design. People lose interest real fast when that's the case :).

What I'm hoping to do is ask for 1 person to work on 1 well defined component which should take maybe no more than 1-4 hours of their time. That's it. I don't want them to stick around and wait for anything. 1-4 hours of a persons total time on the project. If my game has 20 components I would be looking for maybe 15-19 people (I would pick up the other components). That's the idea anyway. If that many would even want to give it a shot would be in question. I think if I use Unity I should have a decent pool to pick from. You don't even have to be that advanced to make some of the basic components, so all knowledge levels could be represented. We'll see how it plays out :).

I'm not really interested in forming a team really, just getting 20 or so people to make 1 component each. So it's up to me to make the entire design of how all components interact and their basic functionality up front. Then hand these out and hope I got it right the first time :)

0

##### Share on other sites

So it's up to me to make the entire design of how all components interact and their basic functionality up front. Then hand these out and hope I got it right the first time

This right here is the kicker, though. Because you probably won't get it right, not on the first try. Software design is complex, and typically requires a lot of iteration. There are always things you didn't account for in the initial design, things that you at first thought would be fun or otherwise contribute to the overall experience but upon practical realization turned out to be duds, things that occur to you later that can contribute greatly, things that you just flat-out overlooked, and so on. I remember being taught the waterfall model (which is basically what you are proposing) in college because back in those dark ages people just didn't know any better, but modern software engineering has moved far beyond that flawed model. Processes now are much more iterative in nature. So rather than being able to bring on coders for 4 hours at a time, you'll need to bring them back to iterate on their code as your own framework changes. Even in the most loosely coupled systems, there are dependencies. There is just no getting around it. So when those dependencies change, code needs to be refactored. That means, the first prototype will probably be a long ways from finished product, and you can't just have fire-and-forget coders doing the grunt work.
0

##### Share on other sites

This right here is the kicker, though. Because you probably won't get it right, not on the first try. Software design is complex, and typically requires a lot of iteration. There are always things you didn't account for in the initial design, things that you at first thought would be fun or otherwise contribute to the overall experience but upon practical realization turned out to be duds, things that occur to you later that can contribute greatly, things that you just flat-out overlooked, and so on.

I agree. If this was a full blown game and I wanted a team to stick with me there would be iterations and evolving components. For my initial research here it'll be a very small game so the design won't be all that hard. Any iterations I need I might just do myself to prove out the idea, but hoping those will be small. I'm probably going to do a clone with this research project so "fun" factors are already created for me . This method isn't really about NOT having iterations. I just choose to limit that for the research project to prove out the method itself. If this works then I would do it again with a medium size game where I would need people to stick around and iterate the code.

I agree with everything you guys are saying, but I'm looking at this research as doing things a different way. Seeing if this way can work and can be efficient in building indie teams around this idea because at it's core for me that's what it's about. Trying to make a game efficiently for indie people who aren't getting paid or lose interest quickly. Can we do this differently to help that problem. I think this method can help. Just have to prove it out.

So many people come and go on indie projects that the code ends up so messed up and often people are scared to make changes for fear of breaking many other things. I believe "total" isolation of components can help ease that problem/fear. It's easier to tell a person to go into this component and fix it's insides (not it's interface) when it's isolated. I think this can help indie project management.

Edited by rpiller
0

##### Share on other sites

Ghost would ordinarily be responsible for implementing the logic that uses Personality to determine how to manipulate Position. So far so good. We have three pieces which are inherently coupled and need to be linked to make any sense; the Ghost entity is the logical place to do this coupling, via composition.

Note: I've never coded anything in ECS and so I'm asking based on what I've read.

Question. Wouldn't the Ghost have a Personality component which held the data (string or enum) of "Pink"? Then in the System (Movement or AI or Update) it would check the Personality component, see it's "Pink" then call then proper AI module "PinkAI", and do the rest from there? Or is my understanding all wrong?

0

##### Share on other sites

@ApochPiQ I'm not disagreeing with anything you are saying. I totally understand it, but this is about trying something different.

I like to think of this design as event component oriented. All interactions are event driven only. There is a Game object but it's main purpose is to wire the events to functions and call all the GameObject Update methods. There is no actual "logic" in it as in there is no looping or if statements or any of that. It's just the central place to wire the objects. In my ideal indie team there would be 1 master who wires the components together. The other programmers just worry about the components themselves. Everything is event driven. All interactions are event driven. Again, it's about trying something different.

I wouldn't consider that a Mud class then so that anti-pattern is off the table. "Excess" indirection and layers is pretty subjective so who's to say in that. But again I'm not worrying about the glue part of it and how efficient it is right now. I'm more concerned with the components.

I think I'm going with a basic bomberman clone. Writing up the components required now.

Everything we do in programming has trade-offs. If this runs a more efficient indie project where team members come and go and are spread all over the world then I will take that trade-off of having excessive indirection. I'd rather have a game that runs than no game at all ya know I think many indie games fail because their design is too coupled together making their code over a period of time unmanageable. Unless one has a better design pattern or a neurotic project manager things generally get pretty messy pretty fast. Programmers are often tripping all over each other or changes from the designer are so drastic that programmers get pissed and quit. Instead of accepting that I'm simply trying something different.

Some components "might" be game specific, but the idea is to try and minimize these and make more generic components. Instead of a ghost personality component I could have a hunter/runner component that can be reused in a FPS game. Some enemies will hunt the player some will run from it. Those are traits found in many games. Breaking components into their true functionality vs the game functionality. Then linking them together to get the game functionality.

Edited by rpiller
0

##### Share on other sites

I think I'm going with a basic bomberman clone. Writing up the components required now.

You've probably seen my article then:

http://www.gamedev.net/page/resources/_/technical/game-programming/case-study-bomberman-mechanics-in-an-entity-component-system-r3159

I didn't do anything with AI for that implementation, but I'm playing around with that now. Not much to report yet though :-).

1

##### Share on other sites

@phil_t I haven't checked that out yet. Will give it a read now. Thanks for the link!

@Apoch Part of my approach is changing code design to fit project management to get the game finished because that's the goal. If the game has some inefficiencies but is completed and completed quickly, I'll take that over perfect code design but becomes messier and harder to ever complete (again in indie more so). So even if we have game specific components, building those components in isolation is still better for project management. It's easier to make and easier to test/debug and easier to change. It doesn't require waiting on other components and when those components change it doesn't require my component to change just because that one did even if the functionality in mine stays the same. That's my take on it anyway.

Edited by rpiller
0

##### Share on other sites

Ghost would ordinarily be responsible for implementing the logic that uses Personality to determine how to manipulate Position. So far so good. We have three pieces which are inherently coupled and need to be linked to make any sense; the Ghost entity is the logical place to do this coupling, via composition.

Note: I've never coded anything in ECS and so I'm asking based on what I've read.

Question. Wouldn't the Ghost have a Personality component which held the data (string or enum) of "Pink"? Then in the System (Movement or AI or Update) it would check the Personality component, see it's "Pink" then call then proper AI module "PinkAI", and do the rest from there? Or is my understanding all wrong?

Sure, that's a totally practical alternative (and might work best if you have enough different components that separating the concerns becomes important). I didn't mean to imply that my approach was the "one true" method or anything :-)

1

##### Share on other sites

@ApochPiQ I'm not disagreeing with anything you are saying. I totally understand it, but this is about trying something different.

I guess what I don't really understand is what you think you're gaining by doing this. Sure, it's "different", but so is trying to cook a chicken using a nuclear bomb; different is not equal to better, or even practically useful.

I wouldn't consider that a Mud class then so that anti-pattern is off the table. "Excess" indirection and layers is pretty subjective so who's to say in that. But again I'm not worrying about the glue part of it and how efficient it is right now. I'm more concerned with the components.

All of your components are trivial. Specifying them is no different than writing the code for them, except possibly in terms of how you actually put the words on the metaphorical page. Once you have all the components specified in your model, what do the "implementers" actually do?

By contrast, what do you as the design overseer not do that saves you work?

(The answer to both questions, from my understanding of your described process, is "nothing." You're asking no work of component creators and putting all the work in your own glue code. How that fails to qualify as a ball-of-mud implementation is beyond me.)

Everything we do in programming has trade-offs.

Suppose you are frustrated by having to park far away from your destination, and wish to make use of the handicapped parking spaces to solve this complaint. Cutting off your legs with a chainsaw "has trade-offs" and technically gives you your desired result... does that mean it's worth trying?

If this runs a more efficient indie project where team members come and go and are spread all over the world then I will take that trade-off of having excessive indirection.

It won't, is what I'm saying.

To risk repeating myself overmuch - you're doing all the real work and everyone else is doing basically a trained parakeet's job. This is not, by any definition, "more efficient."

I think many indie games fail because their design is too coupled together making their code over a period of time unmanageable. Unless one has a better design pattern or a neurotic project manager things generally get pretty messy pretty fast. Programmers are often tripping all over each other or changes from the designer are so drastic that programmers get pissed and quit. Instead of accepting that I'm simply trying something different.

You are partially correct that some projects fail because of excessive coupling. Trying to knee-jerk this away with an extreme approach is not a solution, it's a reactionary leap of faith into a spike pit.

More projects - indie and otherwise - fail because of bad beliefs about how to do division of labor in a software project. I don't "think"i this, I know this from writing software for over 20 years. Subbing out one bad belief for another is not a recipe for success.

Some components "might" be game specific, but the idea is to try and minimize these and make more generic components. Instead of a ghost personality component I could have a hunter/runner component that can be reused in a FPS game. Some enemies will hunt the player some will run from it. Those are traits found in many games. Breaking components into their true functionality vs the game functionality. Then linking them together to get the game functionality.

I don't follow this at all. You're talking about removing functionality from components on the one hand, to minimize coupling, and then talking about them as if they still have functionality. What functionality do they have, and why? How do you propose to "separate" the "game" from the "true" functionality?

Sorry if I'm being excessively obtuse about this - I just genuinely fail to understand what you think you're gaining from this experiment. As I said initially, though, by all means carry it out if you really want to know why it's broken. Just don't expect shining spectacular results ;-)
0

##### Share on other sites
Huh. Message-passing is a very loose form of coupling, but it's still coupling. What happens when the semantics of your messages change? That equates to an interface change on the components listening for particular messages. What if you have a component, HealthComponent, that listens for ApplyDamage events; but then, at some point, you add an intermediary ReduceDamage component that acts as a damage shield? At that point, you have to either rewrite the semantics of ApplyDamage and add another event type, something like ApplyModifiedDamage, in the process modifying the interfaces of any components that listen for ApplyDamage. Additionally, this line...

zombie->GetComponent("CombatAnimationsController")->OnStateChanged.Bind(zombie->GetComponent("AnimatedModel"), AnimatedModel::ChangeAnimation);

...indicates that you are doing a tighter coupling than simple message-passing, given that someone needs to know enough about the receiver (AnimatedModel) to know that it has a ChangeAnimation method; a true message-passing system wouldn't even be that tightly coupled. It wouldn't know, nor care, exactly which method the receiver uses, if any, to respond to a particular message. Only the receiver would need to know that detail, not some third party hooking up wires.

I worry that you are trying to get too flexible with your paradigm here. Every layer of software on your machine needs to abstract away the messy, intimate details of lower layers so that the higher layers that rely upon it don't have to do as much low-level work; that means that each layer should actually restrict what can be done with it in comparison to lower layers. That is the way of computer science. Imposing limitations, setting requirements, harnessing the generic power of the underlying hardware and turning it toward specialized purposes, is the very heart of what we do. By definition, though, that means that by trying to give the users of your particular layer too much flexibility and power, you actually render your layer useless. What point having an infinitely flexible layer to work with, when they could just eliminate your layer and do everything on the layer beneath? Especially considering that more layers equals more processing overhead, so if you can eliminate a layer without sacrificing usability, then you should do so.

This is why the advice to "make games not engines" is given. Too often, you end up with heavily-engineered layers of overly-generic software that really isn't all that useful in practical application, because the users have to do as much coding to make it specific for their game as they would if they had just built it from the ground up anyway. The abstraction layers need to provide sensible restraints upon the solution domain if they are to be of any use at all. Edited by JTippetts
1

##### Share on other sites

@Apoch Your analogies are funny. A little extreme but funny . I wouldn't consider what I'm trying "extreme". It's noted that you don't agree with what I'm doing. Thanks for your thoughts on the topic though as it's always interesting to hear the other side even if I don't agree at this time.

Huh. Message-passing is a very loose form of coupling, but it's still coupling.

I agree. My main approach is about not directly coupling the insides of the components so they are easier/faster to make, less error prone, and more plug and play without dependencies. So programmers aren't tripping on each other or waiting. They make their components to spec and move on. The 1 design master is the one that hooks the components up together. When people come and go on your project it's less damage to the overall project because their roles were making isolated components instead of possibly tightly coupling their code possibly making a mess and making changes bug ridden and "scary".

Part of the motivation is that the connection of components can be abstracted in a GUI (flowgraph or traditional UI elements) allowing designers or non-programmers the ability to create games with pre-designed components.

To track status of the progress I found some time to make basic movement functionality in unity and move my character around for the bomberman clone. I have a KeyboardInput & BasicCharacterController component that are being linked together below via events. Below is an example of what the master designer would be doing. I don't see it as all that complicated. I think it's nice because I could have "farmed" these 2 scripts off to 2 different programmers and they wouldn't even need to know about each other. I can also have them modify the insides and as long as they keep the interface the same this continues to work. This is a simple example to start with of course. It'll get more interesting as I continue


public class BombermanGame : MonoBehaviour {

// Use this for initialization
void Start () {

GameObject player = GameObject.Find ("bomberman");

// connect bomberman input events
player.GetComponent<KeyboardInput>().OnKeyW += player.GetComponent<BasicCharacterController>().MoveNorth;
player.GetComponent<KeyboardInput>().OnKeyA += player.GetComponent<BasicCharacterController>().MoveWest;
player.GetComponent<KeyboardInput>().OnKeyS += player.GetComponent<BasicCharacterController>().MoveSouth;
player.GetComponent<KeyboardInput>().OnKeyD += player.GetComponent<BasicCharacterController>().MoveEast;

}

// Update is called once per frame
void Update () {

}
}



What I gain: 2 very reusable non dependent components that I can plug into many different game types. I could create a more advanced character controller component and still attach it to the same KeyboardInput controller without having to modify it or have different copies of it based on which controller I'm using. Hooking these up is easier than making a copy of it and hunting inside to make it fit the AdvancedCharacterController, or duplicating logic for each specific game.

Next up is dropping "bombs" with the space key. I think I'm going to create more of a PrefabSpawner component that I can assign a prefab GameObject property that I can drag whatever prefab object I want to create at design time (in this case a bomb) and a Create() method to actually create it. This still leaves things fairly decoupled and from specifically making a Bomb game object in a component script and makes it generic and reusable.

Edited by rpiller
0

##### Share on other sites

Next up is dropping "bombs" with the space key. I think I'm going to create more of a PrefabSpawner component that I can assign a prefab GameObject property that I can drag whatever prefab object I want to create at design time (in this case a bomb) and a Create() method to actually create it. This still leaves things fairly decoupled and from specifically making a Bomb game object in a component script and makes it generic and reusable.

How would you handle different kinds of bombs? Different kinds of bombs are dropped depending on the powerups Bomberman has.

0

##### Share on other sites

How would you handle different kinds of bombs? Different kinds of bombs are dropped depending on the powerups Bomberman has.

To stat with I'll just have the 1 bomb so I can figure out how to do it. Then I'll tackle how to get different bombs! That's part of the challenge of this new way. To change my way of thinking about how to solve something like that. Wish me luck!

At first glance I'm thinking something around having multiple prefab components attached for each different bomb type. Then hooking that up with an inventory component that in this case is for bomb types (but could be generic enough for many other things). Details aren't work out 100% yet but the general idea around that might work. The good thing is once I find a good way to handle something like this the same idea can be applied for many situations like this.

Edited by rpiller
0

## Create an account

Register a new account