1、前提:html
完成Hello Game项目的建立编译。node
具体参考:Cocos2dx.3x_Hello Game项目建立篇android
2、本篇目标:windows
l 说说关于塔防游戏的想法和思路网络
l 实现一个简单的塔防游戏原型app
3、内容:eclipse
l 说说关于塔防游戏的想法和思路测试
首先上一张塔防游戏PSD设计效果图this
游戏故事设定:spa
这个游戏说是保卫萝卜,但不能真的是保卫萝卜了,由于保卫萝卜的游戏已经有了,只是借用一下这个大名鼎鼎的塔防游戏宣传和参照一下。如今网络上主流游戏都会先讲一下故事让玩家有一种入戏感,那咱们的这个故事是这样的:好久好久之前,在美丽的大学宿舍区住着一群美丽天真的女孩,可是邪恶的色狼大叔们总想对她们作一些坏事,那么咱们的英雄善良勇敢的女生宿舍管理员利用生活中的武器菜刀、皮鞋、玩具飞机等在大叔必经的路上狙击他们,保护女孩们免受这些大叔的伤害。
游戏元素组成:
一、地图:每一关地图均不相同,主要是道路不一样和炮台位的不一样。
二、炮塔:水果刀、菜刀、老鼠药、高跟鞋、玩具飞机等,不一样的炮台具有不一样的价格、攻击速度、攻击属性、攻击方式。
三、子弹:由炮台发射的,具有不一样的攻击值、扩散值、迟缓值、攻击范围值等。
四、怪物:各种猥琐大叔、叫兽、色狼,不一样的色狼具有不一样的速度值、伤害值、耐揍值,沿着地图上的道路不断的靠近道路终点的女主角。
五、女主角:道路终点的女孩,不具有攻击力须要炮塔的保护,具备必定的纯洁值,当纯洁值被大叔玷污光了就自杀了,游戏也就结束了。
6、分数&资源:杀死不一样的色狼能得到必定的分数,分数能够用来购买新的炮台,每一关都会有必定的初始分数用来支撑游戏最初的消耗,每一关的分数只限在本关使用,下一关开启时前面积累的分数清空。
七、宝箱:用分数资源购买宝箱,能有必定概率得到比投入分数几倍的回报。
八、医生:用分数购买医疗,对女主角的纯洁值进行修补。
游戏开发模式:
整个游戏开发的方式是这样,首先实现一个很小的游戏核心原型,而后不断的修改扩大这个游戏原型直至游戏完成为止。本人认为这样的方式比较适合读者理解,而且跟着文章本身学会理解这个游戏的开发。
l 实现一个简单的游戏原型
新建游戏工程名为DefendTheGirl(保卫女孩),包名为:com.game. defendthegirl。若是还不会建立工程请参照:Cocos2dx.3x入门三部曲-Hello Game项目建立(二)篇
本篇原型须要实现内容:
一、 在主场景中载入一张地图。
二、 在地图终点放置一个女主角,在地图的起点放置一个色狼大叔。
三、 让色狼大叔沿着地图指定的路线向女主角的位置靠近。
素材图片准备:
地图图片level_bg_1.png 960px, 640px
女主角图片 girl.png
色狼大叔图片 dashu.png
把这几张素材图片拷贝到文件夹Resources下面便可
1、在主场景中载入一张地图
第一步:
用Microsoft Visual Studio 2012打开proj.win32工程,而后在src下新建MainScene.h、MainScene.cpp做为游戏的主场景(Scene不会建?参考:Cocos2dx.3x入门三部曲-Hello Game项目解析(三)篇)。
第二步:在init()方法里载入地图图片level_bg_1.png,代码以下:
bool MainScene::init() { if ( !Layer::init() ) { return false; } Size visibleSize = Director::getInstance()->getVisibleSize(); Vec2 origin = Director::getInstance()->getVisibleOrigin(); //载入地图背景 auto sprite = Sprite::create("level_bg_1.png"); sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y)); this->addChild(sprite, 0); return true; }
第三步:打开AppDelegate.cpp文件,引入MainScene.h头文件,而且在applicationDidFinishLaunching方法中把auto scene = HelloWorld::createScene(); 改为auto scene = MainScene::createScene();而后运行。
2、在地图终点放置一个女主角,在地图的起点放置一个色狼大叔
第一步:在init()方法里添加以下代码
//载入地图背景 …… auto sprite = Sprite::create("level_bg_1.png"); sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y)); this->addChild(sprite, 0); //在地图起点处放置一个色狼 auto dsSprite = Sprite::create("dashu.png"); dsSprite->setPosition(Vec2(40, 390)); this->addChild(dsSprite, 0); //在地图终点处放置一个女主角 auto nhSprite = Sprite::create("girl.png"); nhSprite->setPosition(Vec2(920, 480)); this->addChild(nhSprite, 0); …...
第二步:而后运行就能够在画面上看到色狼大叔和女主角了
一、 让色狼大叔沿着地图指定的路线向女主角的位置靠近
这个一个有点难度的任务,首先咱们对这张地图的道路路径进行一下坐标分析:
如上图所示,以地图的右下顶点为坐标系原点,整个道路分红12个坐标点,女主角在1号坐标点,色狼大叔在12号坐标点。如今色狼大叔将沿着图中黄色的线路从12点开始 11点、10点、9点…直至到达1点,个人实现思路是这样,色狼从12点出发时告诉它目标点是11点,当色狼到达11点的时候继续告诉它下一个目标点是10点直到1点。
几个实现技术点:
一、 对Sprite(色狼)进行setPosition的方式能够改变Sprite在图上的位置从而实现Sprite的移动
二、 计算Sprite(色狼)位置到目标点的向量值,而后根据这个向量值和色狼移动速度计算Sprite在x,y方向上的距离偏移值,用这个偏移值调整Sprite的位置
判断Sprite(色狼)到达目标点(如:11点),以下图所示经过Sprite坐标点和目标点之间的距离小于必定值的时就断定为到达目标点,须要设置新的下一个目标点。
有了这些实现思路,如今开始代码编写:
第一步:由上面是实现思路可知咱们的基本实现是路径点,那么这里先新建一个路径点类对象,名称为:Waypoint.h、Waypoint.cpp,继承自:cocos2d::CCNode。
Waypoint.h:
class Waypoint: public cocos2d::CCNode { public: Waypoint(void); ~Waypoint(void); //初始化方法 static Waypoint* nodeWithTheLocation(cocos2d::Point location); bool initWithTheLocation(cocos2d::Point location); //设置当前点的下一个路径点 void setNextWaypoint(Waypoint* waypoint); //获取当前点的下一个路径点 Waypoint* getNextWaypoint(); //当前路径点位置 CC_SYNTHESIZE(cocos2d::Point,_myPosition,MyPosition); private: //下一个路径点 Waypoint* _nextWaypoint; }; Waypoint.cpp: Waypoint::Waypoint(void) { _nextWaypoint=NULL; } Waypoint::~Waypoint(void) { } Waypoint* Waypoint::nodeWithTheLocation(cocos2d::Point location) { Waypoint* pRet=new Waypoint(); if (pRet && pRet->initWithTheLocation(location)) { pRet->autorelease(); return pRet; } else { delete pRet; pRet=NULL; return NULL; } } bool Waypoint::initWithTheLocation(cocos2d::Point location) { bool bRet=false; do { _myPosition=location; this->setPosition(Point::ZERO); bRet=true; } while (0); return bRet; } void Waypoint::setNextWaypoint(Waypoint* waypoint) { _nextWaypoint=waypoint; } Waypoint* Waypoint::getNextWaypoint() { return _nextWaypoint; }
第二步:在MainScene.h里声明以下代码
public: …… //重写Layer的update方法 //咱们主要在这个方法里实现色狼移动 virtual void update(float delta); CREATE_FUNC(MainScene); private: //路径开始点 Waypoint *beginningWaypoint; //路径目标点 Waypoint *destinationWaypoint; //色狼的移动速度 float walkingSpeed; //色狼当前位置 cocos2d::Vec2 myPosition; //色狼大叔 cocos2d::Sprite* dsSprite; //路径点集合 cocos2d::Vector<Waypoint*> wayPositions; //判断2个点是否靠近 bool collisionWithCircle(cocos2d::Vec2 circlePoint,float radius,cocos2d::Vec2 circlePointTwo, float radiusTwo);
这里代码,也对以前代码进行了修改重构,好比把以前在init()方法里声明的dsSprite(色狼)改到了这里变成了一个全局变量,由于在后续的update方法中须要对它进行操做。
第二步:在MainScene.cpp的init()方法中建立路径点集合编写以下代码
……
//得到色狼大叔的高
float dsh=dsSprite->getTextureRect().size.height;
//初始化地图路径点集合 this->wayPositions = Vector<Waypoint*>(); //添加地图1号路径点到集合中 Waypoint *waypoint1=Waypoint::nodeWithTheLocation(Point(920, 435+dsh/2.0f)); if(this->wayPositions.size()>0) { //设置下一个节点 waypoint1->setNextWaypoint(this->wayPositions.back()); } this->wayPositions.pushBack(waypoint1); //添加地图2号路径点到集合中 Waypoint *waypoint2=Waypoint::nodeWithTheLocation(Point(762, 435+dsh/2.0f)); if(this->wayPositions.size()>0) { waypoint2->setNextWaypoint(this->wayPositions.back()); } this->wayPositions.pushBack(waypoint2); //添加地图3号路径点到集合中 Waypoint *waypoint3=Waypoint::nodeWithTheLocation(Point(762, 360+dsh/2.0f)); if(this->wayPositions.size()>0) { waypoint3->setNextWaypoint(this->wayPositions.back()); } this->wayPositions.pushBack(waypoint3); //添加地图4号路径点到集合中 Waypoint *waypoint4=Waypoint::nodeWithTheLocation(Point(685, 360+dsh/2.0f)); if(this->wayPositions.size()>0) { waypoint4->setNextWaypoint(this->wayPositions.back()); } this->wayPositions.pushBack(waypoint4); //添加地图5号路径点到集合中 Waypoint *waypoint5=Waypoint::nodeWithTheLocation(Point(685, 116+dsh/2.0f)); if(this->wayPositions.size()>0) { waypoint5->setNextWaypoint(this->wayPositions.back()); } this->wayPositions.pushBack(waypoint5); //添加地图6号路径点到集合中 Waypoint *waypoint6=Waypoint::nodeWithTheLocation(Point(520, 116+dsh/2.0f)); if(this->wayPositions.size()>0) { waypoint6->setNextWaypoint(this->wayPositions.back()); } this->wayPositions.pushBack(waypoint6); //添加地图7号路径点到集合中 Waypoint *waypoint7=Waypoint::nodeWithTheLocation(Point(520, 180+dsh/2.0f)); if(this->wayPositions.size()>0) { waypoint7->setNextWaypoint(this->wayPositions.back()); } this->wayPositions.pushBack(waypoint7); //添加地图8号路径点到集合中 Waypoint *waypoint8=Waypoint::nodeWithTheLocation(Point(285, 180+dsh/2.0f)); if(this->wayPositions.size()>0) { waypoint8->setNextWaypoint(this->wayPositions.back()); } this->wayPositions.pushBack(waypoint8); //添加地图9号路径点到集合中 Waypoint *waypoint9=Waypoint::nodeWithTheLocation(Point(285, 268+dsh/2.0f)); if(this->wayPositions.size()>0) { waypoint9->setNextWaypoint(this->wayPositions.back()); } this->wayPositions.pushBack(waypoint9); //添加地图10号路径点到集合中 Waypoint *waypoint10=Waypoint::nodeWithTheLocation(Point(204, 268+dsh/2.0f)); if(this->wayPositions.size()>0) { waypoint10->setNextWaypoint(this->wayPositions.back()); } this->wayPositions.pushBack(waypoint10); //添加地图11号路径点到集合中 Waypoint *waypoint11=Waypoint::nodeWithTheLocation(Point(204, 350+dsh/2.0f)); if(this->wayPositions.size()>0) { waypoint11->setNextWaypoint(this->wayPositions.back()); } this->wayPositions.pushBack(waypoint11); //添加地图12号路径点到集合中 Waypoint *waypoint12=Waypoint::nodeWithTheLocation(Point(50, 350+dsh/2.0f)); if(this->wayPositions.size()>0) { waypoint12->setNextWaypoint(this->wayPositions.back()); } this->wayPositions.pushBack(waypoint12); ……
第三步:在MainScene.cpp的init()方法中初始化几个变量以及精灵的初始位置编写以下代码
…… //获取集合中的最后一个点,12号点 Waypoint *waypoint0=wayPositions.back(); //设置运动的开始点 beginningWaypoint=waypoint0; //设置运动的目标点为12号点的下一个点,11号点 destinationWaypoint=waypoint0->getNextWaypoint(); //设置色狼当前位置值 myPosition=waypoint0->getMyPosition(); //设置色狼在地图的初始位置 dsSprite->setPosition(myPosition); //设置女主角在地图的初始位置,为集合中的1号点 nhSprite->setPosition(wayPositions.front()->getMyPosition()); //设置移动速度 this->walkingSpeed=0.2f; //定时器 this->scheduleUpdate(); ……
第四步:在MainScene.cpp中对collisionWithCircle方法进行实现编写以下代码
//判断2个圆点是否靠近 //circlePoint:第一个圆点坐标 //radius:第一个圆半径 //circlePointTwo:第二个圆点坐标 //radiusTwo:第二个圆半径 bool MainScene::collisionWithCircle(cocos2d::Vec2 circlePoint,float radius,cocos2d::Vec2 circlePointTwo, float radiusTwo) { //2点间距离公式计算 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; }
第五步:在MainScene.cpp中对update方法进行实现:
//判断色狼大叔是否和目标点碰到 if (this->collisionWithCircle(myPosition,1,destinationWaypoint->getMyPosition(),1) ) { //是否还有下一个目标点 if (destinationWaypoint->getNextWaypoint()) { //从新设定开始点和目标点 beginningWaypoint=destinationWaypoint; destinationWaypoint=destinationWaypoint->getNextWaypoint(); } } //获取目标点的坐标 Point targetPoint=destinationWaypoint->getMyPosition(); //计算目标点的向量 Point normalized=Point(targetPoint.x-myPosition.x,targetPoint.y-myPosition.y).getNormalized(); //根据速度和向量分别计算x,y方式上的偏移值 float ox=normalized.x * walkingSpeed; float oy=normalized.y *walkingSpeed; myPosition = Point(myPosition.x + ox, myPosition.y +oy); //从新设定色狼的位置实现移动 dsSprite->setPosition(myPosition);
第六步:运行测试游戏效果,看看色狼会不会沿着咱们设定的路径移动。
Windows下的效果
在看看android手机下的效果,把新加的2个cpp文件添加到Android.mk文件里而后开始编译打包so文件(不会请参考:Cocos2dx.3x入门三部曲-Hello Game项目解析(三)篇)。完成打包后在eclipse中链接手机运行看到以下效果:
发如今真机上运行时女主角、色狼的位置至关于道路都有点偏上了,而且好像背景地图也没有显示全背景的顶部和底部有一部分没有显示出来,可是在windows下运行确正常,这个是什么缘由呢,该怎么调整呢?个人手机分辨率是:960x540 而咱们的地图素材图片分辨率是:960x640,因此致使了这个问题,这个是关于不一样手机屏幕分辨率适配问题,在下一篇中咱们将继续的完善修改这个游戏原型解决这个问题。
做者交流QQ:2303452599
邮箱:mymoney1001@126.com