My screen glitches out when I try to run my subprogram code - python

I'm working on a subprogram code that will make this happy face bounce around the screen and turn different colours. For some reason, the screen turns into that black glitchy screen and when I press exit at the top the face shows for a quick second before the program shuts down. I can't figure out why this is, here is my code and I've included a picture of what happens at first when I run it:
""" Program to show a very basic function
Most of the program is exactly the same as other programs we have done
The main difference is the grouping of code into a function called
drawHappy() to draw a few shapes together
In the main loop we "call" this function whenever we want to draw this
group of shapes
"""
# import the necessary modules
import pygame
import sys
import math
import random
from random import randint
# initialize pygame
pygame.init()
# set the size for the surface (screen)
# note this screen is resizable by the user
screen = pygame.display.set_mode((800, 600), pygame.RESIZABLE)
# set the caption for the screen
pygame.display.set_caption("Happy Face")
#screen width and height
screenW = screen.get_width()
screenH = screen.get_height()
# define colours you will be using
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
BLACK = (0, 0, 0)
YELLOW = (255, 255, 0)
# funtion to draw a the "happy face"
# it has 4 parameters passed to it xPos, yPos, radius, and colour
# notice all the shapes are drawn "relative" to the xPos and yPos and the radius
def drawHappy(xPos,yPos,r,colour):
pygame.draw.circle(screen,colour,(xPos,yPos),r,1)
eyeRadius = int(1/6*r)
eyeX = int(xPos-1/3*r)
eyeY = int(yPos- 1/3*r)
pygame.draw.circle(screen,colour,(eyeX,eyeY),eyeRadius,1)
eyeX = int(xPos + 1/3*r)
pygame.draw.circle(screen,colour,(eyeX,eyeY),eyeRadius,1)
wMouth = 1.5*r
xMouth = xPos - 3/4*r
yMouth = yPos - 3/4*r
pygame.draw.arc(screen,colour,(xMouth,yMouth,wMouth,wMouth),math.pi,2*math.pi,1)
randomR = randint(1,300)
r = randomR
randomX = randint(r, 800-r)
randomY = randint(r, 600-r)
dx = 0
dy = 0
x = 100
y = 100
speed = 3
x2 = randomX
y2 = randomY
dx2 = speed
dy2 = -speed
colour_list = [YELLOW, BLACK, BLUE, RED, GREEN]
randomcolour = random.choice(colour_list)
colour = RED
# set up clock to control frames per second
clock = pygame.time.Clock()
FPS = 120
# set main loop to True so it will run
main = True
# main loop
while main:
for event in pygame.event.get(): # check for any events (i.e key press, mouse click etc.)
if event.type == pygame.QUIT: # check to see if it was "x" at top right of screen
main = False # set the "main" variable to False to exit while loop
clock.tick(FPS)
screen.fill(WHITE)
oldx = x
oldy = y
x += dx
y += dy
if x >= 800-r or x <= 0+r:
x = oldx
if y >= 600-r or y <= 0+r:
y = oldy
x2 += dx2
y2 += dy2
if x >= 800-r or x <= 0+r:
dx2 = -dx2
randomcolour = random.choice(colour_list)
colour = randomcolour
if y2 >= 600-r or y2 <= 0+r:
dy2 = -dy2
randomcolour = random.choice(colour_list)
colour = randomcolour
# "call" the function "drawHappy()" to draw the happy face
# this is where we would normally do a pygame.draw or a screen.blit()
# we are "passing" the function 4 values to use(x,y,radius, colour)
# it will use these to know where to draw the happy face
drawHappy(x2,y2,r,colour)
pygame.display.flip()
# quit pygame and exit the program (i.e. close everything down)
pygame.quit()
sys.exit()

