Tuesday, October 7, 2014

Cocos2d-x: Game Sky Defense (Phần 3)

1.1 Các Sprite trong lớp InGameScene

class InGameScene : public Layer

{

public:

      static Scene *createScene();

public:

      virtual bool init();

      CREATE_FUNC(InGameScene);

      ~InGameScene();

      virtual void onEnter();

. . .

}


1.1.1 Thêm background

- Add background cho game, background là 1 ảnh khá lớn, do vậy chúng ta không đưa nó vào sprite sheet ở trên, mà sẽ là một ảnh riêng biệt trong thư mục resource

screenSize_ = Director::getInstance()->getVisibleSize();

origin_ = Director::getInstance()->getVisibleOrigin();

Sprite *bg = Sprite::create("bg.png");

bg->setPosition(Vec2(screenSize_.width * 0.5f + origin_.x, screenSize_.height * 0.5f + origin_.y));

this->addChild(bg, ZODER_BG);

origin_ là điểm tọa độ gốc xác định ban đầu của vùng hiển thị game, thường thì điểm này là điểm (0,0), tuy nhiên trong một số trường hợp, khi mà tỉ lệ scale màn hình làm việc không đồng nhất, điểm này sẽ có giá trị khác, và khi đó, các tọa độ điểm khác của chúng ta cần phụ thuộc vào vị trí của điểm origin_ này.

1.1.2 Thêm các sprite sprite sheet

Để làm việc với sprite sheet, chúng ta cần phải khởi tạo một batch node SpriteBatchNode để load các sprite này, cần dùng đến SpriteFrameCache để lưu cache các thông tin load sprite từ file .plist.


SpriteFrameCache::getInstance()->addSpriteFramesWithFile("sprite_sheet.plist");

gameBatchNode_ = SpriteBatchNode::create("sprite_sheet.png");

this->addChild(gameBatchNode_);

Bước tiếp theo chúng ta sẽ add các sprite “thành phố” trong game. Như đã nói ở trên, thành phố sẽ có 2 lớp, ở xa - ở gần để tạo cảm giác về độ sâu của game.


for (int i = 0; i < 2; i ++) {

      sprite = Sprite::createWithSpriteFrameName("city_light.png");

   sprite->setPosition(Vec2(screenSize_.width * (0.25f + i * 0.5f) + origin_.x, sprite->getBoundingBox().size.height * 0.9f + origin_.y));

      gameBatchNode_->addChild(sprite, ZODER_CITY_BACKGROUND);

}



for (int i = 0; i < 2; i ++) {

      sprite = Sprite::createWithSpriteFrameName("city_dark.png");

      sprite->setPosition(Vec2(screenSize_.width * (0.25f + i * 0.5f) + origin_.x, sprite->getBoundingBox().size.height * 0.5f + origin_.y));

      gameBatchNode_->addChild(sprite, ZODER_CITY_FOREGROUND);

}

Đoạn mã trên sẽ thêm 2 lớp thành phố xa và gần vào game. Để ý thấy, để lấy kích thước vùng biên của một sprite, chúng ta sử dụng hàm getBoundingBox(), khác với lần trước ở game Air Hockey, chúng ta sử dụng hàm getTexture(). Hàm getTexture(), nó sẽ trả về hình ảnh texture của sprite, như vậy ở game Air Hockey, nó sẽ trả về đúng hình ảnh của sprite đó, nhưng ở trong trường hợp này hàm này sẽ trả về nguyên cả hình ảnh sprite sheet ở trên.

Để thêm các sprite vào layer, lúc này chúng ta không dùng layer InGameScene để add nữa mà dùng SpriteBatchNode để add sprite. Việc này có lợi ích gì?

- Thực ra, sau khi chúng ta load các frameName từ file .plist vào SpriteFrameCache, chúng ta hoàn toàn có thể khởi tạo các sprite từ frameName và add chúng vào lớp cha là Layer (trong trường hợp này là InGameScene).

