Giới thiệu


Ngày nay với sự xuất hiện của nhiều platform khác nhau có thể thực thi 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àmSleep(80), ta cần đến thư việnwindows.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ịchGNU GCC

Một ví dụ khác, hàmprintfdù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.

Cách thức cấu hình


Để 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: chứa những thông tin header cần cho chương trình
  • Config.h: chứa các thông tin cấu hình
  • Macros.h: chứa các macro thông dụng





1
2
3
4
5
6
7
8
9
10
11
///Header.h
#ifndef __HEADER_H__
#define __HEADER_H__
#define PLATFORM_WIN32_VS 1
#define PLATFORM_ANDROID 2
#include "Config.h"
#include "Macros.h"
#endif





Trong file Header.h, ta giả sử sẽ có 2 option về platform là win32 VS hoặc Android





1
2
3
4
5
6
7
8
9
10
11
12
13
14
/// 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



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/// 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
# 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àmwrapper 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àmprintf bằng Log, và khai báo #include "header.h" ở tất cả các lớp









Xây dựng lớp đại diện / cầu nối


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








1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/// 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











1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/// 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
}
}









Trong ví dụ trên, hàm SleepEx được thay đổi tùy theo platform.

Lúc này, lớp CGame được hiệu chỉnh:





1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/// 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 đó.

Source code Download

Post a Comment Blogger

 
Top