First of all, you need to call your draw function inside the loop. Your current code shows only a glimpse of "drawing" because it gets executed once you exit the main loop.
So, put your drawHappy() inside of main loop:
while main:
for event in pygame.event.get(): # check for any events (i.e key press, mouse click etc.)
if event.type == pygame.QUIT: # check to see if it was "x" at top right of screen
main = False # set the "main" variable to False to exit while loop
drawHappy(x2,y2,r,colour)
pygame.display.update()
clock.tick(FPS)
screen.fill(WHITE)
Now you will get a random size "smiley" on the screen, But now it will move on exit only, for the same reason it wouldn't display earlier. Next thing is to make it bounce (move). For this you'll need some kind of update of the coordinates, just like you did in the last part of your code, except they also need to be updated during the loop, not after it.
I suggest making a Class because then it will be easier to manipulate the object.
Also, I found it easier to separate draw and update_coordinates code into separate functions and them call them from main loop for example.
Hope this helps, and if you need more help, ask.
Here, I made a quick solution using parts of your code, there is plenty room for improvement especially for update_smiley_position() method where you can control how "smiley" moves.
Also, if you need multiple objects, a list should be passed instead of single object.
import pygame as pg
import math
import random
pg.init()
clock = pg.time.Clock()
window = pg.display.set_mode((800, 600), pg.RESIZABLE)
pg.display.set_caption("Happy Face")
SCREEN_W = window.get_width()
SCREEN_H = window.get_height()
class Smiley:
def __init__(self, x, y, r, color):
self.x = x
self.y = y
self.r = r
self.color = color
self.create_smiley()
def create_smiley(self):
self.eye_radius = int(1/6 * self.r)
self.eye_x1 = int(self.x - 1/3 * self.r)
self.eye_x2 = int(self.x + 1/3 *self.r)
self.eye_y = int(self.y - 1/3 *self.r)
self.mouth_width = 1.5 * self.r
self.mouth_x = self.x - self.r * 0.75
self.mouth_y = self.y - self.r * 0.75
def draw_smiley(self, win):
pg.draw.circle(win, self.color, (self.x, self.y), self.r, 1)
pg.draw.circle(win, self.color, (self.eye_x1, self.eye_y), self.eye_radius, 1)
pg.draw.circle(win, self.color, (self.eye_x2, self.eye_y), self.eye_radius, 1)
pg.draw.arc(win, self.color, (self.mouth_x, self.mouth_y, self.mouth_width, self.mouth_width), math.pi, 2*math.pi, 1)
def update_smiley_position(self):
if self.x >= SCREEN_H - self.r or self.x <= 0 + self.r:
self.x = random.randint(100, 400)
else:
self.x += 5
if self.y >= SCREEN_W - self.r or self.y <= 0 + self.r:
self.y = random.randint(100, 400)
else:
self.y -= 5
self.create_smiley()
def draw(win, smiley):
win.fill(pg.Color("white"))
smiley.draw_smiley(win)
smiley.update_smiley_position()
pg.display.update()
def main_loop(win, smiley):
clock.tick(30)
for event in pg.event.get():
if event.type == pg.QUIT:
return False
draw(win, smiley)
return True
r = random.randint(1, 300)
x = random.randint(r, SCREEN_W - r)
y = random.randint(r, SCREEN_H - r)
smiley = Smiley(x, y, r, pg.Color("red"))
while main_loop(window, smiley):
pass
pg.quit()

Related

Why does this bug happen when I click on two sprites at the same time?

