[Cocos3.0 Tutorial] Use the Cocos2d-x-3.0 game engine. Write a Tower Defender Game.(Part06)

Previous: http://www.cocos2d-x.org/forums/6/topics/53962

7.Rotation

In the Tower.h:

#pragma once
#include "cocos2d.h"
#include "Creep.h"

class Tower: public Sprite 
{
public:
……
	Creep* getClosestTarget();
……
};

In tower.cpp:

Creep* Tower::getClosestTarget()
{
	Creep *closestCreep = NULL;
	double maxDistant = 99999; // It should be bigger than the range.

	DataModel *m = DataModel::getModel();

	for each(Sprite *target in m->targets) 
// for(Vector<Creep*>::iterator i = m->targets.begin(); i < m->targets.end(); ++i) // Use these code if your VC doesn’t support C++11.
	{
// Sprite *target = *i;  // Use these code if your VC doesn’t support C++11.

		Creep *creep = (Creep *)target;
		double curDistance = ccpDistance(this->getPosition(), creep->getPosition());

		if (curDistance < maxDistant) {
			closestCreep = creep;
			maxDistant = curDistance;
		}
	}
	if (maxDistant < this->range)
		return closestCreep;
	return NULL;
}

    Add those codes into ‘void MachineGunTower::towerLogic(float dt)’:

void MachineGunTower::towerLogic(float dt)  {

	this->target = this->getClosestTarget();

	if(this->target != NULL)
	{
		Point shootVector = this->target->getPosition() - this->getPosition();
		float shootAngle = ccpToAngle(shootVector);
		float cocosAngle = CC_RADIANS_TO_DEGREES(-1 * shootAngle);

		float rotateSpeed = 0.5 / M_PI;
		float rotateDuration = fabs(shootAngle * rotateSpeed);

		this->runAction(Sequence::create(RotateTo::create(rotateDuration, cocosAngle), NULL));
	}
}

8.Ammo

    Yeah~ you got it. It is high time to reload the ammo. This probably is the most interesting and exciting thing. After we finished this tutorial, I hope you know how to make a different types of the defend tower exactly like how we create our different type of enemies. And set various values for HP, fire rate, mobility and damage.

    So first thing first, what we are going to do is create the bullets — the class ‘Projectile’. Create another pair of files — Projectile.h and Projectile.cpp.

In the DataModel.h:

#include "Projectile.h" 

and

Vector<Projectile*> projectiles;

You know exactly where you should put them.

In the Projectile.h:

#pragma once
#include "cocos2d.h"

USING_NS_CC;

class Projectile: public Sprite 
{
public:
	static Projectile* projectile();
};

In the Projectile.cpp:

#include "Projectile.h"

Projectile* Projectile::projectile()
{
	Projectile* projectile = (Projectile*)Sprite::create("Projectile.png");
	if (projectile != NULL)
	{
		return projectile;
	} else {
	return NULL;
	}
}

    Now we add some new method into the “void TutorialScene::update(float dt)”. (We already it in the “TutorialScene.cpp”.)

void TutorialScene::update(float dt)  {
	DataModel *m = DataModel::getModel();
	Vector<Projectile*> projectilesToDelete;

	for each(Projectile *projectile in m->projectiles)
	// for (int i = 0; i < m->projectiles.size(); i++) // Use these code if your VC doesn’t support C++11.
	{
		// Projectile* projectile = (Projectile*)(m->projectiles.at(i)); // Use these code if your VC doesn’t support C++11.
		Rect projectileRect = Rect(projectile->getPositionX() - (projectile->getContentSize().width / 2),
			projectile->getPositionY() - (projectile->getContentSize().height / 2),
			projectile->getContentSize().width,
			projectile->getContentSize().height);

		Vector<Creep*> targetsToDelete;

		for each(Creep *target in m->targets) 
		// for (int i = 0; i < m->targets.size(); i++) // Use these code if your VC doesn’t support C++11.
		{
			// Creep* target = (Creep*)(m->targets.at(i)); // Use these code if your VC doesn’t support C++11.

			Rect targetRect = Rect(target->getPositionX() - (target->sprite->getContentSize().width / 2),
				target->getPositionY() - (target->sprite->getContentSize().height / 2),
				target->sprite->getContentSize().width,
				target->sprite->getContentSize().height);

			if (projectileRect.intersectsRect(targetRect)) 
			{
				projectilesToDelete.pushBack(projectile);

				Creep *creep = target;
				creep->curHp -= 1;

				if (creep->curHp <= 0) 
				{
					targetsToDelete.pushBack(creep);
				}
				break;
			}
		}

		//for each(Creep *target in targetsToDelete)
		for (int i = 0; i < targetsToDelete.size(); i++)
		{
			Creep* target = (Creep*)(targetsToDelete.at(i));

			m->targets.eraseObject(target);
			this->removeChild(target, true);
		}		
	}

	for each(Projectile *projectile in projectilesToDelete)
	// for (int i =0; i < projectilesToDelete.size(); i++) // Use these code if your VC doesn’t support C++11.
	{
		// Projectile* projectile = (Projectile*)(projectilesToDelete.at(i)); // Use these code if your VC doesn’t support C++11.

		m->projectiles.eraseObject(projectile);
		this->removeChild(projectile,true);
	}
}

    Here, we traverse all the projectiles. For each of projectiles, we traverse all the targets. And determine whether the bullet — ”projectile” is intersect with the targets. If that is true, the enemy HP will minus 1, the bullet which hit the target will pull into the array. Once the enemy HP equivalent to “0”, we put this enemy into the different array and for the deleting.

    Finally, going back to the “Tower”, let us see the mechanism of the firing.
    Don’t forget add those code in the Tower.h:

