溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2

發(fā)布時(shí)間:2020-06-29 22:08:01 來源:網(wǎng)絡(luò) 閱讀:860 作者:qiou2719 欄目:游戲開發(fā)

cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2
     歡迎回來,上篇我們講到了物理引擎中重力環(huán)境模擬以及主角考拉與地面墻壁的碰撞,相信大家已經(jīng)對(duì)2D世界的物理模擬有了一定的了解,現(xiàn)在我們接著講如何讓考拉動(dòng)起來吧!

     cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2

讓考拉動(dòng)起來!
    這里控制考拉移動(dòng)變得非常簡(jiǎn)單,它只有向前和跳兩個(gè)能力(源碼中我加了考拉向后走功能,建議大家自己加幾個(gè)虛擬按鍵來實(shí)現(xiàn)更非富的功能)如果你按著屏幕左半部考拉會(huì)向前走,按住右半部考拉會(huì)跳起來(原文設(shè)定考拉不會(huì)后退-_-)。
    我們需要在Player.h里加兩個(gè)成員變量:
    bool _forwardMarch;  //是否向前走
    bool _mightAsWellJump; //可以跳躍嗎?
在player.cpp的init方法或在構(gòu)造函數(shù)里把它們?cè)O(shè)置為false
在GameLevelLayer類里加上觸摸,在.h文件里加上:

virtual void registerWithTouchDispatcher();

	void ccTouchesBegan(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent);
	void ccTouchesMoved(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent);
	void ccTouchesEnded(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent);

在GameLevelLayer.cpp的init里加上(加載地圖代碼后)
setTouchEnabled(true);  //設(shè)置可觸摸
然后寫注冊(cè)觸摸方法

void GameLevelLayer::registerWithTouchDispatcher()
{
	CCDirector* pDirector = CCDirector::sharedDirector();
	pDirector->getTouchDispatcher()->addStandardDelegate(this, 0); //注冊(cè)多點(diǎn)觸摸
}

現(xiàn)在,讓我們看看那三個(gè)觸摸事件吧!

void GameLevelLayer::ccTouchesBegan(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent)
{
	CCSetIterator iter = pTouches->begin();
	for (; iter!=pTouches->end(); iter++)
	{
		CCTouch* pTouch = (CCTouch*)(*iter);
		CCPoint touchLocation = this->convertTouchToNodeSpace(pTouch);  //把touch點(diǎn)位置轉(zhuǎn)換為本地坐標(biāo)
		if (touchLocation.x > 240)   //在屏幕最右邊點(diǎn),就是跳
		{
			_player->bMightAsWellJump = true;
		}
		else 
		{
			_player->bForwardMarch = true;
		}

	}
}

void GameLevelLayer::ccTouchesEnded(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent)
{
	
	_player->bForwardMarch = false; //松開按鍵時(shí),設(shè)置為不可跳也不是向前狀態(tài)
	_player->bMightAsWellJump = false;
}

代碼一目了然,ccTouchesBegan時(shí)根據(jù)玩家按的位置決定了考拉狀態(tài)是前進(jìn)還是跳躍,松開按鍵時(shí)將這兩個(gè)狀態(tài)變量重置為false。


真正的角色移動(dòng)是在player的update方法里進(jìn)行的,看代碼:

void Player::update(float delta)
{
	CCPoint gravity = ccp(0.f, -450.f);  //考拉每秒下降450個(gè)單位
	CCPoint gravityStep = ccpMult(gravity, delta); //計(jì)算在重力影響下delta時(shí)間內(nèi)具體下降了多少, 即dt時(shí)間后下落速度為多少

	CCPoint forwardMove = ccp(800.f, 0.f); //前進(jìn)速度,每秒前進(jìn)800.f
	CCPoint forwardStep = ccpMult(forwardMove, delta);   // 1

	this->_velocity = ccpAdd(this->_velocity, gravityStep); //當(dāng)前速度=當(dāng)前速度+重力加速度
	this->_velocity = ccp(this->_velocity.x *0.9f, this->_velocity.y); //2
	
	if (this->bForwardMarch)
	{
		this->_velocity = ccpAdd(this->_velocity, forwardStep); //當(dāng)前速度要加上向前的速度矢量
	}// 3

	CCPoint minMovement = ccp(-120.f, -350.f);
	CCPoint maxMovement = ccp(120.0f, 250.f);
	this->_velocity = ccpClamp(this->_velocity, minMovement, maxMovement); //4

	CCPoint stepVelocity = ccpMult(this->_velocity, delta); //計(jì)算下此速度下主角移動(dòng)了多少

	this->_desiredPosition = ccpAdd(this->getPosition(), stepVelocity); //當(dāng)前期望要去的位置=當(dāng)前位置+當(dāng)前速度
}

