Камера в pyglet + pymunk
Нужно было реализовать камеру для 2D платформера на pyglet с физическим движком pymunk. В pyglet нет стандартной реализации камеры, использовал пример с репозитория pyglet - camera_group. Возникла проблема в том, что физические объекты привязаны к экрану и движение камеры на них не работает. Предполагаю, что можно как то использовать Canvas и разделить визуал (с камерой) от всего остального мира, но способ реализации не нашел.
Вот сам код:
from random import randrange, randint
import pyglet
from pyglet.gl import *
from pyglet.window import key, Window, mouse
from pyglet.sprite import Sprite
import pymunk
from pymunk import pyglet_util
from pymunk import Space, Circle, Poly, Body
from camera import CenteredCameraGroup
WIDTH, HEIGHT = 1920, 1080
pyglet.gl.glClearColor(0.5,0,0,1)
# basic display
display = Window(WIDTH, HEIGHT)
# input handler
keys = key.KeyStateHandler()
display.push_handlers(keys)
# camera as sprite group
camera = CenteredCameraGroup(display, WIDTH / 2, HEIGHT / 2, zoom=1)
batch = pyglet.graphics.Batch()
background_image = pyglet.image.SolidColorImagePattern((150, 150, 150, 255)).create_image(WIDTH, HEIGHT)
background = Sprite(background_image, 0, 0)
tile_image = pyglet.image.SolidColorImagePattern((100, 100, 100, 255)).create_image(32, 32)
tiles = [Sprite(img=tile_image, x=i * 32, y=HEIGHT/4, group=camera, batch=batch) for i in range(int(WIDTH / 32))]
# pymunk physics
options = pyglet_util.DrawOptions(batch=batch) # <- Тут привязан Batch, но эффекта это никакого не даёт
world = Space()
world.gravity = (0, -8000)
# platform
platform_vertices = ((0, HEIGHT/4), (0, HEIGHT/4+32), (WIDTH, HEIGHT/4), (WIDTH, HEIGHT/4+32))
platform = Poly(world.static_body, platform_vertices)
platform.color = (200, 200, 200, 255)
world.add(platform)
def ball_spawner(pos):
radius = randint(10, 50)
body = Body(1, pymunk.moment_for_circle(1, radius, radius))
body.position = pos
shape = Circle(body, radius)
shape.elasticity = 0.5
shape.friction = 1.0
shape.color = [randrange(256) for i in range(3)] + [255]
world.add(body, shape)
def update_physics(dt):
world.step(dt)
@display.event
def on_draw():
display.clear()
background.draw()
world.debug_draw(options)
batch.draw()
@display.event
def on_mouse_press(x, y, button, modifiers):
if button == mouse.LEFT:
ball_spawner((x, y))
def update_input(dt):
if keys[key.W]:
camera.y += 200 * dt
if keys[key.S]:
camera.y -= 200 * dt
if keys[key.A]:
camera.x -= 200 * dt
if keys[key.D]:
camera.x += 200 * dt
if keys[key.UP]:
camera.zoom += 0.1
if keys[key.DOWN]:
if camera.zoom > 0.05:
camera.zoom -= 0.05
if __name__ == "__main__":
pyglet.clock.schedule_interval(update_physics, 1 / 60)
pyglet.clock.schedule_interval(update_input, 1 / 60)
pyglet.app.run()
Ответы (1 шт):
Автор решения: Альберт Давыдов
→ Ссылка
Нашел решение своей проблемы. Вместо camera_group нужно использовать пример с camera, тогда всё будет выглядеть как и задумано.
Вот результат:
Вот готовый код решения:
from random import randrange, randint
import pyglet
from pyglet.gl import *
from pyglet.window import key, Window, mouse
from pyglet.sprite import Sprite
import pymunk
from pymunk import pyglet_util
from pymunk import Space, Circle, Poly, Body
from camera import CenteredCamera
WIDTH, HEIGHT = 1920, 1080
pyglet.gl.glClearColor(0.5,0,0,1)
# basic display
display = Window(WIDTH, HEIGHT)
# input handler
keys = key.KeyStateHandler()
display.push_handlers(keys)
# camera
camera = CenteredCamera(display) # Использую CenteredCamera, а не CenteredCameraGroup
batch = pyglet.graphics.Batch()
background_image = pyglet.image.SolidColorImagePattern((150, 150, 150, 255)).create_image(WIDTH, HEIGHT)
background = Sprite(background_image, 0, 0)
tile_image = pyglet.image.SolidColorImagePattern((100, 100, 100, 255)).create_image(32, 32)
tiles = [Sprite(img=tile_image, x=i * 32, y=HEIGHT/4, batch=batch) for i in range(int(WIDTH / 32))]
# pymunk physics
options = pyglet_util.DrawOptions()
world = Space()
world.gravity = (0, -8000)
# platform
platform_vertices = ((0, HEIGHT/4), (0, HEIGHT/4+32), (WIDTH, HEIGHT/4), (WIDTH, HEIGHT/4+32))
platform = Poly(world.static_body, platform_vertices)
platform.color = (200, 200, 200, 255)
world.add(platform)
def ball_spawner(pos):
radius = randint(10, 50)
body = Body(1, pymunk.moment_for_circle(1, radius, radius))
body.position = pos
shape = Circle(body, radius)
shape.elasticity = 0.5
shape.friction = 1.0
shape.color = [randrange(256) for i in range(3)] + [255]
world.add(body, shape)
def update_physics(dt):
world.step(dt)
@display.event
def on_draw():
display.clear()
background.draw()
with camera: # Всё, что статично должно отрисовываться через with camera
world.debug_draw(options)
batch.draw()
@display.event
def on_mouse_press(x, y, button, modifiers):
if button == mouse.LEFT:
ball_spawner((camera.position[0], camera.position[1])) # <- спавн шара на позиции камеры
def update_input(dt):
if keys[key.W]:
camera.position = (camera.position[0], camera.position[1] + 200 * dt)
if keys[key.S]:
camera.position = (camera.position[0], camera.position[1] - 200 * dt)
if keys[key.A]:
camera.position = (camera.position[0] - 200 * dt, camera.position[1])
if keys[key.D]:
camera.position = (camera.position[0] + 200 * dt, camera.position[1])
if keys[key.UP]:
camera.zoom += 0.1
if keys[key.DOWN]:
if camera.zoom > 0.05:
camera.zoom -= 0.05
if __name__ == "__main__":
pyglet.clock.schedule_interval(update_physics, 1 / 60)
pyglet.clock.schedule_interval(update_input, 1 / 60)
pyglet.app.run()

