cocos2d-x 提升篇 (17) 簡單的桌上足球遊戲

火車上遇見發表於2017-04-05

這個例子改編來自Cocos2d-x by Example.

相當於一個簡單的桌上足球遊戲,可以通過觸控的方式碰撞紅色的球,進入對方的球門就可以加一分。

#ifndef  _APP_DELEGATE_H_
#define  _APP_DELEGATE_H_

#include "cocos2d.h"

/**
@brief    The cocos2d Application.

Private inheritance here hides part of interface from Director.
*/
class  AppDelegate : private cocos2d::Application
{
public:
    AppDelegate();
    virtual ~AppDelegate();

    virtual void initGLContextAttrs();

    /**
    @brief    Implement Director and Scene init code here.
    @return true    Initialize success, app continue.
    @return false   Initialize failed, app terminate.
    */
    virtual bool applicationDidFinishLaunching();

    /**
    @brief  Called when the application moves to the background
    @param  the pointer of the application
    */
    virtual void applicationDidEnterBackground();

    /**
    @brief  Called when the application reenters the foreground
    @param  the pointer of the application
    */
    virtual void applicationWillEnterForeground();
};

#endif // _APP_DELEGATE_H_


#include "AppDelegate.h"
#include "HelloWorldScene.h"
#include "GameLayer.h"
#include "TestLayer.h"

#include "SimpleAudioEngine.h"
using namespace CocosDenshion;

using namespace cocos2d;

USING_NS_CC;

// static cocos2d::Size designResolutionSize = cocos2d::Size(480, 320);
static cocos2d::Size designResolutionSize = cocos2d::Size(720, 1280);
static cocos2d::Size smallResolutionSize = cocos2d::Size(480, 320);
static cocos2d::Size mediumResolutionSize = cocos2d::Size(1024, 768);
static cocos2d::Size largeResolutionSize = cocos2d::Size(2048, 1536);

AppDelegate::AppDelegate()
{
}

AppDelegate::~AppDelegate() 
{
}

// if you want a different context, modify the value of glContextAttrs
// it will affect all platforms
void AppDelegate::initGLContextAttrs()
{
    // set OpenGL context attributes: red,green,blue,alpha,depth,stencil
    GLContextAttrs glContextAttrs = {8, 8, 8, 8, 24, 8};

    GLView::setGLContextAttrs(glContextAttrs);
}

// if you want to use the package manager to install more packages,  
// don't modify or remove this function
static int register_all_packages()
{
    return 0; //flag for packages manager
}

bool AppDelegate::applicationDidFinishLaunching() {
    // initialize director
    auto director = Director::getInstance();
    auto glview = director->getOpenGLView();
    if(!glview) {
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)
        glview = GLViewImpl::createWithRect("GameDemo", cocos2d::Rect(0, 0, designResolutionSize.width, designResolutionSize.height));
#else
        glview = GLViewImpl::create("GameDemo");
#endif
        director->setOpenGLView(glview);
    }

    // turn on display FPS
    director->setDisplayStats(false);

    // set FPS. the default value is 1.0/60 if you don't call this
    director->setAnimationInterval(1.0f / 60);

    glview->setFrameSize(720, 1280);

    // Set the design resolution
    glview->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height, ResolutionPolicy::EXACT_FIT);
    /*
    auto frameSize = glview->getFrameSize();
    // if the frame's height is larger than the height of medium size.
    if (frameSize.height > mediumResolutionSize.height)
    {        
        director->setContentScaleFactor(MIN(largeResolutionSize.height/designResolutionSize.height, largeResolutionSize.width/designResolutionSize.width));
    }
    // if the frame's height is larger than the height of small size.
    else if (frameSize.height > smallResolutionSize.height)
    {        
        director->setContentScaleFactor(MIN(mediumResolutionSize.height/designResolutionSize.height, mediumResolutionSize.width/designResolutionSize.width));
    }
    // if the frame's height is smaller than the height of medium size.
    else
    {        
        director->setContentScaleFactor(MIN(smallResolutionSize.height/designResolutionSize.height, smallResolutionSize.width/designResolutionSize.width));
    }
    */
    register_all_packages();

    // preload sound effects
    // SimpleAudioEngine::sharedEngine()->preloadEffect("standard/hit.wav");
    // SimpleAudioEngine::sharedEngine()->preloadEffect("standard/score.wav");

    auto fileUtils = FileUtils::getInstance();
    std::vector<std::string> searchPaths;
    searchPaths.push_back("standard");
    fileUtils->setSearchPaths(searchPaths);

    auto audioEngine = SimpleAudioEngine::getInstance();
    audioEngine->preloadEffect(fileUtils->fullPathForFilename("hit.wav").c_str());
    audioEngine->preloadEffect(fileUtils->fullPathForFilename("score.wav").c_str());

    audioEngine->setBackgroundMusicVolume(0.5f);
    audioEngine->setEffectsVolume(0.5f);

    // create a scene. it's an autorelease object
    auto scene = HelloWorld::createScene();

    // auto scene = GameLayer::createScene();

    // auto scene = TestLayer::createScene();
    // run
    director->runWithScene(scene);

    return true;
}

