我正在使用 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 中,信号直到进程结束才会更新其值,并将更新到最后一条语句。意思是如果你把它变成负数然后加到它上面,你只会得到加法。
我希望在指南和教程中更多地强调这一点,因为这会花费我大量的时间。