How do I include this variable in the font.render line? - python

I am trying to make the outcome of the dice roll be included in a string of text blit to the screen, "You rolled a" + roll
I believe the problem is related to the while loop "while running:" or the order in which I have written the code but I have no idea how to fix it. Any help would be appreciated. I am new to StackOverflow so I apologize in advance if the title/explanation isn't clear enough.
from random import *
import pygame
import sys
from pygame import rect
"""SETTINGS"""
global roll
clock = pygame.time.Clock()
fps = 60
WHITE = (255, 255, 255)
GREY = (200, 200, 200)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
WIDTH = 520
HEIGHT = 500
bg = (255, 255, 255)
"""functions"""
def dice():
roll = randint(1, 6)
print("You rolled a ", roll)
"""init"""
pygame.init()
pygame.font.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Dice")
"""dice image"""
image = pygame.image.load("diceImage.gif").convert()
image2 = image.get_rect()
imageUsed = pygame.transform.scale(image, (WIDTH, HEIGHT))
"""text object"""
surf = pygame.Surface((WIDTH, 60))
font = pygame.font.SysFont("comicsansms", 37)
text = font.render("Click the dice to roll a number", True, (30, 128, 190))
surf2 = pygame.Surface((400, 60))
text2 = font.render(("You rolled a", roll), True, (30, 128, 190))
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.MOUSEBUTTONDOWN:
dice()
mouse_pos = event.pos
if image2.collidepoint(mouse_pos):
print('button was pressed at {0}'.format(mouse_pos))
screen.fill(bg)
screen.blit(imageUsed, (0, 0))
screen.blit(surf, (0, 0))
screen.blit(surf2, (50, 450))
screen.blit(text, (0, 0))
screen.blit(text2, (50, 450))
pygame.display.update()
clock.tick(fps)
Error message:
Traceback (most recent call last):
File "C:/Users/lee/Documents/PYTHON/Dice/Dice.py", line 50, in <module>
text2 = font.render(("You rolled a", roll), True, (30, 128, 190))
NameError: name 'roll' is not defined

You've put the global statement at the wrong place. The global statement means that the listed identifiers are to be interpreted as globals in the current scope.
Declare and init roll in global namespace, but use the global statement in the function dice, to set the variabel roll in global namespace:
roll = 1
def dice():
global roll
roll = randint(1, 6)
print("You rolled a ", roll)
You have to convert the numerical value roll to a string by str() before you can use it in render() and render the text Surface:
text2 = font.render(("You rolled a " + str(roll)), True, (30, 128, 190))

In the dice() function, roll = won't actually refer to or modify the top-level roll variable, that's where you need the global statement. I'm not sure which IDE you're using, but when pasting the relevant code, mine immediately notifies me that roll = randint(1, 6) Shadows name 'roll' from outer scope.
I think the dice() function should be renamed to something more descriptive, and lose the printing. I'm not knowledgable enough about PyGame to tell if you can get rid of the global variable entirely.
Also, import * is generally bad practice.

Related

Transparency problem displaying text with Pygame

