1
fork

Configure Feed

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

feat: add trail

+203 -26
+9 -5
physics_engine.vhd
··· 29 29 char_height : out std_logic_vector(9 downto 0); 30 30 cam_x : out std_logic_vector(10 downto 0); 31 31 cam_y : out std_logic_vector(10 downto 0); 32 - vel_out : out std_logic_vector(9 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) 33 35 ); 34 36 end physics_engine; 35 37 ··· 82 84 83 85 begin 84 86 85 - char_x <= pos_x; 86 - char_y <= pos_y; 87 - cam_x <= cam_x_sig; 88 - cam_y <= cam_y_sig; 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; 89 93 90 94 -- Absolute values of both velocity axes. 91 95 abs_vel_x <= (not vel_x) + 1 when vel_x(9) = '1' else vel_x;
+86 -16
renderer.vhd
··· 4 4 -- world_row = pixel_row + cam_y 5 5 -- All comparisons in world-space (11-bit). 6 6 -- Obstacles imported from level package (use work.level.all). 7 + -- 8 + -- Color priority: 9 + -- Arrow = White (111) > Char = Red (100) > Obs = Yellow (110) 10 + -- > Ground/Ceil/Walls = Green (010) > Trail = Magenta (101) > Sky = Black (000) 7 11 -- ======================================================================== 8 12 9 13 library IEEE; ··· 22 26 char_height : in std_logic_vector(9 downto 0); 23 27 cam_x : in std_logic_vector(10 downto 0); 24 28 cam_y : in std_logic_vector(10 downto 0); 29 + vel_x_in : in std_logic_vector(9 downto 0); 30 + vel_y_in : in std_logic_vector(9 downto 0); 31 + trail_on_in : in std_logic; 25 32 red : out std_logic; 26 33 green : out std_logic; 27 34 blue : out std_logic ··· 36 43 constant LEFT_WALL : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(8, 11); 37 44 constant RIGHT_WALL : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1492, 11); 38 45 39 - signal char_on, ground_on, wall_on, ceiling_on, obs_on : std_logic; 46 + -- World-space pixel coordinates (shared between processes) 47 + signal wc : std_logic_vector(10 downto 0); 48 + signal wr : std_logic_vector(10 downto 0); 49 + 50 + signal char_on, ground_on, wall_on, ceiling_on, obs_on, arrow_on : std_logic; 40 51 41 52 begin 42 53 43 - render : process(pixel_row, pixel_column, char_x, char_y, 44 - char_width, char_height, cam_x, cam_y) 45 - variable wc : std_logic_vector(10 downto 0); 46 - variable wr : std_logic_vector(10 downto 0); 54 + -- Convert screen coordinates to world coordinates 55 + wc <= ('0' & pixel_column) + cam_x; 56 + wr <= ('0' & pixel_row) + cam_y; 57 + 58 + -- ---------------------------------------------------------------- 59 + -- Scene geometry: character, world edges, obstacles 60 + -- ---------------------------------------------------------------- 61 + render : process(wc, wr, char_x, char_y, char_width, char_height) 47 62 begin 48 - wc := ('0' & pixel_column) + cam_x; 49 - wr := ('0' & pixel_row) + cam_y; 50 - 51 63 char_on <= '0'; 52 64 ground_on <= '0'; 53 65 wall_on <= '0'; ··· 77 89 78 90 end process render; 79 91 80 - -- Priority: char=Red(100), obstacles=Yellow(110), ground/ceil/walls=Green(010), sky=Black 81 - color : process(char_on, ground_on, wall_on, ceiling_on, obs_on) 92 + -- ---------------------------------------------------------------- 93 + -- Velocity vector arrow 94 + -- Line from (char_x, char_y) to (char_x + 3*vx, char_y + 3*vy). 95 + -- Uses cross-product line-segment test: 96 + -- pixel on segment iff |dy*(px-x0) - dx*(py-y0)| <= max(|dx|,|dy|) 97 + -- and dx*(px-x0) + dy*(py-y0) in [0, dx^2+dy^2] 98 + -- ---------------------------------------------------------------- 99 + arrow_proc : process(wc, wr, char_x, char_y, vel_x_in, vel_y_in) 100 + variable wci, wri : integer; 101 + variable cx, cy : integer; 102 + variable vxi, vyi : integer; 103 + variable dx, dy : integer; 104 + variable cross : integer; 105 + variable dot_p : integer; 106 + variable len_sq : integer; 107 + variable maxd : integer; 82 108 begin 83 - if char_on = '1' then 84 - red <= '1'; green <= '0'; blue <= '0'; 109 + wci := CONV_INTEGER(wc); 110 + wri := CONV_INTEGER(wr); 111 + cx := CONV_INTEGER(char_x); 112 + cy := CONV_INTEGER(char_y); 113 + 114 + -- Convert 10-bit 2's complement velocity to signed integer 115 + if vel_x_in(9) = '1' then vxi := CONV_INTEGER(vel_x_in) - 1024; 116 + else vxi := CONV_INTEGER(vel_x_in); end if; 117 + if vel_y_in(9) = '1' then vyi := CONV_INTEGER(vel_y_in) - 1024; 118 + else vyi := CONV_INTEGER(vel_y_in); end if; 119 + 120 + -- Scale arrow by 3 for visibility 121 + dx := vxi * 3; 122 + dy := vyi * 3; 123 + 124 + -- Precompute line metrics 125 + cross := dy * (wci - cx) - dx * (wri - cy); 126 + dot_p := dx * (wci - cx) + dy * (wri - cy); 127 + len_sq := dx * dx + dy * dy; 128 + 129 + -- Chebyshev thickness (~1-2 world-pixels wide) 130 + maxd := dx; if maxd < 0 then maxd := -maxd; end if; 131 + if dy > maxd then maxd := dy; 132 + elsif -dy > maxd then maxd := -dy; end if; 133 + 134 + arrow_on <= '0'; 135 + if maxd > 4 then -- skip arrow when velocity is negligible 136 + if cross >= -maxd and cross <= maxd and 137 + dot_p >= 0 and dot_p <= len_sq then 138 + arrow_on <= '1'; 139 + end if; 140 + end if; 141 + end process arrow_proc; 142 + 143 + -- ---------------------------------------------------------------- 144 + -- Color output with priority 145 + -- ---------------------------------------------------------------- 146 + color : process(char_on, ground_on, wall_on, ceiling_on, 147 + obs_on, arrow_on, trail_on_in) 148 + begin 149 + if arrow_on = '1' then 150 + red <= '1'; green <= '1'; blue <= '1'; -- White 151 + elsif char_on = '1' then 152 + red <= '1'; green <= '0'; blue <= '0'; -- Red 85 153 elsif obs_on = '1' then 86 - red <= '1'; green <= '1'; blue <= '0'; 154 + red <= '1'; green <= '1'; blue <= '0'; -- Yellow 87 155 elsif ground_on = '1' or ceiling_on = '1' then 88 - red <= '0'; green <= '1'; blue <= '0'; 156 + red <= '0'; green <= '1'; blue <= '0'; -- Green 89 157 elsif wall_on = '1' then 90 - red <= '0'; green <= '1'; blue <= '0'; 158 + red <= '0'; green <= '1'; blue <= '0'; -- Green 159 + elsif trail_on_in = '1' then 160 + red <= '1'; green <= '0'; blue <= '1'; -- Magenta 91 161 else 92 - red <= '0'; green <= '0'; blue <= '0'; 162 + red <= '0'; green <= '0'; blue <= '0'; -- Black (sky) 93 163 end if; 94 164 end process color; 95 165
+89
trail.vhd
··· 1 + -- ======================================================================== 2 + -- Trail 3 + -- Stores 16 world-space positions (updated every 3 frames = ~0.8 s at 60 Hz). 4 + -- Combinationally checks each pixel against stored positions (radius 4 box). 5 + -- trail_on fires for any pixel within 4 world-pixels of any stored dot. 6 + -- ======================================================================== 7 + 8 + library IEEE; 9 + use IEEE.STD_LOGIC_1164.all; 10 + use IEEE.STD_LOGIC_ARITH.all; 11 + use IEEE.STD_LOGIC_UNSIGNED.all; 12 + 13 + entity trail is 14 + port( 15 + vert_sync : in std_logic; 16 + char_x : in std_logic_vector(10 downto 0); 17 + char_y : in std_logic_vector(10 downto 0); 18 + pixel_column : in std_logic_vector(9 downto 0); 19 + pixel_row : in std_logic_vector(9 downto 0); 20 + cam_x : in std_logic_vector(10 downto 0); 21 + cam_y : in std_logic_vector(10 downto 0); 22 + trail_on : out std_logic 23 + ); 24 + end trail; 25 + 26 + architecture behavior of trail is 27 + 28 + constant TRAIL_LEN : integer := 16; 29 + constant TRAIL_DIV : integer := 3; -- update once every 3 frames 30 + constant TRAIL_RADIUS : integer := 4; -- Chebyshev radius in world-pixels 31 + 32 + type pos_arr_t is array(0 to TRAIL_LEN-1) of std_logic_vector(10 downto 0); 33 + 34 + signal tx : pos_arr_t := (others => (others => '0')); 35 + signal ty : pos_arr_t := (others => (others => '0')); 36 + signal trail_valid : std_logic_vector(TRAIL_LEN-1 downto 0) := (others => '0'); 37 + signal frame_ctr : integer range 0 to TRAIL_DIV-1 := 0; 38 + 39 + begin 40 + 41 + -- Shift register: push new position every TRAIL_DIV frames 42 + shift : process 43 + begin 44 + wait until vert_sync'event and vert_sync = '1'; 45 + 46 + if frame_ctr = TRAIL_DIV - 1 then 47 + for i in TRAIL_LEN-1 downto 1 loop 48 + tx(i) <= tx(i-1); 49 + ty(i) <= ty(i-1); 50 + trail_valid(i) <= trail_valid(i-1); 51 + end loop; 52 + tx(0) <= char_x; 53 + ty(0) <= char_y; 54 + trail_valid(0) <= '1'; 55 + frame_ctr <= 0; 56 + else 57 + frame_ctr <= frame_ctr + 1; 58 + end if; 59 + end process shift; 60 + 61 + -- Pixel check: is current pixel within TRAIL_RADIUS of any valid trail dot? 62 + check : process(pixel_column, pixel_row, cam_x, cam_y, tx, ty, trail_valid) 63 + variable wci, wri : integer; 64 + variable xi, yi : integer; 65 + variable dx, dy : integer; 66 + variable hit : std_logic; 67 + begin 68 + wci := CONV_INTEGER('0' & pixel_column) + CONV_INTEGER(cam_x); 69 + wri := CONV_INTEGER('0' & pixel_row) + CONV_INTEGER(cam_y); 70 + hit := '0'; 71 + 72 + for i in 0 to TRAIL_LEN-1 loop 73 + if trail_valid(i) = '1' then 74 + xi := CONV_INTEGER(tx(i)); 75 + yi := CONV_INTEGER(ty(i)); 76 + dx := wci - xi; 77 + dy := wri - yi; 78 + if dx < 0 then dx := -dx; end if; 79 + if dy < 0 then dy := -dy; end if; 80 + if dx <= TRAIL_RADIUS and dy <= TRAIL_RADIUS then 81 + hit := '1'; 82 + end if; 83 + end if; 84 + end loop; 85 + 86 + trail_on <= hit; 87 + end process check; 88 + 89 + end behavior;
+19 -5
visualizer.py
··· 11 11 python3 visualizer.py 12 12 """ 13 13 14 + import math 14 15 import pygame 15 16 import sys 16 17 ··· 125 126 if not keys_held['w']: 126 127 jump_pressed = False 127 128 128 - # First press on ground: full jump 129 + # First press on ground: full jump (elif prevents double-apply on same frame) 129 130 if keys_held['w'] and not jump_pressed and on_ground: 130 131 vy = to_unsigned(0) 131 132 vy = (vy - JUMP_FORCE) & MASK 132 133 jump_pressed = True 133 - 134 - # Holding W while bouncing: boost each ground contact 135 - if keys_held['w'] and jump_pressed and on_ground: 134 + elif keys_held['w'] and jump_pressed and on_ground: 135 + # Holding W while bouncing: boost each ground contact 136 136 vy = (vy - JUMP_FORCE) & MASK 137 137 138 138 # S: slam (air only) ··· 369 369 pygame.draw.rect(screen, COLOR_CHAR, 370 370 (char_x - cw, char_y - ch, cw * 2, ch * 2)) 371 371 372 - # HUD 372 + # Velocity vector arrow (scale: 2px per unit of velocity) 373 373 svx = to_signed(vel_x) 374 374 svy = to_signed(vel_y) 375 + ARROW_SCALE = 2 376 + ax = char_x + svx * ARROW_SCALE 377 + ay = char_y + svy * ARROW_SCALE 378 + if svx != 0 or svy != 0: 379 + pygame.draw.line(screen, (255, 255, 255), (char_x, char_y), (ax, ay), 2) 380 + # Arrowhead 381 + angle = math.atan2(svy, svx) 382 + head = 6 383 + for side in (+0.5, -0.5): 384 + hx = ax - head * math.cos(angle + side) 385 + hy = ay - head * math.sin(angle + side) 386 + pygame.draw.line(screen, (255, 255, 255), (ax, ay), (int(hx), int(hy)), 2) 387 + 388 + # HUD 375 389 info = [ 376 390 f"pos: ({char_x}, {char_y}) vel: ({svx}, {svy})", 377 391 f"squish: {squish} ground: {'yes' if on_ground else 'no'}",