Jump to content

  • Log In with Google      Sign In   
  • Create Account


Design question


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

#1 rockstar8577   Members   -  Reputation: 259

Like
0Likes
Like

Posted 08 October 2012 - 10:35 PM

Is it smart to design classes for a game using design composition? I think its called composition. The methods used on this page http://gameprogrammi.../component.html

Sponsor:

#2 jbadams   Senior Staff   -  Reputation: 17220

Like
0Likes
Like

Posted 08 October 2012 - 10:55 PM

Yes, composition is applicable and useful for games.

#3 rockstar8577   Members   -  Reputation: 259

Like
0Likes
Like

Posted 09 October 2012 - 11:18 AM

Is it overall a better choice? Does it have it's pro's con's? Is it better for only some games? Just trying to get an idea about composition.

#4 Telastyn   Crossbones+   -  Reputation: 3718

Like
0Likes
Like

Posted 09 October 2012 - 11:43 AM

All design is a matter of tradeoffs. If your game would make good use of it then it's a good idea. If the game won't, then it might not be.

#5 Servant of the Lord   Crossbones+   -  Reputation: 17117

Like
6Likes
Like

Posted 09 October 2012 - 01:11 PM

That article is talking about Component Based Design, not Composition, though it did slightly mix some Composition into the article and didn't exactly clarify the difference.
Composition is not Component Based Design, and Component Based Design doesn't always use Composition.

This is composition:
class Cat
{
     Teeth teeth; //Generic 'Teeth' stuff.
     Tail tail; //Generic 'Tail' stuff.

     //'Cat'-specific stuff.
     std::string name;
     Color furColor;
};

class Dog
{
	 Teeth teeth; //Generic 'Teeth' stuff.
	 Tail tail; //Generic 'Tail' stuff.
	
	 //'Dog'-specific stuff.
	 std::string name;
	 Color hairColor;
     int hairLength;
     Breed breed;
     int weight;
};
Cat is then "Composed of" Teeth, a Tail, a string, and a Color.
Dog is also "Composed of" Teeth and a Tail, and also other things. Teeth and Tail are re-usable and don't directly know about either Cat or Dog classes.

It is an alternative to inheritance:
class Animal
{
     //Inherited 'Animal'-specific stuff.
     Teeth teeth; 
	 Tail tail;
};

class Cat : public Animal
{
     //'Cat'-specific stuff.
	 std::string name;
	 Color furColor;
};

class Dog : public Animal
{
	 //'Dog'-specific stuff.
	 std::string name;
	 Color hairColor;
	 int hairLength;
	 Breed breed;
	 int weight;
};

Some people use the general rule-of-thumb:
"Composition means 'has-a'. Inheritance means 'is-a'.".
A Cat 'is-a' Animal? Or a Cat 'has-a' Tail and Teeth?

Composition should be prefered over Inheritance, unless inheritance's pros are actually needed.
Inheritances pro's are that you can treat a Cat class as if it is an Animal class. So you can treat a Cat and a Dog using only their Animal interfaces, and handle their Animal-common interface in a single way without knowing the details of a Cat or a Dog class. The Animal interface becomes a way to interact with both Dog and Cat in a uniform way.

Component Based Design is something different, and not directly related to the idea composition vs inheritance. It can be implemented using composition, or using inheritance, or using neither. It's not (directly) related to that subject.

This is Component Based Design:
class MovementComponent
{
     void MoveTo(Position);
}

class DisplayComponent
{
     void SetModelToUse(Model);
     void Draw();
}

class SoundComponent
{
     void SetNoiseToUse(Sound);
     void MakeNoise();
}

class Entity
{
     std::string name;
     MovementComponent movementComponent;
     SoundComponent soundComponent;
     DisplayComponent displayComponent;
}

Entity entity;
entity.name = "Fido the Dog"
entity.displayComponent.SetModelToUse("../data/DogModel.model");
entity.soundComponent.SetSoundToMake("../data/BarkLoudly.wav");

entity.movementComponent.MoveTo(foodBowl);
In the example above I happen to use composition to let Entity know about the different components, but there are a half-dozen ways of doing Component Based Design, some making Entity have a vector of AbstractComponents (using inheritance), so you can add components dynamically.
Some don't even have an explicit Entity class at all, instead using IDs and make components know what ID they belong to, with no actual Entity that contains the components (and the component's lifetime is then managed by a "SubSystem" for each component-type... thus neither Composition nor Inheritance of the actual Entity, because 'Entity' wouldn't exist except as a concept. Each component would instead be controlled (by Composition) with the SubSystem instead)

