あーさん日記

https://akkera102.sakura.ne.jp/gbadev/ の中の人

練習2

あんまり賢くないシューティングゲームの弾の計算方法。まず、240x160x2テーブルを用意します。起点を239, 159として、テーブルの各要素は1.各座標までの距離、2.x単位ベクトル、3.y単位ベクトル、4.角度とします。要素はs32の10.22固定小数点、角度は0~255整数です。

あと角度テーブルで1.x単位ベクトル、2.y単位ベクトルも作ります。思いっきり容量使ってやってみようという方針でしたけど、結果は2.4MB程度となりました。ん-これはコレでありかもです。

# 補助にchatgpt先生使用
import struct
import math

FRAC_BITS = 22
SCALE     = 1 << FRAC_BITS
WIDTH     = 480
HEIGHT    = 320
CENTER_X  = 240 - 1
CENTER_Y  = 160 - 1

def float2fixed(flo):
    return int(flo * SCALE)

def fixed2float(fix):
    return fix / SCALE

def make_table():
    table = [[None for _ in range(WIDTH)] for _ in range(HEIGHT)]

    for y in range(HEIGHT):
        for x in range(WIDTH):
            dx = x - CENTER_X
            dy = y - CENTER_Y

            if dx == 0 and dy == 0:
                fx = 0
                fy = 0
                dist = 0
                angle = 0
            else:
                # 距離
                dist_float = math.sqrt(dx * dx + dy * dy)
                dist = float2fixed(dist_float)

                # 単位ベクトル
                fx = float2fixed(dx / dist_float)
                fy = float2fixed(dy / dist_float)

                # 角度(0〜255)
                angle_float = math.degrees(math.atan2(dy, dx))
                angle = int((angle_float + 360) * 256 / 360) % 256

            table[y][x] = {
                'fx': fx,
                'fy': fy,
                'dist': dist,
                'angle': angle,
            }

    return table

def verify_table(table):
    errors = 0
    maxstep = WIDTH*2

    for y in range(HEIGHT):
        for x in range(WIDTH):

            entry = table[y][x]
            fx = entry['fx']
            fy = entry['fy']

            # print(f"chk:({x},{y})")

            x_pos = x << FRAC_BITS
            y_pos = y << FRAC_BITS

            for step in range(maxstep):
                x_pos -= fx
                y_pos -= fy

                # print(f"org:({CENTER_X},{CENTER_Y}) pos:({(x_pos >> FRAC_BITS)},{(y_pos >> FRAC_BITS)})")

                # 到達判定(±0.5ピクセル以内)
                if abs(x_pos - (CENTER_X << FRAC_BITS)) < (1 << (FRAC_BITS - 1)) and \
                   abs(y_pos - (CENTER_Y << FRAC_BITS)) < (1 << (FRAC_BITS - 1)):
                    # print(f"Hit")
                    break
            else:
                errors += 1
                print(f"Failed to reach origin from ({x},{y})")

    print(f"\n検証完了。誤差数: {errors}")


def save_table_array(table, filename="bullet2.h", var_name="tblMath"):
    with open(filename, "w") as f:
        f.write(f"// fx(10.22), fy(10.22), dist(10.22), angle(32)\n\n")

        # fx
        f.write(f"ROM_DATA u32 tbl_fx[320 * 480] = {{\n")

        for y, row in enumerate(table):
            for x, cell in enumerate(row):
                fx = cell['fx'] & 0xFFFFFFFF
                f.write(f"    0x{fx:08X},\n")
        f.write("};\n\n")

        # fy
        f.write(f"ROM_DATA u32 tbl_fy[320 * 480] = {{\n")
        for y, row in enumerate(table):
            for x, cell in enumerate(row):
                fy = cell['fy'] & 0xFFFFFFFF
                f.write(f"    0x{fy:08X},\n")
        f.write("};\n\n")

        # dist
        f.write(f"ROM_DATA u32 tbl_dist[320 * 480] = {{\n")

        for y, row in enumerate(table):
            for x, cell in enumerate(row):
                dist  = cell['dist'] & 0xFFFFFFFF

                f.write(f"    0x{dist:08X},\n")
        f.write("};\n\n")

        # angle
        f.write(f"ROM_DATA u32 tbl_angle[320 * 480] = {{\n")

        for y, row in enumerate(table):
            for x, cell in enumerate(row):
                angle = cell['angle'] & 0xFF

                f.write(f"    0x{angle:02X},\n")
        f.write("};\n\n")

        # 角度 → 単位ベクトルのテーブルを作成(0〜255)
        f.write(f"ROM_DATA u32 tbl_vxy[256*2] = {{\n")

        for angle in range(256):
            radians = math.radians(angle * 360 / 256)
            fx = float2fixed(math.cos(radians))
            fy = float2fixed(math.sin(radians))
            fx = fx & 0xFFFFFFFF
            fy = fy & 0xFFFFFFFF
            f.write(f"    0x{fx:08X},\n")
            f.write(f"    0x{fy:08X},\n")

        f.write("};\n\n")


if __name__ == "__main__":
    table = make_table()
    verify_table(table)

    save_table_array(table)