Compare commits

...

3 Commits
dev ... master

Author SHA1 Message Date
3e6796220d revert 6a0de3601989df8820bbdf9986aed8f04869b71e
There appears to be some differences between master and my local machines so this didn't work as expected.

revert Updated sim.py

updated sim.py with performance patch from the dev branch.
2024-12-29 12:25:21 -06:00
6a0de36019 Updated sim.py
updated sim.py with performance patch from the dev branch.
2024-12-29 12:14:32 -06:00
83ba8baba5 revert 3059e17622428b804e196941eb21719399a9208a
revert reorganizations.
2024-12-28 20:15:57 -06:00
15 changed files with 81 additions and 187 deletions

2
__init__.py Normal file
View File

@ -0,0 +1,2 @@
import os
import sys

View File

@ -17,7 +17,7 @@
"friction": 0.5, "friction": 0.5,
"viscosity": 0, "viscosity": 0,
"pressure": 0, "pressure": 0,
"melt": "molten-glass", "melt": "molten-Glass",
"melt_temperature": 1700, "melt_temperature": 1700,
"conductive": false, "conductive": false,
"liquid": false, "liquid": false,

View File

@ -22,9 +22,8 @@ The `clear_screen` function is used to reset the simulation grid and clear the d
""" """
from config.settings import pygame, random, particle_properties, engine_settings from settings import pygame, random, particle_properties, engine_settings
from typing import List, Dict
import colorsys
class Rendering: class Rendering:

View File

@ -11,22 +11,19 @@ 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. 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 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. 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 (this fps varies on my mood and the testing), with the actual frame rate displayed in the debug overlay. The main loop runs at a target frame rate of 60 FPS, with the actual frame rate displayed in the debug overlay.
""" """
import cProfile
# Import Require files for the Engine. import pstats
from config.settings import pygame, cProfile, pstats, engine_settings from settings import pygame, engine_settings
from rendering import Rendering
from rendering.rendering import Rendering
from physics.sim import Simulation
""" """
This is for the future physics engine until i figure out a better method used for testing right now. This is for the future physics engine until i figure out a better method used for testing right now.
#import os #import os
#import sys #import sys
#sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) #sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
""" """
from sim import Simulation
def update_simulation(sim, dt, engine_settings): def update_simulation(sim, dt, engine_settings):
"""Update simulation state""" """Update simulation state"""
@ -181,7 +178,7 @@ def main():
screen.fill((0, 0, 0)) screen.fill((0, 0, 0))
fps = clock.get_fps() fps = clock.get_fps()
dt = clock.tick(60) / 1000 dt = clock.tick(1000) / 1000
keys = pygame.key.get_pressed() keys = pygame.key.get_pressed()
mouse_pos = pygame.mouse.get_pos() mouse_pos = pygame.mouse.get_pos()
zoom_active = keys[pygame.K_z] zoom_active = keys[pygame.K_z]

47
settings.py Normal file
View File

@ -0,0 +1,47 @@
"""
#File Name: settings.py
Global settings and imports for the project.
This module defines various settings for the game engine, such as enabling or disabling the cursor, glow effect, gas effect, debug mode, and FPS display. It also provides a function to load particle properties from a JSON file.
The `engine_settings` dictionary contains the configurable settings for the game engine. These settings can be used to customize the behavior of the game.
The `load_particle_properties()` function attempts to load particle properties from a 'particles.json' file. If the file is not found or the JSON data is invalid, it returns an empty dictionary.
The `particle_properties` variable is initialized by calling `load_particle_properties()` when the module is imported.
"""
import pygame
import json
import random
import time
import numpy as np
engine_settings = {
'pause_sim': True,
'enable_cursor': True,
'enable_glow': False,
'enable_gas_effect': True,
'enable_debug': False,
'enable_fps': True,
'enable_WVisuals': False,
'enable_PVisuals': False,
'enable_TempVisuals': False,
'outerwall': True,
# 'settings': True/False
}
# Load particle properties from JSON file
def load_particle_properties():
try:
with open('particles.json') as f:
return json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
print("Error loading particles.json")
return {}
# Load particle properties once when module is imported
particle_properties = load_particle_properties()

