2

我正在使用 VHDL 在 FPGA 上创建 Pong。几天来,我一直在绞尽脑汁想弄清楚如何做以及矩形矩形碰撞的最佳解决方案是什么,我想我已经想出了最好的解决方案,尽管似乎有一个错误(如下所述)。

我从larsbutler 的回答中得到了建议,并使用这种策略来进行碰撞:

  • object.positionX += object.velocityX
  • 检查/响应碰撞
  • object.positionY += object.velocityY
  • 检查/响应碰撞

此伪代码解释了我如何检查/响应冲突:

// right edge of ball in between objects left and right edge
// OR
// left edge of ball in between objects left and right edge
if((ball.right >= object.left && ball.right <= ball.right) || (ball.left >= object.left && ball.left <= object.right))
{
    xCollision = true;
}

// top edge of ball in between objects top and bottom edge
// OR
// bottom edge of ball in between objects top and bottom edge
if((ball.top >= object.top && ball.top <= object.bottom) || (ball.bottom <= object.bottom && ball.bottom >= object.top))
{
    yCollision = true;
}

// respond to collision
if xCollision and yCollision then
{
    // This code block is respective to each x or y update in order to resolve collision
}

请记住,屏幕的左上角是 (0, 0)。对象从它们的中心定位。这是一个图表:

乒乓图设置

这是我希望响应的基本图表:(来源)

碰撞响应图

问题:

目前我只是在尝试处理 x 碰撞。问题在于 xPosition 代码将球从桨中取出以避免卡住。好像if xVelocity < 0 then评价不对。假设球从左向右移动(xVelocity > 0),然后我们击中右侧的球拍。xVelocity 将符号变为负数(xVelocity < 0)。if 语句的问题应该评估为真并减少 xPosition 以使其脱离桨。这不会发生,而是跳过桨,只是来回重复。我们添加或减去 40 的原因是为了测试,实际上是它在桨内的数量。