// This function will be called when the app is inactive. Note, when receiving a phone call it is invoked.
void AppDelegate::applicationDidEnterBackground() {
    Director::getInstance()->stopAnimation();

    // if you use SimpleAudioEngine, it must be paused
    // SimpleAudioEngine::getInstance()->pauseBackgroundMusic();
}

// this function will be called when the app is active again
void AppDelegate::applicationWillEnterForeground() {
    Director::getInstance()->startAnimation();

    // if you use SimpleAudioEngine, it must resume here
    // SimpleAudioEngine::getInstance()->resumeBackgroundMusic();
}


#include "GameLayer.h"
#include "SimpleAudioEngine.h"
using namespace CocosDenshion;

bool GameLayer::init()
{
    if (!Layer::init())
        return false;
    _players = Vector<GameSprite*>(2);
    _player1Score = 0;
    _player2Score = 0;
    _screenSize = Director::getInstance()->getWinSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();


    auto court = Sprite::create("court.png");
    court->setPosition(Vec2(_screenSize.width * 0.5, _screenSize.height * 0.5));
    this->addChild(court);

    _player1 = GameSprite::gameSpriteWithFile("mallet.png");
    _player1->setPosition(Vec2(_screenSize.width/2, _player1->radius()*2));
    _players.pushBack(_player1);
    this->addChild(_player1);

    _player2 = GameSprite::gameSpriteWithFile("mallet.png");
    _player2->setPosition(Vec2(_screenSize.width/2, _screenSize.height - _player1->radius()*2));
    _players.pushBack(_player2);;
    this->addChild(_player2);

    _ball = GameSprite::gameSpriteWithFile("puck.png");
    _ball->setPosition(Vec2(_screenSize.width/2, _screenSize.height/2 - _ball->radius()*2));
    this->addChild(_ball);

    _player1ScoreLabel = Label::createWithTTF("0", "fonts/arial.ttf", 40);
    _player1ScoreLabel->setPosition(Vec2(_screenSize.width/4*3, _screenSize.height/2 - 50));
    _player1ScoreLabel->setAnchorPoint(Vec2(0.5, 0.5));
    _player1ScoreLabel->setRotation(90);
    this->addChild(_player1ScoreLabel);

    _player2ScoreLabel = Label::createWithTTF("0", "fonts/arial.ttf", 40);
    _player2ScoreLabel->setPosition(Vec2(_screenSize.width/4*3, _screenSize.height/2 + 50));
    _player2ScoreLabel->setAnchorPoint(Vec2(0.5, 0.5));
    _player2ScoreLabel->setRotation(90);
    this->addChild(_player2ScoreLabel);

    auto listener = EventListenerTouchAllAtOnce::create();
    listener->onTouchesBegan =
            CC_CALLBACK_2(GameLayer::onTouchesBegan,this);
    listener->onTouchesMoved =
            CC_CALLBACK_2(GameLayer::onTouchesMoved, this);
    listener->onTouchesEnded =
            CC_CALLBACK_2(GameLayer::onTouchesEnded, this);
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);

    // create main loop
    this->scheduleUpdate();

    return true;
}

