Thursday, August 7, 2014

Bài 30: Học làm game thứ 5 - Space Ship ( Part 2 - End )


Hi, Rảnh rỗi tranh thủ viết cho xong game Space Ship này.

Bài trước chúng ta đã thiết kế sơ bộ xong phần màn chơi, song còn thiếu một số phần quan trọng trong game nên có mà chúng ta sẽ bổ sung ngay sau đây:

+ Bắn đạn khi Touch màn hình
+ Bắt sự kiện va chạm giữa đạn và thiên thạch
+ Tính điểm
+ Game Over

Đơn giản có thế thôi, chúng ta sẽ lướt nhanh!

B1: Bắn đạn khi Touch màn hình

Bạn mở file HelloWorldScene.h thêm vào dòng lệnh sau, trong public

// Hàm bắt sự kiện touch, dùng multiTouch, hoặc Touch thôi cũng được
void HelloWorld::onTouchesBegan(const std::vector<Touch*>& touches, Event *event)

Tiếp đó trong HelloWorldScene.cpp ta thiết kế hàm này như sau

void HelloWorld::onTouchesBegan(const std::vector<Touch*>& touches, Event *event)
{
SimpleAudioEngine::getInstance()->playEffect("laser_ship.wav"); // Âm thanh

Size winSize = Director::getInstance()->getWinSize();

// Lấy sprite Laser từ bộ lưu trữ Vector
Sprite *shipLaser = (Sprite *) _shipLasers->at(_nextShipLaser++);

if ( _nextShipLaser >=_shipLasers->size())   // Reset index laser
_nextShipLaser = 0;
// Đặt vị trí ở phía mũi tàu, và cho hiện lên
shipLaser->setPosition(Point(_ship->getPosition().x + shipLaser->getContentSize().width/2, _ship->getPosition().y));
shipLaser->setVisible(true);
// set body
auto laserbody = PhysicsBody::createBox(shipLaser->getContentSize()/2);  

laserbody->setContactTestBitmask(0xf);  
laserbody->setDynamic(true);
shipLaser->setPhysicsBody(laserbody);

// Di chuyển đạn, gọi tới hàm setInvisible để xử lý
shipLaser->stopAllActions();
shipLaser->runAction(Sequence::create( 
MoveBy::create(0.5,Point(winSize.width, 0)),
CallFuncN::create(this, callfuncN_selector(HelloWorld::setInvisible)), 
NULL 
));
}

B2: Bắt sự kiện va chạm

Thêm hàm sau vào file HelloWorldScene.h

bool onContactBegin(const PhysicsContact &contact);

Và xây dựng nó trong file HelloWorldScene.cpp như sau

bool HelloWorld::onContactBegin(const PhysicsContact& contact)    
{
auto laser = (Sprite*)contact.getShapeA()->getBody()->getNode();
int Tag1 = -1;
if(laser) 
Tag1 = laser->getTag();
auto asteroid = (Sprite*)contact.getShapeB()->getBody()->getNode();
int Tag2 = -1;
if(asteroid) Tag2 =  asteroid->getTag();

//Va chạm giữa đạn và Thiên Thạch
if((Tag1==KLASER&Tag2==KASTEROID)||(Tag2==KLASER&Tag1==KASTEROID))
{
SimpleAudioEngine::sharedEngine()->playEffect("explosion_large.wav"); 
_world->removeBody(laser->getPhysicsBody());
laser->setVisible(false);
_world->removeBody(asteroid->getPhysicsBody());
asteroid->setVisible(false); 
}
// Va chạm giữa thiên thạch và Ship
if((Tag1==KSHIP&Tag2==KASTEROID)||(Tag2==KSHIP&Tag1==KASTEROID))

{
_lives--;

}

return true; 
}

Và không được quên đoạn code Listener ở init()

auto contactListener = EventListenerPhysicsContact::create();
contactListener->onContactBegin = CC_CALLBACK_1(HelloWorld::onContactBegin, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);

OK, giờ là tới phần Tính điểm và GameOver

B3: Tính điểm và GameOver