讓我們來詳細(xì)地看一下新增部分:


  1. 當(dāng)玩家觸摸前進(jìn)的時(shí)候和重力模擬類似,我們加了一個(gè)向前的推進(jìn)力800.f,處理方式與重力相同

  2. 第2步橫向速度乘以0.9是模擬摩擦力效果,考慮下當(dāng)有向前推力時(shí)玩家會(huì)向前移動(dòng),沒有了呢?是不是立即停下來?那樣看起來太生硬了,所以這里x軸速度每幀乘以0.9就起到了均勻減速的效果,就像摩擦力一樣

  3. 檢查一下玩家是否按了前,按前進(jìn)時(shí)速度上就要加上向前的推動(dòng)力,就起到了向前進(jìn)的效果了

  4. 這一步是調(diào)用了clamp是限定速度前后上下不要太大,給它一個(gè)最大值。之前的那句設(shè)定下落速度最大值可以去掉了。

好了運(yùn)行一下,我們可以看到我們的主角小考拉現(xiàn)在可以前進(jìn)了!

               cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2

讓考拉跳起來!
 跳躍是動(dòng)作游戲里最明顯的一個(gè)特征。我們希望角色跳的平滑逼真,現(xiàn)在讓我們來實(shí)現(xiàn)它。
 到player類的update方法里,在if(this->_forwardMarch)語句之前加上下面代碼:

CCPoint jumpForce = ccp(0.f, 310.f);

if(this->_mightAsWellJump && this->_onGround)
{
  this->_velocity = ccpAdd(this->_velocity, jumpForce);
}

   只要加一個(gè)向上的力,角色就可以跳起來了。
    如果你止步于此,你會(huì)得到一個(gè)老式的雅代利式跳躍,即每次跳躍都是相同的高度,每次你都施給玩家一個(gè)同樣的力,然后等著重力把你拉回地面來。
     這么做似乎沒什么不妥,如果你要求不高的話,但是仔細(xì)觀察一下各種流行的平臺(tái)游戲,如超級(jí)馬里奧,索尼克,洛克人,水上魂斗羅等,似乎玩家能夠通過按鍵的力度來控制跳躍的高度,達(dá)到更靈活的效果。那是怎么做到的呢?
其實(shí)實(shí)現(xiàn)這個(gè)效果也很簡(jiǎn)單,所謂玩家按鍵力度不過就是按鍵時(shí)間的長(zhǎng)久,按的時(shí)間長(zhǎng)也就是施加跳躍力的時(shí)間就長(zhǎng),跳的當(dāng)然高了,半路上如果玩家不給力了,當(dāng)然會(huì)跳到一半掉鏈子,不過玩家有種錯(cuò)覺就是想跳得高就使勁按著跳躍鍵,不想跳了松開鍵就是-_-

CCPoint jumpForce = ccp(0.f, 310.f);  //向上的跳躍力 玩家一直按著跳躍鍵時(shí)的跳躍力
	float jumpCutOff = 150.f;   //玩家沒有按住跳躍鍵時(shí)的跳躍力

	if(this->bMightAsWellJump && this->onGround)  //如果當(dāng)前玩家按了跳躍鍵并且在地上
	{
		this->_velocity = ccpAdd(this->_velocity, jumpForce); //跳躍就是加上一個(gè)向上的速度
	}
	else if (!this->bMightAsWellJump && this->_velocity.y > jumpCutOff) //玩家沒有按住跳躍鍵,并且向上的速度已經(jīng)超過了設(shè)定的值,就限定向上跳躍速度
	{
		this->_velocity = ccp(this->_velocity.x, jumpCutOff);
	}