#include "Projectile.h"  

void finishFiring(); 
void creepMoveFinished(Node* sender);

In the “void MachineGunTower::towerLogic(float dt)”:

this->runAction(Sequence::create(RotateTo::create(rotateDuration, cocosAngle), CallFunc::create(this, callfunc_selector(MachineGunTower::finishFiring)), NULL)); // Delete the old one.

Then:

void MachineGunTower::finishFiring()
{
	DataModel *m = DataModel::getModel();

	if (this->target != NULL && this->target->curHp > 0 && this->target->curHp < 100)
	{
		this->nextProjectile = Projectile::projectile();
		this->nextProjectile->setPosition(this->getPosition());

		this->getParent()->addChild(this->nextProjectile, 1);
		m->projectiles.pushBack(this->nextProjectile);

		float delta = 1.0f;
		Point shootVector = -(this->target->getPosition() - this->getPosition());
		Point normalizedShootVector = ccpNormalize(shootVector);
		Point overshotVector = normalizedShootVector * 320;
		Point offscreenPoint = (this->getPosition() - overshotVector);

		this->nextProjectile->runAction(Sequence::create(MoveTo::create(delta, offscreenPoint), CallFuncN::create(this, callfuncN_selector(MachineGunTower::creepMoveFinished)), NULL));

		this->nextProjectile->setTag(2);

		this->nextProjectile = NULL;

	}
}

void MachineGunTower::creepMoveFinished(Node* sender)
 {
	DataModel * m = DataModel::getModel();
	Sprite *sprite = (Sprite *)sender;
	this->getParent()->removeChild(sprite,true);

	m->projectiles.eraseObject((Projectile*)sprite);
}

    Once our tower facing the enemy, it will start shooting. This function is focusing on creating the bullets — “projectile”. As you can see, after turning to the right direction the projectile will “runAction” and “MoveTo” the certain point. And we also set the maximum value of the range. Once the bullets get out of the range, they all will be removed.

Now, enjoy yourself:
Image Title


00.png (692.5 KB)

Can you share your source code and image in resource?

@Hoangtaiki Already upload the resources in the first tutorial. Thanks for your support.

2 Likes

@Suigintou This method i can’t find any where

this->nextProjectile = Projectile::projectile();

@Hoangtaiki What do you mean you can’t find the method? You haven’t created the projectile.h and .cpp?

Thanks for the tutorial,
The same error for me:

this->nextProjectile = Projectile::projectile();

Error C2039: 'nextProjectile' : Is not a member of 'MachineGunTower' 

You have human errors in the name of includes in this page in “TutorialSence.h” instead of “TutorialScene.h” in the code lines. :slight_smile:

I have some errors more :frowning: Could you please upload your class files? I only see the images and .tmx file in resources, not the classes.

@panor Oh~god. I will check the error. And since you already read the whole tutorial, so here you go.

Thanks @Suigintou :smiley:

I will try it now!

Now it works perfect! :;ok
Thanks 4 all!

@panor That is something wonderful to hear. My thanks too!

@Suigintou I will try but when i run in on iOS, It crash when i touch a tower in GameHUD :frowning:

@Suigintou The game has crash when i try to build new tower. Please help me

Assertion failed!

Program: ...werDefense\proj.win32\Debug.win32\TowerDefense.exe
File: ..\base\CCValue.cpp
Line: 572

Expression: _type == Type::MAP

For information on how your program can cause an assertion
failure, see the Visual C++ documentation on asserts

(Press Retry to debug the application - JIT must be enabled)

I don’t know why @PhiAvari have this error, but it could be for the name (in Chinese I think) of the background image.

Open the .tmx file with an text editor and change this name for other name and the image file name too.

I’m using VS 2012, and it runs perfect! :wink:

@Hoangtaiki I didn’t run it on the Mac system. Will the problem be using the Chinese name for the picture?

I think not, but I couldn’t open the .tmx file with TiledMap, so I changed it before running the project…

@panor Thank you so much, i have fixed my problem :slight_smile:

You’re welcome! I’m glad to have helped you @PhiAvari :slight_smile:

Hi, I’m using your example and I have changed the orthogonal map for an isometric map.

Can you tell me why the map appears to the right instead of the center of the view?

Thanks and congratulations for the t-shirt! :wink:

@Suigintou can i have the classes to?

I don’t know for sure. Probably its value of anchor point is not suitable in this case.