Adding gradient to moving objects in pygame - python

I want to add gradient to the ball in this program & also possibly the waves drawn to fade into the colour of the background (as if glowing) instead of one colour fills.
I've looked at tons of tutorials however none of them are making much sense to my syntax, the general idea to me is confusing as I have moving objects that draw the space I want to add gradient to quite slowly. Can anyone give an insight into how I can do this?
code:
import sys, pygame, math
from pygame.locals import *
# set up 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
screen = pygame.display.set_mode()
WINDOWWIDTH = 800 # width of the program's window, in pixels
WINDOWHEIGHT = 800 # 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
screen = pygame.display.get_surface()
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.RESIZABLE)
pygame.display.set_caption('Window title')
fontObj = pygame.font.Font('freesansbold.ttf', 16)
# variables that track visibility modes
showSine = True
showSquare = True
pause = False
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
# 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()
# fill the screen to draw from a blank state
DISPLAYSURF.fill(BGCOLOR)
# 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)
#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 += 1
#wave movement
if xPos > WINDOWWIDTH:
#sine
xPos = 0
posRecord['sin'] = []
step = 0
# square
yPosSquare = AMPLITUDE
posRecord['square'] = []
else:
#sine
step += 0.008
#step %= 2 * math.pi
# square
# 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))

Use SPACE to change background color.
First line use only transparency - and has no problem with different background color.
Second line changes only circles color - and depends on background color.
Third and fourth line (it is the same line with different starting color) change circles color and transparency - and depends on background color.
Second and last line look good on one color background and need more work to find good-looking fading.
import pygame
pygame.init()
screen = pygame.display.set_mode((600,200))
#--------------------------------------
# circles positions and transparency (x,y, alpha)
circles = []
for x in range(100):
circles.append( [100+x*3, 200, x*2] )
#--------------------------------------
white = True # background color
#--------------------------------------
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
elif event.key == pygame.K_SPACE:
white = not white
#--------------------------------------
if white:
screen.fill((255,255,255))
else:
screen.fill((0,0,0))
#--------------------------------------
# first
circle_img = pygame.Surface((20,20))
pygame.draw.circle(circle_img, (255,0,0), (10,10), 10)
circle_img.set_colorkey(0)
for x in circles:
circle_img.set_alpha(x[2])
screen.blit(circle_img, (x[0],40))
#--------------------------------------
# second
circle_img = pygame.Surface((20,20))
for x in circles:
pygame.draw.circle(circle_img, (255,255-x[2],255-x[2]), (10,10), 10)
circle_img.set_colorkey(0)
screen.blit(circle_img, (x[0],90))
#--------------------------------------
# last
circle_img = pygame.Surface((20,20))
for x in circles:
pygame.draw.circle(circle_img, (255,255-x[2],255-x[2]), (10,10), 10)
circle_img.set_colorkey(0)
circle_img.set_alpha(x[2])
screen.blit(circle_img, (x[0],140))
#--------------------------------------
pygame.display.flip()
pygame.quit()

Related

Pygame resizing a surface and window based on a rect size

