Sunday, April 27, 2014

Bài 7 - Menu, từ cơ bản tới phức tạp (Part 1)

Hi, cả lò! (Trời bắt đầu thành cái lò nung tới nơi rồi)

Mọi người nghiên cứu tới đâu rồi nhỉ, còn ai chưa qua được bước cài đặt không? Mình đảm bảo làm theo mình chắc chắn thành công nhé. Xin thông báo với tất cả 1 tin mừng đó là phiên bản Cocos2dx-3 đã ra phiên bản FINAL rồi nhé, nghĩa là bản chính thức, đã khắc phục được tất cả các lỗi. Vậy chúng ta yên tâm học tiếp thôi. Nắm vững được 1 phiên bản thì sau này có ra V4,V5 cũng không khó để tiếp cận.

Ở bài trước, chúng ta đã học qua 1 Bài về Physic cơ bản trong Game và cách tạo chúng = engine Cocos2dx v3. À, trong Engine này sử dụng 2 thư viện Physic để xây dựng game, đó là Chipmunk, và Box 2D. Bài trước là dựa trên Chipmunk nhé, đơn giản, gần gũi hơn Box 2D. Sau này mình sẽ tìm hiểu nốt cái Box 2D và giới thiệu với mọi người. Giờ thì đi tiếp nào, Trong bài này, mình sẽ hướng dẫn mọi người làm mấy cái Menu có thể thao tác được để thực hiện 1 công việc nào đó.

Bắt đầu thôi. Tạo 1 Project mới tên Menu như sau, mở cmd lên gõ vào

>cocos new menu -p com.vn.menu -l cpp -d f:android/project

Sau khi Project mới được tạo, bạn chạy luôn lệnh này giúp mình

>cocos compile -s f:android/project/menu -p win32


Đây là biên dịch lần đầu, mục đích là liên kết tới các thư viện của Engine. Lần biên dịch đầu tiên thường lâu là vì thế. Các lần sau thì nhàn hơn nhiều bởi hầu hết chúng ta chỉ thay đổi ở Class và Resource mà thôi.

B1) Copy code và chạy thử
Bạn mở thư mục Classes theo đường dẫn sau F:/android/project/menu/Classes ( có thể khác trên máy bạn)  có chứa 4 file quen thuộc phải không. Nhưng trong bài này chúng ta sẽ không dùng lớp HelloWorldScene nữa mà thay thế bằng 2 lớp khác là MenuLayer và SceneManger.

Bạn xóa 2 file HelloWorldScene.cpp và HelloWorldScene.h, download file nguồn sau tại ĐÂY và copy paste 4 file trong đó vào thư mục Classes. Tiếp theo bạn mở file AppDelegate.cpp, ở dòng #include thứ 2  bạn thay HelloWorldScene.h =  SceneManager.h, 
bỏ dòng auto scene = HelloWorld::createScene();
và director->runWithScene(scene); đi nhé. 
Thêm vào 1 dòng lệnh sau SceneManager::goMenu(); vào bên dưới 2 dòng trên

Xong rồi, trước khi biên dịch chạy thử bạn mở file Menu.vcxproj theo đường dẫn sau F:/android/project/menu/proj.win32/ rồi tìm "HelloWorldScene.cpp" bạn sẽ thấy 4 tên file cơ bản được đặt ở đây khi biên dịch (HelloWorldScene.h, HelloWorldScene.cpp, AppDelegate.cpp, AppDelegate.h )



Vì ở trên bạn đã xóa HelloWorldScene.h và HelloWorldScene.cpp đi rồi nên ở đây bạn cũng phải xóa đi. Đồng thời thêm vào 4 file SceneManager.h, SceneManager.cpp, MenuLayer.h , MenuLayer.cpp vào Menu.vcxproj để trình biên dịch sẽ xử lý thêm 4 file này khi chúng ta biên dịch lại 1 lần nữa để chạy. Cách thêm thì như hình

************************CHÚ Ý************************
 (1/6/2016)

 Mình thấy nhiều bạn mắc ở chỗ này mở file .vcxproj

Xin đính chính lại là làm việc trên Visual Studio thực sực KHÔNG CẦN MỞ FILE NÀY khi thêm File, hoặc thêm Thư viện,

Hãy dùng tính năng sau

Chuột phải vào tên Project của bạn ( trong window Solution ) chọn Add  ( nằm chính giữa menu luôn )

sẽ có menu phụ hiện lên, hãy chú ý tới 2 lựa chọn

+ New Item ( Ctr + Shif + A ) Cho phép bạn tạo file .H, .CPP mới cho Project
+ Existing Item ( SHif + ALt + A ) Cho phép bạn thêm các file .H, .CPP có sẵn ( của Project khác, hoặc bạn copy từ nơi khác vào Thư mục Class ) vào Project

