What causes the attribute problems? - attributes

What is problem with the attribute?
class player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.movex = 0
self.movey = 0
self.frame = 0
self.images = []
for i in range(1, 5):
img = pygame.image.load(os.path.join('Assets','Arts','xeonsheet','xeonsheet_' + str(i) + '.png')).convert()
self.images.append(img)
self.image = self.images[0]
self.rect = self.images.get_rect()
def control(self, x, y): #Control player movement
self.movex += x
self.movey += y
def update(self): #Update sprite position
self.rect.x = self.rect.x + self.movex
self.rect.y = self.rect.y + self.movey
# moving left
if self.movex < 0:
self.frame += 1
if self.frame > 3 * ani:
self.frame = 0
# moving right
if self.movex > 0:
self.frame += 1
if self.frame > 3 * ani:
self.frame = 0
player = player()
player.rect.x = 0
player.rect.y = 0
player_list = pygame.sprite.Group()
player_list.add(player)
ani = 4
steps = 10
Error:
Traceback (most recent call last):
File "C:/Users/Acer/PycharmProjects/Dreamwind-Chronicles/main.py", line 148, in <module>
player = player()
File "C:/Users/Acer/PycharmProjects/Dreamwind-Chronicles/main.py", line 122, in __init__
self.rect = self.images.get_rect()
AttributeError: 'list' object has no attribute 'get_rect'

It looks like you were trying to create a Rect object from the self.images list. The problem here is that self.images is a list and not a pygame.image, therefore it doesn't have a get_rect() method. What you should be doing is getting the Rect from one of the images in that list.
Another problem is that you're running this code in the for loop, so each image's rect is getting overwritten by the proceeding image.
Your code should look something like this:
class player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.movex = 0
self.movey = 0
self.frame = 0
self.images = []
for i in range(1, 5):
img = pygame.image.load(os.path.join('Assets','Arts','xeonsheet','xeonsheet_' + str(i) + '.png')).convert()
self.images.append(img)
self.image = self.images[0]
self.rect = self.image.get_rect() #The part that was changed
def control(self, x, y): #Control player movement
self.movex += x
self.movey += y
def update(self): #Update sprite position
self.rect.x = self.rect.x + self.movex
self.rect.y = self.rect.y + self.movey
# moving left
if self.movex < 0:
self.frame += 1
if self.frame > 3 * ani:
self.frame = 0
# moving right
if self.movex > 0:
self.frame += 1
if self.frame > 3 * ani:
self.frame = 0
But, this is assuming that every image in your self.images list is the same size. If they are sized differently, the Rect of the player will only be scaled to the first image. To change this, you could update the self.rect in the update method. However, I'm not going to expand on this as it's not really part of the question.

Related

How to combine lists or alternatively create list of lists

