suzu-kの日記

メインはプログラミング

マインスイーパー その4

前回はコンソールで作りましたが今回はGUI版を作りました!

f:id:suzu-k:20140922202730p:plain

・概要
GUIで遊べるようにCocos2d-x(v2.2.5)を使用.
cocos2d-xでプロジェクトを作成した後に
GameScene.hとGameScene.cppと画像データ1~8.pngとbackground.pngを用意するのみ.
ミニドット絵メーカー


・感想
大体2〜3日で作りました.合計時間は10時間ほど.
コンソールバージョンを作っていたので作りやすかったです.
なので作成のほとんどはCocos2d-xの使い方を調べてたような気がします.
今回のゲームにフラグとかリトライとかゲームオーバーの画面とか難易度とか細かい点を改良できます.
が,それは気が向いたときに行うとして!すこし違うジャンルでゲームを作りたいと思います.

ソースコード
余り褒められたソースではないですがどうぞ.

GameScene.h

#ifndef __Minesweeper__GameScene__
#define __Minesweeper__GameScene__

#include "cocos2d.h"

class GameScene : public cocos2d::CCLayer{
public:
    // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
    virtual bool init();
    
    // there's no 'id' in cpp, so we recommend returning the class instance pointer
    static cocos2d::CCScene* scene();
    
    // a selector callback
    void menuCloseCallback(CCObject* pSender);
    
    // implement the "static node()" method manually
    CREATE_FUNC(GameScene);
    
    /**********************************
     
              my define

     *********************************/
private:
    const int DEBUG = 1;
    void makeBackground(const char* path);
    void initPanels(int width, int height);
    void makePanels();
    //パネルをオープンする
    cocos2d::CCSprite* openPanel(cocos2d::CCSprite* target);
    //縦横に爆弾がなければ自動でオープンにする.
    void openPanelAuto(cocos2d::CCSprite* target);
    //隣接するにいくつの爆弾があるかを数える
    int searchBomb(cocos2d::CCSprite* target);
    //数字をパネルに追加する
    void setNumber(cocos2d::CCSprite* target);
    //ボムであるのか調べる
    bool isBomb(cocos2d::CCSprite* target);
    //ゲームオーバーを表示する
    void makeGameOver(cocos2d::CCSprite* target);
    
    cocos2d::CCArray* m_panels;
    cocos2d::CCLabelTTF* pLabel;
    
    int m_width = 30, m_height = 20;
    const int maxBomb = 200;

    enum{
        kTagText,
        kTagClose,
        kTagOpen,
        kTagBomb,
    };
    
public:
    virtual bool ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent);
    virtual void ccTouchEnded(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent);
};

#endif /* defined(__Minesweeper__GameScene__) */

GameScene.cpp

#include "GameScene.h"
#include "time.h"

USING_NS_CC;

CCScene* GameScene::scene()
{
    // 'scene' is an autorelease object
    CCScene *scene = CCScene::create();
    
    // 'layer' is an autorelease object
    GameScene *layer = GameScene::create();
    
    // add layer as a child to scene
    scene->addChild(layer);
    
    // return the scene
    return scene;
}

// on "init" you need to initialize your instance
bool GameScene::init()
{
    
    //////////////////////////////
    // 1. super init first
    if ( !CCLayer::init() )
    {
        return false;
    }
    
    CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
    CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
    
    /////////////////////////////
    // 2. add a menu item with "X" image, which is clicked to quit the program
    //    you may modify it.
    
    // add a "close" icon to exit the progress. it's an autorelease object
    CCMenuItemImage *pCloseItem = CCMenuItemImage::create(
                                                          "CloseNormal.png",
                                                          "CloseSelected.png",
                                                          this,
                                                          menu_selector(GameScene::menuCloseCallback));
    
	pCloseItem->setPosition(ccp(origin.x + visibleSize.width - pCloseItem->getContentSize().width/2 ,
                                origin.y + pCloseItem->getContentSize().height/2));
    
    // create menu, it's an autorelease object
    CCMenu* pMenu = CCMenu::create(pCloseItem, NULL);
    pMenu->setPosition(CCPointZero);
    this->addChild(pMenu, 1);
    
    /////////////////////////////
    // 3. add your codes below...
    
    // add a label shows "Hello World"
    // create and initialize a label
    
    pLabel = CCLabelTTF::create("ゲームスタート!!", "Arial", 24);
    
    // position the label on the center of the screen
    pLabel->setPosition(ccp(origin.x + visibleSize.width/2,
                            origin.y + visibleSize.height - pLabel->getContentSize().height));
    pLabel->setTag(kTagText);
    
    // add the label as a child to this layer
    this->addChild(pLabel, 1);
    
    
    // add "HelloWorld" splash screen"
    //    CCSprite* pSprite = CCSprite::create("background.png");
    
    // position the sprite on the center of the screen
    //    pSprite->setPosition(ccp(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
    
    // add the sprite as a child to this layer
    //    this->addChild(pSprite, 0);
    
    srand((unsigned)time(NULL));
    
    m_panels = new CCArray;
    
    // 背景を表示する
    makeBackground("background.png");
    CCLOG("initPanels");
    // パネルを初期化する
    initPanels(m_width, m_height);
    // パネルを表示する
    makePanels();
    
    setTouchEnabled(true);
    setTouchMode(kCCTouchesOneByOne);
    
    return true;
}


