環形彈幕如何瞄準玩家
Elly 的攻擊請求一個初始數量為 16 的自機狙環形彈幕。輔助函式計算每顆子彈的方向。同一圈彈幕既可以朝向固定方向,也可以轉向玩家。
使用REA查詢TH04如何建立一個子彈環並將其瞄準玩家。 解釋計算並顯示程式碼。
一個示例提示,原始可執行檔案和重建簽出可在本地使用。
TH04 用 256 個角度單位表示順時針轉一整圈。16 顆子彈之間各隔 16 個單位:0, 16, 32, …, 240。再加上玩家方向,就能讓所有方向一起旋轉。
還原原始碼中的發射模板
bullet_template.angle = 0;
bullet_template.group = BG_RING_AIMED;
bullet_template.count = 16;
bullet_template_tune();
這些線在難度和效能調整之前選擇模式。 閱讀攻擊的來源.
REA 返回什麼
我們檢查設定每顆子彈速度和角度的函式 bullet_velocity_and_angle_set()。REA 返回指令、虛擬碼和引用,其中三部分解釋了這種彈幕如何生成。
-
在閱讀其程式碼之前檢查DOS映像
程式設計助手→ REAopen_binary {"path": "MAIN.EXE"} inspect_native_load_image {}REA →程式設計助手 · 選定欄位status: "verified" format: "dos-mz" load_segment: 4096 header_bytes: 6144 module_bytes: 150114這項檢查確認載入的程式碼和重定位記錄對應提供的可執行檔案。上方路徑為便於顯示而縮短。
-
檢視環形彈幕的乘除計算
程式設計助手→ REA ·analyze_function{"procedure": "0x2cfc8"}REA →程式設計助手·指令摘錄0x2d0eb: MOV AX, SI 0x2d0ed: SHL AX, 0x8 0x2d0f0: MOV DL, byte ptr [0x53af] 0x2d0f9: IDIV BXSI儲存子彈索引。移位將它乘以 256,再除以從DS:53AF讀取的數量。重建原始碼把這個欄位命名為bullet_template.count。完整指令序列見下文。 -
找到模式轉向玩家的位置
相同的函式結果 · 目標摘錄0x2d190: SUB AX, word ptr [0x53a6] 0x2d198: SUB AX, word ptr [0x53a4] 0x2d19d: CALLF 0x1000:1da8 0x2d1a2: ADD word ptr [BP + -0x2], AX對應維護的C++group_angle_offset += iatan2( player_pos.cur.y - bullet_template.origin.y, player_pos.cur.x - bullet_template.origin.x );指令運算元顯示了兩個座標差,以及一個計算方向的輔助函式。它的返回值加到相對環形角度上。原始碼名稱讓這些地址的含義更易讀;REA 則幫助助手核對地址背後的原始操作。
從彙編到可讀C++
點選每一步,對照原始指令和角度計算。這份摘要展示自機狙環形彈幕的路徑;完整原始碼還處理散射、疊層和隨機模式。
0x2cfcf: MOV SI, word ptr [BP + 0x4]
0x2d0eb: MOV AX, SI
0x2d0ed: SHL AX, 0x8
0x2d0f0: MOV DL, byte ptr [0x53af]
0x2d0f4: MOV DH, 0x0
0x2d0f6: PUSH DX
0x2d0f7: CWD
0x2d0f8: POP BX
0x2d0f9: IDIV BX
0x2d0fb: MOV word ptr [BP + -0x2], AX
0x2d18d: MOV AX, [0x4650]
0x2d190: SUB AX, word ptr [0x53a6]
0x2d194: PUSH AX
0x2d195: MOV AX, [0x464e]
0x2d198: SUB AX, word ptr [0x53a4]
0x2d19c: PUSH AX
0x2d19d: CALLF 0x1000:1da8
0x2d1a2: ADD word ptr [BP + -0x2], AX
0x2d1b9: MOV AL, byte ptr [BP + -0x2]
0x2d1bc: ADD AL, byte ptr [0x53ad]
0x2d1c0: MOV [0xbcc8], AL
unsigned char aimed_ring_angle(
int index, int count,
int player_direction,
unsigned char rotation)
{
int angle = index;
angle = (angle * 256) / count;
angle += player_direction;
angle += rotation;
return (unsigned char)angle;
}
01 · 讀取子彈索引。 棧引數載入到 SI。在 16 顆子彈的環中,索引從 0 到 15。
02 · 在整圈中等分方向。 SHL AX,8 將索引乘以 256。數量先零擴充套件為一個字,再由 IDIV 用乘積除以數量。數量為 16 時,每個索引前進 16 個角度單位。
03 · 加上玩家方向。 兩次減法得到玩家座標與原點座標的差。方向輔助函式在 AX 中返回角度;加上這個值,就能把整圈彈幕轉向玩家。
04 · 加上模板旋轉角。 ADD AL,[0x53ad] 加上模板中的角度。Elly 的初始模板將它設為零;其他彈幕可以用同一個輔助函式進行旋轉。
05 · 用一個位元組儲存最終角度。 儲存指令把 AL 寫入子彈生成角度欄位。值超過 255 後迴繞,與無符號位元組轉換一致。周圍原始碼還會根據這個角度和速率計算速度分量。
彙編節選展示自機狙環形分支、瞄準計算和最終儲存。上面的 C++ 是便於閱讀的摘要。下方的編譯器檢查針對連結專案中維護的原始碼。
閱讀維護的環和瞄準源
angle_offset = (i * BULLET_ANGLE_FULL_TURN) / count;
if (i >= (count - 1)) {
group_complete = true;
}
goto aim_or_no_aim;
aim:
group_angle_offset += iatan2(
(player_pos.cur.y - bullet_template.origin.y),
(player_pos.cur.x - bullet_template.origin.x)
);
no_aim:
vector2_near(
bullet_template.velocity,
(group_angle_offset + bullet_template.angle),
speed
);
bullet_spawn_angle = (group_angle_offset + bullet_template.angle);
檢查維護的源
TH04 專案記錄的檢查使用指定的 Turbo C++ 4.0J 工具鏈,編譯包含這個輔助函式的彈幕生成程式碼。
2,139匹配位元組
編譯的子彈生成程式碼與原始可執行檔案的2,139位元組部分相匹配。
兩個乾淨的構建
兩個單獨的構建產生相同的匹配位元組。 記錄的輸入雜湊值與此處顯示的維護源匹配。
2026年10月3日錄得。 閱讀編譯器檢查.
目標身份和分析範圍
| 原始目標 | MAIN.EXE,156,258位元組,DOS MZ |
|---|---|
| 傭工入境 | 0x2cfc8 在REA的載入影象 |
| 原始檔案偏移量 |
0x1e7c8,返回 address_to_file_offset
|
| 記錄程式碼檢查 | REA4.1.0*2026年10月7日 |
編譯器檢查覆蓋完整的彈幕新增程式碼段,包括這個輔助函式。檢查還比較它在 map 檔案中的貢獻,以及重疊的重定位項。
REA 驗證了載入段為 0x1000 的靜態 DOS 載入映像。地址描述的是分析映像。函式結果在一個 517 位元組範圍內報告了兩段程式碼,共 503 位元組;上方節選的指令都位於這些已報告的程式碼範圍內。
原始碼名稱和編譯器檢查來自 TH04 檢查點 0d72e980。較早的重建分析使用專案自身的指令碼;這個頁面新增了用 REA 對同一原始目標的檢查。遊戲檔案的身份由儲存庫清單確定,獨立原始轉儲的來源仍未確定。
圖中說明的是角度計算。完整遊戲的控制、時序和 PC-98 硬體行為,在重建專案中有各自的執行時檢查。
完整的TH04重建
專案用維護中的 C++ 和彙編構建遊戲的四個 DOS 程式。除了彈幕生成,原始碼還覆蓋關卡、Boss、渲染、輸入、選單、結局和分數處理。
- 子彈生成源完整的助手,其他模式型別和模板調整。
- DOS構建和執行指南構建原始碼並在專案的PC-98模擬器工作流中進行測試。
- 當前專案狀態DOS驗證和單獨的原生x64移植版。