1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at f17b4db2cd1b232c0843571a8a4e9fcaf49edba5 362 lines 17 kB view raw
1-- ======================================================================== 2-- Physics Engine 3-- Runs once per frame on vert_sync rising edge (~60Hz). 4-- Handles gravity, keyboard input, friction, bouncing off walls/floor/ 5-- ceiling/obstacles, and squish animation. 6-- 7-- Outputs character position and animated dimensions for the renderer. 8-- All velocity math is 10-bit 2's complement (bit 9 = sign). 9-- ======================================================================== 10 11library IEEE; 12use IEEE.STD_LOGIC_1164.all; 13use IEEE.STD_LOGIC_ARITH.all; 14use IEEE.STD_LOGIC_UNSIGNED.all; 15 16entity physics_engine is 17 port( 18 vert_sync : in std_logic; -- frame clock (~60Hz) 19 key_w : in std_logic; -- from ps2_decoder 20 key_a : in std_logic; 21 key_s : in std_logic; 22 key_d : in std_logic; 23 char_x : out std_logic_vector(9 downto 0); -- character center X 24 char_y : out std_logic_vector(9 downto 0); -- character center Y 25 char_width : out std_logic_vector(9 downto 0); -- animated half-width 26 char_height : out std_logic_vector(9 downto 0) -- animated half-height 27 ); 28end physics_engine; 29 30architecture behavior of physics_engine is 31 32 -- Character state 33 signal pos_x : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(100, 10); 34 signal pos_y : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(200, 10); 35 signal vel_x : std_logic_vector(9 downto 0) := (others => '0'); 36 signal vel_y : std_logic_vector(9 downto 0) := (others => '0'); 37 38 constant SIZE : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(7, 10); 39 40 -- Tuning constants 41 constant GRAVITY : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(1, 10); 42 constant IMPULSE : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(3, 10); 43 constant JUMP_FORCE : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(13, 10); 44 constant MAX_VEL_X : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(32, 10); 45 46 -- Screen bounds 47 constant GROUND : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(440, 10); 48 constant CEILING : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(16, 10); 49 constant LEFT_WALL : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(8, 10); 50 constant RIGHT_WALL: std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(631, 10); 51 52 -- Obstacle positions (must match renderer) 53 constant O1_L : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(60, 10); 54 constant O1_T : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(370, 10); 55 constant O1_R : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(180, 10); 56 constant O1_B : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(386, 10); 57 58 constant O2_L : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(250, 10); 59 constant O2_T : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(300, 10); 60 constant O2_R : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(390, 10); 61 constant O2_B : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(316, 10); 62 63 constant O3_L : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(440, 10); 64 constant O3_T : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(200, 10); 65 constant O3_R : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(580, 10); 66 constant O3_B : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(216, 10); 67 68 constant O4_L : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(140, 10); 69 constant O4_T : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(120, 10); 70 constant O4_R : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(200, 10); 71 constant O4_B : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(150, 10); 72 73 -- Animation state 74 signal squish : std_logic_vector(3 downto 0) := (others => '0'); 75 signal squish_h : std_logic := '0'; -- '0'=vertical squish, '1'=horizontal 76 signal on_ground : std_logic := '0'; 77 signal jump_pressed : std_logic := '0'; 78 79begin 80 81 -- Output character position 82 char_x <= pos_x; 83 char_y <= pos_y; 84 85 -- Squish deforms the character: vertical hit = wider+shorter, wall hit = taller+narrower 86 char_width <= SIZE + ("000000" & squish) when squish_h = '0' 87 else SIZE - ("000000" & squish(3 downto 1)); 88 char_height <= SIZE - ("000000" & squish(3 downto 1)) when squish_h = '0' 89 else SIZE + ("000000" & squish); 90 91 -- Main physics process: one tick per frame 92 physics : process 93 variable vx, vy : std_logic_vector(9 downto 0); 94 variable px, py : std_logic_vector(9 downto 0); 95 variable bounced : std_logic; 96 variable bounce_wall : std_logic; 97 variable bounce_speed : std_logic_vector(9 downto 0); 98 variable grounded : std_logic; 99 variable c_left, c_right, c_top, c_bot : std_logic_vector(9 downto 0); 100 variable overlap_x, overlap_y : std_logic_vector(9 downto 0); 101 begin 102 wait until vert_sync'event and vert_sync = '1'; 103 104 vx := vel_x; 105 vy := vel_y; 106 bounced := '0'; 107 bounce_wall := '0'; 108 bounce_speed := (others => '0'); 109 grounded := '0'; 110 111 -- == INPUT == 112 113 if key_w = '0' then 114 jump_pressed <= '0'; 115 end if; 116 117 -- Jump on ground (single impulse) 118 if key_w = '1' and jump_pressed = '0' and on_ground = '1' then 119 vy := (others => '0'); 120 vy := vy - JUMP_FORCE; 121 jump_pressed <= '1'; 122 end if; 123 124 -- Bounce boost: holding W adds energy each ground contact 125 if key_w = '1' and jump_pressed = '1' and on_ground = '1' then 126 vy := vy - 4; 127 end if; 128 129 -- Slam down (air only) 130 if key_s = '1' and on_ground = '0' then 131 vy := vy + IMPULSE; 132 end if; 133 134 -- Horizontal: full on ground, reduced in air 135 if key_a = '1' then 136 if on_ground = '1' then vx := vx - IMPULSE; 137 else vx := vx - 2; end if; 138 end if; 139 if key_d = '1' then 140 if on_ground = '1' then vx := vx + IMPULSE; 141 else vx := vx + 2; end if; 142 end if; 143 144 -- == GRAVITY == 145 vy := vy + GRAVITY; 146 147 -- == FRICTION: vel -= vel/4, min 1 == 148 if vx(9) = '0' then 149 if vx > 0 then 150 if vx(9 downto 2) = "00000000" then vx := vx - 1; 151 else vx := vx - ("000" & vx(9 downto 3)); end if; 152 end if; 153 else 154 if vx /= "0000000000" then 155 if vx(9 downto 2) = "11111111" then vx := vx + 1; 156 else vx := vx - ("11" & vx(9 downto 2)); end if; 157 end if; 158 end if; 159 160 -- == CLAMP == 161 if vx(9) = '0' and vx > MAX_VEL_X then vx := MAX_VEL_X; end if; 162 if vx(9) = '1' and vx < (not MAX_VEL_X) + 1 then vx := (not MAX_VEL_X) + 1; end if; 163 if vy(9) = '0' and vy > 63 then vy := CONV_STD_LOGIC_VECTOR(63, 10); end if; 164 if vy(9) = '1' and vy < CONV_STD_LOGIC_VECTOR(960, 10) then vy := CONV_STD_LOGIC_VECTOR(960, 10); end if; 165 166 -- == MOVE == 167 px := pos_x + vx; 168 py := pos_y + vy; 169 170 -- == GROUND == 171 if py >= GROUND then 172 py := GROUND; 173 bounced := '1'; grounded := '1'; 174 bounce_speed := vy; 175 vy := (not vy) + 1; 176 if vy(9) = '1' then 177 vy := vy + ("000" & ((not vy(9 downto 3)) + 1)); 178 end if; 179 if vy(9) = '1' and vy >= CONV_STD_LOGIC_VECTOR(1022, 10) then vy := (others => '0'); 180 elsif vy(9) = '0' then vy := (others => '0'); end if; 181 end if; 182 183 -- == CEILING == 184 if py(9) = '1' or py <= CEILING then 185 py := CEILING; 186 bounced := '1'; 187 bounce_speed := (not vy) + 1; 188 vy := (not vy) + 1; 189 if vy(9) = '0' and vy > 1 then 190 vy := vy - ("000" & vy(9 downto 3)); 191 end if; 192 end if; 193 194 -- == LEFT WALL == 195 if px(9) = '1' or px <= LEFT_WALL then 196 px := LEFT_WALL; 197 bounced := '1'; bounce_wall := '1'; 198 bounce_speed := (not vx) + 1; 199 vx := (not vx) + 1; 200 if vx(9) = '0' and vx > 1 then 201 vx := vx - ("000" & vx(9 downto 3)); 202 end if; 203 end if; 204 205 -- == RIGHT WALL == 206 if px >= RIGHT_WALL then 207 px := RIGHT_WALL; 208 bounced := '1'; bounce_wall := '1'; 209 bounce_speed := vx; 210 vx := (not vx) + 1; 211 -- vx is now negative: reduce magnitude toward zero 212 if vx(9) = '1' then 213 vx := vx + ("000" & ((not vx(9 downto 3)) + 1)); 214 end if; 215 -- Kill tiny bounces 216 if vx(9) = '1' and vx >= CONV_STD_LOGIC_VECTOR(1022, 10) then vx := (others => '0'); end if; 217 end if; 218 219 -- == OBSTACLE COLLISIONS == 220 -- Pattern: AABB overlap -> resolve on shallower axis -> bounce 221 222 -- Obstacle 1 223 c_left := px - SIZE; c_right := px + SIZE; 224 c_top := py - SIZE; c_bot := py + SIZE; 225 if (c_right >= O1_L) and (c_left <= O1_R) and 226 (c_bot >= O1_T) and (c_top <= O1_B) then 227 if vy(9) = '0' then overlap_y := c_bot - O1_T; 228 else overlap_y := O1_B - c_top; end if; 229 if vx(9) = '0' then overlap_x := c_right - O1_L; 230 else overlap_x := O1_R - c_left; end if; 231 if overlap_y <= overlap_x then 232 if vy(9) = '0' then py := O1_T - SIZE; grounded := '1'; 233 else py := O1_B + SIZE; end if; 234 bounced := '1'; 235 vy := (not vy) + 1; 236 if vy(9) = '0' and vy > 1 then vy := vy - ("000" & vy(9 downto 3)); 237 elsif vy(9) = '1' and vy < CONV_STD_LOGIC_VECTOR(1022, 10) then 238 vy := vy + ("000" & ((not vy(9 downto 3)) + 1)); end if; 239 if vy(9) = '0' and vy < 2 then vy := (others => '0'); end if; 240 if vy(9) = '1' and vy >= CONV_STD_LOGIC_VECTOR(1022, 10) then vy := (others => '0'); end if; 241 else 242 if vx(9) = '0' then px := O1_L - SIZE; 243 else px := O1_R + SIZE; end if; 244 bounced := '1'; bounce_wall := '1'; 245 vx := (not vx) + 1; 246 if vx(9) = '0' and vx > 1 then vx := vx - ("000" & vx(9 downto 3)); 247 elsif vx(9) = '1' and vx < CONV_STD_LOGIC_VECTOR(1022, 10) then 248 vx := vx + ("000" & ((not vx(9 downto 3)) + 1)); end if; 249 end if; 250 end if; 251 252 -- Obstacle 2 253 c_left := px - SIZE; c_right := px + SIZE; 254 c_top := py - SIZE; c_bot := py + SIZE; 255 if (c_right >= O2_L) and (c_left <= O2_R) and 256 (c_bot >= O2_T) and (c_top <= O2_B) then 257 if vy(9) = '0' then overlap_y := c_bot - O2_T; 258 else overlap_y := O2_B - c_top; end if; 259 if vx(9) = '0' then overlap_x := c_right - O2_L; 260 else overlap_x := O2_R - c_left; end if; 261 if overlap_y <= overlap_x then 262 if vy(9) = '0' then py := O2_T - SIZE; grounded := '1'; 263 else py := O2_B + SIZE; end if; 264 bounced := '1'; 265 vy := (not vy) + 1; 266 if vy(9) = '0' and vy > 1 then vy := vy - ("000" & vy(9 downto 3)); 267 elsif vy(9) = '1' and vy < CONV_STD_LOGIC_VECTOR(1022, 10) then 268 vy := vy + ("000" & ((not vy(9 downto 3)) + 1)); end if; 269 if vy(9) = '0' and vy < 2 then vy := (others => '0'); end if; 270 if vy(9) = '1' and vy >= CONV_STD_LOGIC_VECTOR(1022, 10) then vy := (others => '0'); end if; 271 else 272 if vx(9) = '0' then px := O2_L - SIZE; 273 else px := O2_R + SIZE; end if; 274 bounced := '1'; bounce_wall := '1'; 275 vx := (not vx) + 1; 276 if vx(9) = '0' and vx > 1 then vx := vx - ("000" & vx(9 downto 3)); 277 elsif vx(9) = '1' and vx < CONV_STD_LOGIC_VECTOR(1022, 10) then 278 vx := vx + ("000" & ((not vx(9 downto 3)) + 1)); end if; 279 end if; 280 end if; 281 282 -- Obstacle 3 283 c_left := px - SIZE; c_right := px + SIZE; 284 c_top := py - SIZE; c_bot := py + SIZE; 285 if (c_right >= O3_L) and (c_left <= O3_R) and 286 (c_bot >= O3_T) and (c_top <= O3_B) then 287 if vy(9) = '0' then overlap_y := c_bot - O3_T; 288 else overlap_y := O3_B - c_top; end if; 289 if vx(9) = '0' then overlap_x := c_right - O3_L; 290 else overlap_x := O3_R - c_left; end if; 291 if overlap_y <= overlap_x then 292 if vy(9) = '0' then py := O3_T - SIZE; grounded := '1'; 293 else py := O3_B + SIZE; end if; 294 bounced := '1'; 295 vy := (not vy) + 1; 296 if vy(9) = '0' and vy > 1 then vy := vy - ("000" & vy(9 downto 3)); 297 elsif vy(9) = '1' and vy < CONV_STD_LOGIC_VECTOR(1022, 10) then 298 vy := vy + ("000" & ((not vy(9 downto 3)) + 1)); end if; 299 if vy(9) = '0' and vy < 2 then vy := (others => '0'); end if; 300 if vy(9) = '1' and vy >= CONV_STD_LOGIC_VECTOR(1022, 10) then vy := (others => '0'); end if; 301 else 302 if vx(9) = '0' then px := O3_L - SIZE; 303 else px := O3_R + SIZE; end if; 304 bounced := '1'; bounce_wall := '1'; 305 vx := (not vx) + 1; 306 if vx(9) = '0' and vx > 1 then vx := vx - ("000" & vx(9 downto 3)); 307 elsif vx(9) = '1' and vx < CONV_STD_LOGIC_VECTOR(1022, 10) then 308 vx := vx + ("000" & ((not vx(9 downto 3)) + 1)); end if; 309 end if; 310 end if; 311 312 -- Obstacle 4 313 c_left := px - SIZE; c_right := px + SIZE; 314 c_top := py - SIZE; c_bot := py + SIZE; 315 if (c_right >= O4_L) and (c_left <= O4_R) and 316 (c_bot >= O4_T) and (c_top <= O4_B) then 317 if vy(9) = '0' then overlap_y := c_bot - O4_T; 318 else overlap_y := O4_B - c_top; end if; 319 if vx(9) = '0' then overlap_x := c_right - O4_L; 320 else overlap_x := O4_R - c_left; end if; 321 if overlap_y <= overlap_x then 322 if vy(9) = '0' then py := O4_T - SIZE; grounded := '1'; 323 else py := O4_B + SIZE; end if; 324 bounced := '1'; 325 vy := (not vy) + 1; 326 if vy(9) = '0' and vy > 1 then vy := vy - ("000" & vy(9 downto 3)); 327 elsif vy(9) = '1' and vy < CONV_STD_LOGIC_VECTOR(1022, 10) then 328 vy := vy + ("000" & ((not vy(9 downto 3)) + 1)); end if; 329 if vy(9) = '0' and vy < 2 then vy := (others => '0'); end if; 330 if vy(9) = '1' and vy >= CONV_STD_LOGIC_VECTOR(1022, 10) then vy := (others => '0'); end if; 331 else 332 if vx(9) = '0' then px := O4_L - SIZE; 333 else px := O4_R + SIZE; end if; 334 bounced := '1'; bounce_wall := '1'; 335 vx := (not vx) + 1; 336 if vx(9) = '0' and vx > 1 then vx := vx - ("000" & vx(9 downto 3)); 337 elsif vx(9) = '1' and vx < CONV_STD_LOGIC_VECTOR(1022, 10) then 338 vx := vx + ("000" & ((not vx(9 downto 3)) + 1)); end if; 339 end if; 340 end if; 341 342 -- == COMMIT == 343 vel_x <= vx; 344 vel_y <= vy; 345 pos_x <= px; 346 pos_y <= py; 347 348 -- Squish on meaningful impacts only 349 if bounced = '1' and bounce_speed > 3 then 350 if bounce_speed >= 8 then squish <= "1000"; 351 else squish <= bounce_speed(3 downto 0); end if; 352 squish_h <= bounce_wall; 353 elsif squish > 0 then 354 squish <= squish - 1; 355 end if; 356 357 if grounded = '1' then on_ground <= '1'; 358 elsif py < GROUND - 1 then on_ground <= '0'; end if; 359 360 end process physics; 361 362end behavior;