Xong, bạn sẽ ko phải mở vcxproj một cách Nông dân như trên

Nhưng cũng nên tìm hiểu cấu trúc file của những cái mình sẽ phải làm để tham khảo   chứ nhỉ

Thanks


************************************************************

 



Chạy thử = lệnh

>cocos run -s f:.android/project/menu -p win32

Rất tiếc! LỖI 1 đống nhé. Cũng phải thôi vì mã nguồn có lẽ không phù hợp với phiên bản Engine đang dùng, phải sửa lại đôi chút. Hoặc là tác giả của source bị sai mấy câu lệnh chẳng hạn. Căn cứ vào mấy cái lỗi để sửa thôi

B2) Nghiên cứu file nguồn 1 chút nhỉ

Nhìn vào thông báo lỗi, khá nhiều lỗi luôn, và không phải lỗi nào cũng có thẻ hiểu được. Sau 1 thời gian có kinh nghiệm chắc chắn bạn sẽ fix được các lỗi này thôi. Quả thật mình cũng không biết phải diễn giải các lỗi này ra đây thế nào nữa hì. Tốt nhất là kiểm tra lại mã nguồn của chúng ta xem có sai sót gì không.

Ở phần trên sẽ báo có 8 lỗi: Identifier 'Object' .....

Mở 2 file Header là SceneManager.h, MenuLayer.h , thêm vào đó dồng USING_NS_CC; vào dưới các dòng #include, cả 2 file nhé. compile lại xem, build được luôn, nhưng chạy thử file exe trong F:/android/project/menu/bin thì lại báo lỗi stop working.

Theo kinh nghiệm của mình thì, khi Build không gặp lỗi gì, tức là code đã chuẩn theo phiên bản Engine. Nhưng khi chạy mà báo lỗi Stop working thì có thể do 2 lỗi sau, không tương thích với hệ thống ( build trên 32 mang chạy trên 64 chẳng hạn) và lỗi thứ 2 là do Resource ( file hình ảnh, âm thanh, font, ...) đã bị thiếu. 

Trong trường hợp này mình cố tình ko copy 1 file font "Marker Felt.ttf" vào thư mục Resource nên build ok nhưng chạy bị lỗi. Bạn copy file "Marker Felt.ttf" từ F:/android/project/menu/Resource/font ra ngay bên ngoài thư mục Resource ( cùng cấp với mấy hình ảnh HelloWorld đó)


Compile lại xem nào


>cocos run -s f:.android/project/menu -p win32

OK nhé


B3) Nghiên cứu Code

Bạn mở 2 file header là SceneManager.h, MenuLayer.h và ngó qua chút: không có gì đặc biệt lắm phải không, ở đó chứa khai báo các hàm hoặc thuộc tính của một lớp. Cách khai báo bạn ôn lại 1 chút trong C++ là hiểu được thôi. Mình sẽ đi vào phần chính ở dưới

(Các bạn theo dõi file CODE nhé, vì nếu chép ra đây thì dài và rối lắm. Chúng ta giải thích là chính thôi)
+ Mở file SceneManager.cpp, trong này có 3 hàm 

void SceneManager::goMenu(), hàm này tạo ra 1 layer ( cái này cũng gọi là lớp nhưng không giống Class nhé. Class là chỉ các đối tượng với thuộc tính và hàm, còn layer giống như 1 mặt phẳng để đặt các thứ khác lên nó, hình dung như thế đi). Sau đó hàm này gọi tiếp 1 hàm void SceneManager::go(Layer* layer)

void SceneManager::go(Layer* layer) thực hiện 2 công việc, truyền layer vừa tạo ở trên cho 1 hàm Scene* SceneManager::wrap(Layer *layer) để xử lý cái layer này,việc xử lý bằng hàm Wrap sẽ trả lại 1 Scene. Sau đó kiểm tra xem có scene nào đang chạy không, nếu có thì thay thế = Scene mới có layer vừa tạo. nếu không có Scene nào đang chạy thì chạy với Scene có layer vừa tạo.

Scene* SceneManager::wrap(Layer *layer) thực hiện công việc là lấy tham số layer truyền vào, đặt lên 1 Scene mới tạo, trả lại giá trị là 1 Scene.

+ Mở file MenuLayer.cpp trong đó cũng lại có 3 hàm

* bool MenuLayer::init(), hàm init luôn trả về bool nhé

Trong này mình sẽ giải thích một số dòng lệnh "lạ"

TTFConfig config_font96("Marker Felt.ttf", 96);

TTFConfig config_font52("Marker Felt.ttf", 52);