I'm making a simple game using pygame where you keep clicking on tiles as fast as you can until you miss a tile. this is the progress I've made so far. sometimes when I click on a tile (usually when 2 tiles are next to each other and you click between them) one of them does what they're supposed to while the other just disappears from the screen.
import pygame
import random
import sys
#Setting up all possible Tile positions
grid = [[0,0], [0,150], [0,300], [0,450], [0,600],
[150,0],[150,150],[150,300],[150,450],[150,600],
[300,0],[300,150],[300,300],[300,450],[300,600],
[450,0],[450,150],[450,300],[450,450],[450,600],
[600,0],[600,150],[600,300],[600,450],[600,600]]
taken = []
#Classes
class Cursor(pygame.sprite.Sprite):
def __init__(self, pic):
super().__init__()
self.image = pygame.image.load(pic).convert_alpha()
self.image = pygame.transform.scale(self.image, (50,50))
self.rect = self.image.get_rect()
def destroyTile(self):
pygame.sprite.spritecollide(cursor, tileGroup, True)
def update(self):
self.rect.topleft = pygame.mouse.get_pos()
class Tiles(pygame.sprite.Sprite):
def __init__(self, tileSize, color, x, y):
super().__init__()
self.image = pygame.Surface(([tileSize, tileSize]))
self.image.fill(color)
self.rect = self.image.get_rect()
self.rect.topleft = [x, y]
def drawTiles():
takenLen = len(taken)
while takenLen != 3:
m = random.randint(0,24)
x, y = grid[m]
if grid[m] not in taken:
blackTile = Tiles(150, black, x, y)
blackTile.add(tileGroup)
taken.append(grid[m])
takenLen += 1
def handleTiles():
mx, my = pygame.mouse.get_pos()
modx = mx % 150
mody = my % 150
x = mx - modx
y = my - mody
taken.remove([x, y])
drawTiles()
def drawRedTile():
mx, my = pygame.mouse.get_pos()
modx = mx % 150
mody = my % 150
x = mx - modx
y = my - mody
redTile = Tiles(150, red, x, y)
redTile.add(tileGroup)
#Colours
white = (255, 255, 255)
black = (0, 0, 0)
red = (255, 0, 0)
blue = (0, 0, 255)
grey = (46, 46, 46)
#Initializing Pygame
pygame.init()
clock = pygame.time.Clock()
#Screen
screenWidth = 750
screenHeight = 900
screen = pygame.display.set_mode((screenWidth, screenHeight))
pygame.display.set_caption("Tiles Game")
whiteSurface = pygame.Surface((750, 750))
whiteSurface.fill(white)
pygame.mouse.set_visible(False)
#Blue line
line = pygame.Surface((750, 10))
line.fill(blue)
#Groups
tileGroup = pygame.sprite.Group()
cursor = Cursor("cursor.png")
cursorGroup = pygame.sprite.Group()
cursorGroup.add(cursor)
score = 0
drawTiles()
while True:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
score += 1
print(score)
print(taken)
print(tileGroup)
cursor.destroyTile()
handleTiles()
#Background
screen.fill(grey)
screen.blit(whiteSurface, (0,0))
screen.blit(line, (0,750))
tileGroup.draw(screen)
cursorGroup.draw(screen)
cursorGroup.update()
pygame.display.update()
In the code I tried using print statements to see if the tile that seems to have disappeared is still there. When this happens, I assume that the tile is not in its group anymore since the number of sprites in the tile group went from 3 to 2. But the list showing all the taken positions still shows that there are 3 positions that are taken. I can still click on the tile if I just click on the space where there should be a tile and the tile comes back. I thought the game should exit when a tile isn't clicked on but it doesn't if there is an "invisible" tile in that position.
How do I make it so that this bug doesn't happen and every new tile made is visible?
The problem is that the cursor has an area and can hit more than one block at a time. So in destroyTile more than 1 block can be removed at once:
def destroyTile(self):
pygame.sprite.spritecollide(cursor, tileGroup, True)
However, the function handleTiles cannot handle this, because it can only remove one block position from the taken list. I suggest to simplify the code and recreate the taken list completely from tileGroup when blocks are removed:
def handleTiles():
taken.clear()
for tile in tileGroup:
x, y = tile.rect.topleft
taken.append([x, y])
drawTiles()

How to delete a single pygame drawing from the screen?