- Tuy nhiên, SpriteBatchNode sẽ hữu ích trong trường hợp chúng ta sử dụng nhiều sprite với cùng 1 ảnh resource lại nhiều lần (ví dụ trong game có sprite thiên thạch, hộp cứu thương…). Và ảnh resource là một sprite sheet đóng gói nhiều ảnh texture vào 1 ảnh chung.

- SpriteBatchNode sẽ giảm thiểu được số lần gọi khi render game, tuy nhiên nó chỉ thực sự tốt cho các hệ thống thiết bị yếu, còn như các thiết bị của Apple thì thực sự không có nhiều hiệu quả.


Thêm lớp cây cối vào thành phố, icon “máu” vào bên trên màn hình game


for (int i = 0; i < 3; i ++) {

      sprite = Sprite::createWithSpriteFrameName("trees.png");

      sprite->setPosition(Vec2(screenSize_.width * (0.2f + i * 0.3f) + origin_.x, sprite->getBoundingBox().size.height * 0.5f + origin_.y));

      gameBatchNode_->addChild(sprite, ZODER_CITY_FOREGROUND);

}

Sprite *sprtHealth = Sprite::createWithSpriteFrameName("health_icon.png");

sprtHealth->setAnchorPoint(Vec2(0, 0.5));

sprtHealth->setPosition(Vec2(screenSize_.width * 0.05f + origin_.x, screenSize_.height * 0.9f + origin_.y));

gameBatchNode_->addChild(sprtHealth, ZODER_TEXT);

1.1.3 Sử dụng Font bitmap


Trong file InGameScene.h


class InGameScene : public Layer

{

private:

      SpriteBatchNode *gameBatchNode_;

      Label *lblHealth_;

      Label *lblScore_;

};

Trong file InGameScene.cpp

lblHealth_ = Label::createWithBMFont("font.fnt", "100%");

lblHealth_->setAnchorPoint(Vec2(0, 0.5));

lblHealth_->setPosition(Vec2(screenSize_.width * 0.15f + origin_.x, pos.y));

this->addChild(lblHealth_, ZODER_TEXT);



lblScore_ = Label::createWithBMFont("font.fnt", "0");

lblScore_->setAnchorPoint(Vec2(0, 0.5));

lblScore_->setPosition(Vec2(screenSize_.width * 0.9f + origin_.x, pos.y));

this->addChild(lblScore_, ZODER_TEXT);

1.1.4 Lớp các đám mây bay


Trong game có các đám mây bay ngang qua bầu trời, chúng ta sẽ thêm chúng vào game, sau đó sẽ quản lý bên trong 1 mảng, ở vòng lặp chính của game, chúng ta sẽ cho các đám mây này thay đổi vị trí và trôi từ từ trên bầu trời.

Trong file InGameScene.h

class InGameScene : public Layer

{

      Vector<Sprite*> clouds_;

};

Trong file InGameScene.cpp


clouds_ = Vector<Sprite*>(4);

Sprite *cloud;

for (int i = 0; i < 4; i ++) {

      cloud = Sprite::createWithSpriteFrameName("cloud.png");

      int cloudY = i % 2 == 0 ? screenSize_.height * 0.45f : screenSize_.height * 0.55f;

      cloud->setPosition(Vec2(screenSize_.width * 0.1f + i * screenSize_.width * 0.3f, cloudY));

      gameBatchNode_->addChild(cloud, ZODER_CLOUD);

      clouds_.pushBack(cloud);

}

1.1.5 Đối tượng quả Bom


Trong file InGameScene.h


Sprite *bomb_;

Sprite *shockWave_;

Trong file InGameScene.cpp


bomb_ = Sprite::createWithSpriteFrameName("bomb.png");

bomb_->getTexture()->generateMipmap();

bomb_->setVisible(false);//ẩn đi quả bom, chỉ hiển thị khi user touch



Size size = bomb_->getBoundingBox().size;

