1、现在,已经实现了三个精灵在场景上的显示和运动,但是子弹打中敌人后,没有产生任何效果。下面就来实现一个子弹打中敌人,子弹和敌人同时消失的功能。最重要的一步就是碰撞检测,即子弹所在的矩形与敌人所在的矩形有重叠时,即为子弹击中敌人。
2、因为子弹何时击中敌人是不确定的,所以我们需要不断的进行碰撞检测,以便确定子弹是否击中敌人。因此,需要将碰撞检测的实现放在计时器中,不断地执行检测。
3、具体实现:定义两个数组,分别用于存储子弹和敌人,在定时器的回调函数中进行碰撞检测(运用intersectsRect函数)。在创建精灵时将其存入数组,数组是类的成员变量。当然,数组需要不断释放移出边界的精灵。
4、碰撞检测函数:
void ShooterLayer::bulletCrashTest()
{
CCArray * projectilesToDelete = new CCArray;
CCArray * targetsToDelete = new CCArray;
CCObject* it = NULL;
CCObject* jt = NULL;
CCARRAY_FOREACH(_projectiles, it)
{
CCSprite *projectile = dynamic_cast<CCSprite *>(it);
CCRect projectileRect = CCRectMake(projectile->getPosition().x -
(projectile->getContentSize().width/2), projectile->getPosition().y -
(projectile->getContentSize().height/2), projectile->getContentSize().width, projectile->getContentSize().height);
CCARRAY_FOREACH(_targets, jt)
{
CCSprite *target = dynamic_cast<CCSprite*>(jt);
CCRect targetRect = CCRectMake(target->getPosition().x - (target->getContentSize().width/2),
target->getPosition().y - (target->getContentSize().height/2),target->getContentSize().width, target->getContentSize().height);
if (/*CCRect::CCRectIntersectsRect(projectileRect, targetRect)*/projectileRect.intersectsRect(targetRect))
{
targetsToDelete->addObject(target);
projectilesToDelete->addObject(projectile);
}
}
}
CCARRAY_FOREACH(targetsToDelete, jt)
{
CCSprite *target = dynamic_cast<CCSprite*>(jt);
_targets->removeObject(target);
this->removeChild(target, true);
}
CCARRAY_FOREACH(projectilesToDelete, it)
{
CCSprite* projectile = dynamic_cast<CCSprite*>(it);
_projectiles->removeObject(projectile);
this->removeChild(projectile, true);
}
projectilesToDelete->release();
targetsToDelete->release();
}
5、完整ShooterLayer类代码:
ShooterLayer.h
#pragma once
#include "cocos2d.h"
class ShooterLayer : public cocos2d::CCLayerColor
{
public:
ShooterLayer(void);
~ShooterLayer(void);
// 初始化函数
bool init();
// new一个对象,如果成功的话则调用初始化函数
CREATE_FUNC(ShooterLayer);
// 关闭菜单的回调函数
void menuCloseCallback(CCObject *pObj);
// 计时器的回调函数
void update(float dt);
// 随机创建敌方精灵
void addTarget();
// 敌方精灵飞出边界的回调函数
void SpriteMoveFinished(CCNode *pObj);
// 触摸操作(即Win32中的鼠标点击操作)响应函数
void ccTouchesEnded(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent);
// 保存射击精灵的大小
cocos2d::CCSize _szPlayer;
// 碰撞检测
void bulletCrashTest();
// 子弹精灵数组
cocos2d::CCArray *_targets;
// 敌人精灵数组
cocos2d::CCArray *_projectiles;
};
ShooterLayer.cpp
#include "ShooterLayer.h"
USING_NS_CC;
// 标记精灵
enum
{
TARGET,
PROJECT
};
ShooterLayer::ShooterLayer(void)
{
_targets = NULL;
_projectiles = NULL;
}
ShooterLayer::~ShooterLayer(void)
{
if (_targets)
{
delete _targets;
_targets = NULL;
}
if (_projectiles)
{
delete _projectiles;
_projectiles = NULL;
}
}
bool ShooterLayer::init()
{
bool bRct = false;
// 初始化数组
_targets = new CCArray;
_projectiles = new CCArray;
do
{
// 1、初始化布景
CC_BREAK_IF(!CCLayerColor::initWithColor(ccc4(255, 227, 132, 255)));
// 2、创建窗口右下角的关闭菜单
// 创建菜单项
CCMenuItemImage *pCloseItem = NULL;
pCloseItem = CCMenuItemImage::create(
"CloseNormal.png",
"CloseSelected.png",
this,
menu_selector(ShooterLayer::menuCloseCallback)); // 回调函数
// 检测创建是否成功
CC_BREAK_IF(!pCloseItem); // create失败则返回
// 设置菜单项位置
pCloseItem->setPosition(CCDirector::sharedDirector()->getWinSize().width - 20, 20);
// 创建菜单
CCMenu *pMenu = CCMenu::create(pCloseItem, NULL);
// 设置菜单位置
pMenu->setPosition(CCPointZero);
// 将菜单添加至布景
this->addChild(pMenu);
//
// 创建精灵
CCSprite *pSprite = CCSprite::create("Player.png");
// 获取窗口大小
CCSize sz = CCDirector::sharedDirector()->getWinSize();
// 设置精灵位置
pSprite->setPosition(CCPointMake(pSprite->getContentSize().width/2, sz.height/2));
// 将精灵添加至布景
this->addChild(pSprite);
// 保存精灵大小
_szPlayer = pSprite->getContentSize();
//
// 设置计时器
this->scheduleUpdate();
//
// 允许触摸(注意:很重要)
this->setTouchEnabled(true);
//
bRct = true;
} while (0);
return bRct;
}
void ShooterLayer::menuCloseCallback(CCObject *pObj)
{
// 退出程序
CCDirector::sharedDirector()->end();
}
void ShooterLayer::update(float dt)
{
static int tmpTimes = 0;
static int tmpCount = 20;
if (tmpTimes++ > tmpCount)
{
addTarget();
tmpCount = rand() % 60 + 20;
tmpTimes = 0;
}
bulletCrashTest();
}
void ShooterLayer::addTarget()
{
// 创建精灵
CCSprite *target = CCSprite::create("Target.png");
// 计算敌方精灵可能出现的位置范围
CCSize sz = CCDirector::sharedDirector()->getWinSize();
int x = sz.width + target->getContentSize().width/2;
int nMax = sz.height - target->getContentSize().height/2;
int nMin = target->getContentSize().height/2;
// 产生随机数
int f = rand() % (nMax - nMin);
int y = nMin + f;
// 根据随机数,设置起始精灵位置
target->setPosition(CCPointMake(x, y));
// 利用计时器确定执行序列动作的速度
float nDurMin = 2.0;
float nDurMax = 4.0;
float nDur = float(rand() % 100) / 100.0 + nDurMin;
nDur = nDur > nDurMax ? nDurMax : nDur;
// 定义两个动作(平移动作, 以及移出边界的响应)
CCFiniteTimeAction *action = CCMoveTo::create(nDur, ccp(-target->getContentSize().width/2, y));
CCFiniteTimeAction *actionDone = CCCallFuncN::create(this,
callfuncN_selector(ShooterLayer::SpriteMoveFinished)); // 这里需要一个移出边界的响应函数
// 添加序列动作
CCAction *pseq = CCSequence::create(action, actionDone, NULL);
// 将序列动作添加至精灵,使其执行
target->runAction(pseq);
// 将精灵添加至布景
this->addChild(target);
//
// 加入数组
_targets->addObject(target);
// 设置标记
target->setTag(TARGET);
}
void ShooterLayer::SpriteMoveFinished(CCNode *pObj)
{
// 获取移出精灵对象指针
CCSprite *pSprite = (CCSprite*)pObj;
// 清除
this->removeChild(pSprite, true);
}
void ShooterLayer::ccTouchesEnded(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent)
{
// 获取CCTouch指针对象
CCTouch *pTouch = (CCTouch*)pTouches->anyObject();
// 获取视图位置
CCPoint location = pTouch->getLocationInView();
// 转化为OpenGL坐标
location = CCDirector::sharedDirector()->convertToGL(location);
// 创建子弹精灵
CCSprite *project = CCSprite::create("Projectile.png");
// 窗口大小
CCSize sz = CCDirector::sharedDirector()->getWinSize();
// 子弹起点
CCPoint pt = ccp(_szPlayer.width + project->getContentSize().width/2, sz.height/2);
// 设置子弹起点
project->setPosition(pt);
// 添加精灵至布景
this->addChild(project);
//
// 添加至数组
_projectiles->addObject(project);
// 添加标记
project->setTag(PROJECT);
//
// 求出子弹终点坐标
int x = sz.width + project->getContentSize().width/2;
float yDistance = (location.y - pt.y) / (location.x - pt.x) * (sz.width - _szPlayer.width);
// 子弹动作
CCFiniteTimeAction *action = CCMoveTo::create(1.0, ccp(x, yDistance + sz.height/2));
CCFiniteTimeAction *actionDone = CCCallFuncN::create(this, callfuncN_selector(ShooterLayer::SpriteMoveFinished));
project->runAction(CCSequence::create(action, actionDone, NULL));
}
void ShooterLayer::bulletCrashTest()
{
// 定义两个数组用于存储待删除的精灵
CCArray * projectilesToDelete = new CCArray;
CCArray * targetsToDelete = new CCArray;
CCObject* it = NULL;
CCObject* jt = NULL;
CCARRAY_FOREACH(_projectiles, it)
{
CCSprite *projectile = dynamic_cast<CCSprite *>(it);
CCRect projectileRect = CCRectMake(projectile->getPosition().x -
(projectile->getContentSize().width/2), projectile->getPosition().y -
(projectile->getContentSize().height/2), projectile->getContentSize().width, projectile->getContentSize().height);
CCARRAY_FOREACH(_targets, jt)
{
CCSprite *target = dynamic_cast<CCSprite*>(jt);
CCRect targetRect = CCRectMake(target->getPosition().x - (target->getContentSize().width/2),
target->getPosition().y - (target->getContentSize().height/2),target->getContentSize().width, target->getContentSize().height);
// 碰撞检测
if (/*CCRect::CCRectIntersectsRect(projectileRect, targetRect)*/projectileRect.intersectsRect(targetRect))
{
targetsToDelete->addObject(target);
projectilesToDelete->addObject(projectile);
}
}
}
// 释放资源
CCARRAY_FOREACH(targetsToDelete, jt)
{
CCSprite *target = dynamic_cast<CCSprite*>(jt);
_targets->removeObject(target);
this->removeChild(target, true);
}
CCARRAY_FOREACH(projectilesToDelete, it)
{
CCSprite* projectile = dynamic_cast<CCSprite*>(it);
_projectiles->removeObject(projectile);
this->removeChild(projectile, true);
}
projectilesToDelete->release();
targetsToDelete->release();
}
6、运行结果:
7、待续