feat(sim): moving platforms and particle effects
This commit is contained in:
1 parent
8c5c8b8fe0
commit
3781a62956
2 files changed
+130
No files matched your search
@@ -0,0 +1,53 @@
|
|||||||
|
from .geometry import Aabb
|
||||||
|
|
||||||
|
|
||||||
|
class MovingPlatform:
|
||||||
|
def __init__(self, x, y, w, h, toX, toY, speed, oneWay=False):
|
||||||
|
self.x = float(x)
|
||||||
|
self.y = float(y)
|
||||||
|
self.w = float(w)
|
||||||
|
self.h = float(h)
|
||||||
|
self.fromX = float(x)
|
||||||
|
self.fromY = float(y)
|
||||||
|
self.toX = float(toX)
|
||||||
|
self.toY = float(toY)
|
||||||
|
self.speed = float(speed)
|
||||||
|
self.oneWay = bool(oneWay)
|
||||||
|
self.progress = 0.0
|
||||||
|
self.direction = 1
|
||||||
|
self.deltaX = 0.0
|
||||||
|
self.deltaY = 0.0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def aabb(self):
|
||||||
|
return Aabb(self.x, self.y, self.w, self.h)
|
||||||
|
|
||||||
|
def spanLength(self):
|
||||||
|
dx = self.toX - self.fromX
|
||||||
|
dy = self.toY - self.fromY
|
||||||
|
return (dx * dx + dy * dy) ** 0.5
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
span = self.spanLength()
|
||||||
|
prevX = self.x
|
||||||
|
prevY = self.y
|
||||||
|
if span == 0.0:
|
||||||
|
self.deltaX = 0.0
|
||||||
|
self.deltaY = 0.0
|
||||||
|
return
|
||||||
|
step = self.speed / span
|
||||||
|
self.progress += step * self.direction
|
||||||
|
if self.progress >= 1.0:
|
||||||
|
self.progress = 1.0
|
||||||
|
self.direction = -1
|
||||||
|
elif self.progress <= 0.0:
|
||||||
|
self.progress = 0.0
|
||||||
|
self.direction = 1
|
||||||
|
self.x = self.fromX + (self.toX - self.fromX) * self.progress
|
||||||
|
self.y = self.fromY + (self.toY - self.fromY) * self.progress
|
||||||
|
self.deltaX = self.x - prevX
|
||||||
|
self.deltaY = self.y - prevY
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def fromSpawn(cls, spawn):
|
||||||
|
return cls(spawn.x, spawn.y, spawn.w, spawn.h, spawn.toX, spawn.toY, spawn.speed, spawn.oneWay)
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import math
|
||||||
|
import random
|
||||||
|
|
||||||
|
particlePresets = {
|
||||||
|
"jump": {"count": 4, "color": (230, 230, 230), "life": 16, "spread": 1.2, "gravity": 0.1, "up": -0.5},
|
||||||
|
"dash": {"count": 6, "color": (120, 200, 255), "life": 14, "spread": 1.6, "gravity": 0.0, "up": 0.0},
|
||||||
|
"stomp": {"count": 6, "color": (200, 120, 60), "life": 18, "spread": 2.0, "gravity": 0.2, "up": -1.0},
|
||||||
|
"pickup": {"count": 5, "color": (247, 213, 29), "life": 20, "spread": 1.4, "gravity": -0.05, "up": -1.2},
|
||||||
|
"hurt": {"count": 8, "color": (220, 40, 40), "life": 22, "spread": 2.2, "gravity": 0.15, "up": -1.5},
|
||||||
|
"spark": {"count": 4, "color": (255, 220, 120), "life": 10, "spread": 2.4, "gravity": 0.0, "up": 0.0},
|
||||||
|
"alert": {"count": 3, "color": (255, 80, 80), "life": 16, "spread": 0.6, "gravity": -0.1, "up": -1.4},
|
||||||
|
"dust": {"count": 3, "color": (200, 190, 160), "life": 12, "spread": 1.0, "gravity": 0.05, "up": -0.2},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Particle:
|
||||||
|
__slots__ = ("x", "y", "vx", "vy", "life", "maxLife", "color", "gravity", "size")
|
||||||
|
|
||||||
|
def __init__(self, x, y, vx, vy, life, color, gravity, size):
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
self.vx = vx
|
||||||
|
self.vy = vy
|
||||||
|
self.life = life
|
||||||
|
self.maxLife = life
|
||||||
|
self.color = color
|
||||||
|
self.gravity = gravity
|
||||||
|
self.size = size
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
self.vy += self.gravity
|
||||||
|
self.x += self.vx
|
||||||
|
self.y += self.vy
|
||||||
|
self.life -= 1
|
||||||
|
|
||||||
|
@property
|
||||||
|
def alive(self):
|
||||||
|
return self.life > 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fade(self):
|
||||||
|
return max(0.0, self.life / self.maxLife)
|
||||||
|
|
||||||
|
|
||||||
|
class ParticleSystem:
|
||||||
|
def __init__(self, rng=None, cap=512):
|
||||||
|
self.particles = []
|
||||||
|
self.rng = rng if rng is not None else random.Random(1234)
|
||||||
|
self.cap = cap
|
||||||
|
|
||||||
|
def emit(self, preset, x, y, count=None):
|
||||||
|
config = particlePresets.get(preset, particlePresets["dust"])
|
||||||
|
emitCount = count if count is not None else config["count"]
|
||||||
|
for _ in range(emitCount):
|
||||||
|
if len(self.particles) >= self.cap:
|
||||||
|
break
|
||||||
|
angle = self.rng.uniform(0, math.tau)
|
||||||
|
magnitude = self.rng.uniform(0.2, config["spread"])
|
||||||
|
vx = math.cos(angle) * magnitude
|
||||||
|
vy = math.sin(angle) * magnitude + config["up"]
|
||||||
|
life = int(config["life"] * self.rng.uniform(0.7, 1.1))
|
||||||
|
size = self.rng.randint(1, 2)
|
||||||
|
self.particles.append(Particle(x, y, vx, vy, life, config["color"], config["gravity"], size))
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
survivors = []
|
||||||
|
for particle in self.particles:
|
||||||
|
particle.update()
|
||||||
|
if particle.alive:
|
||||||
|
survivors.append(particle)
|
||||||
|
self.particles = survivors
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self.particles = []
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.particles)
|
||||||
Reference in new issue
Block a user