Wednesday, October 1, 2014

Cocos2d-x: Làm game đầu tiên - Air Hockey (Phần 2)

1 Xây dựng game

1.1 Chuẩn bị môi trường và Resource

- Chúng ta sẽ phát triển game này cho iPad và iPad Retina, sử dụng IDE Xcode

- Resource
  • Âm thanh: các file âm thanh và nhạc nền cho game
  • Hình ảnh: ảnh của sân bóng, mallet, puck. Vì ta phát triển cho 2 loại kích thước nên sẽ có 2 thư mục ảnh sd và hd. Lưu ý khi add resource ảnh cho 2 thư mục này vào Xcode, không như add âm thanh hay font, chúng ta chỉ add tham chiếu 2 thư mục này để tránh bị báo lỗi trùng tên file bên trong 2 thư mục.




1.2 Định nghĩa các hằng số cho dự án

- Tạo mới một file Constant.h để định nghĩa các hằng số cần thiết cho dự án của chúng ta.
typedef struct tagResource
{
    Size size;
    char directory[100];
} Resource;


static Resource tabletResourceSD    =  { Size(768, 1024),    "sd"};
static Resource tabletResourceHD    =  { Size(1536, 2048),   "hd"};

static Size designResolutionSize    = Size(768, 1024);

#define ZODER_BG        0
#define ZODER_PLAYER    2
#define ZODER_BALL      1
#define ZODER_LABEL     3

#define MATH_PI         3.14159265358979323846f
#define GOAL_WIDTH      400

1.3 Thiết lập hỗ trợ Retina

- Trong file AppDelegate.cpp

auto fileUtils = FileUtils::getInstance();
    std::vector<std::string> searchPaths;
   
    Size screenSize = director->getVisibleSize();
   
    glview->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height, ResolutionPolicy::NO_BORDER);
   
    if (screenSize.width > tabletResourceSD.size.width) {
        director->setContentScaleFactor(tabletResourceHD.size.height / designResolutionSize.height);
        searchPaths.push_back("hd");
        searchPaths.push_back("sd");
    } else {
        director->setContentScaleFactor(tabletResourceSD.size.height / designResolutionSize.height);
        searchPaths.push_back("sd");
    }
    fileUtils->setSearchPaths(searchPaths);
   
    // create a scene. it's an autorelease object
    auto scene = InGameScene::createScene();

    // run
    director->runWithScene(scene);

- Kiểm tra kích thước của màn hình làm việc và xác định đang là màn hình bình thường hay retina. Với màn hình có độ rộng lớn hơn tabletResourceSD.size.width chúng ta xác định là retina, và thiết lập đường dẫn đến thư mục resource là “hd”, tuy nhiên chúng ta cũng add thêm “sd” để phòng trừ trường hợp có một số file ảnh trong “hd” không có thì có thể tìm kiếm ở “sd”.

- Hàm setContentScaleFactor sẽ thiết lập thông số scale cho game, thông số scale này sẽ giúp chúng ta ánh xạ được các tọa độ, kích thước của các kích thước màn hình khác nhau về kích thước DesignResolutionSize mà chúng ta đã định nghĩa thông qua hàm setDesignResolutionSize

1.4 Hiệu ứng âm thanh cho game

- Ta định nghĩa một lớp AudioUtils để phụ trách phần chơi nhạc cho game

typedef enum {
    ATBackgroundMusic,
    ATHit,
    ATGoal
}AudioType;

class AudioUtils
{
public:
    static void prepare();
    static void play(AudioType type);
    static void stopMusic();
};

- Để chơi được nhạc, chúng ta sẽ sử dụng lớp SimpleAudioEngine trong CocosDenshion. Trước khi có thể chơi được các file âm thanh, chúng phải được load chuẩn bị vào bộ nhớ, công việc này sẽ được định nghĩa trong hàm prepare() của lớp AudioUtils
void AudioUtils::prepare()
{
    SimpleAudioEngine::getInstance()->preloadBackgroundMusic(FileUtils::getInstance()->fullPathForFilename("music.wav").c_str());
    SimpleAudioEngine::getInstance()->preloadEffect(FileUtils::getInstance()->fullPathForFilename("hit.wav").c_str());
    SimpleAudioEngine::getInstance()->preloadEffect(FileUtils::getInstance()->fullPathForFilename("goal.wav").c_str());
}
- Và tiếp đến là hàm chơi nhạc
void AudioUtils::play(AudioType type)
{
    switch (type) {
        case ATBackgroundMusic:
        {
            if (!SimpleAudioEngine::getInstance()->isBackgroundMusicPlaying()) {
                SimpleAudioEngine::getInstance()->playBackgroundMusic("music.wav");
            }
        }
            break;
        case ATHit:
        {
            SimpleAudioEngine::getInstance()->playEffect("hit.wav");
        }
            break;
        case ATGoal:
        {
            SimpleAudioEngine::getInstance()->playEffect("goal.wav");
        }
            break;
        default:
            break;
    }
}

1.5 Định nghĩa lớp GameSprite

Thêm mới lớp GameSprite kế thừa từ lớp Sprite của Cocos2d-x.

Ở file GameSprite.h

#include <stdio.h>
#include "cocos2d.h"

using namespace cocos2d;

typedef enum {
    GSWhiteMallet,
    GSBlueMallet,
    GSPuck
}GameSpriteType;

class GameSprite : public Sprite
{
public:
    static GameSprite *createGameSprite(GameSpriteType type);
   
    float radius();
   
    void setNextPosition(Vec2 position);
    Vec2 getNextPosition();
    void setVelocity(Vec2 velocity);
    Vec2 getVelocity();
    void setTouch(Touch *touch);
    Touch* getTouch();
   
private:
    Vec2            _nextPosition;
    Vec2            _velocity;
    Touch           *_touch;
};

- Trong đó
  • _nextPosition: là vị trí sắp di chuyển đến của Sprite theo vector vận tốc hiện thời của nó
  • _velocity: là vector vận tốc 
  • _touch: nắm giữ đối tượng Touch của Sprite để xác định di chuyển khi người chơi di chuyển Sprite (Mallet) 
  • Các hàm set/get thuộc tính cần thiết
Ở trong file GameSprite.cpp, ta sẽ để ý đến hàm create và radius
GameSprite* GameSprite::createGameSprite(GameSpriteType type)
{
    if (type == GSPuck)
    {
        GameSprite *sprite = (GameSprite *)Sprite::create("puck.png");
        sprite->setVelocity(Vec2(0, 0));
        return sprite;
    }
    else if (type == GSWhiteMallet)
    {
        GameSprite *sprite = (GameSprite *)Sprite::create("mallet1.png");
        sprite->setVelocity(Vec2(0, 0));
        return sprite;
    }
    else
    {
        GameSprite *sprite = (GameSprite *)Sprite::create("mallet2.png");
        sprite->setVelocity(Vec2(0, 0));
        return sprite;
    }
}

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

- Trong đó
  • Hàm create sẽ khởi tạo GameSprite theo tham số loại sprite đó là Mallet trắng/xanh hay là Puck
  • Hàm radius sẽ trả về bán kính của đối tượng sprite, bán kính này sẽ được sử dụng để tính toán va chạm 

No comments:

Post a Comment