[Cocos2d Tower Defense Game Development] Cocos2dx-3.X Completes the Tower Defense Game Kingdom Defense War--Map

Keywords: Mobile

Original Link: https://my.oschina.net/wuhaoyu/blog/607832

As above, add a map and add it directly to the map layer. I set AnchorPoint at (0,0) to calculate coordinates.

mapSprite = Sprite::createWithSpriteFrameName(String::createWithFormat("Stage_%d.png",level+1)->getCString());
mapSprite->setAnchorPoint(Point(0,0));
mapSprite->setPosition(Point(0,0));
addChild(mapSprite);

This chapter focuses on the implementation of two fixed and store skills


First, there are two fixed skills, with meteorites as an example

First add the Key Picture Wizard

stoneSprite = Sprite::createWithSpriteFrameName("power_portrait_fireball_0001.png");
stoneSprite->setAnchorPoint(Point(0,0));
stoneSprite->setPosition(Point(10,-20));
stoneSprite->setName("inactive");
//Determine if the countdown is complete
completeStone = false;
addChild(stoneSprite,1);

Then there's the countdown mask, implemented in ProgressTimer, on top of the key picture Genie

stoneTimer = ProgressTimer::create(Sprite::createWithSpriteFrameName("power_loading.png"));
stoneTimer->setAnchorPoint(Point(0,0));
//Turn clockwise
stoneTimer->setReverseDirection(true);
stoneTimer->setPosition(Point(10,-20));
stoneTimer->setPercentage(100);//Percentage of original
this->addChild(stoneTimer,1);


Add timer to update ProgressTimer status

void PlayerStateMenu::updateStoneProgress(float Dt){  
	stoneTimer->setPercentage(stoneTimer->getPercentage() - Dt*2);//Update Progress 2
	if (stoneTimer->getPercentage()==0) {
		this->unschedule(schedule_selector(PlayerStateMenu::updateStoneProgress));//Cancel timer
		completeStone = true;
	}
	return;
}

Schdule when you want to start, like after the first enemy wave


Add touch response

auto stoneListener = EventListenerTouchOneByOne::create();
stoneListener->onTouchBegan = [&](Touch* touch, Event* event){
		
	auto target = static_cast<Sprite*>(event->getCurrentTarget());
	Point locationInNode = target->convertTouchToNodeSpace(touch);
	Size size = target->getContentSize();
	Rect rect = Rect(0, 0, size.width, size.height);
	//If the first click clicks
	if(rect.containsPoint(locationInNode)){
		//If cooling ends
		if(completeStone == true){
			//Remove other skills to touch monitor
			mTouchLayer->removeAllListener();
			if(stoneSprite->getName() == "inactive"){
			//Set to Click Status
			stoneSprite->setSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("power_portrait_fireball_0002.png"));
			//Change state TAG
			stoneSprite->setName("active");
			//Change the other 2 key states
			/****
			****/
			//Touch Layer Set up Meteorite Skill Monitoring
			mTouchLayer->setFireBallTouchShield();
		//Second click, cancel
		}else{
			mTouchLayer->removeFireBallTouchShield();
			stoneSprite->setSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("power_portrait_fireball_0001.png"));
			stoneSprite->setName("inactive");
		<span style="white-space:pre">	</span>}
<span style="white-space:pre">		</span>}
		return true;
	}
		return false;  
};
stoneListener->onTouchEnded = [&](Touch* touch, Event* event){
};
//Touch phagocytosis
stoneListener->setSwallowTouches(true);
_eventDispatcher->addEventListenerWithSceneGraphPriority(stoneListener,stoneSprite);

When the countdown ends, set completeStone to true, and only then click the key will trigger.

Click Skills to add an EventListenerTouchOneByOne to the touch layer, covering the entire touch layer, which is executed when the map is clicked

Let's look at the touch layer


class TouchLayer :public Layer
{
public:
	virtual bool init();
    CREATE_FUNC(TouchLayer);
	EventListenerTouchOneByOne* touchlistener;
	EventListenerTouchOneByOne* FiereBalllistener;

	void setFireBallTouchShield();
	void removeFireBallTouchShield();
	bool onFireBallTouchBegan(Touch* touch, Event* event);
	void onFireBallTouchEnded(Touch* touch, Event* event);

	bool isFlag;
	bool onTouchBegan(Touch* touch, Event* event);
	void onTouchEnded(Touch* touch, Event* event);
	void onTouchMoved(Touch* touch, Event* event);
	Size winSize;
	bool isMoved;

	void removeAllListener();
};
Here I just intercepted the part about the meteorite and the moving map


Add touch monitoring layer in BaseMap

void BaseMap::initTouchLayer()
{
	mTouchLayer = TouchLayer::create();
	mTouchLayer->setContentSize(mapSprite->getContentSize());
	mTouchLayer->setAnchorPoint(Point(0,0));
	mTouchLayer->setPosition(Point(0,0));
	addChild(mTouchLayer,99);
}