Composition and Inheritance are class-level implementation details. What is Class composed of, and how?
Component Based Design is slightly higher-level architectural design: How do you manage the individual objects of the game?

It's a different subject entirely, so it'd be good for you to read up on each different subject, so you don't accidentally get the article's confusion of the two entirely different topics mixed together. Object composition vs Object inheritance is one subject. An entirely different subject is Component Based Design vs Data Based Design vs Object Oriented Design vs Event Driven Design, and how they can actually all be used to different extents in a single program.

It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.

[Fly with me on Twitter] [Google+] [My broken website]

All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.                                                                                                                                                       [Need free cloud storage? I personally like DropBox]

Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal


#6 rockstar8577   Members   -  Reputation: 259

Like
0Likes
Like

Posted 09 October 2012 - 05:10 PM

Ohhh wow, haha more than i thought. And if i understood it correctly you were saying the pros for inheritance was when it was being used for polymorphism?

Also would it be a bad idea to mix composition with inheritance? It seems like in there should be a base object for everything just incase you ever do need to group them.

Edited by rockstar8577, 09 October 2012 - 05:14 PM.


#7 Servant of the Lord   Crossbones+   -  Reputation: 17117

Like
2Likes
Like

Posted 09 October 2012 - 07:20 PM

Inheritance almost always uses composition. The inherited class has to be composed (composition) of something, unless it's just functions.

Also, usually the derived class adds more than just the inherited class' variables, so any derived class is almost always inheriting and​ composing (again: unless it's just adding functions).

Example:
//This class is composition only, even though other classes inherit it.
class BaseClass
{
	 //Composition:
	 std::string stuff;
	 int moreStuff;
	 MyStruct otherStuff;
};

//This class has inheritance AND composition.
class DerivedA : public BaseClass
{
	 //Composition:
	 std::string additionalStuff;
};

//Functions don't typically count as composition, so this class is inheritance only.
class DerivedB : public BaseClass
{
	 void myFunc();
};

