我询问如何获得Pymunk + Pygame世界的摄影机窗口视图,尽管我收到了generously explained answer,但我并不确信,因为该人使用Pygame Surface进行了钝化处理。 API says a Surface旨在表示图像,从处理和渲染的角度来看,我认为这是渲染Pymunk对象的一种无效方法,尤其是当它们具有运动学特性时。
因此,我尝试修改一个Pymunk示例(无论我在何处进行了更改,都已注释了“ 添加了导航”),以便在按下箭头键时在球和静态线上移动。它会产生类似相机的效果。但是,当我使用箭头键将对象向右移动时,甚至鼠标坐标也随之移动。
我以为问题出在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()
总而言之,我正在尝试创建一个比以下所示的世界大得多的世界(一个典型的Mario型世界),在该世界中,虚线矩形区域对于整个计算机监视器上的用户都是可见的,并且某些状态文本显示在该区域的角落。虚线以外的世界任何地方都不在显示器的视野范围内。
我只想知道正确的方法来创建这样的摄影机视图,向该世界添加静态和运动学对象,并在鼠标单击和键盘输入与屏幕的可见区域相关时对其进行更新。由于Pygame和Pymunk具有不同的绘制和更新元素的方式,这已经变得令人困惑。一个明显的例子将为社区带来巨大的帮助。
此外,如果是多人游戏,则可能有必要为通过LAN连接观看同一世界的其他人(蜘蛛)提供单独的观看区域。
答案 0 :(得分:1)
(在包含的第一个代码中),问题是在绘图和处理鼠标输入时都添加了相机X和Y。您应该进行更改,以便在绘制时减去相机X和Y。
您可以通过在使用鼠标单击时创建球的逻辑中添加p变量的少量打印语句来验证逻辑。
"""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
#print("mouseX:"+str(p[0])+" mouseY:"+str(p[1]))
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
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)
注意: 我读了另一个问题/答案,也许跟进这个问题比另一个问题更好,因为似乎第三个示例或多或少地回答了这个问题?