component design with enemies

Started by
19 comments, last by rpiller 10 years, 10 months ago

Again, I feel like I should highlight a simple but non-obvious fact: you are not in fact "decoupling" anything. You're just making the existing coupling harder to understand - both for you and for the hypothetical implementation task force.

Consider a simple PacMan clone for a second. In a typical entity/component model, we'd have a Ghost entity which is a combination of a Position component and a Personality component.

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.

In a standard approach, you'd then have a dedicated Player component which is a Position and a PowerPillStatus component pair. A Ghost's personality would lead them to chase (or evade) the player by querying the world for the player's position component and asking for the coordinates, and also the PowerPillStatus to know if the player can currently eat the ghost. Again, so far so good: ghosts and players and power pills are all intimately connected in the PacMan universe, so this coupling is totally fine. It has nothing to do with "just getting a game done" as you put it, but it's actually representative of good design.

In your model, we'd try to "decouple" all this. But how? The best we could do is introduce another indirection like Game, where the Game queries all the various entities and their components and implements all the logic for moving ghosts and such.

So now we have two antipatterns in our code: a Big Ball of Mud class (Game) which handles far more logic and responsibility than it should, and excess indirections/layers of abstraction.

Scale that up to something like an RTS and your world now equals pain.

My point is that judicious coupling isn't just about doing some expedient-yet-dirty thing to get a job done. Sometimes it is literally the right option.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Advertisement

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?

Beginner in Game Development?  Read here. And read here.

 

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

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 smile.png 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.

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 :-).

@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.

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 :-)

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

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


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 ;-)

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

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.

@Apoch Your analogies are funny. A little extreme but funny smile.png. 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 smile.png


 
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.

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.

This topic is closed to new replies.

Advertisement