This question already exists:
Need Help Creating A Stop Light Event In Pygame (Python 3) [closed]
Closed 9 years ago.
I'm trying to create the last example in lab # 8 for the Program Arcade Games Book.
The last thing I'm trying to implement is a stoplight that changes colors based on three events and timers.
I understand that I have to use pygame.time.set_timer() at one point, but I haven't been exposed to event handling in Pygame yet. I don't know how I should go about making three separate events that would turn each corresponding traffic light its bright color.
This is what I have so far, and if you omit the line 258 to 266, the animation works using Pygame and Python 3 (I tried concatenating the "events" but its just not working obviously).
Here's a revised version where I tried to use timers to literally just change the color instead of making separate events but its not working in this case either ;(
import random
import math
# Requirements:
# Modify the prior Create-a-Picture lab, or start a new one.
# Animate the image. Try one or more of the following:
# Move an item across the screen.
# Move an item back and forth.
# Move up/down/diagonally.
# Move in circles.
# Have a person wave his/her arms.
# Create a stoplight that changes colors.
# import statement
import pygame
# Define colors:
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
DULLRED = (153, 50, 51)
GREEN = (0, 255, 0)
DULLGREEN = (55, 154, 54)
BLUE = (0, 0, 255)
LIGHTBLUE = (103, 255, 246)
YELLOW = (252, 255, 31)
DULLYELLOW = (156, 157, 50)
# initialize pygame:
pygame.init()
# screen:
size = (700, 500)
screen = pygame.display.set_mode(size)
# Set the caption:
pygame.display.set_caption("Chapter 8 Lab")
# Clock:
clock = pygame.time.Clock()
# FPS:
FPS = 60
# boolean variable for game:
done = False
# For loop for white circles:
whiteCircleList = []
for i in range(25):
x = random.randrange(0, 700)
y = random.randrange(0, 50)
whiteCircleList.append([x,y])
# For Loop for Blue Circles:
blueCircleList = []
for i in range(100):
circleX = random.randrange(0, 500)
circleY = random.randrange(0, 700)
blueCircleList.append([circleX, circleY])
# Light Blue Circle For Loop:
lightBlueCircleList = []
for i in range(100):
circleX = random.randrange(0, 500)
circleY = random.randrange(0, 700)
lightBlueCircleList.append([circleX, circleY])
# Surfboard's Rectangle (x-pos, y-pos, x-length, y-length):
surfboardRect = pygame.Rect(325, 225, 50, 150)
boardY = 255.
rectYChange = -5
phase = 0
# Diagonal Rectangle in Top Left Corner:
topLeftDiagonalRect = pygame.Rect(0, 0, 10, 10)
# Diagonal Rectangle in Top Right Corner:
topRightDiagonalRect = pygame.Rect(500, 1, 10, 10)
# Diagonal Rectangle Vectors for Top Left Rectangle:
topLeftDiagonalRectXChange = 5
topLeftDiagonalRectYChange = 5
# Diagonal Rectangle Vectors for Top Right Rectangle:
topRightDiagonalRectXChange = -5
topRightDiagonalRectYChange = -5
# Angle for Hand Rotation
handAngle = 0
# Variable for Traffic Light Cover:
currentTopColor = DULLRED
currentMiddleColor = DULLYELLOW
currentBottomColor = GREEN
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# Game Logic:
phase += 1
if phase > 180:
phase = phase * -1
# Save exact position as a float to avoid floating point errors:
boardY += math.cos(math.radians(phase))*2
surfboardRect.y = int(boardY)
# Clear the screen:
screen.fill(RED)
# Drawing Code:
# Falling Down circles:
for i in range(len(whiteCircleList)):
pygame.draw.circle(screen, WHITE, [whiteCircleList[i][0], whiteCircleList[i][1]], 3)
whiteCircleList[i][1] += 5
# If the rectangles have hit the bottom of the screen, make them appear 10 pixels above the top:
if whiteCircleList[i][1] > 450:
x = random.randrange(0, 700)
whiteCircleList[i][0] = x
y = random.randrange(-50, -10)
whiteCircleList[i][1] = y
# Red Falling Up Circles:
for i in range(len(blueCircleList)):
pygame.draw.circle(screen, BLUE, [blueCircleList[i][0], blueCircleList[i][1]], 5, 5)
blueCircleList[i][1] -= 5
if blueCircleList[i][1] < 50:
circleX = random.randrange(0,700)
circleY = random.randrange(400, 500)
blueCircleList[i][0] = circleX
blueCircleList[i][1] = circleY
# Light Blue Falling Up Circles:
for i in range(len(lightBlueCircleList)):
pygame.draw.circle(screen, LIGHTBLUE, [lightBlueCircleList[i][0], lightBlueCircleList[i][1]], 3, 3)
lightBlueCircleList[i][1] -= 5
if lightBlueCircleList[i][1] < 50:
circleX = random.randrange(0, 700)
circleY = random.randrange(400, 450)
lightBlueCircleList[i][0] = circleX
lightBlueCircleList[i][1] = circleY
# Revised Surfboard Rectangle Code:
pygame.draw.rect(screen, BLACK, surfboardRect, 0)
# Top Left Diagonal Rectangle Code:
pygame.draw.rect(screen, BLACK, topLeftDiagonalRect, 0)
# Add The Top Left Diagonal Rectangle Change Vectors
topLeftDiagonalRect.x += topLeftDiagonalRectXChange
topLeftDiagonalRect.y += topLeftDiagonalRectYChange
# Top and Bottom Screen Collision:
if topLeftDiagonalRect.y >= 500 or topLeftDiagonalRect.y <= 0:
topLeftDiagonalRectYChange = topLeftDiagonalRectYChange * -1
# Left and Right Screen Collision:
if topLeftDiagonalRect.x <= 0 or topLeftDiagonalRect.x >= 700:
topLeftDiagonalRectXChange = topLeftDiagonalRectXChange * -1
# Draw the top right rectangle:
pygame.draw.rect(screen, BLACK, topRightDiagonalRect, 0)
# Add the change vectors for the Top Right Rectangle:
topRightDiagonalRect.x += topRightDiagonalRectXChange
topRightDiagonalRect.y += topRightDiagonalRectYChange
# Top and Bottom Screen Collision:
if topRightDiagonalRect.y <= 0 or topRightDiagonalRect.y >= 500:
topRightDiagonalRectYChange = topRightDiagonalRectYChange * -1
# Left and Right Screen Collision:
if topRightDiagonalRect.x <= 0 or topRightDiagonalRect.x >= 700:
topRightDiagonalRectXChange = topRightDiagonalRectXChange * -1
# Person Waving His Arms:
# Head:
pygame.draw.circle(screen, WHITE, [575, 300], 15)
# Body:
pygame.draw.rect(screen, BLUE, [560, 315, 30, 60], 0)
# Left Rotating Hand:
# Left Hand's Original Dimensions:
# pygame.draw.line(screen, WHITE, [560, 315], [540, 295], 5)
# Original Hand's x position based on the rotating circle idea:
# handX = 40 * math.sin(handAngle) + 560
# Original Hand's y position based on the rotating circle idea:
# handY = 40 * math.cos(handAngle) + 315
handPosition = (40 * math.sin(handAngle) + 560, 40 * math.cos(handAngle) + 315)
pygame.draw.line(screen, WHITE, [560, 315], handPosition, 4)
# Increase the hand angle by 0.05 Radians:
handAngle = handAngle + 0.05
# Reset the angle after a full sweep:
pi = 3.141592653
if handAngle > 2 * pi:
handAngle = handAngle - 2*pi
# Right Immobile Hand:
pygame.draw.line(screen, WHITE, [590, 315], [590, 340], 4)
# Left Leg:
pygame.draw.rect(screen, WHITE, [560, 375, 10, 20], 0)
# Right Leg:
pygame.draw.rect(screen, WHITE, [580, 375, 10, 20], 0)
# Left Shoe Ellipse
# Ellipse Notes: ellipse(Surface, color, Rect, width=0) -> Rect
pygame.draw.ellipse(screen, BLACK, [550, 390, 20, 15], 0)
# Right Shoe Ellipse:
pygame.draw.ellipse(screen, BLACK, [580, 390, 20, 15], 0)
# Add in a changing traffic light
# Rectangle for Traffic Light:
pygame.draw.rect(screen, WHITE, [50, 350, 50, 100], 0)
# Traffic Light Post:
pygame.draw.rect(screen,BLACK, [65, 450, 20, 40], 0)
# Top light:
pygame.draw.circle(screen, currentTopColor, [75, 370], 12)
# Second light:
pygame.draw.circle(screen, currentMiddleColor, [75, 400], 12)
# Third light:
pygame.draw.circle(screen, currentBottomColor, [75, 430], 12)
# Three events must be cycled to change each of the traffic light colors
# from their dull versions to their fullest color forms
# The question is, can this be achieved through a timer?
# 60 frames per second is on the timer itself so every 180 frames would mean
# 3 seconds, or every 300 frames would mean 5 seconds
# DOCS on set timer: set_timer(eventid, milliseconds)
## turnRedLightOn = pygame.draw.circle(screen, RED, [75, 370], 12)
## + pygame.draw.circle(screen, DULLYELLOW, [75, 400], 12)
## + pygame.draw.circle(screen, DULLGREEN, [75, 430], 12)
# Turn the top light red and all other lights dull every three seconds
pygame.time.set_timer(currentTopColor = RED, 3000)
pygame.time.set_timer(currentMiddleColor = DULLYELLOW, 3000)
pygame.time.set_timer(currentBottomColor = DULLGREEN, 3000)
# Turn the middle light yellow and all other lights dull every six seconds
pygame.time.set_timer(currentTopColor = DULLRED, 6000)
pygame.time.set_timer(currentMiddleColor = YELLOW, 6000)
pygame.time.set_timer(currentBottomColor = DULLGREEN, 6000)
# Turn the bottom light green and all other lights dull every nine seconds
pygame.time.set_timer(currentTopColor = DULLRED, 9000)
pygame.time.set_timer(currentMiddleColor = DULLYELLOW, 9000)
pygame.time.set_timer(currentBottomColor = GREEN, 9000)
# Update the screen:
pygame.display.flip()
# Clock FPS:
clock.tick(FPS)
# Quit after main game loop ends:
pygame.quit()
The set_timer shouldn't execute in every cycle of the main loop, you can call it outside and it will repeat itself every 3000ms.
# Current Light
light_on = 0 # 0-red 1-yellow 2-green
LIGHTS_EVENT = pygame.USERVENT + 0 # Event code for Lights change
pygame.time.set_timer(LIGHTS_EVENT, 3000)
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == LIGHTS_EVENT:
light_on += 1
if light_on == 3:
light_on = 0
currentTopColor = DULLRED
currentMiddleColor = DULLYELLOW
currentBottomColor = DULLGREEN
if light_on == 0:
currentTopColor = RED
if light_on == 1:
currentMiddleColor = YELLOW
if light_on == 2:
currentBottomColor = GREEN
# Top light:
...
Related
I try to implement beam collision detection with a predefined track mask in Pygame. My final goal is to give an AI car model vision to see a track it's riding on:
This is my current code where I fire beams to mask and try to find an overlap:
import math
import sys
import pygame as pg
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
pg.init()
beam_surface = pg.Surface((500, 500), pg.SRCALPHA)
def draw_beam(surface, angle, pos):
# compute beam final point
x_dest = 250 + 500 * math.cos(math.radians(angle))
y_dest = 250 + 500 * math.sin(math.radians(angle))
beam_surface.fill((0, 0, 0, 0))
# draw a single beam to the beam surface based on computed final point
pg.draw.line(beam_surface, BLUE, (250, 250), (x_dest, y_dest))
beam_mask = pg.mask.from_surface(beam_surface)
# find overlap between "global mask" and current beam mask
hit = mask.overlap(beam_mask, (pos[0] - 250, pos[1] - 250))
if hit is not None:
pg.draw.line(surface, BLUE, mouse_pos, hit)
pg.draw.circle(surface, GREEN, hit, 3)
surface = pg.display.set_mode((500, 500))
mask_surface = pg.image.load("../assets/mask.png")
mask = pg.mask.from_surface(mask_surface)
clock = pg.time.Clock()
while True:
for e in pg.event.get():
if e.type == pg.QUIT:
pg.quit()
sys.exit()
mouse_pos = pg.mouse.get_pos()
surface.fill((0, 0, 0))
surface.blit(mask_surface, mask_surface.get_rect())
for angle in range(0, 120, 30):
draw_beam(surface, angle, mouse_pos)
pg.display.update()
clock.tick(30)
Let's describe what happens in the code snippet. One by one, I draw beams to beam_surface, make masks from them, and find overlap with background mask defined by one rectangle and a circle (black color in gifs). If there is a "hit point" (overlap point between both masks), I draw it with a line connecting hit point and mouse position.
It works fine for angles <0,90>:
But it's not working for angles in range <90,360>:
Pygame's overlap() documentation tells this:
Starting at the top left corner it checks bits 0 to W - 1 of the first row ((0, 0) to (W - 1, 0)) then continues to the next row ((0, 1) to (W - 1, 1)). Once this entire column block is checked, it continues to the next one (W to 2 * W - 1).
This means that this approach will work only if the beam hits the mask approximately from the top left corner. Do you have any advice on how to make it work for all of the situations? Is this generally a good approach to solve this problem?
Your approach works fine, if the x and y component of the ray axis points in the positive direction, but it fails if it points in the negative direction. As you pointed out, that is caused by the way pygame.mask.Mask.overlap works:
Starting at the top left corner it checks bits 0 to W - 1 of the first row ((0, 0) to (W - 1, 0)) then continues to the next row ((0, 1) to (W - 1, 1)). Once this entire column block is checked, it continues to the next one (W to 2 * W - 1).
To make the algorithm work, you have to ensure that the rays point always in the positive direction. Hence if the ray points in the negative x direction, then flip the mask and the ray vertical and if the ray points in the negative y direction than flip the ray horizontal.
Use pygame.transform.flip() top create 4 masks. Not flipped, flipped horizontal, flipped vertical and flipped vertical and horizontal:
mask = pg.mask.from_surface(mask_surface)
mask_fx = pg.mask.from_surface(pg.transform.flip(mask_surface, True, False))
mask_fy = pg.mask.from_surface(pg.transform.flip(mask_surface, False, True))
mask_fx_fy = pg.mask.from_surface(pg.transform.flip(mask_surface, True, True))
flipped_masks = [[mask, mask_fy], [mask_fx, mask_fx_fy]]
Determine if the direction of the ray:
c = math.cos(math.radians(angle))
s = math.sin(math.radians(angle))
Get the flipped mask dependent on the direction of the ray:
flip_x = c < 0
flip_y = s < 0
filpped_mask = flipped_masks[flip_x][flip_y]
Compute the flipped target point:
x_dest = 250 + 500 * abs(c)
y_dest = 250 + 500 * abs(s)
Compute the flipped offset:
offset_x = 250 - pos[0] if flip_x else pos[0] - 250
offset_y = 250 - pos[1] if flip_y else pos[1] - 250
Get the nearest intersection point of the flipped ray and mask and unflip the intersection point:
hit = filpped_mask.overlap(beam_mask, (offset_x, offset_y))
if hit is not None and (hit[0] != pos[0] or hit[1] != pos[1]):
hx = 500 - hit[0] if flip_x else hit[0]
hy = 500 - hit[1] if flip_y else hit[1]
hit_pos = (hx, hy)
pg.draw.line(surface, BLUE, mouse_pos, hit_pos)
pg.draw.circle(surface, GREEN, hit_pos, 3)
See the example: repl.it/#Rabbid76/PyGame-PyGame-SurfaceLineMaskIntersect-2
import math
import sys
import pygame as pg
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
pg.init()
beam_surface = pg.Surface((500, 500), pg.SRCALPHA)
def draw_beam(surface, angle, pos):
c = math.cos(math.radians(angle))
s = math.sin(math.radians(angle))
flip_x = c < 0
flip_y = s < 0
filpped_mask = flipped_masks[flip_x][flip_y]
# compute beam final point
x_dest = 250 + 500 * abs(c)
y_dest = 250 + 500 * abs(s)
beam_surface.fill((0, 0, 0, 0))
# draw a single beam to the beam surface based on computed final point
pg.draw.line(beam_surface, BLUE, (250, 250), (x_dest, y_dest))
beam_mask = pg.mask.from_surface(beam_surface)
# find overlap between "global mask" and current beam mask
offset_x = 250 - pos[0] if flip_x else pos[0] - 250
offset_y = 250 - pos[1] if flip_y else pos[1] - 250
hit = filpped_mask.overlap(beam_mask, (offset_x, offset_y))
if hit is not None and (hit[0] != pos[0] or hit[1] != pos[1]):
hx = 499 - hit[0] if flip_x else hit[0]
hy = 499 - hit[1] if flip_y else hit[1]
hit_pos = (hx, hy)
pg.draw.line(surface, BLUE, pos, hit_pos)
pg.draw.circle(surface, GREEN, hit_pos, 3)
#pg.draw.circle(surface, (255, 255, 0), mouse_pos, 3)
surface = pg.display.set_mode((500, 500))
#mask_surface = pg.image.load("../assets/mask.png")
mask_surface = pg.Surface((500, 500), pg.SRCALPHA)
mask_surface.fill((255, 0, 0))
pg.draw.circle(mask_surface, (0, 0, 0, 0), (250, 250), 100)
pg.draw.rect(mask_surface, (0, 0, 0, 0), (170, 170, 160, 160))
mask = pg.mask.from_surface(mask_surface)
mask_fx = pg.mask.from_surface(pg.transform.flip(mask_surface, True, False))
mask_fy = pg.mask.from_surface(pg.transform.flip(mask_surface, False, True))
mask_fx_fy = pg.mask.from_surface(pg.transform.flip(mask_surface, True, True))
flipped_masks = [[mask, mask_fy], [mask_fx, mask_fx_fy]]
clock = pg.time.Clock()
while True:
for e in pg.event.get():
if e.type == pg.QUIT:
pg.quit()
sys.exit()
mouse_pos = pg.mouse.get_pos()
surface.fill((0, 0, 0))
surface.blit(mask_surface, mask_surface.get_rect())
for angle in range(0, 359, 30):
draw_beam(surface, angle, mouse_pos)
pg.display.update()
clock.tick(30)
Not,the algorithm can be further improved. The ray is always drawn on the bottom right quadrant of the beam_surface. Hence the other 3 quadrants are no longer needed and the size of beam_surface can be reduced to 250x250. The start of the ray is at (0, 0) rather than (250, 250) and the computation of the offsets hast to be slightly adapted:
beam_surface = pg.Surface((250, 250), pg.SRCALPHA)
def draw_beam(surface, angle, pos):
c = math.cos(math.radians(angle))
s = math.sin(math.radians(angle))
flip_x = c < 0
flip_y = s < 0
filpped_mask = flipped_masks[flip_x][flip_y]
# compute beam final point
x_dest = 500 * abs(c)
y_dest = 500 * abs(s)
beam_surface.fill((0, 0, 0, 0))
# draw a single beam to the beam surface based on computed final point
pg.draw.line(beam_surface, BLUE, (0, 0), (x_dest, y_dest))
beam_mask = pg.mask.from_surface(beam_surface)
# find overlap between "global mask" and current beam mask
offset_x = 499-pos[0] if flip_x else pos[0]
offset_y = 499-pos[1] if flip_y else pos[1]
hit = filpped_mask.overlap(beam_mask, (offset_x, offset_y))
if hit is not None and (hit[0] != pos[0] or hit[1] != pos[1]):
hx = 499 - hit[0] if flip_x else hit[0]
hy = 499 - hit[1] if flip_y else hit[1]
hit_pos = (hx, hy)
pg.draw.line(surface, BLUE, pos, hit_pos)
pg.draw.circle(surface, GREEN, hit_pos, 3)
I have a list of snows that will fall on different coordinates, the default color is white.
I need help with creating a function to change color of 'snows' every 5th iteration of while loop. This function will then be used using a map
Tried creating a function for each snow to change colors, but it doesn't seem to be working.
WHITE = [255, 255, 255]
SILVER = [192, 192, 192]
GOLD = (255,223,0)
RED = [255, 0, 0]
colour_list = [WHITE, SILVER, GOLD, RED]
colour = random.choice(colour_list)
snow_list = []
#assigning random coordinates for each snowflake
for i in range(100):
x = random.randrange(0, 600)
y = random.randrange(0, 600)
snow_list.append([x, y])
# colour changing function
def recolour_snowflake(snowflake):
snowflake.append(colour)
pygame.draw.circle(screen, colour, snowflake, 2)
return snowflake
done = False
while not done:
#change colour every 7th iteration
if count == 7:
snow_list = list(map(recolour_snowflake, snow_list))
count=0
count += 1
# display each snow
for i in range(len(snow_list)):
# Draw the snow
pygame.draw.circle(screen, WHITE, snow_list[i], 3)
The snow list has to contain tuples of color and position. The initial color is WHITE:
snow_list = []
for i in range(100):
x = random.randrange(0, 400)
y = random.randrange(0, 400)
snow_list.append((WHITE, [x, y]))
Randomly change the color of each snowflake after 5 iterations of the loop:
def recolour_snowflake(snowflake):
colour = random.choice(colour_list)
return (colour, snowflake[1])
count = 0
done = False
while not done:
# [...]
if count == 5:
snow_list = list(map(recolour_snowflake, snow_list))
count=0
count += 1
Note, enumerate returns a tuple containing the index and the element.
Use the color of the tuple list to draw the snowflakes:
while not done:
# [...]
for snow in snow_list:
pygame.draw.circle(screen, snow[0], snow[1], 2)
See the full example with falling and recoloring snow flakes:
import pygame
import random
pygame.init()
size = (400,400)
screen = pygame.display.set_mode(size)
clock = pygame.time.Clock()
BLACK = [0, 0, 0]
WHITE = [255, 255, 255]
SILVER = [192, 192, 192]
GOLD = [255, 223, 0]
RED = [255, 0, 0]
colour_list = [WHITE, SILVER, GOLD, RED]
colour = random.choice(colour_list)
snow_list = []
for i in range(100):
x = random.randrange(0, 400)
y = random.randrange(0, 400)
snow_list.append((WHITE, [x, y]))
def recolour_snowflake(snowflake):
colour = random.choice(colour_list)
return (colour, snowflake[1])
def animate_snowflake(snowflake):
x, y = snowflake[1]
y += random.randrange(1,3)
if y > 400:
x, y = (random.randrange(0, 400), random.randrange(-50, -10))
return (snowflake[0], [x, y])
count = 0
done = False
while not done:
# handle events
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# change the color of the snow flakes
if count == 5:
snow_list = list(map(recolour_snowflake, snow_list))
count = 0
count += 1
# Process each snow flake in the list
snow_list = list(map(animate_snowflake, snow_list))
# Set the screen background
screen.fill(BLACK)
# draw the snow flakes
for snow in snow_list:
pygame.draw.circle(screen, *snow, 2)
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
clock.tick(20)
This question already has answers here:
Pygame window not responding after a few seconds
(3 answers)
Pygame unresponsive display
(1 answer)
Pygame window freezes when it opens
(1 answer)
Closed 2 years ago.
This is the first time I am using Pygame. And I am just starting to learn programming. At the moment I learning for about 4 to 7 months.
I followed the tutorial for the game I am making. (How to Program a Game! (in Python) by Keith Galli)
Unfortenately the colors will not be displayed in the game. So at the moment I am just staring at a black 'box'.
The game is a kind of 'space invaders'-game. So there are blue bricks falling from the top of the 'box', but at the moment you don see them.
Because I am jusing Linux Mint as my OS, I thought: mayby I need other code for the colours.
I tried ANSI code:
# ANSI escae code for colours:
#RED = "\e[0;31m"
#BLUE = "\e[34mBlue"
#YELLOW= "\e[33mYellow"
#BACKGROUND_COLOUR = "\e[30m"
instead of RGB:
# RGB colours:
RED = (255, 0, 0))
BLUE = (0, 0, 255))
YELLOW= (204, 204, 0)
BACKGROUND_COLOUR = (0, 0, 0))
But I stil do not see anything when I run the game :(
Here is some code:
import pygame
import random
import sys
import colorama
pygame.init()
WIDTH = 800
HEIGHT = 600
# ANSI escae code for colours:
#RED = "\e[0;31m"
#BLUE = "\e[34mBlue"
#YELLOW= "\e[33mYellow"
#BACKGROUND_COLOUR = "\e[30m"
# RGB colours:
RED = (255, 0, 0))
BLUE = (0, 0, 255))
YELLOW= (204, 204, 0)
BACKGROUND_COLOUR = (0, 0, 0))
player_size = 50
player_pos = [WIDTH/2, HEIGHT - 2 * player_size]
enemy_size = 50
enemy_pos = [random.randint(0, WIDTH - enemy_size), 0]
enemy_list = [enemy_pos]
SPEED = 10
screen = pygame.display.set_mode((WIDTH,HEIGHT))
# gameloop
game_over = False
score = 0
clock = pygame.time.Clock()
myFont = pygame.font.sysFont("monospace", 35)
def set_level(score,SPEED):
if score < 20:
SPEED = 5
elif score < 40:
SPEED = 8
elif score < 60:
SPEED = 12
else:
SPEED = 18
return SPEED
# SPEED = score / 5 +1
# return SPEED
def drop_enemies(enemy_list):
delay = random.random()
if len(enemy_list) < 10 and delay < 0.1:
x_pos = random.randint(0, WIDTH - enemy_size)
y_pos = 0
enemy_list.append([x_pos, y_pos])
def draw_enemies(enemy_list):
for enemy_pos in enemy_list:
pygame.draw.rect(screen, BLUE, (enemy_pos[0], enemy_pos[1],
enemy_size, enemy_size))
I hope to see the bricks in the game.
Whick colorcoding to use... and how to implement these?
Blue for the ones falling from the sky, red for the player itself.
You need a main loop, which does the following:
clear the screen with the back ground color (pygame.Surface.fill())
draw all the objects
update the display (pygame.display.update() or pygame.display.flip())
e.g.:
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
screen.fill(BACKGROUND_COLOUR)
draw_enemies(enemy_list)
pygame.display.update()
I'm wondering how to speed up the smoothness of my code written in Python using pygam. I'm guessing I have to make this more efficient somehow? When this is run, some balls move around randomly in a set area, however, the new position of each ball is not smooth at all, there is a jump between each movement as the cycle is very slow. How do I fix this? Or is there any suggestions on how to improve it?
This is my code so far:
import pygame
from pygame import *
import random
pygame.init()
size = width, height = 800, 600
screen = display.set_mode(size)
pygame.display.set_caption("Year 12: Ideal Gas Simulation")
BLACK = (0, 0, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
WHITE=(255,255,255)
GREEN = (0, 255, 0)
BALLX = 0
BALLY = 1
BALLSPEEDX = 2
BALLSPEEDY = 3
List=[]
radius=5
running=True
myClock=time.Clock()
myClock.tick(60)
def initBall():
for n in range(40):
ballx = random.randint(0, 800) # randomly setting the x position
bally = random.randint(0, 600) # randomly setting the y position
dirx = random.randint(-5,5) # randomly setting the x speed
diry = random.randint(-5,5) # randomly setting the y speed
data=[ballx, bally, dirx, diry]
List.append(data)
# returning a list with all the data the ball needs
return List # returning the list
def drawScreen(List):
draw.rect(screen, WHITE, (0, 0, 800, 600))
for x in range(40):
BALLX=List[x][0]
BALLY=List[x][1]
draw.circle(screen, GREEN, (BALLX,BALLY),radius)
display.flip()
pygame.draw.rect(screen, BLACK, (100-radius,100-radius,600+(2*radius),400+(2*radius)), 1)
f=pygame.font.SysFont(None,60)
text=f.render("PV=nRT",True,(0,0,0))
screen.blit(text,(300,height/20))
def moveBall(List):
for x in range(40):
BALLX=List[x][0]
BALLY=List[x][1]
SPEEDX=List[x][2]#####data[BALLX]== the first index of each list [x][0]
SPEEDY=List[x][3]##data[BALLSPEEDX]= List[x][2]
age=SPEEDX+BALLX
List[x][0]=age
# increases the position of the ball
plus=SPEEDY+BALLY
List[x][1]=plus
# checks to see if the ball is hitting the walls in the x direction
if BALLX > 700:
List[x][0] = 700#NORMALLY 800
third=List[x][2]
answer=third*-1
List[x][2]=answer
elif BALLX < 100:#NORMALLY 0
List[x][0] = 100
third=List[x][2]
answer=third*-1
List[x][2]=answer
# checks to see if the ball is hitting the walls in the y direction
if BALLY < 100:
List[x][1] = 100#NORMALLY 0
third=List[x][3]
answer=third*-1
List[x][3]=answer
elif BALLY > 500:
List[x][1] = 500#NORMALLY 600
third=List[x][3]
answer=third*-1
List[x][3]=answer
return List#return updated list
List=initBall()
while running==True:
for evnt in event.get():
if evnt.type==QUIT:
running=False
quit()
if evnt.type==MOUSEBUTTONDOWN:
mx,my=evnt.pos
button=evnt.button
drawScreen(List)
List=moveBall(List)
In addition to skrx's answer, you can also refactor the code and avoid a lot of duplicate calls. Also, indexing the BALLS array directly might improve performance slightly.
Generally, avoid naming variables inside functions with uppercase. These names are typically given to constants defined at the top of your file.
The version I came up with is below:
import array
import pygame
pygame.init()
import random
from pygame import *
size = WIDTH, HEIGHT = 800, 600
screen = display.set_mode(size)
pygame.display.set_caption("Year 12: Ideal Gas Simulation")
BLACK = (0, 0, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
WHITE = (255,255,255)
GREEN = (0, 255, 0)
BALLX = 0
BALLY = 1
BALLSPEEDX = 2
BALLSPEEDY = 3
RADIUS = 5
BALLS = []
myClock = time.Clock()
myClock.tick(60)
def initBalls():
for n in range(40):
props = array.array('i', [
random.randint(0, WIDTH),
random.randint(0, HEIGHT),
random.randint(-5, 5),
random.randint(-5, 5),
])
BALLS.append(props)
def drawScreen():
draw.rect(screen, WHITE, (0, 0, 800, 600))
props = (100-RADIUS, 100-RADIUS, 600+(2*RADIUS), 400+(2*RADIUS))
pygame.draw.rect(screen, BLACK, props, 1)
f = pygame.font.SysFont(None, 60)
text = f.render("PV=nRT", True,(0, 0, 0))
screen.blit(text,(300, HEIGHT / 20))
for i in range(len(BALLS)):
draw.circle(screen, GREEN, BALLS[i][:2],RADIUS)
display.flip()
def moveBalls():
for i in range(len(BALLS)):
if BALLS[i][0] > 700:
BALLS[i][0] = 700
BALLS[i][2] *= -1
elif BALLS[i][0] < 100:
BALLS[i][0] = 100
BALLS[i][2] *= -1
else:
BALLS[i][0] += BALLS[i][2]
if BALLS[i][1] < 100:
BALLS[i][1] = 100
BALLS[i][3] *= -1
elif BALLS[i][1] > 500:
BALLS[i][1] = 500
BALLS[i][3] *= -1
else:
BALLS[i][1] += BALLS[i][3]
def main():
initBalls()
while True:
for evnt in event.get():
if evnt.type == QUIT:
pygame.quit()
return
elif evnt.type == MOUSEBUTTONDOWN:
mx, my = evnt.pos
button = evnt.button
drawScreen()
moveBalls()
if __name__ == "__main__":
main()
Call pygame.display.flip() only once per frame.
def drawScreen(List):
draw.rect(screen, WHITE, (0, 0, 800, 600))
for x in range(40):
BALLX=List[x][0]
BALLY=List[x][1]
draw.circle(screen, GREEN, (BALLX,BALLY),radius)
# display.flip() # Don't call `display.flip()` here.
pygame.draw.rect(screen, BLACK, (100-radius,100-radius,600+(2*radius),400+(2*radius)), 1)
screen.blit(text,(300,height/20))
pygame.display.flip() # Call it here.
I also recommend to use a pygame.time.Clock to limit the frame rate.
# Define the font object as a global constant.
FONT = pygame.font.SysFont(None, 60)
# If the text doesn't change you can also define it here.
TEXT = FONT.render("PV=nRT", True, (0,0,0))
# Instantiate a clock to limit the frame rate.
clock = pygame.time.Clock()
running = True
while running: # `== True` is not needed.
for evnt in event.get():
if evnt.type == QUIT:
running = False
# Better use `pygame.quit` and `sys.exit` to quit.
pygame.quit()
sys.exit()
drawScreen(List)
List = moveBall(List)
clock.tick(30) # Limit frame rate to 30 fps.
This python code illustrates a sin wave in a pygame window.
I want to draw a square wave in this very same fashion as well, though I have no idea what this code might be or how to draw a square wave / how one is constructed in python.
Does anybody know how I can do this? Is it possible with the same imports or will I need new ones?
Code:
import sys, pygame, math
from pygame.locals import *
# set up a bunch of constants
WHITE = (255, 255, 255)
DARKRED = (128, 0, 0)
RED = (255, 0, 0)
BLACK = ( 0, 0, 0)
BGCOLOR = WHITE
WINDOWWIDTH = 640 # width of the program's window, in pixels
WINDOWHEIGHT = 480 # height in pixels
WIN_CENTERX = int(WINDOWWIDTH / 2) # the midpoint for the width of the window
WIN_CENTERY = int(WINDOWHEIGHT / 2) # the midpoint for the height of the window
FPS = 160 # frames per second to run at
AMPLITUDE = 80 # how many pixels tall the waves with rise/fall.
# standard pygame setup code
pygame.init()
FPSCLOCK = pygame.time.Clock()
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
pygame.display.set_caption('Trig Waves')
fontObj = pygame.font.Font('freesansbold.ttf', 16)
# variables that track visibility modes
showSine = True
pause = False
xPos = 0
step = 0 # the current input f
posRecord = {'sin': []} # keeps track of the ball positions for drawing the waves
# making text Surface and Rect objects for various labels
sinLabelSurf = fontObj.render('sine', True, RED, BGCOLOR)
sinLabelRect = sinLabelSurf.get_rect()
instructionsSurf = fontObj.render('Press Q to toggle wave. P to pause.', True, BLACK, BGCOLOR)
instructionsRect = instructionsSurf.get_rect()
instructionsRect.left = 10
instructionsRect.bottom = WINDOWHEIGHT - 10
# main application loop
while True:
# event handling loop for quit events
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
# check for key presses that toggle pausing and wave visibility
if event.type == KEYUP:
if event.key == K_q:
showSine = not showSine
elif event.key == K_p:
pause = not pause
# fill the screen to draw from a blank state
DISPLAYSURF.fill(BGCOLOR)
# draw instructions
DISPLAYSURF.blit(instructionsSurf, instructionsRect)
# sine wave
yPos = -1 * math.sin(step) * AMPLITUDE
posRecord['sin'].append((int(xPos), int(yPos) + WIN_CENTERY))
if showSine:
# draw the sine ball and label
pygame.draw.circle(DISPLAYSURF, RED, (int(xPos), int(yPos) + WIN_CENTERY), 10)
sinLabelRect.center = (int(xPos), int(yPos) + WIN_CENTERY + 20)
DISPLAYSURF.blit(sinLabelSurf, sinLabelRect)
# draw the waves from the previously recorded ball positions
if showSine:
for x, y in posRecord['sin']:
pygame.draw.circle(DISPLAYSURF, DARKRED, (x, y), 4)
# draw the border
pygame.draw.rect(DISPLAYSURF, BLACK, (0, 0, WINDOWWIDTH, WINDOWHEIGHT), 1)
pygame.display.update()
FPSCLOCK.tick(FPS)
if not pause:
xPos += 0.5
if xPos > WINDOWWIDTH:
xPos = 0
posRecord = {'sin': []}
step = 0
else:
step += 0.008
step %= 2 * math.pi
Python ver.2.6, Pygame ver.1.9.2
I made some modifications to add squared wave.
See places with ### HERE.
import sys, pygame, math
from pygame.locals import *
# set up a bunch of constants
WHITE = (255, 255, 255)
DARKRED = (128, 0, 0)
RED = (255, 0, 0)
BLACK = ( 0, 0, 0)
GREEN = ( 0, 255, 0) ### HERE
BLUE = ( 0, 0, 255) ### HERE
BGCOLOR = WHITE
WINDOWWIDTH = 640 # width of the program's window, in pixels
WINDOWHEIGHT = 480 # height in pixels
WIN_CENTERX = int(WINDOWWIDTH / 2) # the midpoint for the width of the window
WIN_CENTERY = int(WINDOWHEIGHT / 2) # the midpoint for the height of the window
FPS = 160 # frames per second to run at
AMPLITUDE = 80 # how many pixels tall the waves with rise/fall.
# standard pygame setup code
pygame.init()
FPSCLOCK = pygame.time.Clock()
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
pygame.display.set_caption('Trig Waves')
fontObj = pygame.font.Font('freesansbold.ttf', 16)
# variables that track visibility modes
showSine = True
showSquare = True ### HERE
pause = False
xPos = 0
step = 0 # the current input f
### HERE
posRecord = {'sin': [], 'square': []} # keeps track of the ball positions for drawing the waves
# making text Surface and Rect objects for various labels
### HERE
squareLabelSurf = fontObj.render('square', True, BLUE, BGCOLOR)
squareLabelRect = squareLabelSurf.get_rect()
sinLabelSurf = fontObj.render('sine', True, RED, BGCOLOR)
sinLabelRect = sinLabelSurf.get_rect()
instructionsSurf = fontObj.render('Press Q to toggle wave. P to pause.', True, BLACK, BGCOLOR)
instructionsRect = instructionsSurf.get_rect()
instructionsRect.left = 10
instructionsRect.bottom = WINDOWHEIGHT - 10
### HERE
yPosSquare = AMPLITUDE # starting position
# main application loop
while True:
# event handling loop for quit events
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
# check for key presses that toggle pausing and wave visibility
if event.type == KEYUP:
if event.key == K_q:
showSine = not showSine
elif event.key == K_p:
pause = not pause
# fill the screen to draw from a blank state
DISPLAYSURF.fill(BGCOLOR)
# draw instructions
DISPLAYSURF.blit(instructionsSurf, instructionsRect)
# sine wave
yPos = -1 * math.sin(step) * AMPLITUDE
posRecord['sin'].append((int(xPos), int(yPos) + WIN_CENTERY))
if showSine:
# draw the sine ball and label
pygame.draw.circle(DISPLAYSURF, RED, (int(xPos), int(yPos) + WIN_CENTERY), 10)
sinLabelRect.center = (int(xPos), int(yPos) + WIN_CENTERY + 20)
DISPLAYSURF.blit(sinLabelSurf, sinLabelRect)
# draw the waves from the previously recorded ball positions
if showSine:
for x, y in posRecord['sin']:
pygame.draw.circle(DISPLAYSURF, DARKRED, (x, y), 4)
### HERE - drawing horizontal lines
# square
posRecord['square'].append((int(xPos), int(yPosSquare) + WIN_CENTERY))
if showSquare:
# draw the sine ball and label
pygame.draw.circle(DISPLAYSURF, GREEN, (int(xPos), int(yPosSquare) + WIN_CENTERY), 10)
squareLabelRect.center = (int(xPos), int(yPosSquare) + WIN_CENTERY + 20)
DISPLAYSURF.blit(squareLabelSurf, squareLabelRect)
# draw the waves from the previously recorded ball positions
if showSquare:
for x, y in posRecord['square']:
pygame.draw.circle(DISPLAYSURF, BLUE, (x, y), 4)
# draw the border
pygame.draw.rect(DISPLAYSURF, BLACK, (0, 0, WINDOWWIDTH, WINDOWHEIGHT), 1)
pygame.display.update()
FPSCLOCK.tick(FPS)
if not pause:
xPos += 0.5
if xPos > WINDOWWIDTH:
#sine ### HERE
xPos = 0
posRecord['sin'] = []
step = 0
# square ### HERE
yPosSquare = AMPLITUDE
posRecord['square'] = []
else:
#sine ### HERE
step += 0.008
#step %= 2 * math.pi
# square ### HERE
# jump top and bottom every 100 pixels
if xPos % 100 == 0:
yPosSquare *= -1
# add vertical line
for x in range(-AMPLITUDE, AMPLITUDE):
posRecord['square'].append((int(xPos), int(x) + WIN_CENTERY))