敌人子弹如何瞄准C64上的玩家

3
我正在浏览互联网和旧的C64书籍,寻找问题的答案,但最终我只能在这里发帖。我喜欢C64编码的好旧时光,虽然我目前没有在这个平台上编写游戏,但我想知道在那个时代如何克服一些硬件限制。
在所有现代游戏编程书籍和教程中,找到将敌人子弹发射方向调整到玩家的正确方向的方法是使用浮点数的向量数学,就像这个伪代码一样:
bullet_velocity = (player.position - bullet.position).normalize();

现在考虑到C64的限制,我在源代码中看到了大量使用正弦表来提高速度,也许我分心了,但是在阅读旧的C64书籍或C64程序员的注释程序时,从未见过有关向量数学的字眼,我想知道当时是如何实现相同的目标的。
请回答,我有成千上万个类似的疑问,但是回答这个问题也许可以为我找到其他问题的答案!:-)
编辑:只是一个注释:有子弹瞄准玩家的C64游戏示例包括Silkworm和Cybernoid。

这让我想起了往事...据我回忆,大多数C64游戏都通过严格限制所有动作在水平或垂直方向上发生来解决这个问题,因此使整个问题都失去了意义。 - Sam Varshavchik
1
@1Garrett2010:你能给一个具体的游戏/关卡的例子吗?因为不同的游戏可能会有不同的做法。我认为很多旧游戏只允许敌人以固定速度在水平/垂直方向上直线射击。 - Michael
1
同时,目标并不是点大小的,因此角度上的一些不准确仍然会给您一个命中,并且屏幕的分辨率也不是很高。使用较小的查找表应该可以完成定点数学运算。但这只是我的猜测 :) - Jester
@Michael:嗨,游戏《蚕》中的第一关BOSS(不仅仅是他们)会向玩家开火,还有《赛博诺伊德》中的一些敌人也会。但我认为这个游戏列表可以更广泛。 - 1Garrett2010
3
我不确定,但他们肯定可以使用Bresenham算法或ROM中的浮点Basic例程。 - Nick Westgate
2个回答

5
假设你对输出方向数量有足够小的要求,那么最简单的方法是通过查找表来实现。例如,对于64个输出方向,从源到目的地获取向量(x, y),如果两者都为正,则将它们左移直到其中一个填充符号位,然后从每个向量的前两位中形成一个四位表索引,并查找输出向量。
假设您在160x200,则需要在进入之前放弃一些精度。
适当镜像以处理其他象限。假设对象位置采用8.8固定点表示,子弹速度为1,则需要32字节的查找表。
因此,朴素的实现如下:
xPosYPos:

    ; assuming ($00) has the positive x difference
    ; and ($01) has the positive y difference

    lda $00
    ora $01

shiftLoop:
    asl $00
    asl $01
    asl a
    bpl shiftLoop

    ; load A with table index
    lda #$00
    rol $00
    rol a
    rol $00
    rol a
    rol $01
    rol a
    rol $01
    rol a

    ; look up vector
    tax
    lda xVec, x
    ; ... put somewhere ...
    lda yVec, x
    ; ... put somewhere ...

使用更智能的解决方案,可能涉及类似以下内容:

    lda $00
    ora $01

    asl a
    bmi shift1
    asl a
    bmi shift2
    ... etc ...

shift1:

... etc, but you can shift directly to form
the table index rather than going to all the work
of shifting the vector then piecing together from
the top bits ...

或者您可以创建一个256字节的查找表,根据x|y(它们都是正数,最多为127)查找例程地址,并直接跳转到移位而不用计算位数。
关于对象位置和定点数的基础知识:
假设您在160x200模式下,则可以将对象位置的每个组件存储为单个字节。因此,x有一个字节,y也有一个字节。许多游戏所做的是将每个位置存储为两个字节。因此,x和y总共有四个字节。
其中一个字节与单字节格式相同-它是整数位置。另一个是小数部分。因此,如果您有位置和速度(并进行欧拉积分),则会将速度与位置进行16位加法运算。然后,您只需使用顶部字节即可得到位置。
这通常称为定点算术。与浮点数不同,小数点的位置在整数内是固定的。在此处描述的方案中,它始终为8位。
因此,例如,仅使用字节量添加偏移量:
clc
lda xPosition
adc xVelocity
sta xPosition

sta SomeHardwareRegisterForSpritePosition

要使用固定点方案添加偏移量:

clc

lda xFractionalPosition
adc xFractionalVelocity
sta xFractionalPosition

lda xPosition
adc xVelocity
sta xPosition

sta SomeHardwareRegisterForSpritePosition

优点在于您的速度向量现在可以在任何方向上小至1/256个像素。例如,您可以存储一个速度向量,表示每帧子弹将向左移动一个像素和向下移动32/256个像素。使用亚像素精度来移动子弹只需要额外几个字节的存储空间和额外几个ADC。
通过上述建议,您可以通过从一个字节中减去另一个字节来获得源到目标的向量。结果将是两个单独的字节,两者都将是输出的小数部分。例如,您可能决定发射一个速度向量为(87/256, 239/256)即20度角的子弹。

谢谢,我有一些疑问:1)对于“从源到目的地获取向量(x,y)”,您是指获取子弹起始和结束位置向量吗?如果应该从源到目的地计算向量,则涉及浮点数运算,我看不到优势!2)在C64源代码中,通常可以看到查找表(例如正弦表)中已经计算出要移动对象/精灵的位置(x,y)。这里您打算存储单个浮点数对值(x,y),以添加到每个移动步骤的子弹精灵中?因此,在C64游戏编程中,“允许”进行一些浮点计算吗? - 1Garrett2010
不需要浮点数。如果位置是整数,则它们之间的向量也是整数。但对于不仅仅是左、右、上、下或对角线运动的情况,通常会使用定点数——例如,一个字节用于 x 的整数部分,另一个字节用于小数部分。让进位从一个字节流到另一个字节。扩展我的答案以涵盖定点数是否有帮助?这很难放在评论中。 - Tommy
扩展回答,涉及固定点将非常有帮助。谢谢! - 1Garrett2010

1
您还可以滥用 Bresenham 的线条绘制算法来瞄准子弹,将“线”的起始坐标设置为敌人的坐标,结束坐标设置为您的飞船的坐标。不过,与其向线条添加像素,您只需在每次迭代中重新绘制子弹的当前位置即可。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接