作业要求
- 利用键盘事件实现飞船左右移动。
- 利用键盘和触摸事件实现子弹发射。
- 用自定义事件实现:子弹和陨石相距小于一定距离时,陨 石爆炸,子弹消失。
- 游戏过程中有背景音乐,发射子弹、击中陨石有音效。
- 注意飞船、子弹的移动范围。
- 游戏结束飞船爆炸,移除所有监听器。
- 利用触摸事件实现飞船移动。(点击飞船后拖动鼠标)
- 陨石向下移动并生成新的一行陨石
- 子弹和陨石的数量显示正确
游戏演示
实现细节
Thunder.h
#ifndef __THUNDER_H__
#define __THUNDER_H__
#include "cocos2d.h"
#include "SimpleAudioEngine.h"
USING_NS_CC;
using namespace std;
class Thunder : public cocos2d::Layer {
public:
static cocos2d::Scene* createScene();
virtual bool init();
CREATE_FUNC(Thunder);
private:
void preloadMusic();
void playBgm();
void addTouchListener();
void addKeyboardListener();
void addEnemy(int n);
void newEnemy();
void update(float f);
void fire();
void explosion();
void meet(EventCustom* event);
void addCustomListener();
void movePlane(char c);
void Boom(Sprite* enemy);
void stopAc();
bool onTouchBegan(Touch *touch, Event *event);
void onTouchMoved(Touch *touch, Event *event);
void onTouchEnded(Touch *touch, Event *event);
void onKeyPressed(EventKeyboard::KeyCode code, Event* event);
void onKeyReleased(EventKeyboard::KeyCode code, Event* event);
void meet1(EventCustom* event);
bool isMove;
bool isClick;
char movekey;
Size visibleSize;
Sprite* player;
list<Sprite*> bullets;
list<Sprite*> enemys;
cocos2d::Vector<SpriteFrame*> explore;
int stoneType;
Label* bulletsNum;
Label* enemysNum;
};
#endif
Thunder.cpp
#include "Thunder.h"
#include <algorithm>
USING_NS_CC;
using namespace CocosDenshion;
Scene* Thunder::createScene() {
auto scene = Scene::create();
auto layer = Thunder::create();
scene->addChild(layer);
return scene;
}
bool Thunder::init() {
if (!Layer::init()) {
return false;
}
stoneType = 1;
isMove = false;
visibleSize = Director::getInstance()->getVisibleSize();
auto bgsprite = Sprite::create("bg.jpg");
bgsprite->setPosition(visibleSize / 2);
bgsprite->setScale(visibleSize.width / bgsprite->getContentSize().width,
visibleSize.height / bgsprite->getContentSize().height);
this->addChild(bgsprite, 0);
player = Sprite::create("player.png");
player->setAnchorPoint(Vec2(0.5, 0.5));
player->setPosition(visibleSize.width / 2, player->getContentSize().height);
player->setName("player");
this->addChild(player, 1);
enemysNum = Label::createWithTTF("enemys: 0", "fonts/arial.TTF", 20);
enemysNum->setColor(Color3B(255, 255, 255));
enemysNum->setPosition(50, 60);
this->addChild(enemysNum, 3);
bulletsNum = Label::createWithTTF("bullets: 0", "fonts/arial.TTF", 20);
bulletsNum->setColor(Color3B(255, 255, 255));
bulletsNum->setPosition(50, 30);
this->addChild(bulletsNum, 3);
addEnemy(5);
preloadMusic();
playBgm();
explosion();
addTouchListener();
addKeyboardListener();
addCustomListener();
schedule(schedule_selector(Thunder::update), 0.04f, kRepeatForever, 0);
return true;
}
void Thunder::preloadMusic() {
SimpleAudioEngine::getInstance()->preloadBackgroundMusic("music/bgm.mp3");
SimpleAudioEngine::getInstance()->preloadEffect("music/fire.wav");
}
void Thunder::playBgm() {
SimpleAudioEngine::getInstance()->playBackgroundMusic("music/bgm.mp3", true);
}
void Thunder::addEnemy(int n) {
enemys.clear();
for (unsigned i = 0; i < 3; ++i) {
char enemyPath[20];
sprintf(enemyPath, "stone%d.png", 3 - i);
double width = visibleSize.width / (n + 1.0),
height = visibleSize.height - (50 * (i + 1));
for (int j = 0; j < n; ++j) {
auto enemy = Sprite::create(enemyPath);
enemy->setAnchorPoint(Vec2(0.5, 0.5));
enemy->setScale(0.5, 0.5);
enemy->setPosition(width * (j + 1), height);
enemys.push_back(enemy);
addChild(enemy, 1);
}
}
}
void Thunder::newEnemy() {
for (auto enemy : enemys) {
double height = enemy->getContentSize().height;
enemy->setPosition(enemy->getPosition().x, enemy->getPosition().y - 50);
}
const double width = visibleSize.width / 6,
height = visibleSize.height - 50;
char enemyPath[20];
sprintf(enemyPath, "stone%d.png", stoneType);
for (int i = 0; i < 5; i++) {
auto enemy = Sprite::create(enemyPath);
enemy->setAnchorPoint(Vec2(0.5, 0.5));
enemy->setScale(0.5, 0.5);
enemy->setPosition(width * (i + 1) - 80, height);
enemys.push_back(enemy);
addChild(enemy, 1);
}
stoneType = (stoneType + 1) % 3 + 1;
}
void Thunder::movePlane(char c) {
if (c == 'A') {
if (player->getPosition().x > 50) {
player->runAction(MoveBy::create(0.1f, Vec2(-20, 0)));
}
}
else if (c == 'D') {
if (player->getPosition().x < visibleSize.width - 50) {
player->runAction(MoveBy::create(0.1f, Vec2(20, 0)));
}
}
}
void Thunder::fire() {
auto bullet = Sprite::create("bullet.png");
bullet->setAnchorPoint(Vec2(0.5, 0.5));
bullets.push_back(bullet);
bullet->setPosition(player->getPosition());
addChild(bullet, 1);
SimpleAudioEngine::getInstance()->playEffect("music/fire.wav", false);
bullet->runAction(MoveBy::create(1.0f, Vec2(0, visibleSize.height)));
}
void Thunder::explosion() {
explore.reserve(8);
auto texture = Director::getInstance()->getTextureCache()->addImage("explosion.png");
const int width = 188.8;
const int height = 160;
for (int i = 0; i < 8; i++) {
int x = width * (i % 5);
int y = height * (i / 5);
auto frame = SpriteFrame::createWithTexture(texture, CC_RECT_PIXELS_TO_POINTS(Rect(x, y, width, height)));
explore.pushBack(frame);
}
}
void Thunder::update(float f) {
char str[15];
sprintf(str, "enemys: %d", enemys.size());
enemysNum->setString(str);
sprintf(str, "bullets: %d", bullets.size());
bulletsNum->setString(str);
if (isMove)
this->movePlane(movekey);
static int ct = 0;
static int dir = 4;
++ct;
if (ct == 120)
ct = 40, dir = -dir;
else if (ct == 80) {
dir = -dir;
newEnemy();
}
else if (ct == 20)
ct = 40, dir = -dir;
for (Sprite* s : enemys) {
if (s != NULL) {
s->setPosition(s->getPosition() + Vec2(dir, 0));
}
}
EventCustom e("meet");
_eventDispatcher->dispatchEvent(&e);
}
void Thunder::meet(EventCustom * event) {
for (auto bullet : bullets) {
for (auto enemy : enemys) {
if (bullet->getPosition().getDistance(enemy->getPosition()) < 25) {
auto explose = Animate::create(Animation::createWithSpriteFrames(explore, 0.05f));
auto sequence = Sequence::create(explose, CallFunc::create([enemy]() {
enemy->removeFromParentAndCleanup(true);
}), nullptr);
SimpleAudioEngine::getInstance()->playEffect("music/explore.wav");
enemy->runAction(sequence);
bullet->removeFromParentAndCleanup(true);
bullets.remove(bullet);
enemys.remove(enemy);
return;
}
}
}
for (auto enemy : enemys) {
if (enemy->getPosition().getDistance(player->getPosition()) < 50
|| (enemy->getPosition().y < 50)) {
auto explose = Animate::create(Animation::createWithSpriteFrames(explore, 0.05f));
auto sequence = Sequence::create(explose, CallFunc::create([this]() {
player->removeFromParentAndCleanup(true);
}), nullptr);
SimpleAudioEngine::getInstance()->playEffect("music/explore.wav");
player->runAction(sequence);
unschedule(schedule_selector(Thunder::update));
auto gameOver = Sprite::create("gameOver.png");
gameOver->setPosition(Vect(visibleSize.width / 2, visibleSize.height / 2));
addChild(gameOver);
_eventDispatcher->removeAllEventListeners();
}
}
for (auto bullet : bullets) {
auto pos = bullet->getPosition();
if (pos.x < 0 || pos.x > visibleSize.width
|| pos.y < 0 || pos.y > visibleSize.height) {
bullet->removeFromParentAndCleanup(true);
bullets.remove(bullet);
return;
}
}
}
void Thunder::stopAc() {
}
void Thunder::addCustomListener() {
_eventDispatcher->addCustomEventListener("meet", CC_CALLBACK_1(Thunder::meet, this));
}
void Thunder::addKeyboardListener() {
auto keyBoardListener = EventListenerKeyboard::create();
keyBoardListener->onKeyPressed = CC_CALLBACK_2(Thunder::onKeyPressed, this);
keyBoardListener->onKeyReleased = CC_CALLBACK_2(Thunder::onKeyReleased, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(keyBoardListener, player);
}
void Thunder::onKeyPressed(EventKeyboard::KeyCode code, Event* event) {
switch (code) {
case EventKeyboard::KeyCode::KEY_LEFT_ARROW:
case EventKeyboard::KeyCode::KEY_CAPITAL_A:
case EventKeyboard::KeyCode::KEY_A:
movekey = 'A';
isMove = true;
break;
case EventKeyboard::KeyCode::KEY_RIGHT_ARROW:
case EventKeyboard::KeyCode::KEY_CAPITAL_D:
case EventKeyboard::KeyCode::KEY_D:
movekey = 'D';
isMove = true;
break;
case EventKeyboard::KeyCode::KEY_SPACE:
fire();
break;
}
}
void Thunder::onKeyReleased(EventKeyboard::KeyCode code, Event* event) {
switch (code) {
case EventKeyboard::KeyCode::KEY_LEFT_ARROW:
case EventKeyboard::KeyCode::KEY_A:
case EventKeyboard::KeyCode::KEY_CAPITAL_A:
case EventKeyboard::KeyCode::KEY_RIGHT_ARROW:
case EventKeyboard::KeyCode::KEY_D:
case EventKeyboard::KeyCode::KEY_CAPITAL_D:
isMove = false;
break;
}
}
void Thunder::addTouchListener() {
auto touchListener = EventListenerTouchOneByOne::create();
touchListener->onTouchBegan = CC_CALLBACK_2(Thunder::onTouchBegan, this);
touchListener->onTouchEnded = CC_CALLBACK_2(Thunder::onTouchEnded, this);
touchListener->onTouchMoved = CC_CALLBACK_2(Thunder::onTouchMoved, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, player);
}
bool Thunder::onTouchBegan(Touch *touch, Event *event) {
if (touch->getLocation().getDistance(player->getPosition()) <= 30)
isClick = true;
fire();
return true;
}
void Thunder::onTouchEnded(Touch *touch, Event *event) {
isClick = false;
}
void Thunder::onTouchMoved(Touch *touch, Event *event) {
if (isClick) {
auto touchLocation = touch->getLocation();
int shiftLength = player->getContentSize().width;
if (touchLocation.x > 0 && touchLocation.x < visibleSize.width) {
player->setPosition(Vect(touch->getLocation().x, player->getPosition().y));
}
}
}
资源以及视频
资源以及视频