Saturday, May 24, 2014

Bài 18: Game thứ 2 - Breakout - Tạo và phá gạch (Part 2)

Chào mọi người!

Vậy là chúng ta đã đi được 1 chặng đường kha khá của Cocos2d-x V3 rồi. Cũng chuẩn bị xong Project thứ 2 đấy chứ. Trong phần này mình sẽ hướng dẫn các bạn nốt công việc đơn giản là "xếp gạch và phá gạch" nhé. Sẽ rất đơn giản thôi.

Những công việc trong bài này:
+ Tạo gạch
+ Xử lý va chạm vật lý
+ Kiểm tra việc phá gạch, hết thì WINGAME
+ Kiểm tra GameOver khi bóng rơi không trúng thanh chắn

- Nhìn có vẻ nhiều việc vậy thôi, nhưng mà đơn giản lắm, vì cũng khá giống Game đầu tiên

Bắt đầu luôn nhé! À, file Resource và Class các bạn down hết ở bài 17 rồi đó

B1 - Tạo gạch

Vì Class mình up ở bài 17 dùng cho cả bài 18 ( Mình comment những đoạn code chưa dùng, bạn chỉ việc phá comment ra thôi)

Mở file HelloWorldScene.cpp thêm vào đoạn code sau

for (int i = 0; i < 5; i++) {
static int padding = 100;
auto block = Sprite::create("blocks.png");
auto blockBody = PhysicsBody::createBox(block->getContentSize(), PHYSICSBODY_MATERIAL_DEFAULT);
blockBody->getShape(0)->setDensity(10.0f);
blockBody->getShape(0)->setFriction(0.0f);
blockBody->getShape(0)->setRestitution(1.f);
blockBody->setDynamic(false);
// Tạo khoảng cách đều nhau giữa cách khối gạch
int xOffset = padding + block->getContentSize().width / 2 +
((block->getContentSize().width + padding)*i);
block->setPosition(xOffset, 450);
blockBody->setContactTestBitmask(0x000001);
block->setPhysicsBody(blockBody);
block->setTag(3);
this->addChild(block);
}

B2 - Xử lý va chạm - Phá gạch, Game Over

* Thêm ContactListener

auto dispatcher = Director::getInstance()->getEventDispatcher();
auto contactListener = EventListenerPhysicsContact::create();
contactListener->onContactBegin = CC_CALLBACK_1(HelloWorld::onContactBegin, this);  
dispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);

* Dựng 1 hàm onContactBegin

bool HelloWorld::onContactBegin(PhysicsContact& contact)
{
// Lấy 2 đối tượng va chạm
auto spriteA = (Sprite*)contact.getShapeA()->getBody()->getNode();
auto spriteB = (Sprite*)contact.getShapeB()->getBody()->getNode();

// Kiểm tra loại đối tượng
int tagA = spriteA->getTag();
int tagB = spriteB->getTag();

if (tagA == 3) // Là gạch
{

this->removeChild(spriteA,true); // Xóa gạch

//spriteA->removeFromParentAndCleanup(true);
}

if (tagB == 3)  // Là gạch
{
this->removeChild(spriteB,true); // Xóa gạch

//spriteB->removeFromParentAndCleanup(true);
}

// Nếu bóng va chạm với sạn mà tọa độ Y của bóng nhỏ hơn thanh chắn thì Game Over
if ((tagA == 0 || tagB  == 0 )& (ball->getPositionY() <= paddle->getPositionY()))
{
auto gameOverScene = GameOverScene::create();
gameOverScene->getLayer()->getLabel()->setString("You Lose!");
Director::getInstance()->replaceScene(gameOverScene);
}

return true;
}

Lớp GameOverScene này giống với Project đầu tiên nhé

B2 - Kiểm tra Win game

Xây dựng hàm Tick() như sau, nhớ khai báo nguyên mẫu hàm