注釋解釋的很清楚,就不多解釋了。
好了,build一下run下我們的游戲吧,看吧,我們的小考拉可以自由歡騰地上下翻飛了。
    cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2
跳是跳的很歡了,不過悲劇的是,它跳到最右邊就跳出屏幕,看不見了。
來修正這個(gè)問題,這個(gè)問題其實(shí)就是視點(diǎn)跟隨,在cocos2dx上有解決這一問題的標(biāo)準(zhǔn)算法,貼出代碼:

void GameLevelLayer::setViewpointCenter(cocos2d::CCPoint pos)
{
	CCSize winSize = CCDirector::sharedDirector()->getWinSize();

	//限定角色不能超過半屏
	int x = MAX(pos.x, winSize.width/2);
	int y = MAX(pos.y, winSize.height/2);

	//限定角色不能跑出屏幕
	x = MIN(x, (_map->getMapSize().width * _map->getTileSize().width) - winSize.width/2);
	y = MIN(y, (_map->getMapSize().height * _map->getTileSize().height) - winSize.height/2);

	CCPoint actualPosition = ccp(x, y);

	CCPoint centerOfView = ccp(winSize.width/2, winSize.height/2);
	CCPoint viewPoint = ccpSub(centerOfView, actualPosition);
	//設(shè)定一下地圖的位置
	_map->setPosition(viewPoint);
}

     方法參數(shù)就是玩家考拉當(dāng)前位置。這個(gè)方法可以不但能左右跟隨還能上下跟隨主角,非常好用。這個(gè)方法原理在很多博客都有提到,原理其實(shí)就是地圖在跟玩家做返方向運(yùn)動(dòng),大家可以查閱一下其他文章解釋它的原理,不過我在這里不想再多說了,不是一兩句能說清,我們只要會(huì)用這個(gè)方法就行了。
    我們要做的就是在GameLevelLayer類的update方法里調(diào)用就行了,可以放在update方法結(jié)尾之前,如下:
    this->setViewpointCenter(_player->getPosition());
    現(xiàn)在build再run一下,這回我們的小考拉再也不會(huì)跑出屏幕外了!



嘗一下受傷的滋味!
現(xiàn)在我們可以著手做游戲過關(guān)和GameOver的功能了。
地圖里有個(gè)hazards層,這個(gè)層里放置一些考拉碰上就會(huì)掛的object。其實(shí)本質(zhì)也上碰撞檢測(cè),看代碼:

void GameLevelLayer::handleHazardCollisions(Player* player)
{
	CCArray *tiles = this->getSurroundingTilesAtPosition(player->getPosition(), _hazards);
	CCDictionary* dic = NULL;
	CCObject* obj = NULL;
	float x=0.f; float y = 0.f;
	int gid = 0;
	CCARRAY_FOREACH(tiles, obj)
	{
		dic = (CCDictionary*)obj;
		x = dic->valueForKey("x")->floatValue();
		y = dic->valueForKey("y")->floatValue();
		CCRect tileRect = CCRectMake(x, y, _map->getTileSize().width, _map->getTileSize().height);
		CCRect pRect= player->collisionBoundingBox();

		gid = dic->valueForKey("gid")->intValue();
		if (gid && tileRect.intersectsRect(pRect))  //如果有釘刺并且玩家與它發(fā)生碰撞了,gameOver
		{
			this->gameOver(false);
		}
	}
}

代碼看上去是不是很熟悉呀,你沒記錯(cuò),其實(shí)就是從checkAndResolveCollisions方法里拷來的,只不過沒分那么多情況而且處理方式也簡(jiǎn)單了只是調(diào)用了gameOver方法,gameOver的布爾值參數(shù)為true表示游戲勝利,為false就是失敗
_hazards在GameLevelLayer類也是個(gè)CCTMXLayer* 地圖層類型的成員變量,在此類的init方法里初始化它:
_hazards = _map->layerNamed("hazards");
然后在update方法里調(diào)用這個(gè)方法,現(xiàn)在update方法是這個(gè)樣子:

