NES 模擬器開(kāi)發(fā)教程 11 - PPU 精靈

了解 background 如何繪圖之后,sprite 也就簡(jiǎn)單一些了

1. OAM

細(xì)心的人可能會(huì)注意到,結(jié)合到 PPU 內(nèi)存映射時(shí)會(huì)發(fā)現(xiàn),PPU 總線上沒(méi)有和 sprite 直接相關(guān)的信息

  • pattern table:存儲(chǔ)圖像數(shù)據(jù)
  • name table:存儲(chǔ) background 在 pattern table 的索引
  • palette:調(diào)色板

以上數(shù)據(jù)沒(méi)有任何一個(gè)指明了 sprite 的信息,那么 sprite 信息是放在哪里的?
這里就要提到 OAM 了,OAM 全稱(chēng) Object Attribute Memory,位于 PPU 芯片中,一共 256 bytes,一個(gè) sprite 用 4 bytes,所以總共能表示 64 個(gè) sprite。但是 OAM 并不存在于 PPU 或 CPU 總線上,需要 CPU 通過(guò) PPU 寄存器或者 DMA 方式才能寫(xiě)入

  • 寄存器寫(xiě)入
    通過(guò) OAMADDR(0x2003)OAMDATA(0x2004) 寫(xiě)入 OAM 數(shù)據(jù),寫(xiě)入前首先通過(guò) OAMADDR 寫(xiě)入起始地址,之后通過(guò) OAMDATA 寫(xiě)入數(shù)據(jù),數(shù)據(jù)可以連續(xù)寫(xiě)入,每寫(xiě)入一次地址自動(dòng) +1
  • DMA 寫(xiě)入
    通過(guò) OAMDMA(0x4014) 寫(xiě)入 CPU PAGE 地址,之后 DMA 會(huì)自動(dòng)將 CPU 整個(gè) PAGE 的數(shù)據(jù)拷貝到 OAM 中。CPU PAGE 為 256 bytes,比如往 OAMDMA 寫(xiě)入 2,則會(huì)將 CPU 總線上的 0x200 ~ 0x2FF 的數(shù)據(jù)拷貝到 OAM。另外,DMA 會(huì)占用 512 個(gè) CPU 時(shí)鐘(奇數(shù) CPU 周期還會(huì)再加一個(gè)時(shí)鐘,前期可以先不考慮)
    DMA 寫(xiě)入速度快于寄存器寫(xiě)入,所以追求效率的時(shí)候會(huì)采用此方式

2. Sprite 數(shù)據(jù)

一個(gè) Sprite 在 OAM 中需要 4 bytes:

  • Byte 0:
    Sprite 的 Y 坐標(biāo)
  • Byte 1:
    76543210
    ||||||||
    |||||||+- Bank ($0000 or $1000) of tiles
    +++++++-- Tile number of top of sprite (0 to 254; bottom half gets the next tile)
    
    該字節(jié)類(lèi)似于 name table,sprite 有 2 種模式:
    1. 8 x 8
      整個(gè) byte 類(lèi)似于 name table,由 PPUCTRL 的 bit 3 選取 bank 之后,加上自身數(shù)據(jù) x 16 得到偏移量
    2. 8 x 16
      該模式下 PPUCTRL 的 bit 3 不再起作用,bank 由 bit 0 決定,并且偏移量不再是 x 16,而是 x 32,具體參考 http://wiki.nesdev.com/w/index.php/PPU_OAM,講得非常清楚
  • Byte 2:
    76543210
    ||||||||
    ||||||++- Palette (4 to 7) of sprite
    |||+++--- Unimplemented
    ||+------ Priority (0: in front of background; 1: behind background)
    |+------- Flip sprite horizontally
    +-------- Flip sprite vertically
    
    bit 0-1 決定高 2 bit 的 palette,類(lèi)似于 attribute table 的功能
    bit 5 決定優(yōu)先級(jí),如果 sprite 像素和 background 像素都不是透明像素的情況下(即 palette index % 4 != 0),則決定了到底顯示 sprite 還是 background
    bit 6-7 決定是否翻轉(zhuǎn)像素,比如人物往右走設(shè)置為不翻轉(zhuǎn),往左走則設(shè)置為垂直翻轉(zhuǎn)
  • Byte 3:
    Sprite 的 X 坐標(biāo)

3. Sprite 0 hits

Sprite 有一個(gè)非常特殊的地方,叫 精靈 0 命中,如果不實(shí)現(xiàn)這個(gè)功能的話,很多游戲都沒(méi)法正常運(yùn)行,比如馬里奧 1,沒(méi)實(shí)現(xiàn)的情況下,會(huì)一直卡在主界面

該功能的作用是,如果 PPU 在渲染的時(shí)候,如果 background 的不透明像素與 sprite 0 的不透明像素重疊的時(shí)候,會(huì)產(chǎn)生 sprite 0 hits,并且在當(dāng)前幀只會(huì)產(chǎn)生一次

同時(shí),產(chǎn)生 hit 也是有條件的,具體參考:http://wiki.nesdev.com/w/index.php/PPU_OAM#Sprite_zero_hits

那么它的作用是什么?主要用來(lái)分割屏幕。之前在 background 時(shí)介紹過(guò),CPU 通過(guò) PPUSCROLL 來(lái)控制背景移動(dòng),但是如果希望屏幕上半部分靜止,下半部分移動(dòng)呢?比如馬里奧 1:


giphy.gif

可以看到頂部的狀態(tài)欄始終是靜止的,如果 CPU 不知道 PPU 繪制到哪里的情況下,這個(gè)功能沒(méi)有辦法實(shí)現(xiàn)。通過(guò) sprite 0,放置一個(gè) sprite 到需要的地方,產(chǎn)生 hit 之后,CPU 再修改 PPUSCROLL,就能達(dá)到屏幕分割的效果了

4. 時(shí)序

這個(gè)圖前面 2 章看過(guò)好幾遍了


tim

相對(duì)于 background,sprite 時(shí)序簡(jiǎn)單一些

sprite 只在 pre-render line 和 visible line 求值,在 visible line 與 background 一起渲染

PPU 內(nèi)部有一塊 Secondary OAM 內(nèi)存,大小為 4 * 8 = 32 bytes,用來(lái)存儲(chǔ)當(dāng)前掃描線上的 8 個(gè) sprite,這也從側(cè)面說(shuō)明:PPU 最多支持 8 個(gè) sprite 在一條 scanline 上

  • Scanline 的 1 - 64 周期,會(huì)清空 Secondary OAM,將 Secondary OAM 的值全寫(xiě)為 0xFF
  • Scanline 的 65 - 256 周期,對(duì)下一條 scanline 的 Secondary OAM 求值,遍歷 OAM 內(nèi)存,將符合對(duì)應(yīng) scanline 的 OAM 寫(xiě)入 Secondary OAM
  • Scanline 的 257 - 320 周期,通過(guò) Secondary OAM,計(jì)算對(duì)應(yīng) scanline 的 OAM 像素值,用于之后的渲染

詳細(xì)操作參考:http://wiki.nesdev.com/w/index.php/PPU_sprite_evaluation

5. 總結(jié)

至此 PPU 的原理已經(jīng)全過(guò)了一遍了,sprite 內(nèi)存數(shù)據(jù)和顯示這里也就不展示了,和 09 章對(duì) background 數(shù)據(jù)舉例的流程相同。另外代碼也就不舉例了,PPU 相比 CPU 確實(shí)復(fù)雜太多,沒(méi)法切分為小段代碼。最好結(jié)合 fceux 強(qiáng)大的調(diào)試功能,結(jié)合源碼查看

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容