Post date: Jul 4, 2011 4:53:35 PM
Ngày nay với sự xuất hiện của nhiều platform khác nhau có thể thực C/C++, để tiết kiệm chi phí, một thư viện game phải đảm bảo có thể compile và run tốt trên nhiều platform.
Về cơ bản, thư viện chuẩn C/C++ trên các platform là gần như tương thích với nhau hoàn toàn. Tuy nhiên, trên từng platform vẫn có những đặc điểm riêng, khác biệt.
Một ví dụ, trong lớp CGame.cpp, để sử dụng hàm Sleep(80), ta cần đến thư viện windows.h, vốn không phải là thư viện chuẩn của C/C++. Điều này làm cho việc compile CGame.cpp là không thể khi biên dịch trên trình biên dịch GNU GCC
Một ví dụ khác, hàm printf dùng cho debug có thể dùng cho console win32, tuy nhiên với Android thì không thể, mà phải được thay bằng __android_log_print.
Để làm được như đã nêu, trước tiên, ta cần cấu hình cho từng platform khác nhau khi compile. Ta định nghĩa 1 số file header:
Header.h
#ifndef __HEADER_H__ #define __HEADER_H__ #define PLATFORM_WIN32_VS 1 #define PLATFORM_ANDROID 2 #include "Config.h" #include #include "Macros.h" #endif
Trong file Header.h, ta giả sử sẽ có 2 option về platform là win32 VS hoặc Android
Config.h
#ifndef __CONFIG_H__ #define __CONFIG_H__ #include "Header.h" // Specify game platform // Values: // + PLATFORM_WIN32_VS : win32 by visual studio // + PLATFORM_ANDROID : android #define CONFIG_PLATFORM PLATFORM_WIN32_VS #endif
Trong config.h, ta giả chọn cấu hình hiện tại là win32 VS
Macros.h
#ifndef __MACROS_H__ #define __MACROS_H__ #include "Header.h" #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) #if CONFIG_PLATFORM==PLATFORM_ANDROID # include <android/log.h> # define Log(...) __android_log_print(ANDROID_LOG_INFO, "NativeLib",__VA_ARGS__) #elif CONFIG_PLATFORM==PLATFORM_WIN32_VS # define Log(...) printf(__VA_ARGS__);printf("\n") #endif #if CONFIG_PLATFORM==PLATFORM_WIN32_VS # define TODO(x) __pragma(message("[TODO]:" __FILE__ "("TOSTRING(__LINE__)")" TOSTRING(x))); Log(x); #else # define TODO(x) ; #endif #endif
Trong ví dụ trên, ta dùng 1 hàm wrapper là Log, thay cho printf của win32 console và __android_log_print của Android log. Đồng thời thay thế tất cả các hàm printf bằng Log, và khai báo #include "header.h" ở tất cả các lớp
Tuy nhiên, không phải trong trường hợp nào ta cũng có thể sử dụng macro. Với những trường hợp phức tạp, ta thay thế bằng hàm. Ta định nghĩa một lớp gọi là CDevice chứa các tập hàm này. (Trong ví dụ này, ta giả sử Sleep là một trường hợp ví dụ)
Trong trường hợp cần can thiệp sang một môi trường khác C/C++ (Obj-C hoặc Android Java), lớp device làm nhiệm vụ như một wrapper, đóng vai trò cầu nối, giúp thư viện game tương đối độc lập với platform
CDevice.h
#ifndef __CDEVICE_H__ #define __CDEVICE_H__ #include "Header.h" namespace GameTutor { class CDevice { public: static CDevice* GetInstance() { if (!s_pInstance) { s_pInstance = new CDevice(); } return s_pInstance; } virtual ~CDevice(void) {} void SleepEx(unsigned long milisec); protected: static CDevice* s_pInstance; CDevice() {} }; } #endif
CDevice.cpp
#include "CDevice.h" namespace GameTutor { CDevice* CDevice::s_pInstance = 0; void CDevice::SleepEx(unsigned long milisec) { #if CONFIG_PLATFORM==PLATFORM_WIN32_VS Sleep(milisec); #else TODO("Sleep for CONFIG_PLATFORM!=PLATFORM_WIN32_VS is not implement yet !"); #endif } }
Lúc này, lớp CGame được hiệu chỉnh:
CGame.cpp
#include "Header.h" #include "CGame.h" #include "CStateManagement.h" #include "CDevice.h" namespace GameTutor { .................. void CGame::Run() { this->Init(); while (m_isAlived) { if (m_isPaused) { CStateManagement::GetInstance()->Update(true); } else { CStateManagement::GetInstance()->Update(false); } CDevice::GetInstance()->SleepEx(80); } Destroy(); } }
Ngoài CDeivce, trong các phần tới, sẽ có một số lớp khác đóng vai trò tương tự, nhưng chuyên biệt cho một mục đích sử dụng nào đó.
Download