void HelloWorld::tick(float dt)
{
// 1 biến bool xác nhận Win game ban đầu gán = true;
bool isWin = true;
// Vector bodies lấy tất cả các bodies của world ( ball, edge, paddle body), về vector bạn nghiên cứu thêm C++ nâng cao nhé, cũng gần giống mảng, và cũng khá giống Stack. Khai báo vector thì như này Vector<Kiểu biến> tên_biến
Vector<PhysicsBody*> bodies = m_world->getAllBodies();

// Duyệt từng phần tử của vector trên, kiếm tra loại đối tượng = Tag, Bạn nên tìm hiểu lại lệnh for nhé, nó có nhiều biến thể cho từng loại lớp đặc biệt, đọc phần C++ nâng cao phần list, vector, queue,v..
// Đừng dập khuôn for chỉ có dạng for( int i=0; i<N; i++) nhé

for each(PhysicsBody* body in bodies) // Câu lệnh này lỗi Khi build android, bạn hãy sửa lại thành  for (auto body : bodies) nhé, đây là chuẩn mới C++ 11
{
if (body->getNode()->getTag() == 3) // Nếu còn body của "gạch", tức là chưa phá hết
{
isWin = false; // Chưa Win
}
}
// Duyệt hết mà  isWin vẫn ko đổi thì xử lý Win game
if (isWin == true)
{
auto gameOverScene = GameOverScene::create();
gameOverScene->getLayer()->getLabel()->setString("You Win!");
Director::getInstance()->replaceScene(gameOverScene);
}
}

Để gọi hàm Tich() này bạn thêm 1 dòng lệnh này 
this->schedule(schedule_selector(HelloWorld::tick),0); vào cuối hàm init() là xong,

Nếu bạn dùng scheduleUpdate() thì build hàm update(float dt) nhé

Build and Run nào

















Chúng ta kết thúc bài 18 ở đây nhé. Tóm lại trong cả bài 17 và 18 chúng ta học được gì?

+ Tạo world vật lý với Chipmunk
+ Move đối tượng = kéo, drag trên màn hình
+ Xử lý va chạm, 
+ Win game, Over Game
+ Biết thêm 1 chút về Vector, để dành các bài sau nhé

Những hạn chế của game này
+ Thiếu phần tính điểm, để những bài sau
+ Thiếu âm thanh ( bạn có thể thêm vào như Game trước nhé )
+ Thiếu phần Level chơi
+ Thiếu phần menu 
+ Đồ họa hơi cùi mía.

* Lưu ý khi build Android, bạn phải khai báo các file cpp mới tạo vào file Android.MK trong thư mục prj.android, như sau, chú ý dấu \








Dòng cuối ko có dấu \

Các dòng trên đều có

GameOverScene.cpp là Class mới tạo, phải khai báo vào đây, file .h thì ko cần

OK, và chú ý sửa lỗi code theo đúng chuẩn C++11 là build APK thành công thôi. Đã test và rút ra EXP nhé


Các bạn thấy đấy, 1 bài quá dễ đúng không. Nhưng nếu không đọc qua những bài phía trước thì chắc hẳn bài này ko dễ nuốt đâu nhỉ. Mong mọi người đừng ỷ vào những gì đã biết mà coi thường những thứ dễ dàng. Hãy học từ dễ đến khó, từ chưa biết đến biết, từ biết thành PRO. từ pro tới SÁNG TẠO nha, đó mới là đích tới của chúng ta. Thực ra đâu có đích nào là cuối.
Các bạn có thể phát triển thêm game này nếu có thời gian nhé.
Nếu không chúng ta cùng đi tiếp nào.

