1
fork

Configure Feed

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

feat: add level builder

+1048 -559
+213
level.json
··· 1 + { 2 + "level": "level", 3 + "world": { 4 + "w": 1500, 5 + "h": 1500 6 + }, 7 + "obstacles": [ 8 + { 9 + "l": 60, 10 + "t": 1430, 11 + "r": 150, 12 + "b": 1440 13 + }, 14 + { 15 + "l": 250, 16 + "t": 1380, 17 + "r": 360, 18 + "b": 1390 19 + }, 20 + { 21 + "l": 490, 22 + "t": 1420, 23 + "r": 620, 24 + "b": 1430 25 + }, 26 + { 27 + "l": 690, 28 + "t": 1360, 29 + "r": 980, 30 + "b": 1370 31 + }, 32 + { 33 + "l": 460, 34 + "t": 1220, 35 + "r": 620, 36 + "b": 1230 37 + }, 38 + { 39 + "l": 100, 40 + "t": 1170, 41 + "r": 270, 42 + "b": 1180 43 + }, 44 + { 45 + "l": 200, 46 + "t": 980, 47 + "r": 460, 48 + "b": 990 49 + }, 50 + { 51 + "l": 570, 52 + "t": 1060, 53 + "r": 1050, 54 + "b": 1070 55 + }, 56 + { 57 + "l": 930, 58 + "t": 1210, 59 + "r": 1140, 60 + "b": 1220 61 + }, 62 + { 63 + "l": 590, 64 + "t": 480, 65 + "r": 600, 66 + "b": 760 67 + }, 68 + { 69 + "l": 590, 70 + "t": 480, 71 + "r": 790, 72 + "b": 490 73 + }, 74 + { 75 + "l": 790, 76 + "t": 480, 77 + "r": 930, 78 + "b": 490 79 + }, 80 + { 81 + "l": 920, 82 + "t": 500, 83 + "r": 930, 84 + "b": 760 85 + }, 86 + { 87 + "l": 920, 88 + "t": 490, 89 + "r": 930, 90 + "b": 500 91 + }, 92 + { 93 + "l": 590, 94 + "t": 760, 95 + "r": 820, 96 + "b": 770 97 + }, 98 + { 99 + "l": 720, 100 + "t": 640, 101 + "r": 820, 102 + "b": 650 103 + }, 104 + { 105 + "l": 650, 106 + "t": 570, 107 + "r": 730, 108 + "b": 580 109 + }, 110 + { 111 + "l": 310, 112 + "t": 800, 113 + "r": 510, 114 + "b": 810 115 + }, 116 + { 117 + "l": 70, 118 + "t": 620, 119 + "r": 180, 120 + "b": 640 121 + }, 122 + { 123 + "l": 190, 124 + "t": 360, 125 + "r": 370, 126 + "b": 410 127 + }, 128 + { 129 + "l": 420, 130 + "t": 540, 131 + "r": 500, 132 + "b": 550 133 + }, 134 + { 135 + "l": 490, 136 + "t": 200, 137 + "r": 650, 138 + "b": 220 139 + }, 140 + { 141 + "l": 200, 142 + "t": 170, 143 + "r": 300, 144 + "b": 180 145 + }, 146 + { 147 + "l": 1010, 148 + "t": 220, 149 + "r": 1170, 150 + "b": 290 151 + }, 152 + { 153 + "l": 780, 154 + "t": 280, 155 + "r": 890, 156 + "b": 290 157 + }, 158 + { 159 + "l": 1120, 160 + "t": 580, 161 + "r": 1280, 162 + "b": 590 163 + }, 164 + { 165 + "l": 990, 166 + "t": 770, 167 + "r": 1220, 168 + "b": 790 169 + }, 170 + { 171 + "l": 940, 172 + "t": 970, 173 + "r": 1340, 174 + "b": 980 175 + }, 176 + { 177 + "l": 1280, 178 + "t": 850, 179 + "r": 1350, 180 + "b": 890 181 + }, 182 + { 183 + "l": 640, 184 + "t": 910, 185 + "r": 700, 186 + "b": 940 187 + }, 188 + { 189 + "l": 1250, 190 + "t": 350, 191 + "r": 1330, 192 + "b": 380 193 + }, 194 + { 195 + "l": 1200, 196 + "t": 1360, 197 + "r": 1300, 198 + "b": 1380 199 + }, 200 + { 201 + "l": 1330, 202 + "t": 1220, 203 + "r": 1400, 204 + "b": 1250 205 + }, 206 + { 207 + "l": 1270, 208 + "t": 1100, 209 + "r": 1430, 210 + "b": 1140 211 + } 212 + ] 213 + }
+580
level_builder.py
··· 1 + #!/usr/bin/env python3 2 + """ 3 + Level Builder for VHDL Bouncy Game 4 + World: 1500x1500 px | VGA viewport: 640x480 5 + 6 + Controls: 7 + Left-drag draw new platform (snapped to grid) 8 + Right-click delete platform under cursor 9 + Click select platform (shown in orange) 10 + Del/Bksp delete selected 11 + Middle-drag pan view 12 + Scroll zoom 13 + S toggle grid snap 14 + E export JSON + VHDL package 15 + L load level.json 16 + Q / close quit 17 + 18 + Export writes two files: 19 + <name>.json – reloadable level data 20 + <name>_pkg.vhd – VHDL package (use work.<name>_pkg.all) 21 + 22 + Prerequisites: pip install pygame 23 + """ 24 + 25 + import pygame 26 + import json 27 + import sys 28 + from pathlib import Path 29 + 30 + # ── Window layout ────────────────────────────────────────────────────────── 31 + WINDOW_W, WINDOW_H = 1280, 820 32 + SIDEBAR_W = 270 33 + VIEW_X = SIDEBAR_W # left edge of the world canvas 34 + VIEW_W = WINDOW_W - SIDEBAR_W 35 + VIEW_H = WINDOW_H 36 + 37 + # ── World constants (must match VHDL) ────────────────────────────────────── 38 + WORLD_W, WORLD_H = 1500, 1500 39 + WALL_L = 8 # physics LEFT_WALL 40 + WALL_R = 1492 # physics RIGHT_WALL 41 + CEIL_Y = 16 # physics CEILING (character can't go above) 42 + GROUND_Y = 1480 # physics GROUND (character center lands here) 43 + CEIL_VIS = 8 # renderer CEIL_BOT 44 + GROUND_VIS= 1488 # renderer GROUND_TOP 45 + CHAR_SIZE = 7 # physics SIZE (half-width/height) 46 + 47 + GRID = 10 # snap grid in world pixels 48 + MIN_DIM = 10 # minimum platform width or height 49 + 50 + # ── Colours ──────────────────────────────────────────────────────────────── 51 + C_BG = (18, 18, 18) 52 + C_SIDEBAR = (28, 28, 28) 53 + C_SIDEBAR_SEP= (55, 55, 55) 54 + C_SKY = (20, 20, 40) 55 + C_GROUND = (20, 80, 20) 56 + C_CEIL_STRIP = (20, 80, 20) 57 + C_WALL = (0, 70, 70) 58 + C_OBS = (210, 210, 0) 59 + C_OBS_SEL = (255, 130, 0) 60 + C_OBS_GHOST = (255, 255, 100, 70) 61 + C_OBS_BORDER = (255, 255, 255) 62 + C_VIEWPORT = (80, 100, 220) 63 + C_GRID = (35, 35, 45) 64 + C_TEXT = (210, 210, 210) 65 + C_TEXT_DIM = (140, 140, 140) 66 + C_TITLE = (255, 210, 60) 67 + C_ACCENT = (100, 200, 255) 68 + C_OK = (80, 200, 80) 69 + C_WARN = (220, 80, 80) 70 + 71 + 72 + class LevelBuilder: 73 + def __init__(self, level_name: str = "level"): 74 + pygame.init() 75 + self.screen = pygame.display.set_mode((WINDOW_W, WINDOW_H)) 76 + pygame.display.set_caption("Level Builder — VHDL Bouncy Game") 77 + self.clock = pygame.time.Clock() 78 + 79 + self.font = pygame.font.SysFont("monospace", 13) 80 + self.font_sm = pygame.font.SysFont("monospace", 11) 81 + self.font_lg = pygame.font.SysFont("monospace", 15, bold=True) 82 + 83 + self.level_name = level_name 84 + 85 + # ── view state ── 86 + fit = min(VIEW_W / WORLD_W, VIEW_H / WORLD_H) * 0.90 87 + self.zoom = fit 88 + self.pan_x = VIEW_X + (VIEW_W - WORLD_W * fit) / 2 89 + self.pan_y = (VIEW_H - WORLD_H * fit) / 2 90 + self._panning = False 91 + self._pan_last = (0, 0) 92 + 93 + # ── edit state ── 94 + self.snap = True 95 + self._drawing = False 96 + self._draw_start= None # world coords (snapped) 97 + 98 + self.obstacles = [] # list of [l, t, r, b] 99 + self.selected = None # index or None 100 + self.status = "Ready" 101 + self.status_ok = True 102 + 103 + # ═══════════════════════════════════════════════════════ coordinate utils 104 + 105 + def w2s(self, wx, wy): 106 + """World → screen.""" 107 + return (self.pan_x + wx * self.zoom, 108 + self.pan_y + wy * self.zoom) 109 + 110 + def s2w(self, sx, sy): 111 + """Screen → world (float).""" 112 + return ((sx - self.pan_x) / self.zoom, 113 + (sy - self.pan_y) / self.zoom) 114 + 115 + def _snap(self, v): 116 + return round(v / GRID) * GRID if self.snap else int(round(v)) 117 + 118 + def _clamp_wx(self, v): 119 + return max(WALL_L, min(WALL_R, v)) 120 + 121 + def _clamp_wy(self, v): 122 + return max(CEIL_Y, min(GROUND_Y, v)) 123 + 124 + def _obs_at(self, wx, wy): 125 + """Index of topmost obstacle under world point, or None.""" 126 + for i in range(len(self.obstacles) - 1, -1, -1): 127 + l, t, r, b = self.obstacles[i] 128 + if l <= wx <= r and t <= wy <= b: 129 + return i 130 + return None 131 + 132 + # ═══════════════════════════════════════════════════════════ main loop 133 + 134 + def run(self): 135 + while True: 136 + if not self._handle_events(): 137 + break 138 + self._draw() 139 + pygame.display.flip() 140 + self.clock.tick(60) 141 + pygame.quit() 142 + 143 + # ═══════════════════════════════════════════════════════ event handling 144 + 145 + def _handle_events(self): 146 + for ev in pygame.event.get(): 147 + if ev.type == pygame.QUIT: 148 + return False 149 + 150 + elif ev.type == pygame.KEYDOWN: 151 + if ev.key == pygame.K_q: 152 + return False 153 + elif ev.key == pygame.K_ESCAPE: 154 + self.selected = None 155 + self._drawing = False 156 + elif ev.key == pygame.K_s: 157 + self.snap = not self.snap 158 + self._set_status(f"Snap {'ON' if self.snap else 'OFF'}") 159 + elif ev.key == pygame.K_e: 160 + self.export() 161 + elif ev.key == pygame.K_l: 162 + self.load() 163 + elif ev.key in (pygame.K_DELETE, pygame.K_BACKSPACE): 164 + self._delete_selected() 165 + 166 + elif ev.type == pygame.MOUSEBUTTONDOWN: 167 + if not self._in_view(ev.pos): 168 + continue 169 + if ev.button == 2: 170 + self._panning = True 171 + self._pan_last = ev.pos 172 + elif ev.button == 1: 173 + self._on_lmb_down(ev.pos) 174 + elif ev.button == 3: 175 + self._on_rmb_down(ev.pos) 176 + 177 + elif ev.type == pygame.MOUSEBUTTONUP: 178 + if ev.button == 2: 179 + self._panning = False 180 + elif ev.button == 1 and self._drawing: 181 + self._on_lmb_up(ev.pos) 182 + 183 + elif ev.type == pygame.MOUSEMOTION: 184 + if self._panning: 185 + dx = ev.pos[0] - self._pan_last[0] 186 + dy = ev.pos[1] - self._pan_last[1] 187 + self.pan_x += dx 188 + self.pan_y += dy 189 + self._pan_last = ev.pos 190 + 191 + elif ev.type == pygame.MOUSEWHEEL: 192 + if self._in_view(pygame.mouse.get_pos()): 193 + self._do_zoom(ev.y, pygame.mouse.get_pos()) 194 + 195 + return True 196 + 197 + def _in_view(self, pos): 198 + return pos[0] >= VIEW_X 199 + 200 + def _on_lmb_down(self, pos): 201 + wx, wy = self.s2w(*pos) 202 + hit = self._obs_at(wx, wy) 203 + if hit is not None: 204 + self.selected = hit 205 + self._drawing = False 206 + else: 207 + self.selected = None 208 + self._drawing = True 209 + self._draw_start = ( 210 + self._clamp_wx(self._snap(wx)), 211 + self._clamp_wy(self._snap(wy)), 212 + ) 213 + 214 + def _on_lmb_up(self, pos): 215 + self._drawing = False 216 + if self._draw_start is None: 217 + return 218 + wx, wy = self.s2w(*pos) 219 + ex = self._clamp_wx(self._snap(wx)) 220 + ey = self._clamp_wy(self._snap(wy)) 221 + x0, y0 = self._draw_start 222 + l, r = sorted([x0, ex]) 223 + t, b = sorted([y0, ey]) 224 + if r - l >= MIN_DIM and b - t >= MIN_DIM: 225 + self.obstacles.append([l, t, r, b]) 226 + self.selected = len(self.obstacles) - 1 227 + self._set_status(f"Added platform {len(self.obstacles)-1} ({r-l}×{b-t}px)") 228 + else: 229 + self._set_status("Too small — drag further to create platform", ok=False) 230 + self._draw_start = None 231 + 232 + def _on_rmb_down(self, pos): 233 + wx, wy = self.s2w(*pos) 234 + hit = self._obs_at(wx, wy) 235 + if hit is not None: 236 + self.obstacles.pop(hit) 237 + if self.selected == hit: 238 + self.selected = None 239 + elif self.selected is not None and self.selected > hit: 240 + self.selected -= 1 241 + self._set_status(f"Deleted platform {hit}") 242 + 243 + def _delete_selected(self): 244 + if self.selected is not None and self.selected < len(self.obstacles): 245 + self.obstacles.pop(self.selected) 246 + self._set_status(f"Deleted platform {self.selected}") 247 + self.selected = None 248 + 249 + def _do_zoom(self, direction, pivot): 250 + factor = 1.15 if direction > 0 else (1 / 1.15) 251 + new_zoom = max(0.04, min(6.0, self.zoom * factor)) 252 + mx, my = pivot 253 + self.pan_x = mx - (mx - self.pan_x) * (new_zoom / self.zoom) 254 + self.pan_y = my - (my - self.pan_y) * (new_zoom / self.zoom) 255 + self.zoom = new_zoom 256 + 257 + def _set_status(self, msg, ok=True): 258 + self.status = msg 259 + self.status_ok = ok 260 + 261 + # ═══════════════════════════════════════════════════════════ rendering 262 + 263 + def _draw(self): 264 + self.screen.fill(C_BG) 265 + 266 + clip = pygame.Rect(VIEW_X, 0, VIEW_W, VIEW_H) 267 + self.screen.set_clip(clip) 268 + self._draw_world() 269 + self.screen.set_clip(None) 270 + 271 + self._draw_sidebar() 272 + self._draw_cursor_coords() 273 + 274 + def _draw_world(self): 275 + z = self.zoom 276 + 277 + def r(wx, wy, ww, wh, color, border=0, border_color=None): 278 + sx, sy = self.w2s(wx, wy) 279 + sw = max(1, ww * z) 280 + sh = max(1, wh * z) 281 + rect = pygame.Rect(sx, sy, sw, sh) 282 + pygame.draw.rect(self.screen, color, rect) 283 + if border: 284 + pygame.draw.rect(self.screen, border_color or C_OBS_BORDER, rect, border) 285 + 286 + # Sky 287 + r(WALL_L, CEIL_VIS, WALL_R - WALL_L, GROUND_VIS - CEIL_VIS, C_SKY) 288 + # Ground 289 + r(0, GROUND_VIS, WORLD_W, WORLD_H - GROUND_VIS, C_GROUND) 290 + # Ceiling 291 + r(WALL_L, 0, WALL_R - WALL_L, CEIL_VIS, C_CEIL_STRIP) 292 + # Left wall 293 + r(0, 0, WALL_L, WORLD_H, C_WALL) 294 + # Right wall 295 + r(WALL_R, 0, WORLD_W - WALL_R, WORLD_H, C_WALL) 296 + 297 + # Grid 298 + if z >= 0.35: 299 + self._draw_grid() 300 + 301 + # Viewport indicator (blue box showing what FPGA screen would show at start pos) 302 + cam_x = max(0, min(860, 100 - 320)) # start pos x=100 303 + cam_y = max(0, min(1020, 1400 - 240)) # start pos y=1400 304 + vp_sl = self.w2s(cam_x, cam_y) 305 + vp_br = self.w2s(cam_x + 640, cam_y + 480) 306 + pygame.draw.rect(self.screen, C_VIEWPORT, 307 + pygame.Rect(vp_sl[0], vp_sl[1], 308 + vp_br[0] - vp_sl[0], vp_br[1] - vp_sl[1]), 1) 309 + 310 + # Platforms 311 + for i, (l, t, rr, b) in enumerate(self.obstacles): 312 + color = C_OBS_SEL if i == self.selected else C_OBS 313 + r(l, t, rr - l, b - t, color, 1) 314 + 315 + # Ghost while drawing 316 + if self._drawing and self._draw_start: 317 + mx, my = pygame.mouse.get_pos() 318 + wx, wy = self.s2w(mx, my) 319 + ex = self._clamp_wx(self._snap(wx)) 320 + ey = self._clamp_wy(self._snap(wy)) 321 + x0, y0 = self._draw_start 322 + gl, gr = sorted([x0, ex]) 323 + gt, gb = sorted([y0, ey]) 324 + sl = self.w2s(gl, gt) 325 + sr = self.w2s(gr, gb) 326 + gw, gh = max(1, sr[0] - sl[0]), max(1, sr[1] - sl[1]) 327 + ghost = pygame.Surface((gw, gh), pygame.SRCALPHA) 328 + ghost.fill(C_OBS_GHOST) 329 + self.screen.blit(ghost, (sl[0], sl[1])) 330 + pygame.draw.rect(self.screen, (255, 255, 100), 331 + pygame.Rect(sl[0], sl[1], gw, gh), 1) 332 + # Size label 333 + lbl = self.font_sm.render(f"{gr-gl}×{gb-gt}", True, (255, 255, 150)) 334 + self.screen.blit(lbl, (sl[0] + 2, sl[1] + 2)) 335 + 336 + def _draw_grid(self): 337 + g = GRID * self.zoom 338 + if g < 3: 339 + return 340 + sx0, sy0 = self.w2s(0, 0) 341 + sx1, sy1 = self.w2s(WORLD_W, WORLD_H) 342 + wx = 0 343 + while wx <= WORLD_W: 344 + sx = int(self.pan_x + wx * self.zoom) 345 + if VIEW_X <= sx <= VIEW_X + VIEW_W: 346 + pygame.draw.line(self.screen, C_GRID, 347 + (sx, max(0, int(sy0))), (sx, min(VIEW_H, int(sy1)))) 348 + wx += GRID 349 + wy = 0 350 + while wy <= WORLD_H: 351 + sy = int(self.pan_y + wy * self.zoom) 352 + if 0 <= sy <= VIEW_H: 353 + pygame.draw.line(self.screen, C_GRID, 354 + (max(VIEW_X, int(sx0)), sy), (min(VIEW_X + VIEW_W, int(sx1)), sy)) 355 + wy += GRID 356 + 357 + def _draw_sidebar(self): 358 + pygame.draw.rect(self.screen, C_SIDEBAR, 359 + pygame.Rect(0, 0, SIDEBAR_W, WINDOW_H)) 360 + pygame.draw.line(self.screen, C_SIDEBAR_SEP, 361 + (SIDEBAR_W - 1, 0), (SIDEBAR_W - 1, WINDOW_H)) 362 + 363 + y = [10] 364 + 365 + def line(text, color=C_TEXT, font=None): 366 + f = font or self.font 367 + s = f.render(text, True, color) 368 + self.screen.blit(s, (8, y[0])) 369 + y[0] += s.get_height() + 3 370 + 371 + def gap(n=6): 372 + y[0] += n 373 + 374 + line(f"LEVEL: {self.level_name}", C_TITLE, self.font_lg) 375 + gap() 376 + line(f"Platforms: {len(self.obstacles)}", C_TEXT_DIM) 377 + line(f"Snap {GRID}px: {'ON' if self.snap else 'OFF'} [S]", C_TEXT_DIM) 378 + gap(10) 379 + 380 + line("CONTROLS", C_ACCENT, self.font_lg) 381 + gap(2) 382 + for ctrl, desc in [ 383 + ("L-drag", "draw platform"), 384 + ("R-click", "delete"), 385 + ("Click", "select"), 386 + ("Del", "delete selected"), 387 + ("M-drag", "pan"), 388 + ("Scroll", "zoom"), 389 + ("S", "toggle snap"), 390 + ("E", "export"), 391 + ("L", "load level.json"), 392 + ("Q", "quit"), 393 + ]: 394 + row = f" {ctrl:<10}{desc}" 395 + line(row, C_TEXT_DIM, self.font_sm) 396 + 397 + gap(10) 398 + line("PLATFORMS", C_ACCENT, self.font_lg) 399 + gap(2) 400 + 401 + # Scrollable obstacle list 402 + avail_h = WINDOW_H - y[0] - 50 403 + row_h = 14 404 + max_rows = avail_h // row_h 405 + total = len(self.obstacles) 406 + start = max(0, total - max_rows) 407 + 408 + if start > 0: 409 + line(f" … {start} more above …", C_TEXT_DIM, self.font_sm) 410 + 411 + for i in range(start, total): 412 + l, t, rr, b = self.obstacles[i] 413 + w, h = rr - l, b - t 414 + txt = f" {i:2d} ({l:4d},{t:4d}) {w:3d}×{h:2d}" 415 + color = C_OBS_SEL if i == self.selected else C_TEXT_DIM 416 + s = self.font_sm.render(txt, True, color) 417 + self.screen.blit(s, (0, y[0])) 418 + y[0] += row_h 419 + if y[0] > WINDOW_H - 50: 420 + break 421 + 422 + # Status bar 423 + status_color = C_OK if self.status_ok else C_WARN 424 + s = self.font_sm.render(self.status, True, status_color) 425 + self.screen.blit(s, (4, WINDOW_H - 34)) 426 + 427 + # Export button hint 428 + hint = self.font_sm.render("[E] Export JSON + VHDL pkg", True, C_OK) 429 + self.screen.blit(hint, (4, WINDOW_H - 18)) 430 + 431 + def _draw_cursor_coords(self): 432 + mx, my = pygame.mouse.get_pos() 433 + if mx < VIEW_X: 434 + return 435 + wx, wy = self.s2w(mx, my) 436 + txt = f"world ({int(wx)}, {int(wy)}) zoom {self.zoom:.2f}×" 437 + s = self.font_sm.render(txt, True, (100, 100, 120)) 438 + self.screen.blit(s, (VIEW_X + 4, WINDOW_H - 16)) 439 + 440 + # ═══════════════════════════════════════════════════════════ import/export 441 + 442 + def export(self, stem: str = None): 443 + stem = stem or self.level_name 444 + json_path = Path(f"{stem}.json") 445 + vhdl_path = Path(f"{stem}_pkg.vhd") 446 + 447 + # JSON 448 + data = { 449 + "level": stem, 450 + "world": {"w": WORLD_W, "h": WORLD_H}, 451 + "obstacles": [ 452 + {"l": l, "t": t, "r": r, "b": b} 453 + for l, t, r, b in self.obstacles 454 + ], 455 + } 456 + json_path.write_text(json.dumps(data, indent=2)) 457 + 458 + # VHDL package 459 + self._write_vhdl_pkg(vhdl_path, stem) 460 + 461 + self._set_status( 462 + f"Exported {len(self.obstacles)} obstacles → {json_path}, {vhdl_path}" 463 + ) 464 + print(f"[export] {json_path} {vhdl_path}") 465 + 466 + def _write_vhdl_pkg(self, path: Path, pkg_name: str): 467 + n = len(self.obstacles) 468 + pkg = pkg_name.replace("-", "_").replace(" ", "_") 469 + 470 + def arr(field_idx): 471 + if n == 0: 472 + return "(others => (others => '0'))" 473 + vals = ",\n ".join( 474 + f"CONV_STD_LOGIC_VECTOR({self.obstacles[i][field_idx]}, 11)" 475 + for i in range(n) 476 + ) 477 + return f"(\n {vals}\n )" 478 + 479 + content = f"""\ 480 + -- ======================================================================= 481 + -- Level package: {pkg} 482 + -- Auto-generated by level_builder.py — do not edit by hand. 483 + -- 484 + -- Usage in physics_engine / renderer: 485 + -- library work; 486 + -- use work.{pkg}.all; 487 + -- 488 + -- Then iterate: 489 + -- for i in 0 to OBS_COUNT-1 loop 490 + -- if c_right >= OBS_L(i) and ... then <collision logic> end if; 491 + -- end loop; 492 + -- ======================================================================= 493 + library IEEE; 494 + use IEEE.STD_LOGIC_1164.all; 495 + use IEEE.STD_LOGIC_ARITH.all; 496 + use IEEE.STD_LOGIC_UNSIGNED.all; 497 + 498 + package {pkg} is 499 + 500 + constant OBS_COUNT : integer := {n}; 501 + 502 + -- Obstacle coordinate arrays (11-bit world-space, left/top/right/bottom) 503 + type obs_arr_t is array(0 to OBS_COUNT-1) of std_logic_vector(10 downto 0); 504 + 505 + constant OBS_L : obs_arr_t := {arr(0)}; 506 + constant OBS_T : obs_arr_t := {arr(1)}; 507 + constant OBS_R : obs_arr_t := {arr(2)}; 508 + constant OBS_B : obs_arr_t := {arr(3)}; 509 + 510 + end package {pkg}; 511 + """ 512 + path.write_text(content) 513 + 514 + def load(self, stem: str = None): 515 + stem = stem or self.level_name 516 + path = Path(f"{stem}.json") 517 + if not path.exists(): 518 + # Try bare "level.json" as fallback 519 + path = Path("level.json") 520 + if not path.exists(): 521 + self._set_status(f"File not found: {stem}.json", ok=False) 522 + return 523 + try: 524 + data = json.loads(path.read_text()) 525 + self.obstacles = [ 526 + [o["l"], o["t"], o["r"], o["b"]] 527 + for o in data.get("obstacles", []) 528 + ] 529 + self.selected = None 530 + self._set_status( 531 + f"Loaded {len(self.obstacles)} obstacles from {path}" 532 + ) 533 + if "level" in data: 534 + self.level_name = data["level"] 535 + pygame.display.set_caption( 536 + f"Level Builder — {self.level_name}" 537 + ) 538 + except Exception as e: 539 + self._set_status(f"Load error: {e}", ok=False) 540 + 541 + 542 + # ═══════════════════════════════════════════════════════════════════════ main 543 + 544 + def main(): 545 + name = sys.argv[1] if len(sys.argv) > 1 else "level" 546 + builder = LevelBuilder(level_name=name) 547 + 548 + # Pre-populate with the current built-in level so you can tweak it 549 + builder.obstacles = [ 550 + # bottom zone 551 + [100, 1380, 280, 1396], 552 + [450, 1320, 530, 1336], 553 + [750, 1390, 950, 1406], 554 + [1150,1300,1380,1316], 555 + # mid-lower 556 + [60, 1100, 250, 1116], 557 + [400, 1050, 550, 1066], 558 + [720, 1000, 920, 1016], 559 + [1100,1120,1350,1136], 560 + # mid-upper 561 + [150, 780, 380, 796], 562 + [550, 700, 650, 716], 563 + [850, 650,1050, 666], 564 + [1200, 760,1450, 776], 565 + # upper 566 + [200, 450, 420, 466], 567 + [620, 380, 820, 396], 568 + [1000, 320,1200, 336], 569 + [1320, 420,1460, 436], 570 + ] 571 + 572 + # Auto-load JSON if it exists (overrides pre-populated data) 573 + if Path(f"{name}.json").exists(): 574 + builder.load(name) 575 + 576 + builder.run() 577 + 578 + 579 + if __name__ == "__main__": 580 + main()
+171
level_pkg.vhd
··· 1 + -- ======================================================================= 2 + -- Level package: level 3 + -- Auto-generated by level_builder.py — do not edit by hand. 4 + -- 5 + -- Usage in physics_engine / renderer: 6 + -- library work; 7 + -- use work.level.all; 8 + -- 9 + -- Then iterate: 10 + -- for i in 0 to OBS_COUNT-1 loop 11 + -- if c_right >= OBS_L(i) and ... then <collision logic> end if; 12 + -- end loop; 13 + -- ======================================================================= 14 + library IEEE; 15 + use IEEE.STD_LOGIC_1164.all; 16 + use IEEE.STD_LOGIC_ARITH.all; 17 + use IEEE.STD_LOGIC_UNSIGNED.all; 18 + 19 + package level is 20 + 21 + constant OBS_COUNT : integer := 34; 22 + 23 + -- Obstacle coordinate arrays (11-bit world-space, left/top/right/bottom) 24 + type obs_arr_t is array(0 to OBS_COUNT-1) of std_logic_vector(10 downto 0); 25 + 26 + constant OBS_L : obs_arr_t := ( 27 + CONV_STD_LOGIC_VECTOR(60, 11), 28 + CONV_STD_LOGIC_VECTOR(250, 11), 29 + CONV_STD_LOGIC_VECTOR(490, 11), 30 + CONV_STD_LOGIC_VECTOR(690, 11), 31 + CONV_STD_LOGIC_VECTOR(460, 11), 32 + CONV_STD_LOGIC_VECTOR(100, 11), 33 + CONV_STD_LOGIC_VECTOR(200, 11), 34 + CONV_STD_LOGIC_VECTOR(570, 11), 35 + CONV_STD_LOGIC_VECTOR(930, 11), 36 + CONV_STD_LOGIC_VECTOR(590, 11), 37 + CONV_STD_LOGIC_VECTOR(590, 11), 38 + CONV_STD_LOGIC_VECTOR(790, 11), 39 + CONV_STD_LOGIC_VECTOR(920, 11), 40 + CONV_STD_LOGIC_VECTOR(920, 11), 41 + CONV_STD_LOGIC_VECTOR(590, 11), 42 + CONV_STD_LOGIC_VECTOR(720, 11), 43 + CONV_STD_LOGIC_VECTOR(650, 11), 44 + CONV_STD_LOGIC_VECTOR(310, 11), 45 + CONV_STD_LOGIC_VECTOR(70, 11), 46 + CONV_STD_LOGIC_VECTOR(190, 11), 47 + CONV_STD_LOGIC_VECTOR(420, 11), 48 + CONV_STD_LOGIC_VECTOR(490, 11), 49 + CONV_STD_LOGIC_VECTOR(200, 11), 50 + CONV_STD_LOGIC_VECTOR(1010, 11), 51 + CONV_STD_LOGIC_VECTOR(780, 11), 52 + CONV_STD_LOGIC_VECTOR(1120, 11), 53 + CONV_STD_LOGIC_VECTOR(990, 11), 54 + CONV_STD_LOGIC_VECTOR(940, 11), 55 + CONV_STD_LOGIC_VECTOR(1280, 11), 56 + CONV_STD_LOGIC_VECTOR(640, 11), 57 + CONV_STD_LOGIC_VECTOR(1250, 11), 58 + CONV_STD_LOGIC_VECTOR(1200, 11), 59 + CONV_STD_LOGIC_VECTOR(1330, 11), 60 + CONV_STD_LOGIC_VECTOR(1270, 11) 61 + ); 62 + constant OBS_T : obs_arr_t := ( 63 + CONV_STD_LOGIC_VECTOR(1430, 11), 64 + CONV_STD_LOGIC_VECTOR(1380, 11), 65 + CONV_STD_LOGIC_VECTOR(1420, 11), 66 + CONV_STD_LOGIC_VECTOR(1360, 11), 67 + CONV_STD_LOGIC_VECTOR(1220, 11), 68 + CONV_STD_LOGIC_VECTOR(1170, 11), 69 + CONV_STD_LOGIC_VECTOR(980, 11), 70 + CONV_STD_LOGIC_VECTOR(1060, 11), 71 + CONV_STD_LOGIC_VECTOR(1210, 11), 72 + CONV_STD_LOGIC_VECTOR(480, 11), 73 + CONV_STD_LOGIC_VECTOR(480, 11), 74 + CONV_STD_LOGIC_VECTOR(480, 11), 75 + CONV_STD_LOGIC_VECTOR(500, 11), 76 + CONV_STD_LOGIC_VECTOR(490, 11), 77 + CONV_STD_LOGIC_VECTOR(760, 11), 78 + CONV_STD_LOGIC_VECTOR(640, 11), 79 + CONV_STD_LOGIC_VECTOR(570, 11), 80 + CONV_STD_LOGIC_VECTOR(800, 11), 81 + CONV_STD_LOGIC_VECTOR(620, 11), 82 + CONV_STD_LOGIC_VECTOR(360, 11), 83 + CONV_STD_LOGIC_VECTOR(540, 11), 84 + CONV_STD_LOGIC_VECTOR(200, 11), 85 + CONV_STD_LOGIC_VECTOR(170, 11), 86 + CONV_STD_LOGIC_VECTOR(220, 11), 87 + CONV_STD_LOGIC_VECTOR(280, 11), 88 + CONV_STD_LOGIC_VECTOR(580, 11), 89 + CONV_STD_LOGIC_VECTOR(770, 11), 90 + CONV_STD_LOGIC_VECTOR(970, 11), 91 + CONV_STD_LOGIC_VECTOR(850, 11), 92 + CONV_STD_LOGIC_VECTOR(910, 11), 93 + CONV_STD_LOGIC_VECTOR(350, 11), 94 + CONV_STD_LOGIC_VECTOR(1360, 11), 95 + CONV_STD_LOGIC_VECTOR(1220, 11), 96 + CONV_STD_LOGIC_VECTOR(1100, 11) 97 + ); 98 + constant OBS_R : obs_arr_t := ( 99 + CONV_STD_LOGIC_VECTOR(150, 11), 100 + CONV_STD_LOGIC_VECTOR(360, 11), 101 + CONV_STD_LOGIC_VECTOR(620, 11), 102 + CONV_STD_LOGIC_VECTOR(980, 11), 103 + CONV_STD_LOGIC_VECTOR(620, 11), 104 + CONV_STD_LOGIC_VECTOR(270, 11), 105 + CONV_STD_LOGIC_VECTOR(460, 11), 106 + CONV_STD_LOGIC_VECTOR(1050, 11), 107 + CONV_STD_LOGIC_VECTOR(1140, 11), 108 + CONV_STD_LOGIC_VECTOR(600, 11), 109 + CONV_STD_LOGIC_VECTOR(790, 11), 110 + CONV_STD_LOGIC_VECTOR(930, 11), 111 + CONV_STD_LOGIC_VECTOR(930, 11), 112 + CONV_STD_LOGIC_VECTOR(930, 11), 113 + CONV_STD_LOGIC_VECTOR(820, 11), 114 + CONV_STD_LOGIC_VECTOR(820, 11), 115 + CONV_STD_LOGIC_VECTOR(730, 11), 116 + CONV_STD_LOGIC_VECTOR(510, 11), 117 + CONV_STD_LOGIC_VECTOR(180, 11), 118 + CONV_STD_LOGIC_VECTOR(370, 11), 119 + CONV_STD_LOGIC_VECTOR(500, 11), 120 + CONV_STD_LOGIC_VECTOR(650, 11), 121 + CONV_STD_LOGIC_VECTOR(300, 11), 122 + CONV_STD_LOGIC_VECTOR(1170, 11), 123 + CONV_STD_LOGIC_VECTOR(890, 11), 124 + CONV_STD_LOGIC_VECTOR(1280, 11), 125 + CONV_STD_LOGIC_VECTOR(1220, 11), 126 + CONV_STD_LOGIC_VECTOR(1340, 11), 127 + CONV_STD_LOGIC_VECTOR(1350, 11), 128 + CONV_STD_LOGIC_VECTOR(700, 11), 129 + CONV_STD_LOGIC_VECTOR(1330, 11), 130 + CONV_STD_LOGIC_VECTOR(1300, 11), 131 + CONV_STD_LOGIC_VECTOR(1400, 11), 132 + CONV_STD_LOGIC_VECTOR(1430, 11) 133 + ); 134 + constant OBS_B : obs_arr_t := ( 135 + CONV_STD_LOGIC_VECTOR(1440, 11), 136 + CONV_STD_LOGIC_VECTOR(1390, 11), 137 + CONV_STD_LOGIC_VECTOR(1430, 11), 138 + CONV_STD_LOGIC_VECTOR(1370, 11), 139 + CONV_STD_LOGIC_VECTOR(1230, 11), 140 + CONV_STD_LOGIC_VECTOR(1180, 11), 141 + CONV_STD_LOGIC_VECTOR(990, 11), 142 + CONV_STD_LOGIC_VECTOR(1070, 11), 143 + CONV_STD_LOGIC_VECTOR(1220, 11), 144 + CONV_STD_LOGIC_VECTOR(760, 11), 145 + CONV_STD_LOGIC_VECTOR(490, 11), 146 + CONV_STD_LOGIC_VECTOR(490, 11), 147 + CONV_STD_LOGIC_VECTOR(760, 11), 148 + CONV_STD_LOGIC_VECTOR(500, 11), 149 + CONV_STD_LOGIC_VECTOR(770, 11), 150 + CONV_STD_LOGIC_VECTOR(650, 11), 151 + CONV_STD_LOGIC_VECTOR(580, 11), 152 + CONV_STD_LOGIC_VECTOR(810, 11), 153 + CONV_STD_LOGIC_VECTOR(640, 11), 154 + CONV_STD_LOGIC_VECTOR(410, 11), 155 + CONV_STD_LOGIC_VECTOR(550, 11), 156 + CONV_STD_LOGIC_VECTOR(220, 11), 157 + CONV_STD_LOGIC_VECTOR(180, 11), 158 + CONV_STD_LOGIC_VECTOR(290, 11), 159 + CONV_STD_LOGIC_VECTOR(290, 11), 160 + CONV_STD_LOGIC_VECTOR(590, 11), 161 + CONV_STD_LOGIC_VECTOR(790, 11), 162 + CONV_STD_LOGIC_VECTOR(980, 11), 163 + CONV_STD_LOGIC_VECTOR(890, 11), 164 + CONV_STD_LOGIC_VECTOR(940, 11), 165 + CONV_STD_LOGIC_VECTOR(380, 11), 166 + CONV_STD_LOGIC_VECTOR(1380, 11), 167 + CONV_STD_LOGIC_VECTOR(1250, 11), 168 + CONV_STD_LOGIC_VECTOR(1140, 11) 169 + ); 170 + 171 + end package level;
+70 -452
physics_engine.vhd
··· 1 1 -- ======================================================================== 2 2 -- Physics Engine (scrolling 1500x1500 world) 3 - -- Positions: 11-bit unsigned (0..2047, world fits in 0..1500). 4 - -- Velocities: 10-bit 2's complement (bit 9 = sign), unchanged. 5 - -- Camera: follows character, clamped to world bounds, output for renderer. 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_pkg (use work.level_pkg.all). 6 11 -- ======================================================================== 7 12 8 13 library IEEE; 9 14 use IEEE.STD_LOGIC_1164.all; 10 15 use IEEE.STD_LOGIC_ARITH.all; 11 16 use IEEE.STD_LOGIC_UNSIGNED.all; 17 + use work.level_pkg.all; 12 18 13 19 entity physics_engine is 14 20 port( ··· 36 42 signal cam_x_sig : std_logic_vector(10 downto 0) := (others => '0'); 37 43 signal cam_y_sig : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1020, 11); 38 44 39 - -- SIZE: 10-bit for squish outputs, 11-bit for position arithmetic 45 + -- SIZE: 10-bit for squish output math, 11-bit for position arithmetic 40 46 constant SIZE : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(7, 10); 41 47 constant SIZE11 : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(7, 11); 42 48 ··· 49 55 -- World bounds (11-bit) 50 56 constant GROUND : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1480, 11); 51 57 constant CEILING : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(16, 11); 52 - constant LEFT_WALL : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(15, 11); 58 + constant LEFT_WALL : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(8, 11); 53 59 constant RIGHT_WALL : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1492, 11); 54 60 55 - -- ---- Obstacle constants (L, T, R, B) in world-space (11-bit) ---- 56 - -- Bottom zone (near ground, y ~1300-1420) 57 - constant O1_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(100, 11); 58 - constant O1_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1380, 11); 59 - constant O1_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(280, 11); 60 - constant O1_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1396, 11); 61 - 62 - constant O2_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(450, 11); 63 - constant O2_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1320, 11); 64 - constant O2_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(530, 11); 65 - constant O2_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1336, 11); 66 - 67 - constant O3_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(750, 11); 68 - constant O3_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1390, 11); 69 - constant O3_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(950, 11); 70 - constant O3_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1406, 11); 71 - 72 - constant O4_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1150, 11); 73 - constant O4_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1300, 11); 74 - constant O4_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1380, 11); 75 - constant O4_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1316, 11); 76 - 77 - -- Mid-lower zone (y ~1000-1140) 78 - constant O5_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(60, 11); 79 - constant O5_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1100, 11); 80 - constant O5_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(250, 11); 81 - constant O5_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1116, 11); 82 - 83 - constant O6_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(400, 11); 84 - constant O6_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1050, 11); 85 - constant O6_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(550, 11); 86 - constant O6_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1066, 11); 87 - 88 - constant O7_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(720, 11); 89 - constant O7_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1000, 11); 90 - constant O7_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(920, 11); 91 - constant O7_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1016, 11); 92 - 93 - constant O8_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1100, 11); 94 - constant O8_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1120, 11); 95 - constant O8_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1350, 11); 96 - constant O8_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1136, 11); 97 - 98 - -- Mid-upper zone (y ~650-800) 99 - constant O9_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(150, 11); 100 - constant O9_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(780, 11); 101 - constant O9_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(380, 11); 102 - constant O9_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(796, 11); 103 - 104 - constant O10_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(550, 11); 105 - constant O10_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(700, 11); 106 - constant O10_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(650, 11); 107 - constant O10_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(716, 11); 108 - 109 - constant O11_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(850, 11); 110 - constant O11_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(650, 11); 111 - constant O11_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1050, 11); 112 - constant O11_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(666, 11); 113 - 114 - constant O12_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1200, 11); 115 - constant O12_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(760, 11); 116 - constant O12_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1450, 11); 117 - constant O12_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(776, 11); 118 - 119 - -- Upper zone (y ~320-500) 120 - constant O13_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(200, 11); 121 - constant O13_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(450, 11); 122 - constant O13_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(420, 11); 123 - constant O13_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(466, 11); 124 - 125 - constant O14_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(620, 11); 126 - constant O14_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(380, 11); 127 - constant O14_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(820, 11); 128 - constant O14_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(396, 11); 129 - 130 - constant O15_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1000, 11); 131 - constant O15_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(320, 11); 132 - constant O15_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1200, 11); 133 - constant O15_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(336, 11); 134 - 135 - constant O16_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1320, 11); 136 - constant O16_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(420, 11); 137 - constant O16_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1460, 11); 138 - constant O16_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(436, 11); 61 + -- Underflow sentinel: valid positions top out at 1492; underflow wraps to >= 2000. 62 + -- (Min case: CEILING(16) - max_upward(64) = -48 -> 11-bit unsigned = 2000.) 63 + constant UNDERFLOW : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(2000, 11); 139 64 140 65 -- Animation 141 66 signal squish : std_logic_vector(3 downto 0) := (others => '0'); ··· 156 81 else SIZE + ("000000" & squish); 157 82 158 83 physics : process 159 - variable vx, vy : std_logic_vector(9 downto 0); 160 - variable px, py : std_logic_vector(10 downto 0); 161 - variable bounced : std_logic; 162 - variable bounce_wall : std_logic; 163 - variable bounce_speed: std_logic_vector(9 downto 0); 164 - variable grounded : std_logic; 84 + variable vx, vy : std_logic_vector(9 downto 0); 85 + variable px, py : std_logic_vector(10 downto 0); 86 + variable bounced : std_logic; 87 + variable bounce_wall : std_logic; 88 + variable bounce_speed : std_logic_vector(9 downto 0); 89 + variable grounded : std_logic; 165 90 variable c_left, c_right, c_top, c_bot : std_logic_vector(10 downto 0); 166 91 variable overlap_x, overlap_y : std_logic_vector(10 downto 0); 167 92 begin ··· 236 161 elsif vy(9) = '0' then vy := (others => '0'); end if; 237 162 end if; 238 163 239 - -- == CEILING (py(10)='1' catches negative-wrap underflow) == 240 - if py(10) = '1' or py <= CEILING then 164 + -- == CEILING == 165 + -- Underflow detection: py >= 2000 means it wrapped below zero. 166 + -- (GROUND=1480, RIGHT_WALL=1492 are both < 2000, so no false triggers.) 167 + if py >= UNDERFLOW or py <= CEILING then 241 168 py := CEILING; 242 169 bounced := '1'; 243 170 bounce_speed := (not vy) + 1; ··· 247 174 end if; 248 175 end if; 249 176 250 - -- == LEFT WALL (px(10)='1' catches underflow) == 251 - if px(10) = '1' or px <= LEFT_WALL + SIZE11 then 177 + -- == LEFT WALL == 178 + -- Same underflow fix: px >= 2000 means wrapped below zero. 179 + if px >= UNDERFLOW or px <= LEFT_WALL + SIZE11 then 252 180 px := LEFT_WALL + SIZE11; 253 181 bounced := '1'; bounce_wall := '1'; 254 182 bounce_speed := (not vx) + 1; ··· 272 200 end if; 273 201 end if; 274 202 275 - -- == OBSTACLE COLLISIONS == 276 - -- Shared bounce helpers (vert and horiz) used identically per obstacle. 277 - 278 - -- O1 279 - c_left:=px-SIZE11; c_right:=px+SIZE11; c_top:=py-SIZE11; c_bot:=py+SIZE11; 280 - if c_right>=O1_L and c_left<=O1_R and c_bot>=O1_T and c_top<=O1_B then 281 - if vy(9)='0' then overlap_y:=c_bot-O1_T; else overlap_y:=O1_B-c_top; end if; 282 - if vx(9)='0' then overlap_x:=c_right-O1_L; else overlap_x:=O1_R-c_left; end if; 283 - if overlap_y<=overlap_x then 284 - if vy(9)='0' then py:=O1_T-SIZE11; grounded:='1'; else py:=O1_B+SIZE11; end if; 285 - bounced:='1'; 286 - vy:=(not vy)+1; 287 - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); 288 - elsif vy(9)='1' and vy<CONV_STD_LOGIC_VECTOR(1022,10) then vy:=vy+("000"&((not vy(9 downto 3))+1)); end if; 289 - if vy(9)='0' and vy<2 then vy:=(others=>'0'); end if; 290 - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; 291 - else 292 - if vx(9)='0' then px:=O1_L-SIZE11; else px:=O1_R+SIZE11; end if; 293 - bounced:='1'; bounce_wall:='1'; 294 - vx:=(not vx)+1; 295 - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); 296 - elsif vx(9)='1' and vx<CONV_STD_LOGIC_VECTOR(1022,10) then vx:=vx+("000"&((not vx(9 downto 3))+1)); end if; 297 - end if; 298 - end if; 299 - 300 - -- O2 301 - c_left:=px-SIZE11; c_right:=px+SIZE11; c_top:=py-SIZE11; c_bot:=py+SIZE11; 302 - if c_right>=O2_L and c_left<=O2_R and c_bot>=O2_T and c_top<=O2_B then 303 - if vy(9)='0' then overlap_y:=c_bot-O2_T; else overlap_y:=O2_B-c_top; end if; 304 - if vx(9)='0' then overlap_x:=c_right-O2_L; else overlap_x:=O2_R-c_left; end if; 305 - if overlap_y<=overlap_x then 306 - if vy(9)='0' then py:=O2_T-SIZE11; grounded:='1'; else py:=O2_B+SIZE11; end if; 307 - bounced:='1'; 308 - vy:=(not vy)+1; 309 - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); 310 - elsif vy(9)='1' and vy<CONV_STD_LOGIC_VECTOR(1022,10) then vy:=vy+("000"&((not vy(9 downto 3))+1)); end if; 311 - if vy(9)='0' and vy<2 then vy:=(others=>'0'); end if; 312 - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; 313 - else 314 - if vx(9)='0' then px:=O2_L-SIZE11; else px:=O2_R+SIZE11; end if; 315 - bounced:='1'; bounce_wall:='1'; 316 - vx:=(not vx)+1; 317 - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); 318 - elsif vx(9)='1' and vx<CONV_STD_LOGIC_VECTOR(1022,10) then vx:=vx+("000"&((not vx(9 downto 3))+1)); end if; 319 - end if; 320 - end if; 321 - 322 - -- O3 323 - c_left:=px-SIZE11; c_right:=px+SIZE11; c_top:=py-SIZE11; c_bot:=py+SIZE11; 324 - if c_right>=O3_L and c_left<=O3_R and c_bot>=O3_T and c_top<=O3_B then 325 - if vy(9)='0' then overlap_y:=c_bot-O3_T; else overlap_y:=O3_B-c_top; end if; 326 - if vx(9)='0' then overlap_x:=c_right-O3_L; else overlap_x:=O3_R-c_left; end if; 327 - if overlap_y<=overlap_x then 328 - if vy(9)='0' then py:=O3_T-SIZE11; grounded:='1'; else py:=O3_B+SIZE11; end if; 329 - bounced:='1'; 330 - vy:=(not vy)+1; 331 - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); 332 - elsif vy(9)='1' and vy<CONV_STD_LOGIC_VECTOR(1022,10) then vy:=vy+("000"&((not vy(9 downto 3))+1)); end if; 333 - if vy(9)='0' and vy<2 then vy:=(others=>'0'); end if; 334 - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; 335 - else 336 - if vx(9)='0' then px:=O3_L-SIZE11; else px:=O3_R+SIZE11; end if; 337 - bounced:='1'; bounce_wall:='1'; 338 - vx:=(not vx)+1; 339 - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); 340 - elsif vx(9)='1' and vx<CONV_STD_LOGIC_VECTOR(1022,10) then vx:=vx+("000"&((not vx(9 downto 3))+1)); end if; 341 - end if; 342 - end if; 343 - 344 - -- O4 345 - c_left:=px-SIZE11; c_right:=px+SIZE11; c_top:=py-SIZE11; c_bot:=py+SIZE11; 346 - if c_right>=O4_L and c_left<=O4_R and c_bot>=O4_T and c_top<=O4_B then 347 - if vy(9)='0' then overlap_y:=c_bot-O4_T; else overlap_y:=O4_B-c_top; end if; 348 - if vx(9)='0' then overlap_x:=c_right-O4_L; else overlap_x:=O4_R-c_left; end if; 349 - if overlap_y<=overlap_x then 350 - if vy(9)='0' then py:=O4_T-SIZE11; grounded:='1'; else py:=O4_B+SIZE11; end if; 351 - bounced:='1'; 352 - vy:=(not vy)+1; 353 - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); 354 - elsif vy(9)='1' and vy<CONV_STD_LOGIC_VECTOR(1022,10) then vy:=vy+("000"&((not vy(9 downto 3))+1)); end if; 355 - if vy(9)='0' and vy<2 then vy:=(others=>'0'); end if; 356 - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; 357 - else 358 - if vx(9)='0' then px:=O4_L-SIZE11; else px:=O4_R+SIZE11; end if; 359 - bounced:='1'; bounce_wall:='1'; 360 - vx:=(not vx)+1; 361 - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); 362 - elsif vx(9)='1' and vx<CONV_STD_LOGIC_VECTOR(1022,10) then vx:=vx+("000"&((not vx(9 downto 3))+1)); end if; 363 - end if; 364 - end if; 365 - 366 - -- O5 367 - c_left:=px-SIZE11; c_right:=px+SIZE11; c_top:=py-SIZE11; c_bot:=py+SIZE11; 368 - if c_right>=O5_L and c_left<=O5_R and c_bot>=O5_T and c_top<=O5_B then 369 - if vy(9)='0' then overlap_y:=c_bot-O5_T; else overlap_y:=O5_B-c_top; end if; 370 - if vx(9)='0' then overlap_x:=c_right-O5_L; else overlap_x:=O5_R-c_left; end if; 371 - if overlap_y<=overlap_x then 372 - if vy(9)='0' then py:=O5_T-SIZE11; grounded:='1'; else py:=O5_B+SIZE11; end if; 373 - bounced:='1'; 374 - vy:=(not vy)+1; 375 - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); 376 - elsif vy(9)='1' and vy<CONV_STD_LOGIC_VECTOR(1022,10) then vy:=vy+("000"&((not vy(9 downto 3))+1)); end if; 377 - if vy(9)='0' and vy<2 then vy:=(others=>'0'); end if; 378 - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; 379 - else 380 - if vx(9)='0' then px:=O5_L-SIZE11; else px:=O5_R+SIZE11; end if; 381 - bounced:='1'; bounce_wall:='1'; 382 - vx:=(not vx)+1; 383 - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); 384 - elsif vx(9)='1' and vx<CONV_STD_LOGIC_VECTOR(1022,10) then vx:=vx+("000"&((not vx(9 downto 3))+1)); end if; 385 - end if; 386 - end if; 387 - 388 - -- O6 389 - c_left:=px-SIZE11; c_right:=px+SIZE11; c_top:=py-SIZE11; c_bot:=py+SIZE11; 390 - if c_right>=O6_L and c_left<=O6_R and c_bot>=O6_T and c_top<=O6_B then 391 - if vy(9)='0' then overlap_y:=c_bot-O6_T; else overlap_y:=O6_B-c_top; end if; 392 - if vx(9)='0' then overlap_x:=c_right-O6_L; else overlap_x:=O6_R-c_left; end if; 393 - if overlap_y<=overlap_x then 394 - if vy(9)='0' then py:=O6_T-SIZE11; grounded:='1'; else py:=O6_B+SIZE11; end if; 395 - bounced:='1'; 396 - vy:=(not vy)+1; 397 - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); 398 - elsif vy(9)='1' and vy<CONV_STD_LOGIC_VECTOR(1022,10) then vy:=vy+("000"&((not vy(9 downto 3))+1)); end if; 399 - if vy(9)='0' and vy<2 then vy:=(others=>'0'); end if; 400 - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; 401 - else 402 - if vx(9)='0' then px:=O6_L-SIZE11; else px:=O6_R+SIZE11; end if; 403 - bounced:='1'; bounce_wall:='1'; 404 - vx:=(not vx)+1; 405 - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); 406 - elsif vx(9)='1' and vx<CONV_STD_LOGIC_VECTOR(1022,10) then vx:=vx+("000"&((not vx(9 downto 3))+1)); end if; 407 - end if; 408 - end if; 409 - 410 - -- O7 411 - c_left:=px-SIZE11; c_right:=px+SIZE11; c_top:=py-SIZE11; c_bot:=py+SIZE11; 412 - if c_right>=O7_L and c_left<=O7_R and c_bot>=O7_T and c_top<=O7_B then 413 - if vy(9)='0' then overlap_y:=c_bot-O7_T; else overlap_y:=O7_B-c_top; end if; 414 - if vx(9)='0' then overlap_x:=c_right-O7_L; else overlap_x:=O7_R-c_left; end if; 415 - if overlap_y<=overlap_x then 416 - if vy(9)='0' then py:=O7_T-SIZE11; grounded:='1'; else py:=O7_B+SIZE11; end if; 417 - bounced:='1'; 418 - vy:=(not vy)+1; 419 - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); 420 - elsif vy(9)='1' and vy<CONV_STD_LOGIC_VECTOR(1022,10) then vy:=vy+("000"&((not vy(9 downto 3))+1)); end if; 421 - if vy(9)='0' and vy<2 then vy:=(others=>'0'); end if; 422 - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; 423 - else 424 - if vx(9)='0' then px:=O7_L-SIZE11; else px:=O7_R+SIZE11; end if; 425 - bounced:='1'; bounce_wall:='1'; 426 - vx:=(not vx)+1; 427 - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); 428 - elsif vx(9)='1' and vx<CONV_STD_LOGIC_VECTOR(1022,10) then vx:=vx+("000"&((not vx(9 downto 3))+1)); end if; 429 - end if; 430 - end if; 203 + -- == OBSTACLE COLLISIONS (loop over level_pkg arrays) == 204 + for obs_i in 0 to OBS_COUNT-1 loop 205 + c_left := px - SIZE11; 206 + c_right := px + SIZE11; 207 + c_top := py - SIZE11; 208 + c_bot := py + SIZE11; 431 209 432 - -- O8 433 - c_left:=px-SIZE11; c_right:=px+SIZE11; c_top:=py-SIZE11; c_bot:=py+SIZE11; 434 - if c_right>=O8_L and c_left<=O8_R and c_bot>=O8_T and c_top<=O8_B then 435 - if vy(9)='0' then overlap_y:=c_bot-O8_T; else overlap_y:=O8_B-c_top; end if; 436 - if vx(9)='0' then overlap_x:=c_right-O8_L; else overlap_x:=O8_R-c_left; end if; 437 - if overlap_y<=overlap_x then 438 - if vy(9)='0' then py:=O8_T-SIZE11; grounded:='1'; else py:=O8_B+SIZE11; end if; 439 - bounced:='1'; 440 - vy:=(not vy)+1; 441 - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); 442 - elsif vy(9)='1' and vy<CONV_STD_LOGIC_VECTOR(1022,10) then vy:=vy+("000"&((not vy(9 downto 3))+1)); end if; 443 - if vy(9)='0' and vy<2 then vy:=(others=>'0'); end if; 444 - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; 445 - else 446 - if vx(9)='0' then px:=O8_L-SIZE11; else px:=O8_R+SIZE11; end if; 447 - bounced:='1'; bounce_wall:='1'; 448 - vx:=(not vx)+1; 449 - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); 450 - elsif vx(9)='1' and vx<CONV_STD_LOGIC_VECTOR(1022,10) then vx:=vx+("000"&((not vx(9 downto 3))+1)); end if; 451 - end if; 452 - end if; 210 + if c_right >= OBS_L(obs_i) and c_left <= OBS_R(obs_i) and 211 + c_bot >= OBS_T(obs_i) and c_top <= OBS_B(obs_i) then 453 212 454 - -- O9 455 - c_left:=px-SIZE11; c_right:=px+SIZE11; c_top:=py-SIZE11; c_bot:=py+SIZE11; 456 - if c_right>=O9_L and c_left<=O9_R and c_bot>=O9_T and c_top<=O9_B then 457 - if vy(9)='0' then overlap_y:=c_bot-O9_T; else overlap_y:=O9_B-c_top; end if; 458 - if vx(9)='0' then overlap_x:=c_right-O9_L; else overlap_x:=O9_R-c_left; end if; 459 - if overlap_y<=overlap_x then 460 - if vy(9)='0' then py:=O9_T-SIZE11; grounded:='1'; else py:=O9_B+SIZE11; end if; 461 - bounced:='1'; 462 - vy:=(not vy)+1; 463 - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); 464 - elsif vy(9)='1' and vy<CONV_STD_LOGIC_VECTOR(1022,10) then vy:=vy+("000"&((not vy(9 downto 3))+1)); end if; 465 - if vy(9)='0' and vy<2 then vy:=(others=>'0'); end if; 466 - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; 467 - else 468 - if vx(9)='0' then px:=O9_L-SIZE11; else px:=O9_R+SIZE11; end if; 469 - bounced:='1'; bounce_wall:='1'; 470 - vx:=(not vx)+1; 471 - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); 472 - elsif vx(9)='1' and vx<CONV_STD_LOGIC_VECTOR(1022,10) then vx:=vx+("000"&((not vx(9 downto 3))+1)); end if; 473 - end if; 474 - end if; 213 + if vy(9) = '0' then overlap_y := c_bot - OBS_T(obs_i); 214 + else overlap_y := OBS_B(obs_i) - c_top; end if; 215 + if vx(9) = '0' then overlap_x := c_right - OBS_L(obs_i); 216 + else overlap_x := OBS_R(obs_i) - c_left; end if; 475 217 476 - -- O10 477 - c_left:=px-SIZE11; c_right:=px+SIZE11; c_top:=py-SIZE11; c_bot:=py+SIZE11; 478 - if c_right>=O10_L and c_left<=O10_R and c_bot>=O10_T and c_top<=O10_B then 479 - if vy(9)='0' then overlap_y:=c_bot-O10_T; else overlap_y:=O10_B-c_top; end if; 480 - if vx(9)='0' then overlap_x:=c_right-O10_L; else overlap_x:=O10_R-c_left; end if; 481 - if overlap_y<=overlap_x then 482 - if vy(9)='0' then py:=O10_T-SIZE11; grounded:='1'; else py:=O10_B+SIZE11; end if; 483 - bounced:='1'; 484 - vy:=(not vy)+1; 485 - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); 486 - elsif vy(9)='1' and vy<CONV_STD_LOGIC_VECTOR(1022,10) then vy:=vy+("000"&((not vy(9 downto 3))+1)); end if; 487 - if vy(9)='0' and vy<2 then vy:=(others=>'0'); end if; 488 - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; 489 - else 490 - if vx(9)='0' then px:=O10_L-SIZE11; else px:=O10_R+SIZE11; end if; 491 - bounced:='1'; bounce_wall:='1'; 492 - vx:=(not vx)+1; 493 - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); 494 - elsif vx(9)='1' and vx<CONV_STD_LOGIC_VECTOR(1022,10) then vx:=vx+("000"&((not vx(9 downto 3))+1)); end if; 218 + if overlap_y <= overlap_x then 219 + -- Vertical resolution 220 + if vy(9) = '0' then py := OBS_T(obs_i) - SIZE11; grounded := '1'; 221 + else py := OBS_B(obs_i) + SIZE11; end if; 222 + bounced := '1'; 223 + vy := (not vy) + 1; 224 + if vy(9) = '0' and vy > 1 then 225 + vy := vy - ("000" & vy(9 downto 3)); 226 + elsif vy(9) = '1' and vy < CONV_STD_LOGIC_VECTOR(1022, 10) then 227 + vy := vy + ("000" & ((not vy(9 downto 3)) + 1)); 228 + end if; 229 + if vy(9) = '0' and vy < 2 then vy := (others => '0'); end if; 230 + if vy(9) = '1' and vy >= CONV_STD_LOGIC_VECTOR(1022, 10) then 231 + vy := (others => '0'); 232 + end if; 233 + else 234 + -- Horizontal resolution 235 + if vx(9) = '0' then px := OBS_L(obs_i) - SIZE11; 236 + else px := OBS_R(obs_i) + SIZE11; end if; 237 + bounced := '1'; bounce_wall := '1'; 238 + vx := (not vx) + 1; 239 + if vx(9) = '0' and vx > 1 then 240 + vx := vx - ("000" & vx(9 downto 3)); 241 + elsif vx(9) = '1' and vx < CONV_STD_LOGIC_VECTOR(1022, 10) then 242 + vx := vx + ("000" & ((not vx(9 downto 3)) + 1)); 243 + end if; 244 + end if; 495 245 end if; 496 - end if; 497 - 498 - -- O11 499 - c_left:=px-SIZE11; c_right:=px+SIZE11; c_top:=py-SIZE11; c_bot:=py+SIZE11; 500 - if c_right>=O11_L and c_left<=O11_R and c_bot>=O11_T and c_top<=O11_B then 501 - if vy(9)='0' then overlap_y:=c_bot-O11_T; else overlap_y:=O11_B-c_top; end if; 502 - if vx(9)='0' then overlap_x:=c_right-O11_L; else overlap_x:=O11_R-c_left; end if; 503 - if overlap_y<=overlap_x then 504 - if vy(9)='0' then py:=O11_T-SIZE11; grounded:='1'; else py:=O11_B+SIZE11; end if; 505 - bounced:='1'; 506 - vy:=(not vy)+1; 507 - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); 508 - elsif vy(9)='1' and vy<CONV_STD_LOGIC_VECTOR(1022,10) then vy:=vy+("000"&((not vy(9 downto 3))+1)); end if; 509 - if vy(9)='0' and vy<2 then vy:=(others=>'0'); end if; 510 - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; 511 - else 512 - if vx(9)='0' then px:=O11_L-SIZE11; else px:=O11_R+SIZE11; end if; 513 - bounced:='1'; bounce_wall:='1'; 514 - vx:=(not vx)+1; 515 - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); 516 - elsif vx(9)='1' and vx<CONV_STD_LOGIC_VECTOR(1022,10) then vx:=vx+("000"&((not vx(9 downto 3))+1)); end if; 517 - end if; 518 - end if; 519 - 520 - -- O12 521 - c_left:=px-SIZE11; c_right:=px+SIZE11; c_top:=py-SIZE11; c_bot:=py+SIZE11; 522 - if c_right>=O12_L and c_left<=O12_R and c_bot>=O12_T and c_top<=O12_B then 523 - if vy(9)='0' then overlap_y:=c_bot-O12_T; else overlap_y:=O12_B-c_top; end if; 524 - if vx(9)='0' then overlap_x:=c_right-O12_L; else overlap_x:=O12_R-c_left; end if; 525 - if overlap_y<=overlap_x then 526 - if vy(9)='0' then py:=O12_T-SIZE11; grounded:='1'; else py:=O12_B+SIZE11; end if; 527 - bounced:='1'; 528 - vy:=(not vy)+1; 529 - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); 530 - elsif vy(9)='1' and vy<CONV_STD_LOGIC_VECTOR(1022,10) then vy:=vy+("000"&((not vy(9 downto 3))+1)); end if; 531 - if vy(9)='0' and vy<2 then vy:=(others=>'0'); end if; 532 - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; 533 - else 534 - if vx(9)='0' then px:=O12_L-SIZE11; else px:=O12_R+SIZE11; end if; 535 - bounced:='1'; bounce_wall:='1'; 536 - vx:=(not vx)+1; 537 - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); 538 - elsif vx(9)='1' and vx<CONV_STD_LOGIC_VECTOR(1022,10) then vx:=vx+("000"&((not vx(9 downto 3))+1)); end if; 539 - end if; 540 - end if; 541 - 542 - -- O13 543 - c_left:=px-SIZE11; c_right:=px+SIZE11; c_top:=py-SIZE11; c_bot:=py+SIZE11; 544 - if c_right>=O13_L and c_left<=O13_R and c_bot>=O13_T and c_top<=O13_B then 545 - if vy(9)='0' then overlap_y:=c_bot-O13_T; else overlap_y:=O13_B-c_top; end if; 546 - if vx(9)='0' then overlap_x:=c_right-O13_L; else overlap_x:=O13_R-c_left; end if; 547 - if overlap_y<=overlap_x then 548 - if vy(9)='0' then py:=O13_T-SIZE11; grounded:='1'; else py:=O13_B+SIZE11; end if; 549 - bounced:='1'; 550 - vy:=(not vy)+1; 551 - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); 552 - elsif vy(9)='1' and vy<CONV_STD_LOGIC_VECTOR(1022,10) then vy:=vy+("000"&((not vy(9 downto 3))+1)); end if; 553 - if vy(9)='0' and vy<2 then vy:=(others=>'0'); end if; 554 - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; 555 - else 556 - if vx(9)='0' then px:=O13_L-SIZE11; else px:=O13_R+SIZE11; end if; 557 - bounced:='1'; bounce_wall:='1'; 558 - vx:=(not vx)+1; 559 - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); 560 - elsif vx(9)='1' and vx<CONV_STD_LOGIC_VECTOR(1022,10) then vx:=vx+("000"&((not vx(9 downto 3))+1)); end if; 561 - end if; 562 - end if; 563 - 564 - -- O14 565 - c_left:=px-SIZE11; c_right:=px+SIZE11; c_top:=py-SIZE11; c_bot:=py+SIZE11; 566 - if c_right>=O14_L and c_left<=O14_R and c_bot>=O14_T and c_top<=O14_B then 567 - if vy(9)='0' then overlap_y:=c_bot-O14_T; else overlap_y:=O14_B-c_top; end if; 568 - if vx(9)='0' then overlap_x:=c_right-O14_L; else overlap_x:=O14_R-c_left; end if; 569 - if overlap_y<=overlap_x then 570 - if vy(9)='0' then py:=O14_T-SIZE11; grounded:='1'; else py:=O14_B+SIZE11; end if; 571 - bounced:='1'; 572 - vy:=(not vy)+1; 573 - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); 574 - elsif vy(9)='1' and vy<CONV_STD_LOGIC_VECTOR(1022,10) then vy:=vy+("000"&((not vy(9 downto 3))+1)); end if; 575 - if vy(9)='0' and vy<2 then vy:=(others=>'0'); end if; 576 - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; 577 - else 578 - if vx(9)='0' then px:=O14_L-SIZE11; else px:=O14_R+SIZE11; end if; 579 - bounced:='1'; bounce_wall:='1'; 580 - vx:=(not vx)+1; 581 - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); 582 - elsif vx(9)='1' and vx<CONV_STD_LOGIC_VECTOR(1022,10) then vx:=vx+("000"&((not vx(9 downto 3))+1)); end if; 583 - end if; 584 - end if; 585 - 586 - -- O15 587 - c_left:=px-SIZE11; c_right:=px+SIZE11; c_top:=py-SIZE11; c_bot:=py+SIZE11; 588 - if c_right>=O15_L and c_left<=O15_R and c_bot>=O15_T and c_top<=O15_B then 589 - if vy(9)='0' then overlap_y:=c_bot-O15_T; else overlap_y:=O15_B-c_top; end if; 590 - if vx(9)='0' then overlap_x:=c_right-O15_L; else overlap_x:=O15_R-c_left; end if; 591 - if overlap_y<=overlap_x then 592 - if vy(9)='0' then py:=O15_T-SIZE11; grounded:='1'; else py:=O15_B+SIZE11; end if; 593 - bounced:='1'; 594 - vy:=(not vy)+1; 595 - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); 596 - elsif vy(9)='1' and vy<CONV_STD_LOGIC_VECTOR(1022,10) then vy:=vy+("000"&((not vy(9 downto 3))+1)); end if; 597 - if vy(9)='0' and vy<2 then vy:=(others=>'0'); end if; 598 - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; 599 - else 600 - if vx(9)='0' then px:=O15_L-SIZE11; else px:=O15_R+SIZE11; end if; 601 - bounced:='1'; bounce_wall:='1'; 602 - vx:=(not vx)+1; 603 - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); 604 - elsif vx(9)='1' and vx<CONV_STD_LOGIC_VECTOR(1022,10) then vx:=vx+("000"&((not vx(9 downto 3))+1)); end if; 605 - end if; 606 - end if; 607 - 608 - -- O16 609 - c_left:=px-SIZE11; c_right:=px+SIZE11; c_top:=py-SIZE11; c_bot:=py+SIZE11; 610 - if c_right>=O16_L and c_left<=O16_R and c_bot>=O16_T and c_top<=O16_B then 611 - if vy(9)='0' then overlap_y:=c_bot-O16_T; else overlap_y:=O16_B-c_top; end if; 612 - if vx(9)='0' then overlap_x:=c_right-O16_L; else overlap_x:=O16_R-c_left; end if; 613 - if overlap_y<=overlap_x then 614 - if vy(9)='0' then py:=O16_T-SIZE11; grounded:='1'; else py:=O16_B+SIZE11; end if; 615 - bounced:='1'; 616 - vy:=(not vy)+1; 617 - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); 618 - elsif vy(9)='1' and vy<CONV_STD_LOGIC_VECTOR(1022,10) then vy:=vy+("000"&((not vy(9 downto 3))+1)); end if; 619 - if vy(9)='0' and vy<2 then vy:=(others=>'0'); end if; 620 - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; 621 - else 622 - if vx(9)='0' then px:=O16_L-SIZE11; else px:=O16_R+SIZE11; end if; 623 - bounced:='1'; bounce_wall:='1'; 624 - vx:=(not vx)+1; 625 - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); 626 - elsif vx(9)='1' and vx<CONV_STD_LOGIC_VECTOR(1022,10) then vx:=vx+("000"&((not vx(9 downto 3))+1)); end if; 627 - end if; 628 - end if; 246 + end loop; 629 247 630 248 -- == COMMIT == 631 249 vel_x <= vx; ··· 634 252 pos_y <= py; 635 253 636 254 -- == CAMERA: center on character (px/py), clamp to world == 637 - -- cam_x = clamp(px - 320, 0, 860) [world 1500 - screen 640 = 860] 255 + -- cam_x = clamp(px - 320, 0, 860) [1500 - 640 = 860] 638 256 if px < CONV_STD_LOGIC_VECTOR(320, 11) then 639 257 cam_x_sig <= (others => '0'); 640 258 elsif px > CONV_STD_LOGIC_VECTOR(1180, 11) then ··· 643 261 cam_x_sig <= px - CONV_STD_LOGIC_VECTOR(320, 11); 644 262 end if; 645 263 646 - -- cam_y = clamp(py - 240, 0, 1020) [world 1500 - screen 480 = 1020] 264 + -- cam_y = clamp(py - 240, 0, 1020) [1500 - 480 = 1020] 647 265 if py < CONV_STD_LOGIC_VECTOR(240, 11) then 648 266 cam_y_sig <= (others => '0'); 649 267 elsif py > CONV_STD_LOGIC_VECTOR(1260, 11) then ··· 662 280 end if; 663 281 664 282 if grounded = '1' then on_ground <= '1'; 665 - elsif py < GROUND - 1 then on_ground <= '0'; end if; 283 + else on_ground <= '0'; end if; 666 284 667 285 end process physics; 668 286
+14 -107
renderer.vhd
··· 1 1 -- ======================================================================== 2 2 -- Renderer (scrolling world) 3 - -- Converts screen pixel (col, row) to world coords via camera offset, 4 - -- then tests world-space position against all geometry. 5 - -- 6 3 -- world_col = pixel_column + cam_x 7 4 -- world_row = pixel_row + cam_y 8 - -- 9 - -- All boundary/obstacle constants are in world-space (11-bit). 5 + -- All comparisons in world-space (11-bit). 6 + -- Obstacles imported from level_pkg (use work.level_pkg.all). 10 7 -- ======================================================================== 11 8 12 9 library IEEE; 13 10 use IEEE.STD_LOGIC_1164.all; 14 11 use IEEE.STD_LOGIC_ARITH.all; 15 12 use IEEE.STD_LOGIC_UNSIGNED.all; 13 + use work.level_pkg.all; 16 14 17 15 entity renderer is 18 16 port( ··· 38 36 constant LEFT_WALL : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(8, 11); 39 37 constant RIGHT_WALL : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1492, 11); 40 38 41 - -- Obstacle positions (must match physics_engine, 11-bit world-space) 42 - constant O1_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(100, 11); 43 - constant O1_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1380, 11); 44 - constant O1_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(280, 11); 45 - constant O1_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1396, 11); 46 - 47 - constant O2_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(450, 11); 48 - constant O2_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1320, 11); 49 - constant O2_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(530, 11); 50 - constant O2_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1336, 11); 51 - 52 - constant O3_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(750, 11); 53 - constant O3_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1390, 11); 54 - constant O3_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(950, 11); 55 - constant O3_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1406, 11); 56 - 57 - constant O4_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1150, 11); 58 - constant O4_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1300, 11); 59 - constant O4_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1380, 11); 60 - constant O4_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1316, 11); 61 - 62 - constant O5_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(60, 11); 63 - constant O5_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1100, 11); 64 - constant O5_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(250, 11); 65 - constant O5_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1116, 11); 66 - 67 - constant O6_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(400, 11); 68 - constant O6_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1050, 11); 69 - constant O6_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(550, 11); 70 - constant O6_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1066, 11); 71 - 72 - constant O7_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(720, 11); 73 - constant O7_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1000, 11); 74 - constant O7_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(920, 11); 75 - constant O7_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1016, 11); 76 - 77 - constant O8_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1100, 11); 78 - constant O8_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1120, 11); 79 - constant O8_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1350, 11); 80 - constant O8_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1136, 11); 81 - 82 - constant O9_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(150, 11); 83 - constant O9_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(780, 11); 84 - constant O9_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(380, 11); 85 - constant O9_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(796, 11); 86 - 87 - constant O10_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(550, 11); 88 - constant O10_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(700, 11); 89 - constant O10_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(650, 11); 90 - constant O10_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(716, 11); 91 - 92 - constant O11_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(850, 11); 93 - constant O11_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(650, 11); 94 - constant O11_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1050, 11); 95 - constant O11_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(666, 11); 96 - 97 - constant O12_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1200, 11); 98 - constant O12_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(760, 11); 99 - constant O12_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1450, 11); 100 - constant O12_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(776, 11); 101 - 102 - constant O13_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(200, 11); 103 - constant O13_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(450, 11); 104 - constant O13_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(420, 11); 105 - constant O13_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(466, 11); 106 - 107 - constant O14_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(620, 11); 108 - constant O14_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(380, 11); 109 - constant O14_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(820, 11); 110 - constant O14_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(396, 11); 111 - 112 - constant O15_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1000, 11); 113 - constant O15_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(320, 11); 114 - constant O15_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1200, 11); 115 - constant O15_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(336, 11); 116 - 117 - constant O16_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1320, 11); 118 - constant O16_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(420, 11); 119 - constant O16_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1460, 11); 120 - constant O16_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(436, 11); 121 - 122 39 signal char_on, ground_on, wall_on, ceiling_on, obs_on : std_logic; 123 40 124 41 begin 125 42 126 43 render : process(pixel_row, pixel_column, char_x, char_y, 127 44 char_width, char_height, cam_x, cam_y) 128 - variable wc : std_logic_vector(10 downto 0); -- world column 129 - variable wr : std_logic_vector(10 downto 0); -- world row 45 + variable wc : std_logic_vector(10 downto 0); 46 + variable wr : std_logic_vector(10 downto 0); 130 47 begin 131 48 wc := ('0' & pixel_column) + cam_x; 132 49 wr := ('0' & pixel_row) + cam_y; ··· 145 62 char_on <= '1'; 146 63 end if; 147 64 148 - -- Ground / ceiling / walls (world edges) 149 - if wr >= GROUND_TOP then ground_on <= '1'; end if; 65 + -- World edges 66 + if wr >= GROUND_TOP then ground_on <= '1'; end if; 150 67 if wr <= CEIL_BOT then ceiling_on <= '1'; end if; 151 68 if wc < LEFT_WALL or wc > RIGHT_WALL then wall_on <= '1'; end if; 152 69 153 - -- Obstacles 154 - if wc>=O1_L and wc<=O1_R and wr>=O1_T and wr<=O1_B then obs_on<='1'; end if; 155 - if wc>=O2_L and wc<=O2_R and wr>=O2_T and wr<=O2_B then obs_on<='1'; end if; 156 - if wc>=O3_L and wc<=O3_R and wr>=O3_T and wr<=O3_B then obs_on<='1'; end if; 157 - if wc>=O4_L and wc<=O4_R and wr>=O4_T and wr<=O4_B then obs_on<='1'; end if; 158 - if wc>=O5_L and wc<=O5_R and wr>=O5_T and wr<=O5_B then obs_on<='1'; end if; 159 - if wc>=O6_L and wc<=O6_R and wr>=O6_T and wr<=O6_B then obs_on<='1'; end if; 160 - if wc>=O7_L and wc<=O7_R and wr>=O7_T and wr<=O7_B then obs_on<='1'; end if; 161 - if wc>=O8_L and wc<=O8_R and wr>=O8_T and wr<=O8_B then obs_on<='1'; end if; 162 - if wc>=O9_L and wc<=O9_R and wr>=O9_T and wr<=O9_B then obs_on<='1'; end if; 163 - if wc>=O10_L and wc<=O10_R and wr>=O10_T and wr<=O10_B then obs_on<='1'; end if; 164 - if wc>=O11_L and wc<=O11_R and wr>=O11_T and wr<=O11_B then obs_on<='1'; end if; 165 - if wc>=O12_L and wc<=O12_R and wr>=O12_T and wr<=O12_B then obs_on<='1'; end if; 166 - if wc>=O13_L and wc<=O13_R and wr>=O13_T and wr<=O13_B then obs_on<='1'; end if; 167 - if wc>=O14_L and wc<=O14_R and wr>=O14_T and wr<=O14_B then obs_on<='1'; end if; 168 - if wc>=O15_L and wc<=O15_R and wr>=O15_T and wr<=O15_B then obs_on<='1'; end if; 169 - if wc>=O16_L and wc<=O16_R and wr>=O16_T and wr<=O16_B then obs_on<='1'; end if; 70 + -- Obstacles (loop over level_pkg arrays) 71 + for obs_i in 0 to OBS_COUNT-1 loop 72 + if wc >= OBS_L(obs_i) and wc <= OBS_R(obs_i) and 73 + wr >= OBS_T(obs_i) and wr <= OBS_B(obs_i) then 74 + obs_on <= '1'; 75 + end if; 76 + end loop; 170 77 171 78 end process render; 172 79