View File

@ -8,21 +8,18 @@ This module implements a 2D particle simulation with physics, interactions, and
Key Components: Key Components:
-------------- --------------
1. Particle Class 1. Particle Class
- Handles individual particle properties and behaviors - Handles individual particle properties and behaviors
- Supports multiple particle types (solid, liquid, gas) - Supports multiple particle types (solid, liquid, gas)
- Manages temperature and state transitions
2. Simulation Class 2. Simulation Class
- Core simulation engine - Core simulation engine
- Manages particle creation, movement and interactions - Manages particle creation, movement and interactions
- Handles physics calculations and spatial partitioning - Handles physics calculations and spatial partitioning
- Manages temperature and state transitions
""" """
#Load the imports. #Load the imports. Pygame is what makes this even work and so simple may consider other engines for performance depends on learning curve.
from config.settings import random, time, particle_properties from settings import random, time, particle_properties
from dataclasses import dataclass
import math
# Load particle properties from json so we know what particles we got and how they should be simulated. # Load particle properties from json so we know what particles we got and how they should be simulated.
class Particle: class Particle:
@ -100,7 +97,6 @@ class Simulation:
self.wind_zones = [] self.wind_zones = []
self.wind = [0.0, 0.0] # Global wind vector (x, y) self.wind = [0.0, 0.0] # Global wind vector (x, y)
def reset_particle_count(self): def reset_particle_count(self):
self.particle_count = 0 self.particle_count = 0
@ -127,20 +123,6 @@ class Simulation:
return cell_key return cell_key
def _get_neighbors_from_grid(self, x, y):
"""Get neighbors using spatial grid"""
cell_key = self.get_cell_key(x, y)
neighbors = []
# Check current and adjacent cells
for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
check_key = (cell_key[0] + dx, cell_key[1] + dy)
if check_key in self.spatial_grid:
neighbors.extend(self.spatial_grid[check_key])
return neighbors
def update_spatial_grid(self): # this is where we update the spatial grid. def update_spatial_grid(self): # this is where we update the spatial grid.
"""Update spatial grid for optimized collision detection""" """Update spatial grid for optimized collision detection"""
if len(self.active_particles) > 100: if len(self.active_particles) > 100:
@ -155,7 +137,6 @@ class Simulation:
self.spatial_grid = {k: set(v) for k, v in cell_lists.items()} self.spatial_grid = {k: set(v) for k, v in cell_lists.items()}
def _check_dormant_state(self, x, y, particle): def _check_dormant_state(self, x, y, particle):
key = (x, y) key = (x, y)
if particle.particle_type == 'wall': if particle.particle_type == 'wall':
@ -178,7 +159,6 @@ class Simulation:
self.dormant_particles.discard(key) self.dormant_particles.discard(key)
return False return False
def handle_phase_transitions(self, particle, x, y): # this is where we handle all the phase transitions. def handle_phase_transitions(self, particle, x, y): # this is where we handle all the phase transitions.
"""Handle all phase transitions for a particle""" """Handle all phase transitions for a particle"""
# Check evaporation # Check evaporation
@ -207,7 +187,6 @@ class Simulation:
if new_type in self.particle_properties: if new_type in self.particle_properties:
self.transform_particle(x, y, new_type) self.transform_particle(x, y, new_type)
def handle_particle_interactions(self, dt): # this is where we handle all the particle interactions. def handle_particle_interactions(self, dt): # this is where we handle all the particle interactions.
"""Handle interactions between different particle types""" """Handle interactions between different particle types"""
for x, y in list(self.active_particles): for x, y in list(self.active_particles):
@ -281,7 +260,6 @@ class Simulation:
self.active_particles.add((new_x, new_y)) self.active_particles.add((new_x, new_y))
self.active_particles.discard((x, y)) self.active_particles.discard((x, y))
def add_wind_zone(self, x, y): def add_wind_zone(self, x, y):
# Instead of creating particles, store wind zone data # Instead of creating particles, store wind zone data
wind_zone = { wind_zone = {
@ -321,18 +299,15 @@ class Simulation:
fy += drag * particle.velocity[1] fy += drag * particle.velocity[1]
# Check neighboring particles # Check neighboring particles
neighbors = self._get_neighbors_from_grid(x, y) neighbors = self._get_quick_neighbors(x, y)
for nx, ny in neighbors: for nx, ny in neighbors:
if(nx, ny) != (x, y): if 0 <= nx < self.width and 0 <= ny < self.height:
if 0 <= nx < self.width and 0 <= ny < self.height: neighbor = self.particles[nx][ny]
neighbor = self.particles[nx][ny] if neighbor:
if neighbor: self._apply_neighbor_forces(particle, neighbor, fx, fy)
self._apply_neighbor_forces(particle, neighbor, fx, fy)
return fx, fy return fx, fy
def _process_particle_batch(self, batch, dt): def _process_particle_batch(self, batch, dt):
updates = [] updates = []
new_active = set() new_active = set()
@ -381,19 +356,16 @@ class Simulation:
return new_active return new_active
def _get_quick_neighbors(self, x, y): def _get_quick_neighbors(self, x, y):
"""Quick neighbor lookup without full spatial grid""" """Quick neighbor lookup without full spatial grid"""
return [(x+dx, y+dy) for dx, dy in [(-1,0), (1,0), (0,-1), (0,1)]] return [(x+dx, y+dy) for dx, dy in [(-1,0), (1,0), (0,-1), (0,1)]]
def _apply_neighbor_forces(self, particle, neighbor, fx, fy): def _apply_neighbor_forces(self, particle, neighbor, fx, fy):
"""Optimized neighbor force calculation""" """Optimized neighbor force calculation"""
if hasattr(neighbor, 'temperature') and hasattr(particle, 'temperature'): if hasattr(neighbor, 'temperature') and hasattr(particle, 'temperature'):
temp_diff = neighbor.temperature - particle.temperature temp_diff = neighbor.temperature - particle.temperature
fy += temp_diff * 0.05 fy += temp_diff * 0.05
def ignite_particle(self, particle): # this is where we ignite the particle. def ignite_particle(self, particle): # this is where we ignite the particle.
"""Handle ignition and burning of flammable particles.""" """Handle ignition and burning of flammable particles."""
if hasattr(particle, 'flamability') and particle.flamability > 0.5: if hasattr(particle, 'flamability') and particle.flamability > 0.5:
@ -533,7 +505,6 @@ class Simulation:
return particle.particle_type return particle.particle_type
return None return None
def apply_gravity(self, dt): # this is where we apply gravity. def apply_gravity(self, dt): # this is where we apply gravity.
"""Handle only gravity and basic particle movement""" """Handle only gravity and basic particle movement"""
self.spatial_grid.clear() self.spatial_grid.clear()
@ -774,7 +745,6 @@ class Simulation:
self.dormant_particles.discard(key) self.dormant_particles.discard(key)
self.particle_movement_counter[key] = 0 self.particle_movement_counter[key] = 0
def track_tps(self): def track_tps(self):
"""Track Ticks Per Second for simulation performance monitoring""" """Track Ticks Per Second for simulation performance monitoring"""
if not hasattr(self, '_tps_counter'): if not hasattr(self, '_tps_counter'):
@ -796,17 +766,19 @@ class Simulation:
def simulate_step(self, dt, engine_settings): def simulate_step(self, dt, engine_settings):
"""Run simulation step with spatial grid updates""" """Run a single step of the simulation"""
self.update_spatial_grid()
active_list = list(self.active_particles) active_list = list(self.active_particles)
batch_size = 1024 batch_size = 1000
for i in range(0, len(active_list), batch_size): for i in range(0, len(active_list), batch_size):
batch = active_list[i:i + batch_size] batch = active_list[i:i + batch_size]
self._process_particle_batch(batch, dt) self._process_particle_batch(batch, dt)
# Update spatial grid only when needed
if len(self.active_particles) > 100:
self.update_spatial_grid()
# Update particle positions and physics # Update particle positions and physics
self.apply_gravity(dt) self.apply_gravity(dt)
self.apply_physics(dt, engine_settings) self.apply_physics(dt, engine_settings)

View File

View File

View File

@ -1,74 +0,0 @@
"""
#File Name: settings.py
Global settings and imports for the project.
This module defines various settings for the game engine, such as enabling or disabling the cursor, glow effect, gas effect, debug mode, and FPS display. It also provides a function to load particle properties from a JSON file.
The `engine_settings` dictionary contains the configurable settings for the game engine. These settings can be used to customize the behavior of the game.
The `load_particle_properties()` function attempts to load particle properties from a 'particles.json' file. If the file is not found or the JSON data is invalid, it returns an empty dictionary.
The `particle_properties` variable is initialized by calling `load_particle_properties()` when the module is imported.
"""
import cProfile
import pstats
import sys
import os
import pygame
import json
import random
import time
import numpy as np
engine_settings = {
'pause_sim': True,
'enable_cursor': True,
'enable_glow': False,
'enable_gas_effect': True,
'enable_debug': True,
'enable_fps': True,
'enable_WVisuals': False,
'enable_PVisuals': False,
'enable_TempVisuals': False,
'outerwall': False,
# 'settings': True/False
}
# For particles.json loading in settings.py:
particles_path = os.path.join(os.path.dirname(__file__), '..', 'part', 'particles.json')
# Load particle properties from JSON file
def load_particle_properties():
# Load core particles
particle_data = {}
print(f"Loading particles from {particles_path}")
# Core particles
try:
with open(particles_path, 'r') as f:
particle_data.update(json.load(f))
print(f"Loaded {len(particle_data)} core particles")
except (FileNotFoundError, json.JSONDecodeError) as e:
print(f"Error loading core particles: {e}")
# Load mods from mods directory
mods_path = os.path.join(os.path.dirname(__file__), '..', 'part', 'mods')
if os.path.exists(mods_path):
for filename in os.listdir(mods_path):
if filename.endswith('.json'):
mod_file = os.path.join(mods_path, filename)
try:
with open(mod_file, 'r') as f:
mod_data = json.load(f)
print(f"Loading mod: {filename}")
particle_data.update(mod_data)
print(f"Loaded {len(mod_data)} particles from {filename}")
except (FileNotFoundError, json.JSONDecodeError) as e:
print(f"Error loading mod {filename}: {e}")
return particle_data
# Load particle properties once when module is imported
particle_properties = load_particle_properties()
__all__ = ['pygame', 'np', 'random', 'time', 'engine_settings', 'particle_properties', 'cProfile', 'pstats', 'sys', 'os']

View File

View File

@ -1,49 +0,0 @@
{
"wsand": {
"name": "Wet Sand",
"size": 1,
"hardness": 0.5,
"color": [200, 200, 25, 255],
"velocity": 0.5,
"mass": 0.5,
"conductivity": 0,
"heat_capacity": 1,
"flamability": 0.8,
"temperature": 20,
"explosive": false,
"explosion_radius": 0,
"explosion_color": [0, 0, 0],
"friction": 0.5,
"viscosity": 0.3,
"pressure": 0,
"melt": "sand",
"melt_temperature": 100,
"conductive": false,
"liquid": false,
"solid": true,
"is_gas": false
},
"ultratanium": {
"name": "Ultra Tanium",
"size": 1,
"hardness": 1000,
"velocity": 0.1,
"conductivity": 1,
"heat_capacity": 1,
"color": [255, 200, 255, 255],
"pressure": 100,
"explosive": true,
"explosion_radius": 15,
"explosion_color": [
255,
0,
255
],
"temperature": 10000,
"conductive": true,
"liquid": true,
"solid": true,
"is_gas": true,
"special": true
}
}

View File