Я спросил, как получить из окна камеры вид мира Pymunk + Pygame, и хотя я получил щедро объяснил ответ, я не уверен, потому что человек использует Pygame Surface для бликации. В API говорится, что поверхность предназначена для представления изображения и обработки и точки зрения рендеринга. Я чувствовал, что это неэффективный способ рендеринга объектов Pymunk, особенно когда они кинематические.
Итак, я попытался изменить один из примеров Pymunk (я прокомментировал «Nav added» везде, где я делал изменения), чтобы перемещать шары и статические линии при нажатии клавиш со стрелками. Создается эффект камеры. Но когда я использую клавиши со стрелками для перемещения объекта вправо, кажется, что даже координаты мыши перемещаются.
Я думал, что проблема связана со строкой p = event.pos[X]+cameraX, flipy(event.pos[Y])+cameraY
, но даже после изменения ее на p = event.pos[X], flipy(event.pos[Y])
проблема сохраняется.
"""This example lets you dynamically create static walls and dynamic balls
"""
__docformat__ = "reStructuredText"
import pygame
from pygame.locals import *
from pygame.color import *
import pymunk
from pymunk import Vec2d
X,Y = 0,1
### Physics collision types
COLLTYPE_DEFAULT = 0
COLLTYPE_MOUSE = 1
COLLTYPE_BALL = 2
def flipy(y):
"""Small hack to convert chipmunk physics to pygame coordinates"""
return -y+600
def mouse_coll_func(arbiter, space, data):
"""Simple callback that increases the radius of circles touching the mouse"""
s1,s2 = arbiter.shapes
s2.unsafe_set_radius(s2.radius + 0.15)
return False
def main():
pygame.init()
screen = pygame.display.set_mode((600, 600))
clock = pygame.time.Clock()
running = True
# Camera offsets (Nav added)
cameraX = 0
cameraY = 0
### Physics stuff
space = pymunk.Space()
space.gravity = 0.0, -900.0
## Balls
balls = []
### Mouse
mouse_body = pymunk.Body(body_type=pymunk.Body.KINEMATIC)
mouse_shape = pymunk.Circle(mouse_body, 3, (0,0))
mouse_shape.collision_type = COLLTYPE_MOUSE
space.add(mouse_shape)
space.add_collision_handler(COLLTYPE_MOUSE, COLLTYPE_BALL).pre_solve=mouse_coll_func
### Static line
line_point1 = None
static_lines = []
run_physics = True
while running:
for event in pygame.event.get():
if event.type == QUIT:
running = False
elif event.type == KEYDOWN and event.key == K_ESCAPE:
running = False
elif event.type == KEYDOWN and event.key == K_p:
pygame.image.save(screen, "balls_and_lines.png")
elif event.type == MOUSEBUTTONDOWN and event.button == 1:
p = event.pos[X]+cameraX, flipy(event.pos[Y])+cameraY#Nav added
body = pymunk.Body(10, 100)
body.position = p
shape = pymunk.Circle(body, 10, (0,0))
shape.friction = 0.5
shape.collision_type = COLLTYPE_BALL
space.add(body, shape)
balls.append(shape)
#Nav added key detection event
if event.type == KEYDOWN:
if event.key == K_UP:
cameraY -= 10
print("x:"+str(cameraX)+" y:"+str(cameraY))
if event.key == K_LEFT:
cameraX -= 10
if event.key == K_DOWN:
cameraY += 10
if event.key == K_RIGHT:
cameraX += 10
elif event.type == MOUSEBUTTONDOWN and event.button == 3:
if line_point1 is None:
line_point1 = Vec2d(event.pos[X]+cameraX, flipy(event.pos[Y])+cameraY)#Nav added
elif event.type == MOUSEBUTTONUP and event.button == 3:
if line_point1 is not None:
line_point2 = Vec2d(event.pos[X]+cameraX, flipy(event.pos[Y])+cameraY)#Nav added
body = pymunk.Body(body_type=pymunk.Body.STATIC)
shape= pymunk.Segment(body, line_point1, line_point2, 0.0)
shape.friction = 0.99
space.add(shape)
static_lines.append(shape)
line_point1 = None
elif event.type == KEYDOWN and event.key == K_SPACE:
run_physics = not run_physics
p = pygame.mouse.get_pos()
mouse_pos = Vec2d(p[X]+cameraX, flipy(p[Y])+cameraY)#Nav added
mouse_body.position = mouse_pos
if pygame.key.get_mods() & KMOD_SHIFT and pygame.mouse.get_pressed()[0]:
body = pymunk.Body(10, 10)
body.position = mouse_pos
shape = pymunk.Circle(body, 10, (0,0))
shape.collision_type = COLLTYPE_BALL
space.add(body, shape)
balls.append(shape)
### Update physics
if run_physics:
dt = 1.0/60.0
for x in range(1):
space.step(dt)
### Draw stuff
screen.fill(THECOLORS["white"])
# Display some text
font = pygame.font.Font(None, 16)
text = """LMB: Create ball
LMB + Shift: Create many balls
RMB: Drag to create wall, release to finish
Space: Pause physics simulation"""
y = 5
for line in text.splitlines():
text = font.render(line, 1,THECOLORS["black"])
screen.blit(text, (5,y))
y += 10
for ball in balls:
r = ball.radius
v = ball.body.position
rot = ball.body.rotation_vector
p = int(v.x)+cameraX, int(flipy(v.y))+cameraY#Nav added
p2 = Vec2d(rot.x, -rot.y) * r * 0.9
pygame.draw.circle(screen, THECOLORS["blue"], p, int(r), 2)
pygame.draw.line(screen, THECOLORS["red"], p, p+p2)
if line_point1 is not None:
p1 = line_point1.x+cameraX, flipy(line_point1.y)+cameraY#Nav added
p2 = mouse_pos.x+cameraX, flipy(mouse_pos.y)+cameraY#Nav added
pygame.draw.lines(screen, THECOLORS["black"], False, [p1,p2])
for line in static_lines:
body = line.body
pv1 = body.position + line.a.rotated(body.angle)
pv2 = body.position + line.b.rotated(body.angle)
p1 = pv1.x+cameraX, flipy(pv1.y)+cameraY#Nav added
p2 = pv2.x+cameraX, flipy(pv2.y)+cameraY#Nav added
pygame.draw.lines(screen, THECOLORS["lightgray"], False, [p1,p2])
### Flip screen
pygame.display.flip()
clock.tick(50)
pygame.display.set_caption("fps: " + str(clock.get_fps()))
if __name__ == '__main__':
doprof = 0
if not doprof:
main()
else:
import cProfile, pstats
prof = cProfile.run("main()", "profile.prof")
stats = pstats.Stats("profile.prof")
stats.strip_dirs()
stats.sort_stats('cumulative', 'time', 'calls')
stats.print_stats(30)
Правильно ли я поступаю, перемещая все позиции шара и статических линий в каждом кадре? Я предполагаю, что более эффективным способом было бы перемещать их все в каждом кадре, но рисовать только те, которые находятся в пределах экрана?
С другой стороны, в примере, подобном приведенному ниже, статические строки добавляются перед циклом выполнения, и это заставляет меня задуматься, как правильно реализовать камеру в этом случае. Возможно, у меня нет другого выхода, кроме как использовать Surface.
"""This example spawns (bouncing) balls randomly on a L-shape constructed of
two segment shapes. Not interactive.
"""
__version__ = "$Id:$"
__docformat__ = "reStructuredText"
# Python imports
import random
# Library imports
import pygame
from pygame.key import *
from pygame.locals import *
from pygame.color import *
# pymunk imports
import pymunk
import pymunk.pygame_util
class BouncyBalls(object):
"""
This class implements a simple scene in which there is a static platform (made up of a couple of lines)
that don't move. Balls appear occasionally and drop onto the platform. They bounce around.
"""
def __init__(self):
# Space
self._space = pymunk.Space()
self._space.gravity = (0.0, -900.0)
# Camera offsets (Nav added)
self.cameraX = 0
self.cameraY = 0
# Physics
# Time step
self._dt = 1.0 / 60.0
# Number of physics steps per screen frame
self._physics_steps_per_frame = 1
# pygame
pygame.init()
self._screen = pygame.display.set_mode((600, 600))
self._clock = pygame.time.Clock()
self._draw_options = pymunk.pygame_util.DrawOptions(self._screen)
# Static barrier walls (lines) that the balls bounce off of
self._add_static_scenery()
# Balls that exist in the world
self._balls = []
# Execution control and time until the next ball spawns
self._running = True
self._ticks_to_next_ball = 10
def run(self):
# Main loop
while self._running:
# Progress time forward
for x in range(self._physics_steps_per_frame):
self._space.step(self._dt)
self._process_events()
self._update_balls()
self._clear_screen()
self._draw_objects()
pygame.display.flip()
# Delay fixed time between frames
self._clock.tick(50)
pygame.display.set_caption("fps: " + str(self._clock.get_fps()))
def _add_static_scenery(self):
static_body = self._space.static_body
#Nav added offsets
static_lines = [pymunk.Segment(static_body, (111.0+self.cameraX, 280.0+self.cameraY), (407.0+self.cameraX, 246.0+self.cameraY), 0.0),
pymunk.Segment(static_body, (407.0+self.cameraX, 246.0+self.cameraY), (407.0+self.cameraX, 343.0+self.cameraY), 0.0)]
for line in static_lines:
line.elasticity = 0.95
line.friction = 0.9
self._space.add(static_lines)
def _process_events(self):
for event in pygame.event.get():
if event.type == QUIT:
self._running = False
elif event.type == KEYDOWN and event.key == K_ESCAPE:
self._running = False
elif event.type == KEYDOWN and event.key == K_p:
pygame.image.save(self._screen, "bouncing_balls.png")
#Nav added key detection event
if event.type == KEYDOWN:
if event.key == K_UP:
self.cameraY -= 10
print("x:"+str(self.cameraX)+" y:"+str(self.cameraY))
if event.key == K_LEFT:
self.cameraX -= 10
if event.key == K_DOWN:
self.cameraY += 10
if event.key == K_RIGHT:
self.cameraX += 10
def _update_balls(self):
self._ticks_to_next_ball -= 1
if self._ticks_to_next_ball <= 0:
self._create_ball()
self._ticks_to_next_ball = 100
# Remove balls that fall below 100 vertically
balls_to_remove = [ball for ball in self._balls if ball.body.position.y+self.cameraY < 100]#Nav added offset
for ball in balls_to_remove:
self._space.remove(ball, ball.body)
self._balls.remove(ball)
def _create_ball(self):
mass = 10
radius = 25
inertia = pymunk.moment_for_circle(mass, 0, radius, (0, 0))
body = pymunk.Body(mass, inertia)
x = random.randint(115+self.cameraX, 350+self.cameraY)#Nav added offset
body.position = x+self.cameraX, 400+self.cameraY #Nav added offset
shape = pymunk.Circle(body, radius, (0, 0))
shape.elasticity = 0.95
shape.friction = 0.9
self._space.add(body, shape)
self._balls.append(shape)
def _clear_screen(self):
self._screen.fill(THECOLORS["white"])
def _draw_objects(self):
self._space.debug_draw(self._draw_options)
if __name__ == '__main__':
game = BouncyBalls()
game.run()
Таким образом, я пытаюсь создать мир намного больше, чем показанный ниже (типичный мир типа Марио), где прямоугольная область в пунктирных линиях видна пользователю на всем мониторе компьютера и отображается некоторый текст состояния. по углам площадки. Любая часть мира, которая находится за пределами пунктирных линий, находится за пределами поля зрения монитора.
Все, что я хочу знать, это правильный способ создать такой вид камеры, добавить статические и кинематические объекты в этот мир и обновить их в то время как щелчки мыши и ввод с клавиатуры относятся к видимой области экрана. Это сбивает с толку, потому что Pygame и Pymunk имеют разные способы рисования и обновления элементов. Яркий пример будет огромным подспорьем для сообщества.
Кроме того, если это многопользовательская игра, может потребоваться отдельная область просмотра для другого человека (паука), который смотрит на тот же мир через подключение к локальной сети.