1

I am working through pygame tutorials and at the moment I am trying to figure out how to select a circle which is firing a ball where a ball hits another ball which in turn knocks a box down. The knocking of the box works fine when the ball hits the box. However, when I add the mouse movement so that I can select the ball again and place it in the same position so that it can be hit again so that the box knocks again. The ball just rolls backwards without firing the second ball to knock the box.Here is the previous code that works with one ball firing another ball but without the mouse movements i.e without allowing the ball to be selected and dragged.

import pygame
from pygame.locals import *
from pygame.color import *
import pymunk as pm
from pymunk import Vec2d
import sys
from random import randint

def to_pygame(p):
    """Small hack to convert pymunk to pygame coordinates"""
    return int(p[0]), int(-p[1]+600)


def draw_ball(screen, ball, colour):
    r = ball.radius
    rot = ball.body.rotation_vector
    p = to_pygame(ball.body.position)
    p2 = Vec2d(rot.x, -rot.y) * r * 0.9
    pygame.draw.line(screen, THECOLORS["red"], p, p+p2)
    pygame.draw.circle(screen, colour, p, int(r), 3)


def add_ball(space, x=0, y=130):
    mass = 1.3
    radius = 20
    inertia = pm.moment_for_circle(mass, 0, radius, (0,0))
    body = pm.Body(mass, inertia)
    body.position = (x,y)
    shape = pm.Circle(body, radius, (0,0))
    shape.friction = 10.0
    shape.elasticity = 1.0
    space.add(body, shape)

    return shape


def add_box(space, size, pos, mass=0.3):
    points = [(-size, -size), (-size, size), (size,size), (size, -size)]
    moment = pm.moment_for_poly(int(mass), points, (0,0))

    body = pm.Body(mass, moment)
    body.position = pos

    shape = pm.Poly(body, points, (0,0))
    shape.friction = 1
    space.add(body,shape)

    return shape

def draw_box(screen, box):
    ps = box.get_points()
    ps.append(ps[0])
    newps = [to_pygame(x) for x in ps]
    pygame.draw.polygon(screen, THECOLORS["blue"], newps, 3)


def main():
    pygame.init()
    screen = pygame.display.set_mode((600, 600))
    pygame.display.set_caption("Impulsive balls")
    clock = pygame.time.Clock()

    balls = []
    space = pm.Space()
    space.gravity = (0.0, -300.0)

    # ground
    body = pm.Body()
    shape = pm.Segment(body, (0,100), (450,100), .0)
    shape.friction = 6.0
    space.add(shape)

    # hidden ramp
    body = pm.Body()
    slope = pm.Segment(body, (0,100), (180,150), .0)
    space.add(slope)

    balls.append(add_ball(space, 10, 130))
    balls.append(add_ball(space, 100, 150))
    #joint = pm.PinJoint(balls[0].body, balls[1].body)
    #joint.distance = 90

    mass = 1.0
    size = 20
    box = add_box(space, size, (400,100+size), mass)
    count = 0

    while 1:
        space.step(1/30.0)
        clock.tick(30)

        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit(0)
            elif event.type == KEYDOWN:
                if event.key == K_ESCAPE:
                    pygame.quit()
                    sys.exit(0)

        if count == 10:
            pm.Body.apply_impulse(balls[0].body, (450,0))

        screen.fill(THECOLORS["white"])
        pygame.draw.line(screen, THECOLORS["red"], to_pygame((0,100)), to_pygame((450,100)), 3)

        pygame.draw.line(screen, THECOLORS["red"], to_pygame((0,100)), to_pygame((180,150)), 3)

        draw_box(screen, box)
        for ball in balls:
            draw_ball(screen, ball, THECOLORS["green"])

        pygame.display.flip()
        count += 1

if __name__ == '__main__':
    main()

Here is the second version where I add the mouse movement code

import pygame
from pygame.locals import *
from pygame.color import *
import pymunk as pm
from pymunk import Vec2d
import sys
from random import randint

