How can I retrieve coordinates of a pymunk.Circle? - python

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.

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.

Related

TypeError: start_new_game() missing 1 required positional argument: 'max_health' [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
So I have been trying to figure out why I keep getting this error, even though everything is run from my game_view.py module. Everytime I press the 1 key to start a game and test to see if the players health bar and hit points works, I get this error message. TypeError: start_new_game() missing 1 required positional argument: 'max_health'
The sad part is, is that it reffers me to the start_view which doesn't have anything to do with anything else but running the start screen so players can choose to play solo or with another player. Below is the game_view and start_view so you guys can see where I'm going wrong. I wanted to test this out so this way I can add other enemies, bosses and power-ups later, but running into this error for the past several days is hindering progress. Anyway, code for both will be below. Thanks in advance for any and all help.
start_view:
import arcade
from game_view import GameView
class StartView(arcade.View):
def on_show(self):
# This is run once when we switch to this view
arcade.set_background_color(arcade.csscolor.BLACK)
# Reset the viewport, necessary if we have a scrolling game
arcade.set_viewport(0, self.window.width, 0, self.window.height)
def on_draw(self):
# Draw this view
arcade.start_render()
line_height = 70
line_location = self.window.height - line_height * 2
arcade.draw_text("Space Defense Force",
self.window.width / 2,
line_location,
arcade.color.WHITE,
font_size=50,
anchor_x="center",
font_name="SF Atarian System")
line_location -= line_height
line_location -= line_height
arcade.draw_text("1 - Start One Player Game",
self.window.width / 2,
line_location,
arcade.color.WHITE,
font_size=20,
anchor_x="center",
font_name="SF Atarian System")
# if len(self.window.joysticks) > 1:
# color = arcade.color.WHITE
# else:
# color = arcade.color.GRAY
color = arcade.color.GRAY
line_location -= line_height
arcade.draw_text("2 - Start Two Player Game",
self.window.width / 2,
line_location,
color,
font_size=20,
anchor_x="center",
font_name="SF Atarian System")
line_location -= line_height
line_location -= line_height
color = arcade.color.WHITE
arcade.draw_text("Use joysticks to play, or arrow keys to move and number keys to fire.",
self.window.width / 2,
line_location,
color,
font_size=20,
anchor_x="center",
font_name="SF Atarian System")
def on_key_press(self, symbol: int, modifiers: int):
if symbol == arcade.key.KEY_1:
game_view = GameView()
game_view.start_new_game(1)
self.window.show_view(game_view)
elif symbol == arcade.key.KEY_2:
game_view = GameView()
game_view.start_new_game(2)
self.window.show_view(game_view)
game_view:
import random
import math
import arcade
from health import Health
from game_over_view import GameOverView
from typing import cast
from arcade.experimental.shadertoy import Shadertoy
from constants import *
from asteroid_sprite import AsteroidSprite
from ship_sprite import ShipSprite
from bullet import Bullet
from glow_line import GlowLine
from glow_ball import GlowBall
from explosion import ExplosionMaker
from glow_image_sprite import GlowImageSprite
from window import Window as window
class GameView(arcade.View):
# Main application class
def __init__(self):
super().__init__()
# Sprite lists
self.player_sprite_list = arcade.SpriteList()
self.asteroid_list = arcade.SpriteList()
self.bullet_list = arcade.SpriteList()
self.ship_life_list = arcade.SpriteList()
self.health_list = arcade.SpriteList()
# Sounds
self.laser_sound = arcade.load_sound(":resources:sounds/hurt5.wav")
self.hit_sound1 = arcade.load_sound(":resources:sounds/explosion1.wav")
self.hit_sound2 = arcade.load_sound(":resources:sounds/explosion2.wav")
self.hit_sound3 = arcade.load_sound(":resources:sounds/hit1.wav")
self.hit_sound4 = arcade.load_sound(":resources:sounds/hit2.wav")
self.dead_sound = arcade.load_sound(":resources:sounds/gameover2.wav")
self.glowball_shadertoy = Shadertoy.create_from_file(self.window.get_size(), "glow_ball.glsl")
self.glowline_shadertoy = Shadertoy.create_from_file(self.window.get_size(), "glow_line.glsl")
self.explosion_list = []
# for joystick in self.window.joysticks:
# joystick.push_handlers(self)
def start_new_game(self, player_count, max_health):
#Set up the game and initialize the variables
self.game_over = False
arcade.set_background_color(arcade.csscolor.BLACK)
# Sprite lists
self.player_sprite_list = arcade.SpriteList()
self.asteroid_list = arcade.SpriteList()
self.bullet_list = arcade.SpriteList()
self.ship_life_list = arcade.SpriteList()
self.health_list = arcade.SpriteList()
# if len(self.window.joysticks) > 0:
# joystick = self.window.joysticks[0]
# else:
# joystick = None
joystick = None
player_sprite = ShipSprite(":resources:images/space_shooter/playerShip1_orange.png",
SCALE,
joystick,
player_no=1,
player_count=player_count,
max_health=5)
self.player_sprite_list.append(player_sprite)
self.health_list.append(max_health)
if player_count > 1:
joystick = None
# if len(self.window.joysticks) > 1:
# joystick = self.window.joysticks[1]
# else:
# joystick = None
player_sprite = ShipSprite(":resources:images/space_shooter/playerShip1_green.png",
SCALE,
joystick,
player_no=2,
player_count=player_count,
max_health=5
)
self.player_sprite_list.append(player_sprite)
self.health_list.append(max_health)
# Set up the player
for player in self.player_sprite_list:
player.score = 0
player.lives = 3
# Set up the little icons that represent the player lives.
cur_pos = 10
for i in range(self.player_sprite_list[0].lives):
life = arcade.Sprite(":resources:images/space_shooter/playerLife1_orange.png", SCALE)
life.center_x = cur_pos + life.width
life.center_y = life.height
cur_pos += life.width
self.ship_life_list.append(life)
if len(self.player_sprite_list) > 1:
cur_pos = 100
for i in range(self.player_sprite_list[1].lives):
life = arcade.Sprite(":resources:images/space_shooter/playerLife1_green.png", SCALE)
life.center_x = cur_pos + life.width
life.center_y = life.height
cur_pos += life.width
self.ship_life_list.append(life)
# Make the asteroids
image_list = (":resources:images/space_shooter/meteorGrey_big1.png",
":resources:images/space_shooter/meteorGrey_big2.png",
":resources:images/space_shooter/meteorGrey_big3.png",
":resources:images/space_shooter/meteorGrey_big4.png")
for i in range(STARTING_ASTEROID_COUNT):
image_no = random.randrange(4)
enemy_sprite = AsteroidSprite(image_list[image_no], SCALE)
enemy_sprite.guid = "Asteroid"
enemy_sprite.center_y = random.randrange(BOTTOM_LIMIT, TOP_LIMIT)
enemy_sprite.center_x = random.randrange(LEFT_LIMIT, RIGHT_LIMIT)
enemy_sprite.change_x = random.random() * 2 - 1
enemy_sprite.change_y = random.random() * 2 - 1
enemy_sprite.change_angle = (random.random() - 0.5) * 2
enemy_sprite.size = 4
self.asteroid_list.append(enemy_sprite)
def on_draw(self):
# Render the screen.
# This command has to happen before we start drawing
arcade.start_render()
# Draw all the sprites.
self.asteroid_list.draw()
self.ship_life_list.draw()
for bullet in self.bullet_list:
bullet.draw()
self.bullet_list.draw()
for explosion in self.explosion_list:
explosion.render()
self.player_sprite_list.draw()
self.health_list.draw()
# Put the text on the screen.
output = f"Player 1 Score: {self.player_sprite_list[0].score}"
arcade.draw_text(output, 10, 40, arcade.color.AMBER,
font_size=15,
font_name="Arcade")
if len(self.player_sprite_list) > 1:
output = f"Player 2 Score: {self.player_sprite_list[1].score}"
arcade.draw_text(output, 500, 40, arcade.color.AMBER,
font_size=15,
font_name="Arcade")
output = f"Asteroid Count: {len(self.asteroid_list)}"
arcade.draw_text(output, 10, 80, arcade.color.AMBER,
font_size=15,
font_name="Arcade")
for player in health_list:
player.draw_health_number()
player.draw_health_bar()
# def on_joybutton_press(self, joystick, button):
# # What player is this?
# if joystick == self.window.joysticks[0]:
# player_sprite = self.player_sprite_list[0]
# else:
# player_sprite = self.player_sprite_list[1]
# if player_sprite.player_no == 1:
# color = 255, 128, 128
# else:
# color = 128, 255, 128
# if button == 0:
# self.fire_circle(color, player_sprite, player_no=player_sprite.player_no)
# elif button == 1:
# self.fire_line(color, player_sprite, player_no=player_sprite.player_no)
# elif button == 2:
# bullet_sprite = GlowImageSprite(":resources:images/space_shooter/laserBlue01.png",
# SCALE,
# glowcolor=arcade.color.WHITE,
# shadertoy=self.glowball_shadertoy,
# player_no=player_sprite.player_no)
# self.set_bullet_vector(bullet_sprite, 10, player_sprite)
# arcade.play_sound(self.laser_sound)
def on_key_press(self, symbol, modifiers):
# Shoot if the player hit the space bar and we aren't respawning.
if symbol == arcade.key.LEFT:
self.player_sprite_list[0].change_angle = 3
elif symbol == arcade.key.RIGHT:
self.player_sprite_list[0].change_angle = -3
elif symbol == arcade.key.UP:
self.player_sprite_list[0].thrust = 0.15
elif symbol == arcade.key.DOWN:
self.player_sprite_list[0].thrust = -.2
elif symbol == arcade.key.KEY_1:
color = (255, 128, 128)
self.fire_circle(color, self.player_sprite_list[0], player_no=0)
elif symbol == arcade.key.KEY_2:
color = (128, 255, 128)
self.fire_circle(color, self.player_sprite_list[0], player_no=0)
elif symbol == arcade.key.KEY_3:
color = (128, 128, 255)
self.fire_circle(color, self.player_sprite_list[0], player_no=0)
elif symbol == arcade.key.KEY_4:
color = (255, 128, 255)
self.fire_circle(color, self.player_sprite_list[0], player_no=0)
elif symbol == arcade.key.KEY_5:
color = (255, 255, 255)
self.fire_line(color, self.player_sprite_list[0], player_no=0)
elif symbol == arcade.key.KEY_6:
color = (64, 255, 64)
self.fire_line(color, self.player_sprite_list[0], player_no=0)
elif symbol == arcade.key.KEY_7:
bullet_sprite = GlowImageSprite(":resources:images/space_shooter/laserBlue01.png",
SCALE,
glowcolor=arcade.color.WHITE,
shadertoy=self.glowball_shadertoy,
player_no=0)
self.set_bullet_vector(bullet_sprite, 13, self.player_sprite_list[0])
arcade.play_sound(self.laser_sound)
def fire_circle(self, bullet_color, player_sprite, player_no):
bullet_sprite = GlowBall(glowcolor=bullet_color,
radius=5,
shadertoy=self.glowball_shadertoy,
player_no=player_no)
self.set_bullet_vector(bullet_sprite, 5, player_sprite)
arcade.play_sound(self.laser_sound)
def fire_line(self, bullet_color, player_sprite, player_no):
bullet_sprite = GlowLine(glowcolor=bullet_color,
shadertoy=self.glowline_shadertoy,
player=player_sprite,
player_no=player_no)
self.set_bullet_vector(bullet_sprite, 13, player_sprite)
arcade.play_sound(self.laser_sound)
def set_bullet_vector(self, bullet_sprite, bullet_speed, player_sprite):
bullet_sprite.change_y = \
math.cos(math.radians(player_sprite.angle)) * bullet_speed
bullet_sprite.change_x = \
-math.sin(math.radians(player_sprite.angle)) \
* bullet_speed
bullet_sprite.center_x = player_sprite.center_x
bullet_sprite.center_y = player_sprite.center_y
self.bullet_list.append(bullet_sprite)
def on_key_release(self, symbol, modifiers):
# Called whenever a key is released
if symbol == arcade.key.LEFT:
self.player_sprite_list[0].change_angle = 0
elif symbol == arcade.key.RIGHT:
self.player_sprite_list[0].change_angle = 0
elif symbol == arcade.key.UP:
self.player_sprite_list[0].thrust = 0
elif symbol == arcade.key.DOWN:
self.player_sprite_list[0].thrust = 0
def split_asteroid(self, asteroid: AsteroidSprite):
# Split an asteroid into chunks
x = asteroid.center_x
y = asteroid.center_y
if asteroid.size == 4:
for i in range(3):
image_no = random.randrange(2)
image_list = [":resources:images/space_shooter/meteorGrey_med1.png",
":resources:images/space_shooter/meteorGrey_med2.png"]
enemy_sprite = AsteroidSprite(image_list[image_no],
SCALE * 1.5)
enemy_sprite.center_y = y
enemy_sprite.center_x = x
enemy_sprite.change_x = random.random() * 2.5 - 1.25
enemy_sprite.change_y = random.random() * 2.5 - 1.25
enemy_sprite.change_angle = (random.random() - 0.5) * 2
enemy_sprite.size = 3
self.asteroid_list.append(enemy_sprite)
self.hit_sound1.play()
elif asteroid.size == 3:
for i in range(3):
image_no = random.randrange(2)
image_list = [":resources:images/space_shooter/meteorGrey_small1.png",
":resources:images/space_shooter/meteorGrey_small2.png"]
enemy_sprite = AsteroidSprite(image_list[image_no],
SCALE * 1.5)
enemy_sprite.center_y = y
enemy_sprite.center_x = x
enemy_sprite.change_x = random.random() * 3 - 1.5
enemy_sprite.change_y = random.random() * 3 - 1.5
enemy_sprite.change_angle = (random.random() - 0.5) * 2
enemy_sprite.size = 2
self.asteroid_list.append(enemy_sprite)
self.hit_sound2.play()
elif asteroid.size == 2:
for i in range(3):
image_no = random.randrange(2)
image_list = [":resources:images/space_shooter/meteorGrey_tiny1.png",
":resources:images/space_shooter/meteorGrey_tiny2.png"]
enemy_sprite = AsteroidSprite(image_list[image_no],
SCALE * 1.5)
enemy_sprite.center_y = y
enemy_sprite.center_x = x
enemy_sprite.change_x = random.random() * 3.5 - 1.75
enemy_sprite.change_y = random.random() * 3.5 - 1.75
enemy_sprite.change_angle = (random.random() - 0.5) * 2
enemy_sprite.size = 1
self.asteroid_list.append(enemy_sprite)
self.hit_sound3.play()
elif asteroid.size == 1:
self.hit_sound4.play()
def on_update(self, x, delta_time):
# Move everything
self.asteroid_list.update()
self.bullet_list.update()
self.player_sprite_list.update()
self.health_list.update()
explosion_list_copy = self.explosion_list.copy()
for explosion in explosion_list_copy:
explosion.update(x)
if explosion.time > .9:
self.explosion_list.remove(explosion)
for bullet in self.bullet_list:
hit_list = arcade.check_for_collision_with_list(bullet, self.player_sprite_list)
# If it did hit, get rid of sprite
if len(hit_list) > 0:
bullet.remove_from_lists()
for player in hit_list:
if not isinstance(player, ShipSprite):
raise TypeError("List contents must be all ints")
# Remove one health point
player.cur_health -= 1
# Check Health
if player.cur_health <= 0:
arcade.play_sound(self.dead_sound)
view=GameOverView
self.window.show_view(view)
else:
# Not Dead
arcade.play_sound(self.hit_sound1)
assert isinstance(bullet, Bullet)
asteroids = arcade.check_for_collision_with_list(bullet, self.asteroid_list)
if len(asteroids) > 0:
explosion = ExplosionMaker(self.window.get_size(), bullet.position)
self.explosion_list.append(explosion)
for asteroid in asteroids:
assert isinstance(asteroid, AsteroidSprite)
self.player_sprite_list[bullet.player_no - 1].score += 1
self.split_asteroid(cast(AsteroidSprite, asteroid)) # expected AsteroidSprite, got Sprite instead
asteroid.remove_from_sprite_lists()
bullet.remove_from_sprite_lists()
# Remove bullet if it goes off-screen
size = max(bullet.width, bullet.height)
if bullet.center_x < 0 - size:
bullet.remove_from_sprite_lists()
if bullet.center_x > SCREEN_WIDTH + size:
bullet.remove_from_sprite_lists()
if bullet.center_y < 0 - size:
bullet.remove_from_sprite_lists()
if bullet.center_y > SCREEN_HEIGHT + size:
bullet.remove_from_sprite_lists()
for player in self.player_sprite_list:
assert isinstance(player, ShipSprite, max_health)
if not player.respawning:
asteroids = arcade.check_for_collision_with_list(player, self.asteroid_list)
if len(asteroids) > 0:
if player.lives > 0:
player.lives -= 1
player.respawn()
self.split_asteroid(cast(AsteroidSprite, asteroids[0]))
asteroids[0].remove_from_sprite_lists()
self.ship_life_list.pop().remove_from_sprite_lists()
elif len(asteroids) > 0:
if player.health > 0:
player.health -=1
player.respawn()
self.split_asteroid(cast(AsteroidSprite, asteroids[0]))
asteroids[0].remove_from_sprite_lists()
self.ship_list_list.pop().remove_from_sprite_lists()
else:
arcade.play_sound(self.dead_sound)
view = GameOverView()
self.window.show_view(view)
Sorry in advance if I have tons of code to go through. But like I said I am adding some features that wasn't in the Arcade Repository from github. I even set a separte module for the health bar and hit points so this way I can see what conflicts. But the error above has been a royal pain.Thanks again.
P.S. Remarked out joysticks because it was conflicting with another module I built.
As your title says, your start game function requires 2 arguments: player_count and max_healh. And in your code, when using start_new_game, you aren't supplying the max_health argument, thus raising an error.
(function) start_new_game(self, player_count, max_health)

Jumping creates a distance between man and the floor Pygame [duplicate]

This question already has answers here:
How to simulate Jumping in Pygame for this particular code
(1 answer)
jumping too fast?
(1 answer)
How to make a circular object jump using pygame? [duplicate]
(2 answers)
Pygame Bouncy Ball Sinks Through Floor
(1 answer)
Closed 2 years ago.
The full code:
import glob
import pygame
import os
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (100, 40)
class Man(pygame.sprite.Sprite):
# just taking random image to get height and width after trans scale to be able to crop later (see lines 23/36)
idle_images_list = glob.glob(r"C:\Users\aviro\Desktop\מחשבים\python projects\platform game\animation\fighter\PNG\PNG Sequences\Firing"+'\*')
random_image = pygame.image.load(idle_images_list[0])
new_img_width = int(random_image.get_width()/2.5)
new_img_height = int(random_image.get_height()/2.5)
random_image_after_crop = random_image.subsurface((70, 35, new_img_width-125, new_img_height-45))
# to be able to use needed images indexes
# getting idle animations list
right_idle_list = []
left_idle_list = []
for images in idle_images_list:
img = pygame.image.load(images)
wid = img.get_width()
hei = img.get_height()
img = pygame.transform.scale(img, (int(wid/2.5), int(hei/2.5)))
img = img.subsurface((70, 35, new_img_width-125, new_img_height-45))
right_idle_list.append(img)
left_idle_list.append(pygame.transform.flip(img, True, False))
# getting movement animations list
walk_animation_list = glob.glob(r"C:\Users\aviro\Desktop\מחשבים\python projects\platform game\animation\fighter\PNG\PNG Sequences\Run Firing"+'\*')
walk_right_list = []
walk_left_list = []
for files in walk_animation_list: # setting the animation list
img = pygame.image.load(files)
wid = img.get_width()
hei = img.get_height()
img = pygame.transform.scale(img, (int(wid/2.5), int(hei/2.5))) # chaging scale
img = img.subsurface((70, 35, new_img_width-125, new_img_height-45))
walk_right_list.append(img)
walk_left_list.append(pygame.transform.flip(img, True, False)) # mirror list
def __init__(self, x, y,):
super(Man, self).__init__()
self.walk_left_list = Man.walk_left_list
self.walk_right_list = Man.walk_right_list
self.width = Man.new_img_width
self.height = Man.new_img_height
self.hitbox = (x+55, y+35, self.width-125, self.height-45) # nothing special on those num, just Trial and error
self.rect = Man.random_image_after_crop.get_rect()
self.rect.x = x
self.rect.y = y
def game_redraw(): # print things in end for main loop
global right
global left
screen.blit(bg_image, (0, 0))
if right:
screen.blit(man.walk_right_list[frame_count//3], (man.rect.x, man.rect.y))
elif left:
screen.blit(man.walk_left_list[frame_count//3], (man.rect.x, man.rect.y))
else:
if last_action == "right":
screen.blit(man.right_idle_list[frame_count//13], (man.rect.x, man.rect.y))
if last_action == "left":
screen.blit(man.left_idle_list[frame_count//13], (man.rect.x, man.rect.y))
else: # just for the first move
screen.blit(man.right_idle_list[frame_count//13], (man.rect.x, man.rect.y))
pygame.draw.rect(screen, RED, man.rect, 4)
pygame.display.flip()
right = False
left = False
def input_process(key):
global right
global left
global last_action
global man
global frame_count
global is_jump
global neg
global jump_count
#
if is_jump:
if jump_count >= -10: # check if jumping
if jump_count < 0:
neg = -1
man.rect.y -= (jump_count ** 2) * neg * 0.5
jump_count -= 1
else:
neg = 1
is_jump = False
#
if key[pygame.K_RIGHT] and man.rect.right + speed < w:
if left:
frame_count = 0
right = True
left = False
last_action = "right"
man.rect.x += speed
#
if key[pygame.K_LEFT] and man.rect.left + speed > 0:
if right:
frame_count = 0
right = False
left = True
last_action = "left"
man.rect.x -= speed
#
if not is_jump:
if key[pygame.K_UP]:
jump_count = 10
is_jump = True
w = 1728
h = 972
pygame.init()
RED = (255, 0, 0)
images_folder = r'C:\Users\aviro\Desktop\מחשבים\python projects\platform game\images'+'\\'
bg_image = images_folder+"background.png" # setting background image
ground_height = h-143 # the high of the image ground
man = Man(200, ground_height)
man.rect.bottom = ground_height
clock = pygame.time.Clock()
Refresh_Rate = 54
speed = 5
right = False
left = False
frame_count = 0
finish = False
last_action = ""
# jumping ver
is_jump = False
jump_count = 10
neg = 1
#
screen = pygame.display.set_mode((w, h))
bg_image = pygame.image.load(bg_image).convert() # can do convert only after setting surface.
# main loop
while not finish:
if frame_count >= 51:
frame_count = 0
for events in pygame.event.get():
if events.type == pygame.QUIT:
finish = True
key = pygame.key.get_pressed()
input_process(key)
game_redraw()
frame_count += 1
clock.tick(Refresh_Rate)
I'm trying to create a simple shooting-platform game, using pygame module. Everything works fine except the jumping. See in line 88. The player is jumping like an x^2 parabola. I added the *0.5 at the end of the line to make the player jump slower and lower, but when I do it this is what happened.
Before jumping:
After jumping:
Look in the second picture. There's a distance between the floor and the player. When I remove the *0.5 everything works fine. Why?

Pygame: colliding rectangles with other rectangles in the same list

I have a list of 10 drawn rectangles (referenced as cubes in my script) that are affected by gravity. I made a simple collision system for them to stop when they hit the ground. How can I make it so when 2 cubes collide they stop falling like they do with the ground?
import pygame
import time
import random
pygame.init()
clock = pygame.time.Clock()
wnx = 800
wny = 600
black = (0,0,0)
grey = (75,75,75)
white = (255,255,255)
orange = (255,100,30)
wn = pygame.display.set_mode((wnx, wny))
wn.fill(white)
def cube(cx,cy,cw,ch):
pygame.draw.rect(wn, orange, [cx, cy, cw, ch])
def floor(fx,fy,fw,fh):
pygame.draw.rect(wn, grey, [fx, fy, fw, fh])
def main():
floory = 550
number = 30
cubex = [0] * number
cubey = [0] * number
cubew = 10
cubeh = 10
for i in range(len(cubex)):
cubex[i] = (random.randrange(0, 80)*10)
cubey[i] = (random.randrange(2, 5)*10)
gravity = -10
exit = False
while not exit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit = True
for i in range(len(cubex)): #i want to check here if it collides with an other cube
if not (cubey[i] + 10) >= floory:
cubey[i] -= gravity
wn.fill(white)
floor(0,floory,800,50)
for i in range(len(cubex)):
cube(cubex[i], cubey[i], cubew, cubeh)
pygame.display.update()
clock.tick(5)
main()
pygame.quit()
quit()
Use pygame.Rect.colliderect to check if to rectangles are intersecting.
Create an rectangle (pygame.Rect) which defines the next position (area) of the cube:
cubeR = pygame.Rect(cubex[i], cubey[i] + 10, cubew, cubeh)
Find all intersecting rectangles
cl = [j for j in range(len(cubey)) if j != i and cubeR.colliderect(pygame.Rect(cubex[j], cubey[j], cubew, cubeh))]
And don't move (let further "fall") the cube if there is any() collision:
if not any(cl):
# [...]
The check may look like this:
for i in range(len(cubex)):
cubeR = pygame.Rect(cubex[i], cubey[i] + 10, cubew, cubeh)
cisect = [j for j in range(len(cubey)) if j != i and cubeR.colliderect(pygame.Rect(cubex[j], cubey[j], cubew, cubeh))]
if not any(cisect) and not (cubey[i] + 10) >= floory:
cubey[i] -= gravity
Note, since all the cubes are aligned to an 10*10 raster, it is sufficient to check if the origins of the cubes are equal:
for i in range(len(cubex)):
cisect = [j for j in range(len(cubey)) if j != i and cubex[i] == cubex[j] and cubey[i]+10 == cubey[j]]
if not any(cisect) and not (cubey[i] + 10) >= floory:
cubey[i] -= gravity

How do i check for tiles around a specific tile?

I'm making a game.
The game's world is represented by a tilemap. The tiles correspond to values in a 2D array.
I would like to use a special "corner" wall sprite if any three wall tiles make an L shape. That is,
V
## # >##
# ## #
^
The tiles indicated with arrows should be corner tiles.
I have the code to search for the wall but I don't know how to identify which tiles are the corner tiles.
My code is:
import pygame, sys
import Sprites
import random
from pygame.locals import *
pygame.init()
fpsClock = pygame.time.Clock()
cloudx = -200
cloudy = 0
infoObject = pygame.display.Info()
DIRT = 0
GRASS = 1
WATER = 2
COAL = 3
CLOUD = 4
WALL = 5
CWALL = 6
controls = {
DIRT : 49,
GRASS: 50,
WATER: 51,
COAL : 52,
WALL : 53
}
infoObject = pygame.display.Info()
w = infoObject.current_w
h = infoObject.current_h
TILESIZE = 40
MAPWIDTH = 15
MAPHEIGHT = 15
WHITE = (255,255,255)
BLACK = (0,0,0)
resources = [DIRT, GRASS, WATER, COAL]
textures = {
DIRT : pygame.image.load('Sprites/Dirt.png'),
GRASS : pygame.image.load('Sprites/tile130.png'),
WATER : pygame.image.load('Sprites/Water.png'),
COAL : pygame.image.load('Sprites/Coal.png'),
CLOUD : pygame.image.load('Sprites/Cloud.png'),
WALL : pygame.image.load('Sprites/Swall.png'),
CWALL : pygame.image.load('Sprites/Swall.png')
}
playerPos = [0,0]
inventory = {
DIRT : 0,
GRASS : 0,
WATER : 0,
COAL : 0,
WALL : 10,
}
tilemap = [[DIRT for w in range(MAPWIDTH)] for h in range(MAPHEIGHT)]
DISPLAYSURF = pygame.display.set_mode((MAPWIDTH*TILESIZE,MAPHEIGHT*TILESIZE + 50))
pygame.display.set_caption('M I N E C R A F T -- 2D')
pygame.display.set_icon(pygame.image.load('Sprites/player.png'))
PLAYER = pygame.image.load('Sprites/Player.png').convert_alpha()
for rw in range(MAPHEIGHT):
for cl in range(MAPWIDTH):
randomNumber = random.randint(0,15)
if randomNumber == 0:
tile = COAL
elif randomNumber == 1 or randomNumber == 2:
tile = WATER
elif randomNumber >= 3 and randomNumber <=7:
tile = GRASS
else:
tile = DIRT
tilemap[rw][cl] = tile
INVFONT = pygame.font.Font('freesansbold.ttf', 18)
print(tilemap)
while True:
currentTile = tilemap[playerPos[1]][playerPos[0]]
DISPLAYSURF.fill(BLACK)
for event in pygame.event.get():
# print(event)
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
for key in controls:
if (event.key == controls[key]):
if inventory[key] > 0:
inventory[key] -=1
inventory[currentTile] += 1
tilemap[playerPos[1]][playerPos[0]] = key
if(event.key == K_RIGHT) and playerPos[0] < MAPWIDTH - 1:
playerPos[0]+=1
elif(event.key == K_LEFT) and playerPos[0] > 0:
playerPos[0]-=1
elif(event.key == K_DOWN) and playerPos[1] < MAPHEIGHT - 1:
playerPos[1]+=1
elif(event.key == K_UP) and playerPos[1] > 0:
playerPos[1]-=1
if event.key == K_SPACE:
currentTile = tilemap[playerPos[1]][playerPos[0]]
inventory[currentTile] += 1
tilemap[playerPos[1]][playerPos[0]] = DIRT
for row in range(MAPHEIGHT):
for column in range(MAPWIDTH):
DISPLAYSURF.blit(textures[tilemap[row][column]],(column*TILESIZE, row*TILESIZE, TILESIZE, TILESIZE))
DISPLAYSURF.blit(PLAYER,(playerPos[0]*TILESIZE,playerPos[1]*TILESIZE))
DISPLAYSURF.blit(textures[CLOUD].convert_alpha(),(cloudx,cloudy))
cloudx +=1
if cloudx > MAPWIDTH*TILESIZE:
cloudy = random.randint(0, MAPHEIGHT*TILESIZE)
cloudx = -200
placePosition = 10
for item in resources:
DISPLAYSURF.blit(textures[item],(placePosition, MAPHEIGHT*TILESIZE+10))
placePosition+=50
textObj = INVFONT.render(str(inventory[item]), True, WHITE, BLACK)
DISPLAYSURF.blit(textObj,(placePosition, MAPHEIGHT*TILESIZE+20))
placePosition += 50
pygame.display.update()
fpsClock.tick(24)
for x in range(MAPWIDTH):
for y in range(MAPHEIGHT):
if tilemap[x][y] == WALL:
go_left = x > 1
go_right = x < MAPWIDTH - 1
go_up = y > 1
go_down = y < MAPHEIGHT - 1
if go_left:
tilemap[x - 1][y] = CWALL
if go_up:
pass
if go_right:
tilemap[x + 1][y] = WALL
if go_up:
pass
if go_down:
pass
print('WALL')
pygame.display.update()
And here are the links to the sprites:
https://framadrop.org/r/fmReup_rTK#bMSywSUa7nxb1qL/a4FIbns+VaspgE0c/FU+9f1drHI=
https://framadrop.org/r/pBOfrnKcdT#eNEZQ9QjX5Cl6X4gH4UwdIg3eBPnY/L4WcSGYtUR5PE=
https://framadrop.org/r/ZFfTz_Lq9V#2Nd5ba1iE7miyFg8JpPFvwabAkdnHds/GfVkSAQeJaQ=
https://framadrop.org/r/gN9Y748L9G#Z552pPpgjTcSubt9tn74mZ0tT1COv7UCFdkUq2DorAU=
https://framadrop.org/r/d9k4hyCUni#OTza8UbsR8Am/R1PA9MAWkLDPRDBsT1rAHMgr61jusc=
https://framadrop.org/r/1mv777OR6d#pkqwaQrmVRElUPcdEV5K4UhmALsJSYX7z3WtrZXl4TE=
https://framadrop.org/r/CyF-tk7yUb#IFexcePe418JizElZzCJzDENTJPDfz7i1nr+lGns0rU=
https://framadrop.org/r/VzVfAz6bnL#oLHivyHPtTD8+IxliDD4yc+6LS9kpGyEp1HNFGUsBHo=
https://framadrop.org/r/0V0ShMH0uq#PZHdPSQNbgL7QqH2niwdS4HO34ZRMfIlhpvpRqbWwng=
From the comments, I'm not sure what the problem is... let me just show you what I would do so we can discuss further if needed:
for x in range(MAPWIDTH):
for y in range(MAPHEIGHT):
if tilemap[x][y] == WALL:
# there is a wall at indices x and y
# get neighbouring tiles (check for limits)
go_left = x > 1
go_right = x < MAPWIDTH - 1
go_up = y > 1
go_down = y < MAPHEIGHT - 1
if go_left:
# you can use x-1
tilemap[x-1][y] = WALL # set left tile to WALL
if go_up:
# do something in the diagonal with x-1 y-1?
pass
if go_right:
# you can use x+1
tilemap[x+1][y] = WALL # set right tile to WALL
if go_up:
pass # same story
if go_down:
pass # and again
EDIT here is a simple (hence hopefully easy to understand) way of doing this
In order to make the textures clean, I first had to rotate the wall and corner to get all possible configurations (vertical/horizontal for the wall, and all four possibilities for the corner)
VWALL = 5
HWALL = 6
CORNERLD = 7
CORNERRD = 8
CORNERLU = 9
CORNERRU = 10
controls = {
DIRT : 49,
GRASS : 50,
WATER : 51,
COAL : 52,
VWALL : 53,
HWALL : 54,
CORNERLD: 55,
CORNERRD: 56,
CORNERLU: 57,
CORNERRU: 58,
}
tex_wall = pygame.image.load('Sprites/Swall.png')
tex_corner = pygame.image.load('Sprites/Corner.png')
textures = {
DIRT : pygame.image.load('Sprites/Dirt.png'),
GRASS : pygame.image.load('Sprites/tile130.png'),
WATER : pygame.image.load('Sprites/Water.png'),
COAL : pygame.image.load('Sprites/Coal.png'),
CLOUD : pygame.image.load('Sprites/Cloud.png'),
HWALL : pygame.transform.rotate(tex_wall, 90),
VWALL : tex_wall,
CORNERRD: tex_corner,
CORNERLD: pygame.transform.flip(tex_corner, True, False),
CORNERLU: pygame.transform.flip(tex_corner, True, True),
CORNERRU: pygame.transform.flip(tex_corner, False, True),
}
I created a wall dict to quickly check all 6 possibilities for walls with in walls
walls = {
VWALL : None,
HWALL : None,
CORNERLD: None,
CORNERRD: None,
CORNERLU: None,
CORNERRU: None,
}
And then I check the map
for x in range(MAPWIDTH):
for y in range(MAPHEIGHT):
if tilemap[x][y] in walls:
# there is a wall at indices x and y
# get neighbouring tiles (check for limits)
go_left = x > 1
go_right = x < MAPWIDTH - 1
go_up = y > 1
go_down = y < MAPHEIGHT - 1
l_wall = False
r_wall = False
u_wall = False
d_wall = False
if go_left and tilemap[x-1][y] in walls:
# left tile is WALL
l_wall = True
if go_right and tilemap[x+1][y] in walls:
# right tile is WALL
r_wall = True
if go_up and tilemap[x][y-1] in walls:
u_wall = True
if go_down and tilemap[x][y+1] in walls:
d_wall = True
if l_wall and u_wall:
# upper left corner
tilemap[x][y] = CORNERLU
elif l_wall and d_wall:
# down left corner
tilemap[x][y] = CORNERRU
elif r_wall and u_wall:
# upper left corner
tilemap[x][y] = CORNERLD
elif r_wall and d_wall:
# down left corner
tilemap[x][y] = CORNERRD
elif (l_wall or r_wall) and not (u_wall or d_wall):
# tiles in a vertical wall, use VWALL
tilemap[x][y] = VWALL
elif (u_wall or d_wall) and not (l_wall or r_wall):
# tiles in a horizontal wall, use HWALL
tilemap[x][y] = HWALL
And we get
Note that there are random configurations of wall that will not look good, though (T-shapes...) but these would require additional sprites.
The full code I used can be found here
EDIT2 note that you will have to update a few more things to make everything work smoothly (e.g. wall uptake in the inventory)
Also running this check every loop is costly, so you should declare an env_changed boolean to make the test only when a change was made to the environment.
For the inventory, you will need
if currentTile in walls:
inventory[VWALL] += 1
this makes VWALL the default wall in the inventory and the loop takes care of switching it to a proper one for the display.
For the rest, well... it's your game, so I'll let you figure it out ;)
I'd start by generating a set offsets
// 1
//0 2 Neighbour directions
// 3
// 0 1 2 3 Corresponding offsets
dx = [-1, 0, 1, 0]
dy = [0, -1, 0, 1]
Nmax = 4
Now, I can get the n-values of the neighbouring walls, like so:
nwalls = []
for n in range(Nmax): #n is the direction of the neighbour
#Ensure that the neighbour tile, identified as (x+dx[n], y+dy[n])
#is in the map and check to see if it is a wall
if 1<x+dx[n]<WIDTH and 1<y+dy[n]<HEIGHT and tilemap[x+dx[n]][y+dy[n]]==WALL:
nwalls.append(n) #Neighbour was a wall, add its direction to this list
Now, I can convert my list into a tile name:
#nwalls is a list of all the directions pointing to a wall
nwalls.sort() #Sort list, e.g. 3,1,2 -> 1,2,3
nwalls = map(str,nwalls) #Convert from numbers to strings
nwalls = ''.join(nwalls) #Append strings together: ['1','2','3'] -> '123'
if nwalls: #If the string is not empty
nwalls = 'corner_tile_{0}.jpg'.format(nwalls) #Convert string to tile name
Now I just need a bunch of tiles named, e.g.:
corner_tile_01.jpg
corner_tile_013.jpg
Then I can say:
if nwalls: #Only true if this was a corner
Code to display the tile whose name is stored in `nwalls`

Pymunk (chipmunk) - how to turn off physics/collisions temporarily for concrete object

How to turn off collisions for some objects and then again turn it on using pymunk lib in python?
Let me show you the example, based on the code below. I want all red balls to go through first border of lines and stop on the lower border. Blue balls should still collide with upper border.
What needs to be changed in the code?
import pygame
from pygame.locals import *
from pygame.color import *
import pymunk as pm
from pymunk import Vec2d
import math, sys, random
def to_pygame(p):
"""Small hack to convert pymunk to pygame coordinates"""
return int(p.x), int(-p.y+600)
pygame.init()
screen = pygame.display.set_mode((600, 600))
clock = pygame.time.Clock()
running = True
### Physics stuff
space = pm.Space()
space.gravity = (0.0, -900.0)
## Balls
balls = []
### walls
static_body = pm.Body()
static_lines = [pm.Segment(static_body, (111.0, 280.0), (407.0, 246.0), 0.0),
pm.Segment(static_body, (407.0, 246.0), (407.0, 343.0), 0.0),
pm.Segment(static_body, (111.0, 420.0), (407.0, 386.0), 0.0),
pm.Segment(static_body, (407.0, 386.0), (407.0, 493.0), 0.0)]
for line in static_lines:
line.elasticity = 0.95
space.add(static_lines)
ticks_to_next_ball = 10
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
ticks_to_next_ball -= 1
if ticks_to_next_ball <= 0:
ticks_to_next_ball = 100
mass = 10
radius = random.randint(10,40)
inertia = pm.moment_for_circle(mass, 0, radius, (0,0))
body = pm.Body(mass, inertia)
x = random.randint(115,350)
body.position = x, 600
shape = pm.Circle(body, radius, (0,0))
shape.elasticity = 0.95
space.add(body, shape)
balls.append(shape)
### Clear screen
screen.fill(THECOLORS["white"])
### Draw stuff
balls_to_remove = []
for ball in balls:
if ball.body.position.y < 200: balls_to_remove.append(ball)
p = to_pygame(ball.body.position)
if ball.radius > 25:
color = THECOLORS["blue"]
else:
color = THECOLORS["red"]
pygame.draw.circle(screen, color, p, int(ball.radius), 2)
for ball in balls_to_remove:
space.remove(ball, ball.body)
balls.remove(ball)
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 = to_pygame(pv1)
p2 = to_pygame(pv2)
pygame.draw.lines(screen, THECOLORS["lightgray"], False, [p1,p2])
### Update physics
dt = 1.0/60.0
for x in range(1):
space.step(dt)
### Flip screen
pygame.display.flip()
clock.tick(50)
pygame.display.set_caption("fps: " + str(clock.get_fps()))
Chipmunk has a few options filtering collisions:
http://chipmunk-physics.net/release/ChipmunkLatest-Docs/#cpShape-Filtering
It sounds like you just need to use a layers bitmask though.
ex:
# This layer bit is for balls colliding with other balls
# I'm only guessing that you want this though.
ball_layer = 1
# This layer bit is for things that collide with red balls only.
red_ball_layer = 2
# This layer bit is for things that collide with blue balls only.
blue_ball_layer = 4
# Bitwise OR the layer bits together
red_ball_shape.layers = ball_layer | red_ball_layer
blue_ball_shape.layers = ball_layer | blue_ball_layer
# Lower border should collide with red only
upper_border_shape.layers = red_ball_layer
#Upper border with blue balls only
lower_border_shape.layers = blue_ball_layer
I've never actually used Pymunk personally, but I'm guessing that it exposes the Chipmunk layers property simply as .layers
In Pymunk you can use the ShapeFilter class to set the categories (layers) with which an object can collide. I put the upper and lower lines into the categories 1 and 2 and then set the masks of the balls so that they ignore these layers. You need to understand how bitmasking works.
Here's the complete example based on the code in the original question (press left and right mouse button to spawn the balls).
import sys
import pygame as pg
from pygame.color import THECOLORS
import pymunk as pm
def to_pygame(p):
"""Small hack to convert pymunk to pygame coordinates"""
return int(p[0]), int(-p[1]+600)
pg.init()
screen = pg.display.set_mode((600, 600))
clock = pg.time.Clock()
space = pm.Space()
space.gravity = (0.0, -900.0)
# Walls
static_body = space.static_body
static_lines = [
pm.Segment(static_body, (111.0, 280.0), (407.0, 246.0), 0.0),
pm.Segment(static_body, (407.0, 246.0), (407.0, 343.0), 0.0),
pm.Segment(static_body, (111.0, 420.0), (407.0, 386.0), 0.0),
pm.Segment(static_body, (407.0, 386.0), (407.0, 493.0), 0.0),
]
for idx, line in enumerate(static_lines):
line.elasticity = 0.95
if idx < 2: # Lower lines.
# The lower lines are in category 2, in binary 0b10.
line.filter = pm.ShapeFilter(categories=2)
else: # Upper lines.
# The upper lines are in category 1, in binary 0b1.
line.filter = pm.ShapeFilter(categories=1)
space.add(static_lines)
balls = []
running = True
while running:
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
elif event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE:
running = False
if event.type == pg.MOUSEBUTTONDOWN:
radius = 15 if event.button == 1 else 30
mass = 10
inertia = pm.moment_for_circle(mass, 0, radius, (0,0))
body = pm.Body(mass, inertia)
body.position = to_pygame(event.pos)
shape = pm.Circle(body, radius, (0,0))
shape.elasticity = 0.95
if shape.radius > 25:
# bin(pm.ShapeFilter.ALL_MASKS ^ 1) is '0b11111111111111111111111111111110'
# That means all categories are checked for collisions except
# bit 1 (the upper lines) which are ignored.
shape.filter = pm.ShapeFilter(mask=pm.ShapeFilter.ALL_MASKS ^ 1)
else:
# Ignores category bin(2), '0b11111111111111111111111111111101'
# All categories are checked for collisions except bit 2 (the lower lines).
shape.filter = pm.ShapeFilter(mask=pm.ShapeFilter.ALL_MASKS ^ 2)
space.add(body, shape)
balls.append(shape)
screen.fill(THECOLORS["white"])
balls_to_remove = []
for ball in balls:
if ball.body.position.y < 100:
balls_to_remove.append(ball)
p = to_pygame(ball.body.position)
if ball.radius > 25:
color = THECOLORS["red"]
else:
color = THECOLORS["blue"]
pg.draw.circle(screen, color, p, int(ball.radius), 2)
for ball in balls_to_remove:
space.remove(ball, ball.body)
balls.remove(ball)
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 = to_pygame(pv1)
p2 = to_pygame(pv2)
pg.draw.lines(screen, THECOLORS["gray29"], False, [p1, p2])
# Update physics.
dt = 1.0/60.0
for x in range(1):
space.step(dt)
pg.display.flip()
clock.tick(50)
pg.quit()
sys.exit()

Categories