void GameLevelLayer::update(float delta)
{
	_player->update(delta);

	this->handleHazardCollisions(_player);
	
	this->checkForAndResolveCollisions(_player);
	this->setViewpointCenter(_player->getPosition());
}

現(xiàn)在實(shí)現(xiàn)gameOver方法了,當(dāng)玩家跳到harads層里的刺上時(shí),我們會(huì)調(diào)用這個(gè)方法游戲結(jié)束,或者當(dāng)玩家走到終點(diǎn)時(shí),我也會(huì)調(diào)用這個(gè)方法,這個(gè)方法會(huì)顯示出一個(gè)restart按鈕,并在屏幕上打印出一些信息,告訴玩家你死了或者你贏了之類的-_-

void GameLevelLayer::gameOver(bool bWon)
{
	bGameOver = true;

	CCString* gameText;
	if (bWon)
	{
		gameText = CCString::create("You Won!");
	}
	else
		gameText = CCString::create("You have Died!");

	CCLabelTTF* diedLabel = CCLabelTTF::create(gameText->getCString(), "Marker Felt", 40);
	diedLabel->setPosition(ccp(240, 200));
	CCMoveBy *slideIn = CCMoveBy::create(1.f, ccp(0, 250));
	CCMenuItemImage* replay = CCMenuItemImage::create("replay.png","replay.png","replay.png", this, menu_selector(GameLevelLayer::restartGame));

	CCArray *menuItems = CCArray::create();
	menuItems->addObject(replay);
	CCMenu *menu = CCMenu::create();
	menu->addChild(replay);
	menu->setPosition(ccp(240, -100));

	this->addChild(menu);
	this->addChild(diedLabel);

	menu->runAction(slideIn);
}

方法開頭的變量bGameOver也是GameLevelLayer類新定義的一個(gè)成員變量,在init里要初始化為false,此變量表示游戲是否結(jié)束。這個(gè)變量作用是你在update方法里開頭判斷一下,如果bGameOver==true,則直接返回不要做任何事了。如下:

void GameLevelLayer::update(float delta)
{
	if(bGameOver)
		return;

	_player->update(delta);

	this->handleHazardCollisions(_player);
	
	this->checkForAndResolveCollisions(_player);
	this->setViewpointCenter(_player->getPosition());
}

現(xiàn)在你再編譯運(yùn)行,碰上釘板試試,會(huì)出現(xiàn)杯具性的結(jié)局...
cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2
不要嘗試的太多,小心動(dòng)物保護(hù)組織會(huì)找上你家門來(-_-這就是所謂的美式幽默嗎?)


修正一個(gè)致命BUG
還記得前面出現(xiàn)過當(dāng)考拉掉出地圖出現(xiàn)的事情嗎?在游戲地圖里有些坑我們本意是考拉掉下去游戲會(huì)結(jié)束,但事實(shí)上程序崩掉了。凡是帶有TILED地圖的都會(huì)出現(xiàn)這樣的問題,只要角色走出地圖邊界游戲就會(huì)崩掉,這個(gè)問題困擾了好多人,也包括我。其實(shí)解決這個(gè)BUG很簡(jiǎn)單,關(guān)鍵在tileGIDAt方法。
此方法你打開源碼實(shí)現(xiàn)就知道,它有個(gè)CCASSERT宏,判斷出地圖就會(huì)拋出異常。本來嗎此方法是取地圖上某處tile的gid,你都出地圖了還取什么當(dāng)然要拋異常啦。這里我們就在getSurroundingTilesAtPosition方法里加個(gè)判斷,注意當(dāng)然要在調(diào)用tileGIDAt方法之前,不讓考拉出地圖:

if (tilePos.y > (_map->getMapSize().height-1))
{
	this->gameOver(false);
	return NULL;
}

