• 周三. 4 月 22nd, 2026

物嫩软件资讯网

软件资讯来物嫩

如何制作一个横版格斗过关游戏 Cocos2d-x 2.0.4

admin@wunen

6 月 2, 2025

原文在

http://blog.csdn.net/akof1314/article/details/8549150

本文实践自 Allen Tan 的文章《

How To Make A Side-Scrolling Beat ‘Em Up Game Like Scott Pilgrim with Cocos2D – Part 1

》,文中使用Cocos2D,我在这里使用Cocos2D-x 2.0.4进行学习和移植。在这篇文章,将会学习到如何制作一个简单的横版格斗过关游戏。在这当中,学习如何跟踪动画状态、碰撞盒、添加方向键、添加简单敌人AI和更多其它的。

步骤如下:

1.新建Cocos2d-win32工程,工程名为”

PompaDroid

“,去除”

Box2D

“选项,勾选”

Simple Audio Engine in Cocos Den


shion

“选项;

2.添加游戏场景类


GameScene


,派生自


CCScene


类。添加


GameLayer


类和


HudLayer


类,派生自


CCLayer


类。删除


HelloWorldScene.h





HelloWorldScene.cpp


文件。

3.文件


GameScene.h


代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

#pragma

once


#include


“cocos2d.h”



#include


“GameLayer.h”



#include


“HudLayer.h”


class

GameScene :

public

cocos2d::CCScene

{


public

:

GameScene(

void

);

~GameScene(

void

);


virtual


bool

init();

CREATE_FUNC(GameScene);

CC_SYNTHESIZE(GameLayer*, _gameLayer, GameLayer);

CC_SYNTHESIZE(HudLayer*, _hudLayer, HudLayer);

};

文件


GameScene.cpp


代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

#include


“GameScene.h”



using


namespace

cocos2d;

GameScene::GameScene(

void

)

{

_gameLayer =

NULL

;

_hudLayer =

NULL

;

}

GameScene::~GameScene(

void

)

{

}


bool

GameScene::init()

{


bool

bRet =

false

;


do


{

CC_BREAK_IF(!CCScene::init());

_gameLayer = GameLayer::create();


this

->addChild(_gameLayer,

0

);

_hudLayer = HudLayer::create();


this

->addChild(_hudLayer,

1

);

bRet =

true

;

}

while

(

0

);


return

bRet;

}

4.


HudLayer


类增加一个方法:

1
CREATE_FUNC(HudLayer);




GameLayer


类增加一个方法:

1
CREATE_FUNC(GameLayer);

5.修改


AppDelegate.cpp


文件,代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

//#include “HelloWorldScene.h”



#include


“GameScene.h”


bool

AppDelegate::applicationDidFinishLaunching()

{


//…




// create a scene. it’s an autorelease object



//CCScene *pScene = HelloWorld::scene();


CCScene *pScene = GameScene::create();


//…


}

6.编译运行,此时只是空空的界面。

7.下载本游戏所需资源,将资源放置”


Resources


“目录下;

1

2

3

4

bool

init();


void

initTileMap();

cocos2d::CCTMXTiledMap *_tileMap;

打开


GameLayer.cpp


,在构造函数,添加如下代码:

1
_tileMap =

NULL

;

添加如下代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

bool

GameLayer::init()

{


bool

bRet =

false

;


do


{

CC_BREAK_IF(!CCLayer::init());


this

->initTileMap();

bRet =

true

;

}

while

(

0

);


return

bRet;

}


void

GameLayer::initTileMap()

{

_tileMap = CCTMXTiledMap::create(

“pd_tilemap.tmx”

);

CCObject *pObject =

NULL

;

CCARRAY_FOREACH(_tileMap->getChildren(), pObject)

{

CCTMXLayer *child = (CCTMXLayer*)pObject;

child->getTexture()->setAliasTexParameters();

}


this

->addChild(_tileMap, –

6

);

}

对所有图层进行


setAliasTexParameters


设置,该方法是关闭抗锯齿功能,这样就能保持像素风格。

10.编译运行,可以看到地图显示在屏幕上,如下图所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

#pragma

once


#include


“cocos2d.h”



#include


“Defines.h”


class

ActionSprite :

public

cocos2d::CCSprite