When the big circle touches the little circles I want the little circle that it touched to disappear from the screen. However, I can't figure out how exactly you delete an individual drawing in pygame. How do I fix this issue? does pygame have this feature built-in?
from pygame import *
import random as rd
import math as m
init()
screen = display.set_mode((800, 600))
p_1_x = 200
p_1_y = 200
p_1_change_x = 0
p_1_change_y = 0
def p_1(x, y):
player_1 = draw.circle(screen, (0, 0, 0), (x, y), 15)
def pick_up(x, y, xx, yy):
distance = m.sqrt(m.pow(xx - x, 2) + m.pow(yy - y, 2))
if distance < 19:
# I think the code to delete should go here
pass
dots = []
locations = []
for i in range(5):
x = rd.randint(100, 700)
y = rd.randint(100, 500)
locations.append((x, y))
while True:
screen.fill((255, 255, 255))
for events in event.get():
if events.type == QUIT:
quit()
if events.type == KEYDOWN:
if events.key == K_RIGHT:
p_1_change_x = 1
if events.key == K_LEFT:
p_1_change_x = -1
if events.key == K_UP:
p_1_change_y += 1
if events.key == K_DOWN:
p_1_change_y -= 1
if events.type == KEYUP:
if events.key == K_RIGHT or K_LEFT or K_UP or K_DOWN:
p_1_change_x = 0
p_1_change_y = 0
p_1_x += p_1_change_x
p_1_y -= p_1_change_y
for i, locate in enumerate(locations):
dot = draw.circle(screen, (0, 0, 0), locate, 5)
dots.append(dot)
for l in enumerate(locate):
pick_up(p_1_x, p_1_y, locate[0], locate[1])
p_1(p_1_x, p_1_y)
display.update()
Your code was so messy and hard to maintain, first I made 2 classes for Balls & Dots.
I detect collision by pygame.Rect.colliderect, first I make 2 rectangle then I check the collision like this:
def pick_up(ball, dot):
ball_rect = Rect( ball.x - ball.SIZE , ball.y - ball.SIZE , ball.SIZE*2, ball.SIZE*2)
dot_rect = Rect( dot.x - dot.SIZE , dot.y - dot.SIZE , dot.SIZE*2, dot.SIZE*2)
if ball_rect.colliderect(dot_rect):
return True
return False
If collision detects I remove it from dots array in the while loop:
for dot in dots:
if pick_up(ball, dot): # if dot in range ball
dots.remove(dot)
dot.draw()
Here is the whole source:
from pygame import *
import random as rd
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
NUMBER_OF_DOTS = 5
class Ball():
SIZE = 15
def __init__(self, x, y):
self.x = x
self.y = y
def draw(self):
draw.circle(screen, (0, 0, 0), (self.x, self.y), Ball.SIZE)
def move(self, vx, vy):
self.x += vx
self.y += vy
class Dot():
SIZE = 5
def __init__(self, x, y):
self.x = x
self.y = y
def draw(self):
draw.circle(screen, (0, 0, 0), (self.x, self.y), Dot.SIZE)
def pick_up(ball, dot):
ball_rect = Rect( ball.x - ball.SIZE , ball.y - ball.SIZE , ball.SIZE*2, ball.SIZE*2)
dot_rect = Rect( dot.x - dot.SIZE , dot.y - dot.SIZE , dot.SIZE*2, dot.SIZE*2)
if ball_rect.colliderect(dot_rect):
return True
return False
init()
screen = display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
dots = []
ball = Ball(200,200)
# generate dots
for i in range(NUMBER_OF_DOTS):
x = rd.randint(100, 700)
y = rd.randint(100, 500)
dots.append(Dot(x,y))
# the main game loop
while True:
screen.fill((255, 255, 255))
keys=key.get_pressed()
for events in event.get():
keys=key.get_pressed()
if events.type == QUIT:
quit()
if keys[K_RIGHT]:
ball.move(+1,0)
if keys[K_LEFT]:
ball.move(-1,0)
if keys[K_UP]:
ball.move(0,-1)
if keys[K_DOWN]:
ball.move(0,+1)
for dot in dots:
dot.draw()
if pick_up(ball, dot):
dots.remove(dot)
ball.draw()
display.update()
time.delay(1) # Speed down
Update1:
PyGame Rectangle Collision
http://www.pygame.org/docs/ref/rect.html#pygame.Rect.colliderect
Update2:
I make a repo in the github and did some changes,
Dots are colorful, new dot gets random color and the ball gets bigger whenever eats a dot.
https://github.com/peymanmajidi/Ball-And-Dots-Game__Pygame
The code should delete it from the locations list so that it's not re-drawn in the future. You clear the screen each frame, so clearing + not-redrawing is "deleting".
Say you modified pick_up() to simply return True or False:
def pick_up(x, y, xx, yy):
result = False
distance = m.sqrt(m.pow(xx - x, 2) + m.pow(yy - y, 2))
if distance < 19:
result = True # It was picked
return result
Then as you iterate through the locations list drawing & checking for being picked, save the index of the picked circles, then remove them from the locations in a second step. Using the 2-step form means you don't have to worry about accidentally skipping items if you delete from the list as you iterate over it.
p_1_x += p_1_change_x
p_1_y -= p_1_change_y
picked_up = [] # empty list to hold "picked" items
for i, locate in enumerate(locations):
dot = draw.circle(screen, (0, 0, 0), locate, 5)
dots.append(dot)
for l in enumerate(locate):
if ( pick_up(p_1_x, p_1_y, locate[0], locate[1]) ):
picked_up.append( i ) # save the index of anything "picked"
# remove any picked-up circles from the list
for index in sorted( picked_up, reverse=True ): # start with the highest index first
print( "Removing circle from location[%d]" % ( index ) ) # DEBUG
del( locations[ index ] )

