Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411489 Posts in 69371 Topics- by 58428 Members - Latest Member: shelton786

April 24, 2024, 02:38:22 PM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)Component-Based Architechture: Getting Away from Inheritance
Pages: 1 2 3 [4]
Print
Author Topic: Component-Based Architechture: Getting Away from Inheritance  (Read 37060 times)
increpare
Guest
« Reply #60 on: September 26, 2010, 05:50:54 PM »

I just hit this question/answer that got me to this blog post that is really interesting on the subject of Component-Based Architecture. Leads also to Functional-Reactive Architecture that I didn't heard about before!
With unity, I've personally made frequent reuse of components (or downloaded components from the unity wiki).  In unity, components can have dependencies, and it's true that signal functions in FRP can are waaay more composable, but I've not seen any comparably reusable stuff done with them in games yet (I've tried myself, but never got anywhere).  Signals are technically more restricted in what they can do than components also, which may or may not be a problem depending on the environment/FRP setup/usage in question.  I still find the approach beguiling, though.
« Last Edit: September 27, 2010, 08:49:35 AM by increpare » Logged
Evan Balster
Level 10
*****


I live in this head.


View Profile WWW
« Reply #61 on: September 30, 2010, 02:06:29 PM »

The first C++ game engine I wrote used a system of bitwise-ORed bits and mixin classes to indicate what was what and achieve polymorphism.  It was clunky and required active maintenance of the world's object listing every frame, which got to being pretty expensive.

I worked with Unity a little around the time I started writing Plaidgadget (my first experience with component-based design) and wrote my own system, "Universe", based on things I liked and disliked about Unity's.  I've written quite a few games with it now, and I'm satisfied with the design.  Which I will proceed to explain in gruesome detail.



A class called Universe does minimal maintenance and defines a system of interactible Entities.  Entities are lists of Aspects, each of which belongs to one Entity and one Domain.  Domains come in hierarchies based on the inheritance trees of their associated Aspects, and can be used to message all aspects of that type.  (Messages are method pointers and zero or one arguments.)  To facilitate discrete sets of the same kind of Aspect, the Domain tree can be split at any point into "layers", which still receive messages from higher points on the Domain tree.  These can be used to make multiple game-worlds, localized collision checking and other fun stuff.

