Saturday, May 17, 2014

Bài 12: Làm game đầu tiên - GameOver, tính điểm, thêm âm thanh cho game sôi động (Part 4)

Hello all!

Vậy là chúng ta đã đi được 3/4 chặng đường cho project đầu tiên này rồi. Tuy rằng khá đơn giản, nhưng công việc viết ( thực ra là đang áp dụng ) code sao cho đúng và dễ kiểm tra sửa lỗi cũng hoa mắt ghê.

Chỉ cần nốt 1 bài này thôi là chúng ta có 1 game đầu tiên để chơi rồi, hehe. Trong phần cuối này chúng ta sẽ cùng làm nốt những công việc sau:

+ Kiểm tra game Over khi quái lao vào nhân vật
+ Thêm âm thanh khi bắn đạn và thêm chút nhạc nền cho hấp dẫn nhỉ.
+ Mình bỏ phần Tính điểm nha, hơi phức tạp 1 tí, hẹn bài sau. Bài này để mức đơn giản thôi.

Bắt đầu nào!!!!!

B1 - Thêm âm thanh vào game

Các bạn Download resource ở đây, chép vào trong thư mục Resource của Project
Sau đó mở file HelloWordScene.cpp, trong hàm init() thêm vào dòng lệnh sau

CocosDenshion::SimpleAudioEngine::getInstance()->preloadBackgroundMusic("background-music-aac.wav");
CocosDenshion::SimpleAudioEngine::getInstance()->playBackgroundMusic("background-music-aac.wav",true); // True = lặp lại vô hạn


Tìm tới hàm onTouchEnded(), thêm vào dòng lệnh sau


CocosDenshion::SimpleAudioEngine::getInstance()->preloadEffect("pew-pew-lei.wav");

CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("pew-pew-lei.wav");

Thêm vào 1 lệnh #include"SimpleAudioEngine.h" ở sau dòng lệnh #include" " đầu tiên nhé.

B1 - Tạo màn GameOver

Bạn tạo thêm 1 Scene mới tên là GameOverScene.h và GameOverScene.cpp như sau, Down file Class


Chúng ta ngó qua 2 file này chút nhé:

* Mở file GameOverScene.h khai báo 2 Class GameOverLayer, GameOverScene, cùng với các hàm tạo hàm hủy, hàm khởi tạo init(), không quá phức tạp phải không?

Tuy nhiên, nên chú ý 1 chút tới 2 lệnh này

CC_SYNTHESIZE_READONLY(cocos2d::LabelTTF*, _label, Label); (1)

CC_SYNTHESIZE_READONLY(GameOverLayer*, _layer, Layer); (2)

Nhìn có vẻ khó hiểu nhỉ, nhưng bạn có thể hiểu thế này

(1) tạo ra 1 biến _label có kiểu LabelTTF* thông qua 1 phương thức của lớp Label

(2) tạo ra 1 biến _layer có kiểu GameOverLayer* thông qua 1 phương thức của lớp Layer

đơn giản thế thôi, đừng phức tạp hóa lên quá, Bạn có thể tra cứu thêm về các hàm Inline ở đây

* Mở file GameOverScene.cpp, hàm hủy (có dấu ~ ) thì dễ hiểu rồi nhé, đơn giản là giải phóng cho các con trỏ của lớp để giải phóng bộ nhớ.

Xét các hàm Init()
GameOverScene::init(), tạo ra 1 Scene mới và 1 Layer mới
GameOverLayer::init(), tạo 1 layer màu trắng Color4B(255,255,255,255), có ai thắc mắc Color4B, Color3B là gì không?

Color3B tạo ra màu RGB từ 3 tham số vào. Color4B tạo ra màu RGBA từ 4 tham số vào, trong đó tham số cuối điều chỉnh độ opacity ( đục ).

Đoạn này 

this->runAction( Sequence::create(
                                DelayTime::create(3),
                                CallFunc::create(this, 
                                callfunc_selector(GameOverLayer::gameOverDone)),
                                NULL));

Là tạo 2 Action liên tiếp nhau
+ Làm trễ 3 giây  DelayTime::create(3)
+Tiếp theo CallFunc::create(this, callfunc_selector(GameOverLayer::gameOverDone)), // Gọi hàm game OverDone

Hàm gameOverDone() làm mỗi việc là thay thế cái Scene GameOverScene bằng 1 Game Scene mới để tiếp tục chơi

void GameOverLayer::gameOverDone()
{
    Director::getInstance()->replaceScene(HelloWorld::createScene()); // Tạo 1 Game Scene mới
}


