Thông thường khi khởi tạo 1 đối tượng trong game, các lập trình viên đều khởi tạo 1 vị trí nào đó cho chúng. Điều đó có nghĩa là các đối tượng luôn tồn tại trong game với 1 vị trí nào đó tuỳ thời điểm.
Vị trí đó được thể hiện qua 2 con số x
và y
, tương ứng với toạ độ của 1 điểm trên mặt phẳng. Tuy nhiên, cùng 1 tọa độ, nhưng khi lên 2 thiết bị có kích thước màn hình khác nhau thì vị trí (so với màn hình) hiển thị trên màn hình cũng khác nhau.
Hơn nữa, trong games (games cỡ vừa hoặc lớn) không chỉ tồn tại 1 hay là 2 đối tượng mà lại có rất là nhiều, kéo theo việc kiểm soát và chỉnh sửa vị trí cho chúng khi cần thiết là rất khó khăn. Do vậy, việc quản lý các đối tượng trong games được đặc biệt quan tâm.
Kế thừa từ bài viết Quản Lý Vị Trí Của Các Đối Tượng trong Cocos2d-x, tiến hành hiện thực lại lớp PositionManager
.
Ý tưởng
Để tiện cho việc quản lý số lượng lớn vị trí của các đối tượng, tiến hành đưa tất cả chúng vào 1 file XML và lưu chúng dưới dạng key
/value
, đồng thời để hỗ trợ đa màn hình khi load các đối tượng lên game tiến hành thêm đoạn code sau vào hàm applicationDidFinishLaunching()
// Set the design resolution glview->setDesignResolutionSize(width, height, ResolutionPolicy::EXACT_FIT);
Trong đó:
width
,height
: kích thước background tương ứng được thiết kế.EXACT_FIT
: 1 tuỳ chọn hình ảnh trong game sẽ co giãn theo kích thước của màn hình và không cần tuân theo tỷ lệ ban đầu của thiết kế.
Hiện thực
File plist lưu trữ vị trí các đối tượng
Dưới đây là cấu trúc của 1 file plist để lưu trữ vị trí các đối tượng trong game.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>sidebar_background</key> <string>-317,768</string> <key>title</key> <string>317,1382</string> <key>score_gameplay</key> <string>317,768</string> <key>score_lightmap_gamplay</key> <string>317,768</string> <key>board_background_gameplay</key> <string>1341,768</string> <key>mode_light</key> <string>1024,768</string> <key>icon_mode</key> <string>1024,768</string> </dict> </plist>
PositionManager
Tiến hành hiện thực lớp PositionManager
như sau:
class PositionManager { private: static PositionManager* m_instance; std::map<int, Vec2> m_objPosMap; public: static PositionManager* getInstance(); PositionManager(); ~PositionManager(); void loadObjectsPosition(const char* pListPath); Vec2 getObjectPosition(int obj_id); };
PositionManager
được hiện thực với 2 phương thức chủ yếu là loadObjectsPosition
và getObjectPosition
.
Phương thức loadObjectsPosition
Phương thức này load toàn bộ dữ liệu lưu trữ trong file plist và lưu trữ vào biến m_objPosMap
.
void PositionManager::loadObjectsPosition(const char* pListPath) { auto objData = FileUtils::getInstance()->getValueMapFromFile(pListPath); auto strToVec2 = [](std::string str) -> Vec2 { Vec2 position; int value = 0; for (int i = 0; i < str.length(); i++) { if (str[i] != ',') { value *= 10; value += str[i] - '0'; } else { position.x = value; value = 0; } } position.y = value; return position; }; for (int i = 0; i < TOTAL_OBJ; i++) { m_objPosMap[i] = strToVec2(objData.at(OBJ_POS[i]).asString()); } }
Trong đó:
TOTAL_OBJ
: tổng số đối tượng được lưu trữ trong file.OBJ_POS[i]
: biến lưu trữ các key ở file plist.
2 giá trị này sẽ giúp các bạn lấy được ở bên dưới.
Phương thức getObjectPosition
Vec2 PositionManager::getObjectPosition(int obj_id) { return m_objPosMap.at(obj_id); }
Phương thức getObjectPosition
giúp lấy ra vị trí của 1 object khi ta truyền vào 1 obj_id
tương ứng, vậy làm thế nào để biết được 1 obj_id
nào của 1 đối tượng nào? Dưới đây sẽ hướng dẫn bạn thực hiện điều đó dễ dàng với 1 tool đơn giản do tạo ra trong quá trình thực hiện dự án, giờ sẽ chia sẻ cho các bạn điều đó.
Tạo config từ các đối tượng lưu trữ trong file plist
Nhằm tạo ra 1 file config gồm các ID tương ứng với các key trong file plist, và lấy được 2 giá trị mà bên trên đã nói: TOTAL_OBJ
, OBJ_POS[i]
, hiện thực 1 tool nhỏ bằng Python để tạo ra các config của các đối tượng như sau:
Tool
from plistlib import readPlist FILE_NAME = 'cnf_objects_pos.plist' def getDefineStringFromKey(key, id): return '#define ID_POS_' + str.upper(key) + ' ' + str(id) + '\n' def generateConfigForObject(objFile): pl = readPlist(objFile) index = 0 obj_key_id = '#define PATH_CONF_OBJ_POS "' + objFile + '"\n' obj_key_id += 'extern const char* OBJ_POS[];\n' obj_key_name = 'const char* OBJ_POS[] = { \n' for key in pl: obj_key_id += getDefineStringFromKey(key, index) obj_key_name += '\t"' + key + '"' + ',\n' index += 1 obj_key_name = obj_key_name[:-2] obj_key_name += '\n};\n' obj_key_id += '#define TOTAL_OBJ ' + str(index) + '\n' obj_key_name += '\n\n'; obj_key_id += '\n\n'; return obj_key_id, obj_key_name res_h = '#ifndef __CONFIG_H__\n#define __CONFIG_H__\n\n' res_cpp = '#include "Config.h"\n\n' t = generateConfigForObject(FILE_NAME) res_h += t[0] res_cpp += t[1] res_h += '#endif\t//__CONFIG_H__' #Write output to file f = open('Config.h', 'w') f.write(res_h); f.close() f = open('Config.cpp', 'w') f.write(res_cpp) f.close()
File Config.h
#ifndef __CONFIG_H__ #define __CONFIG_H__ #define PATH_CONF_OBJ_POS "cnf_objects_pos.plist" extern const char* OBJ_POS[]; #define ID_POS_MODE_LIGHT 0 #define ID_POS_TITLE 1 #define ID_POS_SIDEBAR_BACKGROUND 2 #define ID_POS_SCORE_LIGHTMAP_GAMPLAY 3 #define ID_POS_ICON_MODE 4 #define ID_POS_BOARD_BACKGROUND_GAMEPLAY 5 #define ID_POS_SCORE_GAMEPLAY 6 #define TOTAL_OBJ 7 #endif //__CONFIG_H__
File Config.cpp
#include "Config.h" const char* OBJ_POS[] = { "mode_light", "title", "sidebar_background", "score_lightmap_gamplay", "icon_mode", "board_background_gameplay", "score_gameplay" };