From c9d3d69c08ceef135916fd52ca3e86085880fd12 Mon Sep 17 00:00:00 2001 From: Kieran Klukas Date: Wed, 25 Mar 2026 16:54:18 -0400 Subject: [PATCH] feat: add level builder --- level.json | 213 +++++++++++++++++ level_builder.py | 580 +++++++++++++++++++++++++++++++++++++++++++++ level_pkg.vhd | 171 +++++++++++++ physics_engine.vhd | 528 ++++++----------------------------------- renderer.vhd | 121 ++-------- 5 files changed, 1051 insertions(+), 562 deletions(-) create mode 100644 level.json create mode 100644 level_builder.py create mode 100644 level_pkg.vhd diff --git a/level.json b/level.json new file mode 100644 index 0000000..d0133bb --- /dev/null +++ b/level.json @@ -0,0 +1,213 @@ +{ + "level": "level", + "world": { + "w": 1500, + "h": 1500 + }, + "obstacles": [ + { + "l": 60, + "t": 1430, + "r": 150, + "b": 1440 + }, + { + "l": 250, + "t": 1380, + "r": 360, + "b": 1390 + }, + { + "l": 490, + "t": 1420, + "r": 620, + "b": 1430 + }, + { + "l": 690, + "t": 1360, + "r": 980, + "b": 1370 + }, + { + "l": 460, + "t": 1220, + "r": 620, + "b": 1230 + }, + { + "l": 100, + "t": 1170, + "r": 270, + "b": 1180 + }, + { + "l": 200, + "t": 980, + "r": 460, + "b": 990 + }, + { + "l": 570, + "t": 1060, + "r": 1050, + "b": 1070 + }, + { + "l": 930, + "t": 1210, + "r": 1140, + "b": 1220 + }, + { + "l": 590, + "t": 480, + "r": 600, + "b": 760 + }, + { + "l": 590, + "t": 480, + "r": 790, + "b": 490 + }, + { + "l": 790, + "t": 480, + "r": 930, + "b": 490 + }, + { + "l": 920, + "t": 500, + "r": 930, + "b": 760 + }, + { + "l": 920, + "t": 490, + "r": 930, + "b": 500 + }, + { + "l": 590, + "t": 760, + "r": 820, + "b": 770 + }, + { + "l": 720, + "t": 640, + "r": 820, + "b": 650 + }, + { + "l": 650, + "t": 570, + "r": 730, + "b": 580 + }, + { + "l": 310, + "t": 800, + "r": 510, + "b": 810 + }, + { + "l": 70, + "t": 620, + "r": 180, + "b": 640 + }, + { + "l": 190, + "t": 360, + "r": 370, + "b": 410 + }, + { + "l": 420, + "t": 540, + "r": 500, + "b": 550 + }, + { + "l": 490, + "t": 200, + "r": 650, + "b": 220 + }, + { + "l": 200, + "t": 170, + "r": 300, + "b": 180 + }, + { + "l": 1010, + "t": 220, + "r": 1170, + "b": 290 + }, + { + "l": 780, + "t": 280, + "r": 890, + "b": 290 + }, + { + "l": 1120, + "t": 580, + "r": 1280, + "b": 590 + }, + { + "l": 990, + "t": 770, + "r": 1220, + "b": 790 + }, + { + "l": 940, + "t": 970, + "r": 1340, + "b": 980 + }, + { + "l": 1280, + "t": 850, + "r": 1350, + "b": 890 + }, + { + "l": 640, + "t": 910, + "r": 700, + "b": 940 + }, + { + "l": 1250, + "t": 350, + "r": 1330, + "b": 380 + }, + { + "l": 1200, + "t": 1360, + "r": 1300, + "b": 1380 + }, + { + "l": 1330, + "t": 1220, + "r": 1400, + "b": 1250 + }, + { + "l": 1270, + "t": 1100, + "r": 1430, + "b": 1140 + } + ] +} \ No newline at end of file diff --git a/level_builder.py b/level_builder.py new file mode 100644 index 0000000..51653d2 --- /dev/null +++ b/level_builder.py @@ -0,0 +1,580 @@ +#!/usr/bin/env python3 +""" +Level Builder for VHDL Bouncy Game +World: 1500x1500 px | VGA viewport: 640x480 + +Controls: + Left-drag draw new platform (snapped to grid) + Right-click delete platform under cursor + Click select platform (shown in orange) + Del/Bksp delete selected + Middle-drag pan view + Scroll zoom + S toggle grid snap + E export JSON + VHDL package + L load level.json + Q / close quit + +Export writes two files: + .json – reloadable level data + _pkg.vhd – VHDL package (use work._pkg.all) + +Prerequisites: pip install pygame +""" + +import pygame +import json +import sys +from pathlib import Path + +# ── Window layout ────────────────────────────────────────────────────────── +WINDOW_W, WINDOW_H = 1280, 820 +SIDEBAR_W = 270 +VIEW_X = SIDEBAR_W # left edge of the world canvas +VIEW_W = WINDOW_W - SIDEBAR_W +VIEW_H = WINDOW_H + +# ── World constants (must match VHDL) ────────────────────────────────────── +WORLD_W, WORLD_H = 1500, 1500 +WALL_L = 8 # physics LEFT_WALL +WALL_R = 1492 # physics RIGHT_WALL +CEIL_Y = 16 # physics CEILING (character can't go above) +GROUND_Y = 1480 # physics GROUND (character center lands here) +CEIL_VIS = 8 # renderer CEIL_BOT +GROUND_VIS= 1488 # renderer GROUND_TOP +CHAR_SIZE = 7 # physics SIZE (half-width/height) + +GRID = 10 # snap grid in world pixels +MIN_DIM = 10 # minimum platform width or height + +# ── Colours ──────────────────────────────────────────────────────────────── +C_BG = (18, 18, 18) +C_SIDEBAR = (28, 28, 28) +C_SIDEBAR_SEP= (55, 55, 55) +C_SKY = (20, 20, 40) +C_GROUND = (20, 80, 20) +C_CEIL_STRIP = (20, 80, 20) +C_WALL = (0, 70, 70) +C_OBS = (210, 210, 0) +C_OBS_SEL = (255, 130, 0) +C_OBS_GHOST = (255, 255, 100, 70) +C_OBS_BORDER = (255, 255, 255) +C_VIEWPORT = (80, 100, 220) +C_GRID = (35, 35, 45) +C_TEXT = (210, 210, 210) +C_TEXT_DIM = (140, 140, 140) +C_TITLE = (255, 210, 60) +C_ACCENT = (100, 200, 255) +C_OK = (80, 200, 80) +C_WARN = (220, 80, 80) + + +class LevelBuilder: + def __init__(self, level_name: str = "level"): + pygame.init() + self.screen = pygame.display.set_mode((WINDOW_W, WINDOW_H)) + pygame.display.set_caption("Level Builder — VHDL Bouncy Game") + self.clock = pygame.time.Clock() + + self.font = pygame.font.SysFont("monospace", 13) + self.font_sm = pygame.font.SysFont("monospace", 11) + self.font_lg = pygame.font.SysFont("monospace", 15, bold=True) + + self.level_name = level_name + + # ── view state ── + fit = min(VIEW_W / WORLD_W, VIEW_H / WORLD_H) * 0.90 + self.zoom = fit + self.pan_x = VIEW_X + (VIEW_W - WORLD_W * fit) / 2 + self.pan_y = (VIEW_H - WORLD_H * fit) / 2 + self._panning = False + self._pan_last = (0, 0) + + # ── edit state ── + self.snap = True + self._drawing = False + self._draw_start= None # world coords (snapped) + + self.obstacles = [] # list of [l, t, r, b] + self.selected = None # index or None + self.status = "Ready" + self.status_ok = True + + # ═══════════════════════════════════════════════════════ coordinate utils + + def w2s(self, wx, wy): + """World → screen.""" + return (self.pan_x + wx * self.zoom, + self.pan_y + wy * self.zoom) + + def s2w(self, sx, sy): + """Screen → world (float).""" + return ((sx - self.pan_x) / self.zoom, + (sy - self.pan_y) / self.zoom) + + def _snap(self, v): + return round(v / GRID) * GRID if self.snap else int(round(v)) + + def _clamp_wx(self, v): + return max(WALL_L, min(WALL_R, v)) + + def _clamp_wy(self, v): + return max(CEIL_Y, min(GROUND_Y, v)) + + def _obs_at(self, wx, wy): + """Index of topmost obstacle under world point, or None.""" + for i in range(len(self.obstacles) - 1, -1, -1): + l, t, r, b = self.obstacles[i] + if l <= wx <= r and t <= wy <= b: + return i + return None + + # ═══════════════════════════════════════════════════════════ main loop + + def run(self): + while True: + if not self._handle_events(): + break + self._draw() + pygame.display.flip() + self.clock.tick(60) + pygame.quit() + + # ═══════════════════════════════════════════════════════ event handling + + def _handle_events(self): + for ev in pygame.event.get(): + if ev.type == pygame.QUIT: + return False + + elif ev.type == pygame.KEYDOWN: + if ev.key == pygame.K_q: + return False + elif ev.key == pygame.K_ESCAPE: + self.selected = None + self._drawing = False + elif ev.key == pygame.K_s: + self.snap = not self.snap + self._set_status(f"Snap {'ON' if self.snap else 'OFF'}") + elif ev.key == pygame.K_e: + self.export() + elif ev.key == pygame.K_l: + self.load() + elif ev.key in (pygame.K_DELETE, pygame.K_BACKSPACE): + self._delete_selected() + + elif ev.type == pygame.MOUSEBUTTONDOWN: + if not self._in_view(ev.pos): + continue + if ev.button == 2: + self._panning = True + self._pan_last = ev.pos + elif ev.button == 1: + self._on_lmb_down(ev.pos) + elif ev.button == 3: + self._on_rmb_down(ev.pos) + + elif ev.type == pygame.MOUSEBUTTONUP: + if ev.button == 2: + self._panning = False + elif ev.button == 1 and self._drawing: + self._on_lmb_up(ev.pos) + + elif ev.type == pygame.MOUSEMOTION: + if self._panning: + dx = ev.pos[0] - self._pan_last[0] + dy = ev.pos[1] - self._pan_last[1] + self.pan_x += dx + self.pan_y += dy + self._pan_last = ev.pos + + elif ev.type == pygame.MOUSEWHEEL: + if self._in_view(pygame.mouse.get_pos()): + self._do_zoom(ev.y, pygame.mouse.get_pos()) + + return True + + def _in_view(self, pos): + return pos[0] >= VIEW_X + + def _on_lmb_down(self, pos): + wx, wy = self.s2w(*pos) + hit = self._obs_at(wx, wy) + if hit is not None: + self.selected = hit + self._drawing = False + else: + self.selected = None + self._drawing = True + self._draw_start = ( + self._clamp_wx(self._snap(wx)), + self._clamp_wy(self._snap(wy)), + ) + + def _on_lmb_up(self, pos): + self._drawing = False + if self._draw_start is None: + return + wx, wy = self.s2w(*pos) + ex = self._clamp_wx(self._snap(wx)) + ey = self._clamp_wy(self._snap(wy)) + x0, y0 = self._draw_start + l, r = sorted([x0, ex]) + t, b = sorted([y0, ey]) + if r - l >= MIN_DIM and b - t >= MIN_DIM: + self.obstacles.append([l, t, r, b]) + self.selected = len(self.obstacles) - 1 + self._set_status(f"Added platform {len(self.obstacles)-1} ({r-l}×{b-t}px)") + else: + self._set_status("Too small — drag further to create platform", ok=False) + self._draw_start = None + + def _on_rmb_down(self, pos): + wx, wy = self.s2w(*pos) + hit = self._obs_at(wx, wy) + if hit is not None: + self.obstacles.pop(hit) + if self.selected == hit: + self.selected = None + elif self.selected is not None and self.selected > hit: + self.selected -= 1 + self._set_status(f"Deleted platform {hit}") + + def _delete_selected(self): + if self.selected is not None and self.selected < len(self.obstacles): + self.obstacles.pop(self.selected) + self._set_status(f"Deleted platform {self.selected}") + self.selected = None + + def _do_zoom(self, direction, pivot): + factor = 1.15 if direction > 0 else (1 / 1.15) + new_zoom = max(0.04, min(6.0, self.zoom * factor)) + mx, my = pivot + self.pan_x = mx - (mx - self.pan_x) * (new_zoom / self.zoom) + self.pan_y = my - (my - self.pan_y) * (new_zoom / self.zoom) + self.zoom = new_zoom + + def _set_status(self, msg, ok=True): + self.status = msg + self.status_ok = ok + + # ═══════════════════════════════════════════════════════════ rendering + + def _draw(self): + self.screen.fill(C_BG) + + clip = pygame.Rect(VIEW_X, 0, VIEW_W, VIEW_H) + self.screen.set_clip(clip) + self._draw_world() + self.screen.set_clip(None) + + self._draw_sidebar() + self._draw_cursor_coords() + + def _draw_world(self): + z = self.zoom + + def r(wx, wy, ww, wh, color, border=0, border_color=None): + sx, sy = self.w2s(wx, wy) + sw = max(1, ww * z) + sh = max(1, wh * z) + rect = pygame.Rect(sx, sy, sw, sh) + pygame.draw.rect(self.screen, color, rect) + if border: + pygame.draw.rect(self.screen, border_color or C_OBS_BORDER, rect, border) + + # Sky + r(WALL_L, CEIL_VIS, WALL_R - WALL_L, GROUND_VIS - CEIL_VIS, C_SKY) + # Ground + r(0, GROUND_VIS, WORLD_W, WORLD_H - GROUND_VIS, C_GROUND) + # Ceiling + r(WALL_L, 0, WALL_R - WALL_L, CEIL_VIS, C_CEIL_STRIP) + # Left wall + r(0, 0, WALL_L, WORLD_H, C_WALL) + # Right wall + r(WALL_R, 0, WORLD_W - WALL_R, WORLD_H, C_WALL) + + # Grid + if z >= 0.35: + self._draw_grid() + + # Viewport indicator (blue box showing what FPGA screen would show at start pos) + cam_x = max(0, min(860, 100 - 320)) # start pos x=100 + cam_y = max(0, min(1020, 1400 - 240)) # start pos y=1400 + vp_sl = self.w2s(cam_x, cam_y) + vp_br = self.w2s(cam_x + 640, cam_y + 480) + pygame.draw.rect(self.screen, C_VIEWPORT, + pygame.Rect(vp_sl[0], vp_sl[1], + vp_br[0] - vp_sl[0], vp_br[1] - vp_sl[1]), 1) + + # Platforms + for i, (l, t, rr, b) in enumerate(self.obstacles): + color = C_OBS_SEL if i == self.selected else C_OBS + r(l, t, rr - l, b - t, color, 1) + + # Ghost while drawing + if self._drawing and self._draw_start: + mx, my = pygame.mouse.get_pos() + wx, wy = self.s2w(mx, my) + ex = self._clamp_wx(self._snap(wx)) + ey = self._clamp_wy(self._snap(wy)) + x0, y0 = self._draw_start + gl, gr = sorted([x0, ex]) + gt, gb = sorted([y0, ey]) + sl = self.w2s(gl, gt) + sr = self.w2s(gr, gb) + gw, gh = max(1, sr[0] - sl[0]), max(1, sr[1] - sl[1]) + ghost = pygame.Surface((gw, gh), pygame.SRCALPHA) + ghost.fill(C_OBS_GHOST) + self.screen.blit(ghost, (sl[0], sl[1])) + pygame.draw.rect(self.screen, (255, 255, 100), + pygame.Rect(sl[0], sl[1], gw, gh), 1) + # Size label + lbl = self.font_sm.render(f"{gr-gl}×{gb-gt}", True, (255, 255, 150)) + self.screen.blit(lbl, (sl[0] + 2, sl[1] + 2)) + + def _draw_grid(self): + g = GRID * self.zoom + if g < 3: + return + sx0, sy0 = self.w2s(0, 0) + sx1, sy1 = self.w2s(WORLD_W, WORLD_H) + wx = 0 + while wx <= WORLD_W: + sx = int(self.pan_x + wx * self.zoom) + if VIEW_X <= sx <= VIEW_X + VIEW_W: + pygame.draw.line(self.screen, C_GRID, + (sx, max(0, int(sy0))), (sx, min(VIEW_H, int(sy1)))) + wx += GRID + wy = 0 + while wy <= WORLD_H: + sy = int(self.pan_y + wy * self.zoom) + if 0 <= sy <= VIEW_H: + pygame.draw.line(self.screen, C_GRID, + (max(VIEW_X, int(sx0)), sy), (min(VIEW_X + VIEW_W, int(sx1)), sy)) + wy += GRID + + def _draw_sidebar(self): + pygame.draw.rect(self.screen, C_SIDEBAR, + pygame.Rect(0, 0, SIDEBAR_W, WINDOW_H)) + pygame.draw.line(self.screen, C_SIDEBAR_SEP, + (SIDEBAR_W - 1, 0), (SIDEBAR_W - 1, WINDOW_H)) + + y = [10] + + def line(text, color=C_TEXT, font=None): + f = font or self.font + s = f.render(text, True, color) + self.screen.blit(s, (8, y[0])) + y[0] += s.get_height() + 3 + + def gap(n=6): + y[0] += n + + line(f"LEVEL: {self.level_name}", C_TITLE, self.font_lg) + gap() + line(f"Platforms: {len(self.obstacles)}", C_TEXT_DIM) + line(f"Snap {GRID}px: {'ON' if self.snap else 'OFF'} [S]", C_TEXT_DIM) + gap(10) + + line("CONTROLS", C_ACCENT, self.font_lg) + gap(2) + for ctrl, desc in [ + ("L-drag", "draw platform"), + ("R-click", "delete"), + ("Click", "select"), + ("Del", "delete selected"), + ("M-drag", "pan"), + ("Scroll", "zoom"), + ("S", "toggle snap"), + ("E", "export"), + ("L", "load level.json"), + ("Q", "quit"), + ]: + row = f" {ctrl:<10}{desc}" + line(row, C_TEXT_DIM, self.font_sm) + + gap(10) + line("PLATFORMS", C_ACCENT, self.font_lg) + gap(2) + + # Scrollable obstacle list + avail_h = WINDOW_H - y[0] - 50 + row_h = 14 + max_rows = avail_h // row_h + total = len(self.obstacles) + start = max(0, total - max_rows) + + if start > 0: + line(f" … {start} more above …", C_TEXT_DIM, self.font_sm) + + for i in range(start, total): + l, t, rr, b = self.obstacles[i] + w, h = rr - l, b - t + txt = f" {i:2d} ({l:4d},{t:4d}) {w:3d}×{h:2d}" + color = C_OBS_SEL if i == self.selected else C_TEXT_DIM + s = self.font_sm.render(txt, True, color) + self.screen.blit(s, (0, y[0])) + y[0] += row_h + if y[0] > WINDOW_H - 50: + break + + # Status bar + status_color = C_OK if self.status_ok else C_WARN + s = self.font_sm.render(self.status, True, status_color) + self.screen.blit(s, (4, WINDOW_H - 34)) + + # Export button hint + hint = self.font_sm.render("[E] Export JSON + VHDL pkg", True, C_OK) + self.screen.blit(hint, (4, WINDOW_H - 18)) + + def _draw_cursor_coords(self): + mx, my = pygame.mouse.get_pos() + if mx < VIEW_X: + return + wx, wy = self.s2w(mx, my) + txt = f"world ({int(wx)}, {int(wy)}) zoom {self.zoom:.2f}×" + s = self.font_sm.render(txt, True, (100, 100, 120)) + self.screen.blit(s, (VIEW_X + 4, WINDOW_H - 16)) + + # ═══════════════════════════════════════════════════════════ import/export + + def export(self, stem: str = None): + stem = stem or self.level_name + json_path = Path(f"{stem}.json") + vhdl_path = Path(f"{stem}_pkg.vhd") + + # JSON + data = { + "level": stem, + "world": {"w": WORLD_W, "h": WORLD_H}, + "obstacles": [ + {"l": l, "t": t, "r": r, "b": b} + for l, t, r, b in self.obstacles + ], + } + json_path.write_text(json.dumps(data, indent=2)) + + # VHDL package + self._write_vhdl_pkg(vhdl_path, stem) + + self._set_status( + f"Exported {len(self.obstacles)} obstacles → {json_path}, {vhdl_path}" + ) + print(f"[export] {json_path} {vhdl_path}") + + def _write_vhdl_pkg(self, path: Path, pkg_name: str): + n = len(self.obstacles) + pkg = pkg_name.replace("-", "_").replace(" ", "_") + + def arr(field_idx): + if n == 0: + return "(others => (others => '0'))" + vals = ",\n ".join( + f"CONV_STD_LOGIC_VECTOR({self.obstacles[i][field_idx]}, 11)" + for i in range(n) + ) + return f"(\n {vals}\n )" + + content = f"""\ +-- ======================================================================= +-- Level package: {pkg} +-- Auto-generated by level_builder.py — do not edit by hand. +-- +-- Usage in physics_engine / renderer: +-- library work; +-- use work.{pkg}.all; +-- +-- Then iterate: +-- for i in 0 to OBS_COUNT-1 loop +-- if c_right >= OBS_L(i) and ... then end if; +-- end loop; +-- ======================================================================= +library IEEE; +use IEEE.STD_LOGIC_1164.all; +use IEEE.STD_LOGIC_ARITH.all; +use IEEE.STD_LOGIC_UNSIGNED.all; + +package {pkg} is + + constant OBS_COUNT : integer := {n}; + + -- Obstacle coordinate arrays (11-bit world-space, left/top/right/bottom) + type obs_arr_t is array(0 to OBS_COUNT-1) of std_logic_vector(10 downto 0); + + constant OBS_L : obs_arr_t := {arr(0)}; + constant OBS_T : obs_arr_t := {arr(1)}; + constant OBS_R : obs_arr_t := {arr(2)}; + constant OBS_B : obs_arr_t := {arr(3)}; + +end package {pkg}; +""" + path.write_text(content) + + def load(self, stem: str = None): + stem = stem or self.level_name + path = Path(f"{stem}.json") + if not path.exists(): + # Try bare "level.json" as fallback + path = Path("level.json") + if not path.exists(): + self._set_status(f"File not found: {stem}.json", ok=False) + return + try: + data = json.loads(path.read_text()) + self.obstacles = [ + [o["l"], o["t"], o["r"], o["b"]] + for o in data.get("obstacles", []) + ] + self.selected = None + self._set_status( + f"Loaded {len(self.obstacles)} obstacles from {path}" + ) + if "level" in data: + self.level_name = data["level"] + pygame.display.set_caption( + f"Level Builder — {self.level_name}" + ) + except Exception as e: + self._set_status(f"Load error: {e}", ok=False) + + +# ═══════════════════════════════════════════════════════════════════════ main + +def main(): + name = sys.argv[1] if len(sys.argv) > 1 else "level" + builder = LevelBuilder(level_name=name) + + # Pre-populate with the current built-in level so you can tweak it + builder.obstacles = [ + # bottom zone + [100, 1380, 280, 1396], + [450, 1320, 530, 1336], + [750, 1390, 950, 1406], + [1150,1300,1380,1316], + # mid-lower + [60, 1100, 250, 1116], + [400, 1050, 550, 1066], + [720, 1000, 920, 1016], + [1100,1120,1350,1136], + # mid-upper + [150, 780, 380, 796], + [550, 700, 650, 716], + [850, 650,1050, 666], + [1200, 760,1450, 776], + # upper + [200, 450, 420, 466], + [620, 380, 820, 396], + [1000, 320,1200, 336], + [1320, 420,1460, 436], + ] + + # Auto-load JSON if it exists (overrides pre-populated data) + if Path(f"{name}.json").exists(): + builder.load(name) + + builder.run() + + +if __name__ == "__main__": + main() diff --git a/level_pkg.vhd b/level_pkg.vhd new file mode 100644 index 0000000..7c48c5f --- /dev/null +++ b/level_pkg.vhd @@ -0,0 +1,171 @@ +-- ======================================================================= +-- Level package: level +-- Auto-generated by level_builder.py — do not edit by hand. +-- +-- Usage in physics_engine / renderer: +-- library work; +-- use work.level.all; +-- +-- Then iterate: +-- for i in 0 to OBS_COUNT-1 loop +-- if c_right >= OBS_L(i) and ... then end if; +-- end loop; +-- ======================================================================= +library IEEE; +use IEEE.STD_LOGIC_1164.all; +use IEEE.STD_LOGIC_ARITH.all; +use IEEE.STD_LOGIC_UNSIGNED.all; + +package level is + + constant OBS_COUNT : integer := 34; + + -- Obstacle coordinate arrays (11-bit world-space, left/top/right/bottom) + type obs_arr_t is array(0 to OBS_COUNT-1) of std_logic_vector(10 downto 0); + + constant OBS_L : obs_arr_t := ( + CONV_STD_LOGIC_VECTOR(60, 11), + CONV_STD_LOGIC_VECTOR(250, 11), + CONV_STD_LOGIC_VECTOR(490, 11), + CONV_STD_LOGIC_VECTOR(690, 11), + CONV_STD_LOGIC_VECTOR(460, 11), + CONV_STD_LOGIC_VECTOR(100, 11), + CONV_STD_LOGIC_VECTOR(200, 11), + CONV_STD_LOGIC_VECTOR(570, 11), + CONV_STD_LOGIC_VECTOR(930, 11), + CONV_STD_LOGIC_VECTOR(590, 11), + CONV_STD_LOGIC_VECTOR(590, 11), + CONV_STD_LOGIC_VECTOR(790, 11), + CONV_STD_LOGIC_VECTOR(920, 11), + CONV_STD_LOGIC_VECTOR(920, 11), + CONV_STD_LOGIC_VECTOR(590, 11), + CONV_STD_LOGIC_VECTOR(720, 11), + CONV_STD_LOGIC_VECTOR(650, 11), + CONV_STD_LOGIC_VECTOR(310, 11), + CONV_STD_LOGIC_VECTOR(70, 11), + CONV_STD_LOGIC_VECTOR(190, 11), + CONV_STD_LOGIC_VECTOR(420, 11), + CONV_STD_LOGIC_VECTOR(490, 11), + CONV_STD_LOGIC_VECTOR(200, 11), + CONV_STD_LOGIC_VECTOR(1010, 11), + CONV_STD_LOGIC_VECTOR(780, 11), + CONV_STD_LOGIC_VECTOR(1120, 11), + CONV_STD_LOGIC_VECTOR(990, 11), + CONV_STD_LOGIC_VECTOR(940, 11), + CONV_STD_LOGIC_VECTOR(1280, 11), + CONV_STD_LOGIC_VECTOR(640, 11), + CONV_STD_LOGIC_VECTOR(1250, 11), + CONV_STD_LOGIC_VECTOR(1200, 11), + CONV_STD_LOGIC_VECTOR(1330, 11), + CONV_STD_LOGIC_VECTOR(1270, 11) + ); + constant OBS_T : obs_arr_t := ( + CONV_STD_LOGIC_VECTOR(1430, 11), + CONV_STD_LOGIC_VECTOR(1380, 11), + CONV_STD_LOGIC_VECTOR(1420, 11), + CONV_STD_LOGIC_VECTOR(1360, 11), + CONV_STD_LOGIC_VECTOR(1220, 11), + CONV_STD_LOGIC_VECTOR(1170, 11), + CONV_STD_LOGIC_VECTOR(980, 11), + CONV_STD_LOGIC_VECTOR(1060, 11), + CONV_STD_LOGIC_VECTOR(1210, 11), + CONV_STD_LOGIC_VECTOR(480, 11), + CONV_STD_LOGIC_VECTOR(480, 11), + CONV_STD_LOGIC_VECTOR(480, 11), + CONV_STD_LOGIC_VECTOR(500, 11), + CONV_STD_LOGIC_VECTOR(490, 11), + CONV_STD_LOGIC_VECTOR(760, 11), + CONV_STD_LOGIC_VECTOR(640, 11), + CONV_STD_LOGIC_VECTOR(570, 11), + CONV_STD_LOGIC_VECTOR(800, 11), + CONV_STD_LOGIC_VECTOR(620, 11), + CONV_STD_LOGIC_VECTOR(360, 11), + CONV_STD_LOGIC_VECTOR(540, 11), + CONV_STD_LOGIC_VECTOR(200, 11), + CONV_STD_LOGIC_VECTOR(170, 11), + CONV_STD_LOGIC_VECTOR(220, 11), + CONV_STD_LOGIC_VECTOR(280, 11), + CONV_STD_LOGIC_VECTOR(580, 11), + CONV_STD_LOGIC_VECTOR(770, 11), + CONV_STD_LOGIC_VECTOR(970, 11), + CONV_STD_LOGIC_VECTOR(850, 11), + CONV_STD_LOGIC_VECTOR(910, 11), + CONV_STD_LOGIC_VECTOR(350, 11), + CONV_STD_LOGIC_VECTOR(1360, 11), + CONV_STD_LOGIC_VECTOR(1220, 11), + CONV_STD_LOGIC_VECTOR(1100, 11) + ); + constant OBS_R : obs_arr_t := ( + CONV_STD_LOGIC_VECTOR(150, 11), + CONV_STD_LOGIC_VECTOR(360, 11), + CONV_STD_LOGIC_VECTOR(620, 11), + CONV_STD_LOGIC_VECTOR(980, 11), + CONV_STD_LOGIC_VECTOR(620, 11), + CONV_STD_LOGIC_VECTOR(270, 11), + CONV_STD_LOGIC_VECTOR(460, 11), + CONV_STD_LOGIC_VECTOR(1050, 11), + CONV_STD_LOGIC_VECTOR(1140, 11), + CONV_STD_LOGIC_VECTOR(600, 11), + CONV_STD_LOGIC_VECTOR(790, 11), + CONV_STD_LOGIC_VECTOR(930, 11), + CONV_STD_LOGIC_VECTOR(930, 11), + CONV_STD_LOGIC_VECTOR(930, 11), + CONV_STD_LOGIC_VECTOR(820, 11), + CONV_STD_LOGIC_VECTOR(820, 11), + CONV_STD_LOGIC_VECTOR(730, 11), + CONV_STD_LOGIC_VECTOR(510, 11), + CONV_STD_LOGIC_VECTOR(180, 11), + CONV_STD_LOGIC_VECTOR(370, 11), + CONV_STD_LOGIC_VECTOR(500, 11), + CONV_STD_LOGIC_VECTOR(650, 11), + CONV_STD_LOGIC_VECTOR(300, 11), + CONV_STD_LOGIC_VECTOR(1170, 11), + CONV_STD_LOGIC_VECTOR(890, 11), + CONV_STD_LOGIC_VECTOR(1280, 11), + CONV_STD_LOGIC_VECTOR(1220, 11), + CONV_STD_LOGIC_VECTOR(1340, 11), + CONV_STD_LOGIC_VECTOR(1350, 11), + CONV_STD_LOGIC_VECTOR(700, 11), + CONV_STD_LOGIC_VECTOR(1330, 11), + CONV_STD_LOGIC_VECTOR(1300, 11), + CONV_STD_LOGIC_VECTOR(1400, 11), + CONV_STD_LOGIC_VECTOR(1430, 11) + ); + constant OBS_B : obs_arr_t := ( + CONV_STD_LOGIC_VECTOR(1440, 11), + CONV_STD_LOGIC_VECTOR(1390, 11), + CONV_STD_LOGIC_VECTOR(1430, 11), + CONV_STD_LOGIC_VECTOR(1370, 11), + CONV_STD_LOGIC_VECTOR(1230, 11), + CONV_STD_LOGIC_VECTOR(1180, 11), + CONV_STD_LOGIC_VECTOR(990, 11), + CONV_STD_LOGIC_VECTOR(1070, 11), + CONV_STD_LOGIC_VECTOR(1220, 11), + CONV_STD_LOGIC_VECTOR(760, 11), + CONV_STD_LOGIC_VECTOR(490, 11), + CONV_STD_LOGIC_VECTOR(490, 11), + CONV_STD_LOGIC_VECTOR(760, 11), + CONV_STD_LOGIC_VECTOR(500, 11), + CONV_STD_LOGIC_VECTOR(770, 11), + CONV_STD_LOGIC_VECTOR(650, 11), + CONV_STD_LOGIC_VECTOR(580, 11), + CONV_STD_LOGIC_VECTOR(810, 11), + CONV_STD_LOGIC_VECTOR(640, 11), + CONV_STD_LOGIC_VECTOR(410, 11), + CONV_STD_LOGIC_VECTOR(550, 11), + CONV_STD_LOGIC_VECTOR(220, 11), + CONV_STD_LOGIC_VECTOR(180, 11), + CONV_STD_LOGIC_VECTOR(290, 11), + CONV_STD_LOGIC_VECTOR(290, 11), + CONV_STD_LOGIC_VECTOR(590, 11), + CONV_STD_LOGIC_VECTOR(790, 11), + CONV_STD_LOGIC_VECTOR(980, 11), + CONV_STD_LOGIC_VECTOR(890, 11), + CONV_STD_LOGIC_VECTOR(940, 11), + CONV_STD_LOGIC_VECTOR(380, 11), + CONV_STD_LOGIC_VECTOR(1380, 11), + CONV_STD_LOGIC_VECTOR(1250, 11), + CONV_STD_LOGIC_VECTOR(1140, 11) + ); + +end package level; diff --git a/physics_engine.vhd b/physics_engine.vhd index c4a55df..78a5884 100644 --- a/physics_engine.vhd +++ b/physics_engine.vhd @@ -1,14 +1,20 @@ -- ======================================================================== -- Physics Engine (scrolling 1500x1500 world) --- Positions: 11-bit unsigned (0..2047, world fits in 0..1500). --- Velocities: 10-bit 2's complement (bit 9 = sign), unchanged. --- Camera: follows character, clamped to world bounds, output for renderer. +-- Positions: 11-bit unsigned. Valid range: 0..1492 (fits in 11-bit fine). +-- Velocities: 10-bit 2's complement, bit 9 = sign. +-- +-- Underflow detection: uses >= 2000 threshold instead of bit-10 sign check. +-- Bit-10 was wrong because GROUND(1480) and RIGHT_WALL(1492) both have +-- bit 10 set (values >= 1024), causing false ceiling/wall triggers. +-- +-- Obstacles imported from level_pkg (use work.level_pkg.all). -- ======================================================================== library IEEE; use IEEE.STD_LOGIC_1164.all; use IEEE.STD_LOGIC_ARITH.all; use IEEE.STD_LOGIC_UNSIGNED.all; +use work.level_pkg.all; entity physics_engine is port( @@ -36,7 +42,7 @@ architecture behavior of physics_engine is signal cam_x_sig : std_logic_vector(10 downto 0) := (others => '0'); signal cam_y_sig : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1020, 11); - -- SIZE: 10-bit for squish outputs, 11-bit for position arithmetic + -- SIZE: 10-bit for squish output math, 11-bit for position arithmetic constant SIZE : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(7, 10); constant SIZE11 : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(7, 11); @@ -49,93 +55,12 @@ architecture behavior of physics_engine is -- World bounds (11-bit) constant GROUND : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1480, 11); constant CEILING : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(16, 11); - constant LEFT_WALL : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(15, 11); + constant LEFT_WALL : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(8, 11); constant RIGHT_WALL : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1492, 11); - -- ---- Obstacle constants (L, T, R, B) in world-space (11-bit) ---- - -- Bottom zone (near ground, y ~1300-1420) - constant O1_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(100, 11); - constant O1_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1380, 11); - constant O1_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(280, 11); - constant O1_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1396, 11); - - constant O2_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(450, 11); - constant O2_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1320, 11); - constant O2_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(530, 11); - constant O2_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1336, 11); - - constant O3_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(750, 11); - constant O3_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1390, 11); - constant O3_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(950, 11); - constant O3_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1406, 11); - - constant O4_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1150, 11); - constant O4_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1300, 11); - constant O4_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1380, 11); - constant O4_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1316, 11); - - -- Mid-lower zone (y ~1000-1140) - constant O5_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(60, 11); - constant O5_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1100, 11); - constant O5_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(250, 11); - constant O5_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1116, 11); - - constant O6_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(400, 11); - constant O6_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1050, 11); - constant O6_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(550, 11); - constant O6_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1066, 11); - - constant O7_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(720, 11); - constant O7_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1000, 11); - constant O7_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(920, 11); - constant O7_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1016, 11); - - constant O8_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1100, 11); - constant O8_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1120, 11); - constant O8_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1350, 11); - constant O8_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1136, 11); - - -- Mid-upper zone (y ~650-800) - constant O9_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(150, 11); - constant O9_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(780, 11); - constant O9_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(380, 11); - constant O9_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(796, 11); - - constant O10_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(550, 11); - constant O10_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(700, 11); - constant O10_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(650, 11); - constant O10_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(716, 11); - - constant O11_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(850, 11); - constant O11_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(650, 11); - constant O11_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1050, 11); - constant O11_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(666, 11); - - constant O12_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1200, 11); - constant O12_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(760, 11); - constant O12_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1450, 11); - constant O12_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(776, 11); - - -- Upper zone (y ~320-500) - constant O13_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(200, 11); - constant O13_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(450, 11); - constant O13_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(420, 11); - constant O13_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(466, 11); - - constant O14_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(620, 11); - constant O14_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(380, 11); - constant O14_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(820, 11); - constant O14_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(396, 11); - - constant O15_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1000, 11); - constant O15_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(320, 11); - constant O15_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1200, 11); - constant O15_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(336, 11); - - constant O16_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1320, 11); - constant O16_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(420, 11); - constant O16_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1460, 11); - constant O16_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(436, 11); + -- Underflow sentinel: valid positions top out at 1492; underflow wraps to >= 2000. + -- (Min case: CEILING(16) - max_upward(64) = -48 -> 11-bit unsigned = 2000.) + constant UNDERFLOW : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(2000, 11); -- Animation signal squish : std_logic_vector(3 downto 0) := (others => '0'); @@ -156,12 +81,12 @@ begin else SIZE + ("000000" & squish); physics : process - variable vx, vy : std_logic_vector(9 downto 0); - variable px, py : std_logic_vector(10 downto 0); - variable bounced : std_logic; - variable bounce_wall : std_logic; - variable bounce_speed: std_logic_vector(9 downto 0); - variable grounded : std_logic; + variable vx, vy : std_logic_vector(9 downto 0); + variable px, py : std_logic_vector(10 downto 0); + variable bounced : std_logic; + variable bounce_wall : std_logic; + variable bounce_speed : std_logic_vector(9 downto 0); + variable grounded : std_logic; variable c_left, c_right, c_top, c_bot : std_logic_vector(10 downto 0); variable overlap_x, overlap_y : std_logic_vector(10 downto 0); begin @@ -236,8 +161,10 @@ begin elsif vy(9) = '0' then vy := (others => '0'); end if; end if; - -- == CEILING (py(10)='1' catches negative-wrap underflow) == - if py(10) = '1' or py <= CEILING then + -- == CEILING == + -- Underflow detection: py >= 2000 means it wrapped below zero. + -- (GROUND=1480, RIGHT_WALL=1492 are both < 2000, so no false triggers.) + if py >= UNDERFLOW or py <= CEILING then py := CEILING; bounced := '1'; bounce_speed := (not vy) + 1; @@ -247,8 +174,9 @@ begin end if; end if; - -- == LEFT WALL (px(10)='1' catches underflow) == - if px(10) = '1' or px <= LEFT_WALL + SIZE11 then + -- == LEFT WALL == + -- Same underflow fix: px >= 2000 means wrapped below zero. + if px >= UNDERFLOW or px <= LEFT_WALL + SIZE11 then px := LEFT_WALL + SIZE11; bounced := '1'; bounce_wall := '1'; bounce_speed := (not vx) + 1; @@ -272,360 +200,50 @@ begin end if; end if; - -- == OBSTACLE COLLISIONS == - -- Shared bounce helpers (vert and horiz) used identically per obstacle. - - -- O1 - c_left:=px-SIZE11; c_right:=px+SIZE11; c_top:=py-SIZE11; c_bot:=py+SIZE11; - if c_right>=O1_L and c_left<=O1_R and c_bot>=O1_T and c_top<=O1_B then - if vy(9)='0' then overlap_y:=c_bot-O1_T; else overlap_y:=O1_B-c_top; end if; - if vx(9)='0' then overlap_x:=c_right-O1_L; else overlap_x:=O1_R-c_left; end if; - if overlap_y<=overlap_x then - if vy(9)='0' then py:=O1_T-SIZE11; grounded:='1'; else py:=O1_B+SIZE11; end if; - bounced:='1'; - vy:=(not vy)+1; - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); - elsif vy(9)='1' and vy'0'); end if; - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; - else - if vx(9)='0' then px:=O1_L-SIZE11; else px:=O1_R+SIZE11; end if; - bounced:='1'; bounce_wall:='1'; - vx:=(not vx)+1; - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); - elsif vx(9)='1' and vx= OBS_L(obs_i) and c_left <= OBS_R(obs_i) and + c_bot >= OBS_T(obs_i) and c_top <= OBS_B(obs_i) then + + if vy(9) = '0' then overlap_y := c_bot - OBS_T(obs_i); + else overlap_y := OBS_B(obs_i) - c_top; end if; + if vx(9) = '0' then overlap_x := c_right - OBS_L(obs_i); + else overlap_x := OBS_R(obs_i) - c_left; end if; + + if overlap_y <= overlap_x then + -- Vertical resolution + if vy(9) = '0' then py := OBS_T(obs_i) - SIZE11; grounded := '1'; + else py := OBS_B(obs_i) + SIZE11; end if; + bounced := '1'; + vy := (not vy) + 1; + if vy(9) = '0' and vy > 1 then + vy := vy - ("000" & vy(9 downto 3)); + elsif vy(9) = '1' and vy < CONV_STD_LOGIC_VECTOR(1022, 10) then + vy := vy + ("000" & ((not vy(9 downto 3)) + 1)); + end if; + if vy(9) = '0' and vy < 2 then vy := (others => '0'); end if; + if vy(9) = '1' and vy >= CONV_STD_LOGIC_VECTOR(1022, 10) then + vy := (others => '0'); + end if; + else + -- Horizontal resolution + if vx(9) = '0' then px := OBS_L(obs_i) - SIZE11; + else px := OBS_R(obs_i) + SIZE11; end if; + bounced := '1'; bounce_wall := '1'; + vx := (not vx) + 1; + if vx(9) = '0' and vx > 1 then + vx := vx - ("000" & vx(9 downto 3)); + elsif vx(9) = '1' and vx < CONV_STD_LOGIC_VECTOR(1022, 10) then + vx := vx + ("000" & ((not vx(9 downto 3)) + 1)); + end if; + end if; end if; - end if; - - -- O2 - c_left:=px-SIZE11; c_right:=px+SIZE11; c_top:=py-SIZE11; c_bot:=py+SIZE11; - if c_right>=O2_L and c_left<=O2_R and c_bot>=O2_T and c_top<=O2_B then - if vy(9)='0' then overlap_y:=c_bot-O2_T; else overlap_y:=O2_B-c_top; end if; - if vx(9)='0' then overlap_x:=c_right-O2_L; else overlap_x:=O2_R-c_left; end if; - if overlap_y<=overlap_x then - if vy(9)='0' then py:=O2_T-SIZE11; grounded:='1'; else py:=O2_B+SIZE11; end if; - bounced:='1'; - vy:=(not vy)+1; - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); - elsif vy(9)='1' and vy'0'); end if; - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; - else - if vx(9)='0' then px:=O2_L-SIZE11; else px:=O2_R+SIZE11; end if; - bounced:='1'; bounce_wall:='1'; - vx:=(not vx)+1; - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); - elsif vx(9)='1' and vx=O3_L and c_left<=O3_R and c_bot>=O3_T and c_top<=O3_B then - if vy(9)='0' then overlap_y:=c_bot-O3_T; else overlap_y:=O3_B-c_top; end if; - if vx(9)='0' then overlap_x:=c_right-O3_L; else overlap_x:=O3_R-c_left; end if; - if overlap_y<=overlap_x then - if vy(9)='0' then py:=O3_T-SIZE11; grounded:='1'; else py:=O3_B+SIZE11; end if; - bounced:='1'; - vy:=(not vy)+1; - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); - elsif vy(9)='1' and vy'0'); end if; - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; - else - if vx(9)='0' then px:=O3_L-SIZE11; else px:=O3_R+SIZE11; end if; - bounced:='1'; bounce_wall:='1'; - vx:=(not vx)+1; - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); - elsif vx(9)='1' and vx=O4_L and c_left<=O4_R and c_bot>=O4_T and c_top<=O4_B then - if vy(9)='0' then overlap_y:=c_bot-O4_T; else overlap_y:=O4_B-c_top; end if; - if vx(9)='0' then overlap_x:=c_right-O4_L; else overlap_x:=O4_R-c_left; end if; - if overlap_y<=overlap_x then - if vy(9)='0' then py:=O4_T-SIZE11; grounded:='1'; else py:=O4_B+SIZE11; end if; - bounced:='1'; - vy:=(not vy)+1; - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); - elsif vy(9)='1' and vy'0'); end if; - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; - else - if vx(9)='0' then px:=O4_L-SIZE11; else px:=O4_R+SIZE11; end if; - bounced:='1'; bounce_wall:='1'; - vx:=(not vx)+1; - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); - elsif vx(9)='1' and vx=O5_L and c_left<=O5_R and c_bot>=O5_T and c_top<=O5_B then - if vy(9)='0' then overlap_y:=c_bot-O5_T; else overlap_y:=O5_B-c_top; end if; - if vx(9)='0' then overlap_x:=c_right-O5_L; else overlap_x:=O5_R-c_left; end if; - if overlap_y<=overlap_x then - if vy(9)='0' then py:=O5_T-SIZE11; grounded:='1'; else py:=O5_B+SIZE11; end if; - bounced:='1'; - vy:=(not vy)+1; - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); - elsif vy(9)='1' and vy'0'); end if; - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; - else - if vx(9)='0' then px:=O5_L-SIZE11; else px:=O5_R+SIZE11; end if; - bounced:='1'; bounce_wall:='1'; - vx:=(not vx)+1; - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); - elsif vx(9)='1' and vx=O6_L and c_left<=O6_R and c_bot>=O6_T and c_top<=O6_B then - if vy(9)='0' then overlap_y:=c_bot-O6_T; else overlap_y:=O6_B-c_top; end if; - if vx(9)='0' then overlap_x:=c_right-O6_L; else overlap_x:=O6_R-c_left; end if; - if overlap_y<=overlap_x then - if vy(9)='0' then py:=O6_T-SIZE11; grounded:='1'; else py:=O6_B+SIZE11; end if; - bounced:='1'; - vy:=(not vy)+1; - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); - elsif vy(9)='1' and vy'0'); end if; - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; - else - if vx(9)='0' then px:=O6_L-SIZE11; else px:=O6_R+SIZE11; end if; - bounced:='1'; bounce_wall:='1'; - vx:=(not vx)+1; - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); - elsif vx(9)='1' and vx=O7_L and c_left<=O7_R and c_bot>=O7_T and c_top<=O7_B then - if vy(9)='0' then overlap_y:=c_bot-O7_T; else overlap_y:=O7_B-c_top; end if; - if vx(9)='0' then overlap_x:=c_right-O7_L; else overlap_x:=O7_R-c_left; end if; - if overlap_y<=overlap_x then - if vy(9)='0' then py:=O7_T-SIZE11; grounded:='1'; else py:=O7_B+SIZE11; end if; - bounced:='1'; - vy:=(not vy)+1; - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); - elsif vy(9)='1' and vy'0'); end if; - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; - else - if vx(9)='0' then px:=O7_L-SIZE11; else px:=O7_R+SIZE11; end if; - bounced:='1'; bounce_wall:='1'; - vx:=(not vx)+1; - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); - elsif vx(9)='1' and vx=O8_L and c_left<=O8_R and c_bot>=O8_T and c_top<=O8_B then - if vy(9)='0' then overlap_y:=c_bot-O8_T; else overlap_y:=O8_B-c_top; end if; - if vx(9)='0' then overlap_x:=c_right-O8_L; else overlap_x:=O8_R-c_left; end if; - if overlap_y<=overlap_x then - if vy(9)='0' then py:=O8_T-SIZE11; grounded:='1'; else py:=O8_B+SIZE11; end if; - bounced:='1'; - vy:=(not vy)+1; - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); - elsif vy(9)='1' and vy'0'); end if; - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; - else - if vx(9)='0' then px:=O8_L-SIZE11; else px:=O8_R+SIZE11; end if; - bounced:='1'; bounce_wall:='1'; - vx:=(not vx)+1; - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); - elsif vx(9)='1' and vx=O9_L and c_left<=O9_R and c_bot>=O9_T and c_top<=O9_B then - if vy(9)='0' then overlap_y:=c_bot-O9_T; else overlap_y:=O9_B-c_top; end if; - if vx(9)='0' then overlap_x:=c_right-O9_L; else overlap_x:=O9_R-c_left; end if; - if overlap_y<=overlap_x then - if vy(9)='0' then py:=O9_T-SIZE11; grounded:='1'; else py:=O9_B+SIZE11; end if; - bounced:='1'; - vy:=(not vy)+1; - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); - elsif vy(9)='1' and vy'0'); end if; - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; - else - if vx(9)='0' then px:=O9_L-SIZE11; else px:=O9_R+SIZE11; end if; - bounced:='1'; bounce_wall:='1'; - vx:=(not vx)+1; - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); - elsif vx(9)='1' and vx=O10_L and c_left<=O10_R and c_bot>=O10_T and c_top<=O10_B then - if vy(9)='0' then overlap_y:=c_bot-O10_T; else overlap_y:=O10_B-c_top; end if; - if vx(9)='0' then overlap_x:=c_right-O10_L; else overlap_x:=O10_R-c_left; end if; - if overlap_y<=overlap_x then - if vy(9)='0' then py:=O10_T-SIZE11; grounded:='1'; else py:=O10_B+SIZE11; end if; - bounced:='1'; - vy:=(not vy)+1; - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); - elsif vy(9)='1' and vy'0'); end if; - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; - else - if vx(9)='0' then px:=O10_L-SIZE11; else px:=O10_R+SIZE11; end if; - bounced:='1'; bounce_wall:='1'; - vx:=(not vx)+1; - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); - elsif vx(9)='1' and vx=O11_L and c_left<=O11_R and c_bot>=O11_T and c_top<=O11_B then - if vy(9)='0' then overlap_y:=c_bot-O11_T; else overlap_y:=O11_B-c_top; end if; - if vx(9)='0' then overlap_x:=c_right-O11_L; else overlap_x:=O11_R-c_left; end if; - if overlap_y<=overlap_x then - if vy(9)='0' then py:=O11_T-SIZE11; grounded:='1'; else py:=O11_B+SIZE11; end if; - bounced:='1'; - vy:=(not vy)+1; - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); - elsif vy(9)='1' and vy'0'); end if; - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; - else - if vx(9)='0' then px:=O11_L-SIZE11; else px:=O11_R+SIZE11; end if; - bounced:='1'; bounce_wall:='1'; - vx:=(not vx)+1; - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); - elsif vx(9)='1' and vx=O12_L and c_left<=O12_R and c_bot>=O12_T and c_top<=O12_B then - if vy(9)='0' then overlap_y:=c_bot-O12_T; else overlap_y:=O12_B-c_top; end if; - if vx(9)='0' then overlap_x:=c_right-O12_L; else overlap_x:=O12_R-c_left; end if; - if overlap_y<=overlap_x then - if vy(9)='0' then py:=O12_T-SIZE11; grounded:='1'; else py:=O12_B+SIZE11; end if; - bounced:='1'; - vy:=(not vy)+1; - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); - elsif vy(9)='1' and vy'0'); end if; - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; - else - if vx(9)='0' then px:=O12_L-SIZE11; else px:=O12_R+SIZE11; end if; - bounced:='1'; bounce_wall:='1'; - vx:=(not vx)+1; - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); - elsif vx(9)='1' and vx=O13_L and c_left<=O13_R and c_bot>=O13_T and c_top<=O13_B then - if vy(9)='0' then overlap_y:=c_bot-O13_T; else overlap_y:=O13_B-c_top; end if; - if vx(9)='0' then overlap_x:=c_right-O13_L; else overlap_x:=O13_R-c_left; end if; - if overlap_y<=overlap_x then - if vy(9)='0' then py:=O13_T-SIZE11; grounded:='1'; else py:=O13_B+SIZE11; end if; - bounced:='1'; - vy:=(not vy)+1; - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); - elsif vy(9)='1' and vy'0'); end if; - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; - else - if vx(9)='0' then px:=O13_L-SIZE11; else px:=O13_R+SIZE11; end if; - bounced:='1'; bounce_wall:='1'; - vx:=(not vx)+1; - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); - elsif vx(9)='1' and vx=O14_L and c_left<=O14_R and c_bot>=O14_T and c_top<=O14_B then - if vy(9)='0' then overlap_y:=c_bot-O14_T; else overlap_y:=O14_B-c_top; end if; - if vx(9)='0' then overlap_x:=c_right-O14_L; else overlap_x:=O14_R-c_left; end if; - if overlap_y<=overlap_x then - if vy(9)='0' then py:=O14_T-SIZE11; grounded:='1'; else py:=O14_B+SIZE11; end if; - bounced:='1'; - vy:=(not vy)+1; - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); - elsif vy(9)='1' and vy'0'); end if; - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; - else - if vx(9)='0' then px:=O14_L-SIZE11; else px:=O14_R+SIZE11; end if; - bounced:='1'; bounce_wall:='1'; - vx:=(not vx)+1; - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); - elsif vx(9)='1' and vx=O15_L and c_left<=O15_R and c_bot>=O15_T and c_top<=O15_B then - if vy(9)='0' then overlap_y:=c_bot-O15_T; else overlap_y:=O15_B-c_top; end if; - if vx(9)='0' then overlap_x:=c_right-O15_L; else overlap_x:=O15_R-c_left; end if; - if overlap_y<=overlap_x then - if vy(9)='0' then py:=O15_T-SIZE11; grounded:='1'; else py:=O15_B+SIZE11; end if; - bounced:='1'; - vy:=(not vy)+1; - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); - elsif vy(9)='1' and vy'0'); end if; - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; - else - if vx(9)='0' then px:=O15_L-SIZE11; else px:=O15_R+SIZE11; end if; - bounced:='1'; bounce_wall:='1'; - vx:=(not vx)+1; - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); - elsif vx(9)='1' and vx=O16_L and c_left<=O16_R and c_bot>=O16_T and c_top<=O16_B then - if vy(9)='0' then overlap_y:=c_bot-O16_T; else overlap_y:=O16_B-c_top; end if; - if vx(9)='0' then overlap_x:=c_right-O16_L; else overlap_x:=O16_R-c_left; end if; - if overlap_y<=overlap_x then - if vy(9)='0' then py:=O16_T-SIZE11; grounded:='1'; else py:=O16_B+SIZE11; end if; - bounced:='1'; - vy:=(not vy)+1; - if vy(9)='0' and vy>1 then vy:=vy-("000"&vy(9 downto 3)); - elsif vy(9)='1' and vy'0'); end if; - if vy(9)='1' and vy>=CONV_STD_LOGIC_VECTOR(1022,10) then vy:=(others=>'0'); end if; - else - if vx(9)='0' then px:=O16_L-SIZE11; else px:=O16_R+SIZE11; end if; - bounced:='1'; bounce_wall:='1'; - vx:=(not vx)+1; - if vx(9)='0' and vx>1 then vx:=vx-("000"&vx(9 downto 3)); - elsif vx(9)='1' and vx '0'); elsif px > CONV_STD_LOGIC_VECTOR(1180, 11) then @@ -643,7 +261,7 @@ begin cam_x_sig <= px - CONV_STD_LOGIC_VECTOR(320, 11); end if; - -- cam_y = clamp(py - 240, 0, 1020) [world 1500 - screen 480 = 1020] + -- cam_y = clamp(py - 240, 0, 1020) [1500 - 480 = 1020] if py < CONV_STD_LOGIC_VECTOR(240, 11) then cam_y_sig <= (others => '0'); elsif py > CONV_STD_LOGIC_VECTOR(1260, 11) then @@ -662,7 +280,7 @@ begin end if; if grounded = '1' then on_ground <= '1'; - elsif py < GROUND - 1 then on_ground <= '0'; end if; + else on_ground <= '0'; end if; end process physics; diff --git a/renderer.vhd b/renderer.vhd index 321759c..86f3a41 100644 --- a/renderer.vhd +++ b/renderer.vhd @@ -1,18 +1,16 @@ -- ======================================================================== -- Renderer (scrolling world) --- Converts screen pixel (col, row) to world coords via camera offset, --- then tests world-space position against all geometry. --- -- world_col = pixel_column + cam_x -- world_row = pixel_row + cam_y --- --- All boundary/obstacle constants are in world-space (11-bit). +-- All comparisons in world-space (11-bit). +-- Obstacles imported from level_pkg (use work.level_pkg.all). -- ======================================================================== library IEEE; use IEEE.STD_LOGIC_1164.all; use IEEE.STD_LOGIC_ARITH.all; use IEEE.STD_LOGIC_UNSIGNED.all; +use work.level_pkg.all; entity renderer is port( @@ -38,95 +36,14 @@ architecture behavior of renderer is constant LEFT_WALL : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(8, 11); constant RIGHT_WALL : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1492, 11); - -- Obstacle positions (must match physics_engine, 11-bit world-space) - constant O1_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(100, 11); - constant O1_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1380, 11); - constant O1_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(280, 11); - constant O1_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1396, 11); - - constant O2_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(450, 11); - constant O2_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1320, 11); - constant O2_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(530, 11); - constant O2_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1336, 11); - - constant O3_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(750, 11); - constant O3_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1390, 11); - constant O3_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(950, 11); - constant O3_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1406, 11); - - constant O4_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1150, 11); - constant O4_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1300, 11); - constant O4_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1380, 11); - constant O4_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1316, 11); - - constant O5_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(60, 11); - constant O5_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1100, 11); - constant O5_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(250, 11); - constant O5_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1116, 11); - - constant O6_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(400, 11); - constant O6_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1050, 11); - constant O6_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(550, 11); - constant O6_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1066, 11); - - constant O7_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(720, 11); - constant O7_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1000, 11); - constant O7_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(920, 11); - constant O7_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1016, 11); - - constant O8_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1100, 11); - constant O8_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1120, 11); - constant O8_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1350, 11); - constant O8_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1136, 11); - - constant O9_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(150, 11); - constant O9_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(780, 11); - constant O9_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(380, 11); - constant O9_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(796, 11); - - constant O10_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(550, 11); - constant O10_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(700, 11); - constant O10_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(650, 11); - constant O10_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(716, 11); - - constant O11_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(850, 11); - constant O11_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(650, 11); - constant O11_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1050, 11); - constant O11_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(666, 11); - - constant O12_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1200, 11); - constant O12_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(760, 11); - constant O12_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1450, 11); - constant O12_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(776, 11); - - constant O13_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(200, 11); - constant O13_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(450, 11); - constant O13_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(420, 11); - constant O13_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(466, 11); - - constant O14_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(620, 11); - constant O14_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(380, 11); - constant O14_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(820, 11); - constant O14_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(396, 11); - - constant O15_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1000, 11); - constant O15_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(320, 11); - constant O15_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1200, 11); - constant O15_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(336, 11); - - constant O16_L : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1320, 11); - constant O16_T : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(420, 11); - constant O16_R : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(1460, 11); - constant O16_B : std_logic_vector(10 downto 0) := CONV_STD_LOGIC_VECTOR(436, 11); - signal char_on, ground_on, wall_on, ceiling_on, obs_on : std_logic; begin render : process(pixel_row, pixel_column, char_x, char_y, char_width, char_height, cam_x, cam_y) - variable wc : std_logic_vector(10 downto 0); -- world column - variable wr : std_logic_vector(10 downto 0); -- world row + variable wc : std_logic_vector(10 downto 0); + variable wr : std_logic_vector(10 downto 0); begin wc := ('0' & pixel_column) + cam_x; wr := ('0' & pixel_row) + cam_y; @@ -145,28 +62,18 @@ begin char_on <= '1'; end if; - -- Ground / ceiling / walls (world edges) - if wr >= GROUND_TOP then ground_on <= '1'; end if; + -- World edges + if wr >= GROUND_TOP then ground_on <= '1'; end if; if wr <= CEIL_BOT then ceiling_on <= '1'; end if; if wc < LEFT_WALL or wc > RIGHT_WALL then wall_on <= '1'; end if; - -- Obstacles - if wc>=O1_L and wc<=O1_R and wr>=O1_T and wr<=O1_B then obs_on<='1'; end if; - if wc>=O2_L and wc<=O2_R and wr>=O2_T and wr<=O2_B then obs_on<='1'; end if; - if wc>=O3_L and wc<=O3_R and wr>=O3_T and wr<=O3_B then obs_on<='1'; end if; - if wc>=O4_L and wc<=O4_R and wr>=O4_T and wr<=O4_B then obs_on<='1'; end if; - if wc>=O5_L and wc<=O5_R and wr>=O5_T and wr<=O5_B then obs_on<='1'; end if; - if wc>=O6_L and wc<=O6_R and wr>=O6_T and wr<=O6_B then obs_on<='1'; end if; - if wc>=O7_L and wc<=O7_R and wr>=O7_T and wr<=O7_B then obs_on<='1'; end if; - if wc>=O8_L and wc<=O8_R and wr>=O8_T and wr<=O8_B then obs_on<='1'; end if; - if wc>=O9_L and wc<=O9_R and wr>=O9_T and wr<=O9_B then obs_on<='1'; end if; - if wc>=O10_L and wc<=O10_R and wr>=O10_T and wr<=O10_B then obs_on<='1'; end if; - if wc>=O11_L and wc<=O11_R and wr>=O11_T and wr<=O11_B then obs_on<='1'; end if; - if wc>=O12_L and wc<=O12_R and wr>=O12_T and wr<=O12_B then obs_on<='1'; end if; - if wc>=O13_L and wc<=O13_R and wr>=O13_T and wr<=O13_B then obs_on<='1'; end if; - if wc>=O14_L and wc<=O14_R and wr>=O14_T and wr<=O14_B then obs_on<='1'; end if; - if wc>=O15_L and wc<=O15_R and wr>=O15_T and wr<=O15_B then obs_on<='1'; end if; - if wc>=O16_L and wc<=O16_R and wr>=O16_T and wr<=O16_B then obs_on<='1'; end if; + -- Obstacles (loop over level_pkg arrays) + for obs_i in 0 to OBS_COUNT-1 loop + if wc >= OBS_L(obs_i) and wc <= OBS_R(obs_i) and + wr >= OBS_T(obs_i) and wr <= OBS_B(obs_i) then + obs_on <= '1'; + end if; + end loop; end process render; -- 2.51.2