Bạn mở file HelloWordScene.cpp tìm tới hàm onContactBegin(), bên trong khối lệnh

// Nhân vật bị quái đụng vào
if((tag==1&tag1==2)||(tag==2&tag1==1))
 {
// Xử lý GameOver
// Tính điểm 
 }

Thêm vào đoạn code sau đây

auto gameOverScene = GameOverScene::create(); // Tạo 1 Scene Over của lớp GameOverScene
gameOverScene->getLayer()->getLabel()->setString("You Lose :["); // Đặt 1 dòng thông báo lên màn hình
Director::getInstance()->replaceScene(gameOverScene); // Thay thế game Scene =  game Over Scene 

Nhớ thêm #include"GameOverScene.h" ở phần #include nhé.

Trước khi biên dịch chạy thử, bạn cần làm 1 việc sau

+ Build Android, hãy mở file Android.mk trong đường dẫn firstgame\proj.android\jni\Android.mk thêm vào dòng ../../Classes/GameOverScene.cpp bên dưới dòng ../../Classes/HelloWorldScene.cpp
+ Build Win32: hãy mở file firstgame.vcxproj theo đường dẫn firstgame\proj.win32\firstgame.vcxproj thêm vào 2 dòng

<ClCompile Include="..\Classes\GameOverScene.h" /> dưới dòng <ClCompile Include="..\Classes\HelloWorldScene.h" />

<ClCompile Include="..\Classes\GameOverScene.cpp"/>dưới dòng <ClCompile Include="..\Classes\HelloWorldScene.cpp" />

Build và chạy thử nhé, 

Ặc, lại lỗi tè le, và ko hiểu là lỗi gì luôn, thông báo lỗi loằng ngoằng quá. Nếu như bạn không đọc cái chú ý của mình ( phần chữ đỏ ) ở Bài 11 chắc chắn sẽ chẳng thể tìm được cách sửa lỗi này đâu, cá đó ( Tinh vi tí thôi, mò mãi cũng ra thôi, chả nhớ ngày trước tìm được cách sửa ở đâu).

Túm lại lỗi này sửa thế này thêm USING_NS_CC; vào dưới phàn #include trong file GameOverScene.h là xong ( mọi file .h nên thêm vào, mất gì đâu ).

Xong, Build lại và chạy là OK ngay, ko OK mình xóa Blog này luôn nhé! Hứa đấy
























Vậy là chúng ta đã cùng nhau hoàn thành xong Project Game đầu tiên rồi đấy, cũng không khó phải không?
Trong Project này chúng ta đã nắm được các phần cơ bản sau đây:

+ Tạo nhân vật bằng Sprite
+ Bắt sự kiện Touch - Chạm màn hình
+ Bắt sự kiện Contact - va chạm vật lý
+ Xử lý âm thanh
+ Xử lý game Over

- Hi vọng ở những bài sau chúng ta sẽ cùng nhau làm được nhiều Project phức tạp hơn, hay hơn, đẹp hơn nhé.

Chào và hẹn gặp lại các bạn ở bài sau!


