Skip to content

Commit

Permalink
Asteroids demo (#333)
Browse files Browse the repository at this point in the history
* wahoo first step of asteroids done

* add basic player

* optimize within

* physics fps to 50 and fix demos

* controller in asteroids

* auto-normalize controller joysticks

* remove docstring

* keyboard support

* shooting

* shooting but capped

* shots delete asteroids
  • Loading branch information
tsedan authored Aug 29, 2022
1 parent 77a8427 commit 96b30a7
Show file tree
Hide file tree
Showing 13 changed files with 288 additions and 84 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
- `GameObject`s and `Group`s can now be hidden in order to make their children not draw.
- `GameObjects`s can be active just like groups.
- Allowing passing in the hidden attribute into `Component` constructors.
- `Raster.fill()` and `Surface.fill()`.
- `Vector.within()` method to check if vector is within a certain distance of another vector.
- `Raster` component and `Surface` now have a changeable alpha. (`Image` and `Sprite` by extension)

### Changed
Expand All @@ -35,6 +37,8 @@
- Rewrote `Rectangle` from the ground-up.
- Window is now shown when begin is called. Not when init is called.
- `mouse_button` key passed in mouse press events renamed to `button`
- Default physics fps to 50 to align with Unity.
- Automatically normalized joystick events/getters to be in the range of -1 to 1 instead of -32768 to 32767.

### Removed

Expand Down
163 changes: 163 additions & 0 deletions demo/asteroids.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
"""
A classic
"""
import random
from rubato import *

size = 1080
bounds = size // 8
radius = size // 40
level = 0

init(name="asteroids", res=(size, size))

main = Scene()

# background
stars = Surface(size, size)
stars.fill(Color.black)
for i in range(200):
pos = random.randint(0, size), random.randint(0, size)
stars.draw_point(pos, Color.white)


# component to remove things that are out of bounds
class BoundsChecker(Component):

def update(self):
if self.gameobj.pos.x < -bounds or self.gameobj.pos.x > size + bounds or \
self.gameobj.pos.y < -bounds or self.gameobj.pos.y > size + bounds:
main.delete(self.gameobj)


# asteroid generator
def make_asteroid():
sides = random.randint(5, 8)

t = random.randint(0, size)
topbottom = random.randint(0, 1)
side = random.randint(0, 1)
if topbottom:
pos = t, side * size + (radius if side else -radius)
else:
pos = side * size + (radius if side else -radius), t

dir = (-Display.center.dir_to(pos)).rotate(random.randint(-45, 45))

main.add(
wrap(
[
Polygon(
[
Vector.from_radial(random.randint(int(radius * .7), int(radius * 0.95)), i * 360 / sides)
for i in range(sides)
],
debug=True,
),
RigidBody(velocity=dir * 100, ang_vel=random.randint(-30, 30)),
BoundsChecker(),
],
"asteroid",
pos,
random.randint(0, 360),
)
)


Time.schedule(ScheduledTask(1000, make_asteroid, 1000))


class PlayerController(Component):

def setup(self):
self.speed = 200
self.steer = 20

self.velocity = Vector()

def update(self):
if Input.controller_button(Input.controllers - 1, 0) or Input.key_pressed("j"):
shoot()

def fixed_update(self):
dx = Input.controller_axis(Input.controllers - 1, 0) or \
(-1 if Input.key_pressed("a") else (1 if Input.key_pressed("d") else 0))
dy = Input.controller_axis(Input.controllers - 1, 1) or \
(-1 if Input.key_pressed("w") else (1 if Input.key_pressed("s") else 0))
target = Vector(dx, dy)

d_vel = target * self.speed
steering = Vector.clamp_magnitude(d_vel - self.velocity, self.steer)

self.velocity = Vector.clamp_magnitude(self.velocity + steering, self.speed)

self.gameobj.pos += self.velocity * Time.fixed_delta

if target != (0, 0):
self.gameobj.rotation = self.velocity.angle


full = [
Vector.from_radial(radius, 0),
Vector.from_radial(radius, 125),
Vector.from_radial(radius // 4, 180),
Vector.from_radial(radius, -125),
]
right = [full[0], full[1], full[2]]
left = [full[0], full[2], full[3]]
player_spr = Raster(radius * 2, radius * 2)
player_spr.draw_poly([v + radius for v in full], Color.debug, 2, aa=True)

main.add(
wrap(
[
PlayerController(),
Polygon(right, trigger=True),
Polygon(left, trigger=True),
player_spr,
],
"player",
Display.center,
)
)

last_shoot = 0
interval = 200 # milliseconds between shots


def bullet_collide(man: Manifold):
if man.shape_b.gameobj.name == "asteroid":
main.delete(man.shape_b.gameobj)


def shoot():
global last_shoot
if Time.now() - last_shoot < interval:
return
last_shoot = Time.now()
main.add(
wrap(
[
Circle(radius // 5, Color.debug, trigger=True, on_collide=bullet_collide),
RigidBody(
velocity=player_spr.gameobj.get(PlayerController).velocity + Vector.from_radial(
500,
player_spr.gameobj.rotation,
)
),
BoundsChecker(),
],
"bullet",
player_spr.gameobj.pos + full[0].rotate(player_spr.gameobj.rotation),
player_spr.gameobj.rotation,
)
)


def new_draw():
Draw.surf(stars, Display.center)


Game.draw = new_draw

begin()
4 changes: 2 additions & 2 deletions demo/offset_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def update():


def handler(m_event):
if m_event["mouse_button"] == "mouse 1":
if m_event["button"] == "mouse 1":
e = extra.clone()
e.pos = V(m_event["x"], m_event["y"])
s.add(e)
Expand All @@ -39,4 +39,4 @@ def handler(m_event):
s.add(go, rb.wrap(text, pos=V(50, 20)))
s.fixed_update = update

rb.begin()
rb.begin()
24 changes: 12 additions & 12 deletions demo/physics_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,16 @@
rb.Circle(radius=rb.Display.res.x // num_obj, color=rb.Color.random_default()),
rb.RigidBody(
mass=0.1,
bounciness=1,
bounciness=0.99,
friction=0.2,
gravity=(0, 50),
gravity=(0, 80),
velocity=(randint(-100, 100), randint(-100, 100)),
)
),
],
pos=(
randint(rb.Display.res.x / 20,
19 * rb.Display.res.x / 20), randint(rb.Display.res.y / 20, 19 * rb.Display.res.y / 20)
)
randint(rb.Display.res.x / 20, 19 * rb.Display.res.x / 20),
randint(rb.Display.res.y / 20, 19 * rb.Display.res.y / 20),
),
)
)
for _ in range(num_obj // 2):
Expand All @@ -78,16 +78,16 @@
rb.Polygon(rb.Vector.poly(randint(3, 9), rb.Display.res.x // num_obj), color=rb.Color.random_default()),
rb.RigidBody(
mass=0.1,
bounciness=1,
bounciness=0.99,
friction=0.2,
gravity=(0, 50),
gravity=(0, 80),
velocity=(randint(-100, 100), randint(-100, 100)),
)
),
],
pos=(
randint(rb.Display.res.x / 20,
19 * rb.Display.res.x / 20), randint(rb.Display.res.y / 20, 19 * rb.Display.res.y / 20)
)
randint(rb.Display.res.x / 20, 19 * rb.Display.res.x / 20),
randint(rb.Display.res.y / 20, 19 * rb.Display.res.y / 20),
),
)
)

