I am working on a Pygame game. The premise of the game is you must dodge obstacles that start at the bottom of the screen and move upward to create the illusion the player is falling. I already have a player sprite and need help with the obstacle class. When I try to initialize my Obstacle class, I get an error.
class Obstable(pg.sprite.Sprite):
def __init__(self, color, width):
pg.sprite.Sprite.__init__(self)
self.image = pygame.surface([width, 50])
self.image.fill(black)
self.rect = self.image.get_rect()
BLACK = (0,0,0)
obst1 = Obstacle(BLACK, 100)
The class is named Obstable, you instantiate it as Obstacle. Simple Typo.
Would be even easier if you included the NameError Exception. Saying "I get an Error" is not helpful.
Firstly, your class is named Obstable. See the problem? You will need to change it to Obstacle. This should fix problem number one. Once you fix this, and run your code, you should run into another mistake.
Your second problem appears to be that you are referencing an undefined variable in the Obstacle class.
class Obstable(pg.sprite.Sprite):
def __init__(self, color, width):
pg.sprite.Sprite.__init__(self)
self.image = pygame.surface([width, 50])
self.image.fill(black)
self.rect = self.image.get_rect()
The error is here:
self.image.fill(black)
The variable black is undefined. Instead, you need to change black to self.color, but before you can do that, you must initialize self.color. I fixed your code for you:
class Obstacle(pg.sprite.Sprite):
def __init__(self, color, width):
self.color = color
pg.sprite.Sprite.__init__(self)
self.image = pygame.surface([width, 50])
self.image.fill(self.color)
self.rect = self.image.get_rect()
BLACK = (0,0,0)
obst1 = Obstacle(BLACK, 100)
Hopefully my answer was of assistance to you!
Related
Hi I'm trying some method overwriting but I dont know how to do it. I have this class called RectCreator
class RectCreator:
def __init__(self, location_x, location_y, width, height, ):
self.location_x = location_x
self.location_y = location_y
self.width = width
self.height = height
def creating_rect(self, display, color):
creating_rect = pygame.Rect(self.location_x, self.location_y, self.width, self.height)
drawing_rect = pygame.draw.rect(display, color, creating_rect, border_radius=20)
return creating_rect, drawing_rect
this class is in a file. I imported the file in my main.py and I'm using the class like this:
button_1 = RectCreator(350, 350, 100, 100)
btn_1 = button_1.creating_rect(display_surface, blue)
Now here is what I want to do. I dont know how to change the color of the btn_1 without writing all the line again like this:
btn_1 = button_1.creating_rect(display_surface, green) ---------------> I DONT WANT TO WRITE THAT
I tried to add a color method to the class and put that method in the method that uses color.
def color(self, color):
return color
def creating_rect(self, display):
creating_rect = pygame.Rect(self.location_x, self.location_y, self.width, self.height)
drawing_rect = pygame.draw.rect(display, self.color(), creating_rect, border_radius=20)
return creating_rect, drawing_rect
That was my solution but self.color() asks me for a parameter. All I want to do is:
btn_1 = button_1.creating_rect(display_surface, blue)
**output : a blue box that display on my app**
btn_1.change_color(green)
**output : now that blue box turned to green box**
Once you draw something on the screen (like a rect), it's there to stay and won't go away until you draw something new at the same position. E.g. if you set the pixel of the screen at position 5, 12 to the color green, you can't magically change the color at that position without interacting with the screen surface again.
So if you draw a blue rect and now want a green rect, you have to call pygame.draw.rect again. Changing a random variable is not enough.
What you could do is add a color attribute to your class and use that to change the color of the rect that you have to draw every frame anyway (exceptions may exist):
class RectCreator:
def __init__(self, location_x, location_y, width, height, color='blue'):
self.rect = pygame.Rect((location_x, location_y, width, height))
self.color = color
def draw(self, display):
pygame.draw.rect(display, self.color, self.rect, border_radius=20)
Then, create an instance and in your main loop, keep calling draw. You can then change the color simply by setting the color attribute
import random
...
btn_1 = RectCreator(350, 350, 100, 100)
...
while True:
for e in pygame.event.get():
if e.type == pygame.KEYDOWN:
btn_1.color = random.choice(('blue', 'green', 'red', 'yellow'))
btn_1.draw(display_surface)
...
pygame.display.flip()
I'm in my early days of pygame coding, but here is the piece I am trying to figure out right now, which is to draw the dialogue box, then the text on top of it. When ran, I see the displayed dialogue box, but no text.
import pygame
from pygame.locals import *
import os
pygame.init()
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
screen = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT])
class Text(pygame.sprite.Sprite):
def __init__(self, text):
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
os.chdir(r"<my directory to the dialogue box>.png")
self.surf = pygame.image.load("spikey_box.png").convert()
os.chdir(r"<my directory to my font>")
self.font = pygame.font.Font("final_fantasy_36_font.ttf", 12)
# set up dialogue box sprite
self.rect = self.surf.get_rect()
self.rect.center = (400, 500)
screen.blit(self.surf, self.rect)
# for text
self.textSurf = self.font.render(text, True, (255, 255, 255))
self.textRect = self.textSurf.get_rect()
self.textRect.center = (400, 500)
screen.blit(self.textSurf, self.textRect)
test_message = Text("Hello, world!")
running = True
clock = pygame.time.Clock()
while running:
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
running = False
elif event.type == QUIT:
running = False
pressed_keys = pygame.key.get_pressed()
screen.fill((255, 255, 255))
screen.blit(test_message.surf, test_message.rect)
pygame.display.flip()
clock.tick(30)
pygame.quit()
I have some sample code that I was able to adapt a little and make work for another piece I'm working on that I tried to use for inspiration to build the class:
def text_objects(text, font):
textSurface = font.render(text, True, (0, 0, 0))
return textSurface, textSurface.get_rect()
def message_display(text):
largeText = pygame.font.Font('freesansbold.ttf', 20)
TextSurf, TextRect = text_objects(text, largeText)
TextRect.center = ((SCREEN_WIDTH//2), (SCREEN_HEIGHT//2))
screen.blit(TextSurf, TextRect)
The set of two functions were defined outside of the game loop, and worked fine when called from within the game loop as {message_display(f-string)}. I respectfully request guidance to learn how to ask the right question to figure out how to make the class implementation work. I want to build on it to the point that I can call the dialogue window and allow the player to scroll back over dialogue already given in that instanced conversation.
Thank you in advance!
When you make a PyGame Sprite based on pygame.sprite.Sprite, you must define two variables as a minimum:
sprite.image - used to hold the sprite bitmap
sprite.rect - defines the location and size of the .image
Your Text Sprite does not appear to be creating the .image so it wont work as a normal sprite. But since you directly blit() the Text.surf to the display, you've dodged this issue for now.
The code is not writing the text image on top of the background dialogue. It's writing it directly to the screen in the sprite __init__(). Once the sprite is constructed, this screen update is lost.
Probably you need something like:
class Text(pygame.sprite.Sprite):
def __init__(self, text):
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
os.chdir(r"<my directory to the dialogue box>.png")
self.image = pygame.image.load("spikey_box.png").convert()
os.chdir(r"<my directory to my font>")
# Draw the text, centred on the background
self.font = pygame.font.Font("final_fantasy_36_font.ttf", 12)
text = self.font.render(text, True, (255, 255, 255))
centre_x = ( self.rect.width - text.get_width() ) // 2
centre_y = ( self.rect.height - text.get_height() ) // 2
self.image.blit( text, ( centre_x, centre_y ) )
# Set the rect
self.rect = self.image.get_rect()
self.rect.center = (400, 500)
Which loads the dialogue background image, and then rendered the text centred into that box so there is just a single image.
Since surf has been renamed to image, the main loop needs a tweak:
...
screen.fill((255, 255, 255))
screen.blit(test_message.image, test_message.rect)
pygame.display.flip()
clock.tick(30)
pygame.quit()
But really the code should use the PyGame Sprite functions for drawing, rather than accessing the internal Surface directly.
Thank you #Kingsley for your input, and you're definitely right, and helped point me in the right direction. I found that the issue was how I was instantiating the class object, and performing an odd call in the game loop that didn't really make sense. I was trying to blit a blit. I restructured my class object, and it now works perfectly!
class Text(pygame.sprite.Sprite):
def __init__(self):
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
os.chdir(r"C:\Users\mcbri\Documents\Python\master\Resources\pv1\pv1_objects")
self.surf = pygame.image.load("spikey_box.png").convert() #for box
os.chdir(r"C:\Users\mcbri\Documents\Python\master\Resources\fonts")
self.font = pygame.font.Font("final_fantasy_36_font.ttf", 12)
self.rect = self.surf.get_rect()
self.rect.center = (400, 500)
def pop_message(self, text):
screen.blit(self.surf, self.rect)
self.textSurf = self.font.render(text, True, (255, 255, 255))
self.textRect = self.textSurf.get_rect()
self.textRect.center = (400, 500)
screen.blit(self.textSurf, self.textRect)
...in loop:
elif event.type == QUIT:
running = False
pressed_keys = pygame.key.get_pressed()
screen.fill((255, 255, 255))
test_message.pop_message("Hello, world!")
pygame.display.flip()
clock.tick(30)
Thank you so much for the fast input!
I'm creating a Space Invaders with PyGame, and I would like to create a subclass of the class Alien, to simplify my code a bit. How would I do this? This is the code i have so far:
class Alien(pygame.sprite.Sprite):
def __init__(self, width, height):
super().__init__()
self.image = pygame.Surface([width, height])
self.image.fill(RED)
self.image = pygame.image.load("alien1.png").convert_alpha()
self.rect = self.image.get_rect()
In fact, you've just created a subclass of Sprite. Just do the same with Alien.
class Reptilian(Alien):
def __init__(self, width, height, human_form): # you can pass some other properties
super().__init__(width, height) # you must pass required args to Alien's __init__
# your custom stuff:
self.human_form = human_form
I use the same format of frame but it doesn't show in the interface, hope someone could tell me the solution, thanks.
class Interface(Frame):
def __init__(self,parent=None):
Frame.__init__(self,parent)
self.master.title("measurement")
self.grid()
# fix the size and parameters of widget
self.master.geometry("700x400+100+50")
self.master.Frame1 = Frame(self,relief=GROOVE,bg='white')
self.master.Frame1.grid(column=1,row=9)
self.can =Canvas(self, bg="ivory", width =200, height =150)
self.master.canvas = Canvas(self.master, width=150, height=120, background='snow')
ligne1=self.master.canvas.create_line(75, 0, 75, 120)
if __name__ == "__main__":
window = Tk()
window.resizable(False, False)
Interface(window).mainloop()
I can't figure out why you have 2 Canvas's, but the problem is that you aren't placing them on their respective parents. I cut out a lot of the code that seemed unnecessary and restructured your code to make it more logical:
class Interface(Frame):
def __init__(self, parent):
self.parent = parent
super().__init__(self.parent)
self.Frame1 = Frame(self, relief=GROOVE)
self.Frame1.grid()
self.canvas = Canvas(self.Frame1, bg="ivory", width=200, height=150)
self.canvas.grid()
self.canvas.create_line(75, 0, 75, 120)
if __name__ == "__main__":
root = Tk()
# Tk configurations are not relevant to
# the Interface and should be done out here
root.title('Measurement')
root.geometry('700x400+100+50')
root.resizable(False, False)
Interface(root).pack()
root.mainloop()
i think I don't really understand your problem, you don't see your frame because you don't have any widget in it, that's all
import tkinter as tk
class Interface(tk.Frame):
def __init__(self,parent=None):
tk.Frame.__init__(self,parent)
self.master.title("measurement")
self.grid(row=0, column=0)
# fix the size and parameters of widget
self.master.geometry("700x400+100+50")
self.master.Frame1 = tk.Frame(self,relief='groove',bg='white')
self.master.Frame1.grid(column=1,row=9)
labelExemple =tk.Label(self.master.Frame1, text="Exemple")
labelExemple.grid(row=0,column=0)
self.can = tk.Canvas(self, bg="ivory", width =200, height =150)
self.master.canvas = tk.Canvas(self.master, width=150, height=120, background='snow')
self.ligne1=self.master.canvas.create_line(75, 0, 75, 120)
if __name__ == "__main__":
window = tk.Tk()
window.resizable(False, False)
Interface(window).mainloop()
PS : use import tkinter as tk instead of from tkinter import *
There are several problems with those few lines of code, almost all having to do with the way you're using grid:
you aren't using the sticky option, so widgets won't expand to fill the space they are given
you aren't setting the weight for any rows or columns, so tkinter doesn't know how to allocate unused space
you aren't using grid or pack to put the canvases inside of frames, so the frames stay their default size of 1x1
The biggest problem is that you're trying to solve all of those problems at once. Layout problems are usually pretty simple to solve as long as you're only trying to solve one problem at a time.
Start by removing all of the widgets from Interface. Then, give that frame a distinctive background color and then try to make it fill the window (assuming that's ultimately what you want it to do). Also, remove the root.resizable(False, False). It's rarely something a user would want (they like to be able to control their windows), plus it makes your job of debugging layout problems harder.
Once you get your instance of Interface to appear, add a single widget and make sure it appears too. Then add the next, and the next, adding one widget at a time and observing how it behaves.
I've been working on creating a small, space invaders style game in pygame. I've almost reached the end however, I want to make it so if the enemy ships collide with my ship, a collision is detected and the game ends.
So far I have the code for detecting when a bullet and an enemy ship collides, however when I tried to rewrite this for a enemy/player collision it doesnt work as expected, so I think im doing something incorrect.
Code in question:
for block in block_list:
player_hit_list = pygame.sprite.spritecollide(block, player_list, True)
for player in player_hit_list:
explosion.play()
block_list.remove(block)
player_list.remove(player)
all_sprites_list.remove(block)
all_sprites_list.remove(player)
if block.rect.y < +10:
block_list.remove(block)
all_sprites_list.remove(block)
Full code: http://pastebin.com/FShPuR6A
Is anyone able to tell my why the code I have isnt functioning?
Thanks a lot
class Block(pygame.sprite.Sprite):
def __init__(self, color):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("spaceship.png")
self.rect = self.image.get_rect()
def update(self):
self.rect.y += 1
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.x=0
self.y=0
self.image = pygame.image.load("alien.png")
self.rect = self.image.get_rect()
def update(self):
pos = pygame.mouse.get_pos()
self.rect.x = pos[0]
At first glance, you have a Block class, with a picture of a spaceship that moves like an Alien. The Player class, has an alien image, and moves with the mouse.
My advice would be to rename the classes so that you will know what is what. It will be easier to spot the error.
EDIT:
It also looks like your Player does not move at all:
def render(self):
if (self.currentImage==0):
screen.blit(self.image, (self.x, self.y))
You update the player via self.rect.x, but you draw using self.x and self.y.