39 comments:

  1. Cảm ơn bạn, mình thực hiện tuần tự các bước mà bị lỗi...

    Error 11 error LNK1120: 2 unresolved externals D:\Android\FirstGame\proj.win32\Debug.win32\FirstGame.exe 1 1 FirstGame




    Error 9 error LNK2019: unresolved external symbol "public: virtual __thiscall GameOverScene::~GameOverScene(void)" (??1GameOverScene@@UAE@XZ) referenced in function "public: virtual void * __thiscall GameOverScene::`scalar deleting destructor'(unsigned int)" (??_GGameOverScene@@UAEPAXI@Z) D:\Android\FirstGame\proj.win32\HelloWorldScene.obj FirstGame



    Error 10 error LNK2001: unresolved external symbol "public: virtual bool __thiscall GameOverScene::init(void)" (?init@GameOverScene@@UAE_NXZ) D:\Android\FirstGame\proj.win32\HelloWorldScene.obj FirstGame

    Giải quyết sao bạn.. :(

    ReplyDelete
    Replies
    1. Vâng, nói thật là với dòng thông báo lỗi loằng ngoằng kiểu này, mình cũng chịu chẳng hiểu mô tê gì luôn,

      Nhưng theo kinh nghiệm của bản thân thì khả năng là bạn ko đọc kỹ cái dòng có chữ đỏ bên trên là phải thêm lệnh USING_NS_CC; ( có dấu chấm phẩy ) vào dưới #include của file GameOverScene.h nhé.

      Delete
  2. Mình có thêm bạn ạ, mình coi kĩ rồi mà :) sao lại mắc lỗi đó được !

    ReplyDelete
    Replies
    1. Lạ nhỉ, mình build máy nào cũng được mà, chắc chắn có lỗi ở chỗ nào đó.

      Bạn thử bỏ tất cả hảm hủy và hàm tạo của 2 lớp kia đi xem. Nếu ko thì tạo new 1 Project khác, copy Class rồi build lại.

      À mà đã khai báo file GameOverScene.cpp, .h trong file firstgame.vcxproj chưa????

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

      Delete
    3. Rồi luôn bạn ạ .
      ở file đó mình thấy cái thẻ này


      Classes


      không giống như của bạn, mình thử làm nguyên cái xml tương tự cho GameOverScene.h mà vẫn không chạy :3

      Còn không có dòng

      đâu bạn ạ .!

      Delete
    4. ClInclude Include="..\Classes\AppDelegate.h"
      ClInclude Include="..\Classes\HelloWorldScene.h"
      ClInclude Include="..\Classes\GameOverScene.h" ( Dòng này phải tự thêm )

      Dòng kiểu này cơ mà, VS2102, ko giống như của mình là chắc chắn đã sai rồi

      Delete
    5. Cảm ơn bạn, mình có chút nhầm lẫn,mình sửa được rồi bạn ạ.. hi,Làm phiền tới bạn nhiều quá...thật ngại......! : )

      Delete
    6. Vậy là nhầm gì thế, share mọi người cùng tránh

      Delete
    7. bạn ơi mình cũng bị lỗi tương tự, không pít bị gì luôn, help me!

      Delete
    8. mình cũng bị lỗi tương tự khi build trên Windows Phone, trên windows thì ko sao :((

      Delete
  3. Cho mình hỏi sao mà player càng bắn đạn thì nó càng bị giựt về phía đằng sau vậy ?

    ReplyDelete
    Replies
    1. Nếu nhớ ko nhầm thì Player mình cũng set body cho nó thì phải, vì thế nên khi viên đạn xuất hiện ngay gần vị trí Player sẽ đẩy nó đi khỏi vị trí đầu

      hãy set thuộc tính tĩnh cho nó = hàm playerBody->setDynamic(false); nó sẽ đứng im với mọi va đập

      Delete
  4. Trong game thì mỗi lần touch bắn 1 đạn, vậy có cách nào mình nhấn và giữ touch lại thì đạn bắn liên tục hk?

    ReplyDelete
    Replies
    1. Thay onTouchEnded = onTouchMoved, nhưng nhớ phải đặt delay time vào không thì đạn ra liên tục, lag máy

      Delete
  5. Cho mình hỏi tạo 1 chữ có đường viền xung quanh làm thế nào vậy?

    ReplyDelete
    Replies
    1. uh, không rõ nữa, nếu ko quan trọng thì chơi sprite với ảnh làm trong PTShop đi

      Delete
    2. ad oi muon gameover phai click chuot moi choi tiep thi lam de delay ntn a.?

      Delete
    3. Ý bạn là như này: Tại Scene Gameover có 1 nút dể Replay ?

      Dễ thôi, bạn tạo 1 nút, khi click vào đó thì gọi hàm replay, như này

      * Tạo nút và add vào Scene GameOver,dễ nhé
      * Ấn nút thì gọi call back tới hàm kiểu sau

      GameOver::replay(Ref psender){

      Director->getinstan()->replaceScene(HelloWorld::CreateScene());


      }

      Đại loại là như thế

      Delete
  6. Mình muốn bắn đạn theo toạ độ của player thì làm sao vậy bn?

    ReplyDelete
    Replies
    1. Lấy tọa độ của Player set cho vị trí của đạn

      Delete
  7. Bạn ơi cho mình hỏi cái CocosDenshion của mình nó không load. Khi mình gõ CocosDen nó không có trong danh sách gợi ý. Mặc dù ở bên phải đã hiện rồi. Bạn có biết cách khắc phục không, giúp mình với?

    ReplyDelete
    Replies
    1. Có include "SimpleAudioEngine.h" ko, Nếu include, thì VAX sẽ tự động lục lọi thư viện để tìm ra hàm tương ứng. Hình như trong bài mình thiếu include này thì phải. Hì

      Delete
  8. Ad ơi cho mình hỏi.
    Mình thấy cách tạo Scene GameOver này phức tạp hơn HelloWord.
    Như bên HelloWorld thì chỉ cần tạo 1 class: class HelloWorld : public cocos2d::Layer
    Zậy sao bên GameOver lại tạo thành 2 class: Layer và Scene.
    Như vậy có phức tạp hơn không?

    Còn 1 thắc mắc nho nhỏ nữa là: Mỗi khi tạo file .h, .cpp mới thì phải bổ sung trong file .vcxproj sao?

    ReplyDelete
    Replies
    1. Ý mình là có cần thiết làm phức tạp cái scene GameOver như vậy không? Tại mình vẫn chưa hiểu tại sao code lại viết như vậy

      Delete
    2. a cho e hoir laf e mowr file.vcxproj no ra i xi nhu chuong trinh minh dang lam chu lam gi co class nao dau nhi

      Delete
  9. Build trên win32 thì ok, build cho android thì báo lỗi bạn ơi, bạn build lại xem giùm mình với

    ReplyDelete
  10. Mình đã xử lý được rồi, đó là không áp dụng đoạn này:
    ////////////////////////////////
    + Build Android, hãy mở file Android.mk trong đường dẫn firstgame\proj.android\jni\Android.mk thêm vào dòng ../../Classes/GameOverScene.cpp bên dưới dòng ../../Classes/HelloWorldScene.cpp
    //////////////////////////////////////////////////////

    ReplyDelete
  11. Cho xin cai source ban oi! Loi nhieu wa! De minh so sanh nua coi loi gi!

    ReplyDelete
  12. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. Minh cung bi loi giong ban Thanh Nguyen(ban ở trên củng) ko biet sua lam sao

      Delete
  13. Ban oi lỗi bảo cocos2d::LabelTTF* chưa dc khai báo là sao nhỉ?

    ReplyDelete
  14. Từ bài 9 đến bài 11 mình làm theo hướng dẫn thì ok.đến bài này mình làm mắc lỗi này mà sửa mãi ko được.Helppppppp
    1>HelloWorldScene.obj : error LNK2019: unresolved external symbol "public: virtu
    al __thiscall GameOverScene::~GameOverScene(void)" (??1GameOverScene@@UAE@XZ) re
    ferenced in function "public: virtual void * __thiscall GameOverScene::`scalar d
    eleting destructor'(unsigned int)" (??_GGameOverScene@@UAEPAXI@Z)
    1>HelloWorldScene.obj : error LNK2001: unresolved external symbol "public: virtu
    al bool __thiscall GameOverScene::init(void)" (?init@GameOverScene@@UAE_NXZ)
    1>H:\CocosTutorial\firstgame\proj.win32\Debug.win32\firstgame.exe : fatal error
    LNK1120: 2 unresolved externals
    ========== Build: 0 succeeded, 1 failed, 3 up-to-date, 0 skipped ==========
    Error running command, return code: 1
    C:\Users\nguyen huy chien>

    ReplyDelete
  15. Mình làm từng bước và run từng bước 1 ok rồi.đến bước cuỗi cùng thêm

    auto gameOverScene = GameOverScene::create(); // Tạo 1 Scene Over của lớp GameOverScene
    gameOverScene->getLayer()->getLabel()->setString("You Lose :["); // Đặt 1 dòng thông báo lên màn hình
    Director::getInstance()->replaceScene(gameOverScene); // Thay thế game Scene = game Over Scene

    vào hàm onContactBegin thì lỗi như trên

    ReplyDelete
  16. B cho T hỏi làm sao để có thể đổi nền game đen thành 1 bức hình khác ?

    ReplyDelete
  17. Chào bạn, hình như là mình chưa có phần code tính điểm thì phải, chỉ mới báo gameover thôi đúng không?
    Thanks bạn

    ReplyDelete
  18. cho mình hỏi class mới tạo ra viết tiếp luôn vào file helloworld.cpp với helloworld.h à hay tạo mới kiểu gì ạ, mình mở file project.vcxproj cũng giống project.sln :(

    ReplyDelete
    Replies
    1. Tạo Class mới = Visual. vào file/Add
      Tạo xong 2 file .H, .CPP nó sẽ là 2 file trống, bạn tạo file H gần giống HelloWorld.h. file CPP thì ImPle... các hàm ra thôi

      Khi tạo bằng VS thì nó sẽ tự động thêm file vào project.vcxproj

      Delete
    2. cái bước tạo scene mới mình vẫn chưa hiểu nên ad nói từng bước làm đc k ạ, em làm đến bước cuối này đang tắc ạ

      Delete