{


public

:

ActionSprite(

void

);

~ActionSprite(

void

);


//action methods



void

idle();


void

attack();


void

hurtWithDamage(

float

damage);


void

knockout();


void

walkWithDirection(cocos2d::CCPoint direction);


//scheduled methods



void

update(

float

dt);


//actions


CC_SYNTHESIZE_RETAIN(cocos2d::CCAction*, _idleAction, IdleAction);

CC_SYNTHESIZE_RETAIN(cocos2d::CCAction*, _attackAction, AttackAction);

CC_SYNTHESIZE_RETAIN(cocos2d::CCAction*, _walkAction, WalkAction);

CC_SYNTHESIZE_RETAIN(cocos2d::CCAction*, _hurtAction, HurtAction);

CC_SYNTHESIZE_RETAIN(cocos2d::CCAction*, _knockedOutAction, KnockedOutAction);


//states


CC_SYNTHESIZE(ActionState, _actionState, ActionState);


//attributes


CC_SYNTHESIZE(

float

, _walkSpeed, WalkSpeed);

CC_SYNTHESIZE(

float

, _hitPoints, HitPoints);

CC_SYNTHESIZE(

float

, _damage, Damage);


//movement


CC_SYNTHESIZE(cocos2d::CCPoint, _velocity, Velocity);

CC_SYNTHESIZE(cocos2d::CCPoint, _desiredPosition, DesiredPosition);


//measurements


CC_SYNTHESIZE(

float

, _centerToSides, CenterToSides);

CC_SYNTHESIZE(

float

, _centerToBottom, CenterToBottom);

};

打开


ActionSprite.cpp


文件,构造函数如下:

1

2

3

4

5

6

7

8
ActionSprite::ActionSprite(

void

)

{

_idleAction =

NULL

;

_attackAction =

NULL

;

_walkAction =

NULL

;

_hurtAction =

NULL

;

_knockedOutAction =

NULL

;

}

各个方法实现暂时为空。以上代码声明了基本变量和方法,可以分为以下几类:

  • Actions:这些是每种状态要执行的动作。这些动作是当角色切换状态时,执行精灵动画和其他触发的事件。

    States:保存精灵的当前动作/状态,使用ActionState类型,这个类型待会我们将会进行定义。

    Attributes:包含精灵行走速度值,受伤时减少生命点值,攻击伤害值。

    Movement:用于计算精灵如何沿着地图移动。

    Measurements:保存对精灵的实际图像有用的测量值。需要这些值,是因为你将要使用的这些精灵画布大小是远远大于内部包含的图像。

    Action methods:不直接调用动作,而是使用这些方法触发每种状态。

    Scheduled methods:任何事需要在一定的时间间隔进行运行,比如精灵位置和速度的更新,等等。

新建一个头文件


Defines.h


,代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

#pragma

once


#include


“cocos2d.h”


// 1 – convenience measurements



#define

SCREEN CCDirector::sharedDirector()->getWinSize()


#define

CENTER ccp(SCREEN.width /

2

, SCREEN.height /

2

)


#define

CURTIME

do

{                                                        \

timeval time;                                                           \

gettimeofday(&time,

NULL

);                                              \


unsigned


long

millisecs = (time.tv_sec *

1000

) + (time.tv_usec /

1000

); \


return

(

float

)millisecs;                                                \

}

while

(

0

)


// 2 – convenience functions



#define

random_range(low, high) (rand() % (high – low +

1

)) + low


#define

frandom (

float

)rand() / UINT64_C(0x100000000)


#define

frandom_range(low, high) ((high – low) * frandom) + low


// 3 – enumerations



typedef


enum

_ActionState {

kActionStateNone =

0

,

kActionStateIdle,

kActionStateAttack,

kActionStateWalk,

kActionStateHurt,

kActionStateKnockedOut

} ActionState;


// 4 – structures



typedef


struct

_BoundingBox {

cocos2d::CCRect actual;

cocos2d::CCRect original;

} BoundingBox;

简要说明下:

①.定义了一些便利的宏,如直接使用SCREEN获取屏幕大小;

②.定义了一些便利的函数,随机返回整型或者浮点型;

③.定义ActionState类型,这个是ActionSprite可能处在不同状态的类型枚举;

④.定义BoundingBox结构体,将用于碰撞检测。

打开


GameLayer.h


文件,添加如下代码:

1
cocos2d::CCSpriteBatchNode *_actors;

打开


GameLayer.cpp


文件,在


init


函数里面添加如下代码:

1

2

3

4
CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile(

“pd_sprites.plist”

);

_actors = CCSpriteBatchNode::create(

“pd_sprites.pvr.ccz”

);

_actors->getTexture()->setAliasTexParameters();


this

->addChild(_actors, –

5

);

加载精灵表单,创建一个CCSpriteBatchNode。这个精灵表单包含我们的所有精灵。它的z值高于CCTMXTiledMap对象,这样才能出现在地图前。

添加


Hero


类,派生自


ActionSprite


类,添加如下代码:

1

2
CREATE_FUNC(Hero);