在checkForAndResolveCollisions方法里有調(diào)用getSurroundingTilesAtPosition方法,如果它返回個(gè)空數(shù)組可就不妙了,所以在checkForAndResolveCollisions也要改一下,在 CCArray* tiles = this->getSurroundingTilesAtPosition(player->getPosition(), _walls);一句之后,加上

if (bGameOver) //可能玩家掉坑里,就不處理了
	{
		return;
	}

編譯再運(yùn)行,把考拉掉進(jìn)坑里試試,發(fā)現(xiàn)程序不再DOWN了!
cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2


Winner!
   在最后,我們處理下考拉走到終點(diǎn)時(shí)顯示勝利的情況。
      這里我們簡(jiǎn)單點(diǎn),只是判斷下考拉向右走了多遠(yuǎn)就取勝

void GameLevelLayer::checkForWin()
{
	if (_player->getPositionX()>3130.0)
	{
		this->gameOver(true);
	}
}

真實(shí)游戲里我們通常在終點(diǎn)放置一個(gè)object,如門呀城堡小旗什么的,主角碰到就勝利了可進(jìn)入下一關(guān),有興趣的自己嘗試!
這個(gè)方法也要放到GameLevelLayer的update方法里,如下:

void GameLevelLayer::update(float delta)
{
	if(bGameOver)
		return;

	_player->update(delta);

	this->handleHazardCollisions(_player);
	this->checkForWin();
	this->checkForAndResolveCollisions(_player);
	this->setViewpointCenter(_player->getPosition());
}

運(yùn)行一下,走到終點(diǎn)試試:
cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2


添加音效
勝利了之后沒有音樂怎么行,一個(gè)無聲的世界可真是會(huì)令人絕望呀,來我們加些音效吧!
在GameLevelLayer.cpp和 player.cpp加上頭文件如下:
#include "SimpleAudioEngine.h"
using namespace CocosDenshion;
在GamelevelLayer的init方法里加上:
SimpleAudioEngine::shareEngine()->playBackgroundMusic("level1.mp3");
在player.cpp里的update方法里判斷玩家跳的地方加上音效:

if(this->bMightAsWellJump && this->onGround)  //如果當(dāng)前玩家按了跳躍鍵并且在地上
	{
		this->_velocity = ccpAdd(this->_velocity, jumpForce); //跳躍就是加上一個(gè)向上的速度
		SimpleAudioEngine::sharedEngine()->playEffect("jump.wav");
	}

在GameLevelLayer.cpp的gameOver方法里也加上音效:

void GameLevelLayer::gameOver(bool bWon)
{
	if (bGameOver)  //不要反復(fù)調(diào)用 
	{
		return;
	}
	bGameOver = true;
	SimpleAudioEngine::sharedEngine()->playEffect("hurt.wav");

編譯運(yùn)行,試一試你親手制作的帶有音樂感的游戲吧!
×××地址:下載

剩下我們還能做什么?
要做的還有什么,角色動(dòng)畫沒有吧,此外還有怪物,AI, 關(guān)卡,角色狀態(tài)機(jī)等等。這些都在IOS GAME三件套 Platformer Start Kit里,前面說過,本教程只是這個(gè)Start Kit的前奏曲,那真正的Platformer game是什么樣的呢?
cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2
學(xué)完此教程后,你可以學(xué)到

cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2
cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2
cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2
cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2
cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2
cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2
cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2

怎么樣?心動(dòng)了吧,涉于版權(quán)問題不便在此公開,有興趣的可到此看看: 橫版平臺(tái)游戲源碼
此外大名鼎鼎的三件套之一橫版格斗游戲 Beat 'Em Up Game Starter Kit 也是非常精彩哦!網(wǎng)上公開的源碼教程還不到里面的二十分之一-_-
cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2


cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2
cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2
cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2
cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2

cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2

cocos2d-x 如何制作一個(gè)類馬里奧的橫版平臺(tái)動(dòng)作游戲續(xù) 2

有興趣可到此看看: 橫版格斗游戲源碼












向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI