1
fork

Configure Feed

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

at d6fe6795c2b0abe93e0f3f5572b8b6c9c449a9fb 399 lines 16 kB view raw
1-- ======================================================================== 2-- Physics Engine (scrolling 1500x1500 world) 3-- Positions: 11-bit unsigned. Valid range: 0..1492 (fits in 11-bit fine). 4-- Velocities: 10-bit 2's complement, bit 9 = sign. 5-- 6-- Underflow detection: uses >= 2000 threshold instead of bit-10 sign check. 7-- Bit-10 was wrong because GROUND(1480) and RIGHT_WALL(1492) both have 8-- bit 10 set (values >= 1024), causing false ceiling/wall triggers. 9-- 10-- Obstacles imported from level package (use work.level.all). 11-- ======================================================================== 12 13library IEEE; 14use IEEE.STD_LOGIC_1164.all; 15use IEEE.STD_LOGIC_ARITH.all; 16use IEEE.STD_LOGIC_UNSIGNED.all; 17use work.level.all; 18 19entity physics_engine is 20 port( 21 vert_sync : in std_logic; 22 key_w : in std_logic; 23 key_a : in std_logic; 24 key_s : in std_logic; 25 key_d : in std_logic; 26 char_x : out std_logic_vector(10 downto 0); 27 char_y : out std_logic_vector(10 downto 0); 28 char_width : out std_logic_vector(9 downto 0); 29 char_height : out std_logic_vector(9 downto 0); 30 cam_x : out std_logic_vector(10 downto 0); 31 cam_y : out std_logic_vector(10 downto 0); 32 vel_out : out std_logic_vector(9 downto 0); 33 vel_x_out : out std_logic_vector(9 downto 0); 34 vel_y_out : out std_logic_vector(9 downto 0) 35 ); 36end physics_engine; 37 38architecture behavior of physics_engine is 39 40 -- Character state 41 signal pos_x : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(100, 11); 42 signal pos_y : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1400, 11); 43 signal vel_x : std_logic_vector(9 downto 0) := (others => '0'); 44 signal vel_y : std_logic_vector(9 downto 0) := (others => '0'); 45 signal cam_x_sig : std_logic_vector(10 downto 0) := (others => '0'); 46 signal cam_y_sig : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1020, 11); 47 48 -- SIZE: 10-bit for squish output math, 11-bit for position arithmetic 49 constant SIZE : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(7, 10); 50 constant SIZE11 : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(7, 11); 51 52 -- Physics tuning 53 constant GRAVITY : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(1, 10); 54 constant IMPULSE : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(3, 10); 55 constant SLAM_FORCE : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(8, 10); 56 constant JUMP_FORCE : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(13, 10); 57 constant MAX_VEL_X : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(32, 10); 58 59 -- World bounds (11-bit) 60 constant GROUND : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1480, 11); 61 constant CEILING : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(16, 11); 62 constant LEFT_WALL : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(8, 11); 63 constant RIGHT_WALL : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1492, 11); 64 65 -- Underflow sentinel: valid positions top out at 1480 (GROUND). 66 -- CEILING(16) - max_upward(128) = -112 -> 11-bit unsigned = 1936. 67 -- So UNDERFLOW must be <= 1936 and > 1480. 1500 gives safe margin. 68 constant UNDERFLOW : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1500, 11); 69 70 -- Tune: vertical speed (0-63) at which all 10 LEDs are fully lit. 71 -- Lower = more sensitive (fewer LEDs at low speed fill up faster). 72 -- Higher = better dynamic range across the full velocity range. 73 -- Higher = less sensitive (need a harder bounce to light all LEDs). 74 constant LED_FULL_VEL : integer := 48; 75 76 -- Animation 77 signal squish : std_logic_vector(3 downto 0) := (others => '0'); 78 signal squish_h : std_logic := '0'; 79 signal on_ground : std_logic := '0'; 80 signal jump_pressed : std_logic := '0'; 81 82 signal abs_vel_x : std_logic_vector(9 downto 0); 83 signal abs_vel_y : std_logic_vector(9 downto 0); 84 85begin 86 87 char_x <= pos_x; 88 char_y <= pos_y; 89 cam_x <= cam_x_sig; 90 cam_y <= cam_y_sig; 91 vel_x_out <= vel_x; 92 vel_y_out <= vel_y; 93 94 -- Absolute values of both velocity axes. 95 abs_vel_x <= (not vel_x) + 1 when vel_x(9) = '1' else vel_x; 96 abs_vel_y <= (not vel_y) + 1 when vel_y(9) = '1' else vel_y; 97 98 -- Thermometer-encode peak speed (max of |vx|,|vy|) → horizontal LED bar. 99 -- n LEDs lit from LSB up → 0000011111 for n=5, 1111111111 for n=10. 100 vel_bar : process(abs_vel_x, abs_vel_y) 101 variable vx, vy, vmax : integer; 102 variable n : integer range 0 to 10; 103 begin 104 vx := CONV_INTEGER(abs_vel_x); 105 vy := CONV_INTEGER(abs_vel_y); 106 if vx > vy then vmax := vx; else vmax := vy; end if; 107 if vmax >= LED_FULL_VEL then n := 10; 108 else n := vmax * 10 / LED_FULL_VEL; 109 end if; 110 case n is 111 when 0 => vel_out <= "0000000000"; 112 when 1 => vel_out <= "0000000001"; 113 when 2 => vel_out <= "0000000011"; 114 when 3 => vel_out <= "0000000111"; 115 when 4 => vel_out <= "0000001111"; 116 when 5 => vel_out <= "0000011111"; 117 when 6 => vel_out <= "0000111111"; 118 when 7 => vel_out <= "0001111111"; 119 when 8 => vel_out <= "0011111111"; 120 when 9 => vel_out <= "0111111111"; 121 when others => vel_out <= "1111111111"; 122 end case; 123 end process vel_bar; 124 125 char_width <= SIZE + ("000000" & squish) when squish_h = '0' 126 else SIZE - ("000000" & squish(3 downto 1)); 127 char_height <= SIZE - ("000000" & squish(3 downto 1)) when squish_h = '0' 128 else SIZE + ("000000" & squish); 129 130 physics : process 131 variable vx, vy : std_logic_vector(9 downto 0); 132 variable px, py : std_logic_vector(10 downto 0); 133 variable bounced : std_logic; 134 variable bounce_wall : std_logic; 135 variable bounce_speed : std_logic_vector(9 downto 0); 136 variable grounded : std_logic; 137 variable c_left, c_right, c_top, c_bot : std_logic_vector(10 downto 0); 138 variable c_prev_top, c_prev_bot, c_prev_left, c_prev_right : std_logic_vector(10 downto 0); 139 variable tcx, tcy, dcx, dcy : std_logic_vector(10 downto 0); 140 begin 141 wait until vert_sync'event and vert_sync = '1'; 142 143 vx := vel_x; vy := vel_y; 144 bounced := '0'; bounce_wall := '0'; 145 bounce_speed := (others => '0'); 146 grounded := '0'; 147 148 -- == INPUT == 149 if key_w = '0' then jump_pressed <= '0'; end if; 150 151 if key_w = '1' and jump_pressed = '0' and on_ground = '1' then 152 vy := (others => '0'); 153 vy := vy - JUMP_FORCE; 154 jump_pressed <= '1'; 155 end if; 156 157 if key_w = '1' and jump_pressed = '1' and on_ground = '1' then 158 vy := vy - JUMP_FORCE; 159 end if; 160 161 if key_s = '1' and on_ground = '0' then vy := vy + SLAM_FORCE; end if; 162 163 if key_a = '1' then 164 if on_ground = '1' then vx := vx - IMPULSE; else vx := vx - 2; end if; 165 end if; 166 if key_d = '1' then 167 if on_ground = '1' then vx := vx + IMPULSE; else vx := vx + 2; end if; 168 end if; 169 170 -- == GRAVITY == 171 vy := vy + GRAVITY; 172 173 -- == FRICTION: vel -= vel/4, min 1 == 174 if vx(9) = '0' then 175 if vx > 0 then 176 if vx(9 downto 2) = "00000000" then vx := vx - 1; 177 else vx := vx - ("00" & vx(9 downto 2)); end if; 178 end if; 179 else 180 if vx /= "0000000000" then 181 if vx(9 downto 2) = "11111111" then vx := vx + 1; 182 else vx := vx - ("11" & vx(9 downto 2)); end if; 183 end if; 184 end if; 185 186 -- == CLAMP velocities == 187 if vx(9) = '0' and vx > MAX_VEL_X then vx := MAX_VEL_X; end if; 188 if vx(9) = '1' and vx < (not MAX_VEL_X) + 1 then vx := (not MAX_VEL_X) + 1; end if; 189 if vy(9) = '0' and vy > 80 then vy := CONV_STD_LOGIC_VECTOR(80, 10); end if; 190 if vy(9) = '1' and vy < CONV_STD_LOGIC_VECTOR(924, 10) then -- 924 = -100 191 vy := CONV_STD_LOGIC_VECTOR(924, 10); 192 end if; 193 194 -- == MOVE: sign-extend 10-bit velocity to 11-bit before adding == 195 px := pos_x + (vx(9) & vx); 196 py := pos_y + (vy(9) & vy); 197 198 -- == GROUND == 199 if py >= GROUND then 200 py := GROUND; 201 bounced := '1'; grounded := '1'; 202 bounce_speed := vy; 203 vy := (not vy) + 1; 204 if vy(9) = '1' then 205 vy := vy + ("000" & ((not vy(9 downto 3)) + 1)); 206 end if; 207 if vy(9) = '1' and vy >= CONV_STD_LOGIC_VECTOR(1022, 10) then 208 vy := (others => '0'); 209 elsif vy(9) = '0' then vy := (others => '0'); end if; 210 end if; 211 212 -- == CEILING == 213 -- Underflow detection: py >= 2000 means it wrapped below zero. 214 -- (GROUND=1480, RIGHT_WALL=1492 are both < 2000, so no false triggers.) 215 if py >= UNDERFLOW or py <= CEILING then 216 py := CEILING; 217 bounced := '1'; 218 bounce_speed := (not vy) + 1; 219 vy := (not vy) + 1; 220 if vy(9) = '0' and vy > 1 then 221 vy := vy - ("000" & vy(9 downto 3)); 222 end if; 223 end if; 224 225 -- == LEFT WALL == 226 -- Same underflow fix: px >= 2000 means wrapped below zero. 227 if px >= UNDERFLOW or px <= LEFT_WALL + SIZE11 then 228 px := LEFT_WALL + SIZE11; 229 bounced := '1'; bounce_wall := '1'; 230 bounce_speed := (not vx) + 1; 231 vx := (not vx) + 1; 232 if vx(9) = '0' and vx > 1 then 233 vx := vx - ("000" & vx(9 downto 3)); 234 end if; 235 end if; 236 237 -- == RIGHT WALL == 238 if px >= RIGHT_WALL - SIZE11 then 239 px := RIGHT_WALL - SIZE11; 240 bounced := '1'; bounce_wall := '1'; 241 bounce_speed := vx; 242 vx := (not vx) + 1; 243 if vx(9) = '1' then 244 vx := vx + ("000" & ((not vx(9 downto 3)) + 1)); 245 end if; 246 if vx(9) = '1' and vx >= CONV_STD_LOGIC_VECTOR(1022, 10) then 247 vx := (others => '0'); 248 end if; 249 end if; 250 251 -- == OBSTACLE COLLISIONS == 252 -- Use previous-frame bounds to detect which face was entered. 253 -- This avoids the overlap-axis bug where a fast horizontal move into 254 -- a thin platform's side gets incorrectly resolved as a vertical hit. 255 c_prev_top := pos_y - SIZE11; 256 c_prev_bot := pos_y + SIZE11; 257 c_prev_left := pos_x - SIZE11; 258 c_prev_right := pos_x + SIZE11; 259 260 for obs_i in 0 to OBS_COUNT-1 loop 261 c_left := px - SIZE11; 262 c_right := px + SIZE11; 263 c_top := py - SIZE11; 264 c_bot := py + SIZE11; 265 266 if c_right >= OBS_L(obs_i) and c_left <= OBS_R(obs_i) and 267 c_bot >= OBS_T(obs_i) and c_top <= OBS_B(obs_i) then 268 269 if c_prev_bot <= OBS_T(obs_i) then 270 -- Entered from top → land on surface 271 py := OBS_T(obs_i) - SIZE11; grounded := '1'; 272 bounced := '1'; 273 vy := (not vy) + 1; 274 if vy(9) = '0' and vy > 1 then 275 vy := vy - ("000" & vy(9 downto 3)); 276 elsif vy(9) = '1' and vy < CONV_STD_LOGIC_VECTOR(1022, 10) then 277 vy := vy + ("000" & ((not vy(9 downto 3)) + 1)); 278 end if; 279 if vy(9) = '0' and vy < 2 then vy := (others => '0'); end if; 280 if vy(9) = '1' and vy >= CONV_STD_LOGIC_VECTOR(1022, 10) then 281 vy := (others => '0'); 282 end if; 283 284 elsif c_prev_top >= OBS_B(obs_i) then 285 -- Entered from bottom → bump head on underside 286 py := OBS_B(obs_i) + SIZE11; 287 bounced := '1'; 288 vy := (not vy) + 1; 289 if vy(9) = '0' and vy > 1 then 290 vy := vy - ("000" & vy(9 downto 3)); 291 end if; 292 293 elsif c_prev_right <= OBS_L(obs_i) then 294 -- Entered from left → bounce off left face 295 px := OBS_L(obs_i) - SIZE11; 296 bounced := '1'; bounce_wall := '1'; 297 vx := (not vx) + 1; 298 if vx(9) = '0' and vx > 1 then 299 vx := vx - ("000" & vx(9 downto 3)); 300 elsif vx(9) = '1' and vx < CONV_STD_LOGIC_VECTOR(1022, 10) then 301 vx := vx + ("000" & ((not vx(9 downto 3)) + 1)); 302 end if; 303 304 elsif c_prev_left >= OBS_R(obs_i) then 305 -- Entered from right → bounce off right face 306 px := OBS_R(obs_i) + SIZE11; 307 bounced := '1'; bounce_wall := '1'; 308 vx := (not vx) + 1; 309 if vx(9) = '0' and vx > 1 then 310 vx := vx - ("000" & vx(9 downto 3)); 311 elsif vx(9) = '1' and vx < CONV_STD_LOGIC_VECTOR(1022, 10) then 312 vx := vx + ("000" & ((not vx(9 downto 3)) + 1)); 313 end if; 314 315 else 316 -- Corner / spawn-inside fallback: resolve by velocity direction 317 if vy(9) = '0' then py := OBS_T(obs_i) - SIZE11; grounded := '1'; 318 else py := OBS_B(obs_i) + SIZE11; end if; 319 bounced := '1'; 320 vy := (not vy) + 1; 321 if vy(9) = '0' and vy > 1 then 322 vy := vy - ("000" & vy(9 downto 3)); 323 end if; 324 end if; 325 end if; 326 end loop; 327 328 -- == COMMIT == 329 vel_x <= vx; 330 vel_y <= vy; 331 pos_x <= px; 332 pos_y <= py; 333 334 -- == CAMERA: lag-follow character (1/8 of gap per frame, min 1px) == 335 -- Compute clamped target 336 if px < CONV_STD_LOGIC_VECTOR(320, 11) then 337 tcx := (others => '0'); 338 elsif px > CONV_STD_LOGIC_VECTOR(1180, 11) then 339 tcx := CONV_STD_LOGIC_VECTOR(860, 11); 340 else 341 tcx := px - CONV_STD_LOGIC_VECTOR(320, 11); 342 end if; 343 344 if py < CONV_STD_LOGIC_VECTOR(240, 11) then 345 tcy := (others => '0'); 346 elsif py > CONV_STD_LOGIC_VECTOR(1260, 11) then 347 tcy := CONV_STD_LOGIC_VECTOR(1020, 11); 348 else 349 tcy := py - CONV_STD_LOGIC_VECTOR(240, 11); 350 end if; 351 352 -- Slide camera toward target 353 if cam_x_sig < tcx then 354 dcx := tcx - cam_x_sig; 355 if dcx(10 downto 3) = "00000000" then 356 cam_x_sig <= cam_x_sig + 1; 357 else 358 cam_x_sig <= cam_x_sig + ("000" & dcx(10 downto 3)); 359 end if; 360 elsif cam_x_sig > tcx then 361 dcx := cam_x_sig - tcx; 362 if dcx(10 downto 3) = "00000000" then 363 cam_x_sig <= cam_x_sig - 1; 364 else 365 cam_x_sig <= cam_x_sig - ("000" & dcx(10 downto 3)); 366 end if; 367 end if; 368 369 if cam_y_sig < tcy then 370 dcy := tcy - cam_y_sig; 371 if dcy(10 downto 3) = "00000000" then 372 cam_y_sig <= cam_y_sig + 1; 373 else 374 cam_y_sig <= cam_y_sig + ("000" & dcy(10 downto 3)); 375 end if; 376 elsif cam_y_sig > tcy then 377 dcy := cam_y_sig - tcy; 378 if dcy(10 downto 3) = "00000000" then 379 cam_y_sig <= cam_y_sig - 1; 380 else 381 cam_y_sig <= cam_y_sig - ("000" & dcy(10 downto 3)); 382 end if; 383 end if; 384 385 -- == SQUISH == 386 if bounced = '1' and bounce_speed > 3 then 387 if bounce_speed >= 8 then squish <= "1000"; 388 else squish <= bounce_speed(3 downto 0); end if; 389 squish_h <= bounce_wall; 390 elsif squish > 0 then 391 squish <= squish - 1; 392 end if; 393 394 if grounded = '1' then on_ground <= '1'; 395 else on_ground <= '0'; end if; 396 397 end process physics; 398 399end behavior;