enter image description hereI would like to display transparent text on a surface that is sized based on the length of the text.
The problem is that the text has a black background even though "None" is specified as the background in the "render" command.
I tried to apply the solutions given for questions similar to mine but they didn't work.
I attach the code and thank you for any suggestions.
import pygame
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((800, 600))
screen.fill ((0,0,255))
# red square
surf1 = pygame.Surface((200, 200))
surf1.fill((255, 0, 0))
rect1 = surf1.get_rect()
rect1.topleft = (50, 50)
screen.blit(surf1, rect1)
# Play button
fnt = pygame.font.SysFont("Times New Roman", 27, bold=True)
btn_play = fnt.render("Play", True, (51, 26, 0), None)
btn_play_size = btn_play.get_size()
btn_play_surface = pygame.Surface(btn_play_size)
btn_play_surface.blit(btn_play, (0, 0))
rect_btn_play = pygame.Rect(380, 50, btn_play_size[0], btn_play_size[1])
screen.blit(btn_play_surface, (380, 50))
pygame.display.flip()
def events():
done = False
while not done:
for ev in pygame.event.get():
if ev.type == QUIT:
return "quit"
elif ev.type == MOUSEBUTTONDOWN:
click = ev.pos
if rect1.collidepoint(click):
return "Red squre"
elif rect_btn_play.collidepoint(click):
return "Play"
else:
print ("You clicked outside of the surfaces")
while True:
event = events()
print (event)
if event == "quit":
break
pygame.quit()
The problem is the surface you are placing the text on. If you want to keep the transparency in the formation of the text, you need to create a pygame.Surface object with an per pixel alpha format. Use the pygame.SRCALPHA flag:
btn_play_surface = pygame.Surface(btn_play_size)
btn_play_surface = pygame.Surface(btn_play_size, pygame.SRCALPHA)
Alternatively you can set the color key for the transparent color with set_colorkey:
btn_play_surface = pygame.Surface(btn_play_size)
btn_play_surface.set_colorkey((0, 0, 0))

why are my bullets not moving even though my tanks are

I'm making a pygame game.When I click on the tanks button and then click on the screen(play area) a tank is blitted on that coordinate. Along with the tank a bullet is also blitted. I'm able to make my tank move but the bullets are not shooting. I want the tanks to keep shooting automatically after the bullet gets reset after travelling, say 40 pixels.
This is the function that gives the tanks and the bullets the coordinates
tank_pos_list = []
bullet_list = []
def spawn_tank():
global tank_pos_list
global bullet_list
qx, qy = pygame.mouse.get_pos()
tankxy = [(qx - 35), (qy - 35)]
tank_pos_list.append(tankxy)
bullet_list.append(tankxy)
This is my movement class for tanks and bullets.
class MovementClass:
global bullet_list
global tank_pos_list
tank_surf = pygame.image.load("tank.png")
bullet = pygame.image.load("bullet.png")
def movetank(self, tankimg):
for tank_pos in tank_pos_list:
screen.blit(tankimg, (tank_pos[0], tank_pos[1]))
tank_pos[0] += 0.2
def movebullet(self, bulletimg):
for j in range(len(bullet_list)):
newx = (bullet_list[j][0] + 35)
screen.blit(bulletimg, (newx, (bullet_list[j][1] + 34)))
newx += 1
This is my main function
def main():
global new_tanks
global spawner
global tank_pos_list
global fire_bullet_tank
run = True
fps = 90
tanks = Button((59, 255, 140), 100, 610, 80, 80, text="Tanks")
tanks_over = Button((0, 255, 0), 100, 610, 80, 80, text="Tanks")
towers = Button((59, 255, 140), 510, 610, 150, 80, text="Towers")
towers_over = Button((0, 255, 0), 510, 610, 150, 80, text="Towers")
blue = pygame.image.load("blue_base.png")
red = pygame.image.load("red_base.png")
spawner = False
while run:
mx, my = pygame.mouse.get_pos()
pos = (mx, my)
x = pos[0]
y = pos[1]
mouse_pos = (mx, my)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.MOUSEBUTTONDOWN:
if spawner and my < 550 and mx < 500:
spawn_tank()
spawner = False
if tanks.isOver(mouse_pos):
spawner = True
screen.fill((50, 168, 66))
if len(tank_pos_list) >= 11:
tank_pos_list.pop(-1)
pygame.draw.rect(screen, (201, 142, 47), (0, 600, 1000, 100))
pygame.draw.line(screen, (0, 0, 0), (500, 0), (500, 600))
if tanks.isOver(mouse_pos):
tanks_over.draw(screen)
else:
tanks.draw(screen)
if towers.isOver(mouse_pos):
towers_over.draw(screen)
else:
towers.draw(screen)
screen.blit(blue, (0, 100))
screen.blit(red, (800, 100))
#movement()
movingtank = MovementClass()
movingtank.movetank(pygame.image.load("tank.png"))
movingbullet = MovementClass()
movingbullet.movebullet(pygame.image.load("bullet.png"))
pygame.display.flip()
clock.tick(fps)
When you run
for tank_pos in tank_pos_list:
...
tank_pos[0] += 0.2
you're changing the first value in a list inside the tank_pos_list list. Note that you add tankxy to both lists in spawn_tank so you can see the change in tank_pos_list and bullet_list. It's the same list in both lists you're changing here.
But when you run
for j in range(len(bullet_list)):
newx = (bullet_list[j][0] + 35)
...
newx += 1
you just create a new variable newx and change its value; but you never change any values of the lists in bullet_list.
Some more notes:
The MovementClass has no internal state; it's basically useless that you create 2 new instances every frame. Use global functions instead (without a class) or just inline those functions.
You load "tank.png" and "bullet.png" every frame from disk. You should just load the images once outside your main loop. Otherwise, it becomes a major performance killer soon.
Try to create a class or multiple classes that represents the different actors of your game (pygame offers the Sprite class for this) and implement the behaviour and state in that class. While that's not the most advanced technique it is the right way to go IMHO for small games.
For an example, maybe take a look at this answer I did for another questions, which is pretty much a step-by-step guide on how I would create such a game.

