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

1062人阅读
本文为 csdn &原创,转载请标明出处。
为了搞懂LUA在我们的GDEX中到底怎么用,我决定研究一下如何比较好的在WPF里封装一个基于lua的APP& framework。
今天先对Lua for C#进行了一次简单的封装。
在C#下用过Lua的人都知道,用C#实现一个函数之后和LUA绑定,需要用到Lua类的RegisterFunction方法。
在函数很少的情况下很好用,但是若需要绑定C#里成百上千个函数,则麻烦了,添加一个函数,至少每次需要修改两个地方:函数实现,函数绑定(RegisterFunction)。并且如果在lua中绑定的名字和C#中不一样,则更麻烦,还需要维护一个函数映射。
今天翻了一下google,翻出GameDev.net上一篇老外的文章,叫《Using Lua with C#》,看了一下,它的方法不错。(改天考虑翻译这篇文章),不过他的示例代码实在是太太太冗长了,大部分是生成函数介绍和函数帮助文档等,直接忽略。把它最核心的东西拿过来,然后自己封装了一下,用起来感觉不错。
基本思想是,使用C#的Attribute来标记函数,实现自动绑定。
核心部分代码如下(LuaFramework.cs):
using System.Collections.G
using System.L
using System.T
using System.W
using System.R
using LuaI
namespace WPFLuaFramework
/// &summary&
/// Lua函数描述特性类
/// &/summary&
public class LuaFunction : Attribute
private String FunctionN
public LuaFunction(String strFuncName)
FunctionName = strFuncN
public String getFuncName()
return FunctionN
/// &summary&
/// Lua引擎
/// &/summary&
class LuaFramework
private Lua pLuaVM = new Lua();//lua虚拟机
/// &summary&
/// 注册lua函数
/// &/summary&
/// &param name=&pLuaAPIClass&&lua函数类&/param&
public void BindLuaApiClass( Object pLuaAPIClass )
foreach (MethodInfo mInfo in pLuaAPIClass.GetType().GetMethods())
foreach (Attribute attr in Attribute.GetCustomAttributes(mInfo))
string LuaFunctionName = (attr as LuaFunction).getFuncName();
pLuaVM.RegisterFunction(LuaFunctionName, pLuaAPIClass, mInfo);
/// &summary&
/// 执行lua脚本文件
/// &/summary&
/// &param name=&luaFileName&&脚本文件名&/param&
public void ExecuteFile(string luaFileName)
pLuaVM.DoFile(luaFileName);
catch (Exception e)
MessageBox.Show(e.ToString());
/// &summary&
/// 执行lua脚本
/// &/summary&
/// &param name=&luaCommand&&lua指令&/param&
public void ExecuteString(string luaCommand)
pLuaVM.DoString(luaCommand);
catch (Exception e)
MessageBox.Show(e.ToString());
我的LUA API类如下,用于实现C# for lua的函数(LuaAPI.cs)
using System.Collections.G
using System.L
using System.T
using System.W
namespace WPFLuaFramework
class LuaAPI
[LuaFunction(&lua1&)]
public void a1()
MessageBox.Show(&a1 called&);
[LuaFunction(&lua2&)]
public int a2()
MessageBox.Show(&a2 called&);
[LuaFunction(&lua3&)]
public void a3(string s)
MessageBox.Show(&a3 called&);
最后看调用代码,是不是很简单
LuaFramework test = new LuaFramework();
test.BindLuaApiClass(new LuaAPI());
test.ExecuteFile(&test.lua&);
test.ExecuteString(&lua1()&);
LUA代码如下
lua3(&test&);
放到.NetFramework 4.0里 需要在
public void BindLuaApiClass( Object pLuaAPIClass )
foreach (MethodInfo mInfo in pLuaAPIClass.GetType().GetMethods())
foreach (Attribute attr in Attribute.GetCustomAttributes(mInfo))
string LuaFunctionName = (attr as LuaFunction).getFuncName();
pLuaVM.RegisterFunction(LuaFunctionName, pLuaAPIClass, mInfo);
里 加一句 判断
if(attr is LuaFunction)
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:511890次
积分:6290
积分:6290
排名:第1247名
原创:36篇
转载:350篇
评论:141条
本博收藏大部分文章为转载,并在文章开头给出了原创作者及原文出处,如有再转,敬请保留相关信息,这是大家对原创作者劳动成果的自觉尊重!!
早期遗留的部分文章未能及时给出相关原创信息,在此谨致歉意,后续会尽全力予以纠正。如为您带来不便,请于本博下留言,谢谢配合。
(1)(1)(1)(4)(2)(1)(4)(5)(3)(9)(18)(5)(1)(2)(2)(9)(3)(4)(1)(4)(3)(2)(1)(2)(1)(1)(6)(14)(1)(22)(4)(1)(13)(7)(9)(7)(4)(26)(10)(22)(9)(15)(12)(11)(1)(4)(3)(4)(1)(10)(7)(3)(12)(17)(1)(9)(17)(18)访问:888415次
积分:9708
积分:9708
排名:第560名
原创:20篇
转载:834篇
评论:146条
(16)(26)(50)(9)(12)(13)(47)(22)(31)(29)(18)(26)(12)(6)(23)(5)(12)(4)(10)(12)(79)(44)(86)(12)(9)(7)(21)(6)(10)(14)(23)(30)(4)(1)(3)(5)(3)(9)(2)(16)(3)(17)(5)(5)(8)(2)(3)(3)(20)(10)(14)(3)4511人阅读
& & & & 在第一篇《》基础上,增加旋转炮塔功能,原文《》,在这里继续以Cocos2d-x进行实现。有关源码、资源等在文章下面给出了地址。
步骤如下:
1.使用上一篇的工程;
2.下载本游戏所需的资源,将资源放置&Resources&目录下:
删除旧的资源player.png和projectile.png;
3.在HelloWorldScene.cpp文件,init函数,修改创建玩家精灵:
CCSprite&*player&=&CCSprite::create(&player2.png&);
在ccTouchesEnded函数,修改创建子弹精灵:
CCSprite&*projectile&=&CCSprite::create(&projectile2.png&);
4.编译运行,可以看到炮塔发射出了子弹,但是有一点奇怪,射击的时候,炮塔并没有朝向那个方向,如下图所示:
5.接下去,就是让炮塔可以旋转射击。在HelloWorldScene.h文件中,添加如下代码:
cocos2d::CCSprite&*_
修改HelloWorldScene.cpp文件中的init函数,如下:
_player&=&CCSprite::create(&player2.png&);
_player-&setPosition(ccp(_player-&getContentSize().width&/&2,&winSize.height&/&2));
this-&addChild(_player);&
6.计算炮塔旋转的角度。看下面图:
数学的知识就是,tan(angle) = 对边 / 邻边,利用反正切angle = arctan(对边 / 邻边),这时计算出的是弧度,用CC_RADIANS_TO_DEGREES宏转换成角度。另外在数学中,逆时针为正,在Cocos2D-x中,顺时针为正,就如下图所示:
需要将最后计算出的角度乘以-1。在ccTouchesEnded函数里,添加如下代码在projectile精灵runAction之前:
float&angleRadians&=&atanf((float)offRealY&/&(float)offRealX);
float&angleDegrees&=&CC_RADIANS_TO_DEGREES(angleRadians);
float&cocosAngle&=&-1&*&angleD
_player-&setRotation(cocosAngle);
7.编译运行,这时就可以看到炮塔旋转射击了。如下图所示:
8.旋转再射击。炮塔的旋转是瞬间完成的,这不符合现实,需要让它有个动作移动炮塔的方向。在HelloWorldScene.h文件中,添加如下声明:
cocos2d::CCSprite&*_nextP
在构造函数里面,添加如下:
_player&=&NULL;
_nextProjectile&=&NULL;
修改ccTouchesEnded函数,并且添加finishShoot方法,代码如下:
void&HelloWorld::ccTouchesEnded(CCSet&*pTouches,&CCEvent&*pEvent)
&&&&if&(_nextProjectile&!=&NULL)
&&&&&&&&return;
&&&&CCTouch&*touch&=&(CCTouch*)pTouches-&anyObject();
&&&&CCPoint&location&=&this-&convertTouchToNodeSpace(touch);
&&&&CCSize&winSize&=&CCDirector::sharedDirector()-&getWinSize();
&&&&_nextProjectile&=&CCSprite::create(&projectile2.png&);
&&&&_nextProjectile-&retain();
&&&&_nextProjectile-&setPosition(ccp(20,&winSize.height&/&2));
&&&&CCPoint&offset&=&ccpSub(location,&_nextProjectile-&getPosition());
&&&&if&(offset.x&&=&0)
&&&&&&&&return;
&&&&int&realX&=&winSize.width&+&_nextProjectile-&getContentSize().width&/&2;
&&&&float&ratio&=&(float)offset.y&/&(float)offset.x;
&&&&int&realY&=&realX&*&ratio&+&_nextProjectile-&getPosition().y;
&&&&CCPoint&realDest&=&ccp(realX,&realY);
&&&&int&offRealX&=&realX&-&_nextProjectile-&getPosition().x;
&&&&int&offRealY&=&realY&-&_nextProjectile-&getPosition().y;
&&&&float&length&=&sqrtf(offRealX&*&offRealX&+&offRealY&*&offRealY);
&&&&float&velocity&=&<span style="color:#ff&/&1;
&&&&float&realMoveDuration&=&length&/&
&&&&float&angleRadians&=&atanf((float)offRealY&/&(float)offRealX);
&&&&float&angleDegrees&=&CC_RADIANS_TO_DEGREES(angleRadians);
&&&&float&cocosAngle&=&-1&*&angleD
&&&&float&rotateDegreesPerSecond&=&<span style="color:#ff&/&0.5;
&&&&float&degreesDiff&=&_player-&getRotation()&-&cocosA
&&&&float&rotateDuration&=&fabs(degreesDiff&/&rotateDegreesPerSecond);
&&&&_player-&runAction(CCSequence::create(CCRotateTo::create(rotateDuration,&cocosAngle),
&&&&&&&&CCCallFunc::create(this,&callfunc_selector(HelloWorld::finishShoot)),&NULL));
&&&&_nextProjectile-&runAction(CCSequence::create(CCMoveTo::create(realMoveDuration,&realDest),&
&&&&&&&&CCCallFuncN::create(this,&callfuncN_selector(HelloWorld::spriteMoveFinished)),&NULL));
&&&&_nextProjectile-&setTag(2);
&&&&CocosDenshion::SimpleAudioEngine::sharedEngine()-&playEffect(&pew-pew-lei.wav&);
void&HelloWorld::finishShoot()
&&&&this-&addChild(_nextProjectile);
&&&&_projectiles-&addObject(_nextProjectile);
&&&&_nextProjectile-&release();
&&&&_nextProjectile&=&NULL;
在函数开头检验_nextProjectile变量,如果非空表示炮塔正在旋转中。不把_nextProjectile立即加到场景中,等待旋转完毕再加入场景。炮塔旋转的速度,为半秒钟旋转半个圆,计算所旋转角度所需的时间。
9.编译运行,可以看到炮塔可以在旋转后进行射击了,如下图所示:
参考资料:
1.How To Make A Simple iPhone Game with Cocos2D 2.X Part 2
2.(译)如何使用cocos2d开发一个简单的iphone游戏:旋转炮塔。(第二部分)
非常感谢以上资料,本例子源代码附加资源下载地址:
如文章存在错误之处,欢迎指出,以便改正。
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1502107次
积分:18973
积分:18973
排名:第150名
原创:360篇
转载:86篇
评论:2918条
联系方式:
(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)如何制作一个塔防游戏
无幻| 09:08|次浏览|
本文实践自 Pablo Ruiz 的文章《》,文中使用Cocos2D,我在这里使用Cocos2D-x 2.0.4进行学习和移植。在这篇文章,将会学习到如何制作一个塔防游戏。在这当中,学习如何在设定的时间内出现一波波的敌人,使这些敌人沿着指定的路点前进,如何在地图上指定的位置创建炮塔,如何使炮塔射击敌人,如何可视化调试路点和炮塔的攻击范围。
步骤如下:
1.新建Cocos2d-win32工程,工程名为"TowerDefense",去除"Box2D"选项,勾选"Simple Audio Engine in Cocos Denshion"选项;
2.下载本游戏所需的资源,将资源放置"Resources"目录下;
3.为场景添加背景图片。打开HelloWorldScene.cpp文件,修改init函数,如下:
bool HelloWorld::init()
bool bRet =
CC_BREAK_IF(! CCLayer::init());
this-&setTouchEnabled(true);
CCSize wins = CCDirector::sharedDirector()-&getWinSize();
CCSprite *background = CCSprite::create("Bg.png");
this-&addChild(background);
background-&setPosition(ccp(wins.width / 2, wins.height / 2));
} while (0);
通过放置的背景图片,可以直观的看出哪些地方允许玩家放置炮塔。编译运行,如下图所示:
4.接着,需要沿路设置一些点,在这些点上能够让玩家触摸和建立炮塔。为了方便管理,使用.plist文件来存储炮塔的放置点,这样就可以很容易的改变它们。TowersPosition.plist已经在资源文件夹中,其中已经有了一些炮塔的位置。查看这个文件,可以看到一个字典数组,字典只包含两个键"x"和"y"。每个字典条目代表一个炮塔位置的x和y坐标。现在需要读取这个文件,并且放置塔基到地图上。打开HelloWorldScene.h文件,添加以下变量:
cocos2d::CCArray* towerB
打开HelloWorldScene.cpp文件,添加如下方法:
void HelloWorld::loadTowerPositions()
CCArray* towerPositions = CCArray::createWithContentsOfFile("TowersPosition.plist");
towerBases = CCArray::createWithCapacity(10);
towerBases-&retain();
CCObject *pObject = NULL;
CCARRAY_FOREACH(towerPositions, pObject)
CCDictionary* towerPos = (CCDictionary*)pO
CCSprite* towerBase = CCSprite::create("open_spot.png");
this-&addChild(towerBase);
towerBase-&setPosition(ccp(((CCString*)towerPos-&objectForKey("x"))-&intValue(),
((CCString*)towerPos-&objectForKey("y"))-&intValue()));
towerBases-&addObject(towerBase);
在init函数里面,添加背景图片代码之后,添加如下代码:
this-&loadTowerPositions();
在析构函数里面,添加如下代码:
towerBases-&release();
编译运行,就可以看到道路两侧的方块,这些是做为玩家炮塔的基座。如下图所示:
5.开始建立炮塔。打开HelloWorldScene.h文件,添加如下代码:
CC_SYNTHESIZE_RETAIN(cocos2d::CCArray*, _towers, Towers);
添加Tower类,派生自CCNode类,Tower.h文件代码如下:
#ifndef __TOWER_H__
#define __TOWER_H__
#include "cocos2d.h"
#include "HelloWorldScene.h"
#define kTOWER_COST 300
class Tower : public cocos2d::CCNode
Tower(void);
~Tower(void);
static Tower* nodeWithTheGame(HelloWorld* game, cocos2d::CCPoint location);
bool initWithTheGame(HelloWorld* game, cocos2d::CCPoint location);
void update(float dt);
void draw(void);
CC_SYNTHESIZE(HelloWorld*, _theGame, TheGame);
CC_SYNTHESIZE(cocos2d::CCSprite*, _mySprite, MySprite);
int attackR
float fireR
// __TOWER_H__
打开Tower.cpp文件,代码如下:
#include "Tower.h"
using namespace cocos2d;
Tower::Tower(void)
Tower::~Tower(void)
Tower* Tower::nodeWithTheGame(HelloWorld* game, CCPoint location)
Tower *pRet = new Tower();
if (pRet && pRet-&initWithTheGame(game, location))
pRet = NULL;
return NULL;
bool Tower::initWithTheGame(HelloWorld* game, CCPoint location)
bool bRet =
attackRange = 70;
damage = 10;
fireRate = 1;
_mySprite = CCSprite::create("tower.png");
this-&addChild(_mySprite);
_mySprite-&setPosition(location);
_theGame =
_theGame-&addChild(this);
this-&scheduleUpdate();
} while (0);
void Tower::update(float dt)
void Tower::draw(void)
#ifdef COCOS2D_DEBUG
ccDrawColor4F(255, 255, 255, 255);
ccDrawCircle(_mySprite-&getPosition(), attackRange, 360, 30, false);
CCNode::draw();
这个Tower类包含几个属性:一个精灵对象,这是炮塔的可视化表现;一个父层的引用,方便访问父层;还有三个变量:
attackRange: 炮塔可以攻击敌人的距离。
damage: 炮塔对敌人造成的伤害值。
fireRate: 炮塔再次攻击敌人的时间间隔。
有了这三个变量,就可以创建各种不同攻击属性的炮塔,比如需要很长时间来重新加载的远程重击,或者范围有限的快速攻击。最后,代码中的draw方法,用于在炮塔周围绘制一个圆,以显示出它的攻击范围,这将方便调试。
6.让玩家添加炮塔。打开HelloWorldScene.cpp文件,加入以下头文件声明:
#include "Tower.h"
在析构函数中添加如下代码:
_towers-&release();
在init函数,添加如下代码:
_towers = CCArray::create();
_towers-&retain();
添加如下两个方法,代码如下:
bool HelloWorld::canBuyTower()
void HelloWorld::ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent)
CCSetIterator iter = pTouches-&begin();
for (; iter != pTouches-&end(); iter++)
CCTouch* pTouch = (CCTouch*)(*iter);
CCPoint location = pTouch-&getLocation();
CCObject *pObject = NULL;
CCARRAY_FOREACH(towerBases, pObject)
CCSprite *tb = (CCSprite*)pO
if (this-&canBuyTower() && tb-&boundingBox().containsPoint(location) && !tb-&getUserData())
//We will spend our gold later.
Tower* tower = Tower::nodeWithTheGame(this, tb-&getPosition());
_towers-&addObject(tower);
tb-&setUserData(tower);
方法ccTouchesBegan检测当用户触摸屏幕上任何点时,遍历towerBases数组,检查触摸点是否包含在任何一个塔基上。不过在创建炮塔前,还有两件事需要检查:
①玩家是否买得起炮塔?canBuyTower方法用来检查玩家是否有足够的金币来购买炮塔。在这里先假设玩家有很多金币,方法返回true。
②玩家是否违法了建筑规则?如果tb的UserData已经设置了,那么这个塔基已经有了炮塔,不能再添加一个新的了。
如果一切检查都通过,那么就创建一个新的炮塔,放置在塔基上,并将它添加到炮塔数组中。编译运行,触摸塔基,就可以看到炮塔放置上去了,并且它的周围还有白色的圆圈显示攻击范围,如下图所示:
7.添加路点。敌人将会沿着一系列的路点前进,这些简单相互连接的点构成了一条路径,敌人在这条路径上进行行走。敌人会出现在第一个路点,搜寻列表中的下一个路点,移动到那个位置,重复这个过程,直到他们到达列表中的最后一个路点——玩家基地。如果被敌人到达基地,那么玩家就会受到损害。添加Waypoint类,派生自CCNode类,Waypoint.h文件代码如下:
#ifndef __WAYPOINT_H__
#define __WAYPOINT_H__
#include "cocos2d.h"
#include "HelloWorldScene.h"
class Waypoint : public cocos2d::CCNode
Waypoint(void);
~Waypoint(void);
static Waypoint* nodeWithTheGame(HelloWorld* game, cocos2d::CCPoint location);
bool initWithTheGame(HelloWorld* game, cocos2d::CCPoint location);
void draw(void);
CC_SYNTHESIZE(cocos2d::CCPoint, _myPosition, MyPosition);
CC_SYNTHESIZE(Waypoint*, _nextWaypoint, NextWaypoint);
HelloWorld* theG
// __WAYPOINT_H__
打开Waypoint.cpp文件,代码如下:
#include "Waypoint.h"
using namespace cocos2d;
Waypoint::Waypoint(void)
_nextWaypoint = NULL;
Waypoint::~Waypoint(void)
Waypoint* Waypoint::nodeWithTheGame(HelloWorld* game, CCPoint location)
Waypoint *pRet = new Waypoint();
if (pRet && pRet-&initWithTheGame(game, location))
pRet = NULL;
return NULL;
bool Waypoint::initWithTheGame(HelloWorld* game, CCPoint location)
bool bRet =
_myPosition =
this-&setPosition(CCPointZero);
theGame-&addChild(this);
} while (0);
void Waypoint::draw(void)
#ifdef COCOS2D_DEBUG
ccDrawColor4F(0, 255, 0, 255);
ccDrawCircle(_myPosition, 6, 360, 30, false);
ccDrawCircle(_myPosition, 2, 360, 30, false);
if (_nextWaypoint)
ccDrawLine(_myPosition, _nextWaypoint-&_myPosition);
CCNode::draw();
首先,通过传入的HelloWorld对象引用和路点位置坐标,进行初始化一个waypoint对象。每个路点都包含下一个路点的引用,这将会创建一个路点链接列表。每个路点知道列表中的下一个路点。通过这种方式,可以引导敌人沿着链表上的路点到达他们的最终目的地。最后,draw方法绘制显示路点的位置,并且绘制一条直线将其与下一个路点进行连接,这仅仅用于调试目的。
8.创建路点列表。打开HelloWorldScene.h文件,添加以下代码:
CC_SYNTHESIZE_RETAIN(cocos2d::CCArray*, _waypoints, Waypoints);
打开HelloWorldScene.cpp文件,加入以下头文件声明:
#include "Waypoint.h"
在析构函数中添加如下代码:
_waypoints-&release();
添加以下方法:
void HelloWorld::addWaypoints()
_waypoints = CCArray::create();
_waypoints-&retain();
Waypoint *waypoint1 = Waypoint::nodeWithTheGame(this, ccp(420, 35));
_waypoints-&addObject(waypoint1);
Waypoint *waypoint2 = Waypoint::nodeWithTheGame(this, ccp(35, 35));
_waypoints-&addObject(waypoint2);
waypoint2-&setNextWaypoint(waypoint1);
Waypoint *waypoint3 = Waypoint::nodeWithTheGame(this, ccp(35, 130));
_waypoints-&addObject(waypoint3);
waypoint3-&setNextWaypoint(waypoint2);
Waypoint *waypoint4 = Waypoint::nodeWithTheGame(this, ccp(445, 130));
_waypoints-&addObject(waypoint4);
waypoint4-&setNextWaypoint(waypoint3);
Waypoint *waypoint5 = Waypoint::nodeWithTheGame(this, ccp(445, 220));
_waypoints-&addObject(waypoint5);
waypoint5-&setNextWaypoint(waypoint4);
Waypoint *waypoint6 = Waypoint::nodeWithTheGame(this, ccp(-40, 220));
_waypoints-&addObject(waypoint6);
waypoint6-&setNextWaypoint(waypoint5);
在init函数,添加如下代码:
this-&addWaypoints();
编译运行,效果如下图所示:
在地图上有6个路点,这是敌人的行走路线。在让敌人出现在游戏中前,还需要添加一个辅助方法。打开HelloWorldScene.cpp文件,添加方法如下:
bool HelloWorld::collisionWithCircle(CCPoint circlePoint, float radius, CCPoint circlePointTwo, float radiusTwo)
float xdif = circlePoint.x - circlePointTwo.x;
float ydif = circlePoint.y - circlePointTwo.y;
float distance = sqrt(xdif * xdif + ydif * ydif);
if(distance &= radius + radiusTwo)
方法collisionWithCircle用于判断两个圆是否碰撞或者相交。这将用于判断敌人是否到达一个路点,同时也可以检测敌人是否在炮塔的攻击范围之内。
9.添加敌人。打开HelloWorldScene.h文件,添加以下代码:
CC_SYNTHESIZE_RETAIN(cocos2d::CCArray*, _enemies, Enemies);
cocos2d::CCLabelBMFont* ui_wave_
打开HelloWorldScene.cpp文件,在析构函数里,添加如下代码:
_enemies-&release();
添加Enemy类,派生自CCNode类,Enemy.h文件代码如下:
#ifndef __ENEMY_H__
#define __ENEMY_H__
#include "cocos2d.h"
#include "HelloWorldScene.h"
#include "Waypoint.h"
class Enemy : public cocos2d::CCNode
Enemy(void);
~Enemy(void);
static Enemy* nodeWithTheGame(HelloWorld* game);
bool initWithTheGame(HelloWorld* game);
void doActivate(float dt);
void getRemoved();
void update(float dt);
void draw(void);
CC_SYNTHESIZE(HelloWorld*, _theGame, TheGame);
CC_SYNTHESIZE(cocos2d::CCSprite*, _mySprite, MySprite);
cocos2d::CCPoint myP
int maxHp;
int currentHp;
float walkingS
Waypoint *destinationW
// __ENEMY_H__
打开Enemy.cpp文件,代码如下:
#include "Enemy.h"
using namespace cocos2d;
#define HEALTH_BAR_WIDTH 20
#define HEALTH_BAR_ORIGIN -10
Enemy::Enemy(void)
Enemy::~Enemy(void)
Enemy* Enemy::nodeWithTheGame(HelloWorld* game)
Enemy *pRet = new Enemy();
if (pRet && pRet-&initWithTheGame(game))
pRet = NULL;
return NULL;
bool Enemy::initWithTheGame(HelloWorld* game)
bool bRet =
maxHp = 40;
currentHp = maxHp;
walkingSpeed = 0.5;
_theGame =
_mySprite = CCSprite::create("enemy.png");
this-&addChild(_mySprite);
Waypoint *waypoint = (Waypoint*)_theGame-&getWaypoints()-&objectAtIndex(_theGame-&getWaypoints()-&count() - 1);
destinationWaypoint = waypoint-&getNextWaypoint();
CCPoint pos = waypoint-&getMyPosition();
myPosition =
_mySprite-&setPosition(pos);
_theGame-&addChild(this);
this-&scheduleUpdate();
} while (0);
void Enemy::doActivate(float dt)
void Enemy::getRemoved()
this-&getParent()-&removeChild(this, true);
_theGame-&getEnemies()-&removeObject(this);
//Notify the game that we killed an enemy so we can check if we can send another wave
_theGame-&enemyGotKilled();
void Enemy::update(float dt)
if (!active)
if (_theGame-&collisionWithCircle(myPosition, 1, destinationWaypoint-&getMyPosition(), 1))
if (destinationWaypoint-&getNextWaypoint())
destinationWaypoint = destinationWaypoint-&getNextWaypoint();
//Reached the end of the road. Damage the player
_theGame-&getHpDamage();
this-&getRemoved();
CCPoint targetPoint =
destinationWaypoint-&getMyPosition();
float movementSpeed = walkingS
CCPoint normalized = ccpNormalize(ccp(targetPoint.x - myPosition.x, targetPoint.y - myPosition.y));
_mySprite-&setRotation(CC_RADIANS_TO_DEGREES(atan2(normalized.y, - normalized.x)));
myPosition = ccp(myPosition.x + normalized.x * movementSpeed, myPosition.y + normalized.y * movementSpeed);
_mySprite-&setPosition(myPosition);
void Enemy::draw(void)
CCPoint healthBarBack[] = {
ccp(_mySprite-&getPosition().x - 10, _mySprite-&getPosition().y + 16),
ccp(_mySprite-&getPosition().x + 10, _mySprite-&getPosition().y + 16),
ccp(_mySprite-&getPosition().x + 10, _mySprite-&getPosition().y + 14),
ccp(_mySprite-&getPosition().x - 10, _mySprite-&getPosition().y + 14)
ccDrawSolidPoly(healthBarBack, 4, ccc4f(255, 0, 0, 255));
CCPoint healthBar[] = {
ccp(_mySprite-&getPosition().x + HEALTH_BAR_ORIGIN, _mySprite-&getPosition().y + 16),
ccp(_mySprite-&getPosition().x + HEALTH_BAR_ORIGIN + (float)(currentHp * HEALTH_BAR_WIDTH) / maxHp, _mySprite-&getPosition().y + 16),
ccp(_mySprite-&getPosition().x + HEALTH_BAR_ORIGIN + (float)(currentHp * HEALTH_BAR_WIDTH) / maxHp, _mySprite-&getPosition().y + 14),
ccp(_mySprite-&getPosition().x + HEALTH_BAR_ORIGIN, _mySprite-&getPosition().y + 14)
ccDrawSolidPoly(healthBar, 4, ccc4f(0, 255, 0, 255));
CCNode::draw();
首先,通过传递一个HelloWorld对象引用进行初始化。在初始化函数里面,对一些重要的变量进行设置:
maxHP: 敌人的生命值。
walkingSpeed: 敌人的移动速度。
mySprite: 存储敌人的可视化表现。
destinationWaypoint: 存储下一个路点的引用。
update方法每帧都会被调用,它首先通过collisionWithCircle方法检查是否到达了目的路点。如果到达了,则前进到下一个路点,直到敌人到达终点,玩家也就受到伤害。接着,它根据敌人的行走速度,沿着一条直线移动精灵到达下一个路点。它通过以下算法:
①计算出从当前位置到目标位置的向量,然后将其长度设置为1(向量标准化)
②将移动速度乘以标准化向量,得到移动的距离,将它与当前坐标进行相加,得到新的坐标位置。
最后,draw方法在精灵上面简单的实现了一条血量条。它首先绘制一个红色背景,然后根据敌人的当前生命值用绿色进行覆盖血量条。
10.显示敌人。打开HelloWorldScene.cpp文件,添加头文件声明:
#include "Enemy.h"
添加如下方法:
bool HelloWorld::loadWave()
CCArray *waveData = CCArray::createWithContentsOfFile("Waves.plist");
if (wave &= waveData-&count())
CCArray *currentWaveData = (CCArray*)waveData-&objectAtIndex(wave);
CCObject *pObject = NULL;
CCARRAY_FOREACH(currentWaveData, pObject)
CCDictionary* enemyData = (CCDictionary*)pO
Enemy *enemy = Enemy::nodeWithTheGame(this);
_enemies-&addObject(enemy);
enemy-&schedule(schedule_selector(Enemy::doActivate), ((CCString*)enemyData-&objectForKey("spawnTime"))-&floatValue());
ui_wave_lbl-&setString(CCString::createWithFormat("WAVE: %d", wave)-&getCString());
void HelloWorld::enemyGotKilled()
//If there are no more enemies.
if (_enemies-&count() &= 0)
if (!this-&loadWave())
CCLog("You win!");
CCDirector::sharedDirector()-&replaceScene(CCTransitionSplitCols::create(1, HelloWorld::scene()));
void HelloWorld::getHpDamage()
在init函数里面,添加如下代码:
ui_wave_lbl = CCLabelBMFont::create(CCString::createWithFormat("WAVE: %d", wave)-&getCString(), "font_red_14.fnt");
this-&addChild(ui_wave_lbl, 10);
ui_wave_lbl-&setPosition(ccp(400, wins.height - 12));
ui_wave_lbl-&setAnchorPoint(ccp(0, 0.5));
_enemies = CCArray::create();
_enemies-&retain();
this-&loadWave();
现在对上面的代码进行一些解释。最重要的部分是loadWave方法,它从Waves.plist文件读取数据。查看这个文件,可以看到它包含了3个数组,每个数组代表着一波敌人。第一个数组包含6个字典,每个字典定义了一个敌人。在本篇文章中,这个字典仅存储敌人的出现时间,但是也可用于定义敌人类型或者其他特殊属性,以区分不同的敌人。loadWave方法检查下一波应出现的敌人,根据波信息创建相应的敌人,并安排它们在规定的时间出现在屏幕上。enemyGotKilled方法检查当前屏幕上的敌人数量,如果已经没有敌人的话,那么就让下一波敌人出现。之后,还使用这个方法来判断玩家是否赢得了游戏。编译运行,敌人正向玩家基地前进,如下图所示:
11.炮塔攻击。每座塔进行检查是否有敌人出现在攻击范围之内,如果有的话,对敌人进行开火,直到以下两种情况之一发生:敌人移动出范围;敌人被消灭。那么炮塔就会寻找下一个敌人。打开Tower.h文件,添加以下代码:
添加以下变量:
Enemy *chosenE
打开Tower.cpp文件,添加头文件声明:
#include "Enemy.h"
在initWithTheGame函数开头if条件之后,添加如下代码:
chosenEnemy = NULL;
添加以下方法:
void Tower::attackEnemy()
this-&schedule(schedule_selector(Tower::shootWeapon), fireRate);
void Tower::chosenEnemyForAttack(Enemy *enemy)
chosenEnemy = NULL;
chosenEnemy =
this-&attackEnemy();
enemy-&getAttacked(this);
void Tower::shootWeapon(float dt)
CCSprite *bullet = CCSprite::create("bullet.png");
_theGame-&addChild(bullet);
bullet-&setPosition(_mySprite-&getPosition());
bullet-&runAction(CCSequence::create(
CCMoveTo::create(0.1, chosenEnemy-&getMySprite()-&getPosition()),
CCCallFunc::create(this, callfunc_selector(Tower::damageEnemy)),
CCCallFuncN::create(this, callfuncN_selector(Tower::removeBullet)),
void Tower::removeBullet(CCSprite *bullet)
bullet-&getParent()-&removeChild(bullet, true);
void Tower::damageEnemy()
if (chosenEnemy)
chosenEnemy-&getDamaged(damage);
void Tower::targetKilled()
if (chosenEnemy)
chosenEnemy = NULL;
this-&unschedule(schedule_selector(Tower::shootWeapon));
void Tower::lostSightOfEnemy()
chosenEnemy-&gotLostSight(this);
if (chosenEnemy)
chosenEnemy = NULL;
this-&unschedule(schedule_selector(Tower::shootWeapon));
最后,更新update方法为如下:
void Tower::update(float dt)
if (chosenEnemy)
//We make it turn to target the enemy chosen
CCPoint normalized = ccpNormalize(ccp(chosenEnemy-&getMySprite()-&getPosition().x - _mySprite-&getPosition().x,
chosenEnemy-&getMySprite()-&getPosition().y - _mySprite-&getPosition().y));
_mySprite-&setRotation(CC_RADIANS_TO_DEGREES(atan2(normalized.y, - normalized.x)) + 90);
if (!_theGame-&collisionWithCircle(_mySprite-&getPosition(), attackRange, chosenEnemy-&getMySprite()-&getPosition(), 1))
this-&lostSightOfEnemy();
CCObject *pObject = NULL;
CCARRAY_FOREACH(_theGame-&getEnemies(), pObject)
Enemy *enemy = (Enemy*)pO
if (_theGame-&collisionWithCircle(_mySprite-&getPosition(), attackRange, enemy-&getMySprite()-&getPosition(), 1))
this-&chosenEnemyForAttack(enemy);
打开Enemy.h文件,添加以下代码:
cocos2d::CCArray *attackedBy;
打开Enemy.cpp文件,在initWithTheGame函数开头if条件之后,添加如下代码:
attackedBy = CCArray::createWithCapacity(5);
attackedBy-&retain();
在getRemoved函数开头,添加如下代码:
CCObject *pObject = NULL;
CCARRAY_FOREACH(attackedBy, pObject)
Tower *attacker = (Tower*)pO
attacker-&targetKilled();
添加如下方法:
void Enemy::getAttacked(Tower* attacker)
attackedBy-&addObject(attacker);
void Enemy::gotLostSight(Tower* attacker)
attackedBy-&removeObject(attacker);
void Enemy::getDamaged(int damage)
currentHp -=
if (currentHp &= 0)
this-&getRemoved();
代码中最重要的部分是在Tower类的update方法。炮塔不断检查敌人是否在攻击范围内,如果是的话,炮塔将旋转朝向敌人,开火攻击。一个敌人一旦被标记为被攻击,将会调用方法让炮塔以攻击间隔发射子弹。反过来,每个敌人都存储有向其攻击的炮塔列表,所以如果敌人被杀死了,那么炮塔就会被通知停止攻击。编译运行,放置几个炮塔在地图上,将会看到一旦敌人进入炮塔的攻击范围,炮塔就会向它们开火攻击,敌人的血量条就会减少,直到被消灭。如下图所示:
12.显示玩家血量。打开HelloWorldScene.h文件,添加以下代码:
int playerHp;
cocos2d::CCLabelBMFont *ui_hp_
bool gameE
变量playerHp表示玩家的生命值,CCLabelBMFont对象是一个标签,用来显示生命数值。gameEnded用来表示游戏是否结束。打开HelloWorldScene.cpp文件,在init函数里面,添加如下代码:
gameEnded =
playerHp = 5;
ui_hp_lbl = CCLabelBMFont::create(CCString::createWithFormat("HP: %d", playerHp)-&getCString(), "font_red_14.fnt");
this-&addChild(ui_hp_lbl, 10);
ui_hp_lbl-&setPosition(ccp(35, wins.height - 12));
添加如下方法:
void HelloWorld::getHpDamage()
playerHp--;
ui_hp_lbl-&setString(CCString::createWithFormat("HP: %d", playerHp)-&getCString());
if (playerHp &= 0)
this-&doGameOver();
void HelloWorld::doGameOver()
if (!gameEnded)
gameEnded =
CCDirector::sharedDirector()-&replaceScene(CCTransitionRotoZoom::create(1, HelloWorld::scene()));
添加的方法为减少玩家生命值,更新标签,并检查玩家生命是否耗尽,如果是的话,游戏就结束了。当敌人到达基地的时候,getHpDamage方法被调用。编译运行,让敌人到达基地,你将会看到玩家的生命在减少,直到游戏失败。如下图所示:
13.限制金币供应量。大多数游戏都实现了“零和”功能,建造每座炮塔需要一定的资源,并给玩家有限的资源进行分配。打开HelloWorldScene.h文件,添加如下代码:
int playerG
cocos2d::CCLabelBMFont *ui_gold_
就像显示生命数值一样,一个变量表示玩家的金币数,一个标签对象显示金币数值。打开HelloWorldScene.cpp文件,在init函数里面,添加如下代码:
playerGold = 1000;
ui_gold_lbl = CCLabelBMFont::create(CCString::createWithFormat("GOLD: %d", playerGold)-&getCString(), "font_red_14.fnt");
this-&addChild(ui_gold_lbl, 10);
ui_gold_lbl-&setPosition(ccp(135, wins.height - 12));
ui_gold_lbl-&setAnchorPoint(ccp(0, 0.5));
添加如下方法:
void HelloWorld::awardGold(int gold)
playerGold +=
ui_gold_lbl-&setString(CCString::createWithFormat("GOLD: %d", playerGold)-&getCString());
替换canBuyTower方法,代码如下:
bool HelloWorld::canBuyTower()
if (playerGold - kTOWER_COST &= 0)
在ccTouchesBegan函数里面,语句//We will spend our gold later.的后面,添加如下代码:
playerGold -= kTOWER_COST;
ui_gold_lbl-&setString(CCString::createWithFormat("GOLD: %d", playerGold)-&getCString());
上述的代码在玩家尝试放置炮塔时,检查是否有足够的金币。如果足够的话,炮塔就会放置上去,并从玩家的金币数中减去炮塔的费用。每次杀死敌人的时候也应该奖励玩家一些金币。打开Enemy.cpp文件,在getDamaged函数里面,if条件后面,添加如下语句:
_theGame-&awardGold(200);
编译运行,会看到不能随意的放置炮塔了,因为每个炮塔都要花费金币。当然,杀死敌人就可以获得金币奖励,这样就可以继续购买炮塔。这是一个很好的系统。如下图所示:
14.加入背景音乐和音效。打开HelloWorldScene.cpp文件,添加头文件声明:
#include "SimpleAudioEngine.h"
在init函数,if条件之后,添加如下代码:
CocosDenshion::SimpleAudioEngine::sharedEngine()-&playBackgroundMusic("8bitDungeonLevel.mp3", true);
在ccTouchesBegan函数,添加一个新的Tower对象前,添加如下代码:
CocosDenshion::SimpleAudioEngine::sharedEngine()-&playEffect("tower_place.wav");
在getHpDamage函数里,添加如下代码:
CocosDenshion::SimpleAudioEngine::sharedEngine()-&playEffect("life_lose.wav");
打开Enemy.cpp文件,添加头文件声明:
#include "SimpleAudioEngine.h"
在getDamaged函数里,添加如下代码:
CocosDenshion::SimpleAudioEngine::sharedEngine()-&playEffect("laser_shoot.wav");
编译运行,现在游戏将有配乐,关闭掉调试绘制后,效果图:
参考资料:
1.How To Make a Tower Defense Game&
2.钓龟岛保卫战-如何从零开始制作一款iOS塔防游戏(新)&
非常感谢以上资料,本例子源代码附加资源下载地址:
来自:csdn}

我要回帖

更多关于 ps2格斗过关游戏 的文章

更多推荐

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

点击添加站长微信