Hướng Dẫn Làm Game Pikachu Với Cocos2d-x (Phần 1)

Xin chào các bạn, hôm nay tôi sẽ giới thiệu các bước đầu tiên để thực hiện thử thách Lập trình trò chơi bằng Cocos2d-x với ngôn ngữ lập trình C++ trong vòng 24 giờ. Chúng ta cùng theo dõi nhé.

1. Cocos2d-x là gì?

Cocos2d-x là một công cụ phát triển trò chơi đa nền tảng: iOS, Android, macOS, Windows và Linux. Đây là một engine trò chơi được hàng triệu lập trình viên sử dụng, với hơn 25 nghìn trò chơi chính thức được phát triển, bao gồm nhiều trò chơi nổi tiếng như: Piano Tiles – Don’t Tap the White Tile, Hill Climbing Racing, FLow Free, Diamond Dash, Idle Heroes, AFK Arena…

Cocos2d-x hỗ trợ 2 ngôn ngữ C++ và Lua, nhưng chủ yếu là C++. Tôi đã chọn cocos2d-x để thực hiện các hướng dẫn này vì C++ có thể coi là ngôn ngữ phổ biến nhất cho người mới bắt đầu lập trình.

Tại sao chọn Cocos2d-x?

– Dễ học cho người mới bắt đầu, hỗ trợ C++11 API (auto, std::function, lambda…).

– Đa nền tảng – Với 1 source code có thể chạy trên cả desktop và di động. Có thể kiểm tra và gỡ lỗi trên máy tính trước khi phát triển trên di động.

– API phong phú với đầy đủ các tính năng (sprites, actions, animations, particles, transitions, timers, events (touch, keyboard, accelerometer, mouse), âm thanh, file IO, persistence, skeletal animations, 3D).

– Hoàn toàn miễn phí.

Trong bài viết này, tôi sẽ tạo một trò chơi Pikachu Onet Connect bằng Cocos2d-x.

2. Cài đặt môi trường

Các yêu cầu:

– Python 2.7.5+ (recommened 2.7.10). Lưu ý rằng phải là Python 2 chứ không phải Python 3. Tải về tại đây.

Thêm python 2 vào PATH. Nếu bạn đã cài đặt cả Python 2 và Python 3, hãy đảm bảo PATH của Python 2 được đặt trước PATH của Python 3.

– CMake 3.6+. Tải về tại đây.

Thêm đường dẫn đến thư mục bin của CMake vào PATH.

Tải về phiên bản mới nhất của cocos2d-x tại đây.

Bài viết này sử dụng phiên bản cocos2d-x 4.0.

Sau khi tải về và giải nén, hãy chạy file setup.py để cập nhật PATH:

cd folder_giai_nen

python setup.py

Nếu bạn không có biến môi trường NDK_ROOT hoặc ANDROID_SDK_ROOT, bạn có thể bỏ qua và nhấn ENTER.

3. Tạo dự án trò chơi mới:

Sau khi cài đặt xong, hãy tạo dự án mới bằng cửa sổ command prompt theo cú pháp:

cocos new -l cpp -d FOLDER_PATH PROJECT_NAME

-l cpp: chọn ngôn ngữ C++

-d FOLDER_PATH: đường dẫn đến thư mục mà bạn muốn chứa dự án (bỏ qua nếu muốn tạo trong thư mục hiện tại)

PROJECT_NAME: tên dự án

Có Thể Bạn Quan Tâm :   Nhân viên ngân hàng tiếng anh là gì và đọc như thế nào cho đúng

Nếu bạn muốn tạo trò chơi màn hình dọc, hãy thêm ‘-portrait’

Ví dụ:

cocos new -l cpp -d D:/MyProjects -portrait Pikachu2020

4. Build và kiểm tra trò chơi với Visual Studio:

Nếu bạn chưa có Visual Studio, bạn có thể tải về phiên bản mới nhất tại đây.

Trong bài viết này, tôi sẽ hướng dẫn sử dụng Visual Studio 2019 để build và kiểm tra dự án.

  • Tạo dự án Visual Studio 2019 bằng cmake:

