Having Trouble Blitting Image From Dictionary - python

So I'm trying to get into using Pygame, but all of the tutorials I can find online utilize only one file. I played around with ideas on how I can load all of the images in a single function. and decided on saving them in a Dictionary. Problem is, when I try to paste the image from the dictionary, I get the following error:
Traceback (most recent call last):
File "J:\Growth of Deities\Main.py", line 32, in <module>
pygame.Surface.blit(Sprites["TileWastelandBasic"], (0, 0))
TypeError: argument 1 must be pygame.Surface, not tuple
So I played around with the code a bit and googled it for an hour or so, but I can't figure out why I'm getting an error. I assume it's because I can't save images in dictionaries, but I'm not certain. Does anyone have any ideas on how to fix it?
My Main File:
import pygame
from Startup import LoadTextures
pygame.init()
#Sets the color White
WHITE = (255, 255, 255)
#Sets screen size Variable
size = (900, 900)
#Sets Screen Size
screen = pygame.display.set_mode(size)
#Sets name of Game
pygame.display.set_caption("Growth of Deities")
closeWindow = False
clock = pygame.time.Clock()
Sprites = LoadTextures.Load()
while not closeWindow:
#Repeat while game is playing
for event in pygame.event.get():
#Close Window if you close the window
if event.type == pygame.QUIT:
closeWindow = True
#Logic Code
#Rendering Code
pygame.Surface.blit(Sprites["TileWastelandBasic"], (0, 0))
#Clear Screen
screen.fill(WHITE)
#Update Screen
pygame.display.flip()
#Set Tick Rate
clock.tick(60)
#Closes Game
pygame.quit()
My Image Loading File:
import pygame
import os, sys
def Load():
Sprites = {}
WastelandSprites = 'Assets\Textures\Tile Sprites\Wasteland'
Sprites["TileWastelandBasic"] = pygame.image.load(os.path.join(WastelandSprites + "\WastelandBasic.png")).convert_alpha()
Sprites["TileWastelandBasic"] = pygame.transform.scale(Sprites["TileWastelandBasic"], (50, 50)).convert_alpha()
return Sprites

The problem is not because of your dictionary. The signature of blit is
blit(source, dest, area=None, special_flags = 0) -> Rect
where source must be a surface. However, this assumes that blit is being invoked with a pygame.Surface instance as the receiver. Instead, you're calling the blit function from its class, which means that its signature is effectively
blit(self, source, dest, area=None, special_flags = 0) -> Rect
where self must also be a surface. You could fix your problem by changing the call to
pygame.Surface.blit(screen, Sprites["TileWastelandBasic"], (0, 0))
but I would recommend the more idiomatic
screen.blit(Sprites["TimeWastelandBasic"], (0, 0))
instead.
See: http://www.pygame.org/docs/ref/surface.html#pygame.Surface.blit

Related

why is my pygame widow only staying open for one second before it closes?

I am currently following a beginner pygame tutorial on YouTube here but even though I copied the code exactly from the tutorial my pygame window only stays open for about a second and then closes.
note: someone asked this question about three years ago here but it didn't fix my problem.
my code is below
import pygame
pygame.init()
win = pygame.display.set_mode((500, 500))
pygame.display.set_caption('hello world')
Your script is ending and so pygame closes everything.
You have to create a loop in order for your game to continue running, with a condition to exit the loop.
You also need to initialize the display with pygame.display.init()
import pygame
pygame.init()
pygame.display.init()
win = pygame.display.set_mode((500, 500))
pygame.display.set_caption('hello world')
clock = pygame.time.Clock()
FPS = 60 # Frames per second.
# Some shortcuts for colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
# For example, display a white rect
rect = pygame.Rect((0, 0), (32, 32))
image = pygame.Surface((32, 32))
image.fill(WHITE)
# Game loop
while True:
# Ensure game runs at a constant speed
clock.tick(FPS)
# 1. Handle events
for event in pygame.event.get():
# User pressed the close button ?
if event.type == pygame.QUIT:
quit()
# Close the program. Other methods like 'raise SystemExit' or 'sys.exit()'.
# Calling 'pygame.quit()' won't close the program! It will just uninitialize the modules.
# 2. Put updates to the game logic here
# 3. Render
win.fill(BLACK) # first clear the screen
win.blit(image, rect) # draw whatever you need
pygame.display.flip() # copy to the screen

How do I save a section of a pygame screen and blit it to another location?