Add Map Move Time Touch

touchlistener = EventListenerTouchOneByOne::create();
touchlistener->onTouchBegan = CC_CALLBACK_2(TouchLayer::onTouchBegan, this);
touchlistener->onTouchEnded = CC_CALLBACK_2(TouchLayer::onTouchEnded, this);
touchlistener->onTouchMoved = CC_CALLBACK_2(TouchLayer::onTouchMoved, this);
touchlistener->setSwallowTouches(true);
_eventDispatcher->addEventListenerWithFixedPriority(touchlistener,-1);
_eventDispatcher->addEventListenerWithSceneGraphPriority(touchlistener,this);


This setting sets FiexPriority to -1 to ensure that touch events are triggered first with other touch times such as skills

void TouchLayer::onTouchEnded(Touch* touch, Event* event)
{
<span style="white-space:pre">	</span>touchlistener->setSwallowTouches(isMoved);
	isMoved = false;
}

void TouchLayer::onTouchMoved(Touch* touch, Event* event)
{
        // Calculating the sliding increment during sliding
        auto diff = touch->getDelta(); 

		//Finger movement correction because finger touch is not as fixed as mouse touch
		if(abs(diff.x) >5|| abs(diff.y) >5){
			isMoved = true;
		}
			// Get the current bgSprite location
			auto currentPos = this->getParent()->getPosition();
			// Get where bgSprite should be after sliding
			auto pos = currentPos + diff;

			//Boundary control, constraining pos position
			pos.x = MIN(pos.x, 0);
			pos.x = MAX(pos.x, -1200 + winSize.width);
			pos.y = MIN(pos.y, 0);
			pos.y = MAX(pos.y, -1000 + winSize.height);
			// Reset Map Layer Location

			this->getParent()->setPosition(pos);
}

IsMoved is true when the finger moves over the touch layer, which is set SwallowTouches (isMoved) will swallow up other touch events
This is to ensure that when you move past or end up happening to be at a touch point, other touch events will not be triggered (for example, when you move your finger exactly on a defense tower, you will not pop up the upgrade layer of the tower)
Additionally, it does not trigger skill event monitoring when moving, so you can select the skill release location after moving.


void TouchLayer::setFireBallTouchShield()
{
	//Call this method to create a meteorite skill touch time
	FiereBalllistener = EventListenerTouchOneByOne::create();
	FiereBalllistener->onTouchBegan = CC_CALLBACK_2(TouchLayer::onFireBallTouchBegan, this);
	FiereBalllistener->onTouchEnded = CC_CALLBACK_2(TouchLayer::onFireBallTouchEnded, this);
	FiereBalllistener->setSwallowTouches(true);
	//Settings higher than mobile touch events
	_eventDispatcher->addEventListenerWithFixedPriority(FiereBalllistener,1);
	_eventDispatcher->addEventListenerWithSceneGraphPriority(FiereBalllistener,this);
}

void TouchLayer::removeFireBallTouchShield()
{
	//Remove this listening time when skills are used
	if(FiereBalllistener!=NULL)
		_eventDispatcher->removeEventListener(FiereBalllistener);
}

bool TouchLayer::onFireBallTouchBegan(Touch* touch, Event* event)
{
	//Return to TRUE directly, intercept other times
	return true;
}

void TouchLayer::onFireBallTouchEnded(Touch* touch, Event* event)
{
	//Play Sound
	SoundManager::playFireballUnleash();
	//Create three meteorites
	auto fireBall1 = FireBall::create();
	addChild(fireBall1);
	fireBall1->shoot(static_cast<TouchLayer*>(event->getCurrentTarget())->convertTouchToNodeSpace(touch)+Point(-30,300));
	auto fireBall2 = FireBall::create();
	addChild(fireBall2);
	fireBall2->shoot(static_cast<TouchLayer*>(event->getCurrentTarget())->convertTouchToNodeSpace(touch)+Point(0,350));
	auto fireBall3 = FireBall::create();
	addChild(fireBall3);
	fireBall3->shoot(static_cast<TouchLayer*>(event->getCurrentTarget())->convertTouchToNodeSpace(touch)+Point(30,280));
	//Get the parent's player state layer after the crash, call startStone, restart the timing, and reset the ProgressTimer mask
	static_cast<BaseMap*>(this->getParent())->playerState->startStone();
	//Remove this listening event
	removeFireBallTouchShield();
}

That's almost what I do with whole skill monitoring plus touch-and-move

Store skills, summoning soldiers and other skills are the same way, but the skills used are different ~Other skills, such as freezing enemies, summoning soldiers, etc., will be introduced in the corresponding modules


I wrote this for the time being today, and there are some hard-working internships waiting for me to find them






Reprinted at: https://my.oschina.net/wuhaoyu/blog/607832

Posted by chopps on Thu, 12 Sep 2019 15:19:19 -0700