Sprite *sparkle = Sprite::createWithSpriteFrameName("sparkle.png");

sparkle->setPosition(Vec2(size.width * 0.72f, size.height * 0.72f));

bomb_->addChild(sparkle, ZODER_BOMB, TAG_SPARKLE);

gameBatchNode_->addChild(bomb_);



Sprite *halo = Sprite::createWithSpriteFrameName("halo.png");

halo->setPosition(Vec2(size.width * 0.4f, size.height * 0.4f));

bomb_->addChild(halo, ZODER_BOMB, TAG_HALO);



shockWave_ = Sprite::createWithSpriteFrameName("shockwave.png");

shockWave_->getTexture()->generateMipmap();

shockWave_->setVisible(false);

gameBatchNode_->addChild(shockWave_);

Ta thấy có hàm bomb_->getTexture()->generateMipmap() được gọi, hàm này có mục đích là làm giảm độ “răng cưa” của ảnh khi scale hoặc zoom một texture. Khi chúng ta scale texture nhỏ đến một mức độ nào đó, “răng cưa” chắc chắn sẽ xảy ra. Khi đó, nếu Mipmap được bật, Cocos2d-x sẽ tự động tạo ra một texture có độ phân giải nhỏ để làm mịn hơn khi được scale nhỏ lại.

- sparkle là đống lửa cháy trên quả bom

- halo là quầng sáng bao quanh quả bom

- shockWave là sóng xung lực, chuẩn bị khi quả bom nổ sẽ sinh ra làn sóng này, các thiên thạch bị sóng này đi qua sẽ bị phát nổ

1.1.6 Thêm các đối tượng “Text”

startMessage_ = Sprite::createWithSpriteFrameName("logo.png");

startMessage_->setPosition(Vec2(screenSize_.width * 0.5f + origin_.x, screenSize_.height * 0.5f));

gameBatchNode_->addChild(startMessage_, ZODER_TEXT);


gameOverMessage_ = Sprite::createWithSpriteFrameName("gameover.png");

gameOverMessage_->setPosition(Vec2(screenSize_.width * 0.5f + origin_.x, screenSize_.height * 0.5f));

gameOverMessage_->setVisible(false);

gameBatchNode_->addChild(gameOverMessage_, ZODER_TEXT);

Kết quả cuối cùng sau khi thêm các đối tượng game


1.1.7 Thiên thạch, hộp cứu thương

Các thiên thạch rơi từ bầu trời, chúng ta sẽ khởi tạo chúng và quản lý vào một mảng, sẽ lần lượt hiển thị chúng. Tương tự cho các hộp cứu thương, chúng ta cũng sử dụng mảng để quản lý như vậy.

Trong file InGameScene.h

class InGameScene : public Layer

{

private:

      void createPools();

private:

     Vector<Sprite*> meteorPool_;

     Vector<Sprite*> healthPool_;

};

Trong file InGameScene.cpp


void InGameScene::createPools()

{

     Sprite *sprite;

     meteorPool_ = Vector<Sprite*>(50);

     meteorPoolIndex_ = 0;


     for (int i = 0; i < 50; i ++) {

     sprite = Sprite::createWithSpriteFrameName("meteor.png");

     sprite->setVisible(false);

     gameBatchNode_->addChild(sprite, ZODER_METEOR, TAG_METEOR);

     meteorPool_.pushBack(sprite);

     }


     healthPool_ = Vector<Sprite*>(20);

     for (int i = 0; i < 20; i ++) {

          sprite = Sprite::createWithSpriteFrameName("health.png");

          sprite->setVisible(false);

          sprite->setAnchorPoint(Vec2(0.5f, 0.8f));

          sprite->setRotation(-45);

          gameBatchNode_->addChild(sprite, ZODER_METEOR, TAG_HEALTH);

          healthPool_.pushBack(sprite);

     }

}


Cocos2d-x: Game Sky Defense (Phần cuối)

No comments:

Post a Comment