我正在使用VHDL在FPGA上创建Pong。我一直绞尽脑汁试图弄清楚如何做,以及矩形矩形碰撞的最佳解决方案是什么,我想我已经想出了最好的解决方案,尽管似乎有一个错误(如下所述)。
我接受了来自larsbutler's answer的建议,并使用此策略进行碰撞:
- 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)。物体从它们的中心定位。这是一个图表:
这是我想要回复的基本图表: (source)
此刻我正试图解决x碰撞问题。问题在于使用xPosition代码将球从球拍中取出以避免卡住。似乎if xVelocity < 0 then
无法正确评估。假设球从左向右行进(xVelocity> 0)然后我们击中右侧的桨。 xVelocity将符号改为负(xVelocity <0)。问题if语句应该求值为true并递减xPosition以使其脱离paddle。这不会发生,而是跳过划桨,只是来回重复。我们添加或减去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;
-- 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中,信号在进程结束前不会更新其值,并将更新到最后一个语句。这意味着如果你把它变成负数然后添加到它上面,你只会得到补充。
我希望在指南和教程中更多地强调这一点,因为这花费了我很多时间。
答案 0 :(得分: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
(记录)和xCollision
(布尔)创建数据类型?
进行下一步并将其作为函数或过程包装:
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编程(设计硬件)与软件编码不同的人绝对是正确的 - 在极限范围内 - 但是当他们建议削弱你创造干净,易懂的代码的能力时,他们会说它做了什么,做了什么呢?说。
硬件设计的不同之处在于:
答案 1 :(得分:0)
我查看了你的代码,但没有详细说明。我会建议你在实践中击中左墙或击中右球没有区别。我的意思是当它撞到左墙时,它会用减号改变方向。它与右墙相似。所以最好将两个不同的if子句与“或”结合起来。这将简化您的代码。
试试这个,希望它能奏效。
编辑:我用fpga做同样的游戏,但它运行正常。