Jump to content
  • Advertisement
MarcusAseth

Control flow based on type in C++

This topic is 444 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi guys, I'm not quite sure how to achieve this :S

Here what I got so far, my GameMode class own an object of type Physics:

class Physics
{
private://variables
	GameMode* Game;

public://constructors
	Physics(GameMode* gameRef);
	~Physics();

public://methods
	bool IsColliding(Entity* current, ECollisionTest collisionTestType);

private://methods
	bool BoxCollisionTest(const Rect& current, const Rect& other);
};

And since all my entities have a pointer to the GameMode they can retrieve a pointer to the Physics object, so when they move they can query the Physics object to see if they collided with something, passing "this" as the object to control against all the Entities inside GameMode, like this:

//Collision Check
if (PhysicsManager->IsColliding(this, ECollisionTest::EBoxCollisionTest))
{
	LogConsole("Collision");
}

and IsColliding() goes like this:

bool Physics::IsColliding(Entity* current, ECollisionTest collisionTestType)
{
	for (auto& E : Game->Entities)
	{
		if (current != E.get())//avoid self testing
		{
			switch (collisionTestType)
			{
				case ECollisionTest::EBoxCollisionTest:
				{
					if (BoxCollisionTest(current->GetCollisionBox(), E->GetCollisionBox()))
					{
						return true;
					}
				}
				break;
			}
		}
	}
	return false;
}

Now, there are two things I don't like about my current setup.

1) I am testing against all the entities. The paddle of the Breakout game can only move left and right, and hit either a ball or the left/right boundary, makes no sense to test it against 50 bricks at every update.

2)From inside the Paddle class I am calling "IsColliding(this, ECollisionTest::EBoxCollisionTest)" so there is the assumption that all the entities that can collide with this one require a box test. May not be the case, maybe I have a "worm" entity running on the screen and it requires another type of collision test, so the choosen function to run shouldn't relying on me explicitly saying which one is, but it should be called at runtime trough overload, based on the type of Entity being compared. So maybe the Paddle<->Ball comparison would do a BoxCollisionTest, while Paddle<->Worm comparison would do something else.

So how do I get it to work the way I want?

This is what I want:

1)Physics object is working with Entity*, and yet I need it to being able to distinguish between a Paddle* and or a Brick* or a Boundary* or a Ball* (all of this inherit from entity), a bit kind of like Unreal Engine BlueprintEditor Cast node, which allow me to say "Cast to Door" and if the thing was a door it return success, otherwise fails. Can you show me an example of how such mechanism is built, in code(C++)?

2)Physics object should just call a generic TestCollision() function between the "this" passed (the current colliding object) and all the other entities, and then the overload resolution calls the appropriate function based on the types. And yet this shouldn't fail to compile for pairs for which I don't explicitly overload, for instance I wouldn't overload for Paddle<->Brick because such comparison will never happen, so when Paddle<->Brick comparison is performed, it should just return false even though I never declared a TestCollision(Paddle* p, Brick* b). 

How can this be achieved? :)

 

 

 

Edited by MarcusAseth

Share this post


Link to post
Share on other sites
Advertisement

Your question is related to multiple sources of solutions so I will do one by one:

It sounds as your system is some type too generic so that you should rework your collision and try to reduce the number of possible collision targets but to answer your question; Unreal Blueprints are working on an UObject unreal object that has capabilities to do some type loockups such as owning class/struct type, value type and so on. So what you would need is either some kind of wrapper or meta storage or some wired c++ tricks that help pointing out if it is one of your derived classes. Such a trick is

dynamic_cast<derived*>(base)

that will return a null_ptr if dosent match the derived type. A more template and not so wired version is to use an own implementation of Object that provides those information to you. This could utilize templates and inheritance for example to wrap an untyped structure arround an internal typed derived class

class Object
{
   private:
      class BaseObject
      {
         public:
            virtual const type_info& GetType() = 0;
            virtual uint32 Size() = 0;
      };
      template<typename T> class TypedObject : BaseObject
      {
         public:
            inline virtual const type_info& GetType() { return typeid(T); }
            inline virtual uint32 Size() { return sizeof(T); } 
      };
        
      template<typename T> inline BaseObject* GetTypeHandler()
      {
         static TypedObject<T> handler;
         return &handler;
      }
        
      BaseObject* handler;
      void* value;
  
   public:
      template<typename T> inline Object(T* ptr) : handler(0), value(ptr)
      {
         handler = GetTypeHandler<T>();
      }
};

