Hiển thị các bài đăng có nhãn Menu. Hiển thị tất cả bài đăng
Hiển thị các bài đăng có nhãn Menu. Hiển thị tất cả bài đăng

Bài 8: Menu - từ cơ bản tới phức tạp ( Part2)

Người đăng: chisenhungsuutam on Thứ Ba, 29 tháng 4, 2014

Hi 30-4, 1-5!

Tình hình là 2 ngày không được nghỉ, chả biết làm gì, đi làm thì chán chán, đi chơi thì 1 mình nên đành rảnh rỗi ngồi viết bài cho vui vậy. Các anh chị em giờ này chắc đang vi vu tận đẩu tận đâu rồi ấy nhỉ. Còn mình thì đi làm, nhưng mà không sao, double lương cũng tạm.

Bài trước chúng ta đã biết cách tạo Label chứa text và cách chỉnh font, cùng với việc tạo 1 menu chứa item để thực hiện 1 tác vụ nào đó. Để nhớ lâu, các bạn nên dùng VS 2012 gõ lại từng đoạn code nhé. Còn trong bài này chúng ta sẽ đi sâu hơn với một số công việc sau:

+ Tạo hiệu ứng chuyển cảnh cho mỗi Scene
+ Tạo công việc cho các nút của Menu
+ Thêm một chút hiệu ứng cho Label, ví dụ như chuyển động chẳng hạn.

Bắt đầu thôi. Các down file Source ở ĐÂY về, giải nén rồi paste đè vào thư mục Classes của project menu nhé.

Sau đó, bạn chỉnh file Menu.vcxproj (f:/android/project/menu/proj.win32) như thế này để không bị lỗi khi compile



Biên dịch rồi chạy thử cái xem thế nào

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

Bạn có thấy thay đổi gì không, có hiệu ứng chuyển Scene, hiệu ứng dịch chuyển chữ, và Click vào 2 menu sẽ chuyển sang Scene khác. OK. Tiếp theo chúng ta sẽ đi vào chi tiết phần code xem làm thế nào để được như vậy.

Chi tiết phần Code

Các file SceneManager.h, và MenuLayer.h không thay đổi gì so với phẩn 1. Ở phần 2 này chúng ta thấy có 2 lớp mới xuât hiện đó là CreditLayer, PlayLayer để quản lý 2 thao tác khi nhấn vào 2 menu khác nhau của chúng ta. Bạn mở 2 file CreditLayer.h, và PlayLayer.h ra nhé

Nói chung là ko có gì đặc biệt phải không , ngoài mấy cái hàm tạo, hàm hủy, rồi hàm init, hàm back ( để ấn nút Back xuất hiện ở Scene này). 2 đối tượng này có mỗi một việc là tạo ra 2 cái Layer rồi sau đó sẽ dính vào 1 Scene nào đó. Bỏ qua 2 file này nhé.

A > Tiếp theo mở file CreditLayer.cpp ra, nhìn vào hàm init() // Hàm này tạo ra 1 layer trống để gắn đối tượng. Trong hàm init () này có đoạn code cần chú ý

auto back = MenuItemFont::create("Back", CC_CALLBACK_1(CreditLayer::back,this)); (1)
auto menu = Menu::create(back, NULL); (2)
menu->setPosition(visibleSize.width/2, visibleSize.height/2);
this->addChild(menu);

(1) Đây là câu lệnh quen thuộc đã học từ bài 7, nhiệm vụ tạo ra 1 item menu tên back, khi ấn vào item menu này sẽ gọi hàm CreditLayer::back() ở phía dưới
(2) là câu lệnh cũng quen thuộc có nhiệm vụ tạo ra 1 Menu chứa cái item menu back đã tạo ở trên.
Còn đoạn bên dưới là đặt vị trí Menu trên ở chính giữa màn hình, sau đó dính cái menu này vào Layer tạo ra bởi hàm init()

B > Tiếp theo mở file PlayLayer.cpp, ta thấy rằng các dòng code hoàn toàn tương tự file CreditLayer.cpp, cũng là tạo ra 1 nút back rồi gắn vào Layer này. Các bạn nghiên cứu kỹ lại cho nhớ nhé.

C > Tiếp theo chúng ta nghiên cứu file MenuLayer.cpp, file này có code khá giống Bài 7 đã nghiên cứu, tuy nhiên, nó bổ sung thêm 1 vài code tạo hiệu ứng di chuyển mấy cái Label, xem qua chút nhỉ. Trong hàm init() ta thấy các câu lệnh quen thuộc để tạo ra các Label chứa Text với font chữ, và cỡ chữ xác định, đặt chúng lên Layer tại các vị trí. Nhưng có 1 đoạn code khá lạ, chúng ta cùng xem:

titleLeft->setPosition(titleLeftPosBegin);
Action *titleLeftAction = Sequence::create(
                            DelayTime::create(delayTime),
                            EaseBackOut::create(MoveTo::create(1.0, titleLeftPosEnd)),
                            NULL);