def to_pygame(p):
    """Small hack to convert pymunk to pygame coordinates"""
    return int(p[0]), int(-p[1]+600)
def from_pygame(p):
    return to_pygame(p)

def draw_ball(screen, ball, colour):
    r = ball.radius
    rot = ball.body.rotation_vector
    p = to_pygame(ball.body.position)
    p2 = Vec2d(rot.x, -rot.y) * r * 0.9
    pygame.draw.line(screen, THECOLORS["blue"], p, p+p2)
    pygame.draw.circle(screen, colour, p, int(r), 3)


def add_ball(space, x=0, y=130):
    mass = 1.3 #1.5
    radius = 20
    inertia = pm.moment_for_circle(mass, 0, radius, (0,0))
    body = pm.Body(mass, inertia)
    body.position = (x,y)
    shape = pm.Circle(body, radius, (0,0))
    shape.friction = 10.0
    shape.elasticity = 1.0
    space.add(body, shape)

    return shape


def add_box(space, size, pos, mass=0.3):
    points = [(-size, -size), (-size, size), (size,size), (size, -size)]
    moment = pm.moment_for_poly(int(mass), points, (0,0))

    body = pm.Body(mass, moment)
    body.position = pos

    shape = pm.Poly(body, points, (0,0))
    shape.friction = 1
    space.add(body,shape)

    return shape

def draw_box(screen, box):
    ps = box.get_points()
    ps.append(ps[0])
    newps = [to_pygame(x) for x in ps]
    pygame.draw.polygon(screen, THECOLORS["blue"], newps, 3)


def main():
    pygame.init()
    screen = pygame.display.set_mode((600, 600))
    pygame.display.set_caption("Impulsive balls")
    clock = pygame.time.Clock()
    body = []
    selected = None
    balls = []
    space = pm.Space()
    space.gravity = (0.0, -300.0)

    # ground
    body = pm.Body()
    shape = pm.Segment(body, (0,100), (450,100), .0)
    shape.friction = 6.0
    space.add(shape)

    # hidden ramp
    body = pm.Body()
    slope = pm.Segment(body, (0,100), (180,150), .0)
    space.add(slope)

    balls.append(add_ball(space, 10, 130))
    balls.append(add_ball(space, 100, 150))
    #joint = pm.PinJoint(balls[0].body, balls[1].body)
    #joint.distance = 90

    mass = 1.0
    size = 20
    box = add_box(space, size, (400,100+size), mass)
    count = 0

    while 1:
        space.step(1/30.0)
        clock.tick(30)

        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit(0)
            elif event.type == KEYDOWN:
                if event.key == K_ESCAPE:
                    pygame.quit()
                    sys.exit(0)
                if count == 10:
                    pm.Body.apply_impulse(balls[0].body, (450,0))
                if event.key == K_p:
                    balls[0].body.apply_impulse((450,0))
                if event.key == K_s:
                    balls[0].body.apply_impulse((-450,0))



            elif event.type == MOUSEBUTTONDOWN:
                p = from_pygame(Vec2d(event.pos))
                selected = space.point_query_first(p)
            elif event.type == MOUSEBUTTONUP:
                if selected != None:
                    selected = None
            elif event.type == MOUSEMOTION:
                if selected != None:
                    selected.body.position = from_pygame(event.pos)

        screen.fill(THECOLORS["white"])
        pygame.draw.line(screen, THECOLORS["red"], to_pygame((0,100)), to_pygame((450,100)), 3)

        pygame.draw.line(screen, THECOLORS["red"], to_pygame((0,100)), to_pygame((180,150)), 3)

        draw_box(screen, box)
        for ball in balls:
            draw_ball(screen, ball, THECOLORS["green"])

        pygame.display.flip()
        count += 1

if __name__ == '__main__':
    main()

