creeper-adventure/camera.py
Waylon S. Walker de1001dd57
Some checks failed
Release / pypi-release (push) Failing after 37s
Release / release (ubuntu-latest) (push) Failing after 1m0s
Release / release (windows-latest) (push) Has been cancelled
Release / build-site (push) Has been cancelled
wip
2025-11-22 22:14:53 -06:00

180 lines
5 KiB
Python

"""Basic showcase on how the transform property on SpaceDebugDrawOptions can
be used as a camera to allow panning. Use arrows to move the camera.
"""
__docformat__ = "reStructuredText"
import random
import sys
import pygame
import pymunk.pygame_util
from pymunk.vec2d import Vec2d
random.seed(0)
def main():
pygame.init()
screen = pygame.display.set_mode((600, 600))
clock = pygame.time.Clock()
running = True
font = pygame.font.Font(None, 16)
text = font.render(
"Use Arrows (up, down, left, right) to move the camera, "
"a and z to zoom in / out and s and x to rotate.",
True,
pygame.Color("black"),
)
### Physics stuff
space = pymunk.Space()
space.gravity = Vec2d(0.0, 900.0)
draw_options = pymunk.pygame_util.DrawOptions(screen)
## Balls
balls = []
body = pymunk.Body()
body.position = pymunk.Vec2d(407, 354)
s1 = pymunk.Segment(body, Vec2d(-300, -30), Vec2d(0, 0), 1.0)
s2 = pymunk.Segment(body, Vec2d(0, 0), Vec2d(0, -100), 1.0)
s1.density = 0.1
s2.density = 0.1
s1.friction = 1
s2.friction = 1
space.add(body, s1, s2)
c1 = pymunk.constraints.DampedSpring(
space.static_body,
body,
(427, 200),
(0, -100),
Vec2d(407, 254).get_distance((427, 200)),
2000,
100,
)
c2 = pymunk.constraints.DampedSpring(
space.static_body,
body,
(87, 200),
(-300, -30),
Vec2d(107, 324).get_distance((87, 200)),
2000,
100,
)
space.add(c1, c2)
# extra to show how constraints are drawn when very small / large
body = pymunk.Body(1, 100)
body.position = 450, 305
c3 = pymunk.constraints.DampedSpring(
space.static_body, body, (450, 300), (0, 0), 5, 1000, 100
)
space.add(body, c3)
body = pymunk.Body(1, 100)
body.position = 500, 2025
c3 = pymunk.constraints.DampedSpring(
space.static_body, body, (500, 25), (0, 0), 2000, 1000, 100
)
space.add(body, c3)
ticks_to_next_ball = 10
translation = pymunk.Transform()
scaling = 1
rotation = 0
while running:
for event in pygame.event.get():
if (
event.type == pygame.QUIT
or event.type == pygame.KEYDOWN
and event.key == pygame.K_ESCAPE
):
running = False
elif event.type == pygame.KEYDOWN and event.key == pygame.K_p:
pygame.image.save(screen, "camera.png")
keys = pygame.key.get_pressed()
left = int(keys[pygame.K_LEFT])
up = int(keys[pygame.K_UP])
down = int(keys[pygame.K_DOWN])
right = int(keys[pygame.K_RIGHT])
zoom_in = int(keys[pygame.K_a])
zoom_out = int(keys[pygame.K_z])
rotate_left = int(keys[pygame.K_s])
rotate_right = int(keys[pygame.K_x])
translate_speed = 10
translation = translation.translated(
translate_speed * left - translate_speed * right,
translate_speed * up - translate_speed * down,
)
zoom_speed = 0.1
scaling *= 1 + (zoom_speed * zoom_in - zoom_speed * zoom_out)
rotation_speed = 0.1
rotation += rotation_speed * rotate_left - rotation_speed * rotate_right
# to zoom with center of screen as origin we need to offset with
# center of screen, scale, and then offset back
draw_options.transform = (
pymunk.Transform.translation(300, 300)
@ pymunk.Transform.scaling(scaling)
@ translation
@ pymunk.Transform.rotation(rotation)
@ pymunk.Transform.translation(-300, -300)
)
ticks_to_next_ball -= 1
if ticks_to_next_ball <= 0:
ticks_to_next_ball = 100
mass = 10
radius = 25
inertia = pymunk.moment_for_circle(mass, 0, radius, (0, 0))
body = pymunk.Body(mass, inertia)
x = random.randint(115, 350)
body.position = x, 100
if random.random() > 0.5:
shape = pymunk.Circle(body, radius)
else:
shape = pymunk.Poly.create_box(
body, size=(radius * 2, radius * 2), radius=2
)
shape.friction = 1
space.add(body, shape)
balls.append(shape)
### Clear screen
screen.fill(pygame.Color("white"))
### Draw stuff
space.debug_draw(draw_options)
balls_to_remove = []
for ball in balls:
if ball.body.position.y > 500:
balls_to_remove.append(ball)
for ball in balls_to_remove:
space.remove(ball, ball.body)
balls.remove(ball)
screen.blit(text, (5, 5))
### Update physics
dt = 1.0 / 60.0
space.step(dt)
### Flip screen
pygame.display.flip()
clock.tick(50)
pygame.display.set_caption("fps: " + str(clock.get_fps()))
if __name__ == "__main__":
sys.exit(main())