[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);
}

<코드실행 데모영상>

 

 

BELATED ARTICLES

more