我询问了如何获得 Pymunk+Pygame 世界的相机窗口视图,虽然我收到了一个慷慨解释的答案,但我不相信,因为该人使用 Pygame Surface 进行 blit。API 说 Surface旨在表示图像,从处理和渲染的角度来看,我觉得这是渲染 Pymunk 对象的一种低效方式,尤其是当它们是运动学时 。
因此,我尝试修改其中一个 Pymunk 示例(我在任何我进行更改的地方都评论了“ Nav added ”)以在按下箭头键时围绕球和静态线移动。它创造了一种相机般的效果。但是当我使用箭头键将对象向右移动时,甚至鼠标坐标似乎也随之移动。
我以为问题出在 line 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 有不同的绘制和更新元素的方式。一个明显的例子将是对社区的巨大帮助。
此外,如果它是多人游戏,则可能需要为通过 LAN 连接查看同一世界的其他人(蜘蛛)提供单独的视图区域。