[C++] cocos2d-x를 활용한 드래곤 플라이트 게임 만들기 (2)
2021. 5. 21. 06:34
안녕하세요. 라이트코드(Light Code) 입니다.
지난 글에서는 cocos2d-x 라는 오픈 소스 2D 게임엔진으로 간단한 게임을 만들었습니다. 5개의 버튼이 왼쪽 상단에 있었고 그 버튼을 클릭하면 각 버튼에 해당하는 모션에 대한 액션이 실행되었습니다. 이번에는 다른 전 게임에서의 부족한 점을 보안하여 다른 게임 형태로 제작해보겠습니다.
(전 1단계에서의 코드이해가 부족하신 분은 [이전단계] 클릭하여 소스코드를 참고하시길 바랍니다)
<설명>
Play/Quit 버튼이 있고 [play] 버튼을 누르게 되면 적들이 나타나고 1개의 목숨을 가지고 태어난 플레이어가 적들에게 미사일을 발사해 적들을 화면에서 제거한다. 목숨이 1개이기 때문에 만약 적하고 접촉하게 된다면 그 상태로 [Game Over] 이라는 메세지와 함께 처음화면으로 돌아간다. [Quit] 버튼을 누르게 되면 프로그램은 정상적으로 종료되게 된다.
<ProjectOneScene.h>
#ifndef __GAME_SCENE_H__
#define __GAME_SCENE_H__
#define TAG_WINDOW_SIZE 50
#define TAG_SPRITE_PLAYER 0
#define TAG_PLAYER_MOVE 1
#define TAG_PLAYER_END 2
#include "cocos2d.h"
USING_NS_CC;
class Enemy:public cocos2d::Sprite {
public: int type;
int life;
CREATE_FUNC(Enemy);
};
class GameScene:public cocos2d::Scene {
public: static Scene* createScene();
Vector<Sprite*> missiles;
Vector<Enemy*> enemies;
virtual bool init();
bool pCheck;
bool gCheck;
void startGame();
void initBackground();
void initPlayer();
void setGameover();
void gameEnd();
void stopGameOver();
void playerEnemy();
void update(float delta);
void setAttack(float delta);
void setEnemy(float delta);
void resetAttack(Ref* sender);
void resetEnemy(Ref* sender);
bool onTouchBegan(Touch* touch, Event* unused_event);
CREATE_FUNC(GameScene);
};
#endif
<ProjectOneScene.cpp>
Scene * GameScene::createScene() {
return GameScene::create();
}
bool GameScene::init() {
if (!Scene::init()) {
return false;
}
auto listener = EventListenerTouchOneByOne::create();
listener -> onTouchBegan = CC_CALLBACK_2(GameScene::onTouchBegan, this);
Director::getInstance() -> getEventDispatcher() -> addEventListenerWithSceneGraphPriority(listener, this);
Size winSize = Director::getInstance() -> getVisibleSize();
startGame();
initBackground();
initPlayer();
stopGameOver();
return true;
}
bool GameScene::onTouchBegan(Touch * touch, Event * unused_event) {
if (gCheck) {
auto scene = TransitionPageTurn::create(3.0, MenuScene::createScene(), false);
Director::getInstance() -> replaceScene(scene);
return true;
} else if (pCheck) {
return true;
}
Label * gameover = (Label *)this -> getChildByTag(TAG_PLAYER_END);
Sprite * dragon = (Sprite *)this -> getChildByTag(TAG_SPRITE_PLAYER);
if (dragon -> getActionByTag(TAG_PLAYER_MOVE)) {
dragon -> stopActionByTag(TAG_PLAYER_MOVE);
}
Vec2 pos = dragon -> getPosition();
Vec2 location = touch -> getLocation();
float distance = sqrtf((location.x - pos.x) * (location.x - pos.x));
auto action = MoveTo::create(distance / 400, Vec2(location.x, pos.y));
action -> setTag(TAG_PLAYER_MOVE);
dragon -> runAction(action);
return true;
}
void GameScene::startGame() {
missiles.clear();
enemies.clear();
pCheck = false;
gCheck = false;
}
void GameScene::initBackground() {
auto gLayer = Layer::create();
this -> addChild(gLayer);
auto background = Sprite::create("images/GameImage.png");
background -> setAnchorPoint(Point::ZERO);
gLayer -> addChild(background);
auto action_0 = MoveBy::create(15.0, Point(0, -900));
auto action_1 = Place::create(Point::ZERO);
auto action_2 = Sequence::create(action_0, action_1, NULL);
auto result = RepeatForever::create(action_2);
gLayer -> runAction(result);
}
void GameScene::initPlayer() {
SpriteFrameCache::getInstance() -> addSpriteFramesWithFile("dragon.plist");
auto dragon = Sprite::createWithSpriteFrameName("dragon_01.png");
dragon -> setScale(2.5 f);
dragon -> setPosition(Vec2(300, 150));
dragon -> setTag(TAG_SPRITE_PLAYER);
this -> addChild(dragon, 1);
auto animation = Animation::create();
animation -> setDelayPerUnit(0.1);
for (int i = 0; i < 5; i ++) {
auto frame = SpriteFrameCache::getInstance() -> getSpriteFrameByName(StringUtils::format("dragon_%0
2 d.png ", i + 1));
animation -> addSpriteFrame(frame);
}
auto animate = Animate::create(animation);
dragon -> runAction(RepeatForever::create(animate));
this -> schedule(schedule_selector(GameScene::setAttack), 0.5 f);
this -> schedule(schedule_selector(GameScene::setEnemy), 0.5 f);
this -> scheduleUpdate();
}
void GameScene::setAttack(float delta) {
Sprite * dragon = (Sprite *)this -> getChildByTag(TAG_SPRITE_PLAYER);
SpriteFrameCache::getInstance() -> addSpriteFramesWithFile("missile.plist");
auto missile = Sprite::createWithSpriteFrameName("missile_01.png");
missile -> setPosition(dragon -> getPosition() + Vec2(0, 20));
this -> addChild(missile, 1);
auto animation = Animation::create();
animation -> setDelayPerUnit(0.1);
for (int i = 0; i < 5; i ++) {
auto frame = SpriteFrameCache::getInstance() -> getSpriteFrameByName(StringUtils::format("missile_%0
2 d.png ", i + 1));
animation -> addSpriteFrame(frame);
}
auto animate = Animate::create(animation);
missile -> runAction(RepeatForever::create(animate));
missiles.pushBack(missile);
missile -> runAction(Sequence::create(MoveBy::create(2.0 f, Vec2(0, 900)), CallFuncN::create(CC_CALLBACK_1(GameScene::resetAttack, this)), NULL));
}
void GameScene::setEnemy(float delta) {
float x = rand() % (600 - TAG_WINDOW_SIZE * 2) + TAG_WINDOW_SIZE;
auto enemy = Enemy::create();
int random = rand() % 100;
float speed;
if (random < 70) {
enemy -> type = 1;
enemy -> life = 1;
enemy -> setScale(1.5 f);
speed = 9.0 f;
} else {
enemy -> type = 2;
enemy -> life = 2;
enemy -> setScale(2.5 f);
speed = 7.0 f;
}
SpriteFrameCache::getInstance() -> addSpriteFramesWithFile("attack.plist");
enemy -> Sprite::createWithSpriteFrameName("attack_01.png");
enemy -> setPosition(Vec2(x, 900 + 100));
this -> addChild(enemy, 1);
enemies.pushBack(enemy);
auto animation = Animation::create();
animation -> setDelayPerUnit(0.3);
for (int i = 0; i < 5; i ++) {
auto frame = SpriteFrameCache::getInstance() -> getSpriteFrameByName(StringUtils::format("attack_%02
d.png ", i + 1));
animation -> addSpriteFrame(frame);
}
auto animate = Animate::create(animation);
enemy -> runAction(RepeatForever::create(animate));
enemy -> runAction(Sequence::create(DelayTime::create(2.0), MoveTo::create(speed, Vec2(300, -1000)), CallFuncN::create(CC_CALLBACK_1(GameScene::resetEnemy, this)), NULL));
}
void GameScene::update(float delta) {
if (pCheck)
return;
bool isCrash = false;
for (Sprite * missile : missiles) {
Rect rectMissile = missile -> getBoundingBox();
for (Enemy * enemys : enemies) {
Rect rectEnemy = enemys -> getBoundingBox();
if (rectMissile.intersectsRect(rectEnemy)) {
enemys -> life --;
if (enemys -> life <= 0) {
enemies.eraseObject(enemys);
this -> removeChild(enemys);
missiles.eraseObject(missile);
this -> removeChild(missile);
isCrash = true;
break;
}
break;
}
}
if (isCrash) {
break;
}
}
playerEnemy();
}
void GameScene::playerEnemy() {
Sprite * player = (Sprite *)this -> getChildByTag(TAG_SPRITE_PLAYER);
Rect rPlayer = Rect(player -> getPositionX() - 10, player -> getPositionY() - 20, 20, 40);
for (Sprite * enemys : enemies) {
Rect renemy = enemys -> getBoundingBox();
if (rPlayer.intersectsRect(renemy)) {
gameEnd();
return;
}
}
}
void GameScene::stopGameOver() {
auto gameover = Label::createWithSystemFont("GAME OVER", "Hobo Std", 60);
gameover -> setPosition(Vec2(300, 450));
gameover -> setTextColor(Color4B::RED);
gameover -> setScale(0.0 f);
gameover -> setVisible(false);
gameover -> setTag(TAG_PLAYER_END);
this -> addChild(gameover, 1);
}
void GameScene::setGameover() {
gCheck = true;
auto gameover = (Label *)this -> getChildByTag(TAG_PLAYER_END);
gameover -> runAction(Sequence::create(Show::create(), Spawn::create(ScaleTo::create(1.0 f, 1.0 f), RotateBy::create(1.0 f, 360 * 1), NULL), NULL));
}
void GameScene::gameEnd() {
pCheck = true;
setGameover();
for (Enemy * enemys : enemies) {
this -> removeChild(enemys);
}
for (Sprite * missile : missiles) {
this -> removeChild(missile);
}
this -> unschedule(schedule_selector(GameScene::setAttack));
this -> unschedule(schedule_selector(GameScene::setEnemy));
}
void GameScene::resetAttack(Ref * sender) {
auto spr = (Sprite *)sender;
missiles.eraseObject(spr);
this -> removeChild(spr);
}
void GameScene::resetEnemy(Ref * sender) {
auto spr = (Enemy *)sender;
enemies.eraseObject(spr);
this -> removeChild(spr);
}
<코드실행 데모영상>