how to display a players final time in pygame - python

I have created a memory card matching game where a player has to match up 30 cards on the screen. There is a timer on the screen which goes up by 1 every second. I want it so once the player has matched up all 15 pairs, the game ends and the players time, so how long they took to complete it is displayed in another screen which says - "Your Time was ..." .
So far i have coded everything , i have a timer that works perfectly fine and all the matching aspects is good. However, once the game ends and the text is displayed on the final winning screen, the Timer is still going and so i cant get the players final time. For example, if the player took 60 seconds to complete all pairs it should say "Your Time was 60 seconds!!". However even after the game is ended the timer is still going. Here is my code so far:
# build a guessing game!
import random
import pygame
global options_list, spaces, used, new_board, first_guess, second_guess, first_guess_num, second_guess_num, score, matches, game_over, rows, cols, correct, time
pygame.init()
# game variables and constants
screen_width = 600
screen_height = 600
white = (255, 255, 255)
black = (0, 0, 0)
orange = (255,97,3)
turquoise = (0, 206, 209)
green = (0, 255, 0)
fps = 60
timer = pygame.time.Clock()
rows = 5
cols = 6
correct = [[0, 0, 0, 0, 0, 0,],
[0, 0, 0, 0, 0, 0,],
[0, 0, 0, 0, 0, 0,],
[0, 0, 0, 0, 0, 0,],
[0, 0, 0, 0, 0, 0]]
options_list = []
spaces = []
used = []
new_board = True
first_guess = False
second_guess = False
#Guess is assigned an index value. Checks what number is clicked
first_guess_num = 0
second_guess_num = 0
score = 0
matches = 0
game_over = False
cards_left_covered = 0
#timer start time
start_time = pygame.time.get_ticks()
time = 0
# create screen
screen = pygame.display.set_mode([screen_width, screen_height])
pygame.display.set_caption('Memory Game!')
#sets fonts
large_font = pygame.font.Font('freesansbold.ttf', 56)
small_font = pygame.font.Font('freesansbold.ttf', 26)
def generate_board():
global options_list, spaces, used
for item in range(rows * cols // 2):
options_list.append(item)
# goes through list of options between 1-24. Guarantees that only 2 cards can be selected
for item in range(rows * cols):
card = options_list[random.randint(0, len(options_list) - 1)]
spaces.append(card)
if card in used:
used.remove(card)
options_list.remove(card)
else:
used.append(card)
#Sets background colours and shapes
def draw_backgrounds():
global start_time, time
top_menu = pygame.draw.rect(screen, orange, [0, 0, screen_width, 75])
score_text = small_font.render(f'Player 1 score : {score}', True, white)
screen.blit(score_text, (20, 20))
board_space = pygame.draw.rect(screen, turquoise, [0, 100, screen_width, screen_height - 200], 0)
bottom_menu = pygame.draw.rect(screen, orange, [0, screen_height - 100, screen_width, 100], 0)
time = 0 + int((pygame.time.get_ticks() + start_time) / 1000)
time_text = small_font.render(f'Your time: {time}', True, white)
screen.blit(time_text, (20, 550))
def draw_cards():
global rows, columns, correct
card_list = []
for i in range(cols):
for j in range(rows):
#draws the cards and sets their size and position
card = pygame.draw.rect(screen, orange,[i * 85 + 48, j *78 + 110, 61, 65], 0, 4)
card_list.append(card)
## randomly adds numbers onto the cards. to make sure that the black numbers dont populate instantly when game is created
'''card_text = small_font.render(f'{spaces[i * rows + j]}', True, black)
screen.blit(card_text, (i * 75 + 18, j * 65 + 120))'''
for r in range(rows):
for c in range(cols):
if correct[r][c] == 1:
#creates green border around cards when match is made
pygame.draw.rect(screen, green, [c * 85 + 48, r * 78 + 110, 61, 65], 3, 4)
card_text = small_font.render(f'{spaces[c * rows + r]}', True, black)
screen.blit(card_text, (c * 85 + 55, r * 78 + 125))
return card_list
def check_guesses(first, second):
global spaces, correct, score, matches
if spaces[first] == spaces[second]:
#floor division
col1 = first // rows
col2 = second // rows
row1 = first - (first // rows * rows)
row2 = second - (second // rows * rows)
#checks for match and score incremented by 1
if correct[row1][col1] == 0 and correct[row2][col2] == 0:
correct[row1][col1] = 1
correct[row2][col2] = 1
score += 1
matches += 1
running = True
while running:
timer.tick(fps)
screen.fill(turquoise)
if new_board:
generate_board()
new_board = False
draw_backgrounds()
board = draw_cards()
if first_guess and second_guess:
check_guesses(first_guess_num, second_guess_num)
##delays code for miliseconds to see second guess
pygame.time.delay(1000)
first_guess = False
second_guess = False
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
for i in range(len(board)):
button = board[i]
#can guess as long as it's not game over
if not game_over:
if button.collidepoint(event.pos) and not first_guess:
first_guess = True
first_guess_num = i
##ensures that the same card cannot be clicked twice
if button.collidepoint(event.pos) and not second_guess and first_guess and i != first_guess_num:
second_guess = True
second_guess_num = i
#Checks for game over
if matches == rows * cols // 2:
game_over = True
winner = pygame.draw.rect(screen, turquoise,[0, 0, 600, 600])
winner_text = large_font.render(f'YOUR TIME WAS {end_time} !!', True, orange)
screen.blit(winner_text, (70, screen_height - 350))
#allows card to be flipped to show number
if first_guess:
card_text = small_font.render(f'{spaces[first_guess_num]}', True, black)
location = (first_guess_num // rows * 85 + 55, (first_guess_num - (first_guess_num // rows * rows)) * 78 + 125)
screen.blit(card_text, (location))
if second_guess:
card_text = small_font.render(f'{spaces[second_guess_num]}', True, black)
location = (second_guess_num // rows * 85 + 55, (second_guess_num - (second_guess_num // rows * rows)) * 78 + 125)
screen.blit(card_text, (location))
pygame.display.flip()
pygame.quit()

Just set end_time when the game state changes to game_over:
while running:
# [...]
if matches == rows * cols // 2:
if not game_over:
end_time = time
game_over = True
winner = pygame.draw.rect(screen, turquoise,[0, 0, 600, 600])
winner_text = large_font.render(f'YOUR TIME WAS {end_time} !!', True, orange)
screen.blit(winner_text, (70, screen_height - 350))

Related

PyGame: Why isn't my object moving automatically

I want 'rock' to be able to automatically move to the left when running the program but nothing happens, from my understanding I have made it so that the rocks x position moves by 3 every iteration
import pygame
p = pygame.display.set_mode((900, 600))
pygame.display.set_caption("First game")
FPS = 60
WHITE = 255, 255, 255
RED = 255, 0, 0
GREEN = 0, 255, 0
BLUE = 0, 0, 255
DBLUE = 57, 64, 90
board1 = pygame.image.load("board.png")
board2 = pygame.image.load("board.png")
rock = pygame.image.load("rock.png")
def draw_window(board1_move, board2_move, rock_scaled):
p.fill(DBLUE)
board1_scaled = pygame.transform.rotate(pygame.transform.scale(board1, (55, 40)), 40)
board2_scaled = pygame.transform.rotate(pygame.transform.scale(board2, (55, 40)), 40)
rock_scaled = pygame.transform.rotate(pygame.transform.scale(rock, (55, 40)), 0)
p.blit(board1_scaled, (board1_move.x, board1_move.y))
p.blit(board2_scaled, (board2_move.x, board2_move.y))
p.blit(rock_scaled, (400, 250))
pygame.display.update()
keypress = pygame.key.get_pressed()
def board1_move_func(keypress, board1_move):
if keypress[pygame.K_w] and board1_move.y > 0:
board1_move.y -= 3
if keypress[pygame.K_s] and board1_move.y < 530:
board1_move.y += 3
def board2_move_func(keypress, board2_move):
if keypress[pygame.K_UP] and board2_move.y > 0:
board2_move.y -= 3
if keypress[pygame.K_DOWN] and board2_move.y < 530:
board2_move.y += 3
def main():
rock_x = 450
rock_y = 250
board1_move = pygame.Rect(20, 250, 55, 40)
board2_move = pygame.Rect(805, 250, 55, 40)
rock_move = pygame.Rect(rock_x, rock_y, 55, 40)
clock = pygame.time.Clock()
run = True
while run == True:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keypress = pygame.key.get_pressed()
rock_x += 3
board1_move_func(keypress, board1_move)
board2_move_func(keypress, board2_move)
draw_window(board1_move, board2_move, rock_move)
pygame.quit()
if __name__ == "__main__":
main()
You change rock_x, but draw the object at the position stored in rock_move. rock_move is not magically tied with rock_x. You can update rock_move after changing rock_x:
rock_x += 3
rock_move.x = rock_x
However, I recommend changing rock_move instead of rock_x:
rock_move.x += 3

How to restart a pygame game with user input and also keep record of score?

I have made a game HANGMAN. I want to restart the game with user input if they want to. I have many fuctions so while loops would not work. I have to keep the record of the score also. I have also variables which need to be reset. Like this one.
word = random.choice(words.final_words).upper()
It choose a word from a huge list of words. That list is in another file named words.py.
hangman_status = 6
This tells at which stage the hangman is. It need to be reset to 0.
There are many variables like this.
This is the whole code if you are intersted to see.
import pygame, math, random, words
# setup display
pygame.init()
WIDTH, HEIGHT = 800, 500
win = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Hangman')
# load images
images = []
for i in range(7):
image = pygame.image.load(r'C:\Users\hp\Pygame\Hangman\hangman' + str(i) + '.png')
images.append(image)
# colours
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
# game variables
hangman_status = 0
word = random.choice(words.final_words).upper()
guessed = []
# fonts
LETTER_FONT = pygame.font.Font(r'C:\Users\hp\AppData\Local\Microsoft\Windows\Fonts\Azonix.otf', 30)
WORD_FONT = pygame.font.Font(r'C:\Users\hp\AppData\Local\Microsoft\Windows\Fonts\Azonix.otf', 35)
RESULT_FONT = pygame.font.Font(r'C:\Users\hp\AppData\Local\Microsoft\Windows\Fonts\Azonix.otf', 45)
# button variables
RADIUS = 20
GAP = 15
let_pos = []
startx = round((WIDTH - (GAP + RADIUS * 2) * 13) / 2)
starty = 380
A = 65
for i in range(26):
x = startx + GAP * 2 + ((RADIUS * 2 + GAP) * (i % 13))
y = starty + ((i // 13) * (GAP + RADIUS * 2))
let_pos.append([x, y, chr(A + i), True])
# setup game loop
FPS = 60
clock = pygame.time.Clock()
run = True
# draw
def draw():
win.fill(WHITE)
text = RESULT_FONT.render('Hangman', 1, BLACK)
win.blit(text, (int(WIDTH/2) - int(text.get_width()/2), 20))
# draw word
display_word = ""
for letter in word:
if letter in guessed:
display_word += letter + ' '
else:
display_word += '_ '
text = WORD_FONT.render(display_word, 1, BLACK)
win.blit(text, (340, 200))
# draw buttons
for letter in let_pos:
x, y, ltr, visible = letter
if visible:
pygame.draw.circle(win, BLACK, (x, y), RADIUS, 2)
text = LETTER_FONT.render(ltr, 1, BLACK)
win.blit(text, (x - int(text.get_width()/2), y - int(text.get_height()/2)))
win.blit(images[hangman_status], (100, 80))
pygame.display.update()
# display word
def last_word():
win.fill(BLACK)
text = RESULT_FONT.render(f'The word was: {word}', 1, WHITE)
win.blit(text, (int(WIDTH/2 - int(text.get_width()/2)), int(HEIGHT/2) - int(text.get_height()/2)))
pygame.display.update()
# result
def display_message(message):
win.fill(BLACK)
text = RESULT_FONT.render(message, 5, WHITE)
win.blit(text, (int(WIDTH/2) - int(text.get_width()/2), int(HEIGHT/2) - int(text.get_height()/2)))
pygame.display.update()
pygame.time.delay(3000)
# game loop
while run:
clock.tick(FPS)
draw()
# events
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
m_x, m_y = pygame.mouse.get_pos()
for letter in let_pos:
x, y, ltr, visible = letter
if visible:
dis = math.sqrt((m_x- x)**2 + (m_y - y)**2)
if dis <= RADIUS:
letter[3] = False
guessed.append(ltr)
if ltr not in word:
hangman_status += 1
won = True
for letter in word:
if letter not in guessed:
won = False
# check if the player lost won
if won:
last_word()
pygame.time.delay(2000)
display_message('You WON!')
break
# check if the player lost
if hangman_status == 6:
pygame.time.delay(1000)
last_word()
pygame.time.delay(4000)
display_message('You Lost.')
break
pygame.quit()
print(' '.join(word.split(' ')).title())
Why don't you make your entire game/round run inside a function?
You could pass in whatever arguments you need to.
eg:
def game_round():
# Preset initial vars if not passing to func:
total_score = 0
while run:
# Have run_game return score and a bool indicating user wants to restart/not.
round_score, restart = run_game()
if restart:
total_score = 0
# Whatever variables you want to reset, do this here too
else:
total_score += round_score

Random generated platforms seems to be hollow (Python - pygame) [duplicate]

This question already has answers here:
How to detect collisions between two rectangular objects or images in pygame
(1 answer)
How do I detect collision in pygame?
(5 answers)
Closed 2 years ago.
The random generated platforms are displayed yet the character still goes through the platforms as if they weren't there.
I'm a complete newbie at pygame (im just watching a bunch of random tutorials on youtube)
Here's the main script:
import pygame as pg
import random
from settings import *
from sprites import *
class Game:
def __init__(self):
# initialize game window, etc.
pg.init()
pg.mixer.init()
self.screen = pg.display.set_mode((width, height), pg.FULLSCREEN)
pg.display.set_caption(title)
self.clock = pg.time.Clock()
self.running = True
self.game_over = False
self.font_name = pg.font.match_font(font_name)
self.score = 0
self.orig_pos = 0
self.sequence = []
self.newPlatform = 90 + height
self.newPlatformInterval = 100
def new(self):
# starting a new game
self.score = 0
self.all_sprites = pg.sprite.Group()
self.platforms = pg.sprite.Group()
for platform in platform_list:
p = Platform(*platform)
self.all_sprites.add(p)
self.platforms.add(p)
self.player = Player(self)
self.all_sprites.add(self.player)
self.run()
def run(self):
# Game loop
while self.running:
self.clock.tick(fps)
self.events()
self.update()
self.draw()
def update(self):
# game loop update
# Plan here was to spawn platforms with specific number of gaps
# The platforms has to be aligned, and has to have enough room for the ball to fall and navigate through
# ISSUE: Platforms are hollow after the initial pre-placed platforms. Still looking into this issue
# Open for suggestions
self.player.update()
# self.platforms.update()
self.timePoint = pg.time.get_ticks()
# check if player hits a platform - only if falling!
for platform in self.platforms:
platform.rect.y -= 2
if platform.rect.top <= -30:
platform.kill()
if self.player.vel.y > 0:
hits = pg.sprite.spritecollide(self.player, self.platforms, False)
self.score += 1
if hits:
self.player.pos.y = hits[0].rect.top - 2
self.player.vel.y = 0
# if player reaches top 1/4 of the screen
"""if self.player.rect.top <= height / 4:
self.player.pos.y += abs(self.player.vel.y)"""
if self.player.rect.bottom > (height / 4) * 3:
for sprite in self.all_sprites:
sprite.rect.y -= max(self.player.vel.y, 10)
"""if sprite.rect.bottom < 10:
sprite.kill()"""
for sprite in self.platforms:
if sprite.rect.bottom < 0:
sprite.kill()
if self.player.rect.top < 0:
self.game_over = True
if len(self.platforms) < 12:
gaps = random.randint(1, 5)
gaps_1 = [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
gaps_2 = [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
gaps_3 = [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1]
gaps_4 = [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1]
if gaps == 1:
random.shuffle(gaps_1)
self.sequence = gaps_1
elif gaps == 2:
random.shuffle(gaps_2)
self.sequence = gaps_2
elif gaps == 3:
random.shuffle(gaps_3)
self.sequence = gaps_3
elif gaps == 4:
random.shuffle(gaps_4)
self.sequence = gaps_4
else:
random.shuffle(gaps_2)
self.sequence = gaps_2
for x in self.sequence:
if x == 1:
p = Platform(self.orig_pos, self.newPlatform, width / 12, 20)
self.platforms.add(p)
self.all_sprites.add(p)
self.platforms.update()
self.orig_pos += width / 12
else:
self.orig_pos += width / 12
# Initial plan was to spawn new platforms to keep same average number
# wide = width / 8
# p = Platform(random.randrange(0, width - wide), random.randrange(120 + height, 350 + width), wide, 20)
"""p = Platform(random.randrange(0, width / 4), 120 + height, wide, 20)
p1 = Platform(random.randrange(100 + width / 4, width), 120 + height, wide, 20)
self.platforms.add(p)
self.platforms.add(p1)
self.all_sprites.add(p)
self.all_sprites.add(p1)"""
def events(self):
# Game loop - EVENTS
for event in pg.event.get():
# check for closing window
if event.type == pg.QUIT:
self.running = False
if event.type == pg.KEYDOWN:
if event.key == pg.K_ESCAPE:
self.running = False
elif event.key == pg.K_r:
self.game_over = False
self.new()
keys = pg.key.get_pressed()
if keys[pg.K_ESCAPE]:
self.running = False
if keys[pg.K_r]:
self.game_over = False
self.new()
def draw(self):
# Game loop for drawing graphics
if not self.game_over:
self.screen.fill(gray)
self.all_sprites.draw(self.screen)
self.draw_text(str(self.score), 22, white, width / 2, 50)
else:
self.screen.fill(dark_red)
self.draw_text('GAME OVER', 26, white, width / 2, height / 2 - 30)
self.draw_text('Press ESC to exit the game.', 24, white, width / 2, height / 2)
self.draw_text('Press \'r\' to restart the game.', 24, white, width / 2, height / 2 + 30)
# *after* drawing everything, flip the display
pg.display.flip()
def show_start_screen(self):
# show splash / start screen
pass
def show_go_screen(self):
# show game over / continue
pass
def draw_text(self, text, size, color, x, y):
# function for drawing the text on the screen
font = pg.font.Font(self.font_name, size)
text_surface = font.render(text, True, color)
text_rect = text_surface.get_rect()
text_rect.midtop = (x, y)
self.screen.blit(text_surface, text_rect)
g = Game()
g.show_start_screen()
while g.running:
if not g.game_over:
g.new()
g.show_go_screen()
pg.quit()
I have no idea what I did wrong.
(im also open for suggestions :D )
EDIT: I edited the code a little bit (i added a limit where it shouldnt spawn platforms if there are already 24 platforms displayed. But now the platforms are no longer showing up.

How to update elements on display after passed time?

I got a program in Pygame which allows me to show up elements from the list list. Each 3 seconds, it updates and displays the next element from list.
My problem is that elements are overlapping on screen, but I want to update it each time 3 seconds have passed. I already used:
pygame.display.update()
but it does not work.
list = ["x", "y", "z"]
if time > 3 and i < len(list):
font = pygame.font.SysFont("comicsansms", 72)
text2 = font.render(str(list[i]), True, (0, 128, 0))
screen.blit(text2,
(430 - text2.get_width() // 1, 220 - text2.get_height() // 2))
pygame.display.update()
pygame.display.flip()
clock.tick(30)
i = i + 1
Here's something that updates what's displayed every three seconds:
import sys
import time
import pygame
from pygame.locals import *
pygame.init()
FPS = 30
WINDOWWIDTH = 640
WINDOWHEIGHT = 480
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
clock = pygame.time.Clock()
font = pygame.font.SysFont("comicsansms", 72)
screen = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
pygame.display.set_caption('Test')
my_list = ["x", "y", "z"]
bkgr = BLACK
i = len(my_list) - 1 # Index of last elememnt (so first is next displayed).
start_time = 0
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if (time.time() - start_time) > 3: # 3 seconds since last update?
i = (i + 1) % len(my_list)
start_time = time.time()
screen.fill(bkgr)
text2 = font.render(str(my_list[i]), True, (0, 128, 0))
screen.blit(text2, (430 - text2.get_width() // 1,
220 - text2.get_height() // 2))
pygame.display.update()
clock.tick(FPS)

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