遊戲設計中的單例模式
恭喜今天股票基金大漲,哈哈。但是,加班還是要繼續,加班寫博客,打發一下時間。因爲晚上的工作效率真的是不高呢,那就做一點總結的工作吧。
說起單例模式,恐怕大家最熟悉不過了,但是每每看到參考書或者參考資料總是覺得很揪心。因爲大部分都是在寫java的單例模式,又要講到多線程狀態下不能保證賦值操作和構造函數執行的順序,所以需要改變一下寫法。今天讀了C++寫的設計模式,才發現C++天生的避免了那些複雜的東西。
下面看一下第一個單例示例
class FileSystem
{
public:
static FileSystem& instance()
{
//惰性初始化
if (instance_ == NULL)
instance_ = new FileSystem();
return *instance_;
}
private:
FileSystem(){}
static FileSystem* instance_;
};
這算是最一般的寫法了,但是不支持多線程情況,稍作修改,代碼是這樣的
class FileSystem
{
public:
static FileSystem& instance()
{
static FileSystem* instance = new FileSystem();
return *instance;
}
private:
FileSystem(){}
};
說一下惰性初始化,其實在遊戲編程來看,單例惰性初始化並沒有特別大的優勢或者說好處。雖然說延遲初始化可以節省一些東西,但是也許初始化的時候正是某個玩家戰鬥激烈或者其他一些什麼重要時刻,這時候分配內存加載資源等等,遊戲也許就會表現的並不是那麼流暢。而且這些單例又是不能銷燬的。
可繼承單例
我第一次見到這種寫法是簡直被驚到了,簡直是完美。更完美的是我學會了用可繼承單例模式來理解這種做法。代碼如下
class FileSystem
{
public:
virtual ~FileSystem(){}
virtual char* readFile(char* path) = 0;
virtual void writeFile(char* path, char* contents) = 0;
};
class PS3FileSystem:public FileSystem
{
public:
virtual char* readFile(char* path)
{
//使用索尼的文件讀寫API......
}
virtual void writeFile(char* path char* contents)
{
//使用索尼的文件讀寫API......
}
};
class WiiFileSystem:public FileSystem
{
public:
virtual char* readFile(char* path)
{
//使用任天堂的文件讀寫API......
}
virtual void writeFile(char* path char* contents)
{
//使用任天堂的文件讀寫API......
}
};
//實例化FileSystem
class FileSystem
{
public:
static FileSystem& instance();
virtual ~FileSystem(){}
virtual char* readFile(char* path) = 0;
virtual void writeFile(char* path, char* contents) = 0;
protected:
FileSystem(){}
};
//靈巧的實例創建
FileSystem& FileSystem::instance()
{
#if PLATFORM == PLAYSTATION3
static FileSystem *instance = new PS3FileSystem();
#elif PLATFORM == WII
static FileSystem* instance = new WiiFileSystem();
#endif
return *instance;
}
這就是代碼的靈性啊。
作者最後給出的代碼和我在實際工作中遇到的代碼很相似,代碼如下:
class Game
{
public:
static Game& instance(){ return instance_; }
//設置log_, et. al....
Log& getLog() { return *log_;}
FileSystem& getFileSystem() { return *fileSystem_;}
AudioPlayer& getAudioPlayer() { return *audioPlayer_;}
private:
static Game instance_;
Log* log_;
FileSystem* fileSystem_;
AudioPlayer* audioPlayer_;
};
這樣,只有Game是全局可見的。函數可以通過它訪問其他系統。
Game::instance().getAudioPlayer().play(VERY_LOUD_BANG);
2018年10月22日