Why is pygame.MOUSEBUTTONDOWN never equal to my Button, even when the user clicks on it?

I'm currently working on a school project using Python and ideally, the project is to make any program using abstraction or algorithms in place. However, I'm stumped with this problem I've been having for days. I'm using Python with Pygame implementation to create a Vision Quiz, and I'm using buttons as multiple choice answers. Whenever I click on the correct button, it always says it's incorrect, and I have not been able to get past this part for a while. Please help me.
Pastebin link if needed: https://pastebin.com/BFPr6x6a
Code formatting here:
#Christopher Ticona
#Vision and Hearing examination
import pygame #All implementation of pygame itself
pygame.init()
#Colors
black = (0, 0, 0)
white = (255, 255, 255)
red = (255, 0, 0)
green = (0, 255, 0)
blue = (0, 0, 255)
yellow = (255, 255, 0)
#Images
imageOne = pygame.image.load("imageOne.gif")
imageOne = pygame.transform.scale(imageOne,(200,200))
imageTwo = pygame.image.load("imageTwo.png")
imageTwo = pygame.transform.scale(imageTwo, (400,400))
#Messages and questions
welcome = "Welcome to my program! Let's get started. Today, we'll be examining your eyesight/color and hearing to see if you need any glasses or support for your sense. Don't worry, this could help your health!"
summaryProgram = "There will be multiple images and sounds when you're answering the exam to test yourself on hearing, eyesight, and color blindness. Be honest and don't judge yourself if you get it wrong! Good luck!"
questionOne = "1) What do you see?: "
questionTwo = "2) What's 1 letter from the 20/20 vision section?"
#Screen to display graphics
screen = pygame.display.set_mode((1550, 720))
screen.fill(white)
pygame.display.set_caption("Vision and Hearing Examination")
class Button:
def __init__(self, rect, command):
self.rect = pygame.Rect(rect)
self.image = pygame.Surface(self.rect.size).convert()
self.image.fill((red))
self.function = command
def get_event(self, event):
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
self.on_click(event)
def on_click(self, event):
if self.rect.collidepoint(event.pos):
self.function()
def draw(self, surf):
surf.blit(self.image, self.rect)
def button_was_pressed():
if (pygame.MOUSEBUTTONDOWN == answerOne):
printText("You're correct! 1 Point", 15, 350, 100, black)
else:
printText("You're incorrect! Keep going!", 15, 350, 100, black)
def button_was_pressed1():
if (pygame.MOUSEBUTTONDOWN == answerTwo):
printText("You're correct! 1 Point", 15, 350, 300, black)
else:
printText("You're incorrect! Keep going!", 15, 350, 300, black)
def printText(txtText, Textsize , Textx, Texty, Textcolor):
#User's font
myfont = pygame.font.SysFont('Comic Sans MS', Textsize)
#Input text
label = myfont.render(txtText, 1, Textcolor)
#Coordinates of text
screen.blit(label, (Textx, Texty))
#Show the full display
pygame.display.flip()
def textButton(text,x,y):
font = pygame.font.SysFont("Comic Sans MS", 30)
textsurface = font.render((text), True, (0,0,0))
button_rect = textsurface.get_rect(topright=(x,y))
screen.blit(textsurface, button_rect)
#Font is automatically Comic Sans MS (Change if needed)
#printText(Text, Size, X, Y, Color)
welcomingMessage = printText(welcome, 15, 5, 10, black)
explanation = printText(summaryProgram, 15, 5, 30, black)
questionOne = printText(questionOne, 15, 5, 100, black)
questionTwo = printText(questionTwo, 15, 5, 300, black)
#Images displayed to the quiz
screen.blit(imageOne,(700,100))
screen.blit(imageTwo,(600,300))
#Button(rect=(x, y, height, width), command=button_was_pressed)
#Question 1
btn = Button(rect=(25,120,105,30), command=button_was_pressed)
btn1 = Button(rect=(200,120,105,30), command=button_was_pressed)
btn2 = Button(rect=(25,180,105,30), command = button_was_pressed)
btn3 = Button(rect=(200,180,105,30), command = button_was_pressed)
#Question 2
btn4 = Button(rect=(25,320,105,30), command=button_was_pressed1)
btn5 = Button(rect=(200,320,105,30), command=button_was_pressed1)
btn6 = Button(rect=(25,380,105,30), command=button_was_pressed1)
btn7 = Button(rect=(200,380,105,30), command=button_was_pressed1)
answerOne = btn
answerTwo = btn6
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
exit()
quit()
btn.get_event(event)
btn1.get_event(event)
btn2.get_event(event)
btn3.get_event(event)
btn4.get_event(event)
btn5.get_event(event)
btn6.get_event(event)
btn7.get_event(event)
btn.draw(screen)
btn1.draw(screen)
btn2.draw(screen)
btn3.draw(screen)
btn4.draw(screen)
btn5.draw(screen)
btn6.draw(screen)
btn7.draw(screen)
#Text over button
#Question 1
textButton("8", 90, 110)
textButton("3", 250,110)
textButton("7", 90, 170)
textButton("18", 250, 170)
#Question 2
textButton("B", 90, 310)
textButton("A", 250, 310)
textButton("O", 90, 370)
textButton("X", 250, 370)
pygame.display.update()
As #UnholySheep points out, you are passing a function, button_was_pressed, into your Button objects. That function contains this line:
if (pygame.MOUSEBUTTONDOWN == answerOne):
Unfortunately, pygame.MOUSEBUTTONDOWN is a contant provided by the pygame library - it may or may not be equal to answerOne once, but it won't always be equal to whatever "right" indicator you need (unless you make your questions always have the same answer, "always pick answer 'C'" or something).
Let me make a few suggestions:
Move your check for mouse button 1 into a separate function, taking an event:
def was_mouse1_click(event):
"""Return true if event is a mouse button 1 down event"""
pass
Write a method on Button to determine if a click was inside the button's area.
Write a function to decide which button was clicked, or None.
Clean up your code so that your main loop is short and sweet:
buttons = [btn1, btn2, btn3, btn4]
while not done:
for event in pygame.event.get():
if was_quit(event):
done = true
break
elif was_mouse1_click(event):
btn = which_button_was_clicked(event, buttons)
if btn is None:
# click outside buttons? ignore
continue
elif btn == right_answer:
celebrate()
else:
wrong()
else:
# Not a quit, not a click, ignore it.
pass
# draw/update screen, etc.
# broke out of while? we're done
exit()