I am making a program with a graph that scrolls and I just need to move a section of the screen.
If I do something like this:
import pygame
screen = pygame.display.set_mode((300, 300))
sub = screen.subsurface((0,0,20,20))
screen.blit(sub, (30,40))
pygame.display.update()
It gives the error message: pygame.error: Surfaces must not be locked during blit
I assume it means the child is locked to its parent surface or something but how else could I go about doing this?
screen.subsurface creates a surface, which reference to the original surface. From documentation:
Returns a new Surface that shares its pixels with its new parent.
To avoid undefined behaviour, the surfaces get locked. You've to .copy the surface, before you can .blit it to its source:
sub = screen.subsurface((0,0,20,20)).copy()
screen.blit(sub, (30,40))
Just don't draw to the screen surface directly. Create a Surface for each part of your game/UI, and blit each of those to the screen.
import pygame
def main():
pygame.init()
screen = pygame.display.set_mode((640, 480))
# create two parts: a left part and a right part
left_screen = pygame.Surface((400, 480))
left_screen.fill((100, 0, 0))
right_screen = pygame.Surface((240, 480))
right_screen.fill((200, 200, 0))
x = 100
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
# don't draw to the screen surface directly
# draw stuff either on the left_screen or right_screen
x += 1
left_screen.fill(((x / 10) % 255, 0, 0))
# then just blit both parts to the screen surface
screen.blit(left_screen, (0, 0))
screen.blit(right_screen, (400, 0))
pygame.display.flip()
if __name__ == '__main__':
main()

Images not showing on surface in pygame

I am attempting to make a game with two surfaces. However when I try and add an image to the game layer it doesn't show up. I have tried using pygame.display.flip() which, from what I understand, should update everything on the screen.
If I try to use either Game_Layer.update() or Game_Layer.flip() as seen in the code below... (I think Game_Layer.flip() doesn't work because .flip is used to update the entire screen and thus can't be called for specific layers but correct me if I am wrong).
#game play
def Dragon():
DragonIMG=pygame.image.load("Green Dragon.gif")
DragonIMG.convert()
global Game_Layer
x=0
y=0
Game_Layer.blit(DragonIMG,(x,y))
Game_Layer.update()
Dragon()
I get the following error message:
RESTART: C:\Users\Alex\OneDrive\A- Levels\1 COMPUTER SCIENCE\Course work\Coding\CSCW Pre Alfa 1.9.5.py
Traceback (most recent call last):
File "C:\Users\Alex\OneDrive\A- Levels\1 COMPUTER SCIENCE\Course work\Coding\CSCW Pre Alfa 1.9.5.py", line 133, in <module>
Dragon()
File "C:\Users\Alex\OneDrive\A- Levels\1 COMPUTER SCIENCE\Course work\Coding\CSCW Pre Alfa 1.9.5.py", line 131, in Dragon
Game_Layer.update()
AttributeError: 'pygame.Surface' object has no attribute 'update'
>>>
However when I try to display an image on the root layer using the code below it works.
#game play
def Dragon():
DragonIMG=pygame.image.load("Green Dragon.gif")
DragonIMG.convert()
global Base_Layer
x=0
y=0
Base_Layer.blit(DragonIMG,(x,y))
pygame.display.flip()
Dragon()
Below is the code I am using to set up the layers:
#libraries
import time, random, pygame, sqlite3, GIFimage2
pygame.init()
#screen setup
#variables
clock=pygame.time.Clock() #for use in .tick
black=pygame.color.Color("black") #set black
white=pygame.color.Color("white") #set white
#set up the base layer
Base_Layer=pygame.display.set_mode((1000,600)) #desplay setup
pygame.display.set_caption("Dragon King: Legacy") #set caption
black=(0,0,0) #colour set
Base_Layer.fill(black) #colour set
Base_Layer.convert() #converts the base layer, may have no effect in current position
icon=pygame.image.load("LOGO.png") #find logo
pygame.display.set_icon(icon) #set icon to logo
#set up the game layer
Game_Layer=pygame.Surface((600,600)) #set layer peramaters
Game_Layer.fill(white) #set layer to white
Game_Layer.convert() #converts the game layer, may have no effect in current position
Base_Layer.blit(Game_Layer, (10, 0)) #blit layer on to screen
pygame.display.flip() #get the layer to show
If anyone could explain to me why this is not working I would appreciate it. I would also appreciate if someone knows a way to display my images in the way I am currently (within a definition) without using global variables.
Pygame programs are usually structured similarly to the following example. First of all, initialize everything and load the images and other resources (do that only once ahead of the main loop), then, in the main while loop, handle the events, update the game and blit everything. Finally, call pygame.display.flip() or pygame.display.update() to make all changes visible and clock.tick(fps) to limit the frame rate.
import pygame
pygame.init()
screen = pygame.display.set_mode((1000, 600))
# Constants (use uppercase letters to signal that these shouldn't be modified).
BLACK = pygame.color.Color("black")
WHITE = pygame.color.Color("white")
GAME_LAYER = pygame.Surface((600, 600))
GAME_LAYER.fill(WHITE)
# convert() returns a new surface, so you have to assign it to a variable.
DRAGON_IMG = pygame.image.load("Green Dragon.gif").convert()
def main(screen):
clock = pygame.time.Clock()
# Variables
x = 0
y = 0
while True:
# Handle events.
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
# Game logic.
x += 1
# Clear the screen and blit everything.
screen.fill(BLACK)
screen.blit(GAME_LAYER, (10, 0))
screen.blit(DRAGON_IMG, (x, y))
pygame.display.flip()
clock.tick(60)
if __name__ == '__main__':
main(screen)
pygame.quit()
If you want to blit onto a background surface instead of the screen/display and it's unicolored, you can just fill the background surface each frame with the fill method, then blit the dragon and finally blit the background onto the screen:
game_layer.fill(WHITE)
game_layer.blit(DRAGON_IMG, (x, y))
screen.blit(game_layer, (10, 0))
Or if your background surface is an actual image, you can create a copy each frame and then blit onto this copy:
game_layer_copy = GAME_LAYER.copy()
game_layer_copy.blit(DRAGON_IMG, (x, y))
screen.blit(game_layer_copy, (10, 0))
Game_Layer is a surface, and surfaces have no update method. update is a function of pygame.display. pygame.display.update is like pygame.display.flip except you can specify what parts of the screen should be flipped.
Also, please don't use global if you have any other choice. It's considered better to wrap everything into a class, pass Game_Layer as a argument, or use pygame.display.get_surface()

