cocos2d-x格斗游戏教程(五)
来源:互联网 发布:天音淘宝破解版 编辑:程序博客网 时间:2024/05/20 15:12
8.简单机器人AI的实现。为了使机器人能够移动,并且能够使用我们为它们所创建的动作,就需要开发一个简单的AI(人工智能)系统。这个AI系统基于决策机制。在特定的时间间隔里,我们给每个机器人一个机会来决定接下来该做什么。它们需要知道的第一件事情就是何时做出选择。打开Robot.h文件,添加如下代码:
1
打开Robot.cpp文件,在init函数后面,添加如下代码:
1
这个属性保存下一次机器人可以作出决定的时间。打开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
34
35
36
37
38
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
1
在updatePositions函数后面,添加如下代码:
1
2
3
4
5
6
7
8
9
10
确保每次游戏循环时,机器人AI方法都被调用。遍历每个机器人,并让它们朝着期望的位置进行移动。
9.编译运行,将会看到沿着走廊过来的机器人。效果如下图所示:
10.为游戏添加重新开始的按钮。打开GameLayer.cpp文件,添加头文件引用:
1
添加如下方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
1
2
3
4
在第二个end game checker here注释后面,添加如下代码:
1
2
3
4
这些语句都是检测游戏结束的条件。第一个检测英雄被机器人攻击后,是否还存活着。如果英雄死亡了,那么游戏就结束了。第二个检测是否所有的机器人都死亡了。如果都死亡了,那么游戏也结束了。另外,在endGame方法里,可以看到游戏结束菜单的tag值为5。因为检测是在循环里面,需要确保游戏结束菜单之前没被创建过。否则的话,将会一直创建游戏结束菜单。
11.编译运行,可以看到游戏结束时的样子,如下图所示:
1
在init函数里,CC_BREAK_IF(!CCLayer::init());后面添加如下代码:
1
2
3
4
5
6
7
打开ActionSprite.cpp文件,添加头文件引用:
1
在hurtWithDamage函数,第一个条件语句里添加如下代码:
1
2
打开ActionSprite.h文件,将knockout方法声明修改如下:
1
打开Hero.cpp文件,添加头文件引用:
1
添加如下方法:
1
2
3
4
5
打开Robot.cpp文件,添加头文件引用:
1
添加如下方法:
1
2
3
4
5
13.编译运行,现在游戏将有配乐,效果图:
CC_SYNTHESIZE(float, _nextDecisionTime, NextDecisionTime);
_nextDecisionTime = 0;
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
#pragma once
#include"cocos2d.h"
// 1 - convenience measurements
#define SCREEN CCDirector::sharedDirector()->getWinSize()
#define CENTER ccp(SCREEN.width / 2, SCREEN.height / 2)
#define CURTIME GetCurTime()
// 2 - convenience functions
#ifndef UINT64_C
#define UINT64_C(val) val##ui64
#endif
#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;
inline float GetCurTime(){
timeval time;
gettimeofday(&time, NULL);
unsigned long millisecs = (time.tv_sec * 1000) + (time.tv_usec / 1000);
return (float)millisecs;
};
#include"cocos2d.h"
// 1 - convenience measurements
#define SCREEN CCDirector::sharedDirector()->getWinSize()
#define CENTER ccp(SCREEN.width / 2, SCREEN.height / 2)
#define CURTIME GetCurTime()
// 2 - convenience functions
#ifndef UINT64_C
#define UINT64_C(val) val##ui64
#endif
#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;
inline float GetCurTime(){
timeval time;
gettimeofday(&time, NULL);
unsigned long millisecs = (time.tv_sec * 1000) + (time.tv_usec / 1000);
return (float)millisecs;
};
打开GameLayer.cpp文件,添加如下方法:
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
void GameLayer::updateRobots(float dt)
{
int alive = 0;
float distanceSQ;
int randomChoice = 0;
CCObject *pObject = NULL;
CCARRAY_FOREACH(_robots, pObject)
{
Robot *robot = (Robot*)pObject;
robot->update(dt);
if (robot->getActionState() != kActionStateKnockedOut)
{
//1使用一个计数来保存仍然存活着的机器人数量。一个机器人只要不是死亡状态,就被认为仍然存活着。这将用于判断游戏是否应该结束
alive++;
//2检查当前应用程序时间的推移是否超过了机器人的下一次决定时间。如果超过了,意味着机器人需要作出一个新的决定。
if (CURTIME > robot->getNextDecisionTime())
{
distanceSQ = ccpDistanceSQ(robot->getPosition(), _hero->getPosition());
//3检查机器人是否足够接近英雄,以便于有机会出拳攻击落在英雄身上。如果接近英雄了,那么就进行一个随机选择,看是要朝着英雄出拳,还是要继续空闲着。
if (distanceSQ <= 50 * 50)
{
robot->setNextDecisionTime(CURTIME + frandom_range(0.1, 0.5) * 1000);
randomChoice = random_range(0, 1);
if (randomChoice == 0)
{
if (_hero->getPosition().x > robot->getPosition().x)
{
robot->setScaleX(1.0);
}
else
{
robot->setScaleX(-1.0);
}
//4假如机器人决定攻击,我们就用之前检测英雄攻击时相同的方式来进行检测碰撞。只是这一次,英雄和机器人的角色互换了。
robot->setNextDecisionTime(robot->getNextDecisionTime() + frandom_range(0.1, 0.5) *2000);
robot->attack();
if (robot->getActionState() == kActionStateAttack)
{
if (fabsf(_hero->getPosition().y - robot->getPosition().y) < 10)
{
if (_hero->getHitbox().actual.intersectsRect(robot->getAttackBox().actual))
{
_hero->hurtWithDamage(robot->getDamage());
//end game checker here
}
}
}
}
else
{
robot->idle();
}
}
elseif (distanceSQ <= SCREEN.width * SCREEN.width)
{
//5如果机器人和英雄之间的距离小于屏幕宽度,那么机器人将作出决定,要么朝着英雄移动,要么继续空闲。机器人的移动基于英雄位置和机器人位置产生的法向量。
robot->setNextDecisionTime(CURTIME + frandom_range(0.5, 1.0) * 1000);
randomChoice = random_range(0, 2);
if (randomChoice == 0)
{
CCPoint moveDirection = ccpNormalize(ccpSub(_hero->getPosition(), robot->getPosition()));
robot->walkWithDirection(moveDirection);
}
else
{
robot->idle();
}
}
}
}
}
//end game checker here
}
{
int alive = 0;
float distanceSQ;
int randomChoice = 0;
CCObject *pObject = NULL;
CCARRAY_FOREACH(_robots, pObject)
{
Robot *robot = (Robot*)pObject;
robot->update(dt);
if (robot->getActionState() != kActionStateKnockedOut)
{
//1使用一个计数来保存仍然存活着的机器人数量。一个机器人只要不是死亡状态,就被认为仍然存活着。这将用于判断游戏是否应该结束
alive++;
//2检查当前应用程序时间的推移是否超过了机器人的下一次决定时间。如果超过了,意味着机器人需要作出一个新的决定。
if (CURTIME > robot->getNextDecisionTime())
{
distanceSQ = ccpDistanceSQ(robot->getPosition(), _hero->getPosition());
//3检查机器人是否足够接近英雄,以便于有机会出拳攻击落在英雄身上。如果接近英雄了,那么就进行一个随机选择,看是要朝着英雄出拳,还是要继续空闲着。
if (distanceSQ <= 50 * 50)
{
robot->setNextDecisionTime(CURTIME + frandom_range(0.1, 0.5) * 1000);
randomChoice = random_range(0, 1);
if (randomChoice == 0)
{
if (_hero->getPosition().x > robot->getPosition().x)
{
robot->setScaleX(1.0);
}
else
{
robot->setScaleX(-1.0);
}
//4假如机器人决定攻击,我们就用之前检测英雄攻击时相同的方式来进行检测碰撞。只是这一次,英雄和机器人的角色互换了。
robot->setNextDecisionTime(robot->getNextDecisionTime() + frandom_range(0.1, 0.5) *2000);
robot->attack();
if (robot->getActionState() == kActionStateAttack)
{
if (fabsf(_hero->getPosition().y - robot->getPosition().y) < 10)
{
if (_hero->getHitbox().actual.intersectsRect(robot->getAttackBox().actual))
{
_hero->hurtWithDamage(robot->getDamage());
//end game checker here
}
}
}
}
else
{
robot->idle();
}
}
elseif (distanceSQ <= SCREEN.width * SCREEN.width)
{
//5如果机器人和英雄之间的距离小于屏幕宽度,那么机器人将作出决定,要么朝着英雄移动,要么继续空闲。机器人的移动基于英雄位置和机器人位置产生的法向量。
robot->setNextDecisionTime(CURTIME + frandom_range(0.5, 1.0) * 1000);
randomChoice = random_range(0, 2);
if (randomChoice == 0)
{
CCPoint moveDirection = ccpNormalize(ccpSub(_hero->getPosition(), robot->getPosition()));
robot->walkWithDirection(moveDirection);
}
else
{
robot->idle();
}
}
}
}
}
//end game checker here
}
这是一个漫长的代码片段。将代码分解为一段段。对于游戏中的每个机器人:
①.使用一个计数来保存仍然存活着的机器人数量。一个机器人只要不是死亡状态,就被认为仍然存活着。这将用于判断游戏是否应该结束。
②.检查当前应用程序时间的推移是否超过了机器人的下一次决定时间。如果超过了,意味着机器人需要作出一个新的决定。
③.检查机器人是否足够接近英雄,以便于有机会出拳攻击落在英雄身上。如果接近英雄了,那么就进行一个随机选择,看是要朝着英雄出拳,还是要继续空闲着。
④.假如机器人决定攻击,我们就用之前检测英雄攻击时相同的方式来进行检测碰撞。只是这一次,英雄和机器人的角色互换了。
⑤.如果机器人和英雄之间的距离小于屏幕宽度,那么机器人将作出决定,要么朝着英雄移动,要么继续空闲。机器人的移动基于英雄位置和机器人位置产生的法向量。
每当机器人作出决定,它的下一个决定的时间被设定为在未来的一个随机时间。在此期间,它将继续执行上次作出决定时所做出的动作。接着在update函数里,this->updatePositions();前添加如下代码:
this->updateRobots(dt);
2
3
4
5
6
7
8
9
10
CCObject *pObject = NULL;
CCARRAY_FOREACH(_robots, pObject)
{
Robot *robot = (Robot*)pObject;
posX = MIN(_tileMap->getMapSize().width * _tileMap->getTileSize().width - robot->getCenterToSides(),
MAX(robot->getCenterToSides(), robot->getDesiredPosition().x));
posY = MIN(3 * _tileMap->getTileSize().height + robot->getCenterToBottom(),
MAX(robot->getCenterToBottom(), robot->getDesiredPosition().y));
robot->setPosition(ccp(posX, posY));
}
CCARRAY_FOREACH(_robots, pObject)
{
Robot *robot = (Robot*)pObject;
posX = MIN(_tileMap->getMapSize().width * _tileMap->getTileSize().width - robot->getCenterToSides(),
MAX(robot->getCenterToSides(), robot->getDesiredPosition().x));
posY = MIN(3 * _tileMap->getTileSize().height + robot->getCenterToBottom(),
MAX(robot->getCenterToBottom(), robot->getDesiredPosition().y));
robot->setPosition(ccp(posX, posY));
}
9.编译运行,将会看到沿着走廊过来的机器人。效果如下图所示:
10.为游戏添加重新开始的按钮。打开GameLayer.cpp文件,添加头文件引用:
#include"GameScene.h"
2
3
4
5
6
7
8
9
10
11
12
13
14
void GameLayer::endGame()
{
CCLabelTTF *restartLabel = CCLabelTTF::create("RESTART", "Arial", 30);
CCMenuItemLabel *restartItem = CCMenuItemLabel::create(restartLabel, this, menu_selector(GameLayer::restartGame));
CCMenu *menu = CCMenu::create(restartItem, NULL);
menu->setPosition(CENTER);
menu->setTag(5);
_hud->addChild(menu, 5);
}
void GameLayer::restartGame(CCObject* pSender)
{
CCDirector::sharedDirector()->replaceScene(GameScene::create());
}
{
CCLabelTTF *restartLabel = CCLabelTTF::create("RESTART", "Arial", 30);
CCMenuItemLabel *restartItem = CCMenuItemLabel::create(restartLabel, this, menu_selector(GameLayer::restartGame));
CCMenu *menu = CCMenu::create(restartItem, NULL);
menu->setPosition(CENTER);
menu->setTag(5);
_hud->addChild(menu, 5);
}
void GameLayer::restartGame(CCObject* pSender)
{
CCDirector::sharedDirector()->replaceScene(GameScene::create());
}
第一个方法创建显示一个重新开始的按钮,当按下它时,触发第二个方法。后者只是命令导演用新的GameScene实例替换当前场景。接着在updateRobots函数里面,在第一个end game checker here注释后面,添加如下代码:
2
3
4
if (_hero->getActionState() == kActionStateKnockedOut && _hud->getChildByTag(5) == NULL)
{
this->endGame();
}
{
this->endGame();
}
2
3
4
if (alive == 0 && _hud->getChildByTag(5) == NULL)
{
this->endGame();
}
{
this->endGame();
}
11.编译运行,可以看到游戏结束时的样子,如下图所示:
12.音乐和音效。打开GameLayer.cpp文件,添加头文件引用:
#include"SimpleAudioEngine.h"
2
3
4
5
6
7
// Load audio
CocosDenshion::SimpleAudioEngine::sharedEngine()->preloadBackgroundMusic("latin_industries.aifc");
CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic("latin_industries.aifc");
CocosDenshion::SimpleAudioEngine::sharedEngine()->preloadEffect("pd_hit0.wav");
CocosDenshion::SimpleAudioEngine::sharedEngine()->preloadEffect("pd_hit1.wav");
CocosDenshion::SimpleAudioEngine::sharedEngine()->preloadEffect("pd_herodeath.wav");
CocosDenshion::SimpleAudioEngine::sharedEngine()->preloadEffect("pd_botdeath.wav");
CocosDenshion::SimpleAudioEngine::sharedEngine()->preloadBackgroundMusic("latin_industries.aifc");
CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic("latin_industries.aifc");
CocosDenshion::SimpleAudioEngine::sharedEngine()->preloadEffect("pd_hit0.wav");
CocosDenshion::SimpleAudioEngine::sharedEngine()->preloadEffect("pd_hit1.wav");
CocosDenshion::SimpleAudioEngine::sharedEngine()->preloadEffect("pd_herodeath.wav");
CocosDenshion::SimpleAudioEngine::sharedEngine()->preloadEffect("pd_botdeath.wav");
#include"SimpleAudioEngine.h"
2
int randomSound = random_range(0, 1);
CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect(CCString::createWithFormat("pd_hit%d.wav", randomSound)->getCString());
CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect(CCString::createWithFormat("pd_hit%d.wav", randomSound)->getCString());
virtualvoid knockout();
#include"SimpleAudioEngine.h"
2
3
4
5
void Hero::knockout()
{
ActionSprite::knockout();
CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect("pd_herodeath.wav");
}
{
ActionSprite::knockout();
CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect("pd_herodeath.wav");
}
#include"SimpleAudioEngine.h"
2
3
4
5
void Robot::knockout()
{
ActionSprite::knockout();
CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect("pd_botdeath.wav");
}
{
ActionSprite::knockout();
CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect("pd_botdeath.wav");
}
代码例子 http://vdisk.weibo.com/s/BDn59yfnBVkqX
- cocos2d-x格斗游戏教程(五)
- cocos2d-x格斗游戏教程
- cocos2d-x格斗游戏教程(一)
- cocos2d-x格斗游戏教程(二)
- cocos2d-x 格斗游戏教程(三)
- cocos2d-x格斗游戏教程(四)攻击敌人
- cocos2d-x横版格斗游戏教程1
- cocos2d-x横版格斗游戏教程2
- cocos2d-x横版格斗游戏教程3
- cocos2d-x横版格斗游戏教程4
- cocos2d-x 格斗游戏
- cocos2d-x学习之格斗游戏总结(2)
- cocos2d-x 3.0 制作横版格斗游戏
- cocos2d-x 3.0 制作横版格斗游戏
- Quick-Cocos2d-x初学者游戏教程(五)
- Cocos2d-x 3.0 制作横版格斗游戏2
- cocos2d-x 3.0制作2D横版格斗游戏-part1
- cocos2d-x-lua基础系列教程五(lua单例)
- Android Menu初步学习
- Java--设计模式之观察者模式
- cocos2d-x 格斗游戏教程(三)
- 从Mac上快速将pdf文件转移到iPad/iPhone上阅读
- SVD分解的理解
- cocos2d-x格斗游戏教程(五)
- Ubuntu 笔记 新手分享
- 参数跟踪---快速调试
- Mac使文件/文件夹隐藏或显示命令
- 求解1-n之间的素数
- cocos2d-x塔防游戏教程(一)
- 链表的实现基于C++
- 资源收集
- HTTPS是如何保证连接安全:每位Web开发者都应知道的