今天的内容终于要与游戏沾边了,sprite是游戏里面贴图的小组件,比如地形上面的每一个方块,草地、水面、道路、铁轨,以及在上面跑的汽车、火车,天上的飞机,都是属于sprite。OpenTTD里面在游戏启动阶段会扫描预设的磁盘路径,把扩展名 grf 的文件信息解密加载到内存cache里面,后面的贴图直接采用内存块拷贝的方式推送到显卡上面。我们使用 openttd -d 2 启动游戏,在主控上可以看到大量包含 Replacing sprites 关键字的信息,如下:
通过这个关键字,我们可以检索到以下的调用链:
KeyWord: Replacing sprites
<- newgrf.cpp GraphicsNew /* Action 0x05 */
<- newgrf.cpp DecodeSpecialSprite()
<- newgrf.cpp LoadNewGRFFile()
<- newgrf_config.cpp FillGRFDetails()
<- gfxinit.cpp LoadSpriteTables()
<- gfxinit.cpp GfxLoadSprites()
<- genworld.cpp GenerateWorld()
这里特别需要说明的是,DecodeSpecialSprite()方法定义二维函数数组 handlers , 通过解析 grf文件里面的字段来确定使用具体的函数实现来解析文件生成相应的sprite
我们进一步分析 GraphicsNew()方法调用了 spritecache.cpp LoadNextSprite()
/**
* Load a real or recolour sprite.
* @param load_index Global sprite index.
* @param file_slot GRF to load from.
* @param file_sprite_id Sprite number in the GRF.
* @param container_version Container version of the GRF.
* @return True if a valid sprite was loaded, false on any error.
*/
bool LoadNextSprite(int load_index, byte file_slot, uint file_sprite_id, byte container_version)
{
...
SpriteCache *sc = AllocateSpriteCache(load_index);
sc->file_slot = file_slot;
sc->file_pos = file_pos;
sc->ptr = data;
sc->lru = 0;
sc->id = file_sprite_id;
sc->type = type;
sc->warned = false;
sc->container_ver = container_version;
return true;
}
在这个方法的最后部分,构造了一个SpriteCache类型的变量 sc,我们进一步分析AllocateSpriteCache()
struct SpriteCache {
void *ptr;
size_t file_pos;
uint32 id;
uint16 file_slot;
int16 lru;
SpriteType type; ///< In some cases a single sprite is misused by two NewGRFs. Once as real sprite and once as recolour sprite. If the recolour sprite gets into the cache it might be drawn as real sprite which causes enormous trouble.
bool warned; ///< True iff the user has been warned about incorrect use of this sprite
byte container_ver; ///< Container version of the GRF the sprite is from.
};
static inline SpriteCache *GetSpriteCache(uint index)
{
return &_spritecache[index]; <-- 看这里
}
static SpriteCache *AllocateSpriteCache(uint index)
{
if (index >= _spritecache_items) {
/* Add another 1024 items to the 'pool' */
uint items = Align(index + 1, 1024);
DEBUG(sprite, 4, "Increasing sprite cache to %u items (" PRINTF_SIZE " bytes)", items, items * sizeof(*_spritecache));
_spritecache = ReallocT(_spritecache, items);
/* Reset the new items and update the count */
memset(_spritecache + _spritecache_items, 0, (items - _spritecache_items) * sizeof(*_spritecache));
_spritecache_items = items;
}
return GetSpriteCache(index);
}
通过以上代码,我们可以了解到 游戏启动阶段,构造了 _spritecache 这个SpriteCache类型的数组,后面的学习中,我们将进一步了解到程序是如何使用内存中的这个数组完成界面元素的绘制工作。