How to make firework effect in a game with TDD

This article is talking about how to add a firework or confetti in a game with TDD.

Here are the content overview:

  • Concept
  • Prepare the firework particle
  • Create a custom view in CocosStudio

  • Create a custom class FireworkView with TDD methodology

  • Import the cocostudio data and Test

  • Bind the particle objects and Test

  • Add method to play specific firework emitter

  • Add logic to play all firework emitter automatically

Demo:

Concept

The firework visual effect is based on playing several firework particles on different location of the screen.

Therefore the components should serves the following functions:

  • Play a specific firework emitter
  • Logic to tell the firework emitter play the “firework”

Project

You can access the related code in the follow project:

https://github.com/tklee1975/SimpleTDD-ccx3-demo

Setup #1: Prepare the firework particle

  • Go to http://particle2dx.com/ an online free particle maker
  • Go to the template and select “fw1” in the “Firework” section
  • Adjust a bit the particle
  • My Setting is:
    • GravityY set to -400 
    • Using Additive blend instead
  • Export the particle and test it in the CocosStudio
  • Setup #2: Create a custom view in CocosStudio

Expect output: the particle files (particle.list and particle.png) are ready to use.

Setup #2: Create a custom view in CocosStudio

The second step is making a view contain several “firework” particle that we need  in the next step. You can use the firework particle made in step1 or particle made from somewhere else;

Here are the steps:

  • Create a custom view (Layer) called “FireworkView” in CocosStudio under ‘/gui/’ folder
  • Size: 320 x 600 // Size can be varied if you like
  • Add the first particle
    • Add a particle to your view
    • Name it as “firework1”
  • Add other 4 particles
    • Copy “firework1” to other 4 particles
    • Rename them as “firework2”, “firework3”, .. “firework5”
    • Place them evenly in the screen
  • note: you can more if you like
  • Publish the file
  • Make sure the particles and gui are imported to XCode

Expect output:

  • The csb file of firework particles is ready to use.
  • The particles are ready to use.

Step 1: Create the FireworkView

This step is simply create the FireworkView with the FireworkView.csb loaded.

  • Create the FireworkView class inherit from ui::Layout (.h & c++)
  • Create setupCsb method with following code:
void FireworkView::setupCsb(const std::string &csbName) {
      Node *rootNode = CSLoader::createNode(csbName);
      addChild(rootNode);
      setContentSize(rootNode->getContentSize()); // update the layout size based on the CSB size
}
  • Call the setupCsb in init like this:
bool FireworkView::init(){
    if(Layout::init() == false) {
       return false;
    }
    std::string csb = "gui/FireworkView.csb";
    setupCsb(csb);
    return true;
}
  •  Assume the FireworkView is good to test now!

Step 2: Create the Unit Test FireworkViewTest