sprite not showing up in pygame & invalid rectstyle object

I am following a tutorial on PyGame and in one of the tutorial we are creating a simple Space Invaders game. I have a few problems.
The spaceship sprite won't show on the screen, and 2. I get an error. (more details on that below)
Code:
import pygame, sys
from pygame.locals import *
clock = pygame.time.Clock() #framerate
size = x,y=800,600
screen = pygame.display.set_mode((size)) #creates window, with resolution of 1600,900
pygame.mouse.set_visible(0)
ship = pygame.image.load('ship.png')
ship_top = screen.get_height() - ship.get_height() #makes it so ship is in the centre at the bottom
ship_left = screen.get_width()/2 - ship.get_width()/2
while True: #main game loop
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
screen.fill(0,0,0)
screen.blit(ship,(ship_left,ship_top))
clock.tick(60)
pygame.display.update
Error:
Traceback (most recent call last):
File "D:\python_tuts\space_invaders.py", line 24, in <module>
screen.fill(0,0,0)
ValueError: invalid rectstyle object
So the sprites do not show, and I get that error.
Thanks,
The fill method looks like this:
fill(color, rect=None, special_flags=0) -> Rect
Since you are calling it like this:
screen.fill(0,0,0)
It assigns 0 to rect and special_flags. I think you meant to do this:
screen.fill((0,0,0))
It's even better to define color tuples at the start of your code
BLACK = (0,0,0)
screen.fill(BLACK)

problem with making background of image transparent: pygame