I have a display area and a surface that is blitted on the display. On the surface is an image, in this case a rect. In the future it may be multiple rects or lines drawn on the surface keep that in mind.
I am trying to enlarge (by pressing x) the Rect named Sprite that is on SpriteSurface and SpriteSurface as well as the whole display window. The SpriteSurface image should be centered despite the resize. Currently the window will enlarge and the image stays centered, but if you uncomment the spritesizeX and Y lines the image gets larger but too big too fast and the window doesn't seem to enlarge big enough. Lowering the values shows that the offset of centering gets thrown off after the first resize. I feel like the solution should be relatively easy but im stumped. Any help would be appreciated.
Settings.py
spriteSizeX = 30
spriteSizeY = 30
SpHalfX = int(round(spriteSizeX / 2))
SpHalfY = int(round(spriteSizeY / 2))
multiplyer = 3
windowSizeX = int(round(spriteSizeX * multiplyer))
windowSizeY = int(round(spriteSizeY * multiplyer))
HalfWinX = int(round((windowSizeX / 2) - SpHalfX))
HalfWinY = int(round((windowSizeY / 2) - SpHalfY))
Orange = (238,154,0)
Gold = (255,215,0)
Black = (0,0,0)
Blue = (0,0,255)
Gray = (128,128,128)
DarkGray = (100,100,100)
Green = (0,128,0)
Lime = (0,255,0)
Purple = (128,0,128)
Red = (255,0,0)
Teal = (0,200, 128)
Yellow = (255,255,0)
White = (255,255,255)
run = True
SpriteCapture.py
#!/usr/local/bin/python3.6
import sys, pygame
from pygame.locals import *
from settings import *
pygame.init()
pygame.display.set_caption("Sprite Capture")
Screen = pygame.display.set_mode((windowSizeX, windowSizeY),RESIZABLE)
SpriteSurface = pygame.Surface((spriteSizeX,spriteSizeY))
Sprite = Rect(0,0,spriteSizeX,spriteSizeY)
while run == True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if pygame.key.get_pressed()[pygame.K_s]:
pygame.image.save(SpriteSurface, 'img1.png')
run = False
if pygame.key.get_pressed()[pygame.K_q]:
run = False
if pygame.key.get_pressed()[pygame.K_z]:
#spriteSizeX += 10
#spriteSizeY += 10
windowSizeX += -10
windowSizeY += -10
HalfWinX = int(round(windowSizeX / 2 - SpHalfX))
HalfWinY = int(round(windowSizeY / 2 - SpHalfY))
Screen = pygame.display.set_mode((windowSizeX, windowSizeY),RESIZABLE)
SpriteSurface = pygame.Surface((spriteSizeX,spriteSizeY))
if pygame.key.get_pressed()[pygame.K_x]:
#spriteSizeX += 10
#spriteSizeY += 10
windowSizeX += 10
windowSizeY += 10
HalfWinX = int(round(windowSizeX / 2 - SpHalfX))
HalfWinY = int(round(windowSizeY / 2 - SpHalfY))
Screen = pygame.display.set_mode((windowSizeX, windowSizeY),RESIZABLE)
SpriteSurface = pygame.Surface((spriteSizeX,spriteSizeY))
Sprite = Sprite = Rect(0,0,spriteSizeX,spriteSizeY)
Screen.fill(Black)
pygame.draw.rect(SpriteSurface,Orange,Sprite)
Screen.blit(SpriteSurface, (HalfWinX,HalfWinY))
pygame.display.flip()
If you want to scale your surfaces or rects according to the screen size, you can define a zoom_factor variable which you can just increase when a key gets pressed and then use it to scale the window and the surfaces. Multiply it by the original screen width and height to scale the window, and also scale your surfaces with pygame.transform.rotozoom and pass the zoom_factor as the scale argument.
import sys
import pygame
from pygame.locals import *
width = 30
height = 30
multiplyer = 3
window_width = round(width * multiplyer)
window_height = round(height * multiplyer)
zoom_factor = 1
ORANGE = (238,154,0)
BLACK = (0,0,0)
pygame.init()
screen = pygame.display.set_mode((window_width, window_height), RESIZABLE)
screen_rect = screen.get_rect() # A rect with the size of the screen.
clock = pygame.time.Clock()
# Keep a reference to the original image to preserve the quality.
ORIG_SURFACE = pygame.Surface((width, height))
ORIG_SURFACE.fill(ORANGE)
surface = ORIG_SURFACE
# Center the rect on the screen's center.
rect = surface.get_rect(center=screen_rect.center)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
run = False
elif event.key == pygame.K_z:
zoom_factor = round(zoom_factor-.1, 1)
# Scale the screen.
w, h = int(window_width*zoom_factor), int(window_height*zoom_factor)
screen = pygame.display.set_mode((w, h), RESIZABLE)
screen_rect = screen.get_rect() # Get a new rect.
# Scale the ORIG_SURFACE (the original won't be modified).
surface = pygame.transform.rotozoom(ORIG_SURFACE, 0, zoom_factor)
rect = surface.get_rect(center=screen_rect.center) # Get a new rect.
elif event.key == pygame.K_x:
zoom_factor = round(zoom_factor+.1, 1)
w, h = int(window_width*zoom_factor), int(window_height*zoom_factor)
screen = pygame.display.set_mode((w, h), RESIZABLE)
screen_rect = screen.get_rect()
surface = pygame.transform.rotozoom(ORIG_SURFACE, 0, zoom_factor)
rect = surface.get_rect(center=screen_rect.center)
# Note that the rect.w/screen_rect.w ratio is not perfectly constant.
print(zoom_factor, screen_rect.w, rect.w, rect.w/screen_rect.w)
screen.fill(BLACK)
screen.blit(surface, rect) # Blit the surface at the rect.topleft coords.
pygame.display.flip()
clock.tick(60)
Alternatively, you could just blit all of your surfaces onto a background surface, then scale this background with pygame.transform.rotozoom each frame and blit it onto the screen. However, scaling a big background surface each frame will be bad for the performance.

How do I change the color of a single rectangle in a grid?