//Chình font "Marker Felt.ttf" thành 2 cỡ 96, và 52 (pixel??)

Label *titleLeft = Label::createWithTTF(config_font96, "Menu ");

// Tạo 1 Label ( nhãn )  titleLeft có hiện dòng chữ "Menu" sử dụng font config_font96, các lênh tương tự bên dưới nhé
    
MenuItemFont *startNew = MenuItemFont::create("New Game", CC_CALLBACK_1(MenuLayer::onNewGame, this));

// Tạo biến menu item startNew hiện từ "New Game", khi ấn lên menu item này sẽ gọi hàm onNewGame() của lớp MenuLayer, lệnh dưới tương tụ tạo menu item "Credits"

Menu *menu = Menu::create(startNew, credits, NULL);
// Tạo 1 biến menu có chứa 2 menu item con đã tạo ở trên là startNew  và credits

1 đoạn bên dưới là công việc nhàm chán đặt vị trí trên màn hình = lệnh setPostion(); và đặt các đối tượng vào layer = lệnh this->addChild();

có 1 câu này
 menu->alignItemsVerticallyWithPadding(80.0f); // căn chỉnh theo chiều dọc các đối tượng của biến menu cách nhau 1 khoảng 80 (pixel??)

- 2 hàm bên dưới onNewGame(), onCredits() thực hiện công việc đơn giản là lại gọi hàm tạo 1 Scene mới khi ấn vào 2 menu tạo ở trên

Tổng kết lại, trong bài này học được :

+ Tạo 1 label với font = lệnh
TTFConfig config_font96("Marker Felt.ttf", 96); 
Label *titleLeft = Label::createWithTTF(config_font96, "Menu ");

+ Tạo 1 đối tượng menu item có thể ấn được 
MenuItemFont *startNew = MenuItemFont::create("New Game", CC_CALLBACK_1(MenuLayer::onNewGame, this));

Menu *menu = Menu::create(startNew, credits, NULL);

Xong rồi. 1 bài đơn giản  mà phải diễn giải dài quá các bạn ạ, nếu bài nào mà dài, nhiều đối tượng chắc chết quá