void GameScene::menuCloseCallback(CCObject* pSender)
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) || (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
	CCMessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert");
#else
    CCDirector::sharedDirector()->end();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    exit(0);
#endif
#endif
}

void GameScene::makeBackground(const char* path)
{
    CCSize winSize = CCDirector::sharedDirector()->getWinSize();
    CCSprite* bg = CCSprite::create(path);
    CCSize bgSize = bg->getContentSize();
    bg->setScale(winSize.width / bgSize.width);

    bg->setPosition(ccp(winSize.width * 0.5, winSize.height * 0.5));
    
    this->addChild(bg);
}

void GameScene::makePanels()
{
    CCObject* it = NULL;
    CCARRAY_FOREACH(m_panels, it)
    {
        CCSprite* target = (CCSprite*)it;
        this->addChild(target);
    }
}

bool GameScene::ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent){
    return true;
}

void GameScene::
ccTouchEnded(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent)
{
    
    CCDirector* pDirector = CCDirector::sharedDirector();
    CCPoint touchPoint = pDirector->convertToGL(pTouch->getLocationInView());
    
    // タッチしたパネルを調べて,オープンにする
    CCObject* it = NULL;
    CCARRAY_FOREACH(m_panels, it)
    {
        CCSprite* target = dynamic_cast<CCSprite*>(it);
        
        CCRect panelRect = CCRectMake(
                                      target->getPosition().x - (target->getContentSize().width / 2),
                                      target->getPosition().y - (target->getContentSize().height / 2),
                                      target->getContentSize().width ,
                                      target->getContentSize().height );
        
        //パネルをめくる
        if (panelRect.containsPoint(touchPoint)) {
            //爆弾だった場合はゲームオーバーを表示
            if(isBomb(target))
                makeGameOver(target);
            else{
                openPanel(target);
                openPanelAuto(target);
            }
        }
        
    }
    
}

bool GameScene::isBomb(cocos2d::CCSprite *target)
{
    return (target->getTag() == kTagBomb) ? true : false;
}

void GameScene::makeGameOver(cocos2d::CCSprite* target)
{
    
    //ゲームオーバー
    pLabel->setString("GameOver");
    
    //bombパネルを作成
    CCSprite* panel = CCSprite::create("bomb.png");
    CCSize panelSize = target->getContentSize();
    CCPoint panelPoint = target->getPosition();
    panel->setPosition(ccp(panelSize.width / 2, panelSize.height / 2));
    (openPanel(target))->addChild(panel);
    
}

cocos2d::CCSprite* GameScene::openPanel(cocos2d::CCSprite* target){
    
    CCSprite* frontPanel = CCSprite::create("front_panel.png");
    frontPanel->setPosition(target->getPosition());

    this->addChild(frontPanel);
    
    // 八方向のボムの個数を調べて画像を用意する
    int bNum = searchBomb(target);
    if (bNum > 0 && bNum < 9){
    CCString* fileName = CCString::createWithFormat("%d.png", bNum);
    CCSprite* bNumber = CCSprite::create(fileName->getCString());
        bNumber->setPosition(ccp(frontPanel->getContentSize().width / 2,
                                 frontPanel->getContentSize().height / 2));
        frontPanel->addChild(bNumber);
    }
    return frontPanel;
}

void GameScene::initPanels(int width, int height)
{
    m_width = width;
    m_height = height;
    
    CCDirector* pDirector = CCDirector::sharedDirector();
    CCSize winSize = pDirector->getWinSize();
    
    // width x heightパネルを作って表示する.
    for (int y = 0; y < m_height; y++) {
        for (int x = 0; x < m_width; x++) {
            
            CCSprite* panel = CCSprite::create("back_panel.png");
            CCSize conSize = panel->getContentSize();
            panel->setPosition(ccp((winSize.width / 2) + (conSize.width  * (x - (m_width / 2))) ,
                                   (winSize.height/ 2) + (conSize.height  * (y - (m_height) / 2))));
            panel->setTag(kTagClose);
            m_panels->addObject(panel);
        }
    }
    
    // 爆弾をセットする.
    for (int n = 0; n < maxBomb; n++) {
        CCSprite* object = (CCSprite*)m_panels->randomObject();
        if( object->getTag() == kTagClose)
            object->setTag(kTagBomb);
        else
            n--;
    }
}