Calling this via the typed constructor will do as follows: Create a static instance of a class TypedObject depending on your pointer passed that will auto evaluate the template argument for T once the function GetTypedHandler is first called and/or return the created instance's pointer as its untyped base class. This handler object instance implements the abstract declared virtual functions of its base class depending on the template argument T passed to derived class, so even if the pointer is BaseObject, it will return the derived classes function values.

This leads to having your

Object obj(MyPaddlePtr);
assert(type_trait<Paddle>::type == obj.GetType())

evaluate to true where it will evaluate to false for for example MyBrickPtr (when those prts were of different derived class type). I implemented a class like this as a toy implementation and it works so may be a solution.

 

Your second question could be easily solved by using implicite template arguments as described in the Object approach and template specialization like follows

template<typename T, typename E> bool Collides(T* ptr, E* entity);
  
template<> bool Collides<Paddle, Ball>(Paddle* ptr, Ball* entity)
{
  ...
}
template<> bool Collides<Ball, Brick>(Ball* ptr, Brick* entity)
{
  ...
}

so you could call

if(Collides(this, derivedEntityType))

and template arguments are automatically choosen from the compiling unit. The compiler will (usually) choose the most matching template function first so you could also easily change the above implementation to

template<typename T, typename E> bool Collides(T* ptr, E* entity)
{
   return false; 
}

as default choosen function so that anything you dont specialized your function for will return a failure collision

Share this post


Link to post
Share on other sites

Thank you for the answer @Shaarigan I just woke up and is already clear I need some cofee and to read it again more carefully and possibly multiple time before I fully grasp it and try :D

Quote

It sounds as your system is some type too generic so that you should rework your collision and try to reduce the number of possible collision targets

That is probably true, but I feel like I will need to learn this anyway if I want to do stuff with components in the future (even though maybe is not necessary? not sure yet...), so I thought to embed this training into this part of the breakout game :)

I already have the feeling that to makes this compile it will take me some tries, so I'll probably be back later with more questions :P

Share this post


Link to post
Share on other sites

For my engines entity component system, I made a template function for each system to query components of a certain type using a static instance of the component list class. Each entity adding/removing a component tells the list about updates for its components. A system then could grab the desired list to operate for example on each collider component continiously. There might be better fitting solutions, I choosed this approach to be memory and performance aware but also flexible

[EDIT] I forgot, the main trick big engines do is to pre-erase possible collision checks by utilizing some kind of collision tree/scene view lookup. For simple games you could spread your entities arround a quad tree instance so collision testing would lead to a query into your quad tree for certain target and result into an O(log n) lookup result list of entities at certain area to test. This leads to significant less computation

Edited by Shaarigan

Share this post


Link to post
Share on other sites

@Shaarigan I've read again your first answer but I'm still not quite sure how to implement it / how does it map into my existing code, I think I'll need to learn this more slowly/gradually, because I am confused x_x (I am a slow learner xD) 

Can you suggest me any books on the subject that will get me there if read from cover to cover? :P

 

Edited by MarcusAseth

Share this post


Link to post
Share on other sites

Sorry, no book but a stackoverflow post that describes the functionality of object/any far better than I could as same as in this blog post


https://stackoverflow.com/questions/24702235/c-stdmap-holding-any-type-of-value

https://akrzemi1.wordpress.com/2014/01/13/type-erasure-part-iv/

 

And a way more performing implementation is for example this one from codeproject

https://www.codeproject.com/Articles/11250/High-Performance-Dynamic-Typing-in-C-using-a-Repla

 

This is all about type erasure using templates and void ptr to hold a type and provide some type information about it. One thought was to solve your first question, to not pass a pointer to an entity object rather than pass a container arround that works similar to what I cross referenced here but a way simpler because

dynamic_cast<derived*>(base)

