Cocos2d-html5《王者之剑》实现 (1)
生活随笔
收集整理的這篇文章主要介紹了
Cocos2d-html5《王者之剑》实现 (1)
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
前面,《 手把手,快速搭建 Cocos2d-HTML5 開(kāi)發(fā)調(diào)試環(huán)境》 與 《 如何自定義 Cocos2d-HTML5 Loading 界面》 兩篇文章,幫助了我們搭建了其開(kāi)發(fā)環(huán)境,并了解了 H5 ( 以下對(duì) Cocos2d-HTML5 簡(jiǎn)稱 H5,顯然,它不是 * 流感,沒(méi)那么大破壞力) 的大致加載流程,此文開(kāi)始就要使用 H5 來(lái)制作一個(gè)簡(jiǎn)單的動(dòng)作游戲 王者之劍,完成效果如下所示(所有的源碼你可以在 【 這里】查看),顯示效果可以在 【 這里】查看(請(qǐng)耐心等待加載):
這是一個(gè)簡(jiǎn)單的游戲打斗場(chǎng)景,一個(gè)英雄,一個(gè)怪物,可以控制英雄來(lái)回走動(dòng)并且攻擊,怪物實(shí)現(xiàn)簡(jiǎn)單 AI 并且自動(dòng)攻擊,有著不同的血量槽,控制系統(tǒng),可以使用觸摸,但為了操作的體驗(yàn),同樣實(shí)現(xiàn)了 鍵盤(pán)映射 ,可以使用 W、A、S、D 來(lái)控制人物的走動(dòng),J、U、I 實(shí)現(xiàn)一個(gè)普通攻擊和兩個(gè)特效攻擊。
項(xiàng)目組織
為了使項(xiàng)目的代碼結(jié)構(gòu)清晰,前期規(guī)劃好功能是必須的,先從整體看一下,項(xiàng)目的組織結(jié)構(gòu),然后會(huì)對(duì)其中內(nèi)部實(shí)現(xiàn)做些必要的解說(shuō):
如上所示, Arthur 為游戲項(xiàng)目的主目錄,與它同級(jí)的目錄,是 H5 的庫(kù)目錄,當(dāng)然截圖中,為了發(fā)布,刪除了一些不必要的文件,在 Arthut 目錄下,包含一般項(xiàng)目都包含的結(jié)構(gòu)組織。
index.html :?這是游戲的展示界面,其中包含 “gameCanvas” ,作為游戲繪制的所在,它引用加載了 cocos2d.js
cocos2d.js :?項(xiàng)目初始化在這里進(jìn)行,并完成系統(tǒng)庫(kù)和項(xiàng)目源碼的 js 加載,最后將控制權(quán)交給 main.js 文件
main.js :?當(dāng) H5 庫(kù)加載完畢,執(zhí)行內(nèi)中代碼,完成項(xiàng)目資源加載,并運(yùn)行第一個(gè)場(chǎng)景
src :?此目錄包含了游戲中編寫(xiě)的 js 源代碼文件
res :?游戲所需的資源,如圖片,字體等
在這個(gè)游戲中相對(duì)復(fù)雜一點(diǎn)的就是控制系統(tǒng)了, HudLayer 中添加實(shí)現(xiàn)了 ActionButton 普通攻擊按鈕, Joypad 可觸摸 360 度 搖桿功能,和 KeyMap 游戲控制鍵盤(pán)映射方案。Characters 實(shí)現(xiàn)了人物和怪物的功能,各種動(dòng)作控制。Loading 替換了 H5 的默認(rèn)加載界面,使用了一個(gè)進(jìn)度條顯示加載進(jìn)度。GameLayer 作為游戲的主場(chǎng)景,各種游戲的流程控制在這里進(jìn)行。
360 度 可觸摸搖桿實(shí)現(xiàn)
這里的搖桿,默認(rèn)是為了觸摸實(shí)現(xiàn),之后添加的鍵盤(pán)映射,只是為了讓操作更為方便而已(在 PC 瀏覽器中),觸摸不同于搖桿的所在,就是這里的搖桿可以是 360 度以內(nèi)的任意角度,也就是可以控制任意以任意方向移動(dòng),這是鍵盤(pán)所不具備的,上下左右四個(gè)鍵,再加上每?jī)蓚€(gè)方向的組合也就八個(gè)方向。
var?Joypad?=?cc.Layer.extend({? _winSize:?null,? _pCenter:?null,? _pControlSprite:?null,? _pDefaultPoint:?null,? ?? _pDefaultRotation:?null,? _pRotation:?null,? ?? _pDelegate:?null,? _pKeyDown:?false,? ctor:function(){? ?this._super();? ?? _winSize?=?cc.Director.getInstance().getWinSize();? _pCenter?=?cc.p(_winSize.width?/?2,?_winSize.height?/?2);? ?? ?},? init:function(){? ?var?bRet?=?false;? ?if?(this._super()){? cc.log("Joypad?init?..");? ?//?控制桿所在位置? ?this._pDefaultPoint?=?cc.p(110,?110);? ?//?默認(rèn)旋轉(zhuǎn)角度,以使開(kāi)口正對(duì)右側(cè)? ?this._pDefaultRotation?=?26;? ?//?實(shí)際旋轉(zhuǎn)角度? ?this._pRotation?=?0;? ?? ?this.setPosition(this._pDefaultPoint);? ?? ?this.addChild(cc.Sprite.create(s_Joypad1));? ?this.addChild(cc.Sprite.create(s_Joypad2));? ?this._pControlSprite?=?cc.Sprite.create(s_Joypad3);? ?this.addChild(this._pControlSprite);? ?this.addChild(cc.Sprite.create(s_Joypad4));? ?? ?this.updateRotation();? ?? bRet?=?true;? ?}? ?return?bRet;? ?},? keyStart:function(degrees){? ?if?(this._pDelegate)? ?this._pDelegate.actionJoypadStart(this._pRotation);? ?},? keyUpdate:function(degrees){? ?this._pRotation?=?degrees;? ?this.updateRotation();? ?if?(this._pDelegate)? ?this._pDelegate.actionJoypadUpdate(this._pRotation);? ?},? keyEnded:function(degrees){? ?if?(this._pDelegate)? ?this._pDelegate.actionJoypadEnded(this._pRotation);? ?},? onEnter:function(){? ?this._super();? cc.Director.getInstance().getTouchDispatcher().addTargetedDelegate(this,?0,?true);? ?},? onTouchBegan:function?(touch,?event){? ?//?點(diǎn)擊點(diǎn)的范圍判斷? ?var?curPoint?=?touch.getLocation();? ?if?(curPoint.x?>?_winSize.width?/?2?||?curPoint.y?>?_winSize.height?/?2?){? ?return?false;? ?}? ?? ?//?var?sp?=?cc.pSub(this._pDefaultPoint,?curPoint);? ?//?var?angle?=?cc.pToAngle(sp);? ?? ?this.updateTouchRotation(touch,?event);? ?this.updateRotation();? ?if(this._pDelegate)? ?this._pDelegate.actionJoypadStart(this._pRotation);? ?else? cc.log('_pDelegate?is?null?...?');? ?? ?//?cc.log("Joypad?touch?...");? ?return?true;? ?},? onTouchMoved:function?(touch,?event){? ?this.updateTouchRotation(touch,?event);? ?this.updateRotation();? ?? ?if?(this._pDelegate)? ?this._pDelegate.actionJoypadUpdate(this._pRotation);? ?else? cc.log('_pDelegate?is?null?...?');? ?? ?//?var?a?=?cc.pAngleSigned(?curPoint,?this._pDefaultPoint);? ?//?cc.log("Joypad?touch?mvove?..."?+?rotation)?;? ?},? onTouchEnded:function?(touch,?event){? ?this.updateTouchRotation(touch,?event);? ?this.updateRotation();? ?if?(this._pDelegate)? ?this._pDelegate.actionJoypadEnded(this._pRotation);? ?else? cc.log('_pDelegate?is?null?...?');? ?},? updateTouchRotation:function(touch,?event){? ?var?curPoint?=?touch.getLocation();? ?var?sp?=?cc.pSub(curPoint,?this._pDefaultPoint);? ?var?angle?=?cc.pToAngle(sp)?;//?*?-57.29577951;? ?var?rotation?=?angle?*?-57.29577951;? rotation?=?rotation?<?0???360?+?rotation:?rotation;? ?this._pRotation?=?rotation;? ?},? updateRotation:function(){? ?this._pControlSprite.setRotation(this._pDefaultRotation?+?this._pRotation);? ?},? setDelegate:function(dg){? ?this._pDelegate?=?dg;? ?}? });?
在初始化方法中,加載了搖桿資源文件,它分解成幾個(gè)組成部分,以便于很好的控制,并且保存了可旋轉(zhuǎn)元素精靈的引用this._pControlSprite,以便于隨時(shí)控制它的旋轉(zhuǎn)角度,如圖中 Joypad3.png 圖片。 以觸摸的動(dòng)作來(lái)控制動(dòng)作的執(zhí)行,Joypad 中包含了一個(gè)名為 _pDelegate 的屬性,它作為控制搖桿的代理,以通知其它 (如 人物),搖桿現(xiàn)在變動(dòng)了,分別在 onTouchBegan 中調(diào)用, this._pDelegate.actionJoypadStart(this._pRotation);,onTouchMoved?中調(diào)用 this._pDelegate.actionJoypadUpdate(this._pRotation);?和在 onTouchEnded 中調(diào)用 this._pDelegate.actionJoypadEnded(this._pRotation);。 只需要在傳入的 _pDelegate 中實(shí)現(xiàn)此三種函數(shù),就可以通過(guò)搖桿來(lái)控制其操作了,H5 使用 javascript 相比如 C++ 倒也省去了接口定義等繁雜的操作。可以看見(jiàn),在三個(gè)函數(shù)調(diào)用中,所傳入的參數(shù)為觸摸的角度,在觸摸是通知控制顯示搖桿中 “羅盤(pán)” 的旋轉(zhuǎn)。Joypad 對(duì)內(nèi)通過(guò)觸摸控制顯示,對(duì)外通過(guò)觸摸調(diào)用代理,以達(dá)到顯示和控制相一致的目的。通過(guò)觸摸的點(diǎn)相對(duì)搖桿原點(diǎn)的位置關(guān)系,很容計(jì)算出其角度。 由于這里的搖桿設(shè)計(jì)是 360 度任意角度,所以在 delegate 中傳出一個(gè)參數(shù),以標(biāo)示角度關(guān)系,如果并不需要那么復(fù)雜的控制,如前文所言,只需固定八個(gè)方向的控制,那么這里傳出的參數(shù)可以使用 枚舉 類型,代表八個(gè)不同的方向,也會(huì)使得游戲邏輯變得稍微簡(jiǎn)單。 最后可以為 Joypad 層封裝一個(gè)簡(jiǎn)單好用的調(diào)用方式:Joypad.create?=?function(){? ?var?joypad?=?new?Joypad();? ?if?(joypad?&&?joypad.init()){? ?return?joypad;? ?}? ?return?null;? };?
攻擊 與 特效攻擊
在這個(gè)游戲中,有一個(gè)普通攻擊和兩個(gè)特效攻擊,這兩個(gè)不同,但很顯然,他們都是攻擊,卻又相同,先看看他們的共同點(diǎn):
//?ActionButton.js? ?? var?ActionButton?=?cc.Node.extend({? _sprite:?null,? _rect:?null,? _delegate:?null,? _attackType:?null,? ?? _childObj:?null,? rect:function(){? ?var?size?=?this._sprite.getContentSize();? ?return?cc.rect(-size.width?/?2,?-size.height?/?2,?size.width,?size.height);? ?},? setChindObj:function(obj){? ?this._childObj?=?obj;? ?},? init:function(image){? ?this._super();? ?? ?this._sprite?=?cc.Sprite.create(image);? ?this.addChild(this._sprite);? ?return?true;? ?},? setDelegate:function(delegate){? ?this._delegate?=?delegate;? ?},? setAttackType:function(at){? ?this._attackType?=?at;? ?},? getAttackType:function(){? ?return?this._attackType;? ?},? onEnter:function(){? cc.Director.getInstance().getTouchDispatcher().addTargetedDelegate(this,?0,?false);? ?this._super();? ?},? onExit:function(){? cc.Director.getInstance().getTouchDispatcher().removeDelegate(this);? ?this._super();? ?},? containsTouchLocation:function(touch){? ?return?cc.rectContainsPoint(this.rect(),?this.convertTouchToNodeSpace(touch));? ?},? onTouchBegan:function(touch,?event){? ?//?區(qū)域判斷? ?if?(!this.containsTouchLocation(touch))? ?return?false;? ?this.click();? ?//?播放點(diǎn)擊動(dòng)畫(huà)? ?return?true;? ?},? click:function(){? ?if(this._delegate?&&?this._childObj.isCanClick()){? ?this._delegate.attackButtonClick(this.getAttackType());? ?this.beganAnimation();? ?}? ?},? onTouchEnded:function(touch,?event){? ?this.endedAnimation();? ?},? beganAnimation:function(){? ?},? endedAnimation:function(){? ?},? isCanClick:function(){? ?return?true;? ?}? });?
定義了一個(gè) ActionButton 攻擊按鈕類型,它實(shí)現(xiàn)了 onTouchBegan 作為按鈕點(diǎn)擊的觸發(fā)場(chǎng)所,觸發(fā)了 click 事件,再由 click 處理調(diào)用代理的事件,傳出一個(gè)參數(shù),以標(biāo)示攻擊的類型 AttackType,在判斷點(diǎn)擊的時(shí)候還需要檢測(cè)點(diǎn)擊區(qū)域是否在按鈕的可點(diǎn)擊范圍之內(nèi),當(dāng)然再觸發(fā)攻擊動(dòng)作之時(shí),按鈕本身也實(shí)現(xiàn)了一些動(dòng)畫(huà)特效,如點(diǎn)擊效果,技能冷卻效果,它由beganAnimation 方法實(shí)現(xiàn)。
但我們看見(jiàn)在 ActionButton 并沒(méi)有實(shí)現(xiàn) beganAnimation,在方法里面并沒(méi)有實(shí)現(xiàn)任何代碼,這因?yàn)?ActionButton 只是作為 攻擊按鈕 的抽象,它只定義了攻擊按鈕具體由那些功能,能做哪些事情,如可以播放點(diǎn)擊時(shí)的動(dòng)畫(huà),但具體的動(dòng)畫(huà)內(nèi)容,需要根據(jù)具體的攻擊按鈕有著不同的實(shí)現(xiàn)。
var?AttackButton?=?ActionButton.extend({? _pt:?null,? _ac:?null,? ?? _defaultScale:?0.35,? _maxScale:?0.5,? ?? _inAction:?null,? _outAction:?null,? ?? _timestamp:?null,? ctor:function(){? ?this._super();? ?this._pt=?cc.Sprite.create(s_AttackO);? ?this._pt.setScale(this._maxScale);? ?this.setChindObj(this);? ?? ?//?this.addChild(this._pt);? ?? ?var?aScale?=?cc.ScaleTo.create(0.1,?this._defaultScale);? ?var?aFadein?=?cc.FadeIn.create(0.1);? ?this._inAction?=?cc.Spawn.create(aScale,?aFadein);? ?? ?var?oScale?=?cc.ScaleTo.create(.2,?this._maxScale);? ?var?oFade?=?cc.FadeOut.create(0.2);? ?this._outAction?=?cc.Spawn.create(oScale,?oFade);? ?},? beganAnimation:function(){? ?var?timestamp?=?(new?Date()).valueOf();? ?this._timestamp?=?timestamp;? ?? ?this.removeChild(this._pt);? ?this.addChild(this._pt);? ?this._pt.runAction(this._inAction);? ?? ?},? endedAnimation:function(){? ?this._pt.stopAllActions();? ?this._pt.runAction(this._outAction);? ?},? clickUp:function(){? ?this.endedAnimation();? ?},? isCanClick:function(){? ?var?timestamp?=?(new?Date()).valueOf();? ?return?timestamp?-?this._timestamp?>?600;? ?}? });?
普通攻擊按鈕的效果,初始化設(shè)置圖片素材,播放動(dòng)畫(huà)為一個(gè)光圈放大縮小顯示,它 繼承 自 ActionButton ,同樣實(shí)現(xiàn)了 beganAnimation 方法。另外一種是特效攻擊的實(shí)現(xiàn):
var?AttackEffect?=?ActionButton.extend({? _pt:?null,? _ac:?null,? _isCanClick:?true,? ctor:function(){? ?this._super();? ?var?h?=?cc.Sprite.create(s_AttackFreeze);? ?this._pt?=?cc.ProgressTimer.create(h);? ?this._pt.setType(cc.PROGRESS_TIMER_TYPE_RADIAL);? ?this._pt.setReverseDirection(true);? ?this._pt.setScale(0.43);? ?? ?var?to?=?cc.ProgressTo.create(0,?99.999);? ?var?to1?=?cc.ProgressTo.create(2,?0);? ?var?ac2?=?cc.CallFunc.create(this.callBack,?this);? ?this._ac?=?cc.Sequence.create(to,?to1,?ac2);? ?this.setChindObj(this);? ?},? beganAnimation:function(){? ?this.removeChild(this._pt);? ?this.addChild(this._pt);? ?this._pt.runAction(this._ac);? ?this._isCanClick?=?false;? ?},? endedAnimation:function(){? ?},? callBack:function(){? ?//?cc.log("call?back");? ?this._isCanClick?=?true;? ?},? isCanClick:function(){? ?return?this._isCanClick;? ?}? });?
特效攻擊有個(gè)冷卻效果,不能在一定時(shí)間范圍內(nèi)連續(xù)攻擊,使用一個(gè) 旋轉(zhuǎn)的 Progress 來(lái)達(dá)到這樣的效果。
在初始化方法中,加載了搖桿資源文件,它分解成幾個(gè)組成部分,以便于很好的控制,并且保存了可旋轉(zhuǎn)元素精靈的引用this._pControlSprite,以便于隨時(shí)控制它的旋轉(zhuǎn)角度,如圖中 Joypad3.png 圖片。 以觸摸的動(dòng)作來(lái)控制動(dòng)作的執(zhí)行,Joypad 中包含了一個(gè)名為 _pDelegate 的屬性,它作為控制搖桿的代理,以通知其它 (如 人物),搖桿現(xiàn)在變動(dòng)了,分別在 onTouchBegan 中調(diào)用, this._pDelegate.actionJoypadStart(this._pRotation);,onTouchMoved?中調(diào)用 this._pDelegate.actionJoypadUpdate(this._pRotation);?和在 onTouchEnded 中調(diào)用 this._pDelegate.actionJoypadEnded(this._pRotation);。 只需要在傳入的 _pDelegate 中實(shí)現(xiàn)此三種函數(shù),就可以通過(guò)搖桿來(lái)控制其操作了,H5 使用 javascript 相比如 C++ 倒也省去了接口定義等繁雜的操作。可以看見(jiàn),在三個(gè)函數(shù)調(diào)用中,所傳入的參數(shù)為觸摸的角度,在觸摸是通知控制顯示搖桿中 “羅盤(pán)” 的旋轉(zhuǎn)。Joypad 對(duì)內(nèi)通過(guò)觸摸控制顯示,對(duì)外通過(guò)觸摸調(diào)用代理,以達(dá)到顯示和控制相一致的目的。通過(guò)觸摸的點(diǎn)相對(duì)搖桿原點(diǎn)的位置關(guān)系,很容計(jì)算出其角度。 由于這里的搖桿設(shè)計(jì)是 360 度任意角度,所以在 delegate 中傳出一個(gè)參數(shù),以標(biāo)示角度關(guān)系,如果并不需要那么復(fù)雜的控制,如前文所言,只需固定八個(gè)方向的控制,那么這里傳出的參數(shù)可以使用 枚舉 類型,代表八個(gè)不同的方向,也會(huì)使得游戲邏輯變得稍微簡(jiǎn)單。 最后可以為 Joypad 層封裝一個(gè)簡(jiǎn)單好用的調(diào)用方式:
總結(jié)
以上是生活随笔為你收集整理的Cocos2d-html5《王者之剑》实现 (1)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python批量拼接两个文件夹相同名字的
- 下一篇: 通过网址自动网页截图(Selenium实