I have a memory game where you have to find pairs of tiles with identical images. I want to modify the game so that you have to find pairs of a tile with image and a tile with corresponding text. For example find a tile with an image of a cat and a tile with the text "cat".
To understand the problem, here's the code that I believe is most relevant, showing how I currently use a list of images to create pairs of tiles:
class Tile(object):
def __init__(self, canvas, x, y, image, cardback):
self.image = image
more stuff...
class MemGame(tk.Frame):
def __init__(self, master):
super(MemGame, self).__init__(master)
self.images = [
photoDog,
more images...
]
selected = []
for i in range(10):
randomInd = randint(0, len(self.images) - 1)
animalImg = self.images[randomInd]
selected.append(animalImg)
selected.append(animalImg)
del self.images[randomInd]
shuffle(selected)
self.flippedTiles = []
NUM_COLS = 5
NUM_ROWS = 4
for x in range(0, NUM_COLS):
for y in range(0, NUM_ROWS):
self.tiles.append(Tile(self.canvas, x * 108 + 10, y * 108 + 40, selected.pop(), photoCardback))
I'm not sure if I should create another list of texts, or change the list of images to a list of lists with an image and a text.
In case it's needed, here's the full code:
import tkinter as tk
from random import randint
from random import shuffle
import pygame
pygame.init()
class Controller(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
if True:
self.frames = {}
for F in (PageMG,):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("PageMG")
self.geometry("800x480")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class PageMG(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
x = MemGame(self)
x.pack()
class Tile(object):
def __init__(self, canvas, x, y, image, cardback):
self.cardback = cardback
self.canvas = canvas
self.y = y
self.x = x
self.image = image
def drawFaceDown(self):
self.canvas.create_rectangle(self.x, self.y, self.x + 100, self.y + 100, fill = "white")
self.canvas.create_image(self.x + 50, self.y + 50, image=self.cardback)
self.isFaceUp = False
def drawFaceUp(self):
self.canvas.create_rectangle(self.x, self.y, self.x + 100, self.y + 100, fill = "white")
self.canvas.create_image(self.x + 50, self.y + 50, image=self.image)
self.isFaceUp = True
def isUnderMouse(self, event):
if(event.x > self.x and event.x < self.x + 100):
if(event.y > self.y and event.y < self.y + 100):
return True
class MemGame(tk.Frame):
def __init__(self, master):
super(MemGame, self).__init__(master)
photoRestart = tk.PhotoImage(file="restart.png")
imgRestart = tk.Label(self, anchor="s", image=photoRestart)
imgRestart.image = photoRestart
buttonRestart = tk.Button(self, activebackground="white",
image=photoRestart, highlightthickness=0, borderwidth=0,
command=lambda: self.restart())
buttonRestart.place(x=560, y=200)
photoDog = tk.PhotoImage(file="Dyr/dog.png")
photoElefant = tk.PhotoImage(file="Dyr/elefant.png")
photoFlamingo = tk.PhotoImage(file="Dyr/flamingo.png")
photoFlodhest = tk.PhotoImage(file="Dyr/flodhest.png")
photoKamel = tk.PhotoImage(file="Dyr/kamel.png")
photoKatt = tk.PhotoImage(file="Dyr/katt.png")
photoKroko = tk.PhotoImage(file="Dyr/krokodille.png")
photoNeshorn = tk.PhotoImage(file="Dyr/neshorn.png")
photoSkilpadde = tk.PhotoImage(file="Dyr/skilpadde.png")
photoStruts = tk.PhotoImage(file="Dyr/struts.png")
photoZebra = tk.PhotoImage(file="Dyr/zebra.png")
photoLove = tk.PhotoImage(file="Dyr/love.png")
photoCardback = tk.PhotoImage(file="cardback.png")
self.cardback = photoCardback
self.riktig_sound = pygame.mixer.Sound("riktig.wav")
self.click_sound = pygame.mixer.Sound("camerashutter.wav")
self.configure(width=650, height=480, bg="white")
self.canvas = tk.Canvas(self, bg="white", width=550, height=480, highlightthickness=0, borderwidth=0)
self.canvas.place(x=0, y=-30)
self.tiles = []
self.images = [
photoDog,
photoElefant,
photoFlamingo,
photoFlodhest,
photoKamel,
photoKatt,
photoKroko,
photoNeshorn,
photoSkilpadde,
photoStruts,
photoZebra,
photoLove
]
selected = []
for i in range(10):
randomInd = randint(0, len(self.images) - 1)
animalImg = self.images[randomInd]
selected.append(animalImg)
selected.append(animalImg)
del self.images[randomInd]
shuffle(selected)
self.flippedTiles = []
NUM_COLS = 5
NUM_ROWS = 4
for x in range(0, NUM_COLS):
for y in range(0, NUM_ROWS):
self.tiles.append(Tile(self.canvas, x * 108 + 10, y * 108 + 40, selected.pop(), photoCardback))
for i in range(len(self.tiles)):
self.tiles[i].drawFaceDown()
self.flippedThisTurn = 0
self.canvas.bind("<Button-1>", self.mouseClicked)
def mouseClicked(self, event):
for tile in self.tiles:
if tile.isUnderMouse(event) and (self.flippedThisTurn < 2):
if (not(tile.isFaceUp)):
self.clickSound()
tile.drawFaceUp()
self.flippedTiles.append(tile)
self.flippedThisTurn += 1
if (self.flippedThisTurn == 2):
if (self.flippedTiles[-1].image == self.flippedTiles[-2].image): #check last two elements
self.riktig()
self.after(1000, self.checkTiles)
def checkTiles(self):
self.flippedThisTurn = 0
if not(self.flippedTiles[-1].image == self.flippedTiles[-2].image):
self.flippedTiles[-1].drawFaceDown()
self.flippedTiles[-2].drawFaceDown()
del self.flippedTiles[-2:]
def restart(self):
for i in range(len(self.tiles)):
self.tiles[i].drawFaceDown()
def riktig(self):
pygame.mixer.Sound.play(self.riktig_sound)
pygame.mixer.music.stop()
def clickSound(self):
pygame.mixer.Sound.play(self.click_sound)
pygame.mixer.music.stop()
if __name__ == '__main__':
c = Controller()
c.mainloop()
I think the easiest approach, which causes the least changes to your current code is to:
Make your list of images (done)
Make a list of text-based corresponding tiles (to do)
Make a combined list of the two above and draw each image only once instead of twice as you are doing now (easy)
Make a dictionary that you can use to look up matches (see below)
Modify your "check tiles" function to see if they match using the dictionary (one-line change)
The dictionary created can be used bi-directionally so it will find match regardless of how comparison is done.
In [6]: image_tiles = ['dog_img', 'cat_img', 'hen_img']
In [7]: tag_tiles = ['dog', 'cat', 'hen']
In [8]: all_tiles = image_tiles + tag_tiles # use this to draw tiles
In [9]: matches = { k:v for (k, v) in zip(image_tiles, tag_tiles) }
In [10]: # add opposite lookup
In [11]: matches.update([(k, v) for (k, v) in zip(tag_tiles, image_tiles)])
In [12]: matches
Out[12]:
{'dog_img': 'dog',
'cat_img': 'cat',
'hen_img': 'hen',
'dog': 'dog_img',
'cat': 'cat_img',
'hen': 'hen_img'}

Merging two applications using tk frames

I have a memory game made with python and tkinter. I've also made an application with a simple GUI that uses tk.Frame to show different frames. My problem is putting the memory game in one of the frames of the GUI app.
I have one .py file for the memory game and one for the GUI. The GUI has multiple classes and frames. I want to put the memory game inside one of those frames so that you can navigate through the menu into the game. The game should only show when navigated to.
I have tried:
importing the memorygame file at the top of the GUI file
importing the memorygame file inside a class of the GUI file
copying the whole memorygame code into a class of the GUI file
Importing the file made both the applications try to run on startup in different windows. Copying the game code into a GUI class gave lots of errors.
I have python 3.7 and tkinter 8.6
The memory game starts by creating a canvas that it draws upon:
win = tk.Tk()
canvas = tk.Canvas(win, width = 600, height = 480)
canvas.pack()
class Tile(object):
def __init__(self, x, y, text):
self.y = y
self.x = x
self.text = text
def drawFaceDown(self):
canvas.create_rectangle(self.x, self.y, self.x + 100, self.y + 100, fill = "green")
...
And this is how I use a class to create a frame where I display different things:
class PageMG(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
# memory game goes here
I can show the entire files if anyone wants to see
I want the game to only show until I click on a button that takes me to the frame where I want the game.
Edit: the whole memory game file
import tkinter as tk
import time
from random import randint
from random import shuffle
win = tk.Tk()
canvas = tk.Canvas(win, width = 500, height = 500)
canvas.pack()
class Tile(object):
def __init__(self, x, y, text):
self.y = y
self.x = x
self.text = text
def drawFaceDown(self):
canvas.create_rectangle(self.x, self.y, self.x + 70, self.y + 70, fill = "blue")
self.isFaceUp = False
def drawFaceUp(self):
canvas.create_rectangle(self.x, self.y, self.x + 70, self.y + 70, fill = "blue")
canvas.create_text(self.x + 35, self.y + 35, text = self.text, width = 70, fill = "white", font='Helvetica 12 bold')
self.isFaceUp = True
def isUnderMouse(self, event):
if(event.x > self.x and event.x < self.x + 70):
if(event.y > self.y and event.y < self.y + 70):
return True
tiles = []
colors = [
"Eple",
"Appelsin",
"Banan",
"Agurk",
"Brokkoli",
"Tomat",
"Sitron",
"Melon",
"Hvitløk",
"Erter",
"Jordbær",
"Blåbær"
]
selected = []
for i in range(10):
randomInd = randint(0, len(colors) - 1)
color = colors[randomInd]
selected.append(color)
selected.append(color)
del colors[randomInd]
shuffle(selected)
flippedTiles = []
def mouseClicked(self):
global numFlipped
global flippedTiles
for i in range(len(tiles)):
if tiles[i].isUnderMouse(self):
if (len(flippedTiles) < 2 and not(tiles[i].isFaceUp)) :
tiles[i].drawFaceUp()
flippedTiles.append(tiles[i])
if (len(flippedTiles) == 2):
if not(flippedTiles[0].text == flippedTiles[1].text):
time.sleep(1)
flippedTiles[0].drawFaceDown()
flippedTiles[1].drawFaceDown()
NUM_COLS = 5
NUM_ROWS = 4
for x in range(0,NUM_COLS):
for y in range(0,NUM_ROWS):
tiles.append(Tile(x * 78 + 10, y * 78 + 40, selected.pop()))
for i in range(len(tiles)):
tiles[i].drawFaceDown()
flippedThisTurn = 0
def mouseClicked(event):
global flippedTiles
global flippedThisTurn
for tile in tiles:
if tile.isUnderMouse(event):
if (not(tile.isFaceUp)) :
tile.drawFaceUp()
flippedTiles.append(tile)
flippedThisTurn += 1
if (flippedThisTurn == 2):
win.after(1000, checkTiles)
flippedThisTurn = 0
def checkTiles():
if not(flippedTiles[-1].text == flippedTiles[-2].text): #check last two elements
flippedTiles[-1].drawFaceDown() #facedown last two elements
flippedTiles[-2].drawFaceDown()
del flippedTiles[-2:] #remove last two elements
win.bind("<Button-1>", mouseClicked)
win.mainloop()
importing the memorygame file at the top of the GUI file
importing the memorygame file inside a class of the GUI file
Both these ways will make "both the applications try to run on startup in different windows" this is because the memorygame file makes a new tk.Tk() window, and so does your GUI handler.
copying the whole memorygame code into a class of the GUI file
This could cause many problems, depending where you copy the files to, since these files have their own import dependencies and the import paths may change depending where you copy the files to.
What I would suggest doing is the following, I would change your code of the memory game to make the memory game a class(tk.Frame) (Frame class), then you should be able to import the memory game and stick that frame into the GUI PageMk(tk.Frame), I do not know the dependencies your code has, but this should work.
Example of change using the provided code
class MemGame(tk.Frame):
def __init__(self, parent):
super(MemGame, self).__init__(parent)
self.configure(width=600, height=480)
canvas = tk.Canvas(self, width=600, height=480, bg="red")
canvas.pack()
class Tile:
def __init__(self, parent, x, y, text):
self.parent = parent
self.y = y
self.x = x
self.text = text
def drawFaceDown(self):
self.parent.create_rectangle(self.x, self.y, self.x + 100, self.y + 100, fill="green")
class PageMG(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
x = MemGame(self)
x.pack()
My Edits to the full code:
import tkinter as tk
import time
from random import randint
from random import shuffle
class Controller(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
if True:
self.frames = {}
for F in (PageMG,):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("PageMG")
self.geometry("500x400")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class PageMG(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
x = MemGame(self)
x.pack()
class Tile(object):
def __init__(self, canvas, x, y, text):
self.canvas = canvas
self.y = y
self.x = x
self.text = text
def drawFaceDown(self):
self.canvas.create_rectangle(self.x, self.y, self.x + 70, self.y + 70, fill = "blue")
self.isFaceUp = False
def drawFaceUp(self):
self.canvas.create_rectangle(self.x, self.y, self.x + 70, self.y + 70, fill = "blue")
self.canvas.create_text(self.x + 35, self.y + 35, text = self.text, width = 70, fill = "white", font='Helvetica 12 bold')
self.isFaceUp = True
def isUnderMouse(self, event):
if(event.x > self.x and event.x < self.x + 70):
if(event.y > self.y and event.y < self.y + 70):
return True
class MemGame(tk.Frame):
def __init__(self, master):
super(MemGame, self).__init__(master)
self.configure(width=500, height=500)
self.canvas = tk.Canvas(self, width=500, height=500)
self.canvas.pack()
self.tiles = []
self.colors = [
"Eple",
"Appelsin",
"Banan",
"Agurk",
"Brokkoli",
"Tomat",
"Sitron",
"Melon",
"Hvitløk",
"Erter",
"Jordbær",
"Blåbær"
]
selected = []
for i in range(10):
randomInd = randint(0, len(self.colors) - 1)
color = self.colors[randomInd]
selected.append(color)
selected.append(color)
del self.colors[randomInd]
shuffle(selected)
self.flippedTiles = []
NUM_COLS = 5
NUM_ROWS = 4
for x in range(0, NUM_COLS):
for y in range(0, NUM_ROWS):
self.tiles.append(Tile(self.canvas, x * 78 + 10, y * 78 + 40, selected.pop()))
for i in range(len(self.tiles)):
self.tiles[i].drawFaceDown()
self.flippedThisTurn = 0
self.bind("<Button-1>", self.mouseClicked)
# def mouseClicked(self):
# for i in range(len(self.tiles)):
# if self.tiles[i].isUnderMouse(self):
# if (len(self.flippedTiles) < 2 and not(self.tiles[i].isFaceUp)) :
# self.tiles[i].drawFaceUp()
# self.flippedTiles.append(self.tiles[i])
# if (len(self.flippedTiles) == 2):
# if not(self.flippedTiles[0].text == self.flippedTiles[1].text):
# time.sleep(1)
# self.flippedTiles[0].drawFaceDown()
# self.flippedTiles[1].drawFaceDown()
def mouseClicked(self, event):
for tile in self.tiles:
if tile.isUnderMouse(event):
if (not(tile.isFaceUp)) :
tile.drawFaceUp()
self.flippedTiles.append(tile)
self.flippedThisTurn += 1
if (self.flippedThisTurn == 2):
self.after(1000, self.checkTiles)
self.flippedThisTurn = 0
def checkTiles(self):
if not(self.flippedTiles[-1].text == self.flippedTiles[-2].text): #check last two elements
self.flippedTiles[-1].drawFaceDown()
self.flippedTiles[-2].drawFaceDown()
del self.flippedTiles[-2:]
if __name__ == '__main__':
c = Controller()
c.mainloop()
Goodluck :)

Pygame image moves but image rectangle does not [duplicate]

My collide_rect function isn't working properly. It always returns True, when it's not suppose to. I have tried looking on the internet but nothing is working for me. I think the collide rect somehow did not use the actual coordinates for the two sprites. Can anyone help with this?
import pygame
import pygame.sprite
import sys
gameDisplay = pygame.display.set_mode((800,600))
pygame.display.set_caption("test_collision")
clock = pygame.time.Clock()
crashed = False
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("ball.png")
self.rect = self.image.get_rect()
self.x = 280
self.y = 475
self.col = False
def update(self):
gameDisplay.blit(self.image, (self.x,self.y))
self.rect = self.image.get_rect()
def test_collisions(self,sprite):
self.col = pygame.sprite.collide_rect(self,sprite)
class Obstacle(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.x = 1000
self.y = 483
self.image = pygame.image.load("obstacle.png")
self.time = pygame.time.get_ticks()
self.rect = self.image.get_rect()
def change_x(self):
self.time = pygame.time.get_ticks()
self.x = -(self.time/5) + 800
def update(self):
self.rect = self.image.get_rect()
gameDisplay.blit(self.image,(self.x,self.y))
obstacle = Obstacle()
ball = Ball()
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
gameDisplay.fill((255,255,255))
ball.update()
obstacle.change_x()
obstacle.update()
ball.test_collisions(obstacle)
if ball.col:
print("colided")
pygame.display.flip()
clock.tick(1000)
pygame.quit()
sys.exit()
P.S This is my first post :)
pygame.Surface.get_rect.get_rect() returns a rectangle with the size of the Surface object, but it returns a rectangle that always starts at (0, 0) since a Surface object has no position.
The Surface is placed at a position on the display with the blit function.
You've to set the location of the rectangle, either by a keyword argument, e.g:
self.rect = self.image.get_rect(topleft = (self.x, self.y))
or an assignment to a virtual attribute (see pygame.Rect), e.g:
self.rect = self.image.get_rect()
self.rect.topleft = (self.x, self.y)
It is absolutely unnecessary to add some extra attributes self.x and self.y. Use the location of the rectangle instead. e.g:
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("ball.png")
self.rect = self.image.get_rect(topleft = (280, 475))
self.col = False
def update(self):
gameDisplay.blit(self.image, self.rect)
def test_collisions(self,sprite):
self.col = pygame.sprite.collide_rect(self,sprite)
class Obstacle(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("obstacle.png")
self.time = pygame.time.get_ticks()
self.rect = self.image.get_rect(topleft = (1000, 483))
def change_x(self):
self.time = pygame.time.get_ticks()
self.rect.x = -(self.time/5) + 800
def update(self):
gameDisplay.blit(self.image, self.rect)
Further note, that you can get rid of the methods Ball.update() respectively Obstacle.update() (you can delete them), if you use a pygame.sprite.Group and call .draw(), which uses the .image and .rect properties of the contained sprites, to draw them. e.g.:
obstacle = Obstacle()
ball = Ball()
all_sprites = pygame.sprite.Group([obstacle, ball])
while not crashed:
# [...]
gameDisplay.fill((255,255,255))
all_sprites.draw(gameDisplay)
pygame.display.flip()
clock.tick(1000)

Getting an error message in pygame

import pygame
import random
from os import path
img_dir = path.join(path.dirname(__file__), 'img')
snd_dir = path.join(path.dirname(__file__), 'sound')
WIDTH = 800
HEIGHT = 600
FPS = 50
WHITE=(255, 255, 255)
BLACK=(0, 0, 0)
RED=(255, 0, 0)
GREEN=(0, 255, 0)
BLUE=(0, 0, 255)
YELLOW=(255, 255, 0)
pygame.init()
pygame.mixer.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("My Game")
clock = pygame.time.Clock()
font_name = pygame.font.match_font('arial')
end = True
def start_screen():
draw_text(screen, "SPACE SHOOTING", 70, WIDTH/2, HEIGHT/4)
draw_text(screen, "INSTRUCTIONS", 25, WIDTH/2, HEIGHT/2)
draw_text(screen, "Press Left arrow key to move left", 20, WIDTH/2, HEIGHT*2/3.5)
draw_text(screen, "Press Right arrow key to move right", 20, WIDTH/2, HEIGHT*2/3.3)
draw_text(screen, "Spacebar to shoot", 20, WIDTH/2, HEIGHT*2/3.1)
draw_text(screen, "Press a key to begin", 20, WIDTH/2, HEIGHT*3/4)
pygame.display.flip()
wait = True
while wait:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
if event.type == pygame.KEYUP:
if event.key == pygame.K_SPACE:
wait = False
def draw_text(surf, text, size, x, y):
font = pygame.font.Font(font_name, size)
text_surface = font.render(text, True, WHITE)
text_rect = text_surface.get_rect()
text_rect.midtop = (x, y)
surf.blit(text_surface, text_rect)
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.transform.scale(player_img, (50, 38))
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
self.radius = 20
self.rect.centerx = WIDTH/2
self.rect.bottom = HEIGHT - 10
self.speedx = 0
def update(self):
self.speedx = 0
keystate = pygame.key.get_pressed()
if keystate[pygame.K_LEFT]:
self.speedx = -5
if keystate[pygame.K_RIGHT]:
self.speedx = 5
self.rect.x += self.speedx
if self.rect.right > WIDTH:
self.rect.right = WIDTH
if self.rect.left < 0:
self.rect.left = 0
def shoot(self):
bullet = Bullets(self.rect.centerx, self.rect.top)
all_sprites.add(bullet)
bullets.add(bullet)
class Aliens(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = aliens_img
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
self.radius = int(self.rect.width*.85/2)
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speedy = random.randrange(1, 8)
def update(self):
self.rect.y += self.speedy
if self.rect.top > HEIGHT + 10 or self.rect.left < -25 or self.rect.right > WIDTH + 20:
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speedy = random.randrange(1, 8)
class SuperAliens(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = superaliens_img
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
self.radius = int(self.rect.width*.85/2)
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speedy = random.randrange(1, 8)
self.speedx = random.randrange(-3, 3)
def update(self):
self.rect.x += self.speedx
self.rect.y += self.speedy
if self.rect.top > HEIGHT + 10 or self.rect.left < -25 or self.rect.right > WIDTH + 20:
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speedy = random.randrange(1, 8)
def shoot1(self):
sbullet = SBullets(self.rect.centerx, self.rect.bottom)
all_sprites.add(sbullet)
bullets.add(sbullet)
class SBullets(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = sbullet_img
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
self.rect.bottom = y
self.rect.centerx = x
self.speedy = +10
def update(self):
self.rect.y += self.speedy
if self.rect.top < 0:
self.kill()
class Bullets(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = bullet_img
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
self.rect.bottom = y
self.rect.centerx = x
self.speedy = -10
def update(self):
self.rect.y += self.speedy
if self.rect.bottom < 0:
self.kill()
class Explode(pygame.sprite.Sprite):
def __init__(self, center, size):
pygame.sprite.Sprite.__init__(self)
self.size = size
self.image = explode[self.size][0]
self.rect = self.image.get_rect()
self.rect.center = center
self.frame = 0
self.last_update = pygame.time.get_ticks()
self.frame_rate = 2
def update(self):
now = pygame.time.get_ticks()
if now - self.last_update > self.frame_rate:
self.last_update = now
self.frame += 1
if self.frame == len(explode[self.size]):
self.kill()
else:
center = self.rect.center
self.image = explode[self.size][self.frame]
self.rect = self.image.get_rect()
self.rect.center = center
background = pygame.image.load(path.join(img_dir, "back.png")).convert()
background_rect = background.get_rect()
player_img = pygame.image.load(path.join(img_dir, "playerShip2_blue.png")).convert()
superaliens_img = pygame.image.load(path.join(img_dir, "enemyBlack4.png")).convert()
aliens_img = pygame.image.load(path.join(img_dir, "enemyBlack1.png")).convert()
bullet_img = pygame.image.load(path.join(img_dir, "laserRed16.png")).convert()
sbullet_img = pygame.image.load(path.join(img_dir, "laserRed11.png")).convert()
explode = {}
explode['super']=[]
explode['small']=[]
for i in range(9):
filename = 'regularExplosion02.png'.format(1)
img = pygame.image.load(path.join(img_dir, filename)).convert()
img.set_colorkey(BLACK)
img_super = pygame.transform.scale(img, (75, 75))
explode ['super'].append(img_super)
img_small = pygame.transform.scale(img, (32, 32))
explode ['small'].append(img_small)
#pygame.mixer.music.load(path.join(snd_dir, 'Orbital.mp3'))
#pygame.mixer.music.set_volume(0.4)
#pygame.mixer.music.play(loops=-1)
end_game = True
running = True
while running:
if end_game:
start_screen()
end_game = False
all_sprites = pygame.sprite.Group()
aliens = pygame.sprite.Group()
superaliens = pygame.sprite.Group()
bullets = pygame.sprite.Group()
sbullets = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
for i in range(5):
m = Aliens()
all_sprites.add(m)
aliens.add(m)
for j in range(1):
n = SuperAliens()
all_sprites.add(n)
superaliens.add(n)
score = 0
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
player.shoot()
all_sprites.update()
hits = pygame.sprite.groupcollide(aliens, bullets, True, True)
for hit in hits:
score += 1
m = Aliens()
expl = Explode(hit.rect.center, 'small')
all_sprites.add(expl)
all_sprites.add(m)
aliens.add(m)
hits = pygame.sprite.groupcollide(superaliens, bullets, True, True)
for hitt in hits:
score += 5
n = SuperAliens()
expl1 = Explode(hitt.rect.center, 'super')
all_sprites.add(expl1)
all_sprites.add(n)
superaliens.add(n)
hits = pygame.sprite.spritecollide(player, aliens, True)
for hit in hits:
expl = Explode(hit.rect.center, 'small')
all_sprites.add(expl)
if hits:
end_game = True
hits = pygame.sprite.spritecollide(player, superaliens, True)
for hitt in hits:
expl1 = Explode(hitt.rect.center, 'super')
all_sprites.add(expl1)
if hits:
end_game = True
screen.fill(BLACK)
screen.blit(background, background_rect)
all_sprites.draw(screen)
draw_text(screen,"SCORE" +str(score), 22, WIDTH/2, 10)
#after drwing everything flip the display
pygame.display.flip()
pygame.quit()
Sorry for the bad formatting but couldn't upload it with the proper formatting. Its my first pygame, so im not good in it. The program is working good, but every time when i close the window, its is showing me this error.
Traceback (most recent call last):
File "/Users/Documents/game.py", line 221, in
start_screen()
File "/Users/Documents/game.py", line 41, in start_screen
for event in pygame.event.get():
pygame.error: video system not initialized
I was also trying to make the super aliens shoot randomly towards the player, and that thing is not working as well.A little help would be appreciated. Thanks
After the line:
pygame.quit()
put:
exit()
Basically, pygame.quit() tells pygame that you are done with it. After you call it in your event loop, pygame no longer can draw things to the screen, but you still try to make it do so later in your program. Calling exit() makes python exit so that pygame is not told to draw anything.

Python-Pygame sprite bounces when gravity is used

I am making a 8bit style platformer. The player falls and gains speed because of the pseudo gravity but he will fall a few pixels into the ground level. Without gravity he will land on the ground and not fall though but it is a constant fall speed.When in the ground you can go up but he will fall when you let up. He will not got down so that is not an issue for now. Any help would be appreciated.
The player class/file.
import pygame,sys
from pygame.locals import *
class Player:
x=0
y=0
offset = 5
L=False
R=False
U=False
D=False
image = None
gravity = .25
velocity = offset
objectDict = None #this si the list of the current objects so that collision can be check with every
#object.. get updated every loop to keep a accurate check of locations
rect = None
grav = True #TODO use this to check if we are paying attention to the gravity
def __init__(self,x,y):
self.x = x
self.y = y
self.image = pygame.image.load('Resources/Pics/player.png')
def draw(self,DISPLAY):
#print('draw will go here')
imgRect = self.image.get_rect()
imgRect.midleft = (self.x,self.y)
self.rect = imgRect
DISPLAY.blit(self.image, imgRect)
#and now im here
def checkCollide(self,otherRect):
return self.rect.colliderect(otherRect)
def checkCollideAll(self):
if(self.objectDict != None):
# print(len(self.objectDict))
# for x in range(1,len(self.objectDict)):
# newb = self.checkCollide(self.objectDict[x].getRect())
# print(self.objectDict[x].getRect())
# if(newb):
# return True
# return False
collideNum = self.rect.collidelist(self.objectDict)
if(collideNum == -1):
return False
else:
return True
def willCollideBelow(self):
if(self.objectDict):
checkRect = (self.x,(self.y),self.image.get_size())
collideNum = self.rect.collidelist(self.objectDict)
if collideNum == -1:
return False
else:
return True
def objUpdate(self,dict):
self.objectDict = dict
def getRect(self):
return self.rect
def update(self):
# while(self.checkCollideAll()):
# print('while happened')
# self.y -= self.offset
# imgRect = self.image.get_rect()
# imgRect.midleft = (self.x,self.y)
# self.rect = imgRect
# print(self.willCollideBelow())
if not self.willCollideBelow():
self.D = True
# print('will fall')
else:
self.D = False
if self.U == True:
self.y -= self.offset
if self.D == True:
self.y += self.velocity
if not self.velocity >= 9.8:
self.velocity += self.gravity
else:
self.velocity = self.offset
if self.L == True:
self.x -= self.offset
if self.R == True:
self.x += self.offset
You didn't provide a running example and your code is hard to read (pascal case, a lot of unnecessary parenthesis), but here's my guess:
In your willCollideBelow function, you check if you hit an object beneath the player:
def willCollideBelow(self):
if(self.objectDict):
checkRect = (self.x,(self.y),self.image.get_size())
collideNum = self.rect.collidelist(self.objectDict)
if collideNum == -1:
return False
else:
return True
instead of just returning True or False, return the object (or the index of the object) you actually collide with:
def will_collide_below(self):
if(self.objectDict):
# using 'collidelistall' would be better, but that's another topic
return self.rect.collidelist(self.objectDict)
Now that you know which object the player collides with, you can adjust the vertical position of the player:
ground_i = self.will_collide_below()
if ground_i:
ground = self.objectDict[ground_i]
self.velocity = 0
self.rect.bottom = ground.top # or self.y = ground.top
You'll get the idea.
Some more notes:
You use different variables to store the position of the player (I see x, y, rect and imgRect). It would make you code a lot simpler if you would just use a single Rect to store the position:
class Player:
...
def __init__(self,x,y):
self.image = pygame.image.load('Resources/Pics/player.png')
self.rect = self.image.get_rect(midleft=(x,y))
def draw(self, display):
display.blit(self.image, self.rect)
def update(self):
...
if self.L: # no need to check == True
self.rect.move_ip(-self.offset)
if self.R: # simply use move_ip to alter the position
self.rect.move_ip(self.offset)
You also use a bunch of class variables where you really should use instance variables, like rect, L, R, U and D.

Resources