updated to me random projects
This commit is contained in:
parent
b96c0a2973
commit
db5400fcaa
235
tragectory-prediction/main.py
Normal file
235
tragectory-prediction/main.py
Normal file
@ -0,0 +1,235 @@
|
||||
import pygame
|
||||
import math
|
||||
import random
|
||||
|
||||
# Initialize PyGame and create window
|
||||
pygame.init()
|
||||
width, height = 800, 600
|
||||
screen = pygame.display.set_mode((width, height))
|
||||
pygame.display.set_caption("Drag to Shoot with Prediction & Trails")
|
||||
clock = pygame.time.Clock()
|
||||
|
||||
# Constants
|
||||
GRAVITY = 300 # pixels per second^2
|
||||
VELOCITY_SCALE = 0.8 # multiplier for the drag-to-velocity conversion (a stronger shot)
|
||||
PREDICTION_TIME = 3.0 # seconds to simulate the predicted trajectory
|
||||
PREDICTION_DT = 0.02 # simulation time step for prediction
|
||||
BALL_RADIUS = 8 # radius for each ball
|
||||
PREDICTION_DISPLAY_TIME = 5.0 # seconds to display the prediction after launch
|
||||
MAX_TRAIL_LENGTH = PREDICTION_DISPLAY_TIME * VELOCITY_SCALE * PREDICTION_TIME * 1000 # maximum number of positions stored for ball trail
|
||||
|
||||
# List to hold active projectiles (each is a dict with position, velocity, lifetime, and a trail)
|
||||
projectiles = []
|
||||
|
||||
# For storing the last prediction (list of positions) and a timer to display it after shot
|
||||
last_prediction = None
|
||||
prediction_timer = 0
|
||||
|
||||
# -----------------------
|
||||
# Collision Handling Code
|
||||
# -----------------------
|
||||
|
||||
def handle_collisions(balls):
|
||||
"""
|
||||
For each pair of balls in the list, check if they overlap.
|
||||
If so, separate them and update their velocities using a simple
|
||||
elastic collision formula (assuming equal masses).
|
||||
"""
|
||||
for i in range(len(balls)):
|
||||
for j in range(i + 1, len(balls)):
|
||||
ball1 = balls[i]
|
||||
ball2 = balls[j]
|
||||
dx = ball1["pos"][0] - ball2["pos"][0]
|
||||
dy = ball1["pos"][1] - ball2["pos"][1]
|
||||
dist_sq = dx * dx + dy * dy
|
||||
min_dist = ball1["radius"] + ball2["radius"]
|
||||
if dist_sq < min_dist * min_dist:
|
||||
dist = math.sqrt(dist_sq) if dist_sq != 0 else 0.1
|
||||
# Calculate overlap amount
|
||||
overlap = 0.5 * (min_dist - dist)
|
||||
nx = dx / dist
|
||||
ny = dy / dist
|
||||
# Separate the balls so they just touch
|
||||
ball1["pos"][0] += nx * overlap
|
||||
ball1["pos"][1] += ny * overlap
|
||||
ball2["pos"][0] -= nx * overlap
|
||||
ball2["pos"][1] -= ny * overlap
|
||||
# Relative velocity in normal direction
|
||||
dvx = ball1["vel"][0] - ball2["vel"][0]
|
||||
dvy = ball1["vel"][1] - ball2["vel"][1]
|
||||
dot = dvx * nx + dvy * ny
|
||||
if dot < 0:
|
||||
# Adjust velocities for a simple elastic collision
|
||||
ball1["vel"][0] -= dot * nx
|
||||
ball1["vel"][1] -= dot * ny
|
||||
ball2["vel"][0] += dot * nx
|
||||
ball2["vel"][1] += dot * ny
|
||||
|
||||
# -----------------------
|
||||
# Prediction Simulation
|
||||
# -----------------------
|
||||
|
||||
def simulate_prediction(initial_pos, initial_vel, other_balls, prediction_time=PREDICTION_TIME, dt_sim=PREDICTION_DT):
|
||||
"""
|
||||
Simulate the predicted path of a new ball starting at 'initial_pos'
|
||||
with initial velocity 'initial_vel'. The simulation takes into account
|
||||
gravity, wall bounces (using the ball radius), and collisions with other balls.
|
||||
Returns a list of (x, y) positions for the predicted ball.
|
||||
"""
|
||||
# Create a copy for the predicted ball (with the same BALL_RADIUS)
|
||||
predicted_ball = {"pos": [initial_pos[0], initial_pos[1]],
|
||||
"vel": [initial_vel[0], initial_vel[1]],
|
||||
"radius": BALL_RADIUS}
|
||||
# Copy the other balls from the current simulation (so as not to modify them)
|
||||
predicted_others = []
|
||||
for ball in other_balls:
|
||||
new_ball = {"pos": [ball["pos"][0], ball["pos"][1]],
|
||||
"vel": [ball["vel"][0], ball["vel"][1]],
|
||||
"radius": ball.get("radius", BALL_RADIUS)}
|
||||
predicted_others.append(new_ball)
|
||||
balls = [predicted_ball] + predicted_others
|
||||
|
||||
positions = []
|
||||
steps = int(prediction_time / dt_sim)
|
||||
for _ in range(steps):
|
||||
# Update every ball in simulation
|
||||
for ball in balls:
|
||||
ball["vel"][1] += GRAVITY * dt_sim
|
||||
ball["pos"][0] += ball["vel"][0] * dt_sim
|
||||
ball["pos"][1] += ball["vel"][1] * dt_sim
|
||||
# Bounce off left/right walls (accounting for ball radius)
|
||||
if ball["pos"][0] < ball["radius"]:
|
||||
ball["pos"][0] = ball["radius"]
|
||||
ball["vel"][0] = -ball["vel"][0]
|
||||
elif ball["pos"][0] > width - ball["radius"]:
|
||||
ball["pos"][0] = width - ball["radius"]
|
||||
ball["vel"][0] = -ball["vel"][0]
|
||||
# Bounce off top/bottom walls
|
||||
if ball["pos"][1] < ball["radius"]:
|
||||
ball["pos"][1] = ball["radius"]
|
||||
ball["vel"][1] = -ball["vel"][1]
|
||||
elif ball["pos"][1] > height - ball["radius"]:
|
||||
ball["pos"][1] = height - ball["radius"]
|
||||
ball["vel"][1] = -ball["vel"][1]
|
||||
# Handle collisions among balls
|
||||
handle_collisions(balls)
|
||||
# Record the predicted ball's position
|
||||
positions.append((int(predicted_ball["pos"][0]), int(predicted_ball["pos"][1])))
|
||||
return positions
|
||||
|
||||
# -----------------------
|
||||
# Main Simulation Loop
|
||||
# -----------------------
|
||||
|
||||
# Variables for the drag-to-aim mechanic
|
||||
dragging = False
|
||||
start_pos = (0, 0)
|
||||
current_pos = (0, 0)
|
||||
|
||||
running = True
|
||||
while running:
|
||||
dt = clock.tick(60) / 1000.0 # delta time in seconds
|
||||
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
running = False
|
||||
|
||||
# Start dragging
|
||||
elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
|
||||
dragging = True
|
||||
start_pos = event.pos
|
||||
current_pos = event.pos
|
||||
|
||||
# Update drag position while holding down the mouse
|
||||
elif event.type == pygame.MOUSEMOTION and dragging:
|
||||
current_pos = event.pos
|
||||
|
||||
# On mouse release, launch a new ball and compute its prediction.
|
||||
elif event.type == pygame.MOUSEBUTTONUP and event.button == 1:
|
||||
if dragging:
|
||||
# Compute initial velocity (invert drag vector so dragging backward shoots forward)
|
||||
v0 = ((start_pos[0] - current_pos[0]) * VELOCITY_SCALE,
|
||||
(start_pos[1] - current_pos[1]) * VELOCITY_SCALE)
|
||||
# Compute and store the prediction simulation (will be displayed for a short time)
|
||||
last_prediction = simulate_prediction(start_pos, v0, projectiles)
|
||||
prediction_timer = PREDICTION_DISPLAY_TIME
|
||||
# Create a new ball with an initial empty trail
|
||||
new_ball = {"pos": [start_pos[0], start_pos[1]],
|
||||
"vel": [v0[0], v0[1]],
|
||||
"radius": BALL_RADIUS,
|
||||
"lifetime": random.uniform(3, 5),
|
||||
"trail": []}
|
||||
projectiles.append(new_ball)
|
||||
dragging = False
|
||||
|
||||
# Update active projectiles (balls)
|
||||
for ball in projectiles:
|
||||
# Append current position to the trail
|
||||
ball["trail"].append((ball["pos"][0], ball["pos"][1]))
|
||||
if len(ball["trail"]) > MAX_TRAIL_LENGTH:
|
||||
ball["trail"].pop(0)
|
||||
|
||||
# Update velocity (apply gravity) and position
|
||||
ball["vel"][1] += GRAVITY * dt
|
||||
ball["pos"][0] += ball["vel"][0] * dt
|
||||
ball["pos"][1] += ball["vel"][1] * dt
|
||||
|
||||
# Bounce off the walls, accounting for ball radius
|
||||
if ball["pos"][0] < ball["radius"]:
|
||||
ball["pos"][0] = ball["radius"]
|
||||
ball["vel"][0] = -ball["vel"][0]
|
||||
elif ball["pos"][0] > width - ball["radius"]:
|
||||
ball["pos"][0] = width - ball["radius"]
|
||||
ball["vel"][0] = -ball["vel"][0]
|
||||
if ball["pos"][1] < ball["radius"]:
|
||||
ball["pos"][1] = ball["radius"]
|
||||
ball["vel"][1] = -ball["vel"][1]
|
||||
elif ball["pos"][1] > height - ball["radius"]:
|
||||
ball["pos"][1] = height - ball["radius"]
|
||||
ball["vel"][1] = -ball["vel"][1]
|
||||
|
||||
# Decrease lifetime (ball will vanish after its lifetime expires)
|
||||
ball["lifetime"] -= dt
|
||||
|
||||
# Remove expired balls
|
||||
projectiles = [ball for ball in projectiles if ball["lifetime"] > 0]
|
||||
|
||||
# Handle collisions among active projectiles
|
||||
handle_collisions(projectiles)
|
||||
|
||||
# Decrease the prediction display timer
|
||||
if prediction_timer > 0:
|
||||
prediction_timer -= dt
|
||||
if prediction_timer <= 0:
|
||||
last_prediction = None
|
||||
|
||||
# -----------------------
|
||||
# Drawing Section
|
||||
# -----------------------
|
||||
screen.fill((255, 255, 255))
|
||||
|
||||
# While dragging, draw drag line and live predicted trajectory
|
||||
if dragging:
|
||||
pygame.draw.line(screen, (0, 0, 0), start_pos, current_pos, 2)
|
||||
v0 = ((start_pos[0] - current_pos[0]) * VELOCITY_SCALE,
|
||||
(start_pos[1] - current_pos[1]) * VELOCITY_SCALE)
|
||||
live_prediction = simulate_prediction(start_pos, v0, projectiles)
|
||||
if len(live_prediction) > 1:
|
||||
pygame.draw.lines(screen, (0, 0, 255), False, live_prediction, 2)
|
||||
|
||||
# Draw the stored prediction (after shot) if available
|
||||
if last_prediction is not None:
|
||||
pygame.draw.lines(screen, (128, 0, 128), False, last_prediction, 2)
|
||||
|
||||
# Draw projectiles and their trails
|
||||
for ball in projectiles:
|
||||
# Draw the trail as a line (if there are at least 2 points)
|
||||
if len(ball["trail"]) > 1:
|
||||
pygame.draw.lines(screen, (150, 150, 150), False, ball["trail"], 2)
|
||||
# Draw the ball as a filled circle
|
||||
pos = (int(ball["pos"][0]), int(ball["pos"][1]))
|
||||
pygame.draw.circle(screen, (255, 0, 0), pos, ball["radius"])
|
||||
|
||||
pygame.display.flip()
|
||||
|
||||
pygame.quit()
|
Loading…
Reference in New Issue
Block a user