How to simulate more realistic movement of an object?

I am writing a game environment, in which a person (worker) should move inside the area in a random direction, until it crosses with one of green-coloured obstacles (defined as pygame.draw.rect(screen, GREEN, [510,150,75,52]) and pygame.draw.rect(screen, GREEN, [450,250,68,40])).
Until now I can simulate a random movement of a worker, but it moves somehow irregularly and non-smoothly, jumping around the same area and slowly shifting to the right bottom corner.
How can I update the function create_randomPATH to support a more realistic smooth movement of a worker inside the screen area? I tried to increase a tick size till 70 or even more (clock.tick(70)) as it is indicated in one of threads, but it does not seem to solve the problem.
import pygame, random
import sys
WHITE = (255, 255, 255)
GREEN = (20, 255, 140)
GREY = (210, 210 ,210)
RED = (255, 0, 0)
PURPLE = (255, 0, 255)
SCREENWIDTH=1000
SCREENHEIGHT=578
class Worker(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
def create_randomPATH(self,x,y):
randomX = random.randint(1,5)
randomY = random.randint(1,5)
if random.uniform(0,1)>0.5:
valX = x + randomX
valY = y + randomY
else:
valX = x - randomX
valY = y - randomY
return valX, valY
class Background(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
pygame.init()
size = (SCREENWIDTH, SCREENHEIGHT)
screen = pygame.display.set_mode(size)
screen_rect=screen.get_rect()
pygame.display.set_caption("TEST")
worker = Worker("worker.png", [0,0])
w_x = worker.rect.left
w_y = worker.rect.top
bg = Background("background.jpg", [0,0])
carryOn = True
clock=pygame.time.Clock()
while carryOn:
for event in pygame.event.get():
if event.type==pygame.QUIT:
carryOn=False
pygame.display.quit()
pygame.quit()
quit()
# Draw floor layout
screen.blit(pygame.transform.scale(bg.image, (SCREENWIDTH, SCREENHEIGHT)), bg.rect)
# Draw obstacles
pygame.draw.rect(screen, GREEN, [510,150,75,52])
pygame.draw.rect(screen, GREEN, [450,250,68,40])
w_x,w_y = worker.create_randomPATH(w_x,w_y)
# worker should not go outside the screen area
worker.rect.clamp_ip(screen_rect)
screen.blit(worker.image, (w_x,w_y))
# Refresh Screen
pygame.display.flip()
clock.tick(5)
pygame.display.quit()
pygame.quit()
quit()
Take screen.blit(worker.image, (w_x,w_y)) and put it in it's own method. Then you can create a loop to loop from the oldX and oldY to the new position found by create_randomPATH You can even make the direction to move random by making them move in the x or y direction randomly each time.
Below is a rough example, but you probably have to modify it to match your code.
def UpdatePerson(image, x, y):
screen.blit(image, x, y)
while carryOn:
...
...
oldX = w_x
oldY = w_y
w_x,w_y = worker.create_randomPATH(w_x,w_y)
while(oldX < w_x || oldY < w_y):
randomDir = random.randint(1,2)
if randomDir == 1 && oldX < w_x:
UpdatePerson(worker.image, oldX, w_y)
oldX += 1
elif randomDir == 2 && oldY < w_x
UpdatePerson(worker.image, w_x, oldY)
oldY += 1
You could use an extra direction variable in your code which says in which direction the player is currently moving. One of four values: up, right, down or left. Every once in a while update that direction variable. Meanwhile move only one coordinate at a time in that direction.
def create_randomPATH(self, x, y, dir):
if random.uniform(0,1)>0.8:
# there is a 20% chance every time that direction is changed
dir = random.randInt(1,4)
if dir == 1:
return x, y+1, dir # up
if dir == 2:
return x+1, y, dir # right
if dir == 3:
return x, y-1, dir # down
if dir == 4:
return x-1, y, dir # left
In your code you would also need a global direction variable, initially it should also have a value 1, 2, 3 or 4:
w_x,w_y,w_dir = worker.create_randomPATH(w_x,w_y,w_dir)
By moving only one step on every iteration, we assure that the movement speed is always constant. In your code, it was varying from 1 to 5 steps. Also if you move a few steps in a given direction straight, it would seem more natural than changing the direction very suddenly very often. Try to play with the percentage (currently 20% chance) of how often the direction is changed and see if it makes the movement even smoother.
EDIT:
Because of the randomness of the movement, eventually the moving object will reach the borders of the game and might leave the screen. Adjust the position after each movement:
w_x,w_y,w_dir = worker.create_randomPATH(w_x,w_y,w_dir)
if (w_x + worker.rect.width > SCREENWIDTH): w_x = SCREENWIDTH - worker.rect.width
if (w_x < 0): w_x = 0
if (w_y + worker.rect.height > SCREENHEIGHT): w_y = SCREENHEIGHT - worker.rect.height
if (w_y < 0): w_y = 0
Then the object will not exit the screen and will eventually randomly move in other directions again. Because each direction is equally likely, it will move all around the screen properly.

Pygame. How to make a rect change direction on collision (boundary check)

Part of an assignment I'm working on is making a ball bounce around the screen, I can make it move, but my boundary test doesn't seem to be working: the ball simply moves in direction instead of changing direction. So to clarify, what I want to ball to do is change direction as it hits the screen edge.
import sys
import pygame
SCREEN_SIZE = 750, 550
BALL_DIAMETER = 16
BALL_RADIUS = BALL_DIAMETER // 2
MAX_BALL_X = SCREEN_SIZE[0] - BALL_DIAMETER
MAX_BALL_Y = SCREEN_SIZE[1] - BALL_DIAMETER
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
LEFT = 11
RIGHT = 12
pygame.init()
clock = pygame.time.Clock()
pygame.display.init()
font = pygame.font.SysFont("impact", 20)
pygame.display.set_caption("Breakout")
screen = pygame.display.set_mode(SCREEN_SIZE)
class Ball:
def __init__(self):
''' '''
self.ball = pygame.Rect(300, 730 -
BALL_DIAMETER,
BALL_DIAMETER, BALL_DIAMETER)
# Draw ball
def draw_ball(self):
pygame.draw.circle(screen,
WHITE, (self.ball.left
+ BALL_RADIUS, self.ball.top +
BALL_RADIUS), BALL_RADIUS)
# Updates the coordinates by adding the speed components
def move_ball(self, x, y):
self.xspeed = x
self.yspeed = y
self.ball = self.ball.move(self.xspeed, self.yspeed)
# bounds check
if self.ball.left <= 0:
self.ball.left = 0
self.xspeed = -self.xspeed
elif self.ball.left >= MAX_BALL_X:
self.ball.left = MAX_BALL_X
self.xspeed = -self.xspeed
if self.ball.top < 0:
self.ball.top = 0
self.yspeed = -self.yspeed
elif self.ball.top >= MAX_BALL_Y:
self.ball.top = MAX_BALL_Y
self.yspeed = -self.yspeed
# shows a message on screen, for testing purposes
class Text:
def show_message(self, message):
self.font = pygame.font.SysFont("impact", 20)
font = self.font.render(message,False, WHITE)
screen.blit(font, (200, 400))
class Game:
def __init__(self):
''' '''
def run(self):
b = Ball()
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
keys = pygame.key.get_pressed()
# fps lock, screen fill and method call for input
clock.tick(60)
screen.fill(BLACK)
b.draw_ball()
b.move_ball(5, -5)
# used to keep track of various elements
# Text().show_message("P: " + str(p))
pygame.display.flip()
# Creates instance of the game class, and runs it
if __name__ == "__main__":
Game().run()
Your only call to move_ball uses a constant vector.
Since you never change the call parameters, the ball moves only that way.
b.move_ball(5, -5)
Yes, you change the vector components within move_ball when you hit a wall. However, on the next call, you change them back to the original values and move the ball in the original direction.
You have to initialize the vector outside move_ball, and then let the routine access the existing vector when it's called.

Python Pygame randomly draw non overlapping circles

Im very new to python and seem to be missing something.
I want to randomly draw circles on a pygame display but only if the circles don't overlap each other.
I believe I must find the distance between all circle centers and only draw it if the distance is bigger than circle radius * 2.
I've tried many different things but all without success, I always get the same result - circles drawn overlapping.
#!/usr/bin/env python
import pygame, random, math
red = (255, 0, 0)
width = 800
height = 600
circle_num = 10
tick = 2
speed = 5
pygame.init()
screen = pygame.display.set_mode((width, height))
class circle():
def __init__(self):
self.x = random.randint(0,width)
self.y = random.randint(0,height)
self.r = 100
def new(self):
pygame.draw.circle(screen, red, (self.x,self.y), self.r, tick)
c = []
for i in range(circle_num):
c.append('c'+str(i))
c[i] = circle()
for j in range(len(c)):
dist = int(math.hypot(c[i].x - c[j].x, c[i].y - c[j].y))
if dist > int(c[i].r*2 + c[j].r*2):
c[j].new()
pygame.display.update()
else:
continue
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
You did not check against all other circles. I added a variable shouldprint which gets set to false if any other circle is too close.
import pygame, random, math
red = (255, 0, 0)
width = 800
height = 600
circle_num = 20
tick = 2
speed = 5
pygame.init()
screen = pygame.display.set_mode((width, height))
class circle():
def __init__(self):
self.x = random.randint(0,width)
self.y = random.randint(0,height)
self.r = 100
def new(self):
pygame.draw.circle(screen, red, (self.x,self.y), self.r, tick)
c = []
for i in range(circle_num):
c.append('c'+str(i))
c[i] = circle()
shouldprint = True
for j in range(len(c)):
if i != j:
dist = int(math.hypot(c[i].x - c[j].x, c[i].y - c[j].y))
if dist < int(c[i].r*2):
shouldprint = False
if shouldprint:
c[i].new()
pygame.display.update()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
The for loop has been changed to a while loop. It will keep trying to generate circles until the target number is reached. A circle is first generated. Then, it checks if it intersects with any existing circle using the formula from this answer.
It iterates through every existing circle (store in the list circles) and performs the check using the formula. any() returns True if the formula evaluates to True for any iteration. If it's True, it means it found an intersection. Thus, it continues to the next iteration to try again with a new circle.
circles = []
while len(circles) < circle_num:
new = circle()
if any(pow(c.r - new.r, 2) <=
pow(c.x - new.x, 2) + pow(c.y - new.y, 2) <=
pow(c.r + new.r, 2)
for c in circles):
continue
circles.append(new)
new.new()
pygame.display.update()

Categories