《游戏中的资源管理――资源高速缓存》
转载请注明出处:http://groups.google.com/group/jianguhan 
1.什么是资源高速缓存 
   
资源高速缓存的原理与其它内存高速缓存的工作原理是相似的。在游戏的状态转换过程中,有些数据是刚才使用过的,那么直接从资源高速缓存中载入即可。例
如,RPG游戏中主角从大地图进入一个房间,探索一番后主角退出房间,此时只要直接从缓存中载入大地图数据即可,节省了从硬盘载入数据的时间,要知道从
硬盘载入数据是非常慢的。当然,如果你的游戏所使用的数据文件很少,那么你可以在游戏运行过程中把这些数据完全储存在内存中,而不使用资源高速缓存。
2.一个简单的资源高速缓存管理器 
    下面我将向你展示一个比较简单的资源高速缓存管理器,源代码来自我上一个游戏,如果你需要知道更多关于资源高速缓存方面的知识,请参考<<Game Coding Complete>>的第八章。 
首先,需要一个机制来唯一标识一个资源,我们用下面这个结构来做资源句柄: 
struct ResHandle 
{ 
     ResHandle(std::string &resName, void *buffer, int size) 
     { 
         m_resName = resName; 
         m_size   = size; 
         m_buffer = buffer; 
     } 
     ~ResHandle() 
     { 
         if (m_buffer != 0) delete[] m_buffer; 
     } 
     std::string   m_resName;    //资源名 
     void          *m_buffer;    //资源句柄所标识的资源 
     DWORD         m_size;       //资源所占内存大小 
}; 
好了,现在我们可以从资源名来找出这个资源了,接下来实现这个资源高速缓存管理器: 
class CacheManager 
{ 
public: 
     CacheManager(); 
     ~CacheManager(); 
     //载入资源,resName为资源名,若载入成功size被设为该资源的大小 
    //注意,管理中的资源不能在管理器外用delete显示的删除它 
    void*    Load(std::string resName, DWORD *size = 0); 
    //设置缓存大小,单位MB 
     void      SetCacheSize(int sizeMB)    { m_cacheSize = sizeMB * 1024 * 1024; } 
     //得到缓存大小,单位MB 
     int      GetCacheSize()              { return m_cacheSize / 1024 /1024; } 
private: 
     void     Free();                          //释放lru链表中最后一个资源 
     void     *Update(ResHandle *res);         //更新lru链表 
     ResHandle *Find(std::string &resName);     //找出该资源名的资源句柄 
private: 
     DWORD m_cacheSize;     //缓存大小 
     DWORD m_allocated;     //已使用的缓存大小 
//lru链表,记录最近被使用过的资源 
     std::list<ResHandle*>                m_lru;   
    //资源标识映射 
     std::map<std::string, ResHandle*>    m_resources; 
 
}; 
CacheManager:: CacheManager () 
{ 
     m_cacheSize = 0; 
     m_allocated = 0; 
} 
CacheManager::~ CacheManager () 
{ 
          while (!m_lru.empty()) Free();   //释放所有管理中的资源 
} 
void * CacheManager::Load(std::string resName, DWORD *size) 
{ 
     ResHandle *handle = Find(resName);   //查找该资源是否在缓存中 
     if (handle != 0) //如果找到该资源句柄,则返回该资源并更新lru链表 
     { 
         if (size != 0) *size = handle->m_size; 
         return Update(handle); 
     } 
     else 
     { 
         //先检测资源大小 
         DWORD _size = 资源大小; 
         //是否有足够空间? 
         while (_size > (m_cacheSize - m_allocated)) 
         { 
              if (m_lru.empty()) break; 
              Free(); 
         } 
         m_allocated += _size; 
         buffer = new char[_size]; 
//在这里用任何你能想到的办法载入资源文件到buffer 
         … 
         … 
//记录当前资源 
         ResHandle *handle = new ResHandle(resName, buffer, _size); 
         m_lru.push_front(handle); 
         m_resources[resName] = handle; 
         if (size != 0) *size = _size; 
         return buffer; 
     } 
     return 0; 
 
} 
void CacheManager::Free() 
{ 
     std::list<ResHandle*>::iterator gonner = m_lru.end(); 
     gonner--; 
     ResHandle *handle = *gonner; 
     m_lru.pop_back(); 
     m_resources.erase(handle->m_resName); 
     m_allocated -= handle->m_size; 
     delete handle; 
} 
void * CacheManager::Update(ResHandle *res) 
{ 
     m_lru.remove(res); 
     m_lru.push_front(res); 
     m_size = res->m_size; 
     return res->m_buffer; 
} 
ResHandle * CacheManager::Find(std::string &resName) 
{ 
     std::map<std::string, ResHandle*>::iterator it = m_resources.find(resName); 
     if (it == m_resources.end()) return 0; 
     return (*it).second; 
} 
至此,你已经可以在游戏中缓存任何你想缓存的资源了^_^ 
3. 资源管理进阶 
    至此你已经可以在游戏中缓存任何你想缓存的资源了,但是你的任务还没完成,当你请求的资源存在于缓存之外时,那个闪耀的硬盘灯可能就是玩家最感兴趣的东西了。 
因此你必须根据不同的游戏类型使用不同的载入方式: 
    一次载入所有东西:适用于任何以界面或关卡切换的游戏 
    只在关键点载入资源:很多射击游戏都使用这样的设计,如“半条命” 
    持续载入:适用于开放型地图的游戏,如“侠盗猎车手” 
    如果有可能的话,你还可以使用缓存预测机制,当CPU有额外时间的时候可以把未来可能用到的资源载入到资源高速缓存。 
    最后,尽管在游戏的资源管理中资源打包不是必须的,但仍然建议大家把资源文件按类型分别打包到单一的文件中,这将为你节省磁盘空间,并加快游戏的载入速度。