Expand Down
48 changes: 27 additions & 21 deletions demo/platformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,20 +96,24 @@ def re_allow():

# create platforms
platforms = [
rb.GameObject(pos=rb.Vector(200, rb.Display.bottom - 140)
).add(rb.Rectangle(
width=90,
height=40,
tag="ground",
color=rb.Color.blue,
)),
rb.GameObject(pos=rb.Vector(400, rb.Display.bottom - 340)
).add(rb.Rectangle(
width=150,
height=40,
tag="ground",
color=rb.Color.blue,
)),
rb.GameObject(pos=rb.Vector(
200,
rb.Display.bottom - 140,
)).add(rb.Rectangle(
width=90,
height=40,
tag="ground",
color=rb.Color.blue,
)),
rb.GameObject(pos=rb.Vector(
400,
rb.Display.bottom - 340,
)).add(rb.Rectangle(
width=150,
height=40,
tag="ground",
color=rb.Color.blue,
)),
]

# create obstacles
Expand All @@ -133,13 +137,15 @@ def re_allow():

# create triggers
triggers = [
rb.GameObject(pos=rb.Vector(950, rb.Display.bottom -
45),).add(rb.Rectangle(
width=400,
height=30,
tag="retry_collider",
trigger=True,
)),
rb.GameObject(pos=rb.Vector(
950,
rb.Display.bottom - 45,
)).add(rb.Rectangle(
width=400,
height=30,
tag="retry_collider",
trigger=True,
)),
]

# Create animation for portal
Expand Down
2 changes: 1 addition & 1 deletion rubato/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,5 +205,5 @@ def update(): # test: skip

@staticmethod
def draw(): # test: skip
"""An overrideable method for drawing the game. Called once per frame, after the current scene draws."""
"""An overrideable method for drawing the game. Called once per frame, before the draw queue is dumped."""
pass
3 changes: 2 additions & 1 deletion rubato/struct/gameobject/physics/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,8 @@ def __init__(
"""The direction that would most quickly separate the two colliders."""

def __str__(self) -> str:
return f"Manifold <{self.penetration}, {self.normal}>"
return f"Manifold <shape_a: {self.shape_a}, shape_b: {self.shape_b}, " + \
f"penetration: {self.penetration}, normal: {self.normal}>"

def flip(self) -> Manifold:
"""
Expand Down
Loading

0 comments on commit 96b30a7

Please sign in to comment.