How to display text in pygame? [duplicate]

This question already has answers here:
pygame - How to display text with font & color?
(7 answers)
Closed 2 years ago.
I can't figure out to display text in pygame.
I know I can't use print like in regular Python IDLE but I don't know how.
import pygame, sys
from pygame.locals import *
BLACK = ( 0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = ( 255, 0, 0)
pygame.init()
size = (700, 500)
screen = pygame.display.set_mode(size)
DISPLAYSURF = pygame.display.set_mode((400, 300))
pygame.display.set_caption('P.Earth')
while 1: # main game loop
for event in pygame.event.get():
if event.type == QUIT:
pygame.display.update()
import time
direction = ''
print('Welcome to Earth')
pygame.draw.rect(screen, RED, [55,500,10,5], 0)
time.sleep(1)
This is only the beginning part of the whole program.
If there is a format that will allow me to show the text I type in the pygame window that'd be great. So instead of using print I would use something else. But I don't know what that something else is.
When I run my program in pygame it doesn't show anything.
I want the program to run in the pygame window instead of it just running in idle.
You can create a surface with text on it. For this take a look at this short example:
pygame.font.init() # you have to call this at the start,
# if you want to use this module.
my_font = pygame.font.SysFont('Comic Sans MS', 30)
This creates a new object on which you can call the render method.
text_surface = my_font.render('Some Text', False, (0, 0, 0))
This creates a new surface with text already drawn onto it.
At the end you can just blit the text surface onto your main screen.
screen.blit(text_surface, (0,0))
Bear in mind, that every time the text changes, you have to recreate the surface again, to see the new text.
There's also the pygame.freetype module which is more modern, works with more fonts and offers additional functionality.
Create a font object with pygame.freetype.SysFont() or pygame.freetype.Font if the font is inside of your game directory.
You can render the text either with the render method similarly to the old pygame.font.Font.render or directly onto the target surface with render_to.
import pygame
import pygame.freetype # Import the freetype module.
pygame.init()
screen = pygame.display.set_mode((800, 600))
GAME_FONT = pygame.freetype.Font("your_font.ttf", 24)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill((255,255,255))
# You can use `render` and then blit the text surface ...
text_surface, rect = GAME_FONT.render("Hello World!", (0, 0, 0))
screen.blit(text_surface, (40, 250))
# or just `render_to` the target surface.
GAME_FONT.render_to(screen, (40, 350), "Hello World!", (0, 0, 0))
pygame.display.flip()
pygame.quit()
When displaying I sometimes make a new file called Funk. This will have the font, size etc. This is the code for the class:
import pygame
def text_to_screen(screen, text, x, y, size = 50,
color = (200, 000, 000), font_type = 'data/fonts/orecrusherexpand.ttf'):
try:
text = str(text)
font = pygame.font.Font(font_type, size)
text = font.render(text, True, color)
screen.blit(text, (x, y))
except Exception, e:
print 'Font Error, saw it coming'
raise e
Then when that has been imported when I want to display text taht updates E.G score I do:
Funk.text_to_screen(screen, 'Text {0}'.format(score), xpos, ypos)
If it is just normal text that isn't being updated:
Funk.text_to_screen(screen, 'Text', xpos, ypos)
You may notice {0} on the first example. That is because when .format(whatever) is used that is what will be updated. If you have something like Score then target score you'd do {0} for score then {1} for target score then .format(score, targetscore)
This is slighly more OS independent way:
# do this init somewhere
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480))
font = pygame.font.Font(pygame.font.get_default_font(), 36)
# now print the text
text_surface = font.render('Hello world', antialias=True, color=(0, 0, 0))
screen.blit(text_surface, dest=(0,0))