void GameScene::openPanelAuto(cocos2d::CCSprite *target)
{
    CCPoint targetPoint = target->getPosition();
    CCSize targetSize = target->getContentSize();
    targetPoint.setPoint(targetPoint.x ,
                         targetPoint.y );
    CCObject* it = NULL;
    
    //targetの上のパネルをオープン
    CCRect targetRect = CCRectMake(
                                   targetPoint.x - (targetSize.width / 2),
                                   targetPoint.y - (targetSize.height / 2) + targetSize.height,
                                   targetSize.width ,
                                   targetSize.height
                                   );
    CCARRAY_FOREACH(m_panels, it)
    {
        CCSprite* nTarget = dynamic_cast<CCSprite*>(it);
        CCPoint nTargetPoint = nTarget->getPosition();
        nTargetPoint.setPoint(nTargetPoint.x , nTargetPoint.y);
        if (nTarget->getTag() == kTagClose){

            if (targetRect.containsPoint(nTargetPoint)) {
                nTarget->setTag(kTagOpen);
                openPanel(nTarget);
                openPanelAuto(nTarget);
            }
        }
    }
    
        //targetの左のパネルをオープン
    targetRect = CCRectMake(
                                   targetPoint.x - (targetSize.width / 2) - targetSize.width,
                                   targetPoint.y - (targetSize.height / 2),
                                   targetSize.width ,
                                   targetSize.height
                                   );
    CCARRAY_FOREACH(m_panels, it)
    {
        CCSprite* nTarget = dynamic_cast<CCSprite*>(it);
        CCPoint nTargetPoint = nTarget->getPosition();
        nTargetPoint.setPoint(nTargetPoint.x , nTargetPoint.y);
        if (nTarget->getTag() == kTagClose){
            
            if (targetRect.containsPoint(nTargetPoint)) {
                nTarget->setTag(kTagOpen);
                openPanel(nTarget);
                openPanelAuto(nTarget);
            }
        }
    }
    
        //targetの右のパネルをオープン
    targetRect = CCRectMake(
                            targetPoint.x - (targetSize.width / 2) + targetSize.width,
                            targetPoint.y - (targetSize.height / 2),
                            targetSize.width ,
                            targetSize.height
                            );
    CCARRAY_FOREACH(m_panels, it)
    {
        CCSprite* nTarget = dynamic_cast<CCSprite*>(it);
        CCPoint nTargetPoint = nTarget->getPosition();
        nTargetPoint.setPoint(nTargetPoint.x , nTargetPoint.y);
        if (nTarget->getTag() == kTagClose){
            
            if (targetRect.containsPoint(nTargetPoint)) {
                nTarget->setTag(kTagOpen);
                openPanel(nTarget);
                openPanelAuto(nTarget);
            }
        }
    }
    
    //targetの下のパネルをオープン
    targetRect = CCRectMake(
                            targetPoint.x - (targetSize.width / 2) ,
                            targetPoint.y - (targetSize.height / 2) - targetSize.height,
                            targetSize.width ,
                            targetSize.height
                            );
    CCARRAY_FOREACH(m_panels, it)
    {
        CCSprite* nTarget = dynamic_cast<CCSprite*>(it);
        CCPoint nTargetPoint = nTarget->getPosition();
        nTargetPoint.setPoint(nTargetPoint.x , nTargetPoint.y);
        if (nTarget->getTag() == kTagClose){
            
            if (targetRect.containsPoint(nTargetPoint)) {
                nTarget->setTag(kTagOpen);
                openPanel(nTarget);
                openPanelAuto(nTarget);
            }
        }
    }
}

int GameScene::searchBomb(cocos2d::CCSprite *target)
{
    CCPoint targetPoint = target->getPosition();
    CCSize targetSize = target->getContentSize();
    CCObject* it = NULL;
    
    //targetの周りにいくつの爆弾があるのか調べる
    int num = 0;
    for (int y = -1; y <= 1; y++) {
        for (int x = -1; x <= 1; x++) {
            CCPoint nTargetPoint = target->getPosition();
            nTargetPoint.setPoint(nTargetPoint.x + (targetSize.width * x),
                                  nTargetPoint.y + (targetSize.height * y));
            CCARRAY_FOREACH(m_panels, it){
                CCSprite* lTarget = dynamic_cast<CCSprite*>(it);
                CCRect rec = CCRectMake(lTarget->getPosition().x - (targetSize.width / 2),
                                        lTarget->getPosition().y - (targetSize.height / 2),
                                        targetSize.width, targetSize.height);
                if (rec.containsPoint(nTargetPoint)) {
                    if(isBomb(lTarget))
                        num++;
                }
            }
        }
    }
    return num;
}