bool

init();



Hero


类的


init


函数的实现如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

bool

Hero::init()

{


bool

bRet =

false

;


do


{

CC_BREAK_IF(!ActionSprite::initWithSpriteFrameName(

“hero_idle_00.png”

));


int

i;


//idle animation


CCArray *idleFrames = CCArray::createWithCapacity(

6

);


for

(i =

0

; i <

6

; i++)

{

CCSpriteFrame *frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(CCString::createWithFormat(

“hero_idle_%02d.png”

, i)->getCString());

idleFrames->addObject(frame);

}

CCAnimation *idleAnimation = CCAnimation::createWithSpriteFrames(idleFrames,

1

.

0

/

12

.

0

);


this

->setIdleAction(CCRepeatForever::create(CCAnimate::create(idleAnimation)));


this

->setCenterToBottom(

39

.

0

);


this

->setCenterToSides(

29

.

0

);


this

->setHitPoints(

100

.

0

);


this

->setDamage(

20

.

0

);


this

->setWalkSpeed(

80

.

0

);

bRet =

true

;

}

while

(

0

);


return

bRet;

}

我们用初始空闲精灵帧创建了英雄角色,配备了一个CCArray数组包含所有的属于空闲动画的精灵帧,然后创建一个CCAction动作播放来这个动画。以每秒12帧的速率进行播放。接下去,为英雄设置初始属性,包括精灵中心到边到底部的值。如下图所示:

1

#include


“Hero.h”



GameLayer


类添加如下代码:

1
Hero *_hero;

打开


GameLayer.cpp


文件,在构造函数添加如下代码:

1
_hero =

NULL

;




init


函数

this->addChild(_actors, -5);

后面添加如下代码:

1

this

->initHero();

添加


initHero


方法,代码如下:

1

2

3

4

5

6

7

8

void

GameLayer::initHero()

{

_hero = Hero::create();

_actors->addChild(_hero);

_hero->setPosition(ccp(_hero->getCenterToSides(),

80

));

_hero->setDesiredPosition(_hero->getPosition());

_hero->idle();

}

创建了一个英雄实例,添加到了精灵表单,并设置了设置。调用


idle


方法,让其处于空闲状态,运行空闲动画。返回到


ActionSprite.cpp


文件,实现


idle


方法,代码如下:

1

2

3

4

5

6

7

8

9

10

void

ActionSprite::idle()

{


if

(_actionState != kActionStateIdle)

{


this

->stopAllActions();


this

->runAction(_idleAction);

_actionState = kActionStateIdle;

_velocity = CCPointZero;

}

}

这个


idle


方法只有当ActionSprite不处于空闲状态才能调用。当它触发时,它会执行空闲动作,改变当前状态到


kActionStateIdle


,并且把速度置零。

13.编译运行,可以看到英雄处于空闲状态。如下图所示:

1

2

3

4

5

6

7

8

9

//attack animation


CCArray *attackFrames = CCArray::createWithCapacity(

3

);


for

(i =

0

; i <

3

; i++)

{

CCSpriteFrame *frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(CCString::createWithFormat(

“hero_attack_00_%02d.png”

, i)->getCString());

attackFrames->addObject(frame);

}

CCAnimation *attackAnimation = CCAnimation::createWithSpriteFrames(attackFrames,

1

.

0

/

24

.

0

);


this

->setAttackAction(CCSequence::create(CCAnimate::create(attackAnimation), CCCallFunc::create(

this

, callfunc_selector(Hero::idle)),

NULL

));

打开


ActionSprite.cpp


文件,实现


attack


方法,代码如下:

1

2

3

4

5

6

7

8

9

void

ActionSprite::attack()

{


if

(_actionState == kActionStateIdle || _actionState == kActionStateAttack || _actionState == kActionStateWalk)

{


this

->stopAllActions();


this

->runAction(_attackAction);

_actionState = kActionStateAttack;

}

}

英雄只有在空闲、攻击、行走状态才能进行出拳。确保英雄正在受伤时、或者死亡时不能进行攻击。为了触发


attack


方法,打开


GameLayer.cpp


文件,在


init


函数添加如下代码:

1

this

->setTouchEnabled(

true

);

重载


ccTouchesBegan


方法,代码如下:

1

2

3

4

void

GameLayer::ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent)

{

_hero->attack();

}

15.编译运行,点击屏幕进行出拳,如下图所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

#pragma

once


#include


“cocos2d.h”


class

SimpleDPad;


class

SimpleDPadDelegate