This step is to create an unit test to the FireworkView

  • Go to to project folder
  • Run ./script/createTest.sh FireworkView
  • when success, an unit test called ‘FireworkViewTest’ in ‘/Classes/UnitTest’ folder
  • Add “FireworkViewTest.cpp” and “FireworkViewTest.h” to the project
  • Change testSample in FireworkViewTest to testCreate and add the following source

    	void FireworkViewTest::testCreate()
    	{
    		FireworkView *view = FireworkView::create();
    		view->setAnchorPoint(Vec2(0.5, 0.5f));
    		view->setPosition(Vec2(250, 160));
    
    		addChild(view);
    
    	}
    
  • Modify “MyTDDCases.h” and “FireworkViewTest” to it like this
       #include "FireworkViewTest.h"
    
    	TDD_CASES
    	{
    		TEST(FireworkViewTest),
    		...
    
  • Run the Test (Main Screen -> Click “simpleTDD” -> Click “FireworkViewTest” -> Click “testCreate”)

Expected:

  • You will see all 5 particles fire at the same time.

What’s next?

  • We are not expect the particles fire at the same time.
  • And we need to bind the Particle in code and control them

Step 3: Setup the Emitter array 

This step is to setup an Emitter Vector that we can control every particles

FireworkView.h

	Vector<ParticleSystemQuad *> mEmitterList;

FireworkView.cpp

// Clean up first
mEmitterList.clear();

// Define the EmitterList and
// By default
int numFirework = 10;

for(int i=1; i<=numFirework; i++) { 	std::string nodeName = StringUtils::format("firework%d", i); 	ParticleSystemQuad *particle = rootNode->getChildByName<ParticleSystemQuad *>(nodeName);
	if(particle != nullptr) {
		mEmitterList.pushBack(particle);
	}
}

// Prevent all emitter being activated
for(ParticleSystemQuad *particle : mEmitterList) {
	particle->stopSystem();
}

In the Firework class:

  • First add the Emitter Vector in the header file like shown above
  • Then, add the logic to bind the Emitter  in setupCsb method

Then go to unit test and then run the test again

Expected:

  • No weird particle firing when create the View.

What’s next?

  • Make the specific emitter fire as we wish to.

Step 4: Activate a specific firework   

This step is to add code to make specific firework fire;

FireworkView.h

void activateEmitter(int index);

FireworkView.cpp

void FireworkView::activateEmitter(int index)
{
	if(index < 0 || index >= mEmitterList.size()) {
		log("FireworkLayer::activateEmitter: out of bound. index=%d", index);
		return;
	}

	ParticleSystemQuad *particle = mEmitterList.at(index);
	particle->resetSystem();
}

In the FireworkView, we will add the method ‘activateEmitter’; The implementation is simply call ‘resetSystem’ for the particle system from the given index;

FireworkViewTest.h

void testActivateEmittor();
FireworkView *mFireworkView;

FireworkViewTest.cpp

void FireworkViewTest::setUp()
{
	log("TDD Setup is called");
	setBackgroundColor(Color3B::BLACK);

	FireworkView *view = FireworkView::create();
	view->setAnchorPoint(Vec2(0.5, 0.5f));
	view->setPosition(Vec2(250, 160));

	addChild(view);

	mFireworkView = view;
}

void FireworkViewTest::testActivateEmittor()
{
	static int idx = 1;

	mFireworkView->activateEmitter(idx);

	idx++;
	if(idx >= 5) {
		idx = 1;
	}
}

In the UnitTest, first add a FireworkView in the view and link to a variable ‘mFireworkView’ and then add the test method ‘testActivateEmittor’;

In the testing method, use a static integer ‘idx’ to make the value different in every calls; and then call the activateEmitter to see the result.

Expect Result:

You can emulate the firework manually by clicking the ‘testActivateEmittor’;
But it is not good by manual, we will try to automate it in next step.

Step 5: Make the firework fire automatically    

Finally add logic to make Firework fire automatically.

FireworkView Class

void FireworkView::startFirework()
{
	scheduleUpdate();
}

void FireworkView::stopFirework()
{
	unscheduleUpdate();

	// Prevent all emitter being activated
	for(ParticleSystemQuad *particle : mEmitterList) {
		particle->stopSystem();
	}
}

void FireworkView::update(float delta)
{
	mCooldown -= delta;

	if(mCooldown > 0) {	// Not yet activate
		return;
	}

	mCooldown = 0.1f * RandomHelper::random_int(5, 12);	// random interval
	activateEmitter(mCounter);
	updateCounter();
}

int FireworkView::getEmitterCount()
{
	return (int) mEmitterList.size();
}

void FireworkView::updateCounter()
{
	mCounter = (mCounter + 1) % getEmitterCount();
}

  • In the FireworkView, add two method ‘startFirework’ and ‘stopFirework’ to control the firework play and pause;
  • Add the firework playing logic in the update logic and it will be called when scheduleUpdate is called.
  • The method ‘updateCounter()’ is to define the emitter to be fired in the next ‘update’ loop.

Unit Test: FireworkViewTest

void FireworkViewTest::testStartFirework()
{
	mFireworkView->startFirework();
}

void FireworkViewTest::testStopFirework()
{
	mFireworkView->stopFirework();
}

 

Expected Result:

  • It is done. You can start and stop the firework in your unit test and apply it in your game.
  • In order to make the firework more fancy, you can modify the FireworkView that.
    • The fire time interval can be configured.
    • The order of the firework emitter is shuffled every time.

Leave a comment