# Example file showing a circle moving on screen import pygame import math from engine import Engine from transmission import Transmission # pygame setup pygame.init() pygame.display.set_caption('Engine Sim') screen = pygame.display.set_mode((1280, 720)) clock = pygame.time.Clock() running = True dt = 0 # Text Setup pygame.font.init() font = pygame.font.SysFont(None, 64) gauge_font = pygame.font.SysFont(None, 24) # Gauge Setup rpm_pos = pygame.Vector2(screen.get_width() * 0.30, screen.get_height() / 2) spd_pos = pygame.Vector2(screen.get_width() * 0.70, screen.get_height() / 2) gauge_radius = screen.get_height() / 5 # Angle for gauge angle_rad = math.radians(145) needle_color = "white" needle_width = 4 tip_radius = needle_width offset_vector = pygame.Vector2( math.cos(angle_rad) * gauge_radius, math.sin(angle_rad) * gauge_radius ) e = Engine() t = Transmission(e) throttle = 0 def map_value_to_angle(value, min_val, max_val): clamped = max(min_val, value) return math.radians(150 + 270 * (clamped - min_val) / (max_val - min_val)) while running: screen.fill("white") sX, sY = screen.get_width(), screen.get_height() mX, mY = pygame.mouse.get_pos() # Gauge Cluster cluster_padding = (sX / sY) * (sX / 64) cluster_padding_x = (sX / sY) * (sX / 64) cluster_y = min(rpm_pos.y, spd_pos.y) - gauge_radius - cluster_padding cluster_bottom = max(rpm_pos.y, spd_pos.y) + gauge_radius + cluster_padding cluster_x = rpm_pos.x - gauge_radius - cluster_padding - cluster_padding_x cluster_right = spd_pos.x + gauge_radius + cluster_padding + cluster_padding_x cluster_width = cluster_right - cluster_x cluster_height = cluster_bottom - cluster_y cluster_rect = pygame.Rect(cluster_x, cluster_y, cluster_width, cluster_height) pygame.draw.rect(screen, (50, 50, 50), cluster_rect, border_radius=30) # dark gray with rounded corners # poll for events # pygame.QUIT event means the user clicked X to close your window for event in pygame.event.get(): if event.type == pygame.KEYDOWN: if (pygame.key.name(event.key) == 'v'): if e.ignition: e.ignition = False else: e.start(dt) if (pygame.key.name(event.key) == 'x'): t.upshift() if (pygame.key.name(event.key) == 'z'): t.downshift() if (pygame.key.name(event.key) == 'left shift'): t.clutch_pressure = 1 if event.type == pygame.KEYUP: if (pygame.key.name(event.key) == 'left shift'): t.clutch_pressure = 0 if event.type == pygame.QUIT: running = False rpm_text = font.render(f"RPM: {int(e.rpm)}", True, (0, 0, 0)) rpm_rect = rpm_text.get_rect(center=(screen.get_width() // 2, screen.get_height() // 4)) screen.blit(rpm_text, rpm_rect) rpm_text = font.render(f"Torque: {int(e.instant_torque)}", True, (255, 0, 0)) rpm_rect = rpm_text.get_rect(center=(screen.get_width() // 2, screen.get_height() // 4 +100)) screen.blit(rpm_text, rpm_rect) rpm_angle = map_value_to_angle(e.rpm, 0, 8000) # RPM Gauge rpm_vector = pygame.Vector2(math.cos(rpm_angle), math.sin(rpm_angle)) * gauge_radius pygame.draw.circle(screen, "black", rpm_pos, gauge_radius) pygame.draw.line(screen, needle_color, rpm_pos, rpm_pos + rpm_vector, needle_width) for rpm_tick in range(0, 9000, 1000): # 1000 to 8000 angle = map_value_to_angle(rpm_tick, 0, 8000) direction = pygame.Vector2(math.cos(angle), math.sin(angle)) label_pos = rpm_pos + direction * (gauge_radius - 20) # slight inward offset label_text = gauge_font.render(str(rpm_tick // 1000), True, (255, 255, 255)) text_rect = label_text.get_rect(center=(label_pos.x, label_pos.y)) screen.blit(label_text, text_rect) # Speed Gauge speed_angle = map_value_to_angle(t.get_velocity(), 0, 160) speed_vector = pygame.Vector2(math.cos(speed_angle), math.sin(speed_angle)) * gauge_radius pygame.draw.circle(screen, "black", spd_pos, gauge_radius) pygame.draw.line(screen, needle_color, spd_pos, spd_pos + speed_vector, needle_width) for speed_tick in range(0, 180, 20): # 1000 to 8000 angle = map_value_to_angle(speed_tick, 0, 160) direction = pygame.Vector2(math.cos(angle), math.sin(angle)) label_pos = spd_pos + direction * (gauge_radius - 20) # slight inward offset label_text = gauge_font.render(str(speed_tick), True, (255, 255, 255)) text_rect = label_text.get_rect(center=(label_pos.x, label_pos.y)) screen.blit(label_text, text_rect) # Fuel Gauge - 10 Steps fuel_steps = math.ceil((e.fuel_capacity / e.max_fuel_capacity) * 10) fuel_x = cluster_x + (cluster_padding / 1.5) fuel_y = cluster_bottom - cluster_padding * 2 fuel_width = cluster_padding / 1.5 fuel_height = cluster_height / 20 fuel_step = cluster_height / 16 #fuel_rect = pygame.rect() Make this a rect as a backdrop for fuel for i in range(fuel_steps): fuel_rect = pygame.Rect(fuel_x, fuel_y, fuel_width, fuel_height) pygame.draw.rect(screen, "white", fuel_rect, border_radius=2) fuel_y -= fuel_step # Temp Gauge - 10 Steps # Throttle throttle_text = font.render(f"Throttle", True, (0, 0, 0)) throttle_text_rect = throttle_text.get_rect(center=((sX - sX // 16), screen.get_height() // 4.5)) screen.blit(throttle_text, throttle_text_rect) throttle_rect = pygame.Rect((sX - sX / 16), sY / 4, sX / 16, sY / 2) pygame.draw.rect(screen, "red", throttle_rect) keys = pygame.key.get_pressed() if keys[pygame.K_w]: e.rpm += 600 * dt if keys[pygame.K_s]: e.rpm -= 600 * dt if keys[pygame.K_r]: e.rpm = e.idle_rpm # Throttle (Using mouse pos) throttle = pow(max(0, min(1 - (((mY - (sY / 4)) / 2) / (sY / 4)), 1)),2) # Clamp value between 0 and 1 # print(mX, mY) pygame.display.flip() # limits FPS to 60 # dt is delta time in seconds since last frame, used for framerate- # independent physics. dt = clock.tick(60) / 1000 e.update(throttle, 0, dt) pygame.quit()