Trong hàm update() bạn thêm vào 1 đoạn sau đây

if (_lives <= 0) { // Kiểm tra không còn mạng nào thì game Over
_ship->stopAllActions();
_ship->setVisible(false);
_world->removeBody(_ship->getPhysicsBody());
this->endScene(KENDREASONLOSE);   // Game Over

Hàm endScene xây dựng như sau

void HelloWorld::endScene( EndReason endReason ) {

if (_gameOver) // trạng thái game
return;
_gameOver = true;

Size winSize = Director::getInstance()->getWinSize();

char message[10] = "";
if ( endReason == KENDREASONLOSE)
strcpy(message,"You Lose"); 

// Tạo 2 Label để làm thông báo
LabelBMFont * label ;
label = LabelBMFont::create(message, "Arial.fnt");
label->setScale(0.1);
label->setPosition(Point(winSize.width/2 , winSize.height*0.6));
this->addChild(label);


// Tạo 1 nút reset game là 1 label
LabelBMFont * restartLabel;
strcpy(message,"Restart");
restartLabel = LabelBMFont::create(message, "Arial.fnt");

MenuItemLabel *restartItem =  MenuItemLabel::create(restartLabel,CC_CALLBACK_1(HelloWorld::resetGame,this));

restartItem->setScale(0.1);
restartItem->setPosition( Point(winSize.width/2, winSize.height*0.4));

Menu *menu = Menu::create(restartItem, NULL);
menu->setPosition(Point::ZERO);
this->addChild(menu);

restartItem->runAction(ScaleTo::create(0.5, 1.0));
label ->runAction(ScaleTo::create(0.5, 1.0));
this->unscheduleUpdate(); // dừng update Scene
}

Và nhớ phải thêm thuộc tính bool _gameOver vào phần public của HelloWorldScene.h, đồng thời trong hàm init() phải khởi tạo nó với giá trị false

Bổ sung hàm endScene() và resetGame() vào trong lớp HelloWorld, và hàm resetGame như sau

void HelloWorld::resetGame(Ref* pSender) {
auto scene = HelloWorld::createScene();
Director::getInstance()->replaceScene(TransitionZoomFlipY::create(0.5, scene));  

Size winSize = Director::getInstance()->getVisibleSize();

}

Giờ ta thêm 1 chút phần tính điểm. Khi bắn mỗi thiên thạch ta được 10 điểm.

Bạn thêm 1 thuộc tính int score, LabelBMFont * _scoreDisplay;vào lớp HelloWorldScene, và khi khởi tạo thêm đoạn code này

_scoreDisplay = LabelBMFont::create("Score: 0", "Arial.fnt", 
visibaleSize.width * 0.3f);
_scoreDisplay->setAnchorPoint(Point(1, 0.5));
_scoreDisplay->setPosition(
Point(visibaleSize.width * 0.8f, visibaleSize.height * 0.94f));
this->addChild(_scoreDisplay);

Trong hàm kiểm tra va chạm chúng ta sẽ tính điểm bằng đoạn code nhỏ như thế này

score+=10;
char szValue[100] = { 0 }; // Lấy ra điểm qua mảng đệm char
sprintf(szValue, "Score: %i", score); // Chuyển sang chuỗi => chuỗi
_scoreDisplay->setString(szValue); // Hiện điểm lên

Bạn có thể làm thế với Live để theo dõi số mạng của Ship

OK, Build thử xem kết quả thế nào nhé, cũng không tệ với 1 game "tự tui".

Và sau đây mình làm thêm 1 bước Bonus nữa là 

Bonus: Điều khiển Ship bằng Accelerometer - gia tốc kế

Trước hết bạn copy 2 file VisibleRect.h, .cpp trong bài cpp-tests vào Class của chúng ta. sau đó trong phần init() thêm đoạn code này vào

#define FIX_POS(_pos, _min, _max) \
if (_pos < _min)        \
_pos = _min;        \
else if (_pos > _max)   \
_pos = _max; 

auto listener = EventListenerAcceleration::create([=](Acceleration* acc, Event* event){
auto shipSize  = _ship->getContentSize();

auto ptNow  = _ship->getPosition();

log("acc: x = %lf, y = %lf", acc->x, acc->y);

ptNow.x += acc->x * 9.81f;
ptNow.y += acc->y * 9.81f;

FIX_POS(ptNow.x, (VisibleRect::left().x+shipSize.width / 2.0), (VisibleRect::right().x - shipSize.width / 2.0));
FIX_POS(ptNow.y, (VisibleRect::bottom().y+shipSize.height / 2.0), (VisibleRect::top().y - shipSize.height / 2.0));
_ship->setPosition(ptNow);
});

auto dispathcher = Director::getInstance()->getEventDispatcher();

dispathcher->addEventListenerWithSceneGraphPriority(listener, this);

Vậy thôi, hãy build lại và thử trên ĐT thật, khi nghiêng xem Ship có di chuyển không nhé, nếu di chuyển là đã thành công

Kết thúc bài này, chúng ta cùng nghiên cứu 1 số vấn đề sau

+ Bắn đạn = Touche, duyệt vector
+ Va chạm
+ Tính điểm, game Over
+ Di chuyển Ship bằng gia tốc kế

Download Code

Mình dừng bài học ở đây nhé

Bài 31: Làm game gì bây giờ?

32 comments:

  1. anh có thể làm 1 ví dụ về sliding menu grid không ạ?

    ReplyDelete
  2. Sự khác nhau giữa hàm schedule() và scheduleUpdate() Mình thấy cách sử dụng hàm schedule() sẽ thường có tham số là một selector(...) khi muốn update một cái gì đó (vị trí,...) Thế còn hàm scheduleUpdate() cách hoạt động nó ra sao bạn? nó sẽ tự gọi hàm update(float dt) cho mình luôn hay sao có phải khi mình dùng this->scheduleUpdate() thì nó sẽ tự cập nhật những gì trong hàm update(float dt) có phải không và thời gian nó thực hiện update là bao lâu khi mình gọi this->scheduleUpdate()

    ReplyDelete
    Replies
    1. Bạn có thể vào wiki để tra về các loại schedule luôn

      http://www.cocos2d-x.org/reference/native-cpp/V3.2/index.html

      Theo mình hiểu thì

      scheduleUpdate() ngầm định gọi tới update(float dt) ( dt xác định ở file Appdelegate

      schedule() thì ko có tham số ngầm định, bạn phải chỉ rõ hàm nào ( có dạng tham số float dt ) thì sẽ gọi tới hàm đó, dt mình tự phải truyền vào

      Đơn giản vậy thôi

      Delete
  3. Là một người chân ướt chân ráo nhưng đã vào mò vào bài cuối của series này... Cám ơn các bạn ( các anh , chị ) đã truyền nhiệt huyết cho mình. Xin các bậc tiền bối chỉ bảo... và đừng ngạc nhiên khi mình cmt vào những Post đầu với những câu hỏi rất ngu nhé vì mình rất muốn học hỏi...

    ReplyDelete
  4. bạn ơi cho mình hỏi ở phần gia tốc kế, mình muốn cho ship của mình khi lắc màn hình về bên phải thì nó di chuyển về phía bên phải và bên trái thì di chuyển về bên trái thì làm như thế nào. ở đây, là ship di chuyển lên trên và phía dưới màn hình đúng ko

    ReplyDelete
  5. Xin lỗi vì mình không biết đặt câu hỏi ở đâu nên đành đặt câu hỏi ở đây vậy:
    Mình cần chia 1 bức ảnh làm 9 phần bằng nhau và Sắp Xếp Ngẫu Nhiên 9 phần này trong ô vuông 3x3.
    Mình đã chia được bức ảnh làm 9 phần bằng nhau rồi:
    auto spriteItem1 = Sprite::create(text, Rect(0, 0, 200, 200));
    auto spriteItem2 = Sprite::create(text, Rect(200, 0, 200, 200));
    auto spriteItem3 = Sprite::create(text, Rect(400, 0, 200, 200));
    auto spriteItem4 = Sprite::create(text, Rect(0, 200, 200, 200));
    auto spriteItem5 = Sprite::create(text, Rect(200, 200, 200, 200));
    auto spriteItem6 = Sprite::create(text, Rect(400, 200, 200, 200));
    auto spriteItem7 = Sprite::create(text, Rect(0, 400, 200, 200));
    auto spriteItem8 = Sprite::create(text, Rect(200, 400, 200, 200));
    auto spriteItem9 = Sprite::create(text, Rect(400, 400, 200, 200));
    Các bạn chỉ cho mình cách sắp xếp ngẫu nhiên các ảnh này vào ô vuông 3x3 với. Mình cảm ơn rất nhiều.

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
    2. vector sprites = {spriteItem1, spriteItem2, spriteItem3, spriteItem4, spriteItem5, spriteItem6, spriteItem7, spriteItem8, spriteItem9};
      static int count;
      static Vec2 pos;
      for (int i = 0; i < (int)sprites.size(); i++) {
      int index = (int)sprites.size() - count;
      index = rand() % index;
      Sprite * sprite = (Sprite*)sprites.at(index);
      float width = sprite->getContentSize().width;
      float height = sprite->getContentSize().height;
      pos.x = i * width;
      pos.y = (i==0) ? height : ((i+1)%3==0) ? (i+1)/3 * height : pos.y;
      sprite->setPosition(pos);
      sprites.earse(index);
      count++;
      }

      Delete
    3. @Hieu Trinh
      Rất cảm ơn bạn đã dành thời gian chỉ bảo giúp mình. :D

      Delete
    4. This comment has been removed by the author.

      Delete
    5. This comment has been removed by the author.

      Delete
    6. using namespace std;
      ...
      vector sprites = {spriteItem1, spriteItem2, spriteItem3, spriteItem4, spriteItem5, spriteItem6, spriteItem7, spriteItem8, spriteItem9};
      static int count;
      static Vec2 pos;
      for (int i = 0; i < (int)sprites.size(); i++) {
      int index = (int)sprites.size() - count;
      index = rand() % index;
      Sprite * sprite = (Sprite*)sprites.at(index);
      float width = sprite->getContentSize().width;
      float height = sprite->getContentSize().height;
      pos.x = i * width;
      pos.y = (i==0) ? height : ((i+1)%3==0) ? (i+1)/3 * height : pos.y;
      sprite->setPosition(pos);
      sprites.earse(index); // Lệnh này viết đúng không bạn? "erase" hay là "earse" vậy :(
      count++;
      }

      Delete
    7. Sorry, sai chính tả, dòng đó đúng ra phải là

      sprites.erase(index);

      bạn sửa lại "earse" thành "erase" nhé.

      Delete
    8. Như thế vẫn có hiện ra lỗi bạn ạ. Hình như bạn chưa test thử đoạn code đó thì phải.
      Bạn dùng "vector" trong câu lệnh:
      vector sprites = {spriteItem1, spriteItem2, spriteItem3, spriteItem4, spriteItem5, spriteItem6, spriteItem7, spriteItem8, spriteItem9};
      thì câu lệnh sprites.erase(index) không sử dụng được?
      mình thay bằng :
      Vector sprites;
      sprites.pushBack(spriteItem1);
      sprites.pushBack(spriteItem2);
      sprites.pushBack(spriteItem3);
      sprites.pushBack(spriteItem4);
      sprites.pushBack(spriteItem5);
      sprites.pushBack(spriteItem6);
      sprites.pushBack(spriteItem7);
      sprites.pushBack(spriteItem8);
      sprites.pushBack(spriteItem9);
      thì sử dụng được.
      Nhưng kết quả không nằm trong ô vuông 3X3 mà nằm trên 1 hàng ngang.
      Bạn xem lại giùm mình với.

      Delete
    9. Sorry, mình pase nhầm công thức tính X và Y cho bạn, bạn sử dụng Vector của cocos2d-x cũng được, còn vector kia là std::vector của C++. Vậy đây là code hoàn chỉnh cho bạn.

      Vector sprites;
      sprites.pushBack(spriteItem1);
      sprites.pushBack(spriteItem2);
      sprites.pushBack(spriteItem3);
      sprites.pushBack(spriteItem4);
      sprites.pushBack(spriteItem5);
      sprites.pushBack(spriteItem6);
      sprites.pushBack(spriteItem7);
      sprites.pushBack(spriteItem8);
      sprites.pushBack(spriteItem9);
      static int count;
      static Vec2 pos;
      for (int i = 0; i < (int)sprites.size(); i++) {
      int index = (int)sprites.size() - count;
      index = rand() % index;
      Sprite * sprite = (Sprite*)sprites.at(index);
      float width = sprite->getContentSize().width;
      float height = sprite->getContentSize().height;
      pos.x =(i%3==0) ? 0 : ((i+1)%3==0) ? 400 : i%3 * 200;
      pos.y = (i<3) ? 0 : (i<6) ? 200 : 400;
      sprite->setPosition(pos);
      sprites.erase(index);
      count++;
      }

      Delete
  6. This comment has been removed by the author.

    ReplyDelete
  7. @ Chè Thái Nguyên

    Làm cái hướng dẫn về Design Pattern trong lập trình game bằng Cocos2d-x đê. Mình sẽ chuyển code mẫu và support cho.

    Nếu quan tâm thì email cho mình hieut@creative-game.com

    ReplyDelete
  8. Mình có một câu hỏi:
    Có thể dùng update(float dt) và schedule ở một hàm khác ko ?

    ReplyDelete
  9. @Sĩ Luận: Cái đó còn tuỳ vào hàm của bạn có phải là 1 phương thức nằm trong 1 lớp được kế thừa từ lớp Ref của cocos2d-x hay không.
    Bạn có thể tham khảo thêm thông tin ở đây: http://www.cocos2d-x.org/reference/native-cpp/V3.2/dc/d82/classcocos2d_1_1_scheduler.html#ad86f74fb8869d343245fabbd206ddbf8

    ReplyDelete
  10. @Hieu Trinh: Rất cảm ơn bạn đã dành thời gian xem xét và giúp mình giải quyết bài toán sắp xếp ngẫu nhiên 9 hình ảnh được cắt ra từ 1 hình ảnh trong ô vuông 3x3.
    Nhưng có vẻ đoạn code sau khi được gửi lên blog thì bị thiếu 1 số phần.
    Mong bạn có thể gửi đoạn code của bạn vào gmail buithanhtung.mrtest@gmail.com hoặc skyper: youinmyeyes@outlook.com giúp mình với.
    Và giúp mình giải thích ý nghĩa của 2 câu code:
    pos.x =(i%3==0) ? 0 : ((i+1)%3==0) ? 400 : i%3 * 200;
    pos.y = (i<3) ? 0 : (i<6) ? 200 : 400;
    Mình xin được cảm ơn rất nhiều.

    ReplyDelete
  11. @Tùng Bùi Thanh

    Đoạn code mình gửi là đầy đủ và đã được test chính xác rồi đó, tuy nhiên đây chỉ là đoạn code xử lý giải thuật cho yêu cầu của bạn còn thực chạy sau khi ghép vào trong project của bạn thế nào thì bạn phải tự debug thôi. Mình có thể giải nghĩa cho bạn chi tiết các đoạn code như sau:

    // Đầu tiên khai báo 1 biến sprites là Vector kiểu con trỏ đối tượng "sprite"
    Vector sprites; // Không chạy được chắc vì thiếu cái định kiểu ''
    // Chèn 9 đối tượng sprite cần sắp xếp vào trong vector này.
    sprites.pushBack(spriteItem1);
    sprites.pushBack(spriteItem2);
    sprites.pushBack(spriteItem3);
    sprites.pushBack(spriteItem4);
    sprites.pushBack(spriteItem5);
    sprites.pushBack(spriteItem6);
    sprites.pushBack(spriteItem7);
    sprites.pushBack(spriteItem8);
    sprites.pushBack(spriteItem9);
    // Khai báo biến đếm count kiểu int tĩnh và biến pos kiểu Vec2 tĩnh
    static int count;
    static Vec2 pos;
    // Chạy vòng lặp duyệt qua toàn bộ 9 phần tử của vector sprites
    for (int i = 0; i < (int)sprites.size(); i++) {
    // Với mỗi vòng lặp xử lý như sau:
    int index = (int)sprites.size() - count; // Tính toán số phần tử còn lại trong vector
    index = rand() % index; // Lấy ngẫu nhiên vị trí 1 phần tử trong số còn lại đó
    Sprite * sprite = (Sprite*)sprites.at(index); // Lấy ra phần tử tại vị trí ngẫu nhiên trên
    float width = sprite->getContentSize().width; // Lấy chiều rộng của phần tử sprite
    float height = sprite->getContentSize().height; // Lấy chiều cao của phần tử sprite

    // Và đây là 2 dòng code sắp xếp quan trọng nhất mà bạn hỏi.
    // Tính toán toạ độ trục tung X của sprite thứ i (pos.x)
    // X = nếu vị trí phần tử đang xét là thứ i chia hết cho 3 thì nó có toạ độ X = 0
    // Còn ngược lại, kiểm tra xem nếu vị trí kế cận nó chia hết cho 3 thì X của nó sẽ là 400
    // Còn không thì X của nó sẽ bằng phần dư của i chia 3 nhân với 200.
    pos.x =(i%3==0) ? 0 : ((i+1)%3==0) ? 400 : i%3 * 200;
    // Tương tự với tọ độ trục hoành Y của phần tử có vị trí thứ i
    // Nếu nó nhỏ hơn 3 thì Y của nó sẽ là 0
    // Còn lại kiểm tra nếu nó nhỏ hơn 6 (tương đương nằm ở giữa) thì Y cuả nó sẽ là 200
    // Cuối cùng thì còn lại là nó nằm trên cùng tương đương với Y của nó là 400
    pos.y = (i<3) ? 0 : (i<6) ? 200 : 400;
    // Bạn sẽ thắc mắc tại sao lại là 400 và 200 đúng không. Mình sẽ giải thích như sau:
    // Thật ra đúng theo giải thuật thì 400 thực chất sẽ bằng width * (max số phần tử của hàng hoặc cột - 1) và theo như dữ liệu của bạn thì ở đây sẽ là 200 * (3-1) = 200 * 2 = 400
    // Và 200 ở đây thực chất chính là width hay height sprite (hình vuông) của bạn.
    // Vậy thật ra ở đây ta có công thức tổng quát cho tính toán toạ độ X, Y để sắp xếp các phần tử thành 1 hình vuông có độ rộng n phần tử sẽ là:
    // X[i] = (i%n==0) ? 0 : ((i+1)%n==0) ? (width*n-1) : (i%n * width);
    // Y[i] = (i < n) ? 0 : (i/n * height) :
    // Đặt phần tử sprite đang xét vào vị trí đã được tính toán ra bên trên
    sprite->setPosition(pos);
    // Xoá phần tử đã được sắp xếp ra khỏi vector những phần tử chưa được sắp xêp
    sprites.erase(index);
    // Cộng biến đếm lên thêm 1 nghĩa là đã xử lý xong thêm 1 phần tử
    count++;
    }

    Trên đây mình đã giải thích tỉ mỉ và tường tận giải thuật sắp xếp hình vuông cho bạn. Bạn lưu ý là trong trao đổi lập trình mọi người chủ yếu chỉ trao đổi giải thuật và code mẫu còn việc áp dụng cụ thể vào project của mình thế nào thì mỗi người sẽ phải tự có sự chủ động vì toàn bộ code của ai thì chỉ có người đó nắm rõ nhất.

    ReplyDelete
    Replies
    1. @Hieu Trinh
      Cảm ơn bạn nhiều nha. Bạn giải thích rất tường tận dễ hiểu.
      Mình sẽ cố gắng để áp dụng vào PJ mình đang làm.

      Delete
  12. @Hieu Trinh: Ban có thể giúp mình phần loại trừ các trường hợp không sắp xếp được về vệ trí đúng của hình ảnh không ? Mình còn bước này là hoàn thành cái game xếp hình rồi mà mình tìm cách làm cả tuần nay không ra rồi :(

    ReplyDelete
  13. @Tùng Bùi Thanh: Mình không hiểu ý của bạn vì mình không biết game của bạn chơi như thế nào. Nhưng theo quy tắc cơ bản của những game ráp hình thì thông thường sẽ có 2 vector. Vector 1 để lưu toàn bộ các mảnh ghép cùng thông số vị trí chính xác của chúng trên hình dùng để đối chiếu với kết quả sau khi ghép của người chơi. Vector thứ 2 chứa các mảnh ghép cùng tọa độ vị trí của nó mà đã được người chơi ghép. Sau cùng là duyệt vector 2 để đối chiếu vị trí của các mảnh ghép với các giá trị tọa độ chính xác của nó bên vector 1, mảnh nào đúng thì bỏ qua mảnh nào sai thì thực hiện trả nó về vị trí chính xác theo thông số tọa độ của nó lưu trong vector 1, chỉ vậy thôi.

    ReplyDelete
    Replies
    1. @Hieu Trinh: Ok bạn. Mình tìm ra thuật toán rồi. Đang tìm cách triển khai thuật toán đó đây.

      Delete
  14. auto spriteItem1 = Sprite::create(text, Rect(0, 0, 200, 200));
    auto spriteItem2 = Sprite::create(text, Rect(200, 0, 200, 200));
    auto spriteItem3 = Sprite::create(text, Rect(400, 0, 200, 200));
    auto spriteItem4 = Sprite::create(text, Rect(0, 200, 200, 200));
    auto spriteItem5 = Sprite::create(text, Rect(200, 200, 200, 200));
    auto spriteItem6 = Sprite::create(text, Rect(400, 200, 200, 200));
    auto spriteItem7 = Sprite::create(text, Rect(0, 400, 200, 200));
    auto spriteItem8 = Sprite::create(text, Rect(200, 400, 200, 200));
    auto spriteItem9 = Sprite::create(text, Rect(400, 400, 200, 200));

    mình muốn gán giá trị cho spriteItem1 = 1; spriteItem2 = 2; spriteItem3 = 3
    để chuyển về bài toán xếp hình 8Pluzzle thì làm thế nào?
    Các bạn chỉ cho mình với.

    ReplyDelete
  15. Có bài nào hướng dẫn cách xài CCSwipeGestureRecognizer ko ad

    ReplyDelete
  16. Mọi người cho mình hỏi chút. Phần Điều khiển Ship bằng Accelerometer - gia tốc kế.
    Khi build trên máy mọi người chpwi có mượt k ?
    Trên máy mình chơi lần đầu tiên khi mở ứng dụng thì rất mượt. Nhưng từ lần tiếp theo ( sau khi die Resert lại) thì chơi không ổn định. Có lúc nghiêng máy mà tàu không di chuyển, hoạc di chuyển rất nhanh. Ai đã fix lỗi này rùi thì chỉ mình với nhé. Thank mọi người

    ReplyDelete
  17. chào các anh chị,em có ý tưởng thế này,thay các thiên thạch bằng 1 con tàu địch và các con tàu địch có thể bắn được ta(vừa bay vừa bắn),nhưng em ko biết làm thế nào để có thể làm cho tàu địch vừa bay vừa bắn,em là newbie còn chưa hiểu biết gì nhiều mong các anh chị chỉ giáo

    ReplyDelete
  18. ví dụ e có đoạn mã này:

    hapieu0.png

    aliases

    spriteOffset
    {0,0}
    spriteSize
    {47,51}
    spriteSourceSize
    {47,51}
    textureRect
    {{621,226},{47,51}}
    textureRotated


    Dãy số {{621,226},{47,51}} có ý nghĩa gì vậy ad?

    ReplyDelete
  19. cảm ơn anh, cả khoá của anh toàn post chất lượng <3

    ReplyDelete
  20. Cảm ơn chủ thread.

    Chuyển qua giải thích sourcecode các game lớn khác cho các mem học thêm đi chủ thread

    ReplyDelete