Godsend Programming #2: Pushable Blocks, Triggers and Animation

In this entry, I will go through the implementation of Pushable blocks, Event-driven objects and Animation, as I think these are some of the more interesting tasks I have worked on.

I am afraid I can not go through all of my work, as it would take up too many entries, such as the collectible UI system, the level flow of collecting items, unlocking the exit, the breakable block, the detailed implementation of moving platforms, the enemy navigation system which I developed with Radu, jumping on enemies heads and so on, but these are documented in code, so it should be easy enough to look into them if the reader is interested.

Pushable Block

The pushable block is something that the player can, well, push. Our initial idea for it was to have it static and only pushable when the player dashes into it.

However, making it stay in place was trickier than we thought it would be, because if it’s a dynamic body and the player collides with it, then the physics engine would resolve the collision by moving the block as well as the player. Hence, the block seemed to move no matter what.

Our other option was to make it static apart from when the player is dashing into it. This gave rise to problems due to the fact that static objects don’t collide with other static objects, such as moving platforms or travelators. Our third pass was making it kinematic so that when it lands on a moving platform or travelator, it adapts the velocity and moves as it should. The problem then arose from the fact that we don’t know when the block leaves a travelator and should become dynamic again to fall down, as kinematic bodies only collide with dynamic ones as well, but the travelator is not dynamic, nor is the block.

The only way we could think around it was to make a dynamic sensor object with 0 gravity at the end of the travelator to detect when the block should become dynamic. Without even going into details, this just seems wrong, hack-y and not scalable. By not scalable, I mean that you’d need to do the same thing for the pressure plate and any future objects the block should interact with. So this approach was not suitable.

Finally, a suggestion was made that the block only becomes kinematic when the player moves into it. I tried implementing this as well, but it wasn’t reliable due to the fact that when the player dashes into the block and it is pushed forward, the player can still move into the block and affect it. This kind of implementation was unreliable.

Therefore, we had to make a decision to make the block pushable in conventional ways. However, this is not necessarily bad, since it’s the kind of behaviour that’s expected from pushables in platformers. Furthermore, we managed to give meaning alternatively to the pushable block, which is bouncing it upwards when the player dashes into it. This way, the block can travel around the level vertically as well, opening up new level design possibilities:

void CGCObjPushable::Push( f32 fDirectionToGoIn )
{
	m_pv2ExternalVelocity = nullptr;
	GetPhysicsBody()->SetLinearVelocity( b2Vec2( fDirectionToGoIn * 2.0f, 10.0f ) );
}

Due to the nature of the player controller, we could not make use of friction to stop the block from moving, as the player relies on hard velocities to move around and ignores friction. This is one more case when I think that a well implemented physics controller would have been better.

Event-driven Objects

I implemented two types of event-driven mechanics. One is a switch-type, where the player activates it and it makes something happen until you de-activate it. The other is a pressure plate, which is active when a dynamic body is standing on it. The implementations of these are fairly straightforward, and the only thing of note is that the pressure plate uses collision counting the same way the player does:

void CGCObjPressurePlate::OnPushEnter()
{
	//if first time triggerring the pressure plate
	if( 0 == m_uNoCollisions )
	{
		//send activate message to target
		if( m_pcTriggerTarget->VIsActive() == false )
		{
			CCAnimation* pNextAnimation = CCAnimationCache::sharedAnimationCache()->animationByName( "Plate_Active" );
			RunAction( GCCocosHelpers::CreateAnimationActionOnce( pNextAnimation ) );

			m_pcTriggerTarget->VActivate();
		}
	}

	m_uNoCollisions++;
}

void CGCObjPressurePlate::OnPushExit()
{
	m_uNoCollisions--;

	//if all colliders have finished triggering the plate
	if( 0 == m_uNoCollisions )
	{
		//send deactivate message to target
		if( m_pcTriggerTarget->VIsActive() == true )
		{
			CCAnimation* pNextAnimation = CCAnimationCache::sharedAnimationCache()->animationByName( "Plate_Inactive" );
			RunAction( GCCocosHelpers::CreateAnimationActionOnce( pNextAnimation ) );

			m_pcTriggerTarget->VDeactivate();
		}
	}
}

