i don't really understand how do i get my choice buttons over my backscreen.surface
import pygame
import sys
pygame.init()
screen = pygame.display.set_mode((520, 680))
clock = pygame.time.Clock()
pygame.display.set_caption("everlasting night")
main_font = pygame.font.Font("/Users//Desktop/dpcomic.ttf", 65)
backscreen_surface = pygame.image.load("//Users//Desktop/snow.jpeg")
text_surface = main_font.render("go home?", False, "Black")
# choice buttons(i deleted the code because it make 0 sense)
while True:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
screen.blit(backscreen_surface, (0, 0))
screen.blit(text_surface, (140, 560))
pygame.display.update()
i tried different ways but what i ger is an only button without backscreen.surface
In pygame draw in order so that
screen.fill(...) # Background
pygame.draw.rect(...) # Draws on top
pygame.draw.rect(...) # Draws on top of both
# ... and so on
This will happend every frame, to make your code clearer I'd encourage you to have a draw function :
def draw():
# Your drawing code goes here
Now that you know that simply put the things in that order :
def draw():
# Fill you background
# Draw anything that should be under the buttons
# Draw your buttons
Your buttons will have multiple properties, for instance you may want to have pos, size, background_color, text.
Hence you should probably create a python dict to represent a button (or an class but I'm assuming you don't use classes yet)
For example, a button could look something like this :
yes_button = {
'pos': (0, 0), # x and y
'size': (80, 40), # width and height
'background_color': (0,255,0), # red, green and blue
'color' : (255,255,255), # text color
'text': 'yes'
}
And simply draw it like so :
def draw():
# ... your code before
# Draw yes button (4 steps)
# 1 - Create a rect object
yes_button_rect = pg.Rect(
yes_button['pos'],
yes_button['size']
)
# 2 - Draw the button's background
pg.draw.rect(
screen,
yes_button['background_color'],
yes_button_rect
)
# 3 - Render the text
button_text_render = main_font.render(
yes_button['text'],
False,
yes_button['color']
)
# 4 - Center and draw the text
button_center_x, button_center_y = yes_button_rect.center
text_width, text_height = button_text_render.get_size() # get the width and height of your text
button_text_x = button_center_x - text_width // 2
button_text_y = button_center_y - text_height // 2
screen.blit(button_text_render, (button_text_x, button_text_y))
Go further :
If you care about performance you should pre-render and store your text inside the button dict, you can also pre-calculate the text drawing pos and store it in the button dict.
For instance you could make a preload function :
def preload_button(button):
# Render the text and store it
button['text_render'] = main_font.render(
button['text'],
False,
button['color']
)
# Calc the button center pos
button_top, button_left = button['pos']
button_width, button_height = button['size']
button_center_x = button_left + button_width // 2
button_center_y = button_top + button_height // 2
# Calc the pos to the draw the text at
text_render_width, text_render_height = button['text_render'].get_size()
text_x = button_center_x - text_render_width // 2
text_y = button_center_y - text_render_height // 2
button['text_pos'] = (text_x, text_y)
If you do so your draw code becomes :
def draw():
# ... your previous code
# Draw yes button (2 steps)
# 1 - Draw the button's background
pg.draw.rect(
screen,
yes_button['background_color'],
(yes_button['pos'], yes_button['size'])
)
# 2 - Draw the text
screen.blit(yes_button['text_render'], yes_button['text_pos'])
Here I created just a yes button as an example but add as many as you want, placing them into a list :
buttons = [yes_button, no_button, maybe_button, ...]
Now you can interact with them using a for each loop
for button in buttons:
# your code
Obviously making a button class would make everything way clearer but it's outside the scope of this answer
Happy programming
Related
I am trying to code Blackjack using pygame. I am having difficulties trying to implement the results of the game.
eg.:
def results(sum_player, sum_dealer)
if sum_player == 21:
print("Blackjack! Player has" + str(sum_player) + "while dealer has:" + str(sum_dealer))
run = False)
I want a button or some kind of output in pygame that shows who won the game. Then I need the game to end or reset so i can play again.
Can anyone help me on that?
I want a button or some kind of output in pygame that shows who won the game. Then I need the game to end or reset so i can play again.
In order to display text on the screen in pygame, we will need to do a couple of things:
First, we need to create a font for the text. In order to do that, we can use the pygame.font.SysFont function. You must add pygame.init() after import pygame to use this function. For information on how to use this function, check out the documentation. Example:
my_font = pygame.font.SysFont("Arial", 50)
Next, we will render the text and save it to a variable.Example:
label = my_font.render("my text", True, (0, 0, 0)) --> (0, 0, 0) is black
Lastly, we will need to display the text on the screen using the window.blit function.
Example: window.blit(label, (50, 100))
Pygame doesn't include a prebuilt function for creating buttons, so we'll have to create one ourselves. In order to do that, I created a Button class that can display buttons. By using a class, we can use multiple buttons in the game without repeating code.
The Button Class:
class Button:
def __init__(self, surface, text, bg, fg, x, y, width, height):
self.surface = surface # the window
self.bg = bg # the color of the button
self.x = x
self.y = y
self.width = width
self.height = height
self.rect = pygame.Rect(self.x, self.y, self.width, self.height) # creating a rectangle for the button
font = pygame.font.SysFont("Arial", height//2, "bold") # creating the font for the button text
self.label = font.render(text, True, fg) # rendering the text (fg = color of button text)
def draw(self):
pygame.draw.rect(self.surface, self.bg, self.rect) # displaying the button rectangle
self.surface.blit(self.label, (self.x + self.width*0.25,
self.y + self.height*0.25)) # displaying the button text
To create a new button, just do the following:
button = Button(window, "play", (255, 0, 0), (0, 0, 0), 100, 200, 300, 80)
The parameters that it takes:
surface --> the window of your game
text --> the button text. For example: "Play"
bg --> the background color of the button
fg --> the color of the button text
x --> the x position of the button
y --> the y position of the button
width --> the width of the button
height --> the height of the button
The class also includes a function called draw, which is responsible for displaying the button.
Combining Everything:
import pygame
pygame.init()
window = pygame.display.set_mode((500, 500))
pygame.display.set_caption("My Game")
# colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
BLUE = (0, 0, 255)
class Button:
def __init__(self, surface, text, bg, fg, x, y, width, height):
self.surface = surface # the window
self.bg = bg # the color of the button
self.x = x
self.y = y
self.width = width
self.height = height
self.rect = pygame.Rect(self.x, self.y, self.width, self.height) # creating a rectangle for the button
font = pygame.font.SysFont("Arial", height//2, "bold") # creating the font for the button text
self.label = font.render(text, True, fg) # rendering the text (fg = color of button text)
def draw(self):
pygame.draw.rect(self.surface, self.bg, self.rect) # displaying the button rectangle
self.surface.blit(self.label, (self.x + self.width*0.25,
self.y + self.height*0.25)) # displaying the button text
def game():
# your game code
def results():
my_font = pygame.font.SysFont("Arial", 50, "bold") # creating the font for the text
label = my_font.render("Player 1 has won!", True, WHITE) # rendering the text in white
reset_button = Button(window, "RESET", BLUE, WHITE, 100, 200, 300, 80) # creating a blue "reset" button
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN: # checking if the mouse is down
if reset_button.rect.collidepoint(event.pos): # checking if the mouse clicked the reset button
game() # resetting the game
window.fill(BLACK) # sets the window color to black
window.blit(label, (50, 100)) # displaying the text on the screen
reset_button.draw() # showing the button on the screen
pygame.display.update() # updating the display
pygame.quit()
results()
I added comments to the code that explain certain lines.
I am trying do end credits like animation the code above for Title crawl, I am trying to make the following changes to it:- 1) The text should begin at the bottom of screen at certain location, such that no other text from the string should be displayed below that location on the screen. 2) The text should stop at certain location on top of screen such that the line at the top should be deleted as soon as it reaches that location making room for other lines in the string.
I am a python newbie, I am just experimenting with things, the following code doesn't belong to me either.
import pygame
from pygame.locals import *
pygame.init()
pygame.display.set_caption('Title Crawl')
screen = pygame.display.set_mode((1000, 800))
screen_r = screen.get_rect()
font = pygame.font.SysFont("franklingothicdemibold", 40)
clock = pygame.time.Clock()
def main():
crawl = ["Star Wars - The Wilds"," ","It is a dark time for the Galaxy. The evil Dark","Lord, Vitiate is rising to power. Alone, a single", "spec is on a trip, a trip that will ultimately", "rectify the wrongs of the galaxy. The keepers ", "of peace are dying out and the DARK SIDE is", "lurking, a conniving force determined to", "become the omniarch."]
texts = []
# we render the text once, since it's easier to work with surfaces
# also, font rendering is a performance killer
for i, line in enumerate(crawl):
s = font.render(line, 1, (229, 177, 58))
# we also create a Rect for each Surface.
# whenever you use rects with surfaces, it may be a good idea to use sprites instead
# we give each rect the correct starting position
r = s.get_rect(centerx=screen_r.centerx, y=screen_r.bottom + i * 45)
texts.append((r, s))
while True:
for e in pygame.event.get():
if e.type == QUIT or e.type == KEYDOWN and e.key == pygame.K_ESCAPE:
return
screen.fill((0, 0, 0))
for r, s in texts:
# now we just move each rect by one pixel each frame
r.move_ip(0, -1)
# and drawing is as simple as this
screen.blit(s, r)
# if all rects have left the screen, we exit
if not screen_r.collidelistall([r for (r, _) in texts]):
return
# only call this once so the screen does not flicker
pygame.display.flip()
# cap framerate at 60 FPS
clock.tick(60)
if __name__ == '__main__':
main()
Use set_clip() to set the clipping region of the display surface.
e.g. Clip 100 rows at the top and the bottom:
# set clipping rectangle
clip_rect = (0, 100, screen.get_width(), screen.get_height()-200)
screen.set_clip(clip_rect)
for r, s in texts:
# now we just move each rect by one pixel each frame
r.move_ip(0, -1)
# and drawing is as simple as this
screen.blit(s, r)
# cancel clipping for further drawing
screen.set_clip(None)
I dont know how I can use my button function to either overlay the background.jpg back over the buttons or wipe the current screen and put the background back in place after the scene has been cleared.
import pygame
pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
BLACK = (0, 0, 0)
BACKGROUND = (200, 230, 234)
WHITE = (255, 255, 255)
HOVER_COLOUR = (50, 70, 90)
# Text Variables
FONT = pygame.font.SysFont ("Times New Norman", 60)
TEXT = FONT.render ("", True, WHITE)
background_images = pygame.image.load("background.jpg").convert()
screen.blit(background_images, [0,0])
screen.blit(TEXT, (150, 50))
# Text & Rectangles construction
text1 = FONT.render("PlAY", True, WHITE)
text2 = FONT.render("CONTROLS", True, WHITE)
text3 = FONT.render("DIFFICULTY", True, WHITE)
text4 = FONT.render("SCOREBOARD", True, WHITE)
rect1 = pygame.Rect(250,200,300,80)
rect2 = pygame.Rect(250,300,300,80)
rect3 = pygame.Rect(250,400,300,80)
rect4 = pygame.Rect(250,500,300,80)
# The button construction arry. Text and Rectangle
buttons = [
[text1, rect1, BACKGROUND, 1],
[text2, rect2, BACKGROUND, 2],
[text3, rect3, BACKGROUND, 3],
[text4, rect4, BACKGROUND, 4],
]
# Function for button printing (testing)
def on_button(buttons):
print(buttons[3])
def game_intro():
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
elif event.type == pygame.MOUSEMOTION:
for button in buttons:
# Uses collisionpoint to detect mouse position collisions
if button[1].collidepoint(event.pos):
# Set the button's colour to the hover colour.
button[2] = HOVER_COLOUR
else:
# resets the colour to normal.
button[2] = BACKGROUND
# Button Controls
elif event.type == pygame.MOUSEBUTTONDOWN:
for button in buttons:
# Uses collisionpoint to detect mouse position collisions
if button[1].collidepoint(event.pos):
on_button(button)
if button == buttons[0]:
screen.fill(0,0,0)
# Draws the buttons with their current colours (normal & collisions)
for text, rect, colour, button_id in buttons:
pygame.draw.rect(screen, colour, rect)
screen.blit(text, rect)
pygame.display.flip()
clock.tick(15)
#Run Game
game_intro()
pygame.quit()
As you can see the operation:
if button == buttons[0]:
screen.fill(0,0,0)
Is what im currently working with. The if statement works fine and iv tested its feedback with print operations but i cannot work it with Pygame functions.
The issue is caused by
screen.fill(0,0,0)
because the 2nd parameter to pygame.Surface.fill() is assumed to be a rectangle (e.g. pygame.Rect), which limits the fill to a specific area.
The 1st parameter to pygame.Surface.fill() has to be a RGB sequence, RGBA sequence or a color index.
So it has to be
screen.fill( (0,0,0) )
or
screen.fill(0)
The buttons are still they, because they are drawn continuously in every frame:
for text, rect, colour, button_id in buttons:
pygame.draw.rect(screen, colour, rect)
screen.blit(text, rect)
Add a global state variable (play) which is set when the play button is pressed. Change the state in the function on_button, use the global statement to change the value of the globale variable play. Draw the scene dependent on the state:
play = False
def on_button(buttons):
global play
play = buttons[3] == 1
print(buttons[3], play)
def game_intro():
# [...]
if play:
screen.fill(0)
# [...]
else:
for text, rect, colour, button_id in buttons:
pygame.draw.rect(screen, colour, rect)
screen.blit(text, rect)
pygame.display.flip()
clock.tick(15)
To directly answer the question:
if button[1].collidepoint(event.pos):
on_button(button)
if button == buttons[0]:
screen.fill(0,0,0)
Check your indentation. For each button, the code does the .collidepoint check and possibly calls on_button, and then it also checks which button is being examined - regardless of the .collidepoint result.
if button[1].collidepoint(event.pos):
on_button(button)
if button == buttons[0]:
screen.fill(0,0,0)
Now the screen.fill only happens if both conditions are true - i.e. the button being examined is buttons[0], and the event.pos (i.e., the place where the user clicked) is inside that button.
But to deal with the problem - you really should use something more sophisticated to represent your buttons. Basically, what we would like to happen is for the on_button code to make the decision of what is done when the button is clicked, according to which button it is. To make that work smoothly, the buttons info needs to include something that tells on_button what to do.
Python allows us to do a neat trick here: names of things are just names, even if the thing being named is a function - and that means, for example, that we can put those names in a list, and then pull them out and use them to call the function. For example, if we had a function that explains what the Play button should do:
def do_play():
# up to you ;)
And then set up the button to store that name, instead of a button ID:
play_button = [text1, rect1, BACKGROUND, do_play]
Now we can have on_button figure it out for us:
def on_button(button):
button[3]()
When the .collidepoint test passes for that button, it gets passed to on_button, which looks up the function name do_play and calls that function. Simple :)
(The next level of sophistication is to use a class to represent the Button information instead of a plain list.)
You might also find some useful ideas here:
How to make buttons in python/pygame?
https://www.pygame.org/tags/button
I have created a definition called message_display(text) but when ever i use it in my code the text gets on top of each other in the lower left corner you see that hello and world is printed on top of eacht other. Your help is appreciated
import pygame
import random
import sys
pygame.init()
win = pygame.display.set_mode((800,700))
pygame.display.set_caption('Knights Of Dungeons')
gb = pygame.image.load('background.png')
font= pygame.font.SysFont('Gabriola', 30, False,True)
game = True
roll_dice = font.render('Press Enter To roll the dice' ,10,(0,0,0))
def dice():
x = random.randint(1,6)
return x
def message_display(text):
dis_text = font.render(text, 10, (0,0,0))
win.blit(dis_text,(10,650))
pygame.display.update()
player_1 = font.render('Name: Kaan '+ 'Health: 100
' + 'Damage: 0 ' + 'Armour: 0 ')
while game:
pygame.time.delay(50)
for event in pygame.event.get():
if event.type == pygame.QUIT:
game = False
keys = pygame.key.get_pressed()
if keys[pygame.K_RETURN]:
num_thrown = dice()
roll_dice = font.render( 'The number you have throwen is:
'+str(0+num_thrown) ,20,(0,0,0))
win.blit(gb,(0,15))
win.blit(player_1 ,(0,100))
win.blit(roll_dice,(455,650))
message_display('Hello')
message_display('world')
pygame.display.update()
pygame.quit()
When I use pygame, I like to make my text display function much more general. Something like this:
import pygame as pg
def draw_text(surface, text, size, color, x, y):
"""Draw text to surface
surface - Pygame surface to draw to
text - string text to draw
size - font size
color - color of text
x - x position of text on surface
y - y position of text on surface
"""
font = pg.font.Font(font_name, size)
text_surf = font.render(str(text), True, color)
text_rect = text_surf.get_rect()
text_rect.topleft = (x, y) # I use topleft here because that makes sense to me
# for English (unless you want it centered).
# But you can use any part of the rect to start drawing the text
surface.blit(text_surf, text_rect)
Note that you also have to set up the font_name. You can do this inside or outside the function (if you just want one). I've done this globally for my usecase like this:
font_name = pg.font.match_font('arial')
I'm a new programmer working on a memory game for my computer science summative.The game goes like this: the computer displays random boxes at random positions and then the user has to guess where the boxes are and click on it.
I'm basically done, except right now I'm trying to create 5 different levels that range in level of difficulty. eg level 1 will display 2 boxes and level 2 will display 5, etc. And then if the user gets through all levels they can play again. I know its a lot but I really want to get an A on this.
But right now I'm stuck because it doesn't really work until I try to close the window, and even then it only goes halfway. Any help would be appreciated.
import pygame , sys
import random
import time
size=[500,500]
pygame.init()
screen=pygame.display.set_mode(size)
# Colours
LIME = (0,255,0)
RED = (255, 0, 0)
BLACK = (0,0,0)
PINK = (255,102,178)
SALMON = (255,192,203)
WHITE = (255,255,255)
LIGHT_PINK = (255, 181, 197)
SKY_BLUE = (176, 226, 255)
screen.fill(BLACK)
# Width and Height of game box
width=50
height=50
# Margin between each cell
margin = 5
rows = 20
columns = 20
# Set title of screen
pygame.display.set_caption("Spatial Recall")
# Used to manage how fast the screen updates
clock=pygame.time.Clock()
coord=[]
# Create a 2 dimensional array. A two dimesional
# array is simply a list of lists.
def resetGrid():
grid = []
for row in range(rows):
# Add an empty array that will hold each cell
# in this row
grid.append([])
for column in range(columns):
grid[row].append(0) # Append a cell
return grid
def displayAllPink(pygame):
for row in range(rows):
for column in range(columns):
color = LIGHT_PINK
pygame.draw.rect(screen,color,[(margin+width)*column + margin,(margin+height)*row+margin,width,height])
pygame.display.flip()
def displayOtherColor(pygame,grid):
coord = []
for i in range(random.randint(2,5)):
x = random.randint(2, rows-1)
y = random.randint(2, columns-1)
color = LIME
pygame.draw.rect(screen,color,[(margin+width)*y + margin,(margin+height)*x+margin,width,height])
coord.append((x,y))
grid[x][y] = 1
pygame.display.flip()
time.sleep(1)
return coord
def runGame(gameCount,coord,pygame,grid):
pygame.event.clear()
pygame.display.set_caption("Spatial Recall: Level "+ str(gameCount))
pygame.time.set_timer(pygame.USEREVENT,1000)
time = 0
#clock.tick(
# -------- Main Program Loop -----------
#Loop until the user clicks the close button.
done = False
while done==False:
event = pygame.event.wait() # User did something
if event.type == pygame.QUIT: # If user clicked close
done=True # Flag that we are done so we exit this loop
pygame.event.clear()
print "Game ",gameCount, "ends"
elif event.type == pygame.USEREVENT:
time = time + 1
pygame.display.set_caption("Spatial Recall: Level "+ str(gameCount) + " Time: "+ str(time))
if time == 100:
done = True
pygame.display.set_caption("Time out, moving to next level")
pygame.event.clear()
return False
elif event.type == pygame.MOUSEBUTTONDOWN:
# User clicks the mouse. Get the position
pos = pygame.mouse.get_pos()
# Change the x/y screen coordinates to grid coordinates
column=pos[0] // (width+margin)
row=pos[1] // (height+margin)
if (row,column) in coord:
print coord
coord.remove((row,column))
print coord
color = LIME
pygame.draw.rect(screen,color,[(margin+width)*column + margin,(margin+height)*row+margin,width,height])
if coord == []:
done=True
pygame.display.set_caption("Time out, moving to next level")
pygame.event.clear()
return True
else:
color = RED
pygame.draw.rect(screen,color,[(margin+width)*column + margin,(margin+height)*row+margin,width,height])
pygame.display.flip()
def startTheGame(gameCount):
grid = resetGrid()
displayAllPink(pygame)
coord = displayOtherColor(pygame,grid)
displayAllPink(pygame)
runGame(gameCount,coord,pygame,grid)
for i in range(2):
startTheGame(i+1)
pygame.quit ()
You may want to use the pygame.font module. http://pygame.org/docs/ref/font.html
First, load a font, either from a file or from one of the system font functions.
Call YourFontObject.render(your_text_string). That'll return a Surface that contains the string rendered in the given font. Note, you can't use newline (\n) characters! You'll have to do the spacing yourself.
Blit this Surface onto the screen after everything else so nothing will obscure it.
Also, you don't need the pygame parameter in your functions.
Hope this helps.