titleLeft->runAction(titleLeftAction); // titleLeft là 1 cái Label nhé
this->addChild(titleLeft);

Đoạn code này hiểu như sau: nhiệm vụ của nó là tạo ra một hành động có tên titleLeftAction = lệnh Sequence::create(). Hành động này cụ thể là gì? hãy xem trong hàm Sequene::create(), hàm này có 3 tham số truyền vào
DelayTime::create(delayTime) // là tạo ra thời gian trễ
 EaseBackOut::create(MoveTo::create(1.0, titleLeftPosEnd)) // MoveTo::create(1.0, titleLeftPosEnd) là hàm tạo 1 hành động di chuyển từ điểm đầu tiên titleLeftPosBegin (đã tính toán ở đoạn code bên trên - trong source) đến điểm cuối titleLeftPosEnd ( cũng đã tính toán trong source), EaseBackOut là 1 lớp tạo ra hiệu ứng ( ko biết phải dịch là gì - đây là 1 trong các hiệu ứng di chuyển thông dụng )
NULL : tham số thứ 3 này chưa hiểu lắm, để hiểu sâu hơn hãy nghiên cứu ở đây http://www.cocos2d-x.org/reference/native-cpp/V3.0rc0/index.html

Lệnh titleLeft->runAction(titleLeftAction); là thực hiện cái hành động di chuyển ở bên trên bằng cách gọi function của đối tượng titleLeft ( đây là 1 cái Label ).

this->addChild(titleLeft); // Gắn cái Label vào Layer tạo bởi init(); 

Đoạn code bên dưới cũng tương tự thế, tạo ra 1 chuyển động cho 1 cái Label khác. OK chưa

Giờ thì tới đoạn này, khá khó hiểu

   for (Node *each : menu->getChildren())
    {
each->setScale(0.0f, 0.0f);
Action *action = Sequence::create(DelayTime::create(delayTime),
                                          ScaleTo::create(0.5f, 1.0f),
                                          NULL);
delayTime += 0.2f;
each->runAction(action);
     }

Đây là vòng lặp for để duyệt các item của Menu đó chính là nút Credits và nút New Game đó ( 2 nút này được tạo ra ở bên trên - trong Source, MenuLayer.cpp)

for (Node *each : menu->getChildren()) cũng khá lại so với for (i=0; i<n; i++) cơ bản của C++ nhỉ, hêhe, nhưng chính xác nó là 1 vòng lặp để duyệt các đơn vị của 1 tập nào đó.

Trong vòng lặp

each->setScale(0.0f, 0.0f); là chỉnh cái nút menu Credits , New Game về tỉ lệ 0, biến mất còn gì, sau đó đến lệnh

Action *action = Sequence::create(DelayTime::create(delayTime),
                                          ScaleTo::create(0.5f, 1.0f),
                                          NULL);
là tạo ra 1 hành động phóng to dần cái nút này lên với tỷ lệ nhất định ScaleTo::create(0.5f, 1.0f),
sau đó là thực hiện hành động each->runAction(action);  Kết quả là tạo 1 hiệu ứng cái nút đó phóng to dần lên thôi, đơn giản và dễ hiểu quá phải không.

D > Tiếp theo chúng ta nghiên cứu file SceneManger.cpp, file này dùng để quản lý các Scene được tạo ra và chạy như thế nào, đồng thời khi chuyển từ Scene này sang scene khác có kèm 1 hiệu ứng chuyển cảnh ( như làm mờ, lộn ngược, quay đảo, v.v...)

Mở file lên nào, Trời ơi, sao code dài thế ~150 dòng code cùng với rất nhiều hàm. Vâng, bạn ạ, thực ra code dài nhưng đơn giản dễ hiểu còn hơn đoạn code ngắn mà phức tạp đọc không hiểu gì. Mình nhớ có lần làm 1 bài tập lớn, search mạng đọc 1 cái code Java thì phải có sấp sỉ 10000 dòng (Notepad ++) đọc mãi mới xong. 

Trong file này tuy nhiều code nhưng thật ra có nhiều đoạn code chức năng na ná nhau đó là tạo ra 1 hiệu ứng chuyển cảnh cho Scene, thế nên mình chỉ giới thiệu với các bạn 1-2 đoạn thôi nhé. Bạn yên tâm rằng các hiệu ứng chuyển cảnh này đều được định nghĩa bằng các lớp tương ứng của Cocos2dx V3 nhé ( ví dụ: TransitionFlipY - lộn theo trục Y,  TransitionFade - làm mờ dần, TransitionZoomFlipX - phóng to theo trục X ) vậy đó

- Bài này đang tạm dừng...

Bài 9: Làm game đầu tiên - Tạo nhân vật (Part -1)
More about

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

Người đăng: chisenhungsuutam on Chủ Nhật, 27 tháng 4, 2014

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ạ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á



More about