Game Studio
Liên kế mạng xã hội

Game Studio


Quản Lý Vị Trí Của Các Đối Tượng Trong Cocos2d-x

Giới thiệu

Trong lập trình game nói chung và lập trình game với Cocos2d-x nói riêng, việc xử lý “cứng” các đối tượng (như vị trí, tỉ lệ, góc quay, …) là điều nên tránh. Khi đem sản phẩm lên một màn hình có tỉ lệ khác, vị trí các đối tượng có thể sẽ không còn được như mong muốn. Do đó, việc quản lý động các đối tượng sẽ tối ưu hơn trong lập trình game đa màn hình. Bài viết sau sẽ giới thiệu các bạn một cách hiện thực đơn giản giải quyết vấn đề trên trong Cocos2d-x.

Tiền đề bài viết

Khi thực hiện dự án Project Zero với các thành viên thuộc STDIO PLAY, được sự hướng dẫn của anhKevin La, Taco Nguyễn, Brian Vũ, tôi đã hiện thực thành công lớp PositionManager để lấy dữ liệu vị trí các đối tượng từ file. Bài viết này nhằm chia sẻ kĩ thuật và kinh nghiệm cá nhân của tôi cho mọi người.

Đối tượng hướng đến

Các lập trình viên phát triển game đa nền tảng, đa màn hình. Bài viết sử dụng Cocos2d-x nên người đọc cần có kiến thức cơ bản về Cocos2d-x. Các đối tượng khác vui lòng đọc bài ở mức độ tham khảo.

Công cụ hỗ trợ

Bài viết sử dụng công cụ Cocos2d-x v3.4, cùng với Visual Studio 2013 Community. Xem hướng dẫn cài đặt và setup môi trường làm việc trên Cocos2d-x tại Cài Đặt Cocos2d-x Và Khởi Tạo Project Trên Windows. Các phiên bản cũ hơn của Cocos2d-x sẽ có sự khác biệt.

Lớp Dictionary

Cocos2d-x đã hiện thực sẵn lớp Dictionary, hỗ trợ lập trình viên đọc file theo cấu trúc của file XML. Các key/value sẽ được lưu trữ trong file dưới dạng tag (Tìm hiểu thêm về XML tại Cơ Bản Về XML).

File lưu trữ position phải tuân theo một cấu trúc định sẵn với phần ở rộng mặc định của file là .plist. Bạn nên giữ nguyên để đồng bộ với cộng đồng lập trình viên Cocos2d-x.

Trong bài viết, tôi hiện thực đơn giản file plist để minh họa với các key/value như sau:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  3. <plist version="1.0">
  4. title
  5. 540,1250
  6. button_play
  7. 540,800
  8.  
  9. button_sound
  10. 200,1400

Các phương thức cần lưu ý khi sử dụng Dictionary:

createWithContentsOfFile

Đối số truyền vào là đường dẫn đến file plist của bạn. Nếu đường dẫn không tồn tại sẽ trả về nullptr. Thông qua hàm này, toàn bộ dữ liệu có trong file plist sẽ được xử lý thành các bộ key/value. Các bạn cần tạo sẵn một đối tượng Dictionary để lưu trữ lại các bộ key/value này.

valueForKey

Hàm có tác dụng trả về value có dạng const __String* với key tương ứng. Nếu key không tồn tại sẽ trả về một chuỗi rỗng.

Các kiểu dữ liệu phức tạp cấp cao như array hay dict sẽ không được trình bày trong bài viết này. Dữ liệu ở dạng chuỗi sẽ hiệu quả nhất, ta hoàn toàn có thể thao tác để chuyển từ chuỗi sang các kiểu dữ liệu mong muốn một cách dễ dàng.

Hiện thực lớp PositionManager

Lớp PositionManager có các phương thức chính là loadObjectsPosition và getObjectPosition. Tôi sẽ hiện thực hai phương thức này bên dưới. Khai báo lớp PositionManager như sau:

  1. class PositionManager
  2. {
  3. private:
  4. static PositionManager* m_instance;
  5. public:
  6. PositionManager();
  7. ~PositionManager();
  8.  
  9. static PositionManager* getInstance();
  10.  
  11. void loadObjectsPosition(const char* pListPath);
  12. Vec2 getObjectPosition(const String* objectPosition);
  13. // Your variable to store object position (Vec2)
  14. Vec2 m_title;
  15. Vec2 m_buttonPlay;
  16. Vec2 m_buttonSound;
  17. }

Do chỉ có duy nhất một PositionManager tồn tại trong game nên tôi áp dụng kỹ thuật Singleton để tiện quản lý.

loadObjectsPosition

Hàm loadObjectsPosition có tác dụng lưu trữ vị trí của các đối tượng được load vào file plist.

  1. void PositionManager::loadObjectsPosition(const char* pListPath)
  2. {
  3. Dictionary* objectsListPosition = Dictionary::createWithContentsOfFile(pListPath);
  4.  
  5. // get title position
  6. const __String* strTitlePosition = objectsListPosition->valueForKey("title");
  7. PositionManager::getInstance()->m_title = getObjectPosition(strTitlePosition);
  8. // get play button position
  9. const __String* strButtonPlayPosition = objectsListPosition->valueForKey("button_play");
  10. PositionManager::getInstance()->m_buttonPlay= getObjectPosition(strButtonPlayPosition);
  11. // get sound button position
  12. const __String* strButtonSoundPosition = objectsListPosition->valueForKey("button_sound");
  13. PositionManager::getInstance()->m_buttonSound= getObjectPosition(strButtonSoundPosition);
  14. }

Value dạng chuỗi sẽ được lưu trữ lại trong các biến const __String* và được phân tích (parse) để lưu trữ lại vào PositionManager dưới dạng Vec2. Các kiểu dữ liệu int, float, bool, … được lưu trữ trực tiếp qua hàm valueForKey.

getObjectPosition

Hàm getObjectPosition sẽ hỗ trợ parse dữ liệu từ dạng const __String* sang Vec2. Việc phân tích dữ liệu chuỗi sang các kiểu dữ liệu khác khá đơn giản và có nhiều cách hiện thực khác nhau. Dưới đây là cách tôi sử dụng trong Project Zero:

  1. Vec2 PositionManager::getObjectPosition(const __String* strObjectPosition)
  2. {
  3. Vec2 objectPosition;
  4.  
  5. int value = 0;
  6. for(size_t i = 0; i < strObjectPosition->_string.length(); i++)
  7. {
  8. if(strObjectPosition->_string[i] >= '0' && strObjectPosition->_string[i] <= '9')
  9. {
  10. value *= 10;
  11. value += (strObjectPosition->_string[i] - '0');
  12. }
  13. else
  14. {
  15. objectPosition.x = value;
  16. value = 0;
  17. }
  18. }
  19.  
  20. objectPosition.y = value;
  21. return objectPosition;
  22. }

Đôi lời nhắn gửi

Qua thời gian làm việc chung với các thành viên của STDIO PLAY, tôi đã học hỏi và tích lũy được nhiều kinh nghiệm, trải nghiệm mới mẻ. Cảm ơn các anh Kevin La, Taco Nguyễn và Brian Vũ đã giúp tôi và các bạn có được cơ hội làm việc với các dự án thực tế.

Theo: Stdio


Đăng sự kiện cho developer