分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow
也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!
本文实践自 Pablo Ruiz 的文章《How To Make a Tower Defense Game》,文中使用Cocos2D,我在这里使用Cocos2D-x 2.0.4进行学习和移植。在这篇文章,将会学习到如何制作一个塔防游戏。在这当中,学习如何在设定的时间内出现一波波的敌人,使这些敌人沿着指定的路点前进,如何在地图上指定的位置创建炮塔,如何使炮塔射击敌人,如何可视化调试路点和炮塔的攻击范围。
步骤如下:
1.新建Cocos2d-win32工程,工程名为"TowerDefense",去除"Box2D"选项,勾选"Simple Audio Engine in Cocos Denshion"选项;
2.下载本游戏所需的资源,将资源放置"Resources"目录下;
3.为场景添加背景图片。打开HelloWorldScene.cpp文件,修改init函数,如下:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
bool HelloWorld::init()
{ bool bRet = false; do { 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)); bRet = true; } while ( 0); return bRet; } |
通过放置的背景图片,可以直观的看出哪些地方允许玩家放置炮塔。编译运行,如下图所示:
4.接着,需要沿路设置一些点,在这些点上能够让玩家触摸和建立炮塔。为了方便管理,使用.plist文件来存储炮塔的放置点,这样就可以很容易的改变它们。TowersPosition.plist已经在资源文件夹中,其中已经有了一些炮塔的位置。查看这个文件,可以看到一个字典数组,字典只包含两个键"x"和"y"。每个字典条目代表一个炮塔位置的x和y坐标。现在需要读取这个文件,并且放置塔基到地图上。打开HelloWorldScene.h文件,添加以下变量:
1
|
cocos2d::CCArray* towerBases;
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
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*)pObject; 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函数里面,添加背景图片代码之后,添加如下代码:
1
|
this->loadTowerPositions();
|
1
|
towerBases->release();
|
5.开始建立炮塔。打开 HelloWorldScene.h文件,添加如下代码:
1
|
CC_SYNTHESIZE_RETAIN(cocos2d::CCArray*, _towers, Towers);
|
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 |
#ifndef __TOWER_H__
#define __TOWER_H__ #include "cocos2d.h" #include "HelloWorldScene.h" #define kTOWER_COST 300 class Tower : public cocos2d::CCNode { public: 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); private: int attackRange; int damage; float fireRate; }; #endif // __TOWER_H__ |
打开Tower.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 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 |
#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)) { return pRet; } else { delete pRet; pRet = NULL; return NULL; } } bool Tower::initWithTheGame(HelloWorld* game, CCPoint location) { bool bRet = false; do { attackRange = 70; damage = 10; fireRate = 1; _mySprite = CCSprite::create( "tower.png"); this->addChild(_mySprite); _mySprite->setPosition(location); _theGame = game; _theGame->addChild( this); this->scheduleUpdate(); bRet = true; } while ( 0); return bRet; } void Tower::update( float dt) { } void Tower::draw( void) { #ifdef COCOS2D_DEBUG ccDrawColor4F( 255, 255, 255, 255); ccDrawCircle(_mySprite->getPosition(), attackRange, 360, 30, false); #endif CCNode::draw(); } |
这个Tower类包含几个属性:一个精灵对象,这是炮塔的可视化表现;一个父层的引用,方便访问父层;还有三个变量:
- attackRange: 炮塔可以攻击敌人的距离。
- damage: 炮塔对敌人造成的伤害值。
- fireRate: 炮塔再次攻击敌人的时间间隔。
6.让玩家添加炮塔。打开 HelloWorldScene.cpp文件,加入以下头文件声明:
1
|
#include
"Tower.h"
|
1
|
_towers->release();
|
1
2 |
_towers = CCArray::create();
_towers->retain(); |
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 |
bool HelloWorld::canBuyTower()
{ return true; } 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*)pObject; 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文件代码如下:
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 |
#ifndef __WAYPOINT_H__
#define __WAYPOINT_H__ #include "cocos2d.h" #include "HelloWorldScene.h" class Waypoint : public cocos2d::CCNode { public: 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); private: HelloWorld* theGame; }; #endif // __WAYPOINT_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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
#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)) { return pRet; } else { delete pRet; pRet = NULL; return NULL; } } bool Waypoint::initWithTheGame(HelloWorld* game, CCPoint location) { bool bRet = false; do { theGame = game; _myPosition = location; this->setPosition(CCPointZero); theGame->addChild( this); bRet = true; } while ( 0); return bRet; } 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); } #endif CCNode::draw(); } |
8.创建路点列表。打开HelloWorldScene.h文件,添加以下代码:
1
|
CC_SYNTHESIZE_RETAIN(cocos2d::CCArray*, _waypoints, Waypoints);
|
1
|
#include
"Waypoint.h"
|
1
|
_waypoints->release();
|
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 |
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函数,添加如下代码:
1
|
this->addWaypoints();
|
在地图上有6个路点,这是敌人的行走路线。在让敌人出现在游戏中前,还需要添加一个辅助方法。打开 HelloWorldScene.cpp文件,添加方法如下:
1
2 3 4 5 6 7 8 9 10 11 12 13 |
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) { return true; } return false; } |
方法collisionWithCircle用于判断两个圆是否碰撞或者相交。这将用于判断敌人是否到达一个路点,同时也可以检测敌人是否在炮塔的攻击范围之内。
9.添加敌人。打开HelloWorldScene.h文件,添加以下代码:
1
2 3 4 |
CC_SYNTHESIZE_RETAIN(cocos2d::CCArray*, _enemies, Enemies);
int wave; cocos2d::CCLabelBMFont* ui_wave_lbl; |
打开HelloWorldScene.cpp文件,在析构函数里,添加如下代码:
1
|
_enemies->release();
|
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 |
#ifndef __ENEMY_H__
#define __ENEMY_H__ #include "cocos2d.h" #include "HelloWorldScene.h" #include "Waypoint.h" class Enemy : public cocos2d::CCNode { public: 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); private: cocos2d::CCPoint myPosition; int maxHp; int currentHp; float walkingSpeed; Waypoint *destinationWaypoint; bool active; }; #endif // __ENEMY_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 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 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
#include
"Enemy.h"
using namespace cocos2d; #define HEALTH_BAR_WIDTH 20 #define HEALTH_BAR_ORIGIN - 10 Enemy::Enemy( |