23 comments:

  1. Không biết chủ thớt ở HN hay SG vậy?

    ReplyDelete
  2. uh, có quan trọng lắm ko nhỉ?

    ReplyDelete
  3. có cách nào đổi màu nền background không bạn?
    Ở chỗ font mình đã thử đổi lại đường dẫn mà sao chạy vẫn không được (làm theo cách của bạn thì ok nhưng mình muốn thử cách khác)
    TTFConfig config_font96("fonts\Marker Felt.ttf", 96);
    TTFConfig config_font52("fonts\Marker Felt.ttf", 52);
    Resource chứa fonts nên mình thiết lập cái đường dẫn trên không biết đúng không? Bạn xem thử đúng không?

    ReplyDelete
  4. Có đổi được màu nền backround. BG chính là Layer của chúng ta.

    trong file .h

    class HelloWorld : public cocos2d::LayerColor // Kế thừa lớp LayerColor, not Layer

    trong file .cpp, trong hàm Init

    if( !LayerColor::initWithColor(Color4B(0,222,111,255)) ) // Màu nền, chả biết màu gì, vì viết bừa


    Resource của mình font đặt sẵn trong thư mục fonts rồi mà , đầu cần đường dẫn cầu kỳ thế này "fonts\Marker Felt.ttf" . THế này thôi Marker Felt.ttf" là nó tự tìm đến font

    ReplyDelete
  5. Ý mình là trong cái thư mục resource có thư mục fonts trong fonts này thì có cái font Marker Felt.ttf thì đường dẫn phải được viết lại như thế nào?

    ReplyDelete
    Replies
    1. + Mình mới update lên 3.1, build lại thì ok, Khi chạy bị lỗi : -10xxxxxxmm (ko nhớ). Đây là lỗi thiếu Resource, thiếu font. bạn copy cái font ra khỏi thư mục fonts là được.

      + Nếu bạn đặt file font trong thư mục fonts thì code phải thế này "fonts/Marker Felt.ttf", chứ ko phải thế này "fonts\Marker Felt.ttf" ( Chú ý 2 dấu gạch)

      OK!

      Delete
    2. mình có thể xin mail của bạn được không có một số câu hỏi phải có hình ảnh mới hỏi được bạn à?

      Delete
    3. G++ của 3 acc bên sidebar phải

      Delete
  6. đến bước xóa trong Menu.vcxproj sao mình không tìm thấy chỗ thêm giống như hình nhỉ?
    Mình mở Menu.vcxproj lên nó ra 4 project
    1- libAudio
    2- libchipmunk
    3- libcocos2d
    4- menu
    Help me..

    ReplyDelete
    Replies
    1. sao file .vcxproj cua e mo len no ra ma nguon chuong trinh chu k giong cua cac ac nhi

      Delete
  7. Bạn mở file Menu.vcxproj bằng Notepad ++ để xem nội dung file đó, chứ đừng CLick đúp nó sẽ mở bằng VSual đấy. Chuột phải Open With....

    ReplyDelete
  8. Tọa độ trong cocos2dx được xác định như thế nào? lúc bạn setposition vị trí các title sẽ như thế nào? Mình đã thử
    Label *titleCenterTop = Label::createWithTTF(config_font52, "How to build a...");
    Sau đó set vị trí thành
    titleCenterTop->setPosition( Point( 0, 0) );
    Thì đoạn text ("How to build a...") nó nằm ở vị trí dưới đáy màn hình mà không phải là ở bên trái đỉnh màn hình tọa độ máy tính. Tại sao? Bạn có thể giải thích rõ hơn phần tọa độ này không?

    ReplyDelete
    Replies
    1. Tọa độ trục X, Y như sau:

      + Trục X, hướng sang phải
      + Trục Y hướng lên trên
      + Gốc tọa độ 0,0 ở điểm Left - Bottom ( dưới bên trái) màn hình.

      Bạn set 1 số vị trí đặc biệt cho đối tượng là sẽ xác định được hệ tọa độ của màn hình

      Delete
    2. Phiên bản cũ V3, đối tượng Font được lấy điểm Trái - Dưới của đối tượng để căn chỉnh đặt lên màn hình.

      V3.1 đã chỉnh lại lấy điểm chính giữa đối tượng đặt lên màn hình. Mới check và thấy các menu bị lệch trái ( do code cho V3).

      Mọi người chỉnh lại chút là ổn

      Delete
  9. Sao mình thấy file appdelegate.h gọi tới function SceneManager.goMenu. chứ không thấy sử dụng các function của file MenuLayer.h

    ReplyDelete
  10. ad ơi giúp mình với , mình làm từng bước theo hướng dẫn mà cuối cùng build lên nó bị lỗi link2019 , mình có tìm trên google mà vẫn ko fix được , bạn xem giùm mình với .
    [URL=https://uphinhnhanh.com/view--669521276_tamgiac.jpg][img]https://uphinhnhanh.com/images/-669521276_tamgiac_tn.jpg

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

    ReplyDelete
  12. Mình làm y như trên nhưng bị lỗi này:
    1> AppDelegate.cpp
    1> HelloWorldScene.cpp
    1> main.cpp
    1>c1xx : fatal error C1083: Cannot open source file: '..\Classes\HelloWorldScene.cpp': No such file or directory
    1>h:\androidstudioprojects\menu\classes\menulayer.h(21): warning C4996: 'cocos2d::Object': was declared deprecated (..\Classes\AppDelegate.cpp)
    1>h:\androidstudioprojects\menu\cocos2d\cocos\deprecated\ccdeprecated.h(539) : see declaration of 'cocos2d::Object'
    1> h:\androidstudioprojects\menu\classes\menulayer.h(22): warning C4996: 'cocos2d::Object': was declared deprecated (..\Classes\AppDelegate.cpp)
    1> h:\androidstudioprojects\menu\cocos2d\cocos\deprecated\ccdeprecated.h(539) : see declaration of 'cocos2d::Object'
    1>h:\androidstudioprojects\menu\classes\menulayer.h(25): warning C4996: 'cocos2d::Object': was declared deprecated (..\Classes\AppDelegate.cpp)
    1>h:\androidstudioprojects\menu\cocos2d\cocos\deprecated\ccdeprecated.h(539) : see declaration of 'cocos2d::Object'
    ========== Build: 0 succeeded, 1 failed, 3 up-to-date, 0 skipped ==========

    Không biết cái "HelloWorldScene.cpp" ở đâu ra nữa. Mình đã xóa khỏi Classes rồi mà.

    ReplyDelete
    Replies
    1. Mình giữ 2 files "HelloWorldScene.cpp" và "HelloWorldScene.h" thì lại bị lỗi :
      ../Classes/AppDelegate.cpp:55: error: undefined reference to 'SceneManager::goMenu()'
      Search gg cũng không sửa được. Ad giúp mình với

      Delete
    2. Có phải bạn đã mở file có tên "Menu.vcxproj" không? Nếu đúng thì bạn đã mở lộn file rồi. Bạn phải mở file "Menu" có hình 2 dấu "+" ấy, đuôi của file đó mới là ".vcxproj", còn đuôi file bạn mở là ".filter". Bạn xem lại đi nhé.

      Delete