How do I solve an attribute error?

So like I said before my code (Or my current project that I am working on) is riddled with errors. So far I have at least solved a dozen errors or more and honestly I just give up. I mean God knows how many more there are.
The current problem that I am having is an AttributeError which is in my opinion one of the easiest errors to fix however I seem to have gone in to complete spaghetti mode and I have no clue on how to fix the problem.
{The error itself:
Traceback (most recent call last):
File "C:\Users\Burak\Desktop\boxtrial.py", line 87, in <module>
myScreen.addPane("1")
File "C:\Users\Burak\Desktop\boxtrial.py", line 67, in addPane
myPane.drawPane()
File "C:\Users\Burak\Desktop\boxtrial.py", line 19, in drawPane
self.Screen.blit(self.font.render(textToDisplay, True, (black)), (250, 115))
AttributeError: 'Pane' object has no attribute 'Screen'
}
I will list the code below but I feel as if I should explain what I am trying to do so you have some sort of understanding of the code.
Basically in the main loop I call upon the "Class Screen" which helps to create a PyGame screen that comes up once run. On that screen I am trying to get rectangles to appear on the screen in fixed positions (The coordinates are specific but the ones I use on the code are just for test purposes). I then have another class that is called "Pane" and this class is there so that I can draw many instances of the class pane within screen (If that makes sense).
If someone can help me get rid of the error that would be of grate help, but if you think that this is not a good way of solving the problem then please be my guest to come up with or teach me of a better way to do the same thing.
{The code:
import pygame
import sys
from pygame.locals import *
white = (255,255,255)
black = (0,0,0)
objs = []
MAIN_BUTTON = 1
class Pane():
def __init__(self, textToDisplay, coordinates, screen):
self.textToDisplay = textToDisplay
self.coordinates = coordinates
self.screen = screen
def drawPane(self):
self.Screen.blit(self.font.render(textToDisplay, True, (black)), (250, 115))
pygame.draw.rect(self.screen, (black), self.coordinates, 2)
pygame.display.update()
class Screen():
#constants/array(?) outlining the x,y boundaries of each of x10 panes
#Note to self - Remember to change co-ordinate values
NoOfPanes = 0
Panes = []
def __init__(self):
pygame.init()
pygame.display.set_caption('Box Test')
self.font = pygame.font.SysFont('Arial', 25)
Screen = pygame.display.set_mode((1000,600), 0, 32)
self.screen = Screen
self.screen.fill((white))
pygame.display.update()
def addPane(self, textToDisplay):
paneLocs = [(175, 75, 200, 100),
(0, 0, 200, 100),
(600, 400, 200, 100),
(175, 75, 200, 100),
(175, 75, 200, 100),
(175, 75, 200, 100),
(175, 75, 200, 100),
(175, 75, 200, 100),
(175, 75, 200, 100),
(175, 75, 200, 100)
]
if self.NoOfPanes > 10:
print("Limit Reached")
else:
myPane = Pane(textToDisplay, paneLocs[self.NoOfPanes], Screen)
myPane.drawPane()
self.NoOfPanes = self.NoOfPanes + 1
pygame.display.update()
def mousePosition(self):
global clickPos
global releasePos
for event in pygame.event.get():
if event.type == MAIN_BUTTON:
self.Pos = pygame.mouse.get_pos()
return MAIN_BUTTON
else:
return False
if __name__ == '__main__':
myScreen = Screen()
myScreen.addPane("1")
myScreen.addPane("2")
myScreen.addPane("3")
myScreen.addPane("4")
while True:
ev = pygame.event.get()
for event in ev:
if event.type == pygame.MOUSEBUTTONUP:
posx,posy = pygame.mouse.get_pos()
if (posx >= 175 and posx <= 375) and (posy >= 75 and posy <= 175):
print("BOB") #Bob was there just for test purposes
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit(); sys.exit();
Fix your case.
class Pane():
def __init__(self, textToDisplay, coordinates, screen):
...
self.screen = screen
def drawPane(self):
self.Screen.... # <<< HERE

Categories