I am new to Pygame and I am in the experimentation phase.
What I have here is a simple display surface onto which I have rendered a few overlapping Rectangles with different colors with the same width and height (96 by 144) to simulate playing cards.
I have written some code in order to be able to highlight a card with the color Yellow when I click on it. If you run the code, you'll notice that when I click on the visible part of one of the cards, all the other cards that are behind it, as well as all of those that are overlapping it are being highlighted. I cannot figure out, what should I do to click the visible part of the card and only that card to highlight.
import pygame, sys
class Card(pygame.sprite.Sprite):
def __init__(self, color):
super().__init__()
self.color = color
self.is_highlight = False
def show_card(self, x, y):
self.rect = pygame.rect.Rect(x, y, 96, 144)
pygame.draw.rect(display_surf, self.color, self.rect)
def highlight_card(self):
pos = pygame.mouse.get_pos()
if self.rect.collidepoint(pos):
self.is_highlight = True
else:
self.is_highlight = False
def update(self):
if self.is_highlight:
pygame.draw.rect(display_surf, 'Yellow', self.rect.inflate(5, 5), width=3, border_radius=5)
class Deck(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.card_deck = []
def build_deck(self):
self.card_deck.append(Card('Green'))
self.card_deck.append(Card('Red'))
self.card_deck.append(Card('Blue'))
self.card_deck.append(Card('brown'))
self.card_deck.append(Card('cadetblue'))
self.card_deck.append(Card('cornsilk'))
self.card_deck.append(Card('darkolivegreen'))
self.card_deck.append(Card('darkorchid'))
self.card_deck.append(Card('darksalmon'))
return self.card_deck
pygame.init()
WINDOW_WIDTH = 1280
WINDOW_HEIGHT = 720
display_surf = pygame.display.set_mode((WINDOW_WIDTH,WINDOW_HEIGHT))
pygame.display.set_caption('Simple Card Game')
clock = pygame.time.Clock()
card_deck = Deck().build_deck()
while True:
display_surf.fill((0, 150,0))
distance = 20
for card in card_deck:
card.show_card(distance, 20)
distance += 20
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
for card in card_deck:
card.highlight_card()
for card in card_deck:
card.update()
pygame.display.update()
So I think I have solved my own question and wish to share the solution.
In the show_card method I have added a new scaled_rect which represents only the visible part of the colored card with a width of only 20, while I draw on the display surface the color using the original full sized rectangle:
def show_card(self, x, y):
self.rect = pygame.rect.Rect(x, y, 96, 144)
self.scaled_rect = pygame.rect.Rect((x, y), (20, 144))
pygame.draw.rect(display_surf, self.color, self.rect)
In the highlight_card method I have made only the visible part of the color interactable with the mouse click, by using the new scaled_rect:
def highlight_card(self):
pos = pygame.mouse.get_pos()
if self.scaled_rect.collidepoint(pos):
self.is_highlight = True
else:
self.is_highlight = False
In the end, in the update method that draws the yellow colored highlight, I use the original full sized rectangle:
def update(self):
if self.is_highlight:
pygame.draw.rect(display_surf, 'Yellow', self.rect.inflate(5, 5), width=3, border_radius=5)
This question already has answers here:
How can I make a sprite move when key is held down
(6 answers)
Closed 1 year ago.
Hi am new fairly new to programming so I tried to learn by making my hands dirty. I tried making a simple box game program all was working fine until I made my player Class which inherit from the base class of Box_User for some reasons my box(player class object) is no longer moving I tried to see what it prints and it seems like none of the keys work when I press them
Can anyone explain what happened?
import pygame
pygame.init()
# Classes
class Window():
def __init__(self, width, height):
self.width = width
self.height = height
self.window_init() # this functions is to get the window up
def window_init(self):
self.window = pygame.display.set_mode((self.width, self.height)) # this is the main window
self.background = pygame.Surface((self.window.get_size())) # this one is named background but should be use like the main window
self.background.fill((255, 255, 255))
#staticmethod
def draw_objects_to_Screen():
win.window.blit(win.background, (0,0))
win.window.blit(box.box, (box.pos_x - scroll[0], box.pos_y + scroll[1])) # the scroll is used to make it look like its moving
win.window.blit(box2.box, (box2.pos_x - scroll[0], box2.pos_y + scroll[1]))
pygame.display.update()
class Box_User():
jump = 10
jump_status = False
def __init__(self, x, y, height, width):
self.pos_x = x
self.pos_y = y
self.box_height = height
self.box_width = width
self.box = pygame.Surface((self.box_height, self.box_width))
self.color = (0, 20, 0)
self.draw_box()
def draw_box(self):
pygame.draw.rect(self.box, self.color, pygame.Rect(self.pos_x, self.pos_y, self.box_height, self.box_width))
#staticmethod
def _jump():
if Box_User.jump >= -10:
box.pos_y -= (Box_User.jump * abs(Box_User.jump)) * 0.3
scroll[1] += (Box_User.jump * abs(Box_User.jump)) * 0.3
Box_User.jump -= 1
else:
Box_User.jump = 10
Box_User.jump_status = False
class Player(Box_User):
key_pressed = pygame.key.get_pressed()
def __init__(self, x, y, height, width):
super().__init__(x, y, height, width)
# self.pos_x = x
#self.pos_y = y
def movements(self):
if self.key_pressed[pygame.K_a]:
self.pos_x -= 5
if self.key_pressed[pygame.K_d]:
self.pos_x += 5
# place here things that you dont want to move while the box is jumping
if not self.jump_status:
if self.key_pressed[pygame.K_w]:
self.jump_status = True
else:
self._jump() # the box jumps here
class Auto_Box(Box_User):
def __init__(self, x, y, height, width):
super().__init__(x, y, height, width)
pass
# Variables
# window
win = Window(700, 500)
clock = pygame.time.Clock()
FPS = 60
# boxes
box = Player(30, 200, 64, 64)
box2 = Box_User(300, 200, 100, 120)
# The Scroll which controls the things when the box is moving
scroll = [0, 0]
# Functions
# Main Loop
def main():
run = True
while run:
clock.tick(FPS)
# value of the scroll is updated here
scroll[0] += (box.pos_x - scroll[0]-250)
#print("coordinate are")
#print(scroll)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
box.movements()
print(box.pos_x, box.pos_y)
Window.draw_objects_to_Screen()
if __name__ == "__main__":
main()
pygame.key.get_pressed() returns a list of booleans that represent the state of the keyboard when you call the function.
If you continually check that list, no keys will ever change state. It's just a list of Trues and Falses.
You need to reset the values of key_pressed in the loop, updating it with fresh values from pygame.key.get_pressed().
This should do the trick:
def movements(self):
self.key_pressed = pygame.key.get_pressed()
if self.key_pressed[pygame.K_a]:
self.pos_x -= 5
...
The key_pressed class variables is only initialized once. It is never changed. Therefore the pressed keys are not detected.
pygame.key.get_pressed() returns a iterable with the current state of all keyboard buttons. You must get the states of the keys in every frame:
class Player(Box_User):
def __init__(self, x, y, height, width):
super().__init__(x, y, height, width)
def movements(self):
# get the current states of the keys
self.key_pressed = pygame.key.get_pressed()
if self.key_pressed[pygame.K_a]:
self.pos_x -= 5
if self.key_pressed[pygame.K_d]:
self.pos_x += 5
# place here things that you dont want to move while the box is jumping
if not self.jump_status:
if self.key_pressed[pygame.K_w]:
self.jump_status = True
else:
self._jump() # the box jumps here
so I have a scroll bar in my game what Im trying to do is make it so if my mouse is over the bar1 button and we are on the moving_spot of the bar1 button then we can move it up and down on its y axis
how can I move the bar up and down and if its colliding with with any of the volume buttons I can change the volume of my background music either 0.1 or 0.2 or 0.3 so it controls my game volumepygame.mixer.music.set_volume(0.3)
my problem is im not sure how I could get this started I have everything in place but not sure where to start ***how can I move the bar with my mouse on the moving_spot on its y values only and if the bar1 is over and of the volume1 2 or 3 4 buttons then it should play the volume at defferent level
im not sure how to approach this problem any help is appreciated I just need a way to adjust my music of my game if the player moves the bar up or down
while run:
# Making game run with fps
clock.tick(fps)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
# this is our bar1 the gray part that we will be able to move
if bar1.isOver(pos):
bar1.y = pos
print("WORKING{")
here are my buttons and positions places the move_spot is where the bar1 can only move up and down
the bar1 is the bar that the player can control to control the volume
and also the volume 1 2 3 4 are where the defferent volume of our background music will be set
move_spot = button((colors),720,125,25,260, '')
bar1 = button((colors),715,125,30,60, '')
volume1 = button((colors2),715,145,30,60, '')
volume2 = button((colors2),715,210,30,60, '')
volume3 = button((colors2),715,280,30,60, '')
volume4 = button((colors2),715,350,30,60, '')
buttons = [bar1,move_spot,volume1,volume2,volume3,volume4]
this is my buttons class
# our buttons
class button():
def __init__(self, color, x,y,width,height, text=''):
self.color = color
self.x = x
self.y = y
self.width = width
self.height = height
self.text = text
self.over = False
def draw(self,window,outline=None):
#Call this method to draw the button on the screen
if outline:
pygame.draw.rect(window, outline, (self.x-2,self.y-2,self.width+4,self.height+4),0)
pygame.draw.rect(window, self.color, (self.x,self.y,self.width,self.height),0)
if self.text != '':
font = pygame.font.SysFont('image/abya.ttf', 60)
text = font.render(self.text, 1, (255,255,255))
window.blit(text, (self.x + (self.width/2 - text.get_width()/2), self.y + (self.height/2 - text.get_height()/2)))
def isOver(self, pos):
#Pos is the mouse position or a tuple of (x,y) coordinates
if pos[0] > self.x and pos[0] < self.x + self.width:
if pos[1] > self.y and pos[1] < self.y + self.height:
return True
return False
def playSoundIfMouseIsOver(self, pos, sound):
if self.isOver(pos):
if not self.over:
click.play()
self.over = True
else:
self.over = False
here a minimal code you can run and test with this bar image
heres the background music music
import pygame
pygame.init()
window = pygame.display.set_mode((800,800))
# our buttons
class button():
def __init__(self, color, x,y,width,height, text=''):
self.color = color
self.x = x
self.y = y
self.width = width
self.height = height
self.text = text
self.over = False
def draw(self,window,outline=None):
#Call this method to draw the button on the screen
if outline:
pygame.draw.rect(window, outline, (self.x-2,self.y-2,self.width+4,self.height+4),0)
pygame.draw.rect(window, self.color, (self.x,self.y,self.width,self.height),0)
if self.text != '':
font = pygame.font.SysFont('freesansbold.ttf', 60)
text = font.render(self.text, 1, (255,255,255))
window.blit(text, (self.x + (self.width/2 - text.get_width()/2), self.y + (self.height/2 - text.get_height()/2)))
def isOver(self, pos):
#Pos is the mouse position or a tuple of (x,y) coordinates
if pos[0] > self.x and pos[0] < self.x + self.width:
if pos[1] > self.y and pos[1] < self.y + self.height:
return True
return False
def playSoundIfMouseIsOver(self, pos, sound):
if self.isOver(pos):
if not self.over:
click.play()
self.over = True
else:
self.over = False
colors = 0,23,56
colors2 = 0,123,56
bar11 = pygame.image.load("bar.png").convert_alpha()
move_spot = button((colors),720,125,25,260, '')
bar1 = button((colors),715,125,30,60, '')
volume1 = button((colors2),715,145,30,60, '')
volume2 = button((colors2),715,210,30,60, '')
volume3 = button((colors2),715,280,30,60, '')
volume4 = button((colors2),715,350,30,60, '')
buttons = [bar1,move_spot,volume1,volume2,volume3,volume4]
# fos
fps = 60
clock = pygame.time.Clock()
# redraw
def redraw():
window.fill((40,100,200))
for button in buttons:
button.draw(window)
window.blit(bar11,(bar1.x,bar1.y))
# main loop
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
redraw()
pygame.display.update()
pygame.quit()
As a general rule of thumb, you want to delegate all the heavy lifting to classes, and leave the "good" stuff to the mainloop of your program. I have created a simple class here, which takes some inputs (width, height, number of slider options), and will take care of all the drawing, positioning, etc. for you. It also has an option of self.chosen, which tells you which option is picked. I then used this to set the volume outputted by the mixer, based on which option is chosen, in the update() function:
class VolumeBar(pygame.sprite.Sprite):
def __init__(self, options, width, height):
# Store these useful variables to the class
self.options = options
self.width = width
self.height = height
# The slider
self.slider = pygame.image.load('slider.png')
self.slider_rect = self.slider.get_rect()
# The background "green" rectangles, mostly for decoration
self.back = []
objectHeight = (height-options*6)/(options-1)
self.backHeight = objectHeight
for index in range(options-1):
self.back.append(pygame.Rect((0, rint((6*index+6)+index*objectHeight)), (width, rint(objectHeight))))
# The foreground "blue" rectangles, mostly for decoration
self.fore = []
for index in range(options):
self.fore.append(pygame.Rect((0, rint(index*(objectHeight+6))), (width, 6)))
# Get list of possible "snaps", which the slider can be dragged to
self.snaps = []
for index in range(options):
self.snaps.append((width/2, 3+(index*(objectHeight+6))))
# A simple variable to tell you which option has been chosen
self.chosen = 0
# Generate the image surface, then render the bar to it
self.image = pygame.Surface((width, height))
self.rect = self.image.get_rect()
self.render()
self.focus = False
def render(self):
self.image.fill([255, 255, 255])
for back in self.back:
pygame.draw.rect(self.image, [0, 192, 0], back)
for fore in self.fore:
pygame.draw.rect(self.image, [0, 0, 0], fore)
self.image.blit(self.slider, (rint((self.width-self.slider_rect.width)/2),
rint(self.snaps[self.chosen][1]-(self.slider_rect.height/2))))
def draw(self, surface):
surface.blit(self.image, self.rect.topleft)
def mouseDown(self, pos):
if self.rect.collidepoint(pos):
self.focus = True
return True
return False
def mouseUp(self, pos):
if not self.focus:
return
self.focus = False
if not self.rect.collidepoint(pos):
return
pos = list(pos)
# We've made sure the mouse started in our widget (self.focus), and ended in our widget (collidepoint)
# So there is no reason to care about the exact position of the mouse, only where it is relative
# to this widget
pos[0] -= self.rect.x
pos[1] -= self.rect.y
# Calculating max distance from a given selection, then comparing that to mouse pos
dis = self.backHeight/2 + 3
for index, snap in enumerate(self.snaps):
if abs(snap[1]-pos[1]) <= dis:
self.chosen = index
break
self.render()
def update(self):
pygame.mixer.music.set_volume((self.options-self.chosen)*0.1)
Most of the __init__ function is spent calculating positions for each of the green and black rectangles, which are drawn in render() and displayed in draw(). The other functions are there for the mouse input, the first checks if the mouseDown happened on said button, and if it did, it sets self.focus to True, so that the mouseUp handler knows that it should change the slider position.
All of this works together to make a working VolumeBar. Below is an example of how it would be used in a mainloop:
import pygame
pygame.init()
rint = lambda x: int(round(x, 0)) # Rounds to the nearest integer. Very useful.
class VolumeBar(pygame.sprite.Sprite):
def __init__(self, options, width, height):
# Store these useful variables to the class
self.options = options
self.width = width
self.height = height
# The slider
self.slider = pygame.image.load('slider.png')
self.slider_rect = self.slider.get_rect()
# The background "green" rectangles, mostly for decoration
self.back = []
objectHeight = (height-options*6)/(options-1)
self.backHeight = objectHeight
for index in range(options-1):
self.back.append(pygame.Rect((0, rint((6*index+6)+index*objectHeight)), (width, rint(objectHeight))))
# The foreground "blue" rectangles, mostly for decoration
self.fore = []
for index in range(options):
self.fore.append(pygame.Rect((0, rint(index*(objectHeight+6))), (width, 6)))
# Get list of possible "snaps", which the slider can be dragged to
self.snaps = []
for index in range(options):
self.snaps.append((width/2, 3+(index*(objectHeight+6))))
# A simple variable to tell you which option has been chosen
self.chosen = 0
# Generate the image surface, then render the bar to it
self.image = pygame.Surface((width, height))
self.rect = self.image.get_rect()
self.render()
self.focus = False
def render(self):
self.image.fill([255, 255, 255])
for back in self.back:
pygame.draw.rect(self.image, [0, 192, 0], back)
for fore in self.fore:
pygame.draw.rect(self.image, [0, 0, 0], fore)
self.image.blit(self.slider, (rint((self.width-self.slider_rect.width)/2),
rint(self.snaps[self.chosen][1]-(self.slider_rect.height/2))))
def draw(self, surface):
surface.blit(self.image, self.rect.topleft)
def mouseDown(self, pos):
if self.rect.collidepoint(pos):
self.focus = True
return True
return False
def mouseUp(self, pos):
if not self.focus:
return
self.focus = False
if not self.rect.collidepoint(pos):
return
pos = list(pos)
# We've made sure the mouse started in our widget (self.focus), and ended in our widget (collidepoint)
# So there is no reason to care about the exact position of the mouse, only where it is relative
# to this widget
pos[0] -= self.rect.x
pos[1] -= self.rect.y
# Calculating max distance from a given selection, then comparing that to mouse pos
dis = self.backHeight/2 + 3
for index, snap in enumerate(self.snaps):
if abs(snap[1]-pos[1]) <= dis:
self.chosen = index
break
self.render()
def update(self):
pygame.mixer.music.set_volume((self.options-self.chosen)*0.1)
screen = pygame.display.set_mode([500, 500])
test = VolumeBar(10, 30, 300)
test.rect.x = 50
test.rect.y = 50
clock = pygame.time.Clock()
game = True
while game:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
game = False
if event.type == pygame.MOUSEBUTTONDOWN:
test.mouseDown(pygame.mouse.get_pos())
if event.type == pygame.MOUSEBUTTONUP:
test.mouseUp(pygame.mouse.get_pos())
if not game:
break
screen.fill([255, 255, 255])
test.update()
test.draw(screen)
pygame.display.update()
clock.tick(60)
The final product:
https://i.gyazo.com/f6c2b5ede828f7715e5fd53a65c32c13.mp4
As long as your mouseDown happened on this widget, mouseUp will determine where the slider ends up. Thusly, you can click, drag, or tap the slider anywhere on it, and the slider will go to the correct position.
Accessing the position of the slider is quite simple, just look at self.chosen. It defaults to 0 (Because it was set to that in the __init__) function, which is at the very top.
I want to make a circular button in pygame.
I had made a class for the rectangle button. (below)
But I want to make it as a circle (and also other polygon).
I know there is no get_cir in Surface and it even doesn't work with it.
So how could I make it?
import pygame
class Button():
def __init__(self, text, x, y, width, height, normal_color, hovered_color,
label_color, font_type, font_size, command = None):
self.text = text
self.normal_color = normal_color
self.hovered_color = hovered_color
self.label_color = label_color
self.font_type = font_type
self.font_size = font_size
self.command = command
self.image_normal = pygame.Surface((width, height))
self.image_normal.fill(self.normal_color)
self.image_hovered = pygame.Surface((width, height))
self.image_hovered.fill(self.hovered_color)
self.image = self.image_normal
self.rect = self.image.get_rect()
font = pygame.font.SysFont(self.font_type, self.font_size)
text_image = font.render(text, True, self.label_color)
text_rect = text_image.get_rect(center = self.rect.center)
self.image_normal.blit(text_image, text_rect)
self.image_hovered.blit(text_image, text_rect)
self.rect.topleft = (x, y)
self.hovered = False
def update(self):
if self.hovered:
self.image = self.image_hovered
else:
self.image = self.image_normal
def draw(self, surface):
surface.blit(self.image, self.rect)
def handle_event(self, event):
if event.type == pygame.MOUSEMOTION:
self.hovered = self.rect.collidepoint(event.pos)
elif event.type == pygame.MOUSEBUTTONDOWN:
if self.hovered == True:
self.command()
You can keep the (fast and efficient) rectangular collision detection, and when that indicates a click, then check the event's (x,y) position distance from the centre-point of the circle.
The distance formula gives us a function:
def lineLength( pos1, pos2 ):
"""Return the distance between the points (pos1) and (pos2)"""
x1,y1 = pos1
x2,y2 = pos2
# length = sqrt((x2 - x1)^2 + (y2 - y1)^2)
x_squared = ( x2 - x1 ) * ( x2 - x1 )
y_squared = ( y2 - y1 ) * ( y2 - y1 )
length = math.sqrt( x_squared + y_squared )
return length
So then add an extra check to work out the distance between the centre of your button, and the mouse-click-point. This should be less than the button's radius for the hover/click to be on the button:
def handle_event( self, event ):
if event.type == pygame.MOUSEMOTION:
self.hovered = False
if ( self.rect.collidepoint( event.pos ) ):
# check click is over the circular button ~
radius = self.width / 2
click_distance = lineLength( self.rect.center, event.pos )
if ( click_distance <= radius ):
self.hovered = True
There is no problem to draw circle or use image with circle. The only problem is to check mouse collision with circle object.
There is sprite.collide_circle which uses circle areas to check collision between two sprites so you would need to create sprite with mouse position and check collision with button's sprite.
OR you can use circle definition
x**2 + y**2 =< R
so calculate distance between mouse and circle's center and it has to be equal or smaller then radius.
You can use even pygame.math.Vector2.distance_to() for this
For other figures the only idea is to use sprite.collide_mask which uses black&white bitmaps to check collisions between sprites. But it would need to create bitmap with filled figure and it can make problem if you want to draw polygon instead of use bitmap with polygon.
I am using Pymunk to generate videos of balls bouncing in a frame. To render the frames, I run:
import pygame
from pymunk.pygame_util import DrawOptions
screen = pygame.display.set_mode((500, 500))
draw_options = DrawOptions(screen)
# define `space` with six balls
# ...
space.debug_draw(draw_options)
which gives the following output:
As we can see, it draws a black line going from the center to the edge of the ball; I suppose it is to indicate the rotation of each ball.
I would like NOT to have this line drawn: i.e. explicitly pass a flag to prevent pygame from drawing it, or specify the color of this line to be the same as the rest of the ball.
Would anyone know how to do that?
The debug_draw method should really be used mainly for debugging. I usually create sprite subclasses for my physics objects and give them a Pymunk body and a shape as attributes. Then I can update the self.rect in the update method by setting the coordinates to the coords of the body and also use it to rotate the image.
import math
import random
import pygame as pg
import pymunk as pm
from pymunk import Vec2d
def flipy(p):
"""Convert chipmunk physics to pygame coordinates."""
return Vec2d(p[0], -p[1]+800)
class Entity(pg.sprite.Sprite):
def __init__(self, pos, space, radius, mass=1):
super().__init__()
# The surface is a bit bigger, so that the circle fits better.
self.orig_image = pg.Surface((radius*2+2, radius*2+2), pg.SRCALPHA)
self.image = self.orig_image
# Draw a circle onto the image.
pg.draw.circle(
self.image,
pg.Color(random.randrange(256),
random.randrange(256),
random.randrange(256)),
(radius+1, radius+1), # +1 looks a bit better.
radius)
self.rect = self.image.get_rect(topleft=pos)
# Create a Pymunk body and a shape and add them to the space.
moment = pm.moment_for_circle(mass, radius, radius)
self.body = pm.Body(mass, moment)
self.shape = pm.Circle(self.body, radius)
self.shape.friction = .1
self.shape.elasticity = .99
self.body.position = pos
self.space = space
self.space.add(self.body, self.shape)
def update(self):
# Update the rect because it's used to blit the image.
self.rect.center = flipy(self.body.position)
# Use the body's angle to rotate the image.
self.image = pg.transform.rotozoom(self.orig_image, math.degrees(self.body.angle), 1)
self.rect = self.image.get_rect(center=self.rect.center)
if self.rect.left < 0 or self.rect.right > 1280 or self.rect.y > 790:
self.space.remove(self.body, self.shape)
self.kill()
class Game:
def __init__(self):
pg.init()
self.screen = pg.display.set_mode((1280, 800))
self.done = False
self.clock = pg.time.Clock()
# Pymunk stuff
self.space = pm.Space()
self.space.gravity = Vec2d(0.0, -900.0)
self.space.damping = .9
self.static_lines = [
pm.Segment(self.space.static_body, flipy((60.0, 780.0)), flipy((650.0, 780.0)), .0),
pm.Segment(self.space.static_body, flipy((650.0, 780.0)), flipy((1218.0, 660.0)), .0)
]
for lin in self.static_lines:
lin.friction = 0.2
lin.elasticity = 0.99
self.space.add(self.static_lines)
self.all_sprites = pg.sprite.Group()
def run(self):
while not self.done:
self.dt = self.clock.tick(60) / 1000
self.handle_events()
self.run_logic()
self.draw()
self.current_fps = self.clock.get_fps()
pg.quit()
def handle_events(self):
for event in pg.event.get():
if event.type == pg.QUIT:
self.done = True
elif event.type == pg.MOUSEBUTTONDOWN:
if event.button == 1: # Left mouse button.
# Spawn an entity.
radius = random.randrange(20, 50)
self.all_sprites.add(Entity(flipy(pg.mouse.get_pos()), self.space, radius))
if pg.mouse.get_pressed()[2]: # Right mouse button.
radius = random.randrange(20, 50)
self.all_sprites.add(Entity(flipy(pg.mouse.get_pos()), self.space, radius))
def run_logic(self):
self.space.step(1/60)
self.all_sprites.update()
def draw(self):
self.screen.fill(pg.Color(140, 120, 110))
self.all_sprites.draw(self.screen) # Draw the images of all sprites.
# Draw the static lines.
for line in self.static_lines:
body = line.body
p1 = flipy(body.position + line.a.rotated(body.angle))
p2 = flipy(body.position + line.b.rotated(body.angle))
pg.draw.lines(self.screen, pg.Color('lightgray'), False, (p1, p2), 5)
pg.display.flip()
if __name__ == '__main__':
Game().run()
I also tried to find a way to hide these direction/rotation lines that debug_draw creates and found out that the lines are colored in the draw_options.shape_outline_color and the circles use the draw_options.shape_dynamic_color by default. So, when you set
draw_options.shape_outline_color = draw_options.shape_dynamic_color
the circles are completely blue. However, if you assign a specific color to each shape, it will still use the blue color for the lines.
And with the draw_options.flags attribute, I could only turn off the collision points or the shapes completely but not the lines:
# This is how you can turn off the collision points.
draw_options.flags ^= draw_options.DRAW_COLLISION_POINTS
# Stops drawing the constraints.
draw_options.flags ^= draw_options.DRAW_CONSTRAINTS
# Stops drawing all shapes.
draw_options.flags ^= draw_options.DRAW_SHAPES