{


public

:


virtual


void

didChangeDirectionTo(SimpleDPad *simpleDPad, cocos2d::CCPoint direction) =

0

;


virtual


void

isHoldingDirection(SimpleDPad *simpleDPad, cocos2d::CCPoint direction) =

0

;


virtual


void

simpleDPadTouchEnded(SimpleDPad *simpleDPad) =

0

;

};


class

SimpleDPad :

public

cocos2d::CCSprite,

public

cocos2d::CCTargetedTouchDelegate

{


public

:

SimpleDPad(

void

);

~SimpleDPad(

void

);


static

SimpleDPad* dPadWithFile(cocos2d::CCString *fileName,

float

radius);


bool

initWithFile(cocos2d::CCString *filename,

float

radius);


void

onEnterTransitionDidFinish();


void

onExit();


void

update(

float

dt);


virtual


bool

ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent);


virtual


void

ccTouchMoved(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent);


virtual


void

ccTouchEnded(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent);


void

updateDirectionForTouchLocation(cocos2d::CCPoint location);

CC_SYNTHESIZE(SimpleDPadDelegate*, _delegate, Delegate);

CC_SYNTHESIZE(

bool

, _isHeld, IsHeld);


protected

:


float

_radius;

cocos2d::CCPoint _direction;

};

对以上的一些声明,解释如下:

  • radius:圆形方向键的半径。

    direction:当前所按下的方向。这是一个矢量,(-1.0, -1.0)是左下方向,(1.0, 1.0)是右上方向。

    delegate:方向键的委托,后续进行介绍。

    isHeld:布尔值表示玩家触摸着方向键。

对于


SimpleDPad


类,使用了委托模式。意味着一个委托类(并非SimpleDPad),将会处理由被委托类(SimpleDPad)启动的任务。在某些你指定的点上,主要是当涉及到处理任何游戏相关的东西,SimpleDPad将会将职责传递给委托类。这使得SimpleDPad无需知道任何游戏逻辑,从而允许你在开发任何其他游戏时,可以进行重用。如下图所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

#include


“SimpleDPad.h”



using


namespace

cocos2d;

SimpleDPad::SimpleDPad(

void

)

{

_delegate =

NULL

;

}

SimpleDPad::~SimpleDPad(

void

)

{

}

SimpleDPad* SimpleDPad::dPadWithFile(CCString *fileName,

float

radius)

{

SimpleDPad *pRet =

new

SimpleDPad();


if

(pRet && pRet->initWithFile(fileName, radius))

{


return

pRet;

}


else


{


delete

pRet;

pRet =

NULL

;


return


NULL

;

}

}


bool

SimpleDPad::initWithFile(CCString *filename,

float

radius)

{


bool

bRet =

false

;


do


{

CC_BREAK_IF(!CCSprite::initWithFile(filename->getCString()));

_radius = radius;

_direction = CCPointZero;

_isHeld =

false

;


this

->scheduleUpdate();

bRet =

true

;

}

while

(

0

);


return

bRet;

}


void

SimpleDPad::onEnterTransitionDidFinish()

{

CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(

this

,

1

,

true

);

}


void

SimpleDPad::onExit()

{

CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(

this

);

}


void

SimpleDPad::update(

float

dt)

{


if

(_isHeld)

{

_delegate->isHoldingDirection(

this

, _direction);

}

}


bool

SimpleDPad::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)

{

CCPoint location = pTouch->getLocation();


float

distanceSQ = ccpDistanceSQ(location,

this

->getPosition());


if

(distanceSQ <= _radius * _radius)

{


this

->updateDirectionForTouchLocation(location);

_isHeld =

true

;


return


true

;

}


return


false

;

}


void

SimpleDPad::ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent)

{

CCPoint location = pTouch->getLocation();


this

->updateDirectionForTouchLocation(location);

}


void

SimpleDPad::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent)

{

_direction = CCPointZero;

_isHeld =

false

;

_delegate->simpleDPadTouchEnded(

this

);

}


void

SimpleDPad::updateDirectionForTouchLocation(CCPoint location)