Scene *GameLayer::createScene()
{
    Scene* scene = Scene::create();
    GameLayer* layer = GameLayer::create();
    scene->addChild(layer);
    return scene;
}

void GameLayer::onTouchesBegan(const std::vector<Touch *> &touches, Event *event)
{
    for (auto touch : touches) {
        if (touch != nullptr) {
            auto tap = touch->getLocation();
            for (auto player : _players) {
                if (player->boundingBox().containsPoint(tap)) {
                    player->setTouch(touch);
                }
            }
        }
    }
}

void GameLayer::onTouchesMoved(const std::vector<Touch *> &touches, Event *event)
{
    for (auto touch : touches) {
        if (touch != nullptr) {
            auto tap = touch->getLocation();
            for (auto player : _players) {
                if (player->getTouch() != nullptr && player->getTouch() == touch) {
                Point nextPosition = tap;
                if (nextPosition.x < player->radius())
                nextPosition.x = player->radius();
                if (nextPosition.x > _screenSize.width - player->radius())
                nextPosition.x = _screenSize.width - player->radius();
                if (nextPosition.y < player->radius())
                    nextPosition.y = player->radius();
                    if (nextPosition.y > _screenSize.height - player->radius())
                    nextPosition.y = _screenSize.height - player->radius();
                    //keep player inside its court
                    if (player->getPositionY() < _screenSize.height* 0.5f) {
                    if (nextPosition.y > _screenSize.height* 0.5 -
                    player->radius()) {
                    nextPosition.y = _screenSize.height* 0.5 -
                    player->radius();
                    }
                    } else {
                    if (nextPosition.y < _screenSize.height* 0.5 +
                    player->radius()) {
                    nextPosition.y = _screenSize.height* 0.5 +
                    player->radius();
                    }
                    }
                    player->setNextPosition(nextPosition);
                    player->setVector(Vec2(tap.x - player->getPositionX(),
                    tap.y - player->getPositionY()));
                    }
                    }
            }
        }
}

void GameLayer::onTouchesEnded(const std::vector<Touch *> &touches, Event *event)
{
    for (auto touch : touches) {
        if (touch != nullptr) {
            auto tap = touch->getLocation();
            for (auto player : _players) {
                if (player->getTouch() != nullptr && player->getTouch() == touch) {
                //if touch ending belongs to this player, clear it
                player->setTouch(nullptr);
                player->setVector(Vec2(0,0));
                }
            }
        }
    }
}