Điều hướng tới thư mục dự án

Tạo thư mục win32-build

Điều hướng tới thư mục win32-build

Sử dụng cmake để tạo dự án VS 2019 (chú ý là phải có -A Win32)

  • Mở solution Pikachu2020.sln trong thư mục win32-build vừa tạo:

  • Sau khi đã mở solution bằng VS, chọn Build -> Build Solution:

  • Đặt Pikachu2020 làm dự án khởi động. Set as StartUp project:

  • Chọn Local Windows Debugger ở thanh công cụ phía trên hoặc nhấn F5 để chạy dự án:

Tham khảo thêm về cách cài đặt cocos2d-x tại đây

5. Điều chỉnh kích thước của cửa sổ trò chơi:

Dự án được tạo ra từ câu lệnh cocos new được sao chép từ template sẵn. Code của chương trình chính (các file .h và .cpp) sẽ được lưu trong thư mục Classes/. Những file này sẽ được sử dụng trên mọi nền tảng. Code dành riêng cho từng nền tảng sẽ được lưu ở các thư mục riêng (proj.android, proj.ios_mac, proj.linux, proj.win32).

Lớp AppDelegate (AppDelegate.h, AppDelegate.cpp) là lớp được gọi khi chương trình bắt đầu chạy.

Kích thước của cửa sổ được định nghĩa trong file AppDelegate.cpp trong thư mục Classes/:

Bạn có thể thay đổi kích thước của cửa sổ giao diện trò chơi bằng cách điều chỉnh các thông số (chiều ngang, chiều dọc) của designResolutionSize.

* Không nên thay đổi smallResolutionSize, mediumResolutionSize, largeResolutionSize.

* Nên để kích thước cửa sổ trò chơi có cùng tỷ lệ với kích thước màn hình thiết bị.

6. Scene và Node

Hãy tưởng tượng Scene là một cảnh trong trò chơi, trên một cảnh chúng ta có thể đặt, sắp xếp, bố trí các đối tượng. Mỗi đối tượng trên cảnh đó chính là một Node, có thể là nhãn (Label), hình ảnh (Image), hình ảnh động (Sprite) và còn nhiều hơn thế.

Một đối tượng thuộc lớp Scene chứa những gì sẽ được hiển thị trên màn hình sau khi chạy lệnh director->runWithScene(scene). Các đối tượng thuộc các lớp thừa kế lớp Node sẽ được vẽ khi chúng được thêm vào một scene.

Trong dự án đã có lớp HelloWorld thừa kế lớp Scene, đây chính là cửa sổ hiển thị trên màn hình khi chạy chương trình.

Trong scene HelloWorld, có các node sau:

– auto menu = Menu::create(…)

  • Đây là đối tượng kiểu Menu. Thường dùng để chứa các node dạng MenuItem, ví dụ như các nút bấm để chuyển Scene, Play, Quit, Settings, About, …

– auto closeItem = MenuItemImage::create(…)

  • Biểu tượng đóng ứng dụng thuộc lớp MenuItemImage – thừa kế lớp MenuItem. Các đối tượng thuộc các lớp thừa kế MenuItem phải được đặt trong Menu.

– auto label = Label::createWithTTF(…)

  • Chữ tiêu đề “Hello World”
Có Thể Bạn Quan Tâm :   Tub girl là gì I 2 girl 1 cup là gì | Tại sao không nên tìm – Phòng khám Bắc Giang

– auto sprite = Sprite::create(…)

  • Ảnh biểu tượng của cocos “HelloWorld.png”

7. Scene Graph và z-order:

Bạn có thể thắc mắc: Nếu 2 node được đặt cùng một vị trí, thì chúng sẽ xếp chồng lên nhau như thế nào? Node nào sẽ ở trên, node nào sẽ ở dưới?

Việc sắp xếp, bố trí các node trong một scene không chỉ đơn thuần là sắp xếp theo vị trí trên không gian 2D (Ox, Oy), bạn còn phải quan tâm đến scene graphz-order.

