cocos2d-x横版格斗游戏教程3
来源:互联网 发布:fcm图像分割算法 编辑:程序博客网 时间:2024/05/09 14:19
这一篇要为英雄创造一些小伙伴了,并且需要让机器人会巡逻,会偷懒,会行走,还会攻击英雄,当然也能受伤。其实机器人和英雄有一些共同的属性:攻击力、生命值和行走速度。但机器人是由电脑控制,状态是随机切换的,所以还需要指定巡逻区域、攻击区域、行走方向、决策时间等。
首先更新BaseSprite类,添加攻击力和生命值属性,在BaseSprite.h中添加:
CC_SYNTHESIZE(unsigned
int
, m_hp, HP);
CC_SYNTHESIZE(unsigned
int
, m_attack, Attack);
创建Enemy类,代表敌方机器人,这里需要实现简单的AI,让机器人能自动思考,根据具体环境切换自己的状态:
Enemy.h
typedef
enum
{
AI_IDLE = 0,
AI_PATROL,
AI_ATTACK,
AI_PURSUIT
}AiState;
class
Enemy :
public
BaseSprite
{
public
:
Enemy();
~Enemy();
bool
init();
CREATE_FUNC(Enemy);
CC_SYNTHESIZE(cocos2d::Point, m_moveDirection, MoveDirection);
CC_SYNTHESIZE(
float
, m_eyeArea, EyeArea);
CC_SYNTHESIZE(
float
, m_attackArea, AttackArea)
CC_SYNTHESIZE(AiState, m_aiState, AiState);
void
execute(
const
cocos2d::Point& target,
float
targetBodyWidth);
private
:
void
decide(
const
cocos2d::Point& target,
float
targetBodyWidth);
unsigned
int
m_nextDecisionTime;
};
AiState表示机器人的四种状态:休闲、巡逻、攻击、跟随。机器人还有几个变量,分别表示:行走方向、巡逻范围、攻击范围、当前AI状态。
m_nextDecisionTime表示机器人距离下一次决策的时间,execute函数是在GameLayer.cpp中update函数调用的,定期执行更新机器人状态。decide函数实现机器人怎么决策,是机器人的内心世界。
这里重点分析机器人AI该怎么实现,因为只是一个demo,所以就尽可能的简单些吧。首先机器人需要根据自己的朝向和英雄的位置来思考,如果机器人背对着英雄或者英雄处于机器人巡逻范围之外,那么此时机器人是看不到英雄的,机器人就会随机的选择继续巡逻或者站着偷懒。如果英雄在机器人的巡逻范围内,且被机器人看到了,则机器人需要判断英雄是否处于自己的攻击范围,来决策是攻击还是追过去。每种状态下的思考时间最好设置成随机的,这样更真实。看源码实现:
Enemy.cpp
Enemy::Enemy():
m_nextDecisionTime(0)
{}
Enemy::~Enemy()
{}
bool
Enemy::init()
{
bool
ret =
false
;
do
{
this
->initWithSpriteFrameName(
"robot_idle_00.png"
);
Animation *pIdleAnim =
this
->createAnimation(
"robot_idle_%02d.png"
, 5, 12);
this
->setIdleAction(RepeatForever::create(Animate::create(pIdleAnim)));
Animation *pWalkAnim =
this
->createAnimation(
"robot_walk_%02d.png"
, 6, 12);
this
->setWalkAction(RepeatForever::create(Animate::create(pWalkAnim)));
Animation *pAttackAnim =
this
->createAnimation(
"robot_attack_%02d.png"
, 5, 20);
this
->setAttackAction(Sequence::create(Animate::create(pAttackAnim), BaseSprite::createIdleCallbackFunc(), NULL));
Animation *pHurtAnim =
this
->createAnimation(
"robot_hurt_%02d.png"
, 3, 20);
this
->setHurtAction(Sequence::create(Animate::create(pHurtAnim), BaseSprite::createIdleCallbackFunc(), NULL));
Animation *pDeadAnim =
this
->createAnimation(
"robot_knockout_%02d.png"
, 5, 12);
this
->setDeadAction(Sequence::create(Animate::create(pDeadAnim), Blink::create(3, 9), NULL));
ret =
true
;
}
while
(0);
return
ret;
}
void
Enemy::execute(
const
Point& target,
float
targetBodyWidth)
{
if
(m_nextDecisionTime == 0)
{
this
->decide(target, targetBodyWidth);
}
else
{
-- m_nextDecisionTime;
}
}
void
Enemy::decide(
const
Point& target,
float
targetBodyWidth)
{
Point location =
this
->getPosition();
float
distance = location.getDistance(target);
distance = distance - (targetBodyWidth / 2 +
this
->getDisplayFrame()->getRect().size.width / 2) + 30;
//log("distance=%f, m_fVelocity=%f", distance, m_fVelocity);
bool
isFlippedX =
this
->isFlippedX();
bool
isOnTargetLeft = (location.x < target.x ?
true
:
false
);
if
((isFlippedX && isOnTargetLeft) || (!isFlippedX && !isOnTargetLeft)) {
this
->m_aiState = CCRANDOM_0_1() > 0.5f ? AI_PATROL : AI_IDLE;
}
else
{
if
(distance < m_eyeArea)
{
this
->m_aiState = distance < m_attackArea ? AI_ATTACK : AI_PURSUIT;
}
else
{
this
->m_aiState = CCRANDOM_0_1() > 0.5f ? AI_PATROL : AI_IDLE;
}
}
switch
(m_aiState)
{
case
AI_ATTACK:
{
this
->runAttackAction();
this
->attack();
this
->m_nextDecisionTime = 50;
}
break
;
case
AI_IDLE:
{
this
->runIdleAction();
this
->m_nextDecisionTime = CCRANDOM_0_1() * 100;
}
break
;
case
AI_PATROL:
{
this
->runWalkAction();
this
->m_moveDirection.x = CCRANDOM_MINUS1_1();
this
->m_moveDirection.y = CCRANDOM_MINUS1_1();
m_moveDirection.x = m_moveDirection.x > 0 ? (m_moveDirection.x + m_fVelocity.x) : (m_moveDirection.x - m_fVelocity.x);
m_moveDirection.y = m_moveDirection.y > 0 ? (m_moveDirection.y + m_fVelocity.y) : (m_moveDirection.y - m_fVelocity.y);
this
->m_nextDecisionTime = CCRANDOM_0_1() * 100;
}
break
;
case
AI_PURSUIT:
{
this
->runWalkAction();
//v.normalize() function return the unit vector of v
this
->m_moveDirection = (target - location).normalize();
this
->setFlippedX(m_moveDirection.x < 0 ?
true
:
false
);
m_moveDirection.x = m_moveDirection.x > 0 ? (m_moveDirection.x + m_fVelocity.x) : (m_moveDirection.x - m_fVelocity.x);
m_moveDirection.y = m_moveDirection.y > 0 ? (m_moveDirection.y + m_fVelocity.y) : (m_moveDirection.y - m_fVelocity.y);
this
->m_nextDecisionTime = 10;
}
break
;
}
}
当机器人思考接下来该做什么时,就会执行相应的操作和动画。
机器人创造完成了,现在把它添加到游戏中去,修改GameLayer.h,添加下面的代码:
#define MIN_ENEMY_COUNT 5
void
updateEnemies(
float
dt);
void
addEnemy();
void
onEnemyAttack(BaseSprite *pSprite);
cocos2d::Array *m_pEnemies;
updateEnemies表示每一次循环都会更新每个机器人的状态,onEnemyAttack是机器人攻击英雄时执行的函数,暂时不实现。m_pEnemies为保存机器人的容器。
修改GameLayer.cpp,添加下面的函数:
void
GameLayer::addEnemy()
{
Size winSize = Director::getInstance()->getWinSize();
Point location = Point::ZERO;
Enemy *pEnemy = Enemy::create();
//log("m_pTiledMap->getMapSize() mapSize=%f", m_pTiledMap->getMapSize().width);
float
halfEnemyFrameHeight = (pEnemy->getDisplayFrame()->getRect().size.height) / 2;
float
heroPosX = m_pHero->getPositionX();
float
halfWinWidth = (winSize.width / 2);
while
(fabsf(heroPosX - location.x) < 150)
{
if
(heroPosX < halfWinWidth)
{
location.x = m_pHero->getPositionX() + CCRANDOM_0_1() * halfWinWidth;
}
else
if
(heroPosX > (m_pTiledMap->getMapSize().width * m_fTileWidth - halfWinWidth)) {
location.x = m_pHero->getPositionX() - CCRANDOM_0_1() * halfWinWidth;
}
else
{
location.x = m_pHero->getPositionX() + CCRANDOM_MINUS1_1() * halfWinWidth;
}
}
float
maxY = m_fTileHeight * 3 + halfEnemyFrameHeight;
location.y = CCRANDOM_0_1() * maxY;
if
(location.y < halfEnemyFrameHeight)
{
location.y = halfEnemyFrameHeight;
}
pEnemy->attack = CC_CALLBACK_0(GameLayer::onEnemyAttack,
this
, pEnemy);
pEnemy->setPosition(m_origin + location);
pEnemy->setZOrder(m_fScreenHeight - pEnemy->getPositionY());
pEnemy->runIdleAction();
pEnemy->setAttack(5);
pEnemy->setHP(30);
pEnemy->setVelocity(Point(0.5f, 0.5f));
pEnemy->setEyeArea(200);
pEnemy->setAttackArea(25);
m_pEnemies->addObject(pEnemy);
m_pSpriteNodes->addChild(pEnemy);
}
void
GameLayer::updateEnemies(
float
dt) {
Object *pObj = NULL;
Point distance = Point::ZERO;
Point heroLocation = m_pHero->getPosition();
Array *pRemovedEnemies = Array::create();
CCARRAY_FOREACH(m_pEnemies, pObj)
{
Enemy *pEnemy = (Enemy*)pObj;
pEnemy->execute(heroLocation, m_pHero->getDisplayFrame()->getRect().size.width);
if
(pEnemy->getCurrActionState() == ACTION_STATE_WALK)
{
Point location = pEnemy->getPosition();
Point direction = pEnemy->getMoveDirection();
Point expect = location + direction;
float
halfEnemyFrameHeight = (pEnemy->getDisplayFrame()->getRect().size.height) / 2;
if
(expect.y < halfEnemyFrameHeight || expect.y > (m_fTileHeight * 3 + halfEnemyFrameHeight) )
{
direction.y = 0;
}
pEnemy->setFlippedX(direction.x < 0 ?
true
:
false
);
pEnemy->setPosition(location + direction);
pEnemy->setZOrder(pEnemy->getPositionY());
}
}
CCARRAY_FOREACH(pRemovedEnemies, pObj)
{
Enemy *pEnemy = (Enemy*)pObj;
m_pEnemies->removeObject(pEnemy);
m_pSpriteNodes->removeChild(pEnemy,
true
);
}
pRemovedEnemies->removeAllObjects();
}
void
GameLayer::onEnemyAttack(BaseSprite *pSprite)
{
}
在GameLayer.cpp的update函数中添加:
this
->updateEnemies(dt);
在init函数中添加:
m_pEnemies = Array::createWithCapacity(MIN_ENEMY_COUNT);
m_pEnemies->retain();
for
(
int
i = 0; i < MIN_ENEMY_COUNT; ++ i)
{
this
->addEnemy();
}
OK,现在编译运行项目,就可以看到屏幕上有5个机器人追着英雄打了,效果如下图:
目前机器人和英雄都没有攻击效果,都是无敌状态,不过他们好日子快到头了,下一篇我们就来让他们接受现实的残酷吧。
- cocos2d-x横版格斗游戏教程3
- cocos2d-x横版格斗游戏教程1
- cocos2d-x横版格斗游戏教程2
- cocos2d-x横版格斗游戏教程4
- cocos2d-x格斗游戏教程
- cocos2d-x 3.0 制作横版格斗游戏
- cocos2d-x 3.0 制作横版格斗游戏
- cocos2d-x格斗游戏教程(一)
- cocos2d-x格斗游戏教程(二)
- cocos2d-x 格斗游戏教程(三)
- cocos2d-x格斗游戏教程(五)
- Cocos2d-x 3.0 制作横版格斗游戏2
- cocos2d-x 3.0制作2D横版格斗游戏-part1
- quick cocos2d-x 实战:做一个手机横版格斗游戏3:游戏开始界面切图
- cocos2d-x 格斗游戏
- cocos2d-x格斗游戏教程(四)攻击敌人
- Beat 'Em Up Game Starter Kit (横版格斗游戏) cocos2d-x游戏源码
- Beat 'Em Up Game Starter Kit (横版格斗游戏) cocos2d-x游戏源码
- 如何解决KEIL 5 编译KEIL4的带有RTX系统的工程解决方法
- 《Unix-Linux编程实践教程》读书笔记(九)
- socket多线程的编程
- java的离线数据集(RowSet)简单入门
- 看看每天你都在犯着什么神错误。。。
- cocos2d-x横版格斗游戏教程3
- Android学习 如何真机测试
- hdu-1690 Bus System
- 监控平台架构设计
- 《Unix-Linux编程实践教程》读书笔记(十)
- 交换数组的第0行同第3行的值和交换第1行同第2行的值
- android判断字符串是否为数字
- 换工作经历和心得
- MergeSort