void GameLayer::update(float dt)
{
    CCPoint ballNextPosition = _ball->getNextPosition();
        CCPoint ballVector = _ball->getVector();
        ballVector = ccpMult(ballVector, 0.98f);

        ballNextPosition.x += ballVector.x;
        ballNextPosition.y += ballVector.y;

        //test for puck and mallet collision
        float squared_radii = pow(_player1->radius() + _ball->radius(), 2);

        GameSprite * player;
        CCPoint playerNextPosition;
        CCPoint playerVector;
        for (int p = 0; p < 2; p++) {

            player = _players.at(p);
            playerNextPosition = player->getNextPosition();
            playerVector = player->getVector();

            float diffx = ballNextPosition.x - player->getPositionX();
            float diffy = ballNextPosition.y - player->getPositionY();

            float distance1 = pow(diffx, 2) + pow(diffy, 2);
            float distance2 = pow(_ball->getPositionX() - playerNextPosition.x, 2) + pow(_ball->getPositionY() - playerNextPosition.y, 2);

            if (distance1 <= squared_radii || distance2 <= squared_radii) {

                float mag_ball = pow(ballVector.x, 2) + pow(ballVector.y, 2);
                float mag_player = pow (playerVector.x, 2) + pow (playerVector.y, 2);

                float force = sqrt(mag_ball + mag_player);
                float angle = atan2(diffy, diffx);

                ballVector.x = force * cos(angle);
                ballVector.y = (force * sin(angle));

                ballNextPosition.x = playerNextPosition.x + (player->radius() + _ball->radius() + force) * cos(angle);
                ballNextPosition.y = playerNextPosition.y + (player->radius() + _ball->radius() + force) * sin(angle);

                SimpleAudioEngine::sharedEngine()->playEffect("hit.wav");
            }
        }

        //check collision of ball and sides
        if (ballNextPosition.x < _ball->radius()) {
            ballNextPosition.x = _ball->radius();
            ballVector.x *= -0.8f;
            SimpleAudioEngine::sharedEngine()->playEffect("hit.wav");
        }

        if (ballNextPosition.x > _screenSize.width - _ball->radius()) {
            ballNextPosition.x = _screenSize.width - _ball->radius();
            ballVector.x *= -0.8f;
            SimpleAudioEngine::sharedEngine()->playEffect("hit.wav");
        }
        //ball and top of the court
        if (ballNextPosition.y > _screenSize.height - _ball->radius()) {
            if (_ball->getPosition().x < _screenSize.width * 0.5f - GOAL_WIDTH * 0.5f ||
                _ball->getPosition().x > _screenSize.width * 0.5f + GOAL_WIDTH * 0.5f) {
                ballNextPosition.y = _screenSize.height - _ball->radius();
                ballVector.y *= -0.8f;
                SimpleAudioEngine::sharedEngine()->playEffect("hit.wav");
            }
        }
        //ball and bottom of the court
        if (ballNextPosition.y < _ball->radius() ) {
            if (_ball->getPosition().x < _screenSize.width * 0.5f - GOAL_WIDTH * 0.5f ||
                _ball->getPosition().x > _screenSize.width * 0.5f + GOAL_WIDTH * 0.5f) {
                ballNextPosition.y = _ball->radius();
                ballVector.y *= -0.8f;
                SimpleAudioEngine::sharedEngine()->playEffect("hit.wav");
            }
        }

        //finally, after all checks, update ball's vector and next position
        _ball->setVector(ballVector);
        _ball->setNextPosition(ballNextPosition);


        //check for goals!
        if (ballNextPosition.y  < -_ball->radius() * 2) {
            this->playerScore(2);

        }
        if (ballNextPosition.y > _screenSize.height + _ball->radius() * 2) {
            this->playerScore(1);
        }

        //move pieces to next position
        _player1->setPosition(_player1->getNextPosition());
        _player2->setPosition(_player2->getNextPosition());
        _ball->setPosition(_ball->getNextPosition());

}

void GameLayer::playerScore(int player)
{
    SimpleAudioEngine::sharedEngine()->playEffect("score.wav");

       _ball->setVector(CCPointZero);

       char score_buffer[10];

       //if player 1 scored...
       if (player == 1) {

           _player1Score++;
           sprintf(score_buffer,"%i", _player1Score);
           _player1ScoreLabel->setString(score_buffer);
           //move ball to player 2 court
           _ball->setNextPosition(ccp(_screenSize.width * 0.5, _screenSize.height * 0.5 + 2 * _ball->radius()));

       //if player 2 scored...
       } else {

           _player2Score++;
           sprintf(score_buffer,"%i", _player2Score);
           _player2ScoreLabel->setString(score_buffer);
           //move ball to player 1 court
           _ball->setNextPosition(ccp(_screenSize.width * 0.5, _screenSize.height * 0.5 - 2 * _ball->radius()));

       }
       //move players to original position
       _player1->setPosition(ccp(_screenSize.width * 0.5, _player1->radius() * 2));
       _player2->setPosition(ccp(_screenSize.width * 0.5, _screenSize.height - _player1->radius() * 2));

       //clear current touches
       _player1->setTouch(NULL);
       _player2->setTouch(NULL);
}

GameLayer::GameLayer()
{

}

GameLayer::~GameLayer()
{

}

#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__

#include "cocos2d.h"

class HelloWorld : public cocos2d::Layer
{
public:
    static cocos2d::Scene* createScene();

    virtual bool init();
    
    // a selector callback
    void menuCloseCallback(cocos2d::Ref* pSender);
    
