Đó là điều kỳ lạ trong ngành công nghiệp của chúng tôi, không những chúng tôi không học được từ những sai phạm của chúng tôi, mà còn không học được từ những thành công của chúng tôi. Brian Kernighan
STDIO Thông thường khi khởi tạo một đối tượng trong game, các lập trình viên đều khởi tạo một 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 một vị trí nào đó tuỳ thời điểm. Vị trí đó được thể hiện qua hai con số x và y, tương ứng với toạ độ của một điểm trên mặt phẳng. Tuy nhiên, cùng một toạ độ, nhưng khi lên hai 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. Do đó, việc quản lý vị trí các đối tượng trong game đặc biệt được quan tâm. Để khắc phục được vấn đề trên, các lập trình viên có thể xử lý vị trí của các đối tượng thông qua kích thước của thiết bị. Dù vậy, theo tôi đây chưa thật sự là giải pháp tối ưu nhất. Thông qua bài viết này, tôi xin giới thiệu đến bạn đọc một giải pháp đơn giản nhưng có thể giải quyết được vấn đề nêu trên.
Nội dung bài viết

Giới thiệu

Thông thường khi khởi tạo một đối tượng trong game, các lập trình viên đều khởi tạo một 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 một vị trí nào đó tuỳ thời điểm. Vị trí đó được thể hiện qua hai con số x và y, tương ứng với toạ độ của một điểm trên mặt phẳng. Tuy nhiên, cùng một toạ độ, nhưng khi lên hai 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 một hay là hai đố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. Bài viết này ra đời là nhằm để giải quyết được điều đó.

Kế thừa từ bài viết Quản Lý Vị Trí Của Các Đối Tượng Trong Cocos2d-x, tôi tiến hành hiện thực lại lớp PositionManager.

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

Bài viết hướng đến tất cả các lập trình viên mới bắt đầu làm game và đang phát triển game đa nền tảng với Cocos2d-x. Các đối tượng khác có thể xem đây như một tài liệu để tham khảo.

Ý tưởng

Để tiện cho việc quản lý số lượng lớn vị trí của các đối tượng, tôi tiến hành đưa tất cả chúng vào một file XML và lưu chúng dưới dạng key/value. Để hiểu hơn về cấu trúc một file XML bạn vui lòng đọc bài viết Cơ bản về XML. Đồng thời để hỗ trợ đa màn hình khi load các đối tượng lên game tôi 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 là kích thước background tương ứng mà bạn thiết kế.  
  • EXACT_FIT là một tuỳ chọn hình ảnh trong game của bạn 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 một 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

Tôi 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. Để hiểu hơn về map trong C++ bạn vui lòng đọc thêm bài viết STL - Map Trong C++.

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 là tổng số đối tượng được lưu trữ trong file
  • OBJ_POS[i] là biến lưu trữ các key ở file plist.

Hai giá trị này tôi 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 một object khi ta truyền vào một obj_id tương ứng. Vậy làm thế nào để biết được một obj_id  nào của một đối tượng nào? Dưới đây tôi sẽ hướng dẫn bạn thực hiện điều đó dễ dàng với một tool đơn giản do tôi tạo ra trong quá trình thực hiện dự án. Giờ tôi 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 một 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 tôi đã nói: TOTAL_OBJ,  OBJ_POS[i]. Tôi hiện thực một 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"
};

Download tool

TOOL_DEMO.zip

Bạn cần hỗ trợ các dự án kết nối không dây?

Quí doanh nghiệp, cá nhân cần hỗ trợ, hợp tác các dự án IoT, kết nối không dây. Vui lòng liên hệ, hoặc gọi trực tiếp 0942.111912.

  • TỪ KHÓA
  • Arduino
  • ESP32
  • ESP8266
  • Wifi
  • Bluetooth
  • Zigbee
  • Raspberry Pi
THẢO LUẬN
ĐÓNG