Правильный способ создания и обновления объектов в оконном мире камер Pymunk? - PullRequest
0 голосов
/ 20 марта 2019

Я спросил о том, как получить вид из окна камеры на мир Pymunk + Pygame, и, хотя я получил щедро объясненный ответ , я не уверен, потому что человек использует поверхность Pygame для блиттинга.API говорит, что Surface предназначен для представления изображения, и как с точки зрения обработки, так и с точки зрения рендеринга я чувствовал, что это неэффективный способ рендеринга объектов Pymunk, особенно когда они кинематические.

Поэтому я попытался изменить один из примеров Pymunk (я прокомментировал " Nav добавил ", где бы я ни делал изменения), чтобы перемещаться по шарам и статическим линиям при нажатии клавиш со стрелками,Это создает эффект камеры.Но когда я использую клавиши со стрелками для перемещения объекта вправо, даже координаты мыши, кажется, движутся вдоль.

enter image description here
Я думал, что проблема была в строке 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 имеют разные способы рисования и обновления элементов.Ярким примером будет огромная помощь для сообщества.

enter image description here

Также, если это многопользовательская игра, может потребоваться отдельная область просмотра для другого человека (паука), который смотрит натот же мир через подключение к локальной сети.

1 Ответ

1 голос
/ 21 марта 2019

(в первом включенном вами коде) Проблема в том, что вы добавляете 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)

Примечание: Я прочитал другой вопрос / ответ, может быть, этот дополнительный вопрос будет лучше для вашего другого вопроса, так как кажется, что в 3-м примере более или менее ответы на него?

...