    // implement the "static create()" method manually
    CREATE_FUNC(HelloWorld);
};

#endif // __HELLOWORLD_SCENE_H__

#include "HelloWorldScene.h"
#include "SimpleAudioEngine.h"
#include "GameLayer.h"
#include "RocketThrough/RocketThrough.h"

USING_NS_CC;

Scene* HelloWorld::createScene()
{
    // 'scene' is an autorelease object
    auto scene = Scene::create();
    
    // 'layer' is an autorelease object
    auto layer = HelloWorld::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 HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }

    auto _screenSize = Director::getInstance()->getWinSize();
    
    auto visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->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
    auto closeItem = MenuItemImage::create(
                                           "CloseNormal.png",
                                           "CloseSelected.png",
                                           CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
    
    closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width/2 ,
                                origin.y + closeItem->getContentSize().height/2));

    // create menu, it's an autorelease object
    auto menu = Menu::create(closeItem, NULL);
    menu->setPosition(Vec2::ZERO);
    this->addChild(menu, 1);



    // create menuMenu, which game to choose
    auto menuItem1 = MenuItemFont::create("Air Hockey");
    menuItem1->setFontSizeObj(60);
    menuItem1->setPosition(Vec2(_screenSize.width/2, _screenSize.height/5*4));

    menuItem1->setCallback([&](Ref *sender){
        auto director = Director::getInstance();
        director->replaceScene(GameLayer::createScene());
    });
    
    auto menuItem2 = MenuItemFont::create("Rocket Through");
    menuItem2->setFontSizeObj(60);
    menuItem2->setPosition(Vec2(_screenSize.width/2, _screenSize.height/5*3));

    menuItem2->setCallback([&](Ref *sender) {
        Director::getInstance()->replaceScene(RocketThrough::createScene());
    });

    auto menuItem3 = MenuItemFont::create("");


    cocos2d::Vector<MenuItem *> menuItems;
    menuItems.pushBack(menuItem1);
    menuItems.pushBack(menuItem2);

    auto menu1 = Menu::createWithArray(menuItems);
    menu1->setPosition(Vec2::ZERO);

    this->addChild(menu1);
    return true;
}


void HelloWorld::menuCloseCallback(Ref* pSender)
{
    //Close the cocos2d-x game scene and quit the application
    Director::getInstance()->end();

    #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    exit(0);
#endif
    
    /*To navigate back to native iOS screen(if present) without quitting the application  ,do not use Director::getInstance()->end() and exit(0) as given above,instead trigger a custom event created in RootViewController.mm as below*/
    
    //EventCustom customEndEvent("game_scene_close_event");
    //_eventDispatcher->dispatchEvent(&customEndEvent);
    
    
}

#ifndef __GAMESPRITE_H__
#define __GAMESPRITE_H__

#include "cocos2d.h"

using namespace cocos2d;

class GameSprite : public Sprite {
public:
    CC_SYNTHESIZE(Vec2, _nextPosition, NextPosition);
    CC_SYNTHESIZE(Vec2, _vector, Vector);
    CC_SYNTHESIZE(Touch*, _touch, Touch);

    GameSprite();
    virtual ~GameSprite();
    static GameSprite* gameSpriteWithFile(const char* pszFileName);

    virtual void setPosition(const Vec2& pos) override;
    float radius();
};
#endif

#include "GameSprite.h"

GameSprite::GameSprite()
{
    _vector = Vec2(0, 0);
}

GameSprite::~GameSprite()
{

}

GameSprite *GameSprite::gameSpriteWithFile(const char *pszFileName)
{
    auto sprite = new GameSprite();
    if (sprite && sprite->initWithFile(pszFileName)) {
        sprite->autorelease();
        return sprite;
    }
    CC_SAFE_DELETE(sprite);
    return sprite = nullptr;
}

void GameSprite::setPosition(const Vec2 &pos)
{
    Sprite::setPosition(pos);
    if (!_nextPosition.equals(pos)) {
        _nextPosition = pos;
    }
}

float GameSprite::radius()
{
    return getTexture()->getContentSize().width * 0.5f;
}



相關文章