In the code above, BaseClass is only using composition, but not inheritance. DerivedA is using both composition and inheritance. DerivedB is only using inheritance (since it doesn't add any member variables).

This class uses neither inheritance nor composition:
class ClassWithFunctionsOnly
{
	 void myFunc();
	 void myOtherFunc();
};
(Arguably, that class would be better off as functions within a namespace instead of a class - but it could legitimately be an abstract base class if one or more functions were virtual)

Yes, the primary pro of inheritance is polymorphism. The cons really start showing up when people go inheritance-crazy (defaulting to inheritance instead of defaulting to composition), and end up creating dozens upon dozens of abstract classes that aren't really needed by their program.
Abstract classes are classes that cannot be used themselves but define the common interface for other classes to use, for polymorphism. One of the goals of programmers is to make most of your code re-usable. This is good. However, in the quest to make code re-usable, many programmers get carried away with creating overly generic solutions that aren't really needed, and don't bring them closer to completing their project. Posted Image

In the big picture, programming happens in layers. Each layer of code helps hone down from the generic (libraries, engines, and frameworks) to the specific (your project). If the code you are writing is too generic, it'll just be another layer of code between the libraries and your end-goal, without actually carrying you further towards that goal. When abstract programming goes too far, you end up just creating another framework ontop of whatever you are already using, thinking you are making your game, but really just making the unneeded framework that you'll eventually actually build your game upon... if you ever complete it, now that you wasted so much time accomplishing the nothing-framework that looks like something impressive. Posted Image

There's an important balance between creating generic code that can be reused in other projects and creating specific code that actually furthers your current project. Posted Image

Edited by Servant of the Lord, 09 October 2012 - 07:21 PM.

It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.

[Fly with me on Twitter] [Google+] [My broken website]

All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.                                                                                                                                                       [Need free cloud storage? I personally like DropBox]

Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal


#8 jbadams   Senior Staff   -  Reputation: 17220

Like
0Likes
Like

Posted 09 October 2012 - 08:26 PM

That article is talking about Component Based Design, not Composition

That'll teach me to actually read rather than giving a quick skim. Thanks for taking the time to give a more detailed explanation! Posted Image

#9 Servant of the Lord   Crossbones+   -  Reputation: 17117

Like
0Likes
Like

Posted 09 October 2012 - 08:33 PM

Heheh, I had to read the article twice to be sure. Posted Image
There was a little ambiguity with the writing as the author semi-mixes the descriptions of both together.

It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.

[Fly with me on Twitter] [Google+] [My broken website]

All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.                                                                                                                                                       [Need free cloud storage? I personally like DropBox]

Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal


#10 rockstar8577   Members   -  Reputation: 259

Like
0Likes
Like

Posted 09 October 2012 - 10:52 PM

So would this be a good way for drawing many objects?

class Drawable
{
   int draw()
   {
	  //Code
   }
}
int main()
{
   List<Drawable> items;
	
   for (int index = 0; index < items.size(); ++index)
   {
	   items.at(index).draw();
   }
}


#11 Servant of the Lord   Crossbones+   -  Reputation: 17117

Like
0Likes
Like

Posted 10 October 2012 - 12:50 PM

Yep. Posted Image

However, "Drawable" sounds like an abstract base class used in inheritance.
If it is a base class, for polymorphism to work you have to use either pointers or references. So your list would be List<Drawable*>, and you'd need to use new and delete to manage the dynamic memory, or preferably: List<some_smart_ptr<Drawable>> to not have to worry about the memory AND enable polymorphism.

If it isn't an abstract base class, a better name would be "Item" (I'm assuming it's an item, since your list says 'items'). If it's not an item, perhaps "Object" or "GameObject" would make more sense.

Another hint, unrelated to the current topic:
When iterating over an array, and if you're confident that you are staying within 0 to size-1, you can use items[index] instead of items.at(index). The at() function checks bounds (but you already know you are within bounds) and throws an exception if you are out of bounds. The subscript [] operator doesn't do any bounds checking, and so has unpredictable results when you go out of bounds, but is faster when iterating over a container since you already know you are within bounds.
(And if you are using C++11, there's an even better way which I won't get into here)

It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.

[Fly with me on Twitter] [Google+] [My broken website]

All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.                                                                                                                                                       [Need free cloud storage? I personally like DropBox]

Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal


#12 rockstar8577   Members   -  Reputation: 259

Like
0Likes
Like

Posted 10 October 2012 - 03:26 PM

This was just an example i could think off of the top of my head.

I actually forgot that you can do that for lists and vectors. Also I would enjoy the C++11 way, incase i do move to that standard.

#13 Servant of the Lord   Crossbones+   -  Reputation: 17117

Like
1Likes
Like

Posted 11 October 2012 - 10:40 AM

In C++11 instead of a normal for-loop:
//Normal for-loop with indexing:
for(size_t index = 0; index < container.size(); index++)
{
     DoSomething( container[index] );
}

//Normal for-loop with iterators:
for(Container<Type>::iterator it = container.begin();
	 it != container.end(); it++)
{
	 DoSomething( *it );
}

First, C++11 includes the keyword 'auto' which greatly reduces the amount of typing, and makes the code cleaner, and makes the code easier to change:
//Normal for-loop with iterators AND using C++11 'auto' keyword:
for(auto it = container.begin(); it != container.end(); it++)
{
	 DoSomething( *it );
}
Or (with std::begin() and std::end() also working for non-container arrays):
for(auto it = std::begin(container); it != std::end(container); it++)
{
	 DoSomething( *it );
}

Second, C++11 also introduces a new version of for() loop for when you know you are iterating from begin() to end() without change, and this further reduces the amount of typing, nicely avoids any (visible) iterator dereferencing, and guarantees to the compiler (and anyone reading the code) that you aren't altering the iterator within the for() loop.

This new for-loop we call 'range-for', and it looks like this:
for(const auto &element : container)
{
     DoSomething( element );
}

So in your example:
some_kind_of_container< some_kind_of_smart_ptr<Drawable> > items;
	   
for(const auto &item : items)
{
	 item->draw();
}

It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.

[Fly with me on Twitter] [Google+] [My broken website]

All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.                                                                                                                                                       [Need free cloud storage? I personally like DropBox]

Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal


#14 rockstar8577   Members   -  Reputation: 259

Like
0Likes
Like

Posted 11 October 2012 - 05:23 PM

The range-for loop looks like a for-each loop. Would that be correct?

#15 Servant of the Lord   Crossbones+   -  Reputation: 17117

Like
0Likes
Like

Posted 12 October 2012 - 09:10 AM

Yes, exactly. Except C++ doesn't have a for-each loop. Microsoft had it as a non-standard extension in Visual Studio that wasn't portable.
for-range is standard C++, and every compiler that claims to support C++ must support it from now on.

Visual Studio had "for each()", Boost had FOR_EACH() macro, Qt had foreach() macro. Now we have a single consistent and properly portable for-each. "for(element-type : container)"

It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.

[Fly with me on Twitter] [Google+] [My broken website]

All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.                                                                                                                                                       [Need free cloud storage? I personally like DropBox]

Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal





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