might work on most compilers but may also fail/suceed in failure so you need something more reliable to get the kind of derived class from your entity object. The idea is to implement some type of object_entity (like object in C#) that has two members, a pointer to your entity passed in it and a pointer to some sort of type-info structure so your systems are able to query type informations about your object. This might accordingly to the code project implementation look like this

class Object
{
   public:
      enum TypeFlag
      {
         Unknown,
         Paddle,
         Brick,
         Ball
      }
  
   private:
      template <typename T> TypeFlag GetTypeFlag() { return Unknown; }
      template <> TypeFlag GetTypeFlag<Paddle>() { return Paddle; }
      template <> TypeFlag GetTypeFlag<Brick>() { return Brick; }
      template <> TypeFlag GetTypeFlag<Ball>() { return Ball; }
  
      struct TypeInfoBase
      {
         virtual int Size() = 0;
         virtual TypeFlag Flag() = 0;
      }
      template <typename T> TypeInfo : TypeInfoBase
      {
         virtual int Size() { return sizeof(T); }
         virtual TypeFlag Flag() { return GetTypeFlag<T>(); }
      }
  
  	  template <typename T> TypeInfoBase* GetTypeInfo()
      {
         static TypeInfo<T> info; //static variable will be initialized once
         return info;
      }
  
      TypeInfoBase* handler;
      Entity* value;
  
      public:
         template <typename T> inline Object(T* value) : handler(0), value(value)
         {
            handler = GetTypeInfo<T>();
         }
  
         inline Enity* Value() const { return value; }
         inline TypeFlag Flag() const { return handler->Flag(); }
         inline int Size() const { return handler->Size(); }
  
         template <typename T> T* Cast() { return static_cast<T*>(value); }
};

(Note: This is untested for syntactical correctness)

You will then use this in this way

Paddle* paddle = new Paddle(); //dont complain about new and plain pointers here, it is an example ;)
Object obj(paddle);

cout << obj.Size(); //obj.Size() => handler.Size() => TypeInfo<Paddle>.Size()
cout << obj.Flag(); //obj.Flag() => handler.Flag() => TypeInfo<Paddle>.Flag() => GetTypeFlag<T>() => GetTypeFlag<Paddle>() => Paddle

 

Share this post


Link to post
Share on other sites

Thanks @Shaarigan , now is already much more clear, probably it was already clear before and the only difference is that the more examples I see the more I can wrap my head around it :P

Should I prefer enum class over enum or there are specific reasons to use plain enum instead?

Also I saw a video on youtube where the guy did it in a way that seems a lot like the one you explained, he called it "The poor man's reflection" though, why is that?! xD

Also I only use VC, but for how you talk about dynamic_cast it sounds kind of unreliable, is that really true?! How much unreliable is it? :S 

Btw, I just woke up so I'll leave those link for after cofee, but I'll probably come back later to ask about those as well :P 

Edited by MarcusAseth

Share this post


Link to post
Share on other sites

I use plain enums to keep the code VS2010 compatible without C++ 11 features, you may use whatever you like.

C++ does not has any reflection capability as for example C# has that enables to fully assemble a class definition tree only by on-board functions. C++ has RTTI but on the cost of performance and just getting a name and compare to given types thats it, so not worth to mention. dynamic_cast belongs to RTTI but may depending on compiler and runtime return false positive results for certain types depending where the base and derived types are in the argument list. Also it is damn slow due to string compare types.

This is the reason why there exists reflection frameworks using different approaches (and I currently expand mine in my engine). RTTI types are not compiler independent so bad for serialization and data exchange (you could not send a serialized type from a Windows client to Linux server for example) and performance is not best at all so bad for games

Share this post


Link to post
Share on other sites

So a "decision based on static c++ type" is not a strategy you need to be using anywhere in your code.  If it did, this would probably be better served by polymorphism via virtual functions ... but whatever.

A decision based on a logical/abstract/property representing a "type" within your game system ... makes tons of sense.  This is cases where you have a "collidable object" class or interface or whatever.  And it has a type, like "simple rect", "simple circle", or "complex polygon" as its 3 supported collision detection types.  then each object's 1 colliadable object has a "detection type" property, and an appropriate associated data info object to match.  Yes this is just like the feature of RTTI, but it is a DATA or OBJECT CONSTRUCTION truth, not a code truth.  You are free to use inheritance, containment, interface, delegation  or any and all other programming strategies to implement this feature - and interestingly, all of them would likely be clearer and/or faster than C++'s RTTI feature ... while giving you more flexibility too.

Share this post


Link to post
Share on other sites

As for optimizing out the NxN performance issue of testing every object against every object even though most can't hit each other.  This would be a case of maintaining more tightly controlled lists of objects for your business at hand. 

A universal "full game object database" collection is great for making saving/loading easy (although it isn't really suited for that, because you really want 2 lists the "static/level" objects and the dynamic/active objects ... because then a save is just a reference to a level, and then the dynamic objects.  But I digress.

For your purpose, depending on the game, you might have lists such as "map objects" and "autonomous objects" if your game rules were such that you only needed to check the list of autonomous objects vs the list of autonomous and map objects.  This way you'd check 3 vs 200, not 200 vs 200 ... or whatever.

Other optimizations exist as well, such as spatial partitions, or even simply sorted lists by each dimension (for cases where more objects move very little each frame)

Share this post


Link to post
Share on other sites

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!