上次给大家介绍了GameMenu类和GameManager类,这次是个大家介绍GameScene类
游戏下载链接:http://download.csdn.net/detail/a402813378/6275427
GameScene类是生成游戏窗口的类。游戏窗口占据了这个Demo中大部分的代码,下面我们来逐一介绍它。
首先来看看类的头文件。这个类的头文件和前面讲的类的头文件基本一致,GameScene类继承了CCLayer,只有一个初始化的方法和一个返回CCScene对象的方法,看起来非常简单。充分表现了面向对象的设计方法的简洁性.下面我们来看看头文件的代码。
如下:
#ifndef _GAME_SCENE_H
#define _GAME_SCENE_H
#include"cocos2d.h"
class GameScene: public cocos2d::CCLayer
{
public:
bool init();
static cocos2d::CCScene *Scene();
CREATE_FUNC(GameScene);
};
#endif
CREATE_FUNC(GameScene)是一个回调函数,上一篇博文已经讲过,这个还是比较好懂的。
下面我们来看看这个类的cpp文件代码如下,我会很努力的为大家进行解释,大家也不用担心很难。在这个游戏窗口类里,它把游戏的背景和游戏的精灵进行分开初始化了,我觉得这个还是降低了程序的复杂性。
#include"GameScene.h"
#include"GameSceneBgLayer.h" //游戏窗口的背景初始哈类
#include"GameScenePlayerLayer.h"//游戏窗口的精灵初始化类,里面有玩家,炸弹,按钮,方向盘,子弹等
#include"GlobalParam.h" //这个类里包含了所有要用的文件的文件名。
#include"cocos2d.h"
using namespace cocos2d;
CCScene * GameScene::Scene()
{
CCScene *scene =NULL ;
do
{
scene = CCScene::create();
CC_BREAK_IF(!scene);
GameScene* layer = GameScene::create();
//自动运行init()函数进行初始化,我理解这里应该是继承CCLayer的作用,上一篇有讲到。
CC_BREAK_IF(! layer);
scene->addChild(layer);
}while(0);
CCLOG("%s", "create GameScene");
return scene;
}
bool GameScene::init()
{
bool bRet = false;
do
{
CCLOG("GameScene::init");
/* 初始化背景 */
GameSceneBgLayer* sceneBgLayer =GameSceneBgLayer::create();//返回一个CCScene对象
CC_BREAK_IF(! sceneBgLayer);
this->addChild(sceneBgLayer);
/* 初始化所有的精灵*/
GameScenePlayerLayer *scenePlayerLayer =GameScenePlayerLayer::create();
CC_BREAK_IF(! scenePlayerLayer);
this->addChild(scenePlayerLayer);
bRet = true;
}while(0);
return bRet;
}
GameScene中主要是进行了背景类和精灵类的初始化并把他们生成的布景加到窗口中去。
下面继续讲解GameSceneBgLayer类,这个类主要是负责游戏画面背景的初始化。
GameSceneBgPlayer中主要是一个初始化函数和一个Update更新坐标的函数,其中初始化函数是初始化两个背景精灵,Update是改变背景的中心点的坐标以达到动态背景的真实的效果,学过DX的同学在这个地方应该很好理解,这个和D3D里实现的动态天空盒子是一个原理。
下面晒出这个GameSceneBgLayer类的头文件,我已经注释了。
GameSceneBgLayer.h
#ifndef __GAME_SCENE_BG_LAYER_H__
#define __GAME_SCENE_BG_LAYER_H__
#include "cocos2d.h"
class GameSceneBgLayer : public cocos2d::CCLayer
{
public:
bool init(); //初始化背景1和背景2
CREATE_FUNC(GameSceneBgLayer);
virtual void update(float dt); //改变背景一和背景2的坐标以达到动态的效果
private:
cocos2d::CCSprite * bgSprite1; /*背景1精灵对象*/
cocos2d::CCSprite * bgSprite2; /*背景2精灵对象*/
int movespeed ; //坐标变换的速度 */
int bgX1 ; //背景1中心点的X坐标 ;
int bgY1 ; //背景1中心点的Y坐标;
int bgX2 ; //背景2中心点的的X坐标 ;
int bgY2 ; //背景2中心点的Y坐标 ;
};
#endif
大家基本上看懂了上面的声明的话下面我们来看这个类的cpp文件,我也进行了注释,大家应该比较好理解。
GameSceneBgPlayer.cpp
#include"GameSceneBgLayer.h"
#include"GlobalParam.h" //这个头文件保留了所有要用的文件的名字
#include"XSystem.h" //这个头文件的功能是将屏幕坐标转换成图片的真实坐标
using namespace cocos2d; //这个是用cocos2d为我们创建的环境
bool GameSceneBgLayer::init()
{
bgSprite1; /*背景1*/ //这两个变量的声明是在头文件里,在cocos2d-x里有很多这样的声明方式
bgSprite2; /*背景2*/ //为什么要两张图呢?是为后面的背景图片的滑动做铺垫,做出动态的背景效果
movespeed;
bool bRet =false ;
do
{
movespeed = 3;
/* 和MFC中的UpdateWindow()是一样的功能,就是刷新屏幕重绘而已。
有了这句,每帧屏幕都会进行刷新,然后实现了窗口中物体的运动。
*/
this->scheduleUpdate();
/*背景1的初始化*/
bgSprite1 = CCSprite::createWithSpriteFrameName(IMG_BACKGROUND);
this->addChild(bgSprite1);
/*背景2的初始化*/
bgSprite2 = CCSprite::createWithSpriteFrameName(IMG_BACKGROUND);
bgSprite2->setScaleX(-1); //这个是图片缩放函数,参数-1我在网上没有查到用法,我想的就是把图片从又往左翻转一次
this->addChild(bgSprite2);
CCSize visible =CCDirector::sharedDirector()->getVisibleSize(); //这个是获取屏幕的分辨率的大小
CCPoint orign =CCDirector::sharedDirector()->getVisibleOrigin(); //这个是获取屏幕在背景图片中绘制的起点位置
/*初始化背景1坐标*/
bgX1 =visible.width/2;
bgY1 =visible.height/2;
//其实在这里整个屏幕就已经被背景1填充满了
bgSprite1->setPosition(XSystem::xccp(bgX1,bgY1)); //将屏幕坐标转换成背景图的实际坐标
CCSize sprite1size =bgSprite1->getContentSize();
bgX2 =bgX1 +sprite1size.width; //这个坐标是为了将两个图拼到一起
bgY2 =bgY1;
bgSprite2->setPosition(XSystem::xccp(bgX2,bgY2)); //将屏幕坐标转换成背景图的实际坐标
bRet =true; //前面了所有的完成了才会为true,我认为这个是为了调试的方便而建立的一个变量
}while(0);
return bRet;
}
void GameSceneBgLayer::update(float dt)
{
CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
//每一帧x左移,显现出动画的效果,其实也可以用到上面的dt。dt是cocos2d-x为我们建立的一个宏,里面有从上次刷新窗口到这次所花的时间 bgX1 -=movespeed ;
bgX2 -=movespeed ;
CCSize bgSprite1Size =bgSprite1->getContentSize();
CCSize bgSprite2Size =bgSprite2->getContentSize();
//这个时候其实bg2已经填充完整个屏幕,例如初始的时候的bg1一样,所以要把bg1放到初始的时候的bg2的位置来进行循环运动
if(bgX1+ bgSprite1Size.width/2 <=0)
bgX1 = bgX2 + bgSprite2Size.width;
if(bgX2 +bgSprite2Size.width/2 <=0)
bgX2 = bgX1 + bgSprite1Size.width;
//这个地方因为是从图片中获取,所以要用xccp()对坐标进行处理
bgSprite1->setPosition(XSystem::xccp(bgX1,bgY1));
bgSprite2->setPosition(XSystem::xccp(bgX2,bgY2));
}
背景类的实现别看代码那么多,实际上就一句话,每一帧改变背景图的坐标,仅此而已,大家先仔细看懂上面的,看懂了再继续往下。
先前我们在GameScene类中讲到了。 游戏的初始化话是分为初始化背景和初始化精灵的。上面所有的初始化函数如下
/* 初始化所有的精灵*/
GameScenePlayerLayer *scenePlayerLayer =GameScenePlayerLayer::create();
CC_BREAK_IF(! scenePlayerLayer);
this->addChild(scenePlayerLayer);
下面我们来讲讲GameScenePlayerLayer类,这个类是最复杂的一个类,不过我们一步步的跟着程序走也就不是很难了,我会尽力给大家讲清楚减轻大家的理解难度。
GameScenePlayerLayer类继承了CCLayer类和CollisionListener类,其中CCLayer类是cocos2d-x中的布景类,这里大家都知道就不做讲解,CollisionListener类只是一个头文件中定义的一个类,里面只有一个虚函数CollisionDetected()的声明。这个函数的定义是在CollisionListener类,CollisionDetected()函数就只有一个操作,当炸弹炸到了玩家的时候会调用这个函数,将窗口转换到GameOver窗口。继承CollisionListener类的作用就是当游戏结束的瞬间虚函数CollisionDetected函数会被执行转换到GameOver窗口。
和前面一样下面来看看GameScenePlayerLayer的头文件
GameScenePlayerLayer.h
#ifndef __GAME_SCENE_PLAYER_LAYER_H__
#define __GAME_SCENE_PLAYER_LAYER_H__
#include "cocos2d.h"
#include "CollisionListener.h"
class GameScenePlayerLayer : public cocos2d::CCLayer,public CollisionListener
{
public:
bool init(); //初始化
CREATE_FUNC(GameScenePlayerLayer); //调用回调函数必须要这个
virtual void CollisionDetected();
};
#endif
头文件中只包含了初始化函数和一个回调函数以及CollisionDectected函数,其中Init()函数主要是初始化怪物,玩家,炸弹等等
看看GameScenePlayerLayer.cpp文件
#include "GameScenePlayerLayer.h"
#include "GameManager.h" //这个是前面说明过的游戏窗口管理类
#include "GlobalParam.h"
#include "MonsterManager.h" //这个是怪物生成的类
#include "Player.h" //这个是玩家生成的类
using namespace cocos2d;
bool GameScenePlayerLayer::init()
{
bool bRet = false;
do
{
//混合图片的纹理,不懂的可以看我这个游戏开发前面的一篇博文中有这个的介绍
CCTexture2D* texture = CCTextureCache::sharedTextureCache()->textureForKey(IMG_FILE_NAME);
//纹理放在这个CCSpriteBatchNode里面了
CCSpriteBatchNode *batchNode = CCSpriteBatchNode::createWithTexture(texture);
this->addChild(batchNode);
/*创建玩家*/
Player* player = Player::createWithSpriteBatchNode(batchNode);
CC_BREAK_IF(! player);
this->addChild(player);
/*创建怪物管理器*/
MonsterManager* monsterManager = MonsterManager::createWithBatchNode(batchNode);
CC_BREAK_IF(! monsterManager);
monsterManager->setAtkTarget(player); //目标是玩家,player
monsterManager->setCollisionListener(this); //这里的this只整个游戏窗口中的所有精灵,里面包括了人,子弹,大家从这里再去看继承类
this->addChild(monsterManager);
/*创建子弹发射器(子弹发射器)*/
BulletManager* mBulletManager = BulletManager::createWithBatchNode(batchNode);
CC_BREAK_IF(! mBulletManager);
player->setShooter(mBulletManager);
mBulletManager->setBulletListener(monsterManager); //只对怪物进行监视
this->addChild(mBulletManager);
bRet = true ;
}while(0);
return bRet;
}
//结束的时候调用,这个函数声明是在CollisionListener中的,调用是在MonsterManager中的Update中
void GameScenePlayerLayer::CollisionDetected()
{
GameManager::sharedGameManager()->runSceneWithId(GameManager::SCENE_ID_GAME_OVER);
}
上面有关于三个精灵类的初始化,很复杂,我们来一个个的开始看。从第一个Player类开始。
Player是玩家类,主要负责玩家角色的初始化和控制器的设定以及按钮的实现
Player类继承与Entity类,对于Entity类,看过上一篇的那个类的继承图就可以知道,Entity类中继承了Collidable类和I_ControllerListener类,Player类继承了Entity类相当于也继承了这两个监控器
Player的头文件如下
#ifndef __PLAYER_H__
#define __PLAYER_H__
#include "Entity.h"
#include "cocos2d.h"
#include "BulletManager.h" //这个跟子弹有关
using namespace cocos2d;
class Player :public Entity
{
public:
bool init(CCSpriteBatchNode *batchNode);
static Player *createWithSpriteBatchNode(CCSpriteBatchNode *batch);
void setShooter(Shooter *mShooter); //设置下面的成员变量
virtual void fire(); //射击
private:
Shooter *mShooter; //射击对象,其实后面只是一个BulletManager类对象
};
#endif
player头文件没看到后面的比较难讲,我们继续看player的cpp文件,后面看到这些我都会一一解答这样比较容易懂点。
player.cpp文件
#include "Player.h"
#include "GlobalParam.h"
#include "XSystem.h"
#include "PlayerJoystickController.h" //这个是一个摇动牌
using namespace cocos2d;
//初始化
bool Player::init( CCSpriteBatchNode *batchNode )
{
bool bRet =false;
do
{
CCSprite *player = CCSprite::createWithSpriteFrameName(IMG_BEETLE_SHIP); //初始化一个人物对象
CC_BREAK_IF(!player);
player->setScaleX(0.5);//x方向图片缩小一半
player->setScaleY(0.5);//y方向图片缩小一半
player->setPosition(XSystem::xccp(100,160));
batchNode->addChild(player);
this->setVisual(player);
/*创建玩家摇杆移动控制器*/
PlayerJoystickController* mController = PlayerJoystickController::createWithBatchNode(batchNode);
this->setController(mController); //后面老是出现的this->listener就是在这里定义的。大家要引起重视
bRet = true;
}while(0);
return bRet;
}
//创建一个player对象
Player * Player::createWithSpriteBatchNode(CCSpriteBatchNode *batchNode)
{
Player *player = new Player();
if(player && player->init(batchNode))
{
return player;
}
else
{
CC_SAFE_DELETE(player);
return NULL;
}
}
void Player::fire() //放子弹的函数
{
if(mShooter)
{
CCPoint pos =GetPosition(); //位置
CCSize playerSize = Entity::getSprite()->getContentSize(); //精灵的大小
pos.x += playerSize.width / 2;
mShooter->shoot(pos);
}
}
void Player::setShooter(Shooter * mShooter ) //这个类的初始化是BulletManager中,然后BulletManager继承了这个类
{
this->mShooter = mShooter;
}
Player主要是包含一个初始化函数,一个放子弹的函数,一个设置射击对象的函数. 后两个是虚函数声明,所以在每次刷新函数的时候如果满足了监听的条件话都会调用。
Init()函数是这个类的主要的代码。下面我们来专门说明这个
先单独贴出Init()代码
bool Player::init( CCSpriteBatchNode *batchNode )
{
bool bRet =false;
do
{
CCSprite *player = CCSprite::createWithSpriteFrameName(IMG_BEETLE_SHIP); //初始化一个人物对象
CC_BREAK_IF(!player);
player->setScaleX(0.5);//x方向图片缩小一半
player->setScaleY(0.5);//y方向图片缩小一半
player->setPosition(XSystem::xccp(100,160));
batchNode->addChild(player);
this->setVisual(player);
/*创建玩家摇杆移动控制器*/
PlayerJoystickController* mController = PlayerJoystickController::createWithBatchNode(batchNode);
this->setController(mController);
bRet = true;
}while(0);
return bRet;
}
this->setVisual(player); //这个是初始化精灵,只有这个,后面才能够去改变精灵的位移.
/*创建玩家摇杆移动控制器,这个类里包含有一个按钮和一个方向盘以及处理函数*/
PlayerJoystickController* mController = PlayerJoystickController::createWithBatchNode(batchNode);
this->setController(mController); //玩家系统中添加控制器,里面包含有移动和放子弹的操作,有这步操作杆才能对玩家进行操作.
PlayerJoystickController类是我们自定义的一个操作类,用来操作玩家的移动和射击的行为
一定要记得虚函数是每帧都会运行的函数,player类设置了控制器,所以每帧系统就会对玩家的事件进行操作
把这个类介绍了一下我们就开始看下类的实现原理
这次直接看PlayerJoystickController的cpp文件
#include"PlayerJoystickController.h"
#include"Controller.h"
#include"SneakyInput\SneakyJoystickSkinnedBase.h" //操作杆底座
#include"SneakyInput\SneakyJoystick.h" //操作杆
#include"SneakyInput\SneakyButtonSkinnedBase.h" //按钮生成需要它
#include"SneakyInput\SneakyButton.h" //按钮生成需要它
#include"cocos2d.h"
#include"GlobalParam.h"
#include"XSystem.h"
using namespace cocos2d;
bool PlayerJoystickController::init(CCSpriteBatchNode *batchNode)
{
bool bRet =false;
do
{
CCLOG("PlayerJoystickController::init");
CC_BREAK_IF(!Controller::init());
/*创建摇杆*/
/*要在遊戏里加上一个 操控杆, 首先我们要準备一个底座和一个操控杆的图像, 接下来,
先要建立一个 SneakyJoystickSkinnedBase: */
SneakyJoystickSkinnedBase* joystickBase = new SneakyJoystickSkinnedBase();
CC_BREAK_IF(! joystickBase);
joystickBase->autorelease();
joystickBase->init();
joystickBase->setBackgroundSprite(CCSprite::createWithSpriteFrameName(IMG_CIRCLE_BIG));
joystickBase->setThumbSprite(CCSprite::createWithSpriteFrameName(IMG_CIRCLE_SMALL));
/*然後我们要建立一个SneakyJoystick 并设置到 SneakyJoystickSkinnedBase 里*/
joystick = new SneakyJoystick();
joystick->autorelease();
joystick->initWithRect(CCRectMake(0, 0, 100, 100));
joystickBase->setJoystick(joystick);
joystickBase->setPosition(XSystem::xccp(80, 80));
batchNode->getParent()->addChild(joystickBase);
/*游戏按钮*/
SneakyButtonSkinnedBase *buttonBase = new SneakyButtonSkinnedBase();
buttonBase->autorelease();
buttonBase->init();
buttonBase->setDefaultSprite(CCSprite::spriteWithSpriteFrameName("buttonBlue.png"));
buttonBase->setActivatedSprite(CCSprite::spriteWithSpriteFrameName("buttonOrange.png"));
buttonBase->setPressSprite(CCSprite::spriteWithSpriteFrameName("buttonOrange.png"));
button = new SneakyButton();
CC_BREAK_IF(!button);
button->autorelease();
button->initWithRect(CCRectMake(0,0,64,64)); //标示按钮的区域的大小,并不是位置!
button->setIsToggleable(false);
button->setIsHoldable(true);
buttonBase->setButton(button);
buttonBase->setPosition(XSystem::xccp(480 - 100, 100));
batchNode->getParent()->addChild(buttonBase);
isBtnDown = false;
bRet = true;
}while(0);
return bRet;
}
PlayerJoystickController* PlayerJoystickController::createWithBatchNode(CCSpriteBatchNode* batchNode)
{
PlayerJoystickController* mController = new PlayerJoystickController();
if(mController && mController->init(batchNode)) {
return mController;
}
else {
CC_SAFE_DELETE(mController);
return NULL;
}
}
void PlayerJoystickController::update(float dt)
{
if(this->listener==NULL) //有前面的player的setController才能做这步操作
return;
/* joystick->getVelocity()取得摇杆移动的距离,乘于一个因数,得到玩家应该移动的距离 */
CCPoint pos = ccpMult(joystick->getVelocity(), SCREEN_DEFAULT_HEIGHT / 2);
//dt是系数,pos.x,pos,y是移动的距离
this->listener->updatePosition(dt, pos.x, pos.y);
if (button->getIsActive())
{
//这个和MFC的WM_LBUTTONDOWN消息一样
if(isBtnDown == false) {
listener->fire(); //放子弹的函数
isBtnDown = true;
}
}
else {
//这个和MFC的WM_LBUTTONUP消息一样
isBtnDown = false;
}
}
上面的前两个函数是初始化的函数,最下面的update是处理操作杆和按钮的函数,因为是虚函数,所以每一帧都会到这个函数里。这个类里用到的操作杆类和按钮类是从网上找到别人已经封装好的类,大家最好把这个保存下来,以后应该也会有用到。
从这里大家应该已经知道player函数的组成原理了,它由一个玩家精灵和一个操作类组成。操作类控制玩家的移动和子弹的射击,至此第一个Player类已经讲解完了。大家可能还有一些迷糊的地方,可以接下来看源码,自己研究一下,应该都没问题了。~~~我都感觉我的表达能力好差的,囧。
下面再讲怪兽炸弹类和子弹类。
我先给大家讲讲炸弹类的原理,炸弹类的原理就是在初始化的时候一次性把所有的炸弹都初始化,设置Isalive为false,然后每隔两秒来显示出来一个,并且更新其他已经输出出来的炸弹的坐标(炸弹会自动往左走)。
子弹类和炸弹类差不多。先创出所有的子弹,设置alive为flase,然后再每次点击按钮就放出一个。并且更新其他已经输出出来的炸弹的坐标,当炸弹和子弹collision(撞击)的时候就会两两消失,将他们的alive设置为false即可,基本原理就是这样。
现在来看看实际的代码.先看怪物炸弹的类的使用
/*创建怪物管理器*/
MonsterManager* monsterManager = MonsterManager::createWithBatchNode(batchNode);
CC_BREAK_IF(! monsterManager);
monsterManager->setAtkTarget(player); //设置目标是玩家
monsterManager->setCollisionListener(this); //设置碰撞监听器
this->addChild(monsterManager);
这里面的setAtkTarge=(player)是设置玩家的对象,因为后面要测试炸弹是否打中玩家,所以需要这个。
monsterManager->setCollisionListener(this);
继续我们的层层剥皮~~,先来讲MonsterManager类。
这个类是管理生成炸弹的类,负责炸弹的生成并且负责碰撞的检测
注意有两个虚函数,先记着等下讲
virtual void update(float dt);
virtual bool collidedWithBullet(Collidable *bullet);
代码如下
MonsterManager.cpp
//怪物管理类
#include"MonsterManager.h"
#include"cocos2d.h"
#include"Monster.h"
using namespace cocos2d;
MonsterManager * MonsterManager::createWithBatchNode(cocos2d::CCSpriteBatchNode *batchNode)
{
CCLOG("%s","MonsterManager::createWithBatchNode");
MonsterManager *monsterManager = new MonsterManager();
if(monsterManager &&monsterManager->init(batchNode))
{
return monsterManager;
}
else
{
CC_SAFE_DELETE(monsterManager);
return NULL;
}
}
bool MonsterManager::init(cocos2d::CCSpriteBatchNode *batchNode)
{
bool bRet =false;
do
{
dtTime =0;
this->batchNode =batchNode;
/*创建怪物*/
mMonsterArray =CCArray::createWithCapacity(MAX_NUM); //20
mMonsterArray->retain(); //防止内存被系统销毁
Monster *mMonster =NULL;
for(int i=0;i< MAX_NUM ;i++)
{
mMonster =Monster::createWithBatchNode(batchNode);
mMonster->setAlive(false);
mMonsterArray->addObject(mMonster);
batchNode->getParent()->addChild(mMonster);
//父窗口我理解的是CCDirector的那个窗口。
}
this->scheduleUpdate();
bRet =true;
}while(0);
return bRet;
}
void MonsterManager::update(float dt)
{
/*碰撞玩家检测*/
if(mAtkTarget) //if 玩家存在
{
CCObject *obj =NULL;
CCARRAY_FOREACH( mMonsterArray,obj) //遍历
{
Monster *mMonster =(Monster *)obj;
if(mMonster && mMonster->getIsAlive() && mMonster->collidedWith(mAtkTarget))
{ //如果打中了玩家,则做游戏结束的操作
mCollisionListener->CollisionDetected();
break;
}
}
}
//2秒钟会刷新一个新怪物
dtTime +=dt;
if(dtTime getParent();
/* 定时产生怪物 ,只产生一个*/
Monster* monster = NULL;
for(int i = 0; i < MAX_NUM; i++)
{
monster = (Monster*)mMonsterArray->objectAtIndex(i);
if(monster && monster->getIsAlive() == false) {
monster->resetPosition();
monster->setAlive(true);
break;
}
}
}
MonsterManager::~MonsterManager()
{
CC_SAFE_RELEASE(mMonsterArray);
}
bool MonsterManager::collidedWithBullet( Collidable* bullet )
{
CCObject* obj = NULL;
CCARRAY_FOREACH(mMonsterArray, obj) {
Monster* mMonster = (Monster*) obj;
//如果子弹打到了炸弹
if(mMonster && mMonster->getIsAlive() && mMonster->collidedWith(bullet)) {
mMonster->setAlive(false);
return true;
}
}
return false;
}
//设置目标玩家
void MonsterManager::setAtkTarget( Collidable* mAtkTarget )
{
this->mAtkTarget = mAtkTarget;
}
//设置撞击监听器
void MonsterManager::setCollisionListener( CollisionListener* mCollisionListener )
{
this->mCollisionListener = mCollisionListener;
}
这里的大家看过前面的应该都很好理解。关于上面的两个虚函数,其中有一个是检验炸弹是否打中子弹的,这个比较简单就不说了,另外一个比较特殊,在这里给大家讲个新知识点。先看代码
void MonsterManager::update(float dt)
细心的朋友肯定会疑惑这个dt,因为我们通过vs可以发现,我们写的所有代码里都没有dt这个参数,那么dt是怎么来的呢? 在这里我来解释,dt是cocos2d-x为我们建立的一个宏,里面记载了从一帧到下一帧刷新所花的时间。我们不需要自己定义,好吧大家懂了。
讲到这里其实游戏的大体框架已经差不多了,我也只准备讲下面的Monster类和里面的SimpleMove类了。至于bullet子弹类和Monster类基本上实现方法一模一样。只不过在simpleMove类中一个设置为速度正数一个设置为速度负数,一个往左一个往右,仅此而已。
接下来讲Monster类,不用看大家也能猜到这里面一定是初始化精灵的代码啦。对,其实真的就是这样。。
看了这么久大家也有点视觉疲劳了,其实我也打字打的手酸了。偷个小懒,直接看Monster代码好了。
#include "Monster.h"
#include "GlobalParam.h"
#include "XSystem.h"
#include "SimpleMoveController.h" //这个是运动的类
#include "Controller.h"
Monster *Monster::createWithBatchNode(cocos2d::CCSpriteBatchNode *batchNode)
{
Monster *monster =new Monster();
if(monster &&monster->init(batchNode))
{
return monster;
}
else
{
CC_SAFE_DELETE(monster);
return NULL;
}
}
bool Monster::init(cocos2d::CCSpriteBatchNode *batchNode)
{
bool bRet =false;
do
{
cocos2d::CCSprite *sprite =cocos2d::CCSprite::createWithSpriteFrameName(IMG_ENEMY_BUG);
CC_BREAK_IF(!sprite);
cocos2d::CCSize monsterSize =sprite->getContentSize();
int ranY =(int)(CCRANDOM_0_1() *(320 - monsterSize.height )) +monsterSize.height/2;
sprite->setPosition(XSystem::xccp(480 - monsterSize.width * 2, ranY));
//sprite->setPosition(XSystem::xccp(270, 100));
sprite->setScaleX(0.5); //我设置的屏幕比较大所以我就把精灵缩小了点
sprite->setScaleY(0.5);
// sprite->setScaleX(-1);
batchNode->addChild(sprite);
setVisual(sprite);//想要让精灵运动这个是必须的,没这个update函数是动不起来的
SimpleMoveController* controller = SimpleMoveController::create();
controller->setiXSpeed(-30); //负数就是往左走咯。正数就是往右走,所以SimpleMoveController类对子弹和炸弹全部通用
this->setController(controller);//set controller(控制) 对象
bRet =true;
}while(0);
return bRet;
}
//重新生成炸弹
void Monster::resetPosition()
{
cocos2d::CCSize monsterSize =getSprite()->getContentSize();
int ranY = (int)(CCRANDOM_0_1() * (320 - monsterSize.height)) + monsterSize.height / 2;
getSprite()->setPosition(XSystem::xccp(480 - monsterSize.width * 2, ranY));
}
这里我要提醒大家注意这个类的下方的函数
//重新生成炸弹
void Monster::resetPosition()
{
cocos2d::CCSize monsterSize =getSprite()->getContentSize();
int ranY = (int)(CCRANDOM_0_1() * (320 - monsterSize.height)) + monsterSize.height / 2;
getSprite()->setPosition(XSystem::xccp(480 - monsterSize.width * 2, ranY));
}
大家有木有想起我说MonsterManager类负责初始化所有的炸弹然后每次隔两秒就生成出来一个的? 说到这里大家已经懂了。
monst类中对所有的Monst对象初始化了一个SimpleMove函数,这是精灵移动的一个类
讲下最后一个类,里面包含了一个初始化的方法和一个Update的方法,还有一个speed的速度属性
看下这个类的头文件,注意虚函数的部分
#ifndef __SIMPLE_MOVE_CONTROLLER_H__
#define __SIMPLE_MOVE_CONTROLLER_H__
#include"Controller.h"
/*往左往右动就直接设置速度的正负就OK了*/
class SimpleMoveController: public Controller
{
public:
virtual void update(float dt);
virtual bool init();
static SimpleMoveController * create();
void setiXSpeed(int iXSpeed);
private:
int iXSpeed; //X方向的移动速度
};
#endif
其实cpp文件的定义也是比较简单,主要的Update函数是运用了我前面给大家说过的dt宏,其他的就没什么难点了。
#include "SimpleMoveController.h"
void SimpleMoveController::setiXSpeed(int iXSpeed)
{
this->iXSpeed =iXSpeed;
}
void SimpleMoveController::update(float dt) //dt是个宏
{
if(this->listener) //this->listener为真表示正在被监视着,如果为Null表示子弹已经消失
{
this->listener->updatePosition(dt ,iXSpeed ,0.0f); //算出新的位置
this->listener->hideIfDead(); //计算新的位置的子弹或者炸弹是否"死亡",当子弹的位置超出屏幕大小则死亡
}
}
SimpleMoveController *SimpleMoveController::create()
{
SimpleMoveController *controller = new SimpleMoveController();
if(controller &&controller->init())
{
return controller;
}
else
{
CC_SAFE_DELETE(controller);
return NULL;
}
}
bool SimpleMoveController::init()
{
bool bRet =false;
do
{
CC_BREAK_IF(! Controller::init());//其实只是刷新一下窗口而已
bRet =true;
}while(0);
return bRet;
}
bullet类似Monster,可以边看代码边参考上面的Monster类的部分
到这里这一节就结束了。大家看到这里就算程序没有全懂,但是大部分应该都懂了,自己再研究下肯定也没问题了。
我所学到的所有东西也都是从别的大神写的文章学到的,所以我也很愿意把我学到的东西贡献给大家。
-----------------------------------------------------自己研究完这个程序,让我学到了很多东西,也感叹编程中坚持的重要性。