如何使用cocos2d js制作类似DNF的2D横版格斗过关游戏

横版格斗游戏
cocos2d-x游戏源码
请大家赏个脸,如果感兴趣的看一下,含金量非常高的横版格斗游戏,
我的淘宝网址:
Beat 'Em Up Game
名将、恐龙、拳皇、三国战纪、战国传承,一个个响当当的名字,承载了80后多少儿时的梦想。横版格斗过关游戏,曾经是最受大众欢迎的一种类型,拳拳到肉的打击感,轻风飘逸的一招一式,还有怒涛般的连击技,令无数玩家如痴如醉。这种游戏也一度是游戏性最强的游戏,但在卡牌横行,快餐文化泛滥的今天,这种游戏已经失去了往日的光彩,但这不是游戏本身的错,而主要在于游戏厂商的急功近利。这种游戏不同于纯界面的卡牌游戏,战斗系统的复杂,灵活多变的AI和精巧严格的判定一向是游戏厂商避开的焦点,现在的游戏已经沦落为战斗系统能简则简甚至直接砍掉不要的程度(没有了战斗还能称之为游戏吗?)。所以在玩家的心目中,这种游戏已经成为了永恒的经典,就像划过天际的流星,依然在玩家脑海保留着流光溢彩的一瞬。看到现在,您想知道这种游戏是怎么做出来的吗?那么请看下面:
开发平台:Mac OS / WindowsvcD4KPHA+CtOm08PGvcyoo7ppT1MgLyBBbmRyb2lkPC9wPgo8cD4Kv6q3ormkvt+julhjb2RlIC8gVmlzdWFsIFN0dWRpbzIwMTA8L3A+CjxwPgqx4LPM0+/R1KO6T2JqZWN0aXZlLUMgLyBDJiM0MzsmIzQzOzwvcD4KPHA+CtPOz7fS/cfmo7pDb2NvczJkLWlwaG9uZSAvIENvY29zMmQteDwvcD4KPHA+CtS0wuvE2sjdo7ogIENvY29zMmQtaXBob25lL0NvY29zMmR4wb3M19S0wusmIzQzO1BERr3Ms8wgJiM0MzsgyKvM183q1fvXytS0PC9wPgo8cD4KPGltZyBhbGlnbj0="absmiddle" src="/uploadfile/Collfiles/7.png" alt="\">
能学到的东西有很多,除此之外还有地图编辑器Tiled,物理碰撞编辑器Physics Editor使用, 脚本控制角色行为,严格到帧出攻击判定等等先进技术。有许多技术是网上再怎么查也查不到的(程序员一定很清楚上网搜自己想要的东西是多么费脑子和痛苦的事情)。也许您还是觉得280多块有点贵,但我想告诉您的事,这套源码是从国外网站花300多美元买来的,换成人民币要多少钱。
外网链接:https://www.raywenderlich.com/store/beat-em-up-game-starter-kit
而且本宝贝含金量极高,不是只有干巴巴的源码,还有长达300多页的手把手教程。另从国外买回来只有MAC的XCODE可运行的cocos2d-iphone/effect-x版,国内大多数程序员无法接受。我本人花了三个多月时间一句一句代码地转换翻译成了现在通用流行的cocos2d-x 2.2 / C++版,在VS2010上绝对可以运行,几乎没有BUG。如果您真的喜爱这类游戏,这份宝贝绝对值的收藏,学习之后一生都会受用终生,因为连最难的战斗系统您都攻克了,还有什么游戏您做不了的呢?
口说无凭,有图有真相:找和DNF一样的2D格斗网游_百度知道
找和DNF一样的2D格斗网游
我有更好的答案
三D横版有个,和DNF相差很远,地图很大,技能差不多但是有许多创新。叫勇士OL
那个好玩不啊?好像不出名的样子哦
还行,不过真的不出名,玩家少,技能和DNF有些不同。开始创建人物有剧情,你成为一个高级别角色,对付BOSS,里面技能个个很厉害的样子,而且很华丽,不过剧情需要你被BOSS打败从一级开始。我也是看技能华丽创新才玩的。和DNF一样的有一个,完全模仿的,叫名将三国。动感没DNF好,地图老土,一成不变,动作生硬画质不好,每次DNF卡我要凑合玩完此游戏,却等上了才发现没意思于是删了又装。每个地区副本级别差距过大,造成组队无法组到级别均等的人,有个副本入口是20到40级的,几乎全是30以上的人,我26觉得太吃力不玩了。
有个动作游戏《都市快打》好像还不错,但是就是不知道什么时候公测
个人觉得此类游戏发展空间很大,但是DNF的确到了颠峰了。以前我非常喜欢玩天关战纪,一次次改名,最后终于没了。其实那画面比DNF好多了,而且不是模仿的,就是一些系统不完整,没有快感而已。完全模仿的名将三国,却没DNF那样职业多更新快,而且画面生硬,粗制滥造。其他游戏多数在技能上和DNF相似,如鬼吹灯,但职业比名将还少2个……勇士OL地图大跑到一NPC旁看着就累,而且加载慢,战斗流利有啥用?进个图好久呢。看了下都市快打发现又是个鬼吹灯类型的。画面和鬼吹灯很像,只是背景不同。题外话:你应该知道所有这样游戏NPC在点的时候会说话吧。我注意到天关战纪和DNF外的其他所有这样的游戏,人物说话十分恶心做作,影响游戏体验,但是天关一度被压到玩不了,所以有兴趣你还是去打其中你最想玩的吧。都市快打毕竟是个刚出来的游戏,职业少,趣味少,系统有待发展。
只是我比较喜欢玩2D的动作,像DNF还有天关战纪,只是玩久了想换一个动作玩却发现真的没什么其他的游戏好玩了,甚至玩的兴趣都快没有了,唉,真的好想有一部好玩的游戏啊!
采纳率:20%
鬼吹灯 鬼吹灯外传
和DNF基本差不多
麻花疼 夫妻大战&&横版2d &&
其他1条回答
为您推荐:
其他类似问题
您可能关注的内容
dnf的相关知识
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。25627人阅读
3.1 Cocos2d-x(22)
& & & & 在第一篇《》基础上,增加角色运动、碰撞、敌人、AI和音乐音效,原文《》,在这里继续以Cocos2d-x进行实现。有关源码、资源等在文章下面给出了地址。
步骤如下:
1.使用上一篇的工程;
2.移动英雄。在第一部分我们创建了虚拟方向键,但是还未实现按下方向键移动英雄,现在让我们进行实现。打开Hero.cpp文件,在init函数attack animation后面,添加如下代码:
//walk&animation
CCArray&*walkFrames&=&CCArray::createWithCapacity(8);
for&(i&=&0;&i&&&8;&i++)
&&&&CCSpriteFrame&*frame&=&CCSpriteFrameCache::sharedSpriteFrameCache()-&spriteFrameByName(CCString::createWithFormat(&hero_walk_%02d.png&,&i)-&getCString());
&&&&walkFrames-&addObject(frame);
CCAnimation&*walkAnimation&=&CCAnimation::createWithSpriteFrames(walkFrames,&float(1.0&/&12.0));
this-&setWalkAction(CCRepeatForever::create(CCAnimate::create(walkAnimation)));
打开ActionSprite.cpp文件,实现walkWithDirection方法,代码如下:
void&ActionSprite::walkWithDirection(CCPoint&direction)
&&&&if&(_actionState&==&kActionStateIdle)
&&&&&&&&this-&stopAllActions();
&&&&&&&&this-&runAction(_walkAction);
&&&&&&&&_actionState&=&kActionStateW
&&&&if&(_actionState&==&kActionStateWalk)
&&&&&&&&_velocity&=&ccp(direction.x&*&_walkSpeed,&direction.y&*&_walkSpeed);
&&&&&&&&if&(_velocity.x&&=&0)
&&&&&&&&&&&&this-&setScaleX(1.0);
&&&&&&&&}&
&&&&&&&&else
&&&&&&&&&&&&this-&setScaleX(-1.0);
这段代码,检查前置动作状态是否空闲,若是的话切换动作到行走。在行走状态时,根据_walkSpeed值改变精灵速度。同时检查精灵的左右方向,并通过将精灵scaleX设置为1或-1来翻转精灵。要让英雄的行走动作跟方向键联系起来,需要借助方向键的委托:GameLayer类。打开GameLayer.cpp文件,实现如下方法:
void&GameLayer::didChangeDirectionTo(SimpleDPad&*simpleDPad,&CCPoint&direction)
&&&&_hero-&walkWithDirection(direction);
void&GameLayer::isHoldingDirection(SimpleDPad&*simpleDPad,&CCPoint&direction)
&&&&_hero-&walkWithDirection(direction);
void&GameLayer::simpleDPadTouchEnded(SimpleDPad&*simpleDPad)
&&&&if&(_hero-&getActionState()&==&kActionStateWalk)
&&&&&&&&_hero-&idle();
此时,编译运行程序的话,通过方向键移动英雄,发现英雄只是原地踏步。改变英雄的位置是ActionSprite和GameLayer共同的责任。一个ActionSprite永远不会知道它在地图上的位置。因此,它并不知道已经到达了地图的边缘,它只知道它想去哪里。而GameLayer的责任就是将它的期望位置转换成实际的位置。打开ActionSprite.cpp文件,实现以下方法:
void&ActionSprite::update(float&dt)
&&&&if&(_actionState&==&kActionStateWalk)
&&&&&&&&_desiredPosition&=&ccpAdd(this-&getPosition(),&ccpMult(_velocity,&dt));
这个方法在每次游戏更新场景的时候都会进行调用,当精灵处于行走状态时,它更新精灵的期望位置。位置+速度*时间,实际上就是意味着每秒移动X和Y点。打开GameLayer.cpp文件,在init函数this-&initTileMap();后面添加如下代码:
this-&scheduleUpdate();
在析构函数,添加如下代码:
GameLayer::~GameLayer(void)
&&&&this-&unscheduleUpdate();
增加如下两个方法:
void&GameLayer::update(float&dt)
&&&&_hero-&update(dt);
&&&&this-&updatePositions();
void&GameLayer::updatePositions()
&&&&float&posX&=&MIN(_tileMap-&getMapSize().width&*&_tileMap-&getTileSize().width&-&_hero-&getCenterToSides(),
&&&&&&&&MAX(_hero-&getCenterToSides(),&_hero-&getDesiredPosition().x));
&&&&float&posY&=&MIN(3&*&_tileMap-&getTileSize().height&+&_hero-&getCenterToBottom(),
&&&&&&&&MAX(_hero-&getCenterToBottom(),&_hero-&getDesiredPosition().y));
&&&&_hero-&setPosition(ccp(posX,&posY));
设定GameLayer的更新方法,每次循环时,GameLayer让英雄更新它的期望位置,然后通过以下这些值,将期望位置进行检查是否在地图地板的范围内:
mapSize:地图tile数量。总共有10x100个tile,但只有3x100属于地板。
tileSize:每个tile的尺寸,在这里是32x32像素。
GameLayer还使用到了ActionSprite的两个测量值,centerToSides和centerToBottom,因为ActionSprite要想保持在场景内,它的位置不能超过实际的精灵边界。假如ActionSprite的位置在已经设置的边界内,则GameLayer让英雄达到期望位置,否则GameLayer会让英雄留停在原地。
3.编译运行,此时点击方向键,移动英雄,如下图所示:
但是,很快你就会发现英雄可以走出地图的右边界,然后就这样从屏幕上消失了。
4.以上的问题,可以通过基于英雄的位置进行滚动地图,这个方法在文章《》中有描述过。打开GameLayer.cpp文件,在update函数里最后添加如下代码:
this-&setViewpointCenter(_hero-&getPosition());
添加如下方法:
void&GameLayer::setViewpointCenter(CCPoint&position)
&&&&CCSize&winSize&=&CCDirector::sharedDirector()-&getWinSize();
&&&&int&x&=&MAX(position.x,&winSize.width&/&2);
&&&&int&y&=&MAX(position.y,&winSize.height&/&2);
&&&&x&=&MIN(x,&(_tileMap-&getMapSize().width&*&_tileMap-&getTileSize().width)&-&winSize.width&/&2);
&&&&y&=&MIN(y,&(_tileMap-&getMapSize().height&*&_tileMap-&getTileSize().height)&-&winSize.height&/&2);
&&&&CCPoint&actualPosition&=&ccp(x,&y);
&&&&CCPoint&centerOfView&=&ccp(winSize.width&/&2,&winSize.height&/&2);
&&&&CCPoint&viewPoint&=&ccpSub(centerOfView,&actualPosition);
&&&&this-&setPosition(viewPoint);
以上代码让英雄处于屏幕中心位置,当然,英雄在地图边界时的情况除外。编译运行,效果如下图所示:
5.创建机器人。我们已经创建了精灵的基本模型:ActionSprite。我们可以重用它来创建游戏中电脑控制的角色。新建Robot类,派生自ActionSprite类,增加如下方法:
CREATE_FUNC(Robot);
bool&init();
打开Robot.cpp文件,init函数代码如下:
bool&Robot::init()
&&&&bool&bRet&=&false;
&&&&&&&&CC_BREAK_IF(!ActionSprite::initWithSpriteFrameName(&robot_idle_00.png&));
&&&&&&&&int&i;
&&&&&&&&//idle&animation
&&&&&&&&CCArray&*idleFrames&=&CCArray::createWithCapacity(5);
&&&&&&&&for&(i&=&0;&i&&&5;&i++)
&&&&&&&&&&&&CCSpriteFrame&*frame&=&CCSpriteFrameCache::sharedSpriteFrameCache()-&spriteFrameByName(
&&&&&&&&&&&&&&&&CCString::createWithFormat(&robot_idle_%02d.png&,&i)-&getCString());
&&&&&&&&&&&&idleFrames-&addObject(frame);
&&&&&&&&CCAnimation&*idleAnimation&=&CCAnimation::createWithSpriteFrames(idleFrames,&float(1.0&/&12.0));
&&&&&&&&this-&setIdleAction(CCRepeatForever::create(CCAnimate::create(idleAnimation)));
&&&&&&&&//attack&animation
&&&&&&&&CCArray&*attackFrames&=&CCArray::createWithCapacity(5);
&&&&&&&&for&(i&=&0;&i&&&5;&i++)
&&&&&&&&&&&&CCSpriteFrame&*frame&=&CCSpriteFrameCache::sharedSpriteFrameCache()-&spriteFrameByName(
&&&&&&&&&&&&&&&&CCString::createWithFormat(&robot_attack_%02d.png&,&i)-&getCString());
&&&&&&&&&&&&attackFrames-&addObject(frame);
&&&&&&&&CCAnimation&*attackAnimation&=&CCAnimation::createWithSpriteFrames(attackFrames,&float(1.0&/&24.0));
&&&&&&&&this-&setAttackAction(CCSequence::create(CCAnimate::create(attackAnimation),&CCCallFunc::create(this,&callfunc_selector(Robot::idle)),&NULL));
&&&&&&&&//walk&animation
&&&&&&&&CCArray&*walkFrames&=&CCArray::createWithCapacity(6);
&&&&&&&&for&(i&=&0;&i&&&6;&i++)
&&&&&&&&&&&&CCSpriteFrame&*frame&=&CCSpriteFrameCache::sharedSpriteFrameCache()-&spriteFrameByName(
&&&&&&&&&&&&&&&&CCString::createWithFormat(&robot_walk_%02d.png&,&i)-&getCString());
&&&&&&&&&&&&walkFrames-&addObject(frame);
&&&&&&&&CCAnimation&*walkAnimation&=&CCAnimation::createWithSpriteFrames(walkFrames,&float(1.0&/&12.0));
&&&&&&&&this-&setWalkAction(CCRepeatForever::create(CCAnimate::create(walkAnimation)));
&&&&&&&&this-&setWalkSpeed(80.0);
&&&&&&&&this-&setCenterToBottom(39.0);
&&&&&&&&this-&setCenterToSides(29.0);
&&&&&&&&this-&setHitPoints(<span style="color:#ff.0);
&&&&&&&&this-&setDamage(10.0);
&&&&&&&&bRet&=&true;
&&&&}&while&(0);
&&&&return&bR
跟英雄一样,以上代码创建一个带有3个动作的机器人:空闲、出拳、行走。它也有两个测量&#20540;:centerToBottom和centerToSides。注意到机器人的属性比英雄低一点,这是合乎逻辑的,不然英雄永远打不赢机器人。让我们开始添加一些机器人到游戏中去。打开GameLayer.h文件,添加如下代码:
CC_SYNTHESIZE_RETAIN(cocos2d::CCArray*,&_robots,&Robots);
打开GameLayer.cpp文件,添加头文件如下:
#include&&Robot.h&
在构造函数里,添加如下代码:
_robots&=&NULL;
在init函数this-&initTileMap();的后面添加如下代码:
this-&initRobots();
添加如下方法:
void&GameLayer::initRobots()
&&&&int&robotCount&=&50;
&&&&this-&setRobots(CCArray::createWithCapacity(robotCount));
&&&&for&(int&i&=&0;&i&&&robotC&i&#43;&#43;)
&&&&&&&&Robot&*robot&=&Robot::create();
&&&&&&&&_actors-&addChild(robot);
&&&&&&&&_robots-&addObject(robot);
&&&&&&&&int&minX&=&SCREEN.width&&#43;&robot-&getCenterToSides();
&&&&&&&&int&maxX&=&_tileMap-&getMapSize().width&*&_tileMap-&getTileSize().width&-&robot-&getCenterToSides();
&&&&&&&&int&minY&=&robot-&getCenterToBottom();
&&&&&&&&int&maxY&=&3&*&_tileMap-&getTileSize().height&&#43;&robot-&getCenterToBottom();
&&&&&&&&robot-&setScaleX(-1);
&&&&&&&&robot-&setPosition(ccp(random_range(minX,&maxX),&random_range(minY,&maxY)));
&&&&&&&&robot-&setDesiredPosition(robot-&getPosition());
&&&&&&&&robot-&idle();
这些代码做了以下事情:
创建一个包含50个机器人的数组,并把它们添加到精灵表单中。使用Defines.h里面的随机函数随机放置50个机器人到地图地板上。同时,让最小随机&#20540;大于屏幕宽度,以确保不会有任何机器人出现在起点处。让每个机器人都处于空闲状态。
编译运行,让英雄向前走,直到看到地图上的机器人,如下图所示:
试着走到机器人区域中,你会发现机器人的绘制有些不对。如果英雄是在机器人的下面,那么他应该被绘制在机器人的前面,而不是在后面。我们需要明确的告诉游戏,哪个对象先绘制,这就是Z轴来进行控制的。添加英雄和机器人时,并没有明确指定其Z轴,默认下,后面添加的对象会比前面的对象Z轴&#20540;高,这就是为什么机器人挡住了英雄。为了解决这个问题,我们需要动态的处理Z轴顺序。每当精灵在屏幕上垂直移动时,它的Z轴&#20540;应该有所改变。屏幕上越高的精灵,其Z轴&#20540;应越低。打开GameLayer.cpp文件,添加如下方法:
void&GameLayer::reorderActors()
&&&&CCObject&*pObject&=&NULL;
&&&&CCARRAY_FOREACH(_actors-&getChildren(),&pObject)
&&&&&&&&ActionSprite&*sprite&=&(ActionSprite*)pO
&&&&&&&&_actors-&reorderChild(sprite,&(_tileMap-&getMapSize().height&*&_tileMap-&getTileSize().height)&-&sprite-&getPosition().y);
然后在update函数this-&updatePositions();的后面,添加如下代码:
this-&reorderActors();
每当精灵的位置更新,这个方法会让CCSpriteBatchNode重新设置它的每个子节点Z轴&#20540;,其根据子节点离地图底部的距离,当子节点离底部更高时,其Z轴&#20540;就会下降。编译运行,可以看到正确的绘制顺序,如下图所示:
6.出拳猛击机器人,碰撞检测。为了让英雄能够出拳,并且能够实际上打在了机器人身上,需要实现一种方式的碰撞检测。在这篇文章中,我们使用矩形创建一个非常简单的碰撞检测系统。在这个系统中,我们为每个角色定义两种矩形/盒子:
Hit box:代表精灵的身体Attack box:代表精灵的手
假如某个ActionSprite的Attack box碰撞到另一个ActionSprite的Hit box,那么这就是一次碰撞发生。这两个矩形之间的区别,将帮助我们知道谁打了谁。Defines.h文件中的BoundingBox定义,包含两种矩形:实际的,和原始的:
①原始矩形,每个精灵的基本矩形,一旦设置后就不会改变。
②实际矩形,这是位于世界空间中的矩形,当精灵移动时,实际的矩形也跟着变动。
打开ActionSprite.h文件,添加如下代码:
CC_SYNTHESIZE(BoundingBox,&_hitBox,&Hitbox);
CC_SYNTHESIZE(BoundingBox,&_attackBox,&AttackBox);
BoundingBox&createBoundingBoxWithOrigin(cocos2d::CCPoint&origin,&cocos2d::CCSize&size);
以上创建了ActionSprite的两个包围盒:Hit box和Attack box。还定义了一个方法,用于根据给定的原点和大小来创建一个BoundingBox结构体。打开ActionSprite.cpp文件,添加如下方法:
BoundingBox&ActionSprite::createBoundingBoxWithOrigin(CCPoint&origin,&CCSize&size)
&&&&BoundingBox&boundingB
&&&&boundingBox.original.origin&=&
&&&&boundingBox.original.size&=&
&&&&boundingBox.actual.origin&=&ccpAdd(this-&getPosition(),&ccp(boundingBox.original.origin.x,&boundingBox.original.origin.y));
&&&&boundingBox.actual.size&=&
&&&&return&boundingB
void&ActionSprite::transformBoxes()
&&&&_hitBox.actual.origin&=&ccpAdd(this-&getPosition(),&ccp(_hitBox.original.origin.x,&_hitBox.original.origin.y));
&&&&_attackBox.actual.origin&=&ccpAdd(this-&getPosition(),&ccp(_attackBox.original.origin.x&&#43;&
&&&&&&&&(this-&getScaleX()&==&-1&?&(-&_attackBox.original.size.width&-&_hitBox.original.size.width)&:&0),
&&&&&&&&_attackBox.original.origin.y));
void&ActionSprite::setPosition(CCPoint&position)
&&&&CCSprite::setPosition(position);
&&&&this-&transformBoxes();
第一个方法创建一个新的包围盒,这有助于ActionSprite的子类创建属于它们自己的包围盒。第二个方法,基于精灵的位置、比例因子,和包围盒原本的原点和大小来更新每个包围盒实际测量的原点和大小。之所以要用到比例因子,是因为它决定着精灵的方向。位于精灵右侧的盒子,当比例因子设置为-1时,将会翻转到左侧。打开Hero.cpp文件,在init函数后面添加如下代码:
this-&setHitbox(this-&createBoundingBoxWithOrigin(ccp(-this-&getCenterToSides(),&-this-&getCenterToBottom()),
&&&&CCSizeMake(this-&getCenterToSides()&*&2,&this-&getCenterToBottom()&*&2)));
this-&setAttackBox(this-&createBoundingBoxWithOrigin(ccp(this-&getCenterToSides(),&-10),&CCSizeMake(20,&20)));
打开Robot.cpp文件,在init函数后面添加如下代码:
this-&setHitbox(this-&createBoundingBoxWithOrigin(ccp(-this-&getCenterToSides(),&-this-&getCenterToBottom()),
&&&&CCSizeMake(this-&getCenterToSides()&*&2,&this-&getCenterToBottom()&*&2)));
this-&setAttackBox(this-&createBoundingBoxWithOrigin(ccp(this-&getCenterToSides(),&-5),&CCSizeMake(25,&20)));
现在我们已经有了英雄和机器人各自的Hit box和Attack box。如果是可视化的箱子,它们会像下面这样:
无论何时,当一个attack box(红色)跟一个hit box(蓝色)交叉,即一次碰撞发生。在开始编写代码,检测包围盒交叉前,需要确保ActionSprite能够对被击中有所反应。我们已经添加了空闲、出拳、行走动作,但还未创建受伤和死亡动作。打开ActionSprite.cpp文件,实现如下方法:
void&ActionSprite::hurtWithDamage(float&damage)
&&&&if&(_actionState&!=&kActionStateKnockedOut)
&&&&&&&&this-&stopAllActions();
&&&&&&&&this-&runAction(_hurtAction);
&&&&&&&&_actionState&=&kActionStateH
&&&&&&&&_hitPoints&-=&
&&&&&&&&if&(_hitPoints&&=&0)
&&&&&&&&&&&&this-&knockout();
void&ActionSprite::knockout()
&&&&this-&stopAllActions();
&&&&this-&runAction(_knockedOutAction);
&&&&_hitPoints&=&0;
&&&&_actionState&=&kActionStateKnockedO
只要精灵还未死亡,被击中时状态将会切换到受伤状态,执行受伤动画,并且精灵的生命&#20540;将会减去相应的伤害&#20540;。如果生命&#20540;少于0,那么死亡的动作将会触发。为了完成这两个动作,我们还需更改Hero类和Robot类。打开Hero.cpp文件,在init函数walk
animation后面添加如下代码:
//hurt&animation
CCArray&*hurtFrames&=&CCArray::createWithCapacity(3);
for&(i&=&0;&i&&&3;&i&#43;&#43;)
&&&&CCSpriteFrame&*frame&=&CCSpriteFrameCache::sharedSpriteFrameCache()-&spriteFrameByName(CCString::createWithFormat(&hero_hurt_%02d.png&,&i)-&getCString());
&&&&hurtFrames-&addObject(frame);
CCAnimation&*hurtAnimation&=&CCAnimation::createWithSpriteFrames(hurtFrames,&float(1.0&/&12.0));
this-&setHurtAction(CCSequence::create(CCAnimate::create(hurtAnimation),&CCCallFunc::create(this,&callfunc_selector(Hero::idle)),&NULL));
//knocked&out&animation
CCArray&*knockedOutFrames&=&CCArray::createWithCapacity(5);
for&(i&=&0;&i&&&5;&i&#43;&#43;)
&&&&CCSpriteFrame&*frame&=&CCSpriteFrameCache::sharedSpriteFrameCache()-&spriteFrameByName(CCString::createWithFormat(&hero_knockout_%02d.png&,&i)-&getCString());
&&&&knockedOutFrames-&addObject(frame);
CCAnimation&*knockedOutAnimation&=&CCAnimation::createWithSpriteFrames(knockedOutFrames,&float(1.0&/&12.0));
this-&setKnockedOutAction(CCSequence::create(CCAnimate::create(knockedOutAnimation),&CCBlink::create(2.0,&10.0),&NULL));
打开Robot.cpp文件,在init函数walk animation后面添加如下代码:
//hurt&animation
CCArray&*hurtFrames&=&CCArray::createWithCapacity(3);
for&(i&=&0;&i&&&3;&i&#43;&#43;)
&&&&CCSpriteFrame&*frame&=&CCSpriteFrameCache::sharedSpriteFrameCache()-&spriteFrameByName(CCString::createWithFormat(&robot_hurt_%02d.png&,&i)-&getCString());
&&&&hurtFrames-&addObject(frame);
CCAnimation&*hurtAnimation&=&CCAnimation::createWithSpriteFrames(hurtFrames,&float(1.0&/&12.0));
this-&setHurtAction(CCSequence::create(CCAnimate::create(hurtAnimation),&CCCallFunc::create(this,&callfunc_selector(Robot::idle)),&NULL));
//knocked&out&animation
CCArray&*knockedOutFrames&=&CCArray::createWithCapacity(5);
for&(i&=&0;&i&&&5;&i&#43;&#43;)
&&&&CCSpriteFrame&*frame&=&CCSpriteFrameCache::sharedSpriteFrameCache()-&spriteFrameByName(CCString::createWithFormat(&robot_knockout_%02d.png&,&i)-&getCString());
&&&&knockedOutFrames-&addObject(frame);
CCAnimation&*knockedOutAnimation&=&CCAnimation::createWithSpriteFrames(knockedOutFrames,&float(1.0&/&12.0));
this-&setKnockedOutAction(CCSequence::create(CCAnimate::create(knockedOutAnimation),&CCBlink::create(2.0,&10.0),&NULL));
以上代码应该不陌生了。我们用创建其他动作同样的方式创建了受伤和死亡动作。受伤动作结束时,会切换到空闲状态。死亡动作结束时,精灵进行闪烁。打开GameLayer.cpp文件,添加碰撞处理,在ccTouchesBegan函数后面添加如下代码:
if&(_hero-&getActionState()&==&kActionStateAttack)
&&&&CCObject&*pObject&=&NULL;
&&&&CCARRAY_FOREACH(_robots,&pObject)
&&&&&&&&Robot&*robot&=&(Robot*)pO
&&&&&&&&if&(robot-&getActionState()&!=&kActionStateKnockedOut)
&&&&&&&&&&&&if&(fabsf(_hero-&getPosition().y&-&robot-&getPosition().y)&&&10)
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&if&(_hero-&getAttackBox().actual.intersectsRect(robot-&getHitbox().actual))
&&&&&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&&&&&robot-&hurtWithDamage(_hero-&getDamage());
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&}
&&&&}&&&&&&&
以上代码通过三个简单步骤来检测碰撞:
①.检测英雄是否处于攻击状态,以及机器人是否处于非死亡状态。
②.检测英雄的位置和机器人的位置垂直相距在10个点以内。这表明它们在同一平面上站立。
③.检测英雄的attack box是否与机器人的hit box进行交叉。
如果这些条件都成立,那么则一次碰撞发生,机器人执行受伤动作。英雄的伤害&#20540;作为参数进行传递,这样该方法就会知道需要减去多少生命&#20540;。
7.编译运行,出拳攻击机器人吧,效果如下图所示:
8.简单机器人AI的实现。为了使机器人能够移动,并且能够使用我们为它们所创建的动作,就需要开发一个简单的AI(人工智能)系统。这个AI系统基于决策机制。在特定的时间间隔里,我们给每个机器人一个机会来决定接下来该做什么。它们需要知道的第一件事情就是何时做出选择。打开Robot.h文件,添加如下代码:
CC_SYNTHESIZE(float,&_nextDecisionTime,&NextDecisionTime);
打开Robot.cpp文件,在init函数后面,添加如下代码:
_nextDecisionTime&=&0;
这个属性保存下一次机器人可以作出决定的时间。打开Defines.h文件,修改成如下代码:
#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
#define&random_range(low,&high)&(rand()&%&(high&-&low&&#43;&1))&&#43;&low
#define&frandom&(float)rand()&/&UINT64_C(0x)
#define&frandom_range(low,&high)&((high&-&low)&*&frandom)&&#43;&low
//&3&-&enumerations
typedef&enum&_ActionState&{
&&&&kActionStateNone&=&0,
&&&&kActionStateIdle,
&&&&kActionStateAttack,
&&&&kActionStateWalk,
&&&&kActionStateHurt,
&&&&kActionStateKnockedOut
//&4&-&structures
typedef&struct&_BoundingBox&{
&&&&cocos2d::CCRect&
&&&&cocos2d::CCRect&
}&BoundingB
inline&float&GetCurTime(){
&&&&timeval&
&&&&gettimeofday(&time,&NULL);
&&&&unsigned&long&millisecs&=&(time.tv_sec&*&<span style="color:#ff)&&#43;&(time.tv_usec&/&<span style="color:#ff);
&&&&return&(float)
打开GameLayer.cpp文件,添加如下方法:
void&GameLayer::updateRobots(float&dt)
&&&&int&alive&=&0;
&&&&float&distanceSQ;
&&&&int&randomChoice&=&0;
&&&&CCObject&*pObject&=&NULL;
&&&&CCARRAY_FOREACH(_robots,&pObject)
&&&&&&&&Robot&*robot&=&(Robot*)pO
&&&&&&&&robot-&update(dt);
&&&&&&&&if&(robot-&getActionState()&!=&kActionStateKnockedOut)
&&&&&&&&&&&&//1
&&&&&&&&&&&&alive&#43;&#43;;
&&&&&&&&&&&&
&&&&&&&&&&&&//2
&&&&&&&&&&&&if&(CURTIME&&&robot-&getNextDecisionTime())
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&distanceSQ&=&ccpDistanceSQ(robot-&getPosition(),&_hero-&getPosition());
&&&&&&&&&&&&&&&&//3
&&&&&&&&&&&&&&&&if&(distanceSQ&&=&50&*&50)
&&&&&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&&&&&robot-&setNextDecisionTime(CURTIME&&#43;&frandom_range(0.1,&0.5)&*&<span style="color:#ff);
&&&&&&&&&&&&&&&&&&&&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()&&#43;&frandom_range(0.1,&0.5)&*&<span style="color:#ff);
&&&&&&&&&&&&&&&&&&&&&&&&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();
&&&&&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&else&if&(distanceSQ&&=&SCREEN.width&*&SCREEN.width)
&&&&&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&&&&&//5
&&&&&&&&&&&&&&&&&&&&robot-&setNextDecisionTime(CURTIME&&#43;&frandom_range(0.5,&1.0)&*&<span style="color:#ff);
&&&&&&&&&&&&&&&&&&&&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);
在updatePositions函数后面,添加如下代码:
CCObject&*pObject&=&NULL;
CCARRAY_FOREACH(_robots,&pObject)
&&&&Robot&*robot&=&(Robot*)pO
&&&&posX&=&MIN(_tileMap-&getMapSize().width&*&_tileMap-&getTileSize().width&-&robot-&getCenterToSides(),
&&&&&&&&MAX(robot-&getCenterToSides(),&robot-&getDesiredPosition().x));
&&&&posY&=&MIN(3&*&_tileMap-&getTileSize().height&&#43;&robot-&getCenterToBottom(),
&&&&&&&&MAX(robot-&getCenterToBottom(),&robot-&getDesiredPosition().y));
&&&&robot-&setPosition(ccp(posX,&posY));
确保每次游戏循环时,机器人AI方法都被调用。遍历每个机器人,并让它们朝着期望的位置进行移动。
9.编译运行,将会看到沿着走廊过来的机器人。效果如下图所示:
10.为游戏添加重新开始的按钮。打开GameLayer.cpp文件,添加头文件引用:
#include&&GameScene.h&
添加如下方法:
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());
第一个方法创建显示一个重新开始的按钮,当按下它时,触发第二个方法。后者只是命令导演用新的GameScene实例替换当前场景。接着在updateRobots函数里面,在第一个end game checker here注释后面,添加如下代码:
if&(_hero-&getActionState()&==&kActionStateKnockedOut&&&&_hud-&getChildByTag(5)&==&NULL)
&&&&this-&endGame();
在第二个end game checker here注释后面,添加如下代码:
if&(alive&==&0&&&&_hud-&getChildByTag(5)&==&NULL)
&&&&this-&endGame();
这些语句都是检测游戏结束的条件。第一个检测英雄被机器人攻击后,是否还存活着。如果英雄死亡了,那么游戏就结束了。第二个检测是否所有的机器人都死亡了。如果都死亡了,那么游戏也结束了。另外,在endGame方法里,可以看到游戏结束菜单的tag&#20540;为5。因为检测是在循环里面,需要确保游戏结束菜单之前没被创建过。否则的话,将会一直创建游戏结束菜单。
11.编译运行,可以看到游戏结束时的样子,如下图所示:
12.音乐和音效。打开GameLayer.cpp文件,添加头文件引用:
#include&&SimpleAudioEngine.h&
在init函数里,CC_BREAK_IF(!CCLayer::init());后面添加如下代码:
//&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&);
打开ActionSprite.cpp文件,添加头文件引用:
#include&&SimpleAudioEngine.h&
在hurtWithDamage函数,第一个条件语句里添加如下代码:
int&randomSound&=&random_range(0,&1);
CocosDenshion::SimpleAudioEngine::sharedEngine()-&playEffect(CCString::createWithFormat(&pd_hit%d.wav&,&randomSound)-&getCString());
打开ActionSprite.h文件,将knockout方法声明修改如下:
virtual&void&knockout();
打开Hero.cpp文件,添加头文件引用:
#include&&SimpleAudioEngine.h&
添加如下方法:
void&Hero::knockout()
&&&&ActionSprite::knockout();
&&&&CocosDenshion::SimpleAudioEngine::sharedEngine()-&playEffect(&pd_herodeath.wav&);
打开Robot.cpp文件,添加头文件引用:
#include&&SimpleAudioEngine.h&
添加如下方法:
void&Robot::knockout()
&&&&ActionSprite::knockout();
&&&&CocosDenshion::SimpleAudioEngine::sharedEngine()-&playEffect(&pd_botdeath.wav&);
13.编译运行,现在游戏将有配乐,效果图:
参考资料:
1.How To Make A Side-Scrolling Beat ‘Em Up Game Like Scott Pilgrim with Cocos2D – Part 2
2.如何使用cocos2d制作类&#20284;Scott Pilgrim的2D横版&#26684;斗过关游戏part2(翻译)
非常感谢以上资料,本例子源代码附加资源下载地址:
如文章存在错误之处,欢迎指出,以便改正
对此示例的内存泄露修正说明:《》
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:3499920次
积分:35797
积分:35797
排名:第138名
原创:400篇
转载:88篇
评论:3419条
联系方式:
(1)(2)(1)(1)(1)(1)(1)(1)(2)(1)(1)(1)(1)(1)(1)(1)(1)(1)(3)(1)(1)(1)(1)(1)(1)(1)(2)(1)(1)(1)(1)(1)(3)(1)(1)(1)(1)(2)(1)(3)(3)(3)(1)(1)(3)(1)(1)(8)(1)(3)(2)(2)(3)(2)(3)(2)(1)(1)(2)(3)(6)(1)(4)(3)(1)(3)(5)(5)(5)(5)(1)(3)(5)(4)(4)(4)(5)(5)(1)(13)(11)(7)(5)(2)(5)(4)(5)(2)(7)(14)(18)(23)(19)(5)(35)(22)(21)(8)(10)(42)(49)}

我要回帖

更多关于 cocos2d x 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信