Scene Graph: là một đồ thị cây biểu diễn quan hệ cha con giữa các node. Trong scene HelloWorld, menu, label và sprite được thêm trực tiếp vào scene nên chúng là con của scene, trong khi đó closeItem được thêm trực tiếp vào menu nên là con của menu.

z-order: Quyết định thứ tự hiển thị của các node con cùng một node cha.

  • Các node con của một scene hoặc của một node cha sẽ được sắp xếp theo thứ tự z-order không giảm. Nếu 2 node có z-order bằng nhau, node được thêm trước sẽ ở phía trước.
  • Khi thêm một node vào scene hoặc một node khác, bạn có thể thêm một tham số int vào hàm addChild với ý nghĩa là z-order của node được thêm. Z-order mặc định là 0 nếu không được định nghĩa.

Trong hàm HelloWorld::init():

this->addChild(menu, 1); // menu là con của scene, z-order = 1

auto menu = Menu::create(closeItem, NULL); // closeItem là con của menu, z-order mặc định = 0

this->addChild(label, 1); // label là con của scene, z-order = 1

this->addChild(sprite, 0); // sprite là con của scene, z-order = 0

Cocos2d-x vẽ các node theo thứ tự duyệt in-order: Khi duyệt đến một node cha:

– Duyệt những node con có giá trị z-order < 0, theo thứ tự tăng dần z-order.

– Vẽ node cha.

– Duyệt những node con có giá trị z-order >= 0, theo thứ tự tăng dần z-order.

Thứ tự vẽ của scene HelloWorld: Scene -> sprite -> menu -> closeItem -> label .

(scene và menu không có hình dạng nên không hiển thị trên màn hình)

Khi một node được vẽ, nó có thể được vẽ lên trên các node đã được vẽ trước đó và cũng có thể bị vẽ lên bởi các node sau.

8. Tạo thêm Node và Scene

8.1 Tạo nút PLAY

Tôi sẽ xóa sprite và tạo một nút PLAY ở giữa cửa sổ HelloWorld. Khi nhấp vào nút này, màn hình sẽ chuyển sang cửa sổ chơi trò chơi.

Tạo nút mới trong hàm HelloWorld::init() như sau:

“`cpp
bool HelloWorld::init() {
// …
// Thêm label “PLAY” với callback HelloWorld::play
auto play = MenuItemLabel::create(
Label::createWithTTF(“PLAY”, “fonts/Marker Felt.ttf”, 40),
CC_CALLBACK_1(HelloWorld::play, this));
// Kiểm tra play có hợp lệ hay không
CCASSERT(play != nullptr, “problem loading fonts/Marker Felt.ttf”);
// Đặt vị trí PLAY ở giữa màn hình
play->setPosition((Vec2(visibleSize) – origin) / 2);
// Thêm PLAY vào menu
menu->addChild(play);
// …
}

void HelloWorld::play(Ref* pSender) {
auto gameScene = GameScene::createScene();
Director::getInstance()->replaceScene(
TransitionFade::create(0.5, gameScene, Color3B(0, 255, 255)));
}
“`

Với các node thuộc lớp thừa kế MenuItem (MenuItemLabel, MenuItemImage, MenuItemSprite…), ta có thể gắn cho nó một hàm callback. Hàm này sẽ được gọi mỗi khi node được kích hoạt (nhấp chuột, bấm, …).

Có Thể Bạn Quan Tâm :   Milk Thistle (Kế sữa) là gì?Những lợi ích của Milk Thistle (kế sữa) đối với sức khoẻ

Mỗi lần chúng ta nhấp vào chữ PLAY, hàm callback HelloWorld::play(Ref*) sẽ được gọi. Tạm thời tôi để hàm này in ra dòng log với nội dung “PLAY”.

8.2 Tạo GameScene

Tiếp theo, tôi sẽ tạo một Scene mới. Scene này sẽ được sử dụng để chứa các yếu tố hiển thị khi chúng ta đang chơi trò chơi, ví dụ như bảng chơi Pikachu, thanh thời gian, nút quay lại, …. Đầu tiên, tôi sẽ tạo lớp GameScene:

