updated to me random projects

This commit is contained in:
OusmBlueNinja 2025-03-30 20:23:07 -05:00
parent b96c0a2973
commit db5400fcaa
5 changed files with 235 additions and 0 deletions

View 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()