(I use Aspect as my class name instead of Component for two reasons.  Firstly, it's much shorter.  Secondly, I often have Aspects string together mostly co-agnostic parts of the program, like the graphics engine and a particular game.)

This lets Plaidgadget act as a general-purpose game engine without it having any notion of what a "world object" is -- that's left to the game.



So, in a given game, I can have a class World::Object which is an Aspect subclass and has its own Domain (and Layer, if I want to have multiple separate worlds).  We'll say it has a virtual method update(float time) and a reference to a World::Position (a separate aspect).  Then I could have classes like Damageable or Solid that inherit from World::Object and can coexist in one Entity.


When I want to create a new world object, I create an Entity, then a World::Position in that Entity, then World::Object subclasses which take references to the World::Position which they share, and attach to its Entity.
Code:
World::Position *pos = new World::Position(universe.entity(), layer);

new Damageable(pos);
new Block(pos); //Adds graphical Aspects to the Entity
new PlayerControlled(pos);
new OnFire(pos); //Generates fire particles, causes damage if it detects a Damageable


To update all Aspects at once, I do something like this:
Code:
//In constructor...
layer = universe.layer();
worldObjects = universe.domain(World::Object::_type, layer); //I use my own reflection system...

//In update...
worldObjects(World::Object::update)(time);


And for whatever it's worth, here's how you indicate inheritance so Universe can structure Domains.  No one's ever used my code so I'm curious as to whether this syntax makes much sense to anyone else.
Code:
//In the class definition for 'World::Object'...

//Reflection info
static Type _type;
virtual Type type() const {return _type;}


//In a source file

Type World::Object::_type = TypeSetup<World::Object, NoConstructor>

(L"infiniteblank::World::Object",
L"Represents an object in InfiniteBlank's world.", 1) //1 is a version, for serialization.

<< ChildOf(Aspect::_type)

<< Register(&World::Object::pos,
L"pos", L"A pointer to the Position aspect of this World::Object.",
IsSynced, 1);


At present, Aspects are only aware of one another by means of pre-arranged pointers and references, but in the future (when I actually have use for it) I can add Unity-style auto-detection of Aspects without much trouble.
« Last Edit: September 30, 2010, 02:13:32 PM by Cellulose Man » Logged

Creativity births expression.  Curiosity births exploration.
Our work is as soil to these seeds; our art is what grows from them...


Wreath, SoundSelf, Infinite Blank, Cave Story+, <plaid/audio>
DangerMomentum
Level 3
***



View Profile WWW
« Reply #62 on: December 01, 2010, 09:52:09 AM »

So I've been reading up on this and I have a basic layout for my game, I think. This is C#, but I didn't pay a whole lot of attention to syntax at the moment.
Code:

//We'll only have one entity class and its behavior and appearance are determined by the components it
//owns. Entities can be defined by XML files and be created in the editor / at runtime.
public class Entity
{
//Probably set by the editor at runtime, mostly used for debugging and reference
String type;
//This contains every component that makes this entity tick
List<Components> componentList;

//This is a stack of messages that gets passed and parsed by every component.
//Basic communication of events is done throught this
Stack<String> messages;

//These are component references that are also contained in the above list, but
//can be used as shortcuts. Kind of hack-ish, but should be able to resolve most
//required dependencies. This will let components that require one of these to
//function to be able to check if they exist. One thing to note: These components
//are mutually exclusive. Each entity can only have one of each of these types.
//Any other component can be duplicated as often as the designer wants. AddComponent
//should enforce this exclusivity

//Just a simple position, probably won't need to have many different kinds, if any.
//Contains position and velocity
SpaceComponent location;

//More complex Entities could need this. States will take current state, messages,
//and inputComponent as the input and figure out the target state from that.
//This is how RenderComponent will know what to draw when multiple animations / images
//are needed (Drawing the player vs a Ripper)
FSMComponent stateMachine;

//This could be keyboard, AI, etc
InputComponent input;

//SpriteComponent, ShapeComponent, SkeletonComponent, etc
RenderComponent render;

//This should add a component to the list and automatically update the shortcut references
public void AddComponent();

//Opposite of add
public void RemoveComponent();

//Called after every component has been added and the entity is ready to be used.
//Should be called if components change
public void Initialize();


//Does behavior for all components. Vague, I know. Should include passing messages to components
public void Update();

//Passes event to all components
public void Event(String message);

}


//All components will derive from this base class
public abstract class Component
{
//Reference to the Entity that contains this. Should be set on add, whether here or in the Entity
Entity owner;

String[] listensFor; //Array of message tags that the Component listens for in Events, such as "takedamage" or "examine"

//Called when the component is added to an entity
public void OnAdd();

//Herp derp
public void OnRemove();

//Called when this component receives a message. It's up to the component to figure out what the
//string means and what to do about it. This can also be used for setting parameters that define
//the components behavior, both on startup and during gameplay.
public void Event(String message);

//Does whatever this component... does.
public void Update();
}


/* Random ideas for different components:

comp_Health
comp_RegenerativeHealth
comp_SheildedHealth

comp_RenderSprite
comp_RenderSpriteSheet

comp_PhysProp

comp_PlayerLogic (Breaks the component model a little, but necessary)

comp_Collidable (Contains message to give to other Entities on collide as parameter)

comp_Ammunition (Container for keeping track of the different ammo types / count something has)


comp_Upgrade (Added to Player's components and allows upgrades to not only be checked for by seeing if an
instance exists in the list, but also allows upgrades to perform passive effects as well and
communicate these to the Player via Event)
comp_SpeedBooster
comp_GravitySuit


comp_Examinable (Defines what happens when Entity receives an "Examine" message)


*/


Events are communicated through strings that look something like "takedamage 10 incinerate" for collision with fire, or "activatespeedbooster" when the timer contained in comp_SpeedBooster hits the target value. How does this look? I wanted to get some feedback before I start actually coding this.
« Last Edit: December 01, 2010, 10:23:40 AM by AMT » Logged

oahda
Level 10
*****



View Profile
« Reply #63 on: December 01, 2010, 10:10:11 AM »

The Problem
Then, your design team decides that at some point in the game, the player meets a talking door which will give the player an item before letting him pass through. They say this is an incredibly important part of the story, and is a necessary feature for the game.
I stopped reading here, and I have not read the discussion, so in case I'm missing something, just tell me.
I just wanted to point out that this is easily solved in C++ by using virtual inheritance. You add a keyword, and it's all working again.
Logged

DangerMomentum
Level 3
***



View Profile WWW
« Reply #64 on: December 01, 2010, 10:25:14 AM »

The Problem
Then, your design team decides that at some point in the game, the player meets a talking door which will give the player an item before letting him pass through. They say this is an incredibly important part of the story, and is a necessary feature for the game.
I stopped reading here, and I have not read the discussion, so in case I'm missing something, just tell me.
I just wanted to point out that this is easily solved in C++ by using virtual inheritance. You add a keyword, and it's all working again.

Yeah, that's a situation that Components help in, but it's not the only use. The main thing I took from it is that Entities can be defined at runtime instead of compile time, which allows creation and modification of Entities in-editor and in-game.
Logged

oahda
Level 10
*****



View Profile
« Reply #65 on: December 01, 2010, 12:12:12 PM »

The Problem
Then, your design team decides that at some point in the game, the player meets a talking door which will give the player an item before letting him pass through. They say this is an incredibly important part of the story, and is a necessary feature for the game.
I stopped reading here, and I have not read the discussion, so in case I'm missing something, just tell me.
I just wanted to point out that this is easily solved in C++ by using virtual inheritance. You add a keyword, and it's all working again.

Yeah, that's a situation that Components help in, but it's not the only use. The main thing I took from it is that Entities can be defined at runtime instead of compile time, which allows creation and modification of Entities in-editor and in-game.
Sounds interesting. I'll read on.
Logged

Pages: 1 2 3 [4]
Print
Jump to:  

Theme orange-lt created by panic