17 comments:

  1. Mình muốn dùng Accelerometer để điều khiển paddle, nhưng mình không biết làm cách nào để debug trên máy thật,
    Bạn có thể chỉ cách chạy project trên eclipse được không

    ReplyDelete
    Replies
    1. Ờ mình ko xài Eclipse, Ko xài gì vẫn build ra android (APK) Vẫn cài trên máy ảo được mà.
      Cái gia tốc kế, bạn hãy tham khảo Classs trong bài test gốc của Cocos chỉ có mấy lệnh thôi.

      Delete
    2. Bạn ơi cocos 2dx có hỗ trợ socket không vậy, trong trường hợp mình muốn dùng socket mà không quen thuộc với c++ thì làm thế nào?

      Delete
    3. Có Socket bạn nhé, nằm trong thư mục Extenal/websockets, bạn tham khảo xem dùng được ko

      Delete
  2. Cho mình hỏi bây giờ mình muốn chụp 1 cái ảnh rồi đưa lên chương trình của mình thì làm ntn ạ ? cảm ơn bạn !

    ReplyDelete
    Replies
    1. Không hiểu ý bạn cho lắm?

      Nghĩa là tạo 1 hình ảnh làm đối tượng trong game á? Dùng 1 ảnh .PNG ( cắt gọt trong Photoshop ấy), rồi quẳng rào Resource, rồi import vào game = Sprite, vậy thôi.

      Còn ý gì khác à

      Delete
    2. Trời, vậy thì mình hỏi làm gì,hi, bạn hiểu sai ý mình rồi, ..ý mình là trong game của mình, làm sao để có cái nút add picture để load hình vừa chụp lên game đó bạn, mình tính chụp hình rồi đưa cái hình đó lên làm Sprite luôn, bạn giúp mình được không ?

      Delete
    3. Có vẻ giống như là 1 soft load file từ source. Bạn nghiên cứu phần Fileutils trong bài test gốc. Chứ mình cũng chưa động tới cái này

      Delete
  3. bài đọc rất dễ hiểu, cám ơn bạn nhiều :)

    ReplyDelete
  4. Khi build ra android thì mình gặp phải lỗi như thế này, bạn xem giúp mình sửa ntn nhé

    Microsoft Windows [Version 6.1.7601]
    Copyright (c) 2009 Microsoft Corporation. All rights reserved.

    C:\Users\Nguyen Huy>cocos compile -s "D:\Cocos Project\breakout" -p android --ap
    16
    Runing command: compile
    Building mode: debug
    building native
    NDK build mode: debug
    The Selected NDK toolchain version was 4.8 !
    running: 'C:\ANDROID\NDK\ndk-build -C D:\Cocos Project\breakout\proj.android -j1
    NDK_MODULE_PATH=D:\Cocos Project\breakout\proj.android\../cocos2d;D:\Cocos Proj
    ect\breakout\proj.android\../cocos2d/cocos;D:\Cocos Project\breakout\proj.androi
    d\../cocos2d/external NDK_DEBUG=1'

    make.exe: *** D:\Cocos: No such file or directory. Stop.
    Error running command, return code: 2
    C:\Users\Nguyen Huy>

    ReplyDelete
    Replies
    1. Để build Android chuẩn, bạn làm 2 việc như sau:

      mở file Android.MK trong proj.android nhé + sửa lại đoạn code nguồn như sau

      1/
      LOCAL_SRC_FILES := hellocpp/main.cpp \
      ../../Classes/AppDelegate.cpp \
      ../../Classes/HelloWorldScene.cpp\ ( phải có dấu \ ở cuối cùng - bạn thử bỏ đi, lỗi luôn)
      ../../Classes/GameOverScene.cpp (Thêm vào dòng này, Class có bao nhiều file cpp bạn phải thêm vào đây, và dòng cuối cùng ko có dấu \)

      2/

      Bạn sửa lệnh FOR trong hàm TICK()

      từ

      for each(PhysicsBody* body in bodies) (Lệnh này theo chuẩn C++ cũ)

      thành

      for (auto body : bodies) (Lệnh này theo chuẩn C++11 mới nhất, Cocos3 theo chuẩn C++ 11 mới) vòng lặp cho vector

      Build là OK, Sorry vì mình ko để ý. Các bài sau nếu build android bạn nên chú ý bước 1 trước, sau đó nếu trong code có lỗi thì căn cứ vào thông báo lỗi mà sửa thôi. Chú ý chuẩn C++11 là OK

      Delete
    2. À quên nữa, chỉ dùng lệnh Cocos compile -s .... nhé, ko dùng cocos run -s ....

      Delete
    3. Vẫn ko được bạn ơi, vẫn báo lỗi ở trên là sao nhỉ ?

      Delete
  5. Oh mình sửa được rồi. Lỗi của mình là project đặt trong ổ D:\CocosProject chứ ko đặt trong C:\Android có chứa sdk, ndk...

    ReplyDelete
  6. 1. Phần mềm làm hiệu ứng âm thanh rất nhẹ mà dể sử dụng
    http://www.drpetter.se/project_sfxr.html

    ReplyDelete
  7. thực sự, cảm ơn tác giả nhìu lắm

    ReplyDelete
  8. thank you it's a nice tutorial.But the speed of the ball has been slowed down over a while.How to fix it.how to make a ball move with constant speed

    ReplyDelete