Blitting certain images to the screen is painfully slow in pygame [duplicate] - python

This question already has answers here:
Why my pygame game with a tiled map is lagging?
(1 answer)
How could I optimise this simple python pygame code
(1 answer)
Closed 2 years ago.
I am trying to make a game, but when I try to blit a few flowers onto the screen it slows down from 60 FPS to 12 FPS.
At first, I tried to .convert() all my images, but then they turned pink. So I used .convert_alpha() instead. using .convert_alpha() made the images not pink, but it only made the FPS go up a teeny bit.
the relevant parts of my code are
### Creates a random assortment of Flora ###
def createFlora(screenSize, cellSize):
flower1 = pygame.transform.scale(pygame.image.load("Flower1.PNG"), (64, 64))
flower1 = flower1.convert_alpha()
flower2 = pygame.transform.scale(pygame.image.load("Flower2.PNG"), (64, 64))
flower2 = flower2.convert_alpha()
flowers = [flower1, flower2]
flower1ds = pygame.transform.scale(pygame.image.load("Flower1DropShadow.PNG"), (64, 64))
flower1ds = flower1ds.convert_alpha()
flower2ds = pygame.transform.scale(pygame.image.load("Flower2DropShadow.PNG"), (64, 64))
flower2ds = flower2ds.convert_alpha()
flowersds = [flower1ds, flower2ds]
flora = []
for i in range(screenSize[0] // cellSize + 1):
floraRow = []
for j in range(screenSize[1] // cellSize + 1):
if randint(0, len(flowers) * 4) < len(flowers):
choice = randint(0, len(flowers) - 1)
floraRow.append((flowers[choice],
(i * cellSize, j * cellSize),
randint(0, 3) * 90, choice))
else: floraRow.append(False)
flora.append(floraRow)
floraFinal = []
for i in range(len(flora)):
for j in range(len(flora[0])):
if flora[i][j] != False:
if flora[i][j][3] == 0:
if flora[i][j][2] == 0: #depending on the rotation of parent plant, the shadow will be changed.
floraFinal.append((pygame.transform.rotate(flowersds[flora[i][j][3]], flora[i][j][2]), flora[i][j][1]))
elif flora[i][j][2] == 90:
floraFinal.append((pygame.transform.rotate(flowersds[flora[i][j][3]], flora[i][j][2]), (flora[i][j][1][0], flora[i][j][1][1] + 8)))
elif flora[i][j][2] == 180:
floraFinal.append((pygame.transform.rotate(flowersds[flora[i][j][3]], flora[i][j][2]), (flora[i][j][1][0] + 8, flora[i][j][1][1] + 8)))
elif flora[i][j][2] == 270:
floraFinal.append((pygame.transform.rotate(flowersds[flora[i][j][3]], flora[i][j][2]), (flora[i][j][1][0] + 8, flora[i][j][1][1])))
elif flora[i][j][3] == 1:
if flora[i][j][2] == 0:
floraFinal.append((flowersds[flora[i][j][3]], flora[i][j][1]))
elif flora[i][j][2] == 90:
floraFinal.append((flowersds[flora[i][j][3]], (flora[i][j][1][0], flora[i][j][1][1] + 4)))
elif flora[i][j][2] == 180:
floraFinal.append((flowersds[flora[i][j][3]], (flora[i][j][1][0] + 4, flora[i][j][1][1] + 4)))
elif flora[i][j][2] == 270:
floraFinal.append((flowersds[flora[i][j][3]], (flora[i][j][1][0] + 4, flora[i][j][1][1])))
floraFinal.append((pygame.transform.rotate(flora[i][j][0], flora[i][j][2]), flora[i][j][1]))
return floraFinal
def renderFlora(flora, screen):
for i in range(len(flora)):
screen.blit(flora[i][0], flora[i][1])
def main():
os.environ['SDL_VIDEO_CENTERED'] = '1' #center the screen
pygame.init()
screenSize = (1856, 960) #24 x 14 cells
onTop(pygame.display.get_wm_info()['window']) #move pygame window to the front
screen = pygame.display.set_mode(screenSize)
screen.fill((25, 25, 25))
""" VARS """
cellSize = 64
pygame.font.init()
myfont = pygame.font.SysFont('Comic Sans MS', 30)
path = createPath()
flora = createFlora(screenSize, cellSize)
tileMap = createTileMap(screenSize, cellSize)
tileGridOn = False
currentButton = 0
buttons = []
gridButtonImg = pygame.transform.scale(pygame.image.load("GridButton.PNG").convert_alpha(), (48, 48))
#Grid Button Toggles a grid so you can see where Tiles are easier
buttons.append(Button((20, screenSize[1] - 64), False, gridButtonImg, screen, 48))
pathSds = pygame.transform.scale(pygame.image.load("StraightPathDropShadow.PNG").convert_alpha(), (64, 64))
pathCds = pygame.transform.scale(pygame.image.load("CornerPathDropShadow.PNG").convert_alpha(), (64, 64))
#==# MAIN LOOP #==#
clock = pygame.time.Clock()
while True:
screen.fill((25, 25, 25))
for evt in pygame.event.get():
if evt.type == pygame.QUIT:
pygame.quit()
quit()
#Draw Background Tiles
renderBackground(tileMap, screen)
#Draw Midground Items/Tiles
renderFlora(flora, screen)
#Draw Foreground Items/Tiles
renderPath(path, screen, pathSds, pathCds)
textsurface = myfont.render(str(round(clock.get_fps() * 10) / 10), False, (0, 0, 0))
screen.blit(textsurface,(15, 15))
### Buttons ###
for i in range(len(buttons)):
buttons[i].update(screen)
if buttons[i].getCTF():
currentButton = i
#Toggle Grid
tileGridOn = buttons[0].getValue()
if tileGridOn: drawGrid(screenSize, cellSize, screen)
pygame.display.flip()
clock.tick(60)
main()
The flora is created before the main loop, so I'm not creating images every cycle, I'm just passing a list through the render function, which only blits the images onto the screen.
If I comment out the renderFlora() method it goes to 60 FPS, but if I leave it in it drops significantly, I don't know why, because I already blit a lot more images for the tiled background. method.

Improve performance by pre-treating the flora. Render on a surface before the application loop and blit the surface in the application loop:
def main():
# [...]
flora_surf = pygame.Surface(screen.get_size())
renderFlora(flora, flora_surf)
while True:
# [...]
#Draw Midground Items/Tiles
# renderFlora(flora, screen) <-- DELETE
screen.blit(flora_surf, (0, 0))
# [...]

Related

How to I make multiple rects in different places? [duplicate]

Right now, my game blits all the images in random positions correctly and also gets the rect of the images correctly, but I can´t figure out how to use colliderect to make sure the images don´t overlap. How could it work for my code?
Also I´m trying to make the first text fade out and I don´t know why it doesn´t work for me.
Here is the code:
class GAME1:
def __init__(self, next_scene):
self.background = pygame.Surface(size)
# Create an array of images with their rect
self.images = []
self.rects = []
self.imagenes1_array = ['autobus.png','coche.png','barco.png','autobus2.png','grua.png','bici.png']
for i in self.imagenes1_array:
# We divide in variables so we can then get the rect of the whole Img (i2)
i2 = pygame.image.load(i)
self.images.append(i2)
s = pygame.Surface(i2.get_size())
r = s.get_rect()
# Trying to use colliderect so it doesnt overlap
if pygame.Rect.colliderect(r,r) == True:
x = random.randint(300,1000)
y = random.randint(200,700)
self.rects.append(r)
def start(self, gamestate):
self.gamestate = gamestate
for rect in self.rects:
# Give random coordinates (we limit the dimensions (x,y))
x = random.randint(300,1000)
y = random.randint(200,700)
rect.x = x
rect.y = y
def draw(self,screen):
self.background = pygame.Surface(size)
font = pygame.font.SysFont("comicsansms",70)
# First half (Show image to remember)
text1 = font.render('¡A recordar!',True, PURPLE)
text1_1 = text1.copy()
# This surface is used to adjust the alpha of the txt_surf.
alpha_surf = pygame.Surface(text1_1.get_size(), pygame.SRCALPHA)
alpha = 255 # The current alpha value of the surface.
if alpha > 0:
alpha = max(alpha-4, 0)
text1_1 = text1.copy()
alpha_surf.fill((255, 255, 255, alpha))
text1_1.blit(alpha_surf, (0,0), special_flags = pygame.BLEND_RGBA_MULT)
screen.blit(text1_1, (600,50))
# Second half (Show all similar images)
text2 = font.render('¿Cuál era el dibujo?',True, PURPLE)
#screen.blit(text2, (500,50))
for i in range(len(self.images)):
#colliding = pygame.Rect.collidelistall(self.rects)
screen.blit(self.images[i], (self.rects[i].x, self.rects[i].y))
def update(self, events, dt):
for event in events:
if event.type == pygame.MOUSEBUTTONDOWN:
for rect in self.rects:
if rect.collidepoint(event.pos):
print('works!')
Use collidelist() to test test if one rectangle in a list intersects:
for i in self.imagenes1_array:
s = pygame.image.load(i)
self.images.append(s)
r = s.get_rect()
position_set = False
while not position_set:
r.x = random.randint(300,1000)
r.y = random.randint(200,700)
margin = 10
rl = [rect.inflate(margin*2, margin*2) for rect in self.rects]
if len(self.rects) == 0 or r.collidelist(rl) < 0:
self.rects.append(r)
position_set = True
See the minimal example, that uses the algorithm to generate random not overlapping rectangles:
import pygame
import random
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
def new_recs(rects):
rects.clear()
for _ in range(10):
r = pygame.Rect(0, 0, random.randint(30, 40), random.randint(30, 50))
position_set = False
while not position_set:
r.x = random.randint(10, 340)
r.y = random.randint(10, 340)
margin = 10
rl = [rect.inflate(margin*2, margin*2) for rect in rects]
if len(rects) == 0 or r.collidelist(rl) < 0:
rects.append(r)
position_set = True
rects = []
new_recs(rects)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.KEYDOWN:
new_recs(rects)
window.fill(0)
for r in rects:
pygame.draw.rect(window, (255, 0, 0), r)
pygame.display.flip()
pygame.quit()
exit()

Sprite touching something else than a white square [duplicate]

This question already has answers here:
How do I detect collision in pygame?
(5 answers)
Collision between masks in pygame
(1 answer)
Closed 1 year ago.
So, I'm making an app/game in python using pygame module, and my problem is that, I cannot find a way to check if a sprite is entirely touching another sprite or not.
What I mean is
if (sprite is touching anything else that some white rectangle):
do some code
Here is my messy code :
main.py :
import pygame, Classes, Groups, Images, random, time
pygame.init()
pygame.font.init()
# App Variables
run_bool = False
# Fonts
ARAL_20_ITALIC = pygame.font.SysFont('Arial' , 20, italic=True)
# Colors
BG = (30, 30, 30)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
# Window
size = (800, 600)
scr = pygame.display.set_mode(size)
pygame.display.set_caption('Simulation by Cold Fire (0.1)')
# App Loop
while True:
time.sleep(0.01)
# Graphics
scr.fill(BG)
"""TERRAIN"""
terrain_hitbox = pygame.draw.rect(scr, WHITE, (300, 100, size[0] - 350, size[1] - 150))
pygame.draw.rect(scr, BLACK, (300, 100, size[0] - 350, size[1] - 150), width=10)
"""SUBJECTS"""
for subject in Groups.G_Subject:
if run_bool == True:
subject.update__()
scr.blit(subject.img, subject.rect)
"""GUI"""
pygame.draw.line(scr, WHITE, (200, 0), (200, size[1]), width=3)
scr.blit(ARAL_20_ITALIC.render('Subjects' ,False, WHITE), (30, 10))
add_hitbox = scr.blit(Images.add_img.convert_alpha(), (30, 50))
remove_hitbox = scr.blit(Images.remove_img.convert_alpha(), (30, 90))
scr.blit(ARAL_20_ITALIC.render(f'Subjects: {len(Groups.G_Subject)}' , False, WHITE), (30, 130))
if run_bool == False:
run_hitbox = scr.blit(Images.run_img.convert_alpha(), (210, size[1] - 40))
else:
run_hitbox = scr.blit(Images.stop_img.convert_alpha(), (210, size[1] - 40))
# Updating Screen
pygame.display.flip()
# Events
for event in pygame.event.get():
# Quitting App
if event.type == pygame.QUIT:
pygame.quit()
quit()
# Clicking
if event.type == pygame.MOUSEBUTTONDOWN:
mouse = pygame.mouse.get_pos()
if add_hitbox.collidepoint(mouse[0], mouse[1]):
rand_x = random.randint(terrain_hitbox[0], terrain_hitbox[0] + terrain_hitbox[2])
rand_y = random.randint(terrain_hitbox[1], terrain_hitbox[1] + terrain_hitbox[3])
Classes.Subject(rand_x, rand_y, Images.subject0_img.convert_alpha())
if remove_hitbox.collidepoint(mouse[0], mouse[1]) and not 1 > len(Groups.G_Subject):
Groups.G_Subject.remove(random.choice(Groups.G_Subject.sprites()))
if run_hitbox.collidepoint(mouse[0], mouse[1]):
if run_bool == True:
run_bool = False
else:
run_bool = True
Classes.py :
import pygame, Groups, random, math
class Subject(pygame.sprite.Sprite):
def __init__(self, x, y, img=pygame.image.load('assets/img/subject0.png').convert_alpha(pygame.display.set_mode((800, 600))), speed=5):
super().__init__()
self.img = img
self.rect = self.img.get_rect()
self.rect.x = x
self.rect.y = y
self.oldx = self.rect.x
self.oldy = self.rect.y
self.speed = speed
Groups.G_Subject.add(self)
def calculat_new_xy(self, old_xy, speed, angle_in_radians):
new_x = old_xy.x + (speed * math.cos(angle_in_radians))
new_y = old_xy.y + (speed * math.sin(angle_in_radians))
return new_x, new_y
def update__(self):
self.oldx, self.oldy = self.rect.x, self.rect.y
self.rect.x, self.rect.y = self.calculat_new_xy(self.rect, self.speed, random.randint(1, 360))
Images.py :
import pygame
add_img = pygame.transform.scale(pygame.image.load('assets/img/gui/add.png'), (90, 30))
remove_img = pygame.transform.scale(pygame.image.load('assets/img/gui/remove.png'), (90, 30))
subject0_img = pygame.image.load('assets/img/subject0.png')
run_img = pygame.transform.scale(pygame.image.load('assets/img/gui/run.png'), (90, 30))
stop_img = pygame.transform.scale(pygame.image.load('assets/img/gui/stop.png'), (90, 30))
Groups.py :
import pygame
G_Subject = pygame.sprite.Group()
I do know my code is a mess, so if you don't wanna help, it's ok ! Thx in advance :D

How can I reduce the lag and CPU usage in pygame? (pytmx is used for loading the map)

I have a problem with massive lag in my game. My game has a map that is quite large. The map is 250x250 tiles. Each tile is 32x32. Note that I'm using an older laptop. When I open a task manager it says that my CPU usage is up to 50%, but the memory usage is only 30-40 MB. I'm a beginner so I would appreciate any help.
I'm using the code that I got from some YouTube tutorial. I did some modifying for it to work with my project.
import pygame as pg, time, random
from pygame.locals import *
from pytmx.util_pygame import load_pygame
def blit_all_tiles(screen, tmxdata, world_offset):
for layer in tmxdata:
for tile in layer.tiles():
# tile[0] = x coordinate
# tile[1] = y coordinate
# tile[2] = image data
x_pixel = tile[0] * 32 + world_offset[0]
y_pixel = tile[1] * 32 + world_offset[1]
screen.blit(tile[2], (x_pixel, y_pixel))
# Game Variables
def main():
tmxdata = load_pygame("assets/Chris' adventure.tmx")
# Standing
player_stand = pg.image.load("CharAssets/Cris 01.png")
player_stand = pg.transform.scale(player_stand, (32, 32))
# Moving Right
player_left = [
pg.image.load("CharAssets/Cris 04.png"),
pg.image.load("CharAssets/Cris 05.png"),
pg.image.load("CharAssets/Cris 06.png"),
]
player_up = [
pg.image.load("CharAssets/Cris 07.png"),
pg.image.load("CharAssets/Cris 08.png"),
pg.image.load("CharAssets/Cris 09.png"),
]
player_down = [
pg.image.load("CharAssets/Cris 01.png"),
pg.image.load("CharAssets/Cris 02.png"),
pg.image.load("CharAssets/Cris 03.png"),
]
# Resize
player_left = [pg.transform.scale(image, (32, 32)) for image in player_left]
player_left_f = 0
player_up = [pg.transform.scale(image, (32, 32)) for image in player_up]
player_up_f = 0
player_down = [pg.transform.scale(image, (32, 32)) for image in player_down]
player_down_f = 0
# Flipping
player_right = [pg.transform.flip(image, True, False) for image in player_left]
player_right_f = 0
direction = "stand"
world_offset = [0, 0]
quit = False
x = 400
y = 200 + 128
# Game Loop
while not quit:
screen.fill((0, 0, 0))
blit_all_tiles(screen, tmxdata, world_offset)
# Events
keypressed = pg.key.get_pressed()
for event in pg.event.get():
# print(event)
if event.type == QUIT:
quit = True
if keypressed[ord("a")]:
x = x - 20
world_offset[0] += 40
direction = "left"
if sum(keypressed) == 0:
direction = "stand"
if keypressed[ord("d")]:
x = x + 20
world_offset[0] -= 40
direction = "right"
if keypressed[ord("w")]:
y = y - 20
world_offset[1] += 40
direction = "up"
if keypressed[ord("s")]:
y = y + 20
world_offset[1] -= 40
direction = "down"
if y < 204:
y = 204
if y >= screen.get_height() - 204 - 32:
y = screen.get_height() - 204 - 32
if x < 204:
x = 204
# world_offset[0] += 10
if x >= screen.get_width() - 204 - 32:
x = screen.get_width() - 204 - 32
# Game Logic
# player = Rect(x, y, 32, 32)
# pg.draw.rect(screen, (225, 0, 120), player)
if direction == "stand":
screen.blit(player_stand, (x, y))
elif direction == "left":
screen.blit(player_left[player_left_f], (x, y))
player_left_f = (player_left_f + 1) % len(player_left)
elif direction == "right":
screen.blit(player_right[player_right_f], (x, y))
player_right_f = (player_right_f + 1) % len(player_right)
elif direction == "up":
screen.blit(player_up[player_up_f], (x, y))
player_up_f = (player_up_f + 1) % len(player_up)
elif direction == "down":
screen.blit(player_down[player_down_f], (x, y))
player_down_f = (player_down_f + 1) % len(player_down)
# Screen Update
pg.display.update()
clock.tick(60)
# Game Initialization
if __name__ == "__main__":
width, height = 800, 600
pg.init()
pg.mixer.init()
screen = pg.display.set_mode((width, height))
pg.display.set_caption("Chris' quest")
clock = pg.time.Clock()
main()
pg.quit()
I successfully imported the map, and it works for now but the lag is extreme. I'm confused and i don't know how to solve this problem. I would appreciate help. Thanks in advance.
Ensure that the image Surface has the same format as the display Surface. Use convert() (or convert_alpha()) to create a Surface that has the same pixel format. This improves performance when the image is blit on the display, because the formats are compatible and blit does not need to perform an implicit transformation.
For example:
player_stand = pg.image.load("CharAssets/Cris 01.png")
player_stand = pg.image.load("CharAssets/Cris 01.png").convert_alpha()
Another option is to create a single large map of the game.
Create a pygame.Surface the size of the entire map by drawing all the tiles on it:
def create_map():
game_map = pygame.Surface((250 * 32, 250 * 32))
for layer in tmxdata:
for tile in layer.tiles():
# tile[0] = x coordinate
# tile[1] = y coordinate
# tile[2] = image data
x_pixel = tile[0] * 32
y_pixel = tile[1] * 32
game_map.blit(tile[2], (x_pixel, y_pixel))
return game_map
game_map = create_map()
In the application, you draw only a subsection of the map on the screen, using the optional area argument of the blit method:
while not quit:
# [...]
sub_area = screen.get_rect(topleft = world_offset)
screen.blit(game_map, (0, 0), sub_area)
# [...]
This solution improves performance. However, you pay with memory usage.

Python, Pygame, my while loop breaks before it ends and i dont kno how to fix it [duplicate]

This question already has answers here:
Why is my PyGame application not running at all?
(2 answers)
Why is nothing drawn in PyGame at all?
(2 answers)
Closed 1 year ago.
Im working on a school project on a motion projectile but my while loop breaks before it ends.
import pygame, sys
from pygame.locals import *
from math import *
def rot_center(image, center_angle):
orig_rect = image.get_rect()
rot_image = pygame.transform.rotate(image, center_angle)
rot_rect = orig_rect.copy()
rot_rect.center = rot_image.get_rect().center
rot_image = rot_image.subsurface(rot_rect).copy()
return rot_image
pygame.init()
FPS = 144
fpsClock = pygame.time.Clock()
SCENE = pygame.display.set_mode((1280, 720), 0, 32)
pygame.display.set_caption('Domača naloga')
backgroundImg = pygame.image.load('images/background.png')
cannonBaseImg = pygame.image.load('images/base.png')
cannonImg = pygame.image.load('images/cannon.png')
ballImg = pygame.image.load('images/cannonBall.png')
enemy = pygame.image.load('images/enemy.png')
cannonBasePos = (9,490)
cannonPos = (-5, 480)
ballPos = (22,510)
enemyPos = (40, 400)
cannonImg = pygame.transform.rotate(cannonImg, -15)
ang = 45
cannonMovImg = rot_center(cannonImg, ang)
# bug: backgoround image dissapears and it bugges green
SCENE.blit(backgroundImg, (0,0))
SCENE.blit(cannonMovImg, cannonPos )
SCENE.blit(ballImg, ballPos)
SCENE.blit(cannonBaseImg, cannonBasePos)
SCENE.blit(enemy, enemyPos)
t = 0
s = ballPos
v = (0, 0)
vm = 100
launched = False
while True: #<-- while starts here
dt = fpsClock.tick(FPS)
if launched:
t = t + dt/250.0 # cajt is clocka
a = (0.0, 10.0) # acceleration
v = (v0[0] + a[0]*t, v0[1] + a[1]*t) # spazz out nad 1000 !!!
vm = sqrt(v[0]*v[0] + v[1]*v[1])
s0 = ballPos # poz
s = (s0[0] + v0[0]*t + a[0]*t*t/2, s0[1] + v0[1]*t + a[1]*t*t/2)
if s[1] >= 510: # tla
launched = False
font = pygame.font.Font(None, 30)
text_ang = font.render("Kot = %d" % ang, 1, (10, 10, 10))
text_ang_pos = (1050, 50)
text_vm = font.render("Hitrost = %.1f m/s" % vm, 1, (10, 10, 10))
text_vm_pos = (1050, 80)
text_stat = font.render("Statistika:", 1, (10, 10, 10))
text_stat_pos = (1050, 20)
text_x = font.render("Dolžina = %.1f m" % s[0], 1, (10, 10, 10))
text_x_pos = (1050, 110)
text_t = font.render("Čas = %.1f s" % t, 1, (10, 10, 10))
text_t_pos = (1050, 140)
SCENE.blit(backgroundImg, (0,0))
SCENE.blit(cannonMovImg, cannonPos)
SCENE.blit(ballImg, s)
SCENE.blit(cannonBaseImg, cannonBasePos)
SCENE.blit(enemy, enemyPos) #<--- while loop breaks here
SCENE.blit(text_t, text_t_pos)
SCENE.blit(text_stat, text_stat_pos)
SCENE.blit(text_vm, text_vm_pos)
SCENE.blit(text_x, text_x_pos)
SCENE.blit(text_ang, text_ang_pos)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_SPACE:
ballPos = (0,478)
s = ballPos
t = 0
launched = True
v0 = (vm*cos(radians(ang)), -vm*sin(radians(ang)))
keystate = pygame.key.get_pressed()
if keystate[K_LEFT]:
ang+=2
if ang > 90:
ang = 90
cannonMovImg = rot_center(cannonImg, ang)
if keystate[K_RIGHT]:
ang-=2
if ang < 0:
ang = 0
cannonMovImg = rot_center(cannonImg, ang)
if keystate[K_UP]:
vm+=2
if keystate[K_DOWN]:
vm-=2
pygame.display.flip() #<-- while should end here
I've tried everything but even my teacher doesnt know the anwser. He said it's a bug in Python 3.6.3. Any ideas. I've researched for the past week and im frustrated. If anyone has any ideas please let me know. Thanks for your anwsers in advance

pygame both audial and visual sine wave

Here is a function from my program, it called when a key is pressed. What is supposed to happen is the key is pressed and a corresponding note is played, and a sine wave appears on the screen also. The sound plays fine, so I won't post any more code for the sound, but it is just the visual side of things that don't work, why won't this wave show?
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
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
fontObj = pygame.font.SysFont('freesansbold.ttf', 16)
FPSCLOCK = pygame.time.Clock()
# set up a bunch of constants
BLUE = ( 0, 0, 255)
WHITE = (255, 255, 255)
DARKRED = (128, 0, 0)
DARKBLUE = ( 0, 0, 128)
RED = (255, 0, 0)
GREEN = ( 0, 255, 0)
DARKGREEN = ( 0, 128, 0)
YELLOW = (255, 255, 0)
DARKYELLOW = (128, 128, 0)
BLACK = ( 0, 0, 0)
BGCOLOR = WHITE
FPS = 160 # frames per second to run at
pause = False
# making text Surface and Rect objects for various labels
sinLabelSurf = fontObj.render('sine', True, RED, BGCOLOR)
squareLabelSurf = fontObj.render('square', True, BLUE, BGCOLOR)
sinLabelRect = sinLabelSurf.get_rect()
squareLabelRect = squareLabelSurf.get_rect()
def MakeSineWave(freq=1000):
#### visual part ####
xPos = 0
step = 0
AMPLITUDE = 80 # how many pixels tall the waves with rise/fall.
posRecord = {'sin': [], 'line': []}
yPos = -1 * math.sin(step) * AMPLITUDE
posRecord['sin'].append((int(xPos), int(yPos) + WIN_CENTERY))
# draw the sine ball and label
pygame.draw.circle(DISPLAYSURF, DARKRED, (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
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
#### audial part ####
return MakeSound(SineWave(freq))
You run MakeSineWave only once - when button is pressed - but code which draws wave have to be run all the time in all loop. It draws longer wave in every loop.
It seems you ask in another question how to draw all wave and move only red ball - and answer need differen changes in MakeSineWave then changes for this question.
EDIT: It is working code - but there is a mess - it needs less code directly in mainloop but more code in some new functions.
import pygame
from pygame.locals import *
import math
import numpy
#----------------------------------------------------------------------
# functions
#----------------------------------------------------------------------
def SineWave(freq=1000, volume=16000, length=1):
num_steps = length * SAMPLE_RATE
s = []
for n in range(num_steps):
value = int(math.sin(n * freq * (6.28318/SAMPLE_RATE) * length) * volume)
s.append( [value, value] )
return numpy.array(s)
#-------------------
def SquareWave(freq=1000, volume=100000, length=1):
num_steps = length * SAMPLE_RATE
s = []
length_of_plateau = int( SAMPLE_RATE / (2*freq) )
print num_steps, length_of_plateau
counter = 0
state = 1
for n in range(num_steps):
value = state * volume
s.append( [value, value] )
counter += 1
if counter == length_of_plateau:
counter = 0
state *= -1
return numpy.array(s)
#-------------------
def MakeSound(arr):
return pygame.sndarray.make_sound(arr)
#-------------------
def MakeSquareWave(freq=1000):
return MakeSound(SquareWave(freq))
#-------------------
def MakeSineWave(freq=1000):
return MakeSound(SineWave(freq))
#-------------------
def DrawSineWave():
# 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(screen, RED, (int(xPos), int(yPos) + WIN_CENTERY), 10)
sinLabelRect.center = (int(xPos), int(yPos) + WIN_CENTERY + 20)
screen.blit(sinLabelSurf, sinLabelRect)
# draw the waves from the previously recorded ball positions
if showSine:
for x, y in posRecord['sin']:
pygame.draw.circle(screen, DARKRED, (x, y), 4)
#-------------------
def DrawSquareWave():
# square wave
posRecord['square'].append((int(xPos), int(yPosSquare) + WIN_CENTERY))
if showSquare:
# draw the square ball and label
pygame.draw.circle(screen, GREEN, (int(xPos), int(yPosSquare) + WIN_CENTERY), 10)
squareLabelRect.center = (int(xPos), int(yPosSquare) + WIN_CENTERY + 20)
screen.blit(squareLabelSurf, squareLabelRect)
# draw the waves from the previously recorded ball positions
if showSquare:
for x, y in posRecord['square']:
pygame.draw.circle(screen, BLUE, (x, y), 4)
#----------------------------------------------------------------------
# constants - (uppercase name)
#----------------------------------------------------------------------
# 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)
BLUE = ( 0, 0, 255)
BGCOLOR = WHITE
WINDOWWIDTH = 1200 # width of the program's window, in pixels
WINDOWHEIGHT = 720 # 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.
#-------------------
SAMPLE_RATE = 22050 ## This many array entries == 1 second of sound.
SINE_WAVE_TYPE = 'Sine'
SQUARE_WAVE_TYPE = 'Square'
#----------------------------------------------------------------------
# main program
#----------------------------------------------------------------------
#-------------------
# variables (which don't depend on pygame)
#-------------------
sound_types = {SINE_WAVE_TYPE:SQUARE_WAVE_TYPE, SQUARE_WAVE_TYPE:SINE_WAVE_TYPE}
current_type = SINE_WAVE_TYPE
current_played = { 'z': None, 'c': None }
current_drawn = None
#-------------------
# variables that track visibility modes
showSine = True
showSquare = True
xPos = 0
step = 0 # the current input f
posRecord = {'sin': [], 'square': []} # keeps track of the ball positions for drawing the waves
yPosSquare = AMPLITUDE # starting position
#-------------------
# start program
#-------------------
pygame.init()
screen = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), pygame.HWSURFACE | pygame.DOUBLEBUF)
pygame.display.set_caption('Nibbles!')
# making text Surface and Rect objects for various labels
pygame.display.set_caption('Trig Waves')
fontObj = pygame.font.Font('freesansbold.ttf', 16)
### HERE
squareLabelSurf = fontObj.render('square', True, BLUE, BGCOLOR)
squareLabelRect = squareLabelSurf.get_rect()
sinLabelSurf = fontObj.render('sine', True, RED, BGCOLOR)
sinLabelRect = sinLabelSurf.get_rect()
#-------------------
# mainloop
#-------------------
fps_clock = pygame.time.Clock()
_running = True
while _running:
#-------------------
# events
#-------------------
for event in pygame.event.get():
if event.type == pygame.QUIT:
_running = False
# some keys don't depend on `current_type`
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
_running = False
if event.key == K_RETURN:
current_type = sound_types[current_type] #Toggle
print 'new type:', current_type
# some keys depend on `current_type`
if current_type == SINE_WAVE_TYPE:
if event.type == KEYDOWN:
#lower notes DOWN
if event.key == K_z:
print current_type, 130.81
current_played['z'] = MakeSineWave(130.81)
current_played['z'].play()
current_drawn = DrawSineWave
elif event.key == K_c:
print current_type, 180.81
current_played['c'] = MakeSineWave(180.81)
current_played['c'].play()
current_drawn = DrawSineWave
elif event.type == KEYUP:
#lower notes UP
if event.key == K_z:
current_played['z'].fadeout(350)
current_drawn = None
#sine - reset data
xPos = 0
posRecord['sin'] = []
step = 0
elif event.key == K_c:
current_played['c'].fadeout(350)
current_drawn = None
#sine - reset data
xPos = 0
posRecord['sin'] = []
step = 0
elif current_type == SQUARE_WAVE_TYPE:
if event.type == KEYDOWN:
#lower notes DOWN
if event.key == K_z:
print current_type, 130.81
current_played['z'] = MakeSquareWave(130.81)
current_played['z'].play()
current_drawn = DrawSquareWave
elif event.key == K_c:
print current_type, 180.81
current_played['c'] = MakeSquareWave(180.81)
current_played['c'].play()
current_drawn = DrawSquareWave
elif event.type == KEYUP:
#lower notes UP
if event.key == K_z:
current_played['z'].fadeout(350)
current_drawn = None
# square - reset data
xPos = 0
yPosSquare = AMPLITUDE
posRecord['square'] = []
step = 0
elif event.key == K_c:
current_played['c'].fadeout(350)
current_drawn = None
# square - reset data
xPos = 0
yPosSquare = AMPLITUDE
posRecord['square'] = []
step = 0
#-------------------
# draws
#-------------------
# fill the screen to draw from a blank state
screen.fill(BGCOLOR)
if current_drawn:
current_drawn()
pygame.display.update()
#-------------------
# moves
#-------------------
if current_drawn:
xPos += 1.0 #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))
#-------------------
# FPS
#-------------------
fps_clock.tick(FPS)
#-------------------
# end program
#-------------------
pygame.quit()
#----------------------------------------------------------------------

Categories

Resources