So I'm developing a game using Pygame and trying to abstract away a lot of the code. In the process though, I'm getting some weird errors. Namely, when I run main.py, I get this trace:
>>>
initializing pygame...
initalizing screen...
initializing background...
<Surface(Dead Display)> #Here I print out the background instance
Traceback (most recent call last):
File "C:\Users\Ceasar\Desktop\pytanks\main.py", line 19, in <module>
background = Background(screen, BG_COLOR)
File "C:\Users\Ceasar\Desktop\pytanks\background.py", line 8, in __init__
self.fill(color)
error: display Surface quit
I imagine it has something to do with me using a context in my main to manage the screen.
#main.py
import math
import sys
import pygame
from pygame.locals import *
...
from screen import controlled_screen
from background import Background
BATTLEFIELD_SIZE = (800, 600)
BG_COLOR = 100, 0, 0
FRAMES_PER_SECOND = 20
with controlled_screen(BATTLEFIELD_SIZE) as screen:
background = Background(screen, BG_COLOR)
...
#screen.py
import pygame.display
import os
#The next line centers the screen
os.environ['SDL_VIDEO_CENTERED'] = '1'
class controlled_screen:
def __init__(self, size):
self.size = size
def __enter__(self):
print "initializing pygame..."
pygame.init()
print "initalizing screen..."
return pygame.display.set_mode(self.size)
def __exit__(self, type, value, traceback):
pygame.quit()
#background.py
import pygame
class Background(pygame.Surface):
def __init__(self, screen, color):
print "initializing background..."
print screen
super(pygame.Surface, self).__init__(screen.get_width(),
screen.get_height())
print self
self.fill(color)
self = self.convert()
screen.blit(self, (0, 0))
Any thoughts on what is causing the error here?
Not technically my answer here, but the problem is that Surface cannot be extended with Python's super. Rather, it should be referred to as a Python old style class like so:
class ExtendedSurface(pygame.Surface):
def __init__(self, string):
pygame.Surface.__init__(self, (100, 100))
self.fill((220,22,22))
# ...
SOURCE: http://archives.seul.org/pygame/users/Jul-2009/msg00211.html
i was also trying to subclass pygame.Surface because i wanted to be able to add attributes to it. the following accomplishes that. i hope it helps future people.
pygame.display.set_mode() must be called because it inits all the pygame.video stuff. it appears that pygame.display is the surface that ultimately gets drawn to the screen. therefore we need to blit whatever surface we create to the return value of pygame.display.set_mode() (which is just another pygame.Surface object).
import pygame
from pygame.locals import *
pygame.init()
SCREEN_SIZE = (800, 600)
font = pygame.font.SysFont('exocet', 16)
class Screen(pygame.Surface):
def __init__(self):
pygame.Surface.__init__(self, SCREEN_SIZE)
self.screen = pygame.display.set_mode((SCREEN_SIZE))
self.text = "ella_rox"
My_Screen = Screen()
text_surface = font.render(My_Screen.text, 1, (155, 0, 0))
while True:
My_Screen.fill((255, 255, 255))
My_Screen.blit(text_surface, (50, 50))
My_Screen.screen.blit(My_Screen, (0, 0))
pygame.display.update()
Related
I am following the instructions of a pygame project on youtube, this is the video, I am on the project called "Snake", and in the description of the video, you can find what time it starts and the actual code. I am about 10 minutes into the video. I will show you the code that I have written so far:
# Snake project on python
import math
import random
import pygame
import tkinter as tk
from tkinter import messagebox
class cube(object):
rows = 0
w = 0
def __init__(self, start,dirnx=1, dirny=0, colour=(255,0,0)):
pass
def move(self, dirnx, dirny):
pass
def draw(self, surface, eyes=False):
pass
class snake(object):
def __init__(self, color, pos):
pass
def move(self):
pass
def reset(self, pas):
pass
def addCube(self):
pass
def draw(self, surface):
pass
def drawGrid(w, rows, surface):
sizeBtwn = w // rows
x = 0
y = 0
for l in range(rows):
x = x + sizeBtwn
y = y + sizeBtwn
pygame.draw.line(surface, (255,255,255), (x,0), (x,w))
pygame.draw.line(surface, (255,255,255), (0,y), (w,y))
def redrawWindow(surface):
global rows, width
surface.fill(0,0,0)
drawGrid(width, rows, surface)
pygame.display.update()
def randomSnack(rows, items):
pass
def message_box(subject, content):
pass
def main():
global width, rows
width = 500
rows = 20
win = pygame.display.set_mode((width, width))
s = snake((255, 0, 0), (10, 10))
flag = True
clock = pygame.time.Clock()
while flag:
pygame.time.delay(50)
clock.tick(10)
redrawWindow(win)
main()
When I run the code the tutorial says I should be getting a grid (shown in 55:32 of the video). Instead, I am receiving this message:
Windows PowerShell
Try the new cross-platform PowerShell https://aka.ms/pscore6
PS C:\Users\holca\Desktop\Snake> & C:/Users/holca/AppData/Local/Programs/Python/Python38-32/python.exe c:/Users/holca/Desktop/Snake/snake.py
pygame 1.9.6
Hello from the pygame community. https://www.pygame.org/contribute.html
Traceback (most recent call last):
File "c:/Users/holca/Desktop/Snake/snake.py", line 77, in <module>
main()
File "c:/Users/holca/Desktop/Snake/snake.py", line 74, in main
redrawWindow(win)
File "c:/Users/holca/Desktop/Snake/snake.py", line 51, in redrawWindow
surface.fill(0,0,0)
ValueError: invalid rectstyle object
PS C:\Users\holca\Desktop\Snake>
I am using VSCode, and I have 2 warning messages talking about some unused variables, but I doubt they have to do anything with the problem. Am I missing something?
You have to specify the color by a tuple:
surface.fill(0,0,0)
surface.fill( (0, 0, 0) )
Explanation:
The first argument of fill() is the color (tuple). The other arguments are optional. The 2nd argument is an optional rectangle specifying area to be filled. The 3rd arguments are optional flags.
Hence the instruction surface.fill(0,0,0) can be read as:
surface.fill(0, rect=0, special_flags=0)
The 2nd argument (rect=0) causes the error
ValueError: invalid rectstyle object
I am trying to understand the tilemaps in pygame, and to learn how to use Tiled Map Editor, but I can't succeed to load the map in the pygame.
Here is the code:
import pygame
import pytmx
pygame.init()
display = pygame.display.set_mode((600,400))
clock = pygame.time.Clock()
gameMap = pytmx.TiledMap("map.tmx")
while(True):
clock.tick(60)
keys = pygame.key.get_pressed()
for event in pygame.event.get():
if(event.type == pygame.QUIT):
quit()
if(keys[pygame.K_ESCAPE]):
quit()
for layer in gameMap.visible_layers:
for x, y, gid, in layer:
tile = gameMap.get_tile_image_by_gid(gid)
if(tile != None):
display.blit(tile, (x * gameMap.tilewidth, y * gameMap.tileheight))
pygame.display.update()
It keep giving me this error:
Traceback (most recent call last):
File "main.py", line 27, in
display.blit(tile, (x * gameMap.tilewidth, y * gameMap.tileheight))
TypeError: argument 1 must be pygame.Surface, not tuple
I know that when I print the tile in console this is what I get
(a tuple):
('img/NES - Super Mario Bros - Tileset.png', (0, 144, 16, 16), TileFlags(flipped_horizontally=False, flipped_vertically=False, flipped_diagonally=False)).
What is the simplest method to successfully load a tilemap in pygame and what am I doing wrong?
use:
gameMap = pytmx.load_pygame("map.tmx")
instead of:
gameMap = pytmx.TiledMap("map.tmx")
I think that the following covers most of the mechanics of rendering the mapSurface and drawing something on it with update and loadMap. I don't have any real TMX files to test with. ymmv. Good luck.
import time
import pygame
import pytmx
class TiledMap():
""" This is creating the surface on which you make the draw updates """
def __init__(self):
self.gameMap = pytmx.load_pygame("map.tmx", pixelalpha=True)
self.mapwidth = self.gameMap.tilewidth * self.gameMap.width
self.mapheight = self.gameMap.tileheight * self.gameMap.height
def render(self, surface):
for layer in self.gameMap.visible_layers:
if isinstance(layer, pytmx.TiledTileLayer):
for x, y, gid in layer:
tile = self.gameMap.get_tile_image_by_gid(gid)
if tile:
surface.blit(tile, (x * self.gameMap.tilewidth, y * self.gameMap.tileheight))
def make_map(self):
mapSurface = pygame.Surface((self.mapwidth, self.mapheight))
self.render(mapSurface)
return mapSurface
pygame.init()
class Display():
""" This is the class that makes the changes that you want to display. You would add most of your changes here. """
def __init__(self):
self.displayRunning = True
self.displayWindow = pygame.display.set_mode((600, 400))
self.clock = pygame.time.Clock()
def update(self):
pygame.display.set_caption("{:.2f}".format(self.clock.get_fps()))
pygame.display.update()
def loadMap(self):
self.map = TiledMap()
self.map_img = self.map.make_map()
self.map_rect = self.map_img.get_rect()
def displayLoop(self):
self.clock.tick()
self.update()
self.loadMap()
# Here is the start of the main driver
runDisplay = Display()
runDisplay.update()
runDisplay.loadMap()
time.sleep(60)
If you want it to run in a loop then you would change that bottom driver block to something like:
runDisplay = Display()
while runDisplay.displayRunning is True:
runDisplay.displayLoop()
Now below what i did is simply created a sprite as shown..
futher to do something interesting,thought of threading i added a thread which will check the cursor position, hence update global x & y resulting in change of sprite position that is trigered by display.update
I am newbie so i might me wrong in many ways ...so please bear me....
a lot thanks in advance
from pygame import *
from main import *
import threading
import sys
#lets add our sprites
(x,y) = (0, 0)
class threading1(threading.Thread):
def run(self):
global x,y
clockobj1 = pygame.time.Clock()
while (True):
clockobj1.tick(6)
(x,y)=pygame.mouse.get_pos()
display.update()
class sprites(pygame.sprite.Sprite ):
def __init__(self,color= redd, width=120, height=120):
super(sprites,self).__init__()
self.image = pygame.Surface((width,height))
self.image.fill(color)
self.rect=self.image.get_rect()
self.rect.move_ip(x,y)
display.update()
if __name__ == '__main__':
clockobj = pygame.time.Clock()
init()
mainwin = pygame.display.set_mode((720,640))
sprite1 = sprites()
spritegrp = pygame.sprite.Group()
spritegrp.add(sprite1)
spritegrp.update()
mainwin.fill(blue)
spritegrp.draw(mainwin)
threadingobj = threading1()
threadingobj.start()
x = True
while(x):
display.update()
for evt in event.get() :
if (evt.type == QUIT) :
quit()
x=False
clockobj.tick(40)
***BELOW IS MY LATEST CODE----------UPDATED AS PER ANSWERS***PLEASE CHECK
import pygame
from main import *
import threading
import sys
# lets add our sprites
class Sprites(pygame.sprite.Sprite ):
def __init__(self, color=redd, width=120, height=120):
super().__init__()
self.image = pygame.Surface((width, height))
self.image.fill(color)
self.rect = self.image.get_rect()
def updaterect(self):
print("i m in updatereact")
print(pygame.mouse.get_pos())
self.rect.center= pygame.mouse.get_pos()
pygame.display.update()
if __name__ == '__main__':
clockobj = pygame.time.Clock()
pygame.init()
mainwin = pygame.display.set_mode((720,640))
sprite1 = Sprites()
sprite1.updaterect()
spritegrp = pygame.sprite.Group()
spritegrp.add(sprite1)
spritegrp.update()
mainwin.fill(blue)
spritegrp.draw(mainwin)
x = True
while x:
sprite1.updaterect()
pygame.display.update()
for evt in pygame.event.get() :
if evt.type == pygame.QUIT :
quit()
x=False
Threading will only just complicate things. Get rid of it, and take self.rect.move_ip(x,y) and display.update() out of the __init__ for the class. Make a function in the class called update(). This function will move the rect just by saying. self.rect.center = pygame.mouse.get_pos(). Then in the main game loop, put sprite1.update() in there or update it by using the group name instead (as you did with spritegrp.update() ) and then call display.update() there too instead of in the function.
Other things:
super().__init__() doesn't need any args
if and while loops don't need parentheses
you don't need to import main (I may be wrong about this but I don't think so)
from module import * is a bad practice in general, but if you are going to do it (I don't recommend it), you can't put module.method anywhere. You did from pygame import *, but still put pygame.mouse and others like that. Maybe you meant from pygame.locals import * ?
colons don't have a space in between them and the word, i.e. for evt in event.get() : is bad
Indentation should also be the length of a tab, or four spaces. (What IDE are you using? Most do it automatically.)
variables assignments should have spaces: x = False
hello im new to python/pygame and tried to make a basic project. it did not turn out as planned as i don't understand this error if you can tell my why my image is not loading it will very much appreciated. Traceback (most recent call last):
File "C:\Users\Nicolas\Desktop\template\templat.py", line 15, in <module>
screen.fill(background_colour)
NameError: name 'background_colour' is not defined
this is the error i was speaking of however i have fixed now. how ever now the screen opens displayes the background and crashes.
import pygame, sys
pygame.init()
def game():
background_colour = (255,255,255)
(width, height) = (800, 600)
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption('Tutorial 1')
pygame.display.set_icon(pygame.image.load('baller.jpg'))
background=pygame.image.load('path.png')
target = pygame.image.load('Player.png')
targetpos =target.get_rect()
screen.blit(target,targetpos)
screen.blit(background,(0,0))
pygame.display.update()
while True:
screen.blit(background,(0,0))
screen.blit(target,targetpos)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if__name__==('__main__')
game()
You missed the __init__ definition!
While your code does run, it is not what you want. There's an infinite loop inside the definition of the class, which means, however you are running this, there's something missing. You should put this code (most of it at least) inside the __init__ function, and then create an instance of the class.
This is what I assume you want:
import pygame, sys
class game():
width, height = 600,400
def __init__(self):
ball_filename = "Ball.png"
path_filename = "path.png"
self.screen = pygame.display.set_mode((self.width, self.height))
pygame.display.set_caption('Star catcher')
self.background = pygame.image.load(path_filename)
self.screen.blit(self.background,(0,0))
self.target = pygame.image.load(ball_filename)
def run(self):
while True:
self.screen.blit(self.background, (0,0))
targetpos = self.target.get_rect()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
if __name__ == "__main__":
# To run this
pygame.init()
g = game()
g.run()
UPDATE:
I made more modifications than what you must do, but they could be useful. I did not test this, but it should be fine.
The error of width and height not being defined is because they are not local/global variables, but bound to the class and/or the instance of the class, therefore in their namespace. So you need to access these either via game.width (per class) or self.width (per instance, only inside a method defined in the class) or g.width (per instance, if you are outside the class definition and g is an instance of game class).
I hope I'm clear. :)
I'm trying to make a basic (Mario style) game but my sprite(plumber) doesn't appear, it could be hidden behind background? i'm not exactly sure, i am not getting any errors either.
import pygame
import sys
import itertools
import pygame
from pygame.sprite import Sprite
cloud_background = pygame.image.load('clouds.bmp')
brick_tile = pygame.image.load('brick_tile.png')
pink = (255, 64, 64)
w = 640
h = 480
screen = pygame.display.set_mode((w, h))
running = 1
def setup_background():
screen.fill((pink))
screen.blit(cloud_background,(0,0))
brick_width, brick_height = brick_tile.get_width(), brick_tile.get_height()
for x,y in itertools.product(range(0,640,brick_width),
range(390,480,brick_height)):
# print(x,y)
screen.blit(brick_tile, (x, y))
pygame.display.flip()
while running:
setup_background()
event = pygame.event.poll()
if event.type == pygame.QUIT: sys.exit()
class plumber(sprite):
def __init__(
self, screen, img_filename, init_position,
init_direction, speed):
Sprite.__init__(self)
self.screen = screen
self.speed = speed
self.base_image = pygame.image.load(Mario_sideways_sprite_2xL.png).convert_alpha()
self.image = self.base_image
self.pos = 50,50
First problem found is that you must modify
pygame.image.load(Mario_sideways_sprite_2xL.png)
with something like.
pygame.image.load("Mario_sideways_sprite_2xL.png")
Besides this, the code has many problems that impedes it to work. For example,
you do not instantiate your plumber class.
class plumber(sprite) should be plumber(Sprite) (still better Plumber(Sprite))
You need something like:
myplumber = Plumber()
allsprites = pygame.sprite.RenderPlain((myplumber, ....))
clock = pygame.time.Clock()
You could see here the main parts of a simple program like yours.