I'm in the middle of working on a simple typing tutor using pygame. My problem is that I'm using an image that has a white background, waves1.png. Now's I've specified that I want white to be transparent in the image (self.image.set_colorkey((255, 255, 255))) and it is for everything except the text block. When the waves intersect with the text object, the white background of the waves show on top of the text. You can try running this if you have pygame (with the exception of the waves1.png image).
import pygame
from pygame.locals import *
class TextSprite(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.wordList = ['words yes', 'hello', 'this is a sentence', 'this is another sentence'] # read directly from external file
self.pos = 0
self.wordNum = 0
self.update1()
def update1(self):
# Render the given word
self.image = pygame.font.Font(None, 36).render(self.wordList[self.wordNum], 1, (0, 0, 0))
# Render the correctly guessed letters
self.correct = pygame.font.Font(None, 36).render(self.wordList[self.wordNum][:self.pos], 1, (255, 0, 0))
# Copy correct letters onto given word
self.image.blit(self.correct, (0, 0))
self.rect = self.image.get_rect()
# set the center of the center the given word to the center of the screen
self.rect.center = pygame.display.get_surface().get_rect().center
def keyin(self, key):
word = self.wordList[self.wordNum]
letter = word[self.pos]
if letter == key:
self.pos = self.pos + 1
if self.pos == len(word):
self.reset()
self.update1()
def reset(self):
self.pos = 0
self.wordNum = self.wordNum + 1
self.update1()
class Waves(pygame.sprite.Sprite):
# Constructor. Pass in the color of the block,
# and its x and y position
def __init__(self, filename):
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
# Create an image of the block, and fill it with a color.
# This could also be an image loaded from the disk.
self.image = pygame.image.load(filename).convert()
# makes any white in the image transparent
self.image.set_colorkey((255, 255, 255))
self.rect = self.image.get_rect()
# Decrease the y coordinate so the waves look like they're moving up
def update(self, text):
self.rect.y = self.rect.y - 6
if self.rect.y <= 200:
text.reset()
self.rect.y = 485
def main():
#I - Import and initialize
pygame.init()
#D - Display configuration
# The screen variable is a pygame Surface object
# Note that the set_mode() method creates a Surface object for you automatically
screen = pygame.display.set_mode((640, 480))
pygame.display.set_caption("Typing Game")
#E - Entities (just background for now)
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((255, 255, 255))
screen.blit(background, (0,0))
#A - Action (broken into ALTER steps)
#A - Assign values to key variables
clock = pygame.time.Clock()
keepGoing = True
# Collect the sprite in a list
all = pygame.sprite.RenderPlain()
waveList = pygame.sprite.RenderPlain()
text = TextSprite()
all.add(text)
waves = Waves("waves1.png")
waveList.add(waves)
waves.rect.x = 0
waves.rect.y = 485
#L - Set up main loop
while keepGoing:
#T - Timer to set frame rate
# Tick is a method in the Clock class that determines the maximum frame rate
clock.tick(30)
#E - Event handling
for event in pygame.event.get():
if event.type == QUIT:
keepGoing = False
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
keepGoing = False
else:
text.keyin(event.unicode)
# update position of waves
waves.update(text)
# clears screen
all.clear(screen, background)
# update screen
all.draw(screen)
waveList.clear(screen, background)
waveList.draw(screen)
# display.flip is a method that copies everything from the screen object to the actual visual display
pygame.display.flip()
pygame.quit ()
if __name__ == '__main__': main()
I don't know if it's an option for you, but you should get better results with png's native alpha transparency.
If you can edit/recreate the png yourself, then try using a transparent background.
From there, you can use convert_alpha() arfter loading the image. (instead of using a colorkey)
http://pygame.org/docs/ref/surface.html#Surface.convert_alpha
EDIT: one other aspect, is that the image may have an alpha channel interfering with the colorkey. Best to ensure you're not trying to use both.
I'm told that you can detect an image's alpha channel programmatically. Something like ...
if self.image.get_masks()[3]!=0:
print "image has alpha!"
See here http://pygame.org/docs/ref/surface.html#Surface.get_masks
HTH
Well done! You've actually done everything correctly to take advantage of transparency and colorkey (ie, making sure to call convert on the surface, making sure to pass the color into the set_colorkey method, etc).
The problem is with the order of calls to draw and clear on your respective sprite groups, "all" and "waveList". After you've rendered the text blocks by calling all.draw, you then follow it with the call to waveList.clear.
Here's the problem: once you've drawn the text sprites, you don't want to clear the space underneath the wave sprites, or that will wipe out the area that overlaps the already-drawn text blocks.
If you want to do this properly, try doing it in this order:
waves.update()
all.clear(screen,background)
waveList.clear(screen,background)
all.draw(screen)
waveList.draw(screen)
(more simply, just move waveList.clear(screen, background) to the line just below all.clear(screen, background); that should do it)
When I'm working with sprite groups, I usually try to group it so that each sprite group calls the same method in this order: clears, updates, collision checks (if any), draws.
This usually handles things in the right order. Then you still may have to pay attention to whether there is any layering of sprites, but that's another story for another day.

Categories

Resources