I've already programmed a grid, but now I want to change the color of a single rectangle in the grid.
x = 5
y = 5
height = 30
width = 50
size = 20
color = (255,255,255)
new_color = (255,255,0)
screen.fill((0,0,0))
def draw_grid():
for y in range(height):
for x in range(width):
rect = pygame.Rect(x * (size + 1),y * (size + 1),size,size)
pygame.draw.rect(screen,color,rect)
x += 20
rects.append((rect,color))
y += 20
rects = []
colored_rects = []
while 1:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
draw_grid()
if pygame.mouse.get_pressed()[0]:
mouse_pos = pygame.mouse.get_pos()
for i,(rect,color) in enumerate(rects):
if rect.collidepoint(mouse_pos):
rects[i] = (rect,new_color)
colored_rects.append((rect,new_color))
for rect,color in rects:
pygame.draw.rect(screen,color,rect)
for rect,new_color in colored_rects:
pygame.draw.rect(screen,new_color,rect)
pygame.display.flip()
clock.tick()
Now I only want to change one rectangle when I click on it, but later they must change automatically (for example when there are three rectangles touching in the same color, they all must become white). I've updated a little bit, but there are still some problems. For example: You have to click on the rectangle till it changes color, and it takes to much time te change color.
One solution would be to store the rects together with their color in tuples. If the mouse button is pressed, you iterate over the rectangles list, if a rectangle collides with the mouse, you create a tuple with the rect and the new color and replace the tuple at the current index.
import sys
import pygame as pg
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
height = 30
width = 50
size = 20
color = (255, 255, 255)
new_color = (255, 255, 0)
rectangles = []
for y in range(height):
for x in range(width):
rect = pg.Rect(x * (size+1), y * (size+1), size, size)
# The grid will be a list of (rect, color) tuples.
rectangles.append((rect, color))
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
if pg.mouse.get_pressed()[0]:
mouse_pos = pg.mouse.get_pos()
# Enumerate creates tuples of a number (the index)
# and the rect-color tuple, so it looks like:
# (0, (<rect(0, 0, 20, 20)>, (255, 255, 255)))
# You can unpack them directly in the head of the loop.
for index, (rect, color) in enumerate(rectangles):
if rect.collidepoint(mouse_pos):
# Create a tuple with the new color and assign it.
rectangles[index] = (rect, new_color)
screen.fill((30, 30, 30))
# Now draw the rects. You can unpack the tuples
# again directly in the head of the for loop.
for rect, color in rectangles:
pg.draw.rect(screen, color, rect)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
sys.exit()

How can I implement a growing bar in pygame?

I'm trying to make a heat bar which grows every time I press 'x' and I can't figure out how. What can I do?
heatBar = [45, 30]
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_x:
for pos in heatBar:
pygame.draw.rect(DISPLAYSURF, GREEN,(pos[0],pos[1],10,50))
You can pass an area argument to Surface.blit. The area has to be a rect or rect-style tuple (x_coord, y_coord, width, height) and allows you to control the visible area of the surface. So if you have a surface that is 150 pixels wide and pass a rect with a width of 100, then the surface will only be rendered up to the 100 pixel border.
Now just use the heat value (which is the percentage of the image width) to calculate the current width, heat_rect.w/100*heat, and pass it to the blit method.
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
BG_COLOR = pg.Color(30, 30, 50)
HEAT_BAR_IMAGE = pg.Surface((150, 20))
color = pg.Color(0, 255, 0)
# Fill the image with a simple gradient.
for x in range(HEAT_BAR_IMAGE.get_width()):
for y in range(HEAT_BAR_IMAGE.get_height()):
HEAT_BAR_IMAGE.set_at((x, y), color)
if color.r < 254:
color.r += 2
if color.g > 1:
color.g -= 2
def main():
clock = pg.time.Clock()
heat_rect = HEAT_BAR_IMAGE.get_rect(topleft=(200, 100))
# `heat` is the percentage of the surface's width and
# is used to calculate the visible area of the image.
heat = 5 # 5% of the image are already visible.
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
keys = pg.key.get_pressed()
if keys[pg.K_x]:
heat += 4 # Now 4% more are visible.
heat -= 1 # Reduce the heat every frame.
heat = max(1, min(heat, 100)) # Clamp the value between 1 and 100.
screen.fill(BG_COLOR)
screen.blit(
HEAT_BAR_IMAGE,
heat_rect,
# Pass a rect or tuple as the `area` argument.
# Use the `heat` percentage to calculate the current width.
(0, 0, heat_rect.w/100*heat, heat_rect.h)
)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()

inconsistent motion with pygame

