1
fork

Configure Feed

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

feat: add the sweeping checks

+85 -80
+30 -31
physics_engine.vhd
··· 93 93 signal on_ground : std_logic := '0'; 94 94 signal jump_pressed : std_logic := '0'; 95 95 signal slam_held : std_logic := '0'; 96 + signal slam_tap : std_logic := '0'; -- S pressed then released before landing 96 97 signal slam_start_y : std_logic_vector(10 downto 0) := (others => '0'); 97 98 signal prev_key_s : std_logic := '0'; 98 99 ··· 159 160 variable jforce_slv : std_logic_vector(9 downto 0); 160 161 variable slam_dist : std_logic_vector(10 downto 0); 161 162 variable slam_boost : std_logic_vector(9 downto 0); 163 + variable x_overlap : boolean; 164 + variable y_overlap : boolean; 162 165 begin 163 166 wait until vert_sync'event and vert_sync = '1'; 164 167 ··· 194 197 if key_s = '1' and prev_key_s = '0' and on_ground = '0' then 195 198 slam_start_y <= pos_y; 196 199 slam_held <= '1'; 200 + slam_tap <= '1'; 197 201 vy := vy + SLAM_TAP_FORCE; 198 202 end if; 199 - if key_s = '0' then slam_held <= '0'; end if; 203 + -- S released before landing: mark as tap, clear hold 204 + if key_s = '0' and slam_held = '1' then slam_held <= '0'; end if; 200 205 prev_key_s <= key_s; 201 206 202 207 if key_a = '1' then ··· 249 254 end if; 250 255 251 256 -- == CEILING == 252 - -- Underflow detection: py >= 2000 means it wrapped below zero. 253 - -- (GROUND=1480, RIGHT_WALL=1492 are both < 2000, so no false triggers.) 254 257 if py >= UNDERFLOW or py <= CEILING then 255 258 py := CEILING; 256 259 bounced := '1'; ··· 262 265 end if; 263 266 264 267 -- == LEFT WALL == 265 - -- Same underflow fix: px >= 2000 means wrapped below zero. 266 268 if px >= UNDERFLOW or px <= LEFT_WALL + SIZE11 then 267 269 px := LEFT_WALL + SIZE11; 268 270 bounced := '1'; bounce_wall := '1'; ··· 287 289 end if; 288 290 end if; 289 291 290 - -- == OBSTACLE COLLISIONS == 291 - -- Use previous-frame bounds to detect which face was entered. 292 - -- This avoids the overlap-axis bug where a fast horizontal move into 293 - -- a thin platform's side gets incorrectly resolved as a vertical hit. 292 + -- == OBSTACLE COLLISIONS (swept AABB) == 294 293 c_prev_top := pos_y - SIZE11; 295 294 c_prev_bot := pos_y + SIZE11; 296 295 c_prev_left := pos_x - SIZE11; ··· 302 301 c_top := py - SIZE11; 303 302 c_bot := py + SIZE11; 304 303 305 - -- 1. Swept Horizontal Overlap 306 - if vx(9) = '0' then -- moving right 304 + -- Swept horizontal overlap: union of prev and new extents along motion axis 305 + if vx(9) = '0' then 307 306 x_overlap := (c_right >= OBS_L(obs_i)) and (c_prev_left <= OBS_R(obs_i)); 308 - else -- moving left 307 + else 309 308 x_overlap := (c_prev_right >= OBS_L(obs_i)) and (c_left <= OBS_R(obs_i)); 310 309 end if; 311 310 312 - -- 2. Swept Vertical Overlap 313 - if vy(9) = '0' then -- moving down (gravity is positive) 311 + -- Swept vertical overlap 312 + if vy(9) = '0' then 314 313 y_overlap := (c_bot >= OBS_T(obs_i)) and (c_prev_top <= OBS_B(obs_i)); 315 - else -- moving up 314 + else 316 315 y_overlap := (c_prev_bot >= OBS_T(obs_i)) and (c_top <= OBS_B(obs_i)); 317 316 end if; 318 317 319 - -- 3. Check collision using the swept bounds 320 318 if x_overlap and y_overlap then 321 319 322 320 if c_prev_bot <= OBS_T(obs_i) then 323 - -- Entered from top → land on surface 324 321 py := OBS_T(obs_i) - SIZE11; grounded := '1'; 325 322 bounced := '1'; 326 323 vy := (not vy) + 1; ··· 335 332 end if; 336 333 337 334 elsif c_prev_top >= OBS_B(obs_i) then 338 - -- Entered from bottom → bump head on underside 339 335 py := OBS_B(obs_i) + SIZE11; 340 336 bounced := '1'; 341 337 vy := (not vy) + 1; ··· 344 340 end if; 345 341 346 342 elsif c_prev_right <= OBS_L(obs_i) then 347 - -- Entered from left → bounce off left face 348 343 px := OBS_L(obs_i) - SIZE11; 349 344 bounced := '1'; bounce_wall := '1'; 350 345 vx := (not vx) + 1; ··· 355 350 end if; 356 351 357 352 elsif c_prev_left >= OBS_R(obs_i) then 358 - -- Entered from right → bounce off right face 359 353 px := OBS_R(obs_i) + SIZE11; 360 354 bounced := '1'; bounce_wall := '1'; 361 355 vx := (not vx) + 1; ··· 366 360 end if; 367 361 368 362 else 369 - -- Corner / spawn-inside fallback: resolve by velocity direction 370 363 if vy(9) = '0' then py := OBS_T(obs_i) - SIZE11; grounded := '1'; 371 364 else py := OBS_B(obs_i) + SIZE11; end if; 372 365 bounced := '1'; ··· 378 371 end if; 379 372 end loop; 380 373 381 - -- == HOLD-SLAM LANDING BOOST == 382 - -- Bigger boost when slam started closer to the surface 383 - if grounded = '1' and slam_held = '1' then 384 - slam_dist := py - slam_start_y; 385 - if slam_dist < SLAM_CLOSE_THR then 386 - slam_boost := SLAM_BOOST_CLOSE; 387 - elsif slam_dist < SLAM_MED_THR then 388 - slam_boost := SLAM_BOOST_MED; 374 + -- == SLAM LANDING RESOLUTION == 375 + if grounded = '1' and slam_tap = '1' then 376 + if slam_held = '0' then 377 + -- Tap slam: S released before landing — kill bounce, plant on surface 378 + vy := (others => '0'); 389 379 else 390 - slam_boost := SLAM_BOOST_FAR; 380 + -- Hold slam: distance-based upward boost 381 + slam_dist := py - slam_start_y; 382 + if slam_dist < SLAM_CLOSE_THR then 383 + slam_boost := SLAM_BOOST_CLOSE; 384 + elsif slam_dist < SLAM_MED_THR then 385 + slam_boost := SLAM_BOOST_MED; 386 + else 387 + slam_boost := SLAM_BOOST_FAR; 388 + end if; 389 + vy := vy - slam_boost; 390 + slam_held <= '0'; 391 391 end if; 392 - vy := vy - slam_boost; 393 - slam_held <= '0'; 392 + slam_tap <= '0'; 394 393 end if; 395 394 396 395 -- == COMMIT ==
+55 -49
visualizer.py
··· 91 91 on_ground = False 92 92 jump_pressed = False 93 93 slam_held = False 94 + slam_tap = False # S pressed and released before landing — suppress bounce 94 95 slam_start_y = 0 95 96 prev_key_s = False 96 97 ··· 116 117 char_x, char_y = 100, 200 117 118 vel_x = vel_y = to_unsigned(0) 118 119 squish = 0; squish_h = False; on_ground = False; jump_pressed = False 119 - slam_held = False; slam_start_y = 0; prev_key_s = False 120 + slam_held = False; slam_tap = False; slam_start_y = 0; prev_key_s = False 120 121 trail.clear() 121 122 elif event.type == pygame.KEYUP: 122 123 if event.key == pygame.K_w: keys_held['w'] = False ··· 156 157 if keys_held['s'] and not prev_key_s and not on_ground: 157 158 slam_start_y = char_y 158 159 slam_held = True 160 + slam_tap = True 159 161 vy = (vy + SLAM_TAP_FORCE) & MASK 160 - if not keys_held['s']: 162 + if not keys_held['s'] and slam_held: 163 + # S released before landing — mark as tap, no hold boost 161 164 slam_held = False 162 165 prev_key_s = keys_held['s'] 163 166 ··· 208 211 # --- Ground bounce --- 209 212 if py >= GROUND: 210 213 py = GROUND 211 - bounced = True 212 - grounded = True 214 + bounced = True; grounded = True 213 215 bounce_speed = abs(to_signed(vy)) 214 216 vy = negate(vy) 215 217 svy = to_signed(vy) 216 - # svy is now negative (upward), apply energy loss on magnitude 217 218 if svy < -1: 218 - svy += (-svy) >> BOUNCE_SHIFT # reduce magnitude 219 - # Kill tiny bounces 219 + svy += (-svy) >> BOUNCE_SHIFT 220 220 if abs(svy) < 2: 221 221 svy = 0 222 222 vy = to_unsigned(svy) ··· 231 231 if abs(svy) > 1: 232 232 svy_abs = abs(svy) 233 233 loss = svy_abs >> BOUNCE_SHIFT 234 - if svy > 0: 235 - svy -= loss 236 - else: 237 - svy += loss 234 + if svy > 0: svy -= loss 235 + else: svy += loss 238 236 vy = to_unsigned(svy) 239 237 240 - # --- Left wall bounce --- 241 - if px <= LEFT_WALL + SIZE: 242 - px = LEFT_WALL + SIZE 243 - bounced = True; bounce_wall = True 244 - bounce_speed = abs(to_signed(vx)) 245 - vx = negate(vx) 246 - svx = to_signed(vx) 247 - if svx > 1: 248 - svx -= svx >> BOUNCE_SHIFT 249 - vx = to_unsigned(svx) 250 - 251 - # --- Right wall bounce --- 252 - if px >= RIGHT_WALL - SIZE: 253 - px = RIGHT_WALL - SIZE 254 - bounced = True; bounce_wall = True 255 - bounce_speed = abs(to_signed(vx)) 256 - vx = negate(vx) 257 - svx = to_signed(vx) 258 - if svx < -1: 259 - svx += (-svx) >> BOUNCE_SHIFT 260 - vx = to_unsigned(svx) 261 - 262 - # --- Obstacle collisions (entry-face detection using prev position) --- 238 + # --- Obstacle collisions (swept AABB — mirrors VHDL swept check) --- 263 239 prev_top = char_y - SIZE 264 240 prev_bot = char_y + SIZE 265 241 prev_left = char_x - SIZE 266 242 prev_right = char_x + SIZE 267 243 268 244 for (ol, ot, orr, ob) in OBSTACLES: 269 - c_left = px - SIZE 270 - c_right = px + SIZE 271 - c_top = py - SIZE 272 - c_bot = py + SIZE 245 + c_left = px - SIZE; c_right = px + SIZE 246 + c_top = py - SIZE; c_bot = py + SIZE 273 247 274 - if c_right >= ol and c_left <= orr and c_bot >= ot and c_top <= ob: 248 + if to_signed(vx) >= 0: 249 + x_overlap = c_right >= ol and prev_left <= orr 250 + else: 251 + x_overlap = prev_right >= ol and c_left <= orr 252 + if to_signed(vy) >= 0: 253 + y_overlap = c_bot >= ot and prev_top <= ob 254 + else: 255 + y_overlap = prev_bot >= ot and c_top <= ob 256 + 257 + if x_overlap and y_overlap: 275 258 if prev_bot <= ot: 276 - # Entered from top 277 259 py = ot - SIZE; grounded = True 278 260 bounced = True 279 261 bounce_speed = abs(to_signed(vy)) ··· 285 267 if abs(svy) < 2: svy = 0 286 268 vy = to_unsigned(svy) 287 269 elif prev_top >= ob: 288 - # Entered from bottom 289 270 py = ob + SIZE 290 271 bounced = True 291 272 vy = negate(vy) ··· 294 275 svy -= svy >> BOUNCE_SHIFT 295 276 vy = to_unsigned(svy) 296 277 elif prev_right <= ol: 297 - # Entered from left 298 278 px = ol - SIZE 299 279 bounced = True; bounce_wall = True 300 280 bounce_speed = abs(to_signed(vx)) 301 281 vx = negate(vx) 302 282 svx = to_signed(vx) 303 - if svx > 1: svx -= svx >> BOUNCE_SHIFT 304 - elif svx < -1: svx += (-svx) >> BOUNCE_SHIFT 283 + if svx > 1: svx -= svx >> BOUNCE_SHIFT 284 + elif svx < -1: svx += (-svx) >> BOUNCE_SHIFT 305 285 vx = to_unsigned(svx) 306 286 elif prev_left >= orr: 307 - # Entered from right 308 287 px = orr + SIZE 309 288 bounced = True; bounce_wall = True 310 289 bounce_speed = abs(to_signed(vx)) 311 290 vx = negate(vx) 312 291 svx = to_signed(vx) 313 - if svx > 1: svx -= svx >> BOUNCE_SHIFT 314 - elif svx < -1: svx += (-svx) >> BOUNCE_SHIFT 292 + if svx > 1: svx -= svx >> BOUNCE_SHIFT 293 + elif svx < -1: svx += (-svx) >> BOUNCE_SHIFT 315 294 vx = to_unsigned(svx) 316 295 else: 317 - # Corner/inside fallback 318 296 svy_now = to_signed(vy) 319 297 if svy_now >= 0: py = ot - SIZE; grounded = True 320 298 else: py = ob + SIZE ··· 326 304 svy = svy - loss if svy > 0 else svy + loss 327 305 vy = to_unsigned(svy) 328 306 307 + # --- Left wall bounce --- 308 + if px <= LEFT_WALL + SIZE: 309 + px = LEFT_WALL + SIZE 310 + bounced = True; bounce_wall = True 311 + bounce_speed = abs(to_signed(vx)) 312 + vx = negate(vx) 313 + svx = to_signed(vx) 314 + if svx > 1: 315 + svx -= svx >> BOUNCE_SHIFT 316 + vx = to_unsigned(svx) 317 + 318 + # --- Right wall bounce --- 319 + if px >= RIGHT_WALL - SIZE: 320 + px = RIGHT_WALL - SIZE 321 + bounced = True; bounce_wall = True 322 + bounce_speed = abs(to_signed(vx)) 323 + vx = negate(vx) 324 + svx = to_signed(vx) 325 + if svx < -1: 326 + svx += (-svx) >> BOUNCE_SHIFT 327 + vx = to_unsigned(svx) 328 + 329 + # Tap slam: S was released before landing — kill bounce, plant on surface 330 + if grounded and slam_tap and not slam_held: 331 + vy = to_unsigned(0) 332 + slam_tap = False 333 + 329 334 # Hold-slam landing boost: bigger boost when slam started closer to the surface 330 - if grounded and slam_held: 335 + elif grounded and slam_held: 331 336 dist = py - slam_start_y 332 337 if dist < SLAM_CLOSE_THR: 333 338 slam_boost_val = SLAM_BOOST_CLOSE ··· 337 342 slam_boost_val = SLAM_BOOST_FAR 338 343 vy = (vy - slam_boost_val) & MASK 339 344 slam_held = False 345 + slam_tap = False 340 346 341 347 # Commit 342 348 char_x = px