我的许多实现似乎都陷入了 xVelocity 评估不正确的陷阱。如果您在 if else 中切换加号和减号,则该代码有效,但这在我看来没有任何逻辑意义。为什么它必须与我下面的相反?(请记住,在此之前 xVelocity 乘以 -1。

-- respond to collision
if xCollision = '1' and yCollision = '1' then

    -- Change direction
    xVelocity <= xVelocity * (-1);
    -- Add glancing y velocity of paddle
    yVelocity <= yVelocity + (collisionObjects(i)(5)/4);

    -- If bouncing in the left direction
    if xVelocity < 0 then
        -- move outwards as much as we are inside the paddle
        -- Should be negating from xPosition as we are bouncing left and want to resolve that way
        xPosition <= xPosition - 40;
    else
        xPosition <= xPosition + 40;
    end if;
end if;

完整代码:(VHDL)

-- Ball collision is using discrete collision! 
-- not sweep collision which helps with small fast objects passing through each other


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.std_logic_unsigned.all;

--note this line.The package is compiled to this directory by default.
--so don't forget to include this directory. 
library work;
--this line also is must.This includes the particular package into your program.
use work.my_package.all;

entity Ball is
    generic(
        numCollisionObjects: integer := 2;
        ballRadius : integer := 10;
        rgbColor : std_logic_vector(7 downto 0) := "111" & "111" & "11"
    );
    port(
        reset: in std_logic;
        clk: in std_logic;
        hCounter: in std_logic_vector(9 downto 0);
        vCounter: in std_logic_vector(9 downto 0);
        colObject: out type_collisionObject;
        collisionObjects: in type_collisionObjectArray(0 to numCollisionObjects-1);

        pixelOn: out std_logic;
        rgbPixel: out std_logic_vector(7 downto 0) := rgbColor
    );
end Ball;

architecture Behavioral of Ball is
    signal xPosition : integer := 0;
    signal yPosition : integer := 0;

    signal xVelocity : integer := 0;
    signal yVelocity : integer := 0;

    signal pixelBuffer : std_logic;
    signal RGBBuffer : std_logic_vector(7 downto 0) := rgbColor;

    signal colObjectBuffer: type_collisionObject;
begin
    pixelOn <= pixelBuffer;
    rgbPixel <= RGBBuffer;

    colObjectBuffer <= (xPosition, yPosition, ballRadius * 2, ballRadius * 2, xVelocity, yVelocity);
    colObject <= colObjectBuffer;

    animation: process(clk)
        variable update_clk_count: natural := 0;
        variable update_clk_prescaler: natural := 10000000; -- 833333; -- Slow because of debuging... 50 Mhz / clk_prescaler = desired speed
        --variable i: natural := 1;
        variable xCollision: std_logic := '0';
        variable yCollision: std_logic := '0';

        variable colObject_lastState: type_collisionObject;
    begin

        if rising_edge(clk) then
            -- While reset is high then we reset the positions
            if reset = '1' then

                xPosition <= SCREEN_RESX/2;
                yPosition <= SCREEN_RESY/2;

                xVelocity <= 3;
                yVelocity <= 0;

            else
                if update_clk_count >= update_clk_prescaler then

                    colObject_lastState := colObjectBuffer;

                    -- if we are hitting the left wall
                    if (xPosition - ballRadius + xVelocity) <= 0 then
                        RGBBuffer <= rgbColor;

                        if xVelocity < 0 then
                            xVelocity <= xVelocity * (-1);
                        end if;
                    end if;

                    -- if we are hitting the right wall
                    if (xPosition + ballRadius + xVelocity) >= 640 then
                        RGBBuffer <= rgbColor;

                        if xVelocity > 0 then
                            xVelocity <= xVelocity * (-1);
                        end if;
                    end if;

                    -- if we are hitting the top wall
                    if (yPosition - ballRadius + yVelocity) <= 0 then
                        RGBBuffer <= rgbColor;

                        if yVelocity < 0 then
                            yVelocity <= yVelocity * (-1);
                        end if;
                    end if;

                    -- if we are hitting the bottom wall
                    if (yPosition + ballRadius + yVelocity) >= 480 then
                        RGBBuffer <= rgbColor;

                        if yVelocity > 0 then
                            yVelocity <= yVelocity * (-1);
                        end if;
                    end if;

                    -- Update x position
                    xPosition <= xPosition + xVelocity;

                    -- Check for collision after x updates
                    if not(xVelocity = 0) then
                        for i in collisionObjects'range loop
                            xCollision := '0';
                            yCollision := '0';

                            -- right edge of ball in between objects left and right edge
                            -- OR
                            -- left edge of ball in between objects left and right edge
                            if (xPosition + ballRadius >= collisionObjects(i)(0) - (collisionObjects(i)(2)/2) and xPosition + ballRadius <= collisionObjects(i)(0) + (collisionObjects(i)(2)/2)) 
                                    OR (xPosition - ballRadius >= collisionObjects(i)(0) - (collisionObjects(i)(2)/2) and xPosition - ballRadius <= collisionObjects(i)(0) + (collisionObjects(i)(2)/2)) then
                                xCollision := '1';
                            end if;

                            -- top edge of ball in between objects top and bottom edge
                            -- OR
                            -- bottom edge of ball in between objects top and bottom edge
                            if (yPosition - ballRadius >= collisionObjects(i)(1) - (collisionObjects(i)(3)/2) and yPosition - ballRadius <= collisionObjects(i)(1) + (collisionObjects(i)(3)/2)) 
                                    OR (yPosition + ballRadius >= collisionObjects(i)(1) - (collisionObjects(i)(3)/2) and yPosition + ballRadius <= collisionObjects(i)(1) + (collisionObjects(i)(3)/2)) then
                                yCollision := '1';
                            end if;

                            -- respond to collision
                            if xCollision = '1' and yCollision = '1' then

                                -- Change direction
                                xVelocity <= xVelocity * (-1);
                                -- Add glancing y velocity of paddle
                                yVelocity <= yVelocity + (collisionObjects(i)(5)/4);

                                -- If bouncing in the left direction
                                if xVelocity < 0 then
                                    -- move outwards as much as we are inside the paddle
                                    -- Should be negating from xPosition as we are bouncing left and want to resolve that way
                                    xPosition <= xPosition - 40;
                                else
                                    xPosition <= xPosition + 40;
                                end if;
                            end if;

                        end loop;
                    end if;


                    -- Update y position
                    yPosition <= yPosition + yVelocity;

                    -- Check for collision after y updates
                    if not(yVelocity = 0) then
                        for i in collisionObjects'range loop
                            xCollision := '0';
                            yCollision := '0';

                            -- right edge of ball in between objects left and right edge
                            -- OR
                            -- left edge of ball in between objects left and right edge
                            if (xPosition + ballRadius >= collisionObjects(i)(0) - (collisionObjects(i)(2)/2) and xPosition + ballRadius <= collisionObjects(i)(0) + (collisionObjects(i)(2)/2)) 
                                    OR (xPosition - ballRadius >= collisionObjects(i)(0) - (collisionObjects(i)(2)/2) and xPosition - ballRadius <= collisionObjects(i)(0) + (collisionObjects(i)(2)/2)) then
                                xCollision := '1';
                            end if;

                            -- top edge of ball in between objects top and bottom edge
                            -- OR
                            -- bottom edge of ball in between objects top and bottom edge
                            if (yPosition - ballRadius >= collisionObjects(i)(1) - (collisionObjects(i)(3)/2) and yPosition - ballRadius <= collisionObjects(i)(1) + (collisionObjects(i)(3)/2)) 
                                    OR (yPosition + ballRadius >= collisionObjects(i)(1) - (collisionObjects(i)(3)/2) and yPosition + ballRadius <= collisionObjects(i)(1) + (collisionObjects(i)(3)/2)) then
                                yCollision := '1';
                            end if;

                            -- respond to collision
                            if xCollision = '1' and yCollision = '1' then

                                yVelocity <= yVelocity * (-1);

                                -- If ball is moving in same direction the paddle is
                                if (yVelocity < 0 and collisionObjects(i)(5) < 0) 
                                        OR (yVelocity > 0 and collisionObjects(i)(5) > 0) then
                                    yVelocity <= yVelocity + (collisionObjects(i)(5)/2);
                                end if;
                            end if;

                        end loop;
                    end if;

                    update_clk_count := 0;
                end if;
            end if;

            update_clk_count := update_clk_count + 1;
        end if;
    end process;



    drawing: process(hCounter, vCounter)
    begin
        -- If within pixel bounds of bar
        if hCounter >= (xPosition - ballRadius) and hCounter <= (xPosition + ballRadius) and vCounter >= (yPosition - ballRadius) and vCounter <= (yPosition + ballRadius) then
            pixelBuffer <= '1';
        else
            pixelBuffer <= '0';
        end if;
    end process;

end Behavioral;

以及来自 my_package.vhd 的相关信息:

constant SCREEN_RESX: integer := 640;
constant SCREEN_RESY: integer := 480;

-- 0: position X
-- 1: position Y
-- 2: size X 
-- 3: size Y
-- 4: velocityX
-- 5: velocityY
type type_collisionObject is array (0 to 5) of integer; 
type type_collisionObjectArray is array(natural range <>) of type_collisionObject; 

更新

我的碰撞检测不是防弹的,也不是令我满意的工作,但我似乎确实找到了我的错误。我不知道,但在 VHDL 中,信号直到进程结束才会更新其值,并将更新到最后一条语句。意思是如果你把它变成负数然后加到它上面,你只会得到加法。

我希望在指南和教程中更多地强调这一点,因为这会花费我大量的时间。

4

2 回答 2

2

我没有详细检查你的每一行代码,但看起来你在从伪代码到 VHDL 的翻译中引入了许多错误机会。即使转码是完全正确的,追踪到/从伪码也比它需要的要困难得多......

假设您信任伪代码(我可以看到它存在一些问题:-)

为什么不翻译

if((ball.right >= object.left && ball.right <= ball.right) || (ball.left >= object.left && ball.left <= object.right))
{
    xCollision = true;
}

进入VHDL(纠正显而易见的)作为

if (ball.right >= object.left and ball.right <= object.right) 
   or (ball.left >= object.left and ball.left <= object.right) then
   xCollision := true;
end if;

或者干脆

xCollision := (ball.right >= object.left and ball.right <= object.right) 
   or (ball.left >= object.left and ball.left <= object.right);

ball并适当地为, object(records) 和xCollision(boolean)创建数据类型?

进行下一步并将其包装为函数或过程:

   function XCollision (ball, object : rect) return boolean is
   begin 
      return (ball.right >= object.left and ball.right <= ball.right) 
       or (ball.left >= object.left and ball.left <= object.right);
   end XCollision;

可调用为

   x_collision := false;
   for i in objects'range loop
      x_collision := x_collision or XCollision (ball, objects(i));
   end loop;

VHDL 是一种提供强大类型模型的高级语言 - 可能比编写伪代码的语言更好。以这种方式使用它。(您已经在使用包方面取得了良好的开端)。

您将更轻松地编写、分析和调试 VHDL。作为使用记录的替代方法,创建一个枚举并使用它来索引一个值数组。

type Sides is (left, right, top, bottom); 
type obj_type is array (sides) of integer;
variable ball, object : obj_type;

在这种情况下 XCollision 现在可以

   function XCollision (ball, object : rect) return boolean is
   begin 
      return (ball(right) >= object(left) and ...);
   end XCollision;

上面没有什么是根本不可合成的。

尽管我已经看到了 2010 年针对某些函数构建的 Synplicity 对象——它在某些方面大大落后于 XST——但奇怪的是,它对返回值的 OUT 参数的等效过程感到满意。

那些说 FPGA 编程(设计硬件)与软件编码不同的人是绝对正确的——在一定范围内——但几乎从来没有当他们建议削弱你创建干净、可理解的代码的能力时,这些代码说明了它做了什么,做了它所说的。

硬件设计的不同之处在于:

  • 硬件可行吗?具有已知边界的 FOR 循环是可行的(给定足够大的 FPGA) - 具有未知边界的 WHILE 循环会生成未知数量的硬件......不可行!指针(VHDL 访问类型)和“malloc”显然不存在了……
  • 硬件够小够快吗?给定足够多的对象,上面的“for”循环将溢出 FPGA 或生成如此长的逻辑链,以至于无法满足您的速度目标。然后,您必须将其转换为流水线设计(大而快)或状态机(小而快,但需要很多周期)才能实现您的目标。
  • 使用适当的数据类型。如果我需要一个 17 位的数字,我只需创建一个,并在 C 程序员可用的最佳可用大小(32 位)上节省很多门。(显然,有些软件语言允许这样做,但它们的用户仍然不是大多数)
  • 毫无疑问,其他人,但这些 IMO 是最重要的。
  • 重新评论;并行处理方面和使用信号进行可靠的进程间通信是最大的区别:而以上几点在单个进程中有效;信号和多进程与大多数其他编程范例有很大不同。
于 2013-07-07T09:35:30.850 回答
0

我已经查看了您的代码,但没有详细查看。我建议你在练习中打左墙或打右球没有区别。我的意思是当它撞到左边的墙上时,它会用一个减号改变它的方向。它与右墙相似。因此最好将两个不同的 if 子句与“或”结合起来。这将简化您的代码。

试试这个,希望它会工作。

编辑:我用 fpga 做了同样的游戏,它工作正常。

于 2013-07-08T13:08:15.180 回答