分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow
也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!
本文实践自 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 Denshion"选项;
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);
|
1
|
CREATE_FUNC(GameLayer);
|
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"目录下;
8.用Tiled工具打开pd_tilemap.tmx,就可以看到游戏的整个地图:
地图上有两个图层:Wall和Floor,即墙和地板。去掉每个图层前的打钩,可以查看层的组成。你会发现下数第四行是由两个图层一起组成的。每个tile都是32x32大小。可行走的地板tile位于下数三行。
9.打开GameLayer.h文件,添加如下代码:
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.编译运行,可以看到地图显示在屏幕上,如下图所示:
11.创建英雄。在大多数2D横版游戏中,角色有不同的动画代表不同类型的动作。我们需要知道什么时候播放哪个动画。这里采用状态机来解决这个问题。状态机就是某种通过切换状态来改变行为的东西。单一状态机在同一时间只能有一个状态,但可以从一种状态过渡到另一种状态。在这个游戏中,角色共有五种状态,空闲、行走、出拳、受伤、死亡,如下图所示:
为了有一个完整的状态流,每个状态应该有一个必要条件和结果。例如:行走状态不能突然转变到死亡状态,因为你的英雄在死亡前必须先受伤。
12.添加ActionSprite类,派生自CCSprite类,ActionSprite.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 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; } |
新建一个头文件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; |
1
|
cocos2d::CCSpriteBatchNode *_actors;
|
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(); |
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帧的速率进行播放。接下去,为英雄设置初始属性,包括精灵中心到边到底部的值。如下图所示:
英雄的每个精灵帧都在280x150像素大小的画布上创建,但实际上英雄精灵只占据这个空间的一部分。所以需要两个测量值,以便更好的设置精灵的位置。需要额外的空间,是因为每个动画精灵绘制的方式是不同的,而有些就需要更多的空间。
打开GameLayer.h文件,添加头文件声明:
1
|
#include
"Hero.h"
|
1
|
Hero *_hero;
|
1
|
_hero =
NULL;
|
1
|
this->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(); } |
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; } } |
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)); |
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; } } |
1
|
this->setTouchEnabled(
true);
|
1
2 3 4 |
void GameLayer::ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent)
{ _hero->attack(); } |
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 给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow |