Search code examples
pythonpygamepymunk

How can I retrieve coordinates of a pymunk.Circle?


I'm new to python so please forgive me if I'm misnaming pygame objects. I've been tasked to build a virtual Pachinko gaming machine. I'm having trouble getting the coordinates of the ball as it falls through the window. I need the coordinates to keep track of score and reset the loops so the user can drop another ball once it bottoms out.

Here's my source code.

#Project specific libraries
import pygame                   #https://www.pygame.org/news
import pymunk                   #http://www.pymunk.org/en/latest/
import pymunk.util
import pymunk.pygame_util
import tkinter                  #https://docs.python.org/2/library/tkinter.html

#Standard libraries
import sys
import math
import random
import os
import time

#Import ALL tools from tkinter & pygame libraies
from tkinter import *
from pygame import *

#Constants for object to object interaction
COLLTYPE_FLOOR = 3
COLLTYPE_BOUNCER = 2
COLLTYPE_BALL = 1

#****************************************************************************
def goal_reached(arbiter, space1, data):

    ball_i, floor_i  = arbiter.shapes

    space_i = space1

    space_i.remove(ball_i, ball_i.body)
    remove_from_ball_list(ball_i)
    return True
#*************************************************************

main = Tk()
main.resizable(width=False, height=False)
main.title("Pachinko")
embed = Frame(main, width = 500, height = 500) 

embed.pack() #packs window to the left
os.environ['SDL_WINDOWID'] = str(embed.winfo_id())

screen = pygame.display.set_mode((500,500))
screen.fill(pygame.Color(255,215,255))
clock = pygame.time.Clock()

#List of ball "objects"
balls = []

#might not need balls_to_remove List
balls_to_remove = []

#velocity, gravity
space = pymunk.Space()
space.gravity = 0, -200

#Floor boundaries
floor = pymunk.Segment(space.static_body, (0.0, 10.0), (500.0, 10.0), 1.0)
floor.collision_type = COLLTYPE_FLOOR
space.add(floor)

#Left wall boundaries
left_wall = pymunk.Segment(space.static_body, (0.0, 500.0), (0.0, 0.0), 1.0)
left_wall.friction = 1.0
left_wall.elasticity = 0.9
left_wall.collision_type = COLLTYPE_BOUNCER
space.add(left_wall)

#Right wall boundaries
right_wall = pymunk.Segment(space.static_body, (500.0, 500.0), (500.0, 0.0), 1.0)
right_wall.friction = 1.0
right_wall.elasticity = 0.9
right_wall.collision_type = COLLTYPE_BOUNCER
space.add(right_wall)

draw_options = pymunk.pygame_util.DrawOptions(screen)
space.debug_draw(draw_options)

#Generate a fixed field of pins
done = 0
x_shift = 45
y_shift = 150
step = 0
tier = 0
while(done == 0):
    variance = random.randint(1, 15) 
    pin_radius = random.randint(14, 17)
    newPin = pymunk.Body(body_type=pymunk.Body.KINEMATIC)
    x = x_shift + variance
    y = y_shift + variance
    newPin.position = x, y
    shape = pymunk.Circle(newPin, pin_radius)
    shape.collision_type = COLLTYPE_BOUNCER
    space.add(newPin, shape)
    x_shift += 85
    step += 1
    if(step == 5):          #Tier one
        x_shift = 100
        y_shift += 60
    if(step == 10):         #Tier two
        x_shift = 50
        y_shift += 60
    if(step == 15):         #Tier three
        x_shift = 100
        y_shift += 60
    if(step == 20):         #Tier four
        x_shift =50
        y_shift += 60
    if(step == 25):         #Tier five
        x_shift = 100
        y_shift += 60
        done = 1

    #Generate the five poles (left to right)
step = 0
x_shift = 100
while(step < 4):
    pole0 = pymunk.Segment(space.static_body, (x_shift, 100.0), (x_shift, 10.0), 5.0)
    pole0.friction = 1.0
    pole0.elasticity = 0.9
    pole0.collision_type = COLLTYPE_BOUNCER
    space.add(pole0)
    step += 1
    x_shift += 100


pygame.display.flip()
pygame.display.init()
pygame.display.update()

#https://stackoverflow.com/questions/23410161/pygame-collision-code
h = space.add_collision_handler(COLLTYPE_BALL, COLLTYPE_FLOOR)

h.begin = goal_reached

def remove_from_ball_list(temp1):
    #print("where in list is it?")
    for ball in balls:
        if temp1 == ball:
            #print("Time to remove from the list")
            balls.remove(ball)



#Primary game loop
ticks = 50
play = True
while play == True:
    mouseClick = pygame.event.get()
    dropHeight = 440

    for event in mouseClick:
        if event.type == pygame.MOUSEBUTTONUP:
            mouseX, mouseY = pygame.mouse.get_pos()
            ticks = 0

    #keep making new balls & pins
    if ticks == 0:
        step = 0
        x_shift = 0
        y_shift = 0

        #Generate the new ball
        mass = 1
        inertia = pymunk.moment_for_circle(mass, 0, 14, (0, 0))
        radius = 12
        ball = pymunk.Body(mass, inertia)

        #Keep the ball in bounds when user drops
        if(mouseX < 25):
            mouseX = 10
        if(mouseX > 480):
            mouseX = 490
        ball.position = mouseX, dropHeight
        shape = pymunk.Circle(ball, radius)
        shape.collision_type = COLLTYPE_BALL
        space.add(ball, shape)
        balls.append(shape)
        ticks = 50
    pygame.draw.line(screen, (255,0,255), (20,60), (480,60), 2)
    space.step(1/50.0)
    space.debug_draw(draw_options)
    pygame.display.update()
    pygame.display.flip()
    screen.fill(pygame.Color(255,215,255))
    clock.tick(50)
    #ticks -= 1
    main.update()

And here is the specific piece of code I'd like to add the feature:

#Primary game loop
ticks = 50
play = True
while play == True:
    mouseClick = pygame.event.get()
    dropHeight = 440

    for event in mouseClick:
        if event.type == pygame.MOUSEBUTTONUP:
            mouseX, mouseY = pygame.mouse.get_pos()
            ticks = 0

    #keep making new balls & pins
    if ticks == 0:
        step = 0
        x_shift = 0
        y_shift = 0

        #Generate the new ball
        mass = 1
        inertia = pymunk.moment_for_circle(mass, 0, 14, (0, 0))
        radius = 12
        ball = pymunk.Body(mass, inertia)

        #Keep the ball in bounds when user drops
        if(mouseX < 25):
            mouseX = 10
        if(mouseX > 480):
            mouseX = 490
        ball.position = mouseX, dropHeight
        shape = pymunk.Circle(ball, radius)
        shape.collision_type = COLLTYPE_BALL
        space.add(ball, shape)
        balls.append(shape)
        ticks = 50
    pygame.draw.line(screen, (255,0,255), (20,60), (480,60), 2)
    space.step(1/50.0)
    space.debug_draw(draw_options)
    pygame.display.update()
    pygame.display.flip()
    screen.fill(pygame.Color(255,215,255))
    clock.tick(50)
    #ticks -= 1
    main.update()

I would like to print the ball coordinates to the terminal so I can add scoring and restart features. I feel like that I'm missing a very simple solution.

screenshot


Solution

  • It seems like the balls are collected in a list, balls. So you can just loop through it to get all the info you need. For example, if you put the below code in the while loop it will print the location of each ball each frame.

    for b in balls:
      print("ball position", b.body.position)
    

    However, maybe you do not need to check this yourself each frame? You already have a goal_reached function, that is called once the ball reached the goal if I understand it correctly. In there you can update the score and toggle something to allow the player to add another ball.