I think I managed to do one thing really well when implementing this mechanic, something I struggled to make use of within the framework in other cases: Inheritance and Polymorphism. I implemented an abstract Interface for an object than can be triggered by a switch or pressure plate, which specifies all of the activation/deactivation functions. This is supplied to the switch/pressure plate at construction and there can be many types of trigger targets/behaviours, which will simply work when supplied to the triggers, as they just need to implement the functions:

class CGCObjTriggerTarget
{
public:
	CGCObjTriggerTarget() {};
	virtual ~CGCObjTriggerTarget() {};

	//Activates the action associated with this object to happen
	//this is called by the trigger object linked to this target
	virtual void VActivate() = 0;
	//Deactivates the action of the trigger target
	virtual void VDeactivate() = 0;
	//Accessor to check if the target is activated
	virtual bool VIsActive() = 0;
};

By using inheritance like this, we can define many more interesting ways of using event-driven mechanics than a plain switch (which was noted in Milestone Reviews), so using inheritance here seems to have really paid off.

I think using inheritance as much as possible in game logic would make the code a lot more manageable. It worked here, since the type is supplied directly at construction. However, many great uses of inheritance exist in collision logic. For example, defining a Dashable interface to combine all objects that do something when dashed into. Or a AffectorTarget interface for objects that can be affected by a travelator or moving platform just to name a few. The problem arises in not being able to safely identify these types, as the GCTypeID can only hold the information of one type. I am still currently trying to find a good way to extend this, and I think it is worth the time and effort to pursue it, as making collision logic generic would greatly reduce code duplication in the (already cluttered) collision code.

Efficient (?) Animations

Something that the team noticed is that creating animations in the current framework is done every time it needs to be done, more or less. However, we would like to be able to re-use these animations and load them all in at creation, then simply switch between them at run time. It took a bit of research into the API, such as the AnimationCache Class Reference(Cocos2D-x, 2013) to find a way to do this, but intuition from seeing a shared Sprite frame cache suggested that Animations could be cached too.

We managed to make use of this and achieved our desired effect of creating animations to start with, then retrieving them from the cache when we want to change animations. This was trickier to figure out than I thought it would be, but it should greatly improve efficiency. Furthermore, we could simply load all animations through the object group, so that they don’t need to be created by each instance of the object:

//add animations to the shared cache for future use(this way only create the animation once)
CCAnimationCache::sharedAnimationCache()->addAnimation( pAnimationRotate, "Rotate" );
CCAnimationCache::sharedAnimationCache()->addAnimation( pAnimationDead, "Dead" );

In this example of a deliverable object, we can then use the animations in the individual code, but we do not need to create them from scratch anymore:

//apply new dead animation
CCAnimation* p_StartAnimation = CCAnimationCache::sharedAnimationCache()->animationByName( "Dead" );
this->RunAction( GCCocosHelpers::CreateAnimationActionLoop( p_StartAnimation ) );

I think this is a great performance/efficiency gain, and I definitely think that we should be using this sort of animation creation for all objects. This helps us avoid constructing animations from scratch at run-time and helps us reduce the resource loading work.

Related Files

GCObjSwitch.cpp – https://drive.google.com/open?id=0B_BnvnFZLH7meElhMXVoaXpfanM

GCObjPushable.cpp – https://drive.google.com/open?id=0B_BnvnFZLH7mQ3pnSWRYU18zbjA

GCObjPressurePlate.h – https://drive.google.com/open?id=0B_BnvnFZLH7mOGtaS19oLVR1djg

GCObjPressurePlate.cpp – https://drive.google.com/open?id=0B_BnvnFZLH7mblFVdlAxLUN5akE

GCObjDelivery.cpp – https://drive.google.com/open?id=0B_BnvnFZLH7mbGp1RDFsLV84dlk

GCObjGroupDelivery.cpp – https://drive.google.com/open?id=0B_BnvnFZLH7mdGZLUlRLX1FEX3M