Godsend Programming #9: Player VFX

There are two types of VFX on the player that I did. One is the “dust” effect when the player jumps and lands (which I tacked onto other objects that land as well), as well as when he charges into something or dashes. The other one is the “dash trail” which is more of a particle effect, as the player places a multitude of these behind him when dashing. I’m not particularly proud of the implementation of both of these, but I am proud of how it looks. In terms of actual code quality, I think Alvaro did it better, as he created a separate attribute class for the VFX on other enemies – much cleaner than what I pumped out in a rush.

The way the dust effects are initialised and managed is crude: they are member variables of the player object, which you place next to the player and play their animation when needed. I did my best to make the VFX placement relative to the player, rather than hard-coded pixels, as I was thinking forward to when we might port it to different resolution devices (if ever). So the placement is done as ratios of sprite sizes:


Note that one VFX sprite is used for the jump/land/hit obstacle dust effect, which is good in that it uses less memory, but bad in that we can’t play these simultaneously. I used a separate object for the dash dust sprite, purely because dashing and landing happened so close to each other all the time and I wanted them both to be displayed.

The sprites are initialised on resource acquire, used up and then deleted on level exit (not explicitly, but by the default object group, so it might look leaky in the player code, but it’s not – all CGC objects get deleted by their object groups). A further optimisation would probably be to carry over all of the player data to the next level – would save us a bit of time loading each level.

There’s one sneaky implementation detail that I did for the dust VFX that saved me a bunch of time – I play the VFX once, and rather than tracking the animation (which is not so easy in cocos2d-x) and turning it to invisible once it finishes running, I just end the animation with a blank sprite. This means that it disappears and just stays there until it’s next needed. I think this helped make the code cleaner, but it might not be obvious to programmers as to why/how/where the sprite becomes invisible.

Ideally, for the next project, I would like to set up an animation system where the client can set a callback or event to be sent when the animation finishes running. I know Unity allows this quite easily, and it can definitely be useful for hiding sprites or syncing animation states.

The other type of VFX is the dash trail, as mentioned earlier.

I managed to make this one quite easily extendable, as the number of sprites (or particles if you like) to use can be easily changed via a constant variable. The way the effect works is that sprites are being placed at the player position during set time intervals in the dash:


The calculation is a bit messy, but it bears the trade-off that it’s easily tweakable, since the time intervals for placing a new sprite are calculated dynamically. The calculation itself could be improved, since it’s a bit processor wasteful – it determines which dash trail sprite should be placed, then checks if it’s already been placed, and ignores it if so. Rather, the “trail placer” code should just avoid the calculation altogether until it’s time to place a new sprite. This is yet another “if I did it again, what would I do differently” thought. I implemented in the simplest way possible, but definitely not the most efficient way possible. Then again, it doesn’t cause performance hiccups, so I guess what I’m talking about would be considered premature optimization.

Related files

GCObjPlayer.h – https://drive.google.com/open?id=0B_BnvnFZLH7mRGxJaG5mMWY0Tnc

GCObjPlayer.cpp – https://drive.google.com/open?id=0B_BnvnFZLH7mdUFJb3hEUDBtYnc

GCObjPlayerDashingState.h – https://drive.google.com/open?id=0B_BnvnFZLH7mZmNxd1Z4TmNyV00

GCObjPlayerDashingState.cpp – https://drive.google.com/open?id=0B_BnvnFZLH7mTUdHZ1dhQ09EYk0