Also, how can I have the ball at the same position so that I can drag it and push the other ball to knock the box instead of making the ball roll back, so later I can select the fired ball again and place it next to the ball without rolling back

4

1 回答 1

1

对于您的第一个问题,球没有在第二个文件中射击。问题是您已将该代码放入事件块中,这仅在触发外部事件时发生(例如按下键)。要修复它,需要将该块移出该 for 循环,如下所示:

...

while 1:
    space.step(1/30.0)
    clock.tick(30)

    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit(0)
        elif event.type == KEYDOWN:
            if event.key == K_ESCAPE:
                pygame.quit()
                sys.exit(0)
            if event.key == K_p:
                balls[0].body.apply_impulse((450,0))
            if event.key == K_s:
                balls[0].body.apply_impulse((-450,0))
        elif event.type == MOUSEBUTTONDOWN:
            p = from_pygame(Vec2d(event.pos))
            selected = space.point_query_first(p)
        elif event.type == MOUSEBUTTONUP:
            if selected != None:
                selected = None
        elif event.type == MOUSEMOTION:
            if selected != None:
                selected.body.position = from_pygame(event.pos)

    if count == 10:
        pm.Body.apply_impulse(balls[0].body, (450,0))

...

为了防止球移动,我建议将它们放在平坦的地面上。我对 main 进行了以下更改以说明我的意思。请注意,我禁用了球发射,以便您可以看到球保持在原位。我还建议您在屏幕上放置一些看不见的墙,以使所有对象都卡在框架内。

...

def main():
    pygame.init()
    screen = pygame.display.set_mode((600, 600))
    pygame.display.set_caption("Impulsive balls")
    clock = pygame.time.Clock()
    body = []
    selected = None
    balls = []
    space = pm.Space()
    space.gravity = (0.0, -300.0)

    # ground
    body = pm.Body()
    shape = pm.Segment(body, (0,100), (450,100), .0)
    shape.friction = 6.0
    space.add(shape)

    # hidden ramp
    body = pm.Body()
    slope = pm.Segment(body, (20,100), (180,150), .0)
    space.add(slope)
    body = pm.Body()
    slopetop = pm.Segment(body, (180,150), (190,150), .0)
    space.add(slopetop)

    balls.append(add_ball(space, 10, 130))
    balls.append(add_ball(space, 185, 170))
    #joint = pm.PinJoint(balls[0].body, balls[1].body)
    #joint.distance = 90

    mass = 1.0
    size = 20
    box = add_box(space, size, (400,100+size), mass)
    count = 0

    while 1:
        space.step(1/30.0)
        clock.tick(30)

        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit(0)
            elif event.type == KEYDOWN:
                if event.key == K_ESCAPE:
                    pygame.quit()
                    sys.exit(0)
                if event.key == K_p:
                    balls[0].body.apply_impulse((450,0))
                if event.key == K_s:
                    balls[0].body.apply_impulse((-450,0))
            elif event.type == MOUSEBUTTONDOWN:
                p = from_pygame(Vec2d(event.pos))
                selected = space.point_query_first(p)
            elif event.type == MOUSEBUTTONUP:
                if selected != None:
                    selected = None
            elif event.type == MOUSEMOTION:
                if selected != None:
                    selected.body.position = from_pygame(event.pos)

        if count == 10 and 0:
            pm.Body.apply_impulse(balls[0].body, (450,0))

        screen.fill(THECOLORS["white"])
        pygame.draw.line(screen, THECOLORS["red"], to_pygame((0,100)), to_pygame((450,100)), 3)
        pygame.draw.line(screen, THECOLORS["red"], to_pygame((20,100)), to_pygame((180,150)), 3)
        pygame.draw.line(screen, THECOLORS["red"], to_pygame((180,150)), to_pygame((190,150)), 3)

        draw_box(screen, box)
        for ball in balls:
            draw_ball(screen, ball, THECOLORS["green"])

        pygame.display.flip()
        count += 1
...
于 2013-05-19T04:10:41.670 回答