I'm currently working on a Space Ship Simulator project and I came across a problem. In my code I have an Arrow class and I create 4 arrow objects that are going to be buttons.
This is my code:
from pygame.locals import *
pygame.init()
SCR_WIDTH = 700
SCR_HEIGHT = 900
screen = pygame.display.set_mode((SCR_WIDTH, SCR_HEIGHT))
pygame.display.set_caption("Space Ship Simulator")
# image loads
bg_image = pygame.image.load("img/space_background.jpg")
bg_image = pygame.transform.scale(bg_image, (SCR_WIDTH, SCR_HEIGHT))
ss_board = pygame.image.load("img/spaceship_board.png")
class Arrow:
def __init__(self, degree, x, y):
self.degree = degree
self.active = 0
self.arrow_list = []
self.x = x
self.y = y
self.active = 0
self.released = True
# arrow color: R: 0, G: 234, B: 0
for x in range(2):
img = pygame.image.load(f"img/arrow_{x}.png")
self.arrow_list.append(img)
self.image = pygame.transform.rotate(self.arrow_list[self.active], self.degree)
self.width = self.image.get_width()
self.height = self.image.get_height()
self.rect = Rect((self.x, self.y), (self.width, self.height))
def draw(self):
# get mouse position
mouse_pos = pygame.mouse.get_pos()
# check mouseover and clicked conditions
if self.rect.collidepoint(mouse_pos):
if pygame.mouse.get_pressed()[0] == 1 and self.active == 0 and self.released:
self.active = 1
self.released = False
print('Active')
if pygame.mouse.get_pressed()[0] == 0:
self.released = True
if self.active and self.released:
if pygame.mouse.get_pressed()[0] == 1:
self.active = 0
print('Inactive')
self.released = False
# draw arrow on screen
self.image = pygame.transform.rotate(self.arrow_list[self.active], self.degree)
screen.blit(self.image, (self.x, self.y))
arrowRight = Arrow(0, 553, 700)
arrowLeft = Arrow(180, 425, 700)
arrowUp = Arrow(91, 440, 776)
arrowDown = Arrow(271, 557, 779)
run = True
while run:
# background
screen.blit(bg_image, (0, 0))
screen.blit(ss_board, (0, 200))
# draw arrows
arrowRight.draw()
arrowLeft.draw()
arrowUp.draw()
arrowDown.draw()
# event handlers
for event in pygame.event.get():
# quit game
if event.type == pygame.QUIT:
run = False
pygame.display.update()
pygame.quit()
I have two requirements for the buttons:
If I click a button that is active, it should be deactivated again. I already managed to implement this.
If I click a button that is not active, it should be activated, and every other active button should be deactivated.
How can I implement the second requirement? Right now, it is possible for all four buttons to be active at the same time.
You need to link the buttons. Create a list of the buttons and set the list of buttons to each button object:
class Arrow:
def __init__(self, degree, x, y):
self.linkedButtons = []
self.active = 0
# [...]
arrowList = [
Arrow(0, 553, 700)
Arrow(180, 425, 700)
Arrow(91, 440, 776)
Arrow(271, 557, 779)
]
for arrow in arrowList:
arrow.linkedButtons = arrowList
Deactivate all buttons before you activate a button:
class Arrow:
# [...]
def draw(self):
# get mouse position
mouse_pos = pygame.mouse.get_pos()
# check mouseover and clicked conditions
if self.rect.collidepoint(mouse_pos):
if pygame.mouse.get_pressed()[0] == 1 and self.active == 0 and self.released:
# deactivate all buttons
for button in self.linkedButtons:
button.active = 0
# activate this button
self.active = 1
self.released = False
print('Active')
# [...]
Draw the buttons in a loop:
run = True
while run:
# background
screen.blit(bg_image, (0, 0))
screen.blit(ss_board, (0, 200))
# draw arrows
for arrow in arrowList:
arrow.draw()
# event handlers
for event in pygame.event.get():
# quit game
if event.type == pygame.QUIT:
run = False
pygame.display.update()
pygame.quit()
i think you should make a list of array button like
arrow_list = [arrowRight,arrowLeft,arrowUp,arrowDown]
then in the game loop you can can function to do the rest of the work
def button_activation(arrow_list):
index = none
for i,arrow in enumerate(arrow_list):
if arrow.activate:
index = i
for i,arrow in enumerate(arrow_list):
if index != i:
arrow.activate = False
Related
I have an issue with my dialogue index updating but not updating the text drawn to the screen. If the player is within distance of the npc the space button should update the dialogue. Currently it does draw the first index of dialogue if the player collides with the npc but moving away doesn't change anything as well as entering space. npc_list should be updating the self.image and then redrawing them.
self.npcs = [ [[450,500,[]]],]
self.npc_dialogue = [['Hi','Bye']]
self.npc_dialogue_sizes = [(25,50),(25,50)]
self.npc_list = pygame.sprite.Group()
#x,y,size,dialog,path_list
for i,npc in enumerate(self.npcs[self.level]):
new_npc = Npc((npc[0],npc[1]),self.npc_dialogue_sizes,self.npc_dialogue[0],npc[2])
self.npc_list.add(new_npc)
def display_stages(self,screen):
clock = pygame.time.Clock()
running = True
#Break if exit goes into a cutscene maybe use -1 and then update the level.
dialog = False
npc = None
while running:
if self.stages.level>=len(self.stages.exits) or self.state!=0:
break
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE]:
if dialog and npc!=None:
#Update a single npc list dialogue
npc.update()
print('Update')
screen.fill(white)
dialog, npc = self.npc_dialog()
if dialog and npc!=None:
npc.draw_dialog(screen)
self.stages.draw(screen)
pygame.display.flip()
clock.tick(FPS)
def npc_dialog(self):
for npc in self.stages.npc_list:
gets_hit = npc.rect.colliderect(self.stages.player.rect)
if gets_hit:
return True, npc
return False, None
def draw(self,screen):
if self.level < len(self.boundaries):
screen.blit(self.image,self.rect)
self.npc_list.draw(screen)
class Npc(pygame.sprite.Sprite):
def __init__(self,position,size,dialog,path_list):
pygame.sprite.Sprite.__init__(self)
print(size,position,dialog)
self.sizes = size
self.image = pygame.Surface(self.sizes[0])
self.rect = self.image.get_rect()
self.rect.topleft = position
self.path_list = path_list
# rework size and position to n*elements with dialog size
positions = []
for i in range(len(size)):
positions.append(position)
self.dialog = Dialog(positions,size,dialog)
def update(self):
#self.dialog.update()
print(self.dialog.index,len(self.dialog.size))
if self.dialog.index < len(self.dialog.size)-1:
self.dialog.update()
def draw_dialog(self,screen):
print(self.dialog.dialog[self.dialog.index])
if self.dialog.index < len(self.dialog.size)-1:
print('Updating drawing')
text = self.dialog.font.render(self.dialog.dialog[self.dialog.index], True, white)
self.image.blit(text, (self.dialog.dialog_text_offset,self.dialog.dialog_text_offset))
class Dialog():
def __init__(self,position,size,dialog):
self.index = 0
self.dialog = dialog
self.sizes = size
self.size = self.sizes[self.index]
self.positions = position
self.pos = position[self.index]
self.image = pygame.Surface(self.size, pygame.SRCALPHA)
self.rect = pygame.Rect((0,0), self.size)
self.font = pygame.font.Font(None,32)
self.rect.topleft = self.pos
self.dialog_background = (0, 0, 255, 127)
self.dialog_text_offset = 5
def update(self):
self.size = self.sizes[self.index]
self.pos = self.positions[self.index]
self.index += 1
The list of size tuples is self.dialog.sizes, not self.dialog.size:
if self.dialog.index < len(self.dialog.size)-1:
if self.dialog.index < len(self.dialog.sizes)-1:
For some reason, my return isn't working. This is from a tutorial. When I download the file and edit it from there it works, but if I copy and paste it from the exact file it doesn't work. Sorry, I am a beginner - open to any suggestions
The is the tutorial I used:
https://www.youtube.com/watch?v=G8MYGDf_9ho
Code:
import pygame
import sys
pygame.init()
WinHeight = 600
WinWidth = 900
Window = pygame.display.set_mode((WinWidth,WinHeight))
#button class
class Button():
def __init__(self, x, y, image, scale):
width = image.get_width()
height = image.get_height()
self.image = pygame.transform.scale(image, (int(width * scale), int(height * scale)))
self.rect = self.image.get_rect()
self.rect.topleft = (x, y)
self.clicked = False
def draw(self, surface):
action = False
#get mouse position
pos = pygame.mouse.get_pos()
#check mouseover and clicked conditions
if self.rect.collidepoint(pos):
if pygame.mouse.get_pressed()[0] == 1 and self.clicked == False:
self.clicked = True
action = True
if pygame.mouse.get_pressed()[0] == 0:
self.clicked = False
#draw button on screen
surface.blit(self.image, (self.rect.x, self.rect.y))
return action
Good_ball_img = pygame.image.load("GoodBall.png")
Bad_ball_img = pygame.image.load("BadBall.png")
#Button instances
Good_ball = Button(100,100,Good_ball_img,2)
Bad_ball = Button(200,200,Bad_ball_img,3)
def drawwin():
Window.fill((202,241,208))
Good_ball.draw(Window)
Bad_ball.draw(Window)
pygame.display.update()
def Main():
run = True
while run:
if Good_ball.draw(Window):
print("green clicked")
if Bad_ball.draw(Window)=="t":
print("red clicked")
for event in pygame.event.get():
#quit game
if event.type == pygame.QUIT:
run = False
pygame.quit()
sys.exit()
drawwin()
checkpress()
if __name__ == "__main__":
Main()
Code works for me if I change order - first process all events later check mouse position.
Problema can be because PyGame updates values in pygame.mouse and pygame.key only if you run pygame.event.get() - so it may need pygame.event.get() or at least pygame.event.pump() before other functions.
for event in pygame.event.get():
#quit game
if event.type == pygame.QUIT:
run = False
pygame.quit()
sys.exit()
if Good_ball.draw(Window):
print("green clicked")
if Bad_ball.draw(Window):
print("red clicked")
EDIT:
In documentation for pygame.event there is
To get the state of various input devices, you can forego the event queue and
access the input devices directly with their appropriate modules: `pygame.mouse`,
`pygame.key` and `pygame.joystick`. If you use this method, remember
that pygame requires some form of communication with the system window manager
and other parts of the platform. To keep pygame in sync with the system,
you will need to call `pygame.event.pump()` to keep everything current.
Minimal working code - with surfaces instead images so everyone can simply copy and run it
import pygame
import sys
# --- constants --- # PEP8: `UPPER_CASE_NAMES`
WINDOW_WIDTH = 900
WINDOW_HEIGHT = 600
# --- classes --- # PEP8: `CamelCaseNames`
class Button():
def __init__(self, x, y, image, scale):
width = image.get_width()
height = image.get_height()
self.image = pygame.transform.scale(image, (int(width * scale), int(height * scale)))
self.rect = self.image.get_rect()
self.rect.topleft = (x, y)
self.clicked = False
def check_click(self):
action = False
# get mouse
pos = pygame.mouse.get_pos()
left_button = pygame.mouse.get_pressed()[0]
# check mouseover and clicked conditions
if left_button:
if self.rect.collidepoint(pos) and not self.clicked:
self.clicked = True
action = True
else:
self.clicked = False
return action
def draw(self, surface):
# draw button on screen
surface.blit(self.image, self.rect)
# --- functions --- # PEP8: `lower_case_names`
def draw_window(window):
window.fill((202, 241, 208))
good_ball.draw(window)
bad_ball.draw(window)
pygame.display.update()
# --- main ---
pygame.init()
window = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
# button class
#good_ball_img = pygame.image.load("GoodBall.png")
good_ball_img = pygame.Surface((100, 50))
good_ball_img.fill((0, 255, 0))
#bad_ball_img = pygame.image.load("BadBall.png")
bad_ball_img = pygame.Surface((100, 50))
bad_ball_img.fill((255,0,0))
# Button instances
good_ball = Button(100, 100, good_ball_img, 2) #
bad_ball = Button(200, 200, bad_ball_img, 3)
run = True
while run:
# - events -
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.quit()
sys.exit()
# - checks and updates -
if good_ball.check_click():
print("green clicked")
if bad_ball.check_click():
print("red clicked")
#checkpress()
# - draws -
draw_window(window)
PEP 8 -- Style Guide for Python Code
I am making a match-3 style game. I am a beginner with pygame and have managed to randomly generate a board so that each playthrough is different. My only issue at the moment is my tiles seem to be continuously cycling through the list I created for each image. I tried adding a for loop to delete each element as it is being generated but nothing I seem to change is working. Could anybody help? You can find the images I used in a google drive! I appreciate anything!
GoogleDrive
import pygame
from pygame.locals import *
import math
import time
import sys
import random
pygame.init()
# game variables
display_h = 900
display_w = 750
game_row = 12
game_column = 10
background_img = pygame.image.load('pink_background_resized.png')
clock = pygame.time.Clock()
running = True
# build screen and window name
screen = pygame.display.set_mode((display_w, display_h))
pygame.display.set_caption("Jelliez")
screen.fill((255,255,255))
class Tile():
# initialize tiles
def __init__(self, x, y, image, clicked):
self.image = image
self.rect = self.image.get_rect()
self.rect.topleft = (x,y)
self.clicked = clicked
def draw(self):
action = False
# get mouse position
pos = pygame.mouse.get_pos()
# check mouse over and clicked psotion
if self.rect.collidepoint(pos):
if (pygame.mouse.get_pressed()[0] == 1) and (self.clicked == False):
self.clicked = True
action = True
if pygame.mouse.get_pressed()[0] == 0:
self.clicked = False
screen.blit(self.image, (self.rect.x, self.rect.y))
return action
def generateBoard():
screen.blit(background_img, (0,0))
return [[image_list[random.randrange(0, len(image_list))] for i in range (game_column)] for x in range(game_row)]
def showBoard(board):
screen.blit(background_img, (0,0))
rowNum = 0
for row in board:
columnNum = 0
for shape in row:
screen.blit(shape, (70 * columnNum, 64 * rowNum ))
columnNum += 1
rowNum += 1
# add game images
blue_jelly_img = pygame.image.load('blue_jelly.png').convert_alpha()
gray_jelly_img = pygame.image.load('gray_jelly.png').convert_alpha()
green_jelly_img = pygame.image.load('green_jelly.png').convert_alpha()
pink_jelly_img = pygame.image.load('pink_jelly.png').convert_alpha()
red_jelly_img = pygame.image.load('red_jelly.png').convert_alpha()
yellow_jelly_img = pygame.image.load('yellow_jelly.png').convert_alpha()
# create tile istances
blue_jelly = Tile(100, 231, blue_jelly_img, False)
gray_jelly = Tile(100, 231, gray_jelly_img, False)
green_jelly = Tile(100, 231, green_jelly_img, False)
pink_jelly = Tile(100, 231, pink_jelly_img, False)
red_jelly = Tile(100, 231, red_jelly_img, False)
yellow_jelly = Tile(100, 231, yellow_jelly_img, False)
image_list = [blue_jelly_img, gray_jelly_img, green_jelly_img, pink_jelly_img, red_jelly_img, yellow_jelly_img]
for x in range(len(image_list) - 6):
del(image_list[0])
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
gameBoard = generateBoard()
showBoard(gameBoard)
pygame.display.update()
pygame.quit()
The problem is that the board is recreated every frame.
Generate the board once before the application loop. Clear the display and show the board in the application loop:
gameBoard = generateBoard()
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.blit(background_img, (0,0))
showBoard(gameBoard)
pygame.display.update()
pygame.quit()
I am trying to play my sound only once when my mouse is over the the green button but it doesn't seem to work — plays repeatedly.
mouseisover = True
if greenbutton2.isOver(pos):
window.blit(bls2,(0,0))
if mouseisover:
mouseover.play()
mouseisover = False
my whole game _intro code at the main loop for my game intro first I set a mouseisover variable and made it True then after in my main loop I said if thats True then play the sound then under it I said mouseisover = False but still it plays my sound again and again none stop
# start screen
def game_intro():
carsound = pygame.mixer.Sound("carsound1.wav") #
mouseisover = True
mouseover = pygame.mixer.Sound("lols (2).wav") # MOUSEOVERSOUND
class button():
def __init__(self, color, x,y,width,height, text=''):
self.color = color
self.x = x
self.y = y
self.width = width
self.height = height
self.text = text
def draw(self,window,outline=None):
#Call this method to draw the button on the screen
if outline:
pygame.draw.rect(window, outline, (self.x-2,self.y-2,self.width+4,self.height+4),0)
pygame.draw.rect(window, self.color, (self.x,self.y,self.width,self.height),0)
if self.text != '':
font = pygame.font.SysFont('comicsans', 60)
text = font.render(self.text, 1, (0,0,0))
window.blit(text, (self.x + (self.width/2 - text.get_width()/2), self.y + (self.height/2 - text.get_height()/2)))
def isOver(self, pos):
#Pos is the mouse position or a tuple of (x,y) coordinates
if pos[0] > self.x and pos[0] < self.x + self.width:
if pos[1] > self.y and pos[1] < self.y + self.height:
return True
return False
white = (250,250,250)
greenbutton = button((0,255,0),60,449,200,80, 'Click Me )')
greenbutton2 = button((0,255,0),50,518,200,80, 'Click Me )')
greenbutton3 = button((0,255,0),50,600,200,70, 'Click Me )')
bls2 = pygame.image.load("about2.png")
bls3 = pygame.image.load("credits25.png")
bls4 = pygame.image.load("playgame1.png")
def fade(width, height):
fade = pygame.Surface((width, height))
fade.fill((0,0,0))
for alpha in range(0, 100):
fade.set_alpha(alpha)
window.blit(fade, (0,0))
pygame.display.update()
pygame.time.delay(15)
def redraw():
bls = pygame.image.load("bgs.png")
window.blit(bls,(0,0))
# this makes it
snow_list=[]
no_of_circles=100;
clock = pygame.time.Clock()
FPS = 60
clock.tick(FPS)
for i in range(no_of_circles):
x = random.randrange(0, 800)
y = random.randrange(0, 700)
snow_list.append([x,y])
red = (200,0,0)
green = (255,250,250)
bright_red = (255,250,0)
bright_green = (0,255,0)
clock = pygame.time.Clock()
intro = True
while intro:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
intro = False
pygame.quit()
redraw()
pos = pygame.mouse.get_pos()
if event.type == pygame.MOUSEBUTTONDOWN:
if greenbutton.isOver(pos):
fade(800,800)
main_loop()
pos = pygame.mouse.get_pos()
if event.type == pygame.MOUSEBUTTONDOWN:
if greenbutton2.isOver(pos):
window.blit(bls2,(0,0))
fade(800,800)
menu()
if greenbutton2.isOver(pos):
window.blit(bls2,(0,0))
if mouseisover:
mouseover.play()
mouseisover = False
if greenbutton3.isOver(pos):
mouseover.play()
window.blit(bls3,(0,0))
if greenbutton.isOver(pos):
mouseover.play()
window.blit(bls4,(0,0))
pos = pygame.mouse.get_pos()
if event.type == pygame.MOUSEBUTTONDOWN:
if greenbutton3.isOver(pos):
fade(800,800)
creditss()
# GAME INTRO IMAGE
for point in snow_list:
point[1]+=1
pygame.draw.circle(window, (255,255,255), point, 2)
if(point[1] >= 600):
point[0] = random.randrange(0, 600)
point[1] = random.randrange(-10, -5)
clock.tick(FPS)
partics()
pygame.display.update()
It is difficult to answer without any more context, but my best guess is that this code is located in a loop.
This would mean that every time the loop runs, the variable mouseisover is assigned the value True, discarding the previous value.
Assuming this is your case you want to initialize your variable outside of the loop.
what your code looks like right now
while (loop):
mouseisover = True
if greenbutton2.isOver(pos):
window.blit(bls2,(0,0))
if mouseisover:
mouseover.play()
mouseisover = False
what you should do
mouseisover = True
while (loop):
if greenbutton2.isOver(pos):
window.blit(bls2,(0,0))
if mouseisover:
mouseover.play()
mouseisover = False
This, however, means that the sound will play only once and never again.
Update :
You can add a method like this to your button class :
def playSoundIfMouseIsOver(self, pos, sound):
if self.isOver(pos):
if not self.over:
sound.play()
self.over = True
else:
self.over = False
and then to make them beep simply call playSoundIfMouseIsOver with the position of the mouse and the sound you want to play
greenbutton2.playSoundIfMouseIsOver(pos, mouseover)
greenbutton3.playSoundIfMouseIsOver(pos, mouseover)
greenbutton.playSoundIfMouseIsOver(pos, mouseover)
This simply remembers the last value of isOver, and if it changes from False to True, it plays a sound.
These changes will require you to change every if that handled playing a sound with the new method
If you have more buttons you could place all your buttons inside a list and then iterate on the list to do the same action for every button
Creating the list (place this after creating the different buttons):
myButtonList = [greenbutton, greenbutton2, greenbutton3]
Iterating on that list (replaces the three similar lines) :
for myButton in myButtonList:
myButton.playSoundIfMouseIsOver(pos, mouseover)
Also, I saw some weird stuff in your code, you have 4 button classes that are the exact same. You can define the button class a single time by simply placing it at the very beginning of the program, outside of any loop.
If you place your class definitions inside loops, they will only be accessible inside that loop!
class menu:
hover = False
def __init__(self, text, pos):
self.text = text
self.pos = pos
self.set_rect()
self.draw()
def draw(self):
self.set_render()
screen.blit(self.render, self.rect)
def set_render(self):
self.render = subFont.render(self.text, True, self.get_color())
def get_color(self):
if self.hover:
return (BLACK)
else:
return (GREEN)
def set_rect(self):
self.set_render()
self.rect = self.render.get_rect()
self.rect.topleft = self.pos
select = [menu("Computer Virus", (100, 200)),
menu("Computer Crime", (100, 300)),
menu("QUIT", (100, 400))]
running = True
while running:
for evnt in event.get():
if evnt.type == QUIT:
running = False
screen.fill(WHITE)
title()
for menu in select:
if menu.rect.collidepoint(mouse.get_pos()):
menu.hover = True
else:
menu.hover = False
menu.draw()
pointer()
display.update()
This is my game menu where hovering over will allow you it to change colour
Im planning to make it so the that when you click on one of the options, it would bring you elsewhere. How do I find the position of the rect and with which text it the mouse collides with?
This code does what you want:
class menu:
hover = False
def __init__(self, text, pos, callback):
self.text = text
self.pos = pos
self.callback = callback # so we now what function to call
self.set_rect()
self.draw()
# the rest of your code
def quit_loop():
global running
running = False
select = [menu("Computer Virus", (100, 200), lambda: print("Computer Virus")), # Add a callback
menu("Computer Crime", (100, 300), lambda: print("Computer Crime")),
menu("QUIT", (100, 400), quit_loop)]
running = True
while running:
for evnt in event.get():
if evnt.type == QUIT:
running = False
if evnt.type == MOUSEBUTTONDOWN: # if a mousebutton got pressed
if evnt.button == 1: # if the first button got pressed
for menu in select:
if menu.rect.collidepoint(evnt.pos): # does this one collide
menu.callback() # call the callback