“`cpp
// GameScene.h:
#pragma once

#include “cocos2d.h”

USING_NS_CC;

class GameScene : public Scene
{
public:
static Scene* createScene();
virtual bool init();
CREATE_FUNC(GameScene);
};

// GameScene.cpp:
#include “GameScene.h”
#include

Scene* GameScene::createScene()
{
return GameScene::create();
}

bool GameScene::init()
{
// super init()
if (!Scene::init())
return false;

return true;
}
“`

Đây là một Scene trống, khi vẽ sẽ chỉ có màn hình màu đen.

8.3 Chuyển Scene

Tôi muốn khi nhấp nút PLAY, màn hình sẽ chuyển từ HelloWorld sang GameScene, vì vậy tôi có thể chỉnh sửa hàm callback của nút PLAY như sau:

“`cpp
void HelloWorld::play(Ref* pSender)
{
Director::getInstance()->replaceScene(GameScene::createScene());
}
“`

Hoặc có thể sử dụng hiệu ứng TransitionFade (có nhiều hiệu ứng khác tại đây)

“`cpp
void HelloWorld::play(Ref* pSender)
{
auto gameScene = GameScene::createScene();
Director::getInstance()->replaceScene(
TransitionFade::create(0.5, gameScene, Color3B(0, 255, 255)));
}
“`

Tương tự như nút PLAY, tôi sẽ tạo một nút BACK để quay lại scene HelloWorld từ GameScene. Lần này, tôi sử dụng MenuItemImage.

GameScene.cpp:

“`cpp
bool GameScene::init()
{
// super init()
if (!Scene::init())
return false;

auto visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();

auto back = MenuItemImage::create(“back1.png”, “back2.png”, CC_CALLBACK_1(GameScene::back, this));
CCASSERT(back != nullptr, “Fail to load BACK images”);

auto menu = Menu::create(back, nullptr);
this->addChild(menu);
// Đặt vị trí BACK ở phía trên cùng bên trái màn hình
back->setPosition(origin + Vec2(back->getContentSize().width / 2, visibleSize.height – back->getContentSize().height / 2));

return true;
}

void GameScene::back(Ref* pSender)
{
auto homeScene = HelloWorld::createScene();
Director::getInstance()->replaceScene(
TransitionFade::create(0.5, homeScene, Color3B(0, 255, 255)));
}
“`

Hàm khởi tạo MenuItemImage::create() như trên nhận vào 2 ảnh “back1.png” và “back2.png”, lần lượt là ảnh trong trạng thái bình thường và ảnh trong trạng thái được chọn (khi nhấp chuột, bấm, …). Bạn hãy lưu ảnh trong thư mục Resource/ của dự án và sử dụng khi cần tạo Image, Sprite, …

Ở đây là ảnh back1.png và back2.png của tôi:

Và đây là kết quả:

Kết luận:

Đến đây, tôi đã giới thiệu về công cụ Cocos2d-x và những khái niệm, yếu tố cơ bản của nó. Đây là một trong những công cụ phát triển trò chơi phổ biến nhất và đã góp phần tạo nên nhiều trò chơi nổi tiếng.

Sau bài này, tôi hy vọng các bạn có thể tạo một scene, tạo và thêm node vào scene. Tôi khuyến khích bạn thử tạo scene với nhiều node ở nhiều vị trí khác nhau để thử nghiệm xem chúng tương tác (chồng lên nhau, che lấp nhau…) như thế nào. Nếu có bất kỳ vấn đề gì, hãy để lại comment dưới bài viết này.

Ở các phần tiếp theo, tôi sẽ nói về cách xử lý sự kiện trong cocos2d-x, tạo bảng game, …

Code phần 1 của tôi:

– https://github.com/s34vv1nd/Cocos2dx-Tutorials/tree/master/part1

Tài liệu tham khảo:

– https://docs.cocos2d-x.org/cocos2d-x/v4/en/

Back to top button