feat: Performance optimizations and UI improvements - Added dormant state tracking for static particles - Fixed settings menu particle spawn overlap - Optimized particle batch processing - Added brush cursor visualization - Improved UI interaction zones - Enhanced gas particle effects - Added glow toggle functionality disabled by default due to performance impact - Fixed particle rendering issues - Debug Overlay Disabled by default - FPS counter added semi seperate from Debug Overlay, Turn this one off when using Debug Overlay - Improved particle rendering performance Performance improvements focus on reducing unnecessary calculations for static particles while maintaining core simulation mechanics. UI changes prevent unwanted particle spawning during menu interactions.
190 lines
7.8 KiB
Python
190 lines
7.8 KiB
Python
"""
|
|
#File Name: sandpypi.py
|
|
# Sandpypi by Stanton.
|
|
# Project name is a placeholder.
|
|
# This has been a multimonth or year project i have time blindness sorta.
|
|
# This is my most functional system for falling sand in python yet i took some things i learned in JS.
|
|
# This needs further optimizations to core performance sections.
|
|
|
|
The main function to run the Sandpypi program.
|
|
|
|
This function initializes the Pygame environment, creates the simulation and rendering objects, and enters the main event loop.
|
|
It handles user input events such as mouse clicks, mouse wheel scrolling, and keyboard presses.
|
|
It also updates the simulation, draws the particles, buttons, and other UI elements, and manages the settings menu.
|
|
The main loop runs at a target frame rate of 60 FPS, with the actual frame rate displayed in the debug overlay.
|
|
"""
|
|
from settings import pygame, engine_settings
|
|
from rendering import Rendering
|
|
from sim import Simulation
|
|
|
|
def main(): # Main function to run the program
|
|
pygame.init()
|
|
clock = pygame.time.Clock()
|
|
width = 1024
|
|
height = 768
|
|
screen = pygame.display.set_mode((width, height), pygame.HWSURFACE | pygame.DOUBLEBUF)
|
|
sim = Simulation(width, height)
|
|
rendering = Rendering(width, height)
|
|
mouse_down_left = False
|
|
mouse_down_right = False
|
|
mouse_down_middle = False
|
|
mouse_down_wheel_up = False
|
|
mouse_down_wheel_down = False
|
|
over_button = False
|
|
zoom_active = False
|
|
zoom_locked = False
|
|
zoom_pos = None
|
|
settings_visible = False
|
|
settings_menu_y = 100
|
|
running = True
|
|
|
|
while running:
|
|
fps = clock.get_fps() # Get the current frame rate
|
|
dt = clock.tick(300) / 1000 # sets the target frame rate to 60 FPS
|
|
keys = pygame.key.get_pressed()
|
|
zoom_active = keys[pygame.K_z]
|
|
|
|
|
|
# Handle events
|
|
for event in pygame.event.get():
|
|
if event.type == pygame.MOUSEBUTTONDOWN:
|
|
mouse_pos = pygame.mouse.get_pos()
|
|
# Check if clicking in settings area when menu is visible
|
|
in_settings_area = False
|
|
if settings_visible:
|
|
settings_rect = pygame.Rect(rendering.width - 320, 100, 300, 400)
|
|
in_settings_area = settings_rect.collidepoint(mouse_pos)
|
|
|
|
if event.button == 4: # Mouse wheel up
|
|
sim.brush_size = min(sim.brush_size + 1, sim.max_brush_size)
|
|
elif event.button == 5: # Mouse wheel down
|
|
sim.brush_size = max(sim.brush_size - 1, 1)
|
|
elif event.button == 1: # Left click
|
|
over_button = False
|
|
|
|
# Check settings button first
|
|
if rendering.settings_button.collidepoint(mouse_pos):
|
|
settings_visible = not settings_visible
|
|
over_button = True
|
|
|
|
if zoom_active:
|
|
zoom_locked = not zoom_locked
|
|
if zoom_locked:
|
|
zoom_pos = mouse_pos
|
|
|
|
# Handle settings menu interactions
|
|
elif settings_visible and in_settings_area:
|
|
relative_y = mouse_pos[1] - settings_menu_y
|
|
setting_index = relative_y // 30
|
|
if 0 <= setting_index < len(engine_settings):
|
|
setting_name = list(engine_settings.keys())[setting_index]
|
|
engine_settings[setting_name] = not engine_settings[setting_name]
|
|
over_button = True
|
|
|
|
elif not in_settings_area:
|
|
|
|
# Check category buttons
|
|
for category, button in rendering.category_buttons.items():
|
|
if button.collidepoint(mouse_pos):
|
|
rendering.current_category = category
|
|
over_button = True
|
|
break
|
|
|
|
# Check particle buttons
|
|
if not over_button:
|
|
for particle_type, button in rendering.buttons.items():
|
|
if button.collidepoint(mouse_pos):
|
|
sim.current_particle_type = particle_type
|
|
over_button = True
|
|
break
|
|
|
|
# Check clear screen button
|
|
if rendering.clear_screen_button.collidepoint(mouse_pos):
|
|
rendering.clear_screen(sim)
|
|
over_button = True
|
|
|
|
if not over_button and not in_settings_area:
|
|
mouse_down_left = True
|
|
|
|
elif event.button == 3: # Right click
|
|
mouse_down_right = True
|
|
|
|
elif event.button == 2: # Middle click
|
|
mouse_down_middle = True
|
|
|
|
elif event.type == pygame.MOUSEBUTTONUP and event.button == 1:
|
|
mouse_down_left = False
|
|
elif event.type == pygame.MOUSEBUTTONUP and event.button == 3:
|
|
mouse_down_right = False
|
|
elif event.type == pygame.MOUSEBUTTONUP and event.button == 2:
|
|
mouse_down_middle = False
|
|
|
|
elif event.type == pygame.KEYDOWN:
|
|
if event.key == pygame.K_ESCAPE:
|
|
print("Escape button pressed")
|
|
print(f"Exiting Program {__file__}")
|
|
running = False
|
|
|
|
elif event.key == pygame.K_SPACE:
|
|
print(f"Pause button pressed but not functional 'Space'")
|
|
pass
|
|
|
|
elif event.key == pygame.K_c:
|
|
rendering.clear_screen(sim)
|
|
|
|
elif event.key == pygame.K_z:
|
|
zoom_active = not zoom_active
|
|
if zoom_active:
|
|
zoom_locked = False
|
|
zoom_pos = pygame.mouse.get_pos()
|
|
|
|
elif event.type == pygame.QUIT:
|
|
running = False
|
|
|
|
if mouse_down_left and not over_button:
|
|
x, y = pygame.mouse.get_pos()
|
|
sim.create_particle_circle(x, y)
|
|
|
|
if mouse_down_right:
|
|
x, y = pygame.mouse.get_pos()
|
|
sim.clear_particles_circle(x, y)
|
|
|
|
if mouse_down_middle:
|
|
x, y = pygame.mouse.get_pos()
|
|
sim.create_particle(x, y)
|
|
|
|
if zoom_active or zoom_locked:
|
|
mouse_pos = zoom_pos if zoom_locked else pygame.mouse.get_pos()
|
|
zoom_surface = rendering.draw_zoom_window(sim.particles, sim.particle_size, rendering.particle_colors, mouse_pos)
|
|
# Position zoom window
|
|
zoom_x = 10 if mouse_pos[0] > width/2 else width - 110
|
|
zoom_y = 10 if mouse_pos[1] > height/2 else height - 110
|
|
screen.blit(zoom_surface, (zoom_x, zoom_y))
|
|
|
|
# Draw Settings
|
|
if settings_visible:
|
|
settings_menu = rendering.draw_settings_menu()
|
|
rendering.screen.blit(settings_menu, (rendering.width - 320, 100))
|
|
rendering.draw_debug_overlay(fps, sim.particles)
|
|
pygame.display.flip()
|
|
|
|
# Update and draw particles
|
|
sim.simulate_step(dt=0.016)
|
|
# Draw particles
|
|
rendering.draw_particles(sim.particles, sim.active_particles, sim.particle_size, rendering.particle_colors)
|
|
# Draw buttons
|
|
rendering.draw_buttons()
|
|
# Draw brush size slider
|
|
rendering.draw_brush_size_slider(sim.brush_size)
|
|
# Get current mouse position
|
|
mouse_x, mouse_y = pygame.mouse.get_pos()
|
|
# Draw brush cursor at mouse position
|
|
rendering.render_brush_cursor(mouse_x, mouse_y, sim.brush_size * sim.particle_size)
|
|
|
|
|
|
|
|
pygame.quit()
|
|
|
|
if __name__ == "__main__":
|
|
main()
|