{


float

radians = ccpToAngle(ccpSub(location,

this

->getPosition()));


float

degrees = –

1

* CC_RADIANS_TO_DEGREES(radians);


if

(degrees <=

22

.

5

&& degrees >= –

22

.

5

)

{


//right


_direction = ccp(

1

.

0

,

0

.

0

);

}


else


if

(degrees >

22

.

5

&& degrees <

67

.

5

)

{


//bottomright


_direction = ccp(

1

.

0

, –

1

.

0

);

}


else


if

(degrees >=

67

.

5

&& degrees <=

112

.

5

)

{


//bottom


_direction = ccp(

0

.

0

, –

1

.

0

);

}


else


if

(degrees >

112

.

5

&& degrees <

157

.

5

)

{


//bottomleft


_direction = ccp(-

1

.

0

, –

1

.

0

);

}


else


if

(degrees >=

157

.

5

|| degrees <= –

157

.

5

)

{


//left


_direction = ccp(-

1

.

0

,

0

.

0

);

}


else


if

(degrees < –

22

.

5

&& degrees > –

67

.

5

)

{


//topright


_direction = ccp(

1

.

0

,

1

.

0

);

}


else


if

(degrees <= –

67

.

5

&& degrees >= –

112

.

5

)

{


//top


_direction = ccp(

0

.

0

,

1

.

0

);

}


else


if

(degrees < –

112

.

5

&& degrees > –

157

.

5

)

{


//topleft


_direction = ccp(-

1

.

0

,

1

.

0

);

}

_delegate->didChangeDirectionTo(

this

, _direction);

}

以上方法中,


onEnterTransitionDidFinish


注册SimpleDPad委托类,


onExit


移除SimpleDPad委托类,


update


方法是当方向键被触摸时,传递方向值到委托类。


ccTouchBegan


方法检测触摸位置是否在方向键圆内,如果是,则将isHeld置为true,并更新方向值,返回true以拥有触摸事件优先权。


ccTouchMoved


当触摸点移动时,更新方向值。


ccTouchEnded


将isHeld置为false,重置方向,并通知委托触摸结束。


updateDirectionForTouchLocation


方法计算触摸点到方向键中心距离值,转换成角度,得到正确的方向值,然后传递值到委托。

打开


HudLayer.h


文件,添加头文件声明:

1

#include


“SimpleDPad.h”

添加如下代码:

1

2

bool

init();

CC_SYNTHESIZE(SimpleDPad*, _dPad, DPad);

打开


HudLayer.cpp


文件,添加如下代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22
HudLayer::HudLayer(

void

)

{

_dPad =

NULL

;

}


bool

HudLayer::init()

{


bool

bRet =

false

;


do


{

CC_BREAK_IF(!CCLayer::init());

_dPad = SimpleDPad::dPadWithFile(CCString::create(

“pd_dpad.png”

),

64

);

_dPad->setPosition(ccp(

64

.

0

,

64

.

0

));

_dPad->setOpacity(

100

);


this

->addChild(_dPad);

bRet =

true

;

}

while

(

0

);


return

bRet;

}

以上代码实例化


SimpleDPad


,并且添加到


HudLayer


上。现在GameScene同时控制GameLayer和HudLayer,但有时候想直接通过HudLayer访问GameLayer。打开


GameLayer.h


文件,添加头文件声明:

1

2

#include


“SimpleDPad.h”



#include


“HudLayer.h”




GameLayer


类声明修改成如下:

1

class

GameLayer :

public

cocos2d::CCLayer,

public

SimpleDPadDelegate

并添加以下声明:

1

2

3

4

5

virtual


void

didChangeDirectionTo(SimpleDPad *simpleDPad, cocos2d::CCPoint direction);


virtual


void

isHoldingDirection(SimpleDPad *simpleDPad, cocos2d::CCPoint direction);


virtual


void

simpleDPadTouchEnded(SimpleDPad *simpleDPad);

CC_SYNTHESIZE(HudLayer*, _hud, Hud);

以上方法的实现暂时为空。这样我们就在


GameLayer


中添加了


HudLayer


的引用,同时还让


GameLayer


遵循


SimpleDPad


所创建的协议。打开


GameScene.cpp


文件,在


init


函数

this->addChild(_hudLayer, 1);

后面,添加如下代码:

1

2
_hudLayer->getDPad()->setDelegate(_gameLayer);

_gameLayer->setHud(_hudLayer);

17.编译运行,可以看到左下角的虚拟方向键,如下图所示:

别试着压下方向键,英雄不会有任何反应,因为还未实现协议方法,这在

第二部分

将完成。



参考资料:



1.How To Make A Side-Scrolling Beat ‘Em Up Game Like Scott Pilgrim with Cocos2D – Part 1

http://www.raywenderlich.com/24155/how-to-make-a-side-scrolling


2.如何使用cocos2d制作类似Scott Pilgrim的2D横版格斗过关游戏part1(翻译)

http://blog.sina.com.cn/s/blog_4b55f6860101a9b7.html


3.如何使用Cocos2d-x做一DNF类的游戏-part1

http://blog.csdn.net/jyzgo/article/details/8471306

非常感谢以上资料,本例子源代码附加资源


下载地址




http://download.csdn.net/detail/akof1314/5038013


如文章存在错误之处,欢迎指出,以便改正。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注