I am having a problem where i am trying to get a circle to go to the same spot every time i execute the program. But each time I run the code, the dot doesn't always line up. I have a test circle in the same place to compare run to run. The Red circle should cover the white circle perfectly but it changes every time i run the program. I am reseting the kernal as i am using pygame.time.get_ticks() to time everything.
import sys, pygame, math
from pygame.locals import *
# set up a bunch of constants
BLUE = ( 0, 0, 255)
WHITE = (255, 255, 255)
ORANGE = (255, 165, 0)
PINK = (255, 20, 147)
RED = (255, 0, 0)
GREEN = ( 0, 255, 0)
LIMEGREEN = ( 50, 205, 50)
YELLOW = (255, 255, 0)
PURPLE = (160, 32, 240)
BLACK = ( 0, 0, 0)
#Background Colour
BGCOLOR = BLACK
#Setting Window Size and finding window x and y centre
WINDOWWIDTH = 1918# width of the program's window, in pixels 960x540
WINDOWHEIGHT = 1078# 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
# frames per second to run at
FPS = 60
#intializing Variables
AMPLITUDE = 450
colourArray=[BLUE,WHITE,YELLOW,GREEN,RED,PINK,PURPLE,LIMEGREEN,ORANGE]
i=0
xPos = 0
step = 0
small_step =0
stop_step=step=0
xPos=0
yPos=0
c=RED
timestep=0
# standard pygame setup code
pygame.init()
FPSCLOCK = pygame.time.Clock()
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT),pygame.FULLSCREEN)
pygame.display.set_caption('Task1')
fontObj = pygame.font.Font('freesansbold.ttf', 16)
# 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()
#setup for label and time
tempTime=pygame.time.get_ticks()/1000
time_string=str(tempTime)
instructionsSurf = fontObj.render(time_string, True, WHITE, BGCOLOR)
instructionsRect = instructionsSurf.get_rect()
instructionsRect.left = 10
instructionsRect.bottom = WINDOWHEIGHT - 10
# fill the screen to draw from a blank state
DISPLAYSURF.fill(BGCOLOR)
DISPLAYSURF.blit(instructionsSurf, instructionsRect)
tempTime=pygame.time.get_ticks()/1000
#Color change loop
c=RED
if (0<=(tempTime)<3):
c=RED
if (3<=(tempTime)<5):
c=BLUE
if (5<=(tempTime)<7):
c=GREEN
if (7<=(tempTime)<9):
c=YELLOW
if (9<=(tempTime)<11):
c=WHITE
if (11<=(tempTime)<17):
c=RED
if (17<=(tempTime)<42):
c=RED
if (42<=(tempTime)<46):
c=RED
if (46<=(tempTime)<120):
c=colourArray[i]
#Setting position of x and y coordinates
if (0<=(tempTime)<14):
xPos = 0
yPos = 0
if (14<(tempTime)<17):
small_step += 5.111
xPos = small_step
yPos = 0
if (17<(tempTime)<43):
step += 0.05001
step %= 2 * math.pi
xPos = math.cos(step) * AMPLITUDE
yPos = math.sin(step) * AMPLITUDE
if (43<(tempTime)<46):
stop_step=step
xPos = math.cos(stop_step) * AMPLITUDE
yPos = math.sin(stop_step) * AMPLITUDE
if (46<(tempTime)<120):
step += 0.05001
step %= 2 * math.pi
xPos = math.cos(step) * AMPLITUDE
yPos = math.sin(step) * AMPLITUDE
#test dot
pygame.draw.circle(DISPLAYSURF, WHITE, (WIN_CENTERX+AMPLITUDE, 0+WIN_CENTERY),12,0)
# draw dot1
dot1=pygame.draw.circle(DISPLAYSURF, c, (int(xPos)+ WIN_CENTERX, int(yPos) + WIN_CENTERY), 12,0)
# draw dot2
dot2=pygame.draw.circle(DISPLAYSURF, BLACK, (int(xPos) + WIN_CENTERX, int(yPos) + WIN_CENTERY), 6,0)
#refresh
pygame.draw.rect(DISPLAYSURF, BLACK, (0, 0, WINDOWWIDTH, WINDOWHEIGHT), 1)
pygame.display.update()
FPSCLOCK.tick(FPS)
I've only scanned your code but I would guess your inconsistency is due to a high frame rate (60). FPSCLOCK.tick(FPS) will make sure you go up to 60, but does not mean you will go 60 fps. So if your computer can not handle 60 frames per second, it will go below 60 frames.
Cristph Terasa's recommendation of using busy_loop should do the job for ya, but I personally have no experience for it and want to share a method of normalizing game speed across different FPSs.
Rather than reinventing a wheel, here's a link to a question that explains it. I recommend the second answer, written by pmoleri.
In Pygame, normalizing game-speed across different fps values
This solution should help your game run at the same speed no matter the frame rate.

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