Initial commit
This commit is contained in:
+138
@@ -0,0 +1,138 @@
|
||||
# launcher.py
|
||||
#
|
||||
# Represents the top-center launcher tube.
|
||||
#
|
||||
# Features:
|
||||
# - Pivot point attached at top-center of the screen (x = width / 2, y = -15),
|
||||
# extending above the screen top so it is clipped by the screen boundary.
|
||||
# - Consists ONLY of a short, elongated solid tube/barrel (no base/tank/turret).
|
||||
# - Continuous smooth rotation between LAUNCHER_MIN_ANGLE and LAUNCHER_MAX_ANGLE.
|
||||
# - Provides collision parameters for PhysicsWorld (_Solid OBB).
|
||||
# - Provides ball spawn transform (position + velocity) outward from nozzle exit.
|
||||
|
||||
import math
|
||||
|
||||
from config import (
|
||||
LAUNCHER_MIN_ANGLE,
|
||||
LAUNCHER_MAX_ANGLE,
|
||||
LAUNCHER_LENGTH,
|
||||
LAUNCHER_WIDTH,
|
||||
LAUNCHER_ROTATION_SPEED,
|
||||
LAUNCHER_BALL_SPAWN_SPEED,
|
||||
BALL_RADIUS,
|
||||
BALL_RADIUS_VARIATION,
|
||||
)
|
||||
|
||||
|
||||
class Launcher:
|
||||
def __init__(self, screen_width, screen_height):
|
||||
self.screen_width = float(screen_width)
|
||||
self.screen_height = float(screen_height)
|
||||
|
||||
# Pivot point at top center. Placed slightly above y=0 (-15px) so the upper part
|
||||
# extends beyond the top of the screen and is clipped by the screen boundary.
|
||||
self.pivot_x = self.screen_width / 2.0
|
||||
self.pivot_y = -15.0
|
||||
|
||||
self.min_angle = float(LAUNCHER_MIN_ANGLE)
|
||||
self.max_angle = float(LAUNCHER_MAX_ANGLE)
|
||||
self.length = float(LAUNCHER_LENGTH)
|
||||
self.tube_width = float(LAUNCHER_WIDTH)
|
||||
self.rotation_speed = float(LAUNCHER_ROTATION_SPEED)
|
||||
self.spawn_speed = float(LAUNCHER_BALL_SPAWN_SPEED)
|
||||
|
||||
self._time = 0.0
|
||||
self.angle = 0.0 # degrees from straight down (0=down, negative=left, positive=right)
|
||||
self._update_angle()
|
||||
|
||||
def update_screen_size(self, width, height):
|
||||
self.screen_width = float(width)
|
||||
self.screen_height = float(height)
|
||||
self.pivot_x = self.screen_width / 2.0
|
||||
|
||||
def update(self, delta_time):
|
||||
self._time += delta_time
|
||||
self._update_angle()
|
||||
|
||||
def _update_angle(self):
|
||||
mid_angle = (self.min_angle + self.max_angle) / 2.0
|
||||
amplitude = (self.max_angle - self.min_angle) / 2.0
|
||||
|
||||
if amplitude <= 0:
|
||||
self.angle = mid_angle
|
||||
return
|
||||
|
||||
# Smooth harmonic oscillation within [min_angle, max_angle]
|
||||
period = max(0.1, (4.0 * amplitude) / max(1.0, self.rotation_speed))
|
||||
omega = (2.0 * math.pi) / period
|
||||
|
||||
sine_val = math.sin(omega * self._time)
|
||||
subtle_wave = 0.85 * sine_val + 0.15 * math.sin(omega * 0.5 * self._time)
|
||||
|
||||
self.angle = mid_angle + amplitude * max(-1.0, min(1.0, subtle_wave))
|
||||
|
||||
def get_solid_params(self):
|
||||
"""
|
||||
Returns (cx, cy, rect_long, rect_short, angle) for creating a _Solid in physics.
|
||||
"""
|
||||
rad = math.radians(self.angle)
|
||||
dir_x = math.sin(rad)
|
||||
dir_y = math.cos(rad)
|
||||
|
||||
cx = self.pivot_x + dir_x * (self.length / 2.0)
|
||||
cy = self.pivot_y + dir_y * (self.length / 2.0)
|
||||
|
||||
# OBB angle in geometry convention: 90 degrees minus launcher angle
|
||||
obb_angle = 90.0 - self.angle
|
||||
|
||||
return cx, cy, self.length, self.tube_width, obb_angle
|
||||
|
||||
def get_spawn_transform(self):
|
||||
"""
|
||||
Returns (spawn_x, spawn_y, dir_x, dir_y) for spawning a ball outward
|
||||
along the nozzle direction, positioned slightly outside the nozzle tip.
|
||||
"""
|
||||
rad = math.radians(self.angle)
|
||||
dir_x = math.sin(rad)
|
||||
dir_y = math.cos(rad)
|
||||
|
||||
max_ball_radius = BALL_RADIUS * (1.0 + BALL_RADIUS_VARIATION)
|
||||
safe_dist = self.length + max_ball_radius + 3.0
|
||||
|
||||
spawn_x = self.pivot_x + dir_x * safe_dist
|
||||
spawn_y = self.pivot_y + dir_y * safe_dist
|
||||
|
||||
return spawn_x, spawn_y, dir_x, dir_y
|
||||
|
||||
def get_points(self):
|
||||
"""
|
||||
Returns the 4 corner points of the launcher tube rectangle.
|
||||
"""
|
||||
rad = math.radians(self.angle)
|
||||
u_x = math.sin(rad)
|
||||
u_y = math.cos(rad)
|
||||
|
||||
v_x = -u_y
|
||||
v_y = u_x
|
||||
|
||||
half_w = self.tube_width / 2.0
|
||||
length = self.length
|
||||
|
||||
p_top_left = (
|
||||
self.pivot_x - v_x * half_w,
|
||||
self.pivot_y - v_y * half_w,
|
||||
)
|
||||
p_top_right = (
|
||||
self.pivot_x + v_x * half_w,
|
||||
self.pivot_y + v_y * half_w,
|
||||
)
|
||||
p_bot_right = (
|
||||
self.pivot_x + u_x * length + v_x * half_w,
|
||||
self.pivot_y + u_y * length + v_y * half_w,
|
||||
)
|
||||
p_bot_left = (
|
||||
self.pivot_x + u_x * length - v_x * half_w,
|
||||
self.pivot_y + u_y * length - v_y * half_w,
|
||||
)
|
||||
|
||||
return [p_top_left, p_top_right, p_bot_right, p_bot_left]
|
||||
Reference in New Issue
Block a user