Pygame object on sprite not showing - python
I have a little problem when trying to learn about sprites in Pygame.
To begin with, i have already put a great amount of effort into this with sprites but i dont get it to work.
I have done exactly what guides on the internet are showing when drawing an object onto a sprite but object doesnt show up while running program.
Any suggestions would be more than welcome!
tick = 0
sprite = pygame.Surface([20, 20])
sprite.fill(white)
sprite.set_colorkey(white)
rect = sprite.get_rect()
rect.x = 400
rect.y = 300
while tick < 100:
screen.fill(black)
pygame.draw.rect(sprite, red, [rect.x, rect.y, 20, 20])
pygame.display.update()
clock.tick(10)
tick += 1
pygame.quit()
quit()
So, there are two major problems with your code.
First, you are filling the surface you are calling sprite with "white" and then just setting "white" to be treated as a transparent color for that surface, with the call to set_colorkey. So, nothing could show up anyway.
Second, you are drawing your rectangle on the Surface itself, not on your screen. So, you do suceed in painting it red, but you don't "stamp" it on the screen - you call pygame.draw.rect to draw on the Surface you are calling sprite itself, not on the screen.
Your code does not show you declaring a screen at all - I suppose you did that correctly, nonetheless keep in mind you always should post a complete example that will get the behavior you get there. If there is an error in your code to set up the screen, I can't know about it. Also, I've changed our call to draw.rect to call screen.blit : that will stamp whatever image is currently on the sprite on the surface owner of the blit method - in this case, the screen itself:
import pygame
screen = pygame.display.set_mode((800,600))
white = 255, 255, 255
tick = 0
sprite = pygame.Surface([20, 20])
sprite.fill(white)
sprite.set_colorkey(white)
rect = sprite.get_rect()
rect.x = 400
rect.y = 300
while tick < 100:
screen.fill(black)
screen.blit(sprite, (rect.x, rect.y))
pygame.display.update()
clock.tick(10)
tick += 1
pygame.quit()
quit()
With that you should see a white rectangle shortly - from them on you can start improving your code to delay the program ending, or wait an user event to close the screen and get things rolling.
By the way, in Pygame talk, a Surface is not quite considered a "sprite" - a Sprite is a special class meant to be used as members of "groups" upon which a series of helper functions will work when you start organizing a more complete game or animated sequence.
Pygame documents is a good friend.
1.Why would you fill sprite white and then set its color key to white? This will just make the sprite transparent. See set_colorkey.
sprite = pygame.Surface([20, 20])
sprite.fill(white)
sprite.set_colorkey(white)
If you need a red sprite, just create one and fill it red.
sprite = pygame.Surface((20, 20))
sprite.fill(red)
2.What pygame.draw.rect do is just drawing a rectangular shape on the Surface. So if you want to draw a red rect on the screen, just
pygame.draw.rect(screen, red, (0, 0, 20, 20))
Or if you want to show the sprite on the screen, which is usually more effective, use blit
screen.blit(sprite, (0, 0))
Have fun with pygame :)
If you want to start doing some programming with Pygame directly using sprites, it would be a good idea to do some more preliminary study of available tutorials to get some basic understanding how applications using GUIs and capable of handling user events work in general.
Another option is to start with easier things where it is possible to see the result directly without the need of writing the necessary (sprites need Groups and a class) voluminous preliminary code.
There are beside a huge amount of hard to understand tutorials on Pygame sprites also some which provide nice examples of code and quite good explanations. I suggest you take a closer look at:
Introduction to sprites --- sprite_collect_blocks.py
and then come back here with further questions, if any.
Below I have provided two pieces of code, so that you can compare them to each other to see what I have been speaking above about.
The first piece of the two is your own code with slight modifications. It creates three blocks on the screen and what it actually does has nothing to do with sprites (as it is also the case in the another answer), but shows how to populate the displayed Pygame screen with rectangles. Read carefully also the another answers as they provide good explanations of the issues your own code comes with.
import pygame
WHITE = (255,255,255)
BLACK = (0,0,0)
RED = (255,0,0)
BLUE = (0,0,255)
# Set the height and width of the screen:
DISPLAY_width = 640
DISPLAY_height = 480
DISPLAY = pygame.display.set_mode([DISPLAY_width, DISPLAY_height])
DISPLAY.fill(WHITE)
# See here: [(3) stackoverflow.com/.../pygame-drawing-a-rectangle][3]
pygame.draw.rect(DISPLAY,BLACK,(120,40,50,50))
pygame.draw.rect(DISPLAY,RED ,(520,355,50,50))
pygame.draw.rect(DISPLAY,BLUE ,(320,240,50,50))
pygame.display.flip() # without .flip() you get only black screen
pygame.display.init() # IMPORTANT !
clock = pygame.time.Clock()
tick = 0
while tick < 100:
clock.tick(10)
tick += 1
# BETTER do it as follows:
"""
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
"""
pygame.quit()
import sys
sys.exit()
Here a screenshot of what the code above creates:
The other piece of code is actual code using sprites. Sprites are usually used together with images. This makes it necessary to get some images in the scripts directory first:
And here goes the second piece of code (with TRUE sprites) which produces much MOTION on the screen (here only a static screenshot). All of the dots are in movement with different speeds filling the initially black screen with yellow color:
strPythonScriptHeader = """
# pygCodeSample_UsageOfGroupsAndSprites.py updated: 2017-04-12 09:17
# Copyright (c) 2005, Claudio at stackoverflow.com created: 2005-01-24 19:43
"""
# #############################################################################
# VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
def pygCodeSample_UsageOfGroupsAndSprites():
strMainInfo = """/*
WATCH OUT using pygame.sprite.Sprite and pygame.sprite.RenderClear classes:
- the order of Sprites in a Group is not necessary the order in which the
Sprites are drawn to the screen.
- the .clear() method of a Group clears the entire image square, not as
expected only the non-transparent part of it.
*/"""
print( strMainInfo )
strSpriteTutorial = """/*
The 'pygame.sprite' library provides classes representing images and
group of images which are to be drawn to the screen during the game.
The concept of a 'Sprite' is to provide a class which objects represent
real-world physical objects which can be viewed on the computer screen.
The Sprite class is not used directly. It is used to derive own classes
of customized Sprites from it:
class CustomizedSpriteDerivedFromSpriteClass(pygame.sprite.Sprite): ...
Within the definition of the CustomizedSpriteDerivedFromSpriteClass
it is necessary to define a member variable
.image (class CustomizedSpriteDerivedFromSpriteClass)
storing an 'Image' object which is to draw to the screen and the
member variable
.rect (class CustomizedSpriteDerivedFromSpriteClass)
storing a 'Rect' object holding the target position of the 'Image'
on the screen.
Usually both variables are set when running the
.__init__() (class CustomizedSpriteDerivedFromSpriteClass)
function which should contain as first command:
pygame.sprite.Sprite.__init__( self )
Further is it necessary to define also an
.update() (class CustomizedSpriteDerivedFromSpriteClass)
function which provides the code of rules telling how the by the Sprite
graphically represented physical objects should change when the .update()
function is executed.
The main purpose of a 'Sprite' is to be added to a container(called Group)
which is an object of the pygame.sprite.RenderClear class:
pGobjGroupOfSprites = pygame.sprite.RenderClear()
ready to hold any number of Sprite objects. A single Sprite is added to it
using
pGobjGroupOfSprites.add(pGobjCustomizedSpriteDerivedFromSpriteClass)
call.
The pGobjGroupOfSprites object of the pygame.sprite.RenderClear class is
equipped with following methods:
pGobjGroupOfSprites.clear(pGobjSCREENsurface, pGobjImage_SCREENsurfaceBackground)
pGobjGroupOfSprites.update()
pGobjGroupOfSprites.draw( pGobjSCREENsurface)
.draw() draws all Sprites to as parameter passed pGobjSCREENsurface
using the .image and .rect properties defined as member variables
within the CustomizedSpriteDerivedFromSpriteClass class.
.clear() draws those parts of the pGobjImage_SCREENsurfaceBackground image
to the pGobjSCREENsurface which are the Rects representing the
positions of the Sprites on the screen using the .rect property of Sprites.
.update() runs the .update() method of each Sprite stored in the GroupOfSprites.
Finally the main game loop looks like:
while True:
evaluateInputEvents()
pGobjGroupOfSprites_Hero.clear(pGobjSCREENsurface, pGobjImage_SCREENsurfaceBackground)
pGobjGroupOfSprites_Hero.update()
pGobjGroupOfSprites_Hero.draw( pGobjSCREENsurface)
pGobjGroupOfSprites_Enemy.clear(pGobjSCREENsurface, pGobjImage_SCREENsurfaceBackground)
pGobjGroupOfSprites_Enemy.update()
pGobjGroupOfSprites_Enemy.draw( pGobjSCREENsurface)
pGobjGroupOfSprites_Widgets.clear(pGobjSCREENsurface, pGobjImage_SCREENsurfaceBackground)
pGobjGroupOfSprites_Widgets.update()
pGobjGroupOfSprites_Widgets.draw( pGobjSCREENsurface)
adjustFrameRateTo(intTargetFrameRate) # time.sleep()/time.clock()
pGdefUpdateScreenWithDataStoredIn_pGobjSCREENsurface # == pygame.display.flip
#:while
How many and which Groups should be used and how to spread all the Sprites
used in the game over the defined Groups is part of the architecture of
the game code which is designed according to the needs of the game and the
individual programming style.
*/"""
print( strSpriteTutorial )
mPGvDctRectOfSCREENsurface = { 'left' : 0, 'right' : 640, 'top' : 0, 'bottom' : 480 }
mPGvIntScreenOriginX = mPGvDctRectOfSCREENsurface['left']
mPGvIntScreenOriginY = mPGvDctRectOfSCREENsurface['top']
mPGvIntScreenWidth = mPGvDctRectOfSCREENsurface['right'] - mPGvDctRectOfSCREENsurface['left']
mPGvIntScreenHeight = mPGvDctRectOfSCREENsurface['bottom'] - mPGvDctRectOfSCREENsurface['top']
mPGvTplScreenSize = (mPGvIntScreenWidth, mPGvIntScreenHeight)
import pygame
pGdefUpdateScreenWithDataStoredIn_pGobjSCREENsurface = pygame.display.flip
class pGobjCustomizedSprite_Dot( pygame.sprite.Sprite ):
def __init__( self, strColorOfDot, tplVelocity ):
# Intialize the Sprite class:
pygame.sprite.Sprite.__init__( self )
# .image property is used by the pGobjGroup hosting the sprite, so it is
# necessary to set it here in order to make this Sprite useful:
self.image = pygame.image.load(
r'dot-32x32_'+strColorOfDot+'.gif'
) # picture shows a coloured dot and has a transparent background
# .rect property is used by the pGobjGroup hosting the sprite, so it is
# necessary to set it here in order to make this Sprite useful:
self.rect = self.image.get_rect()
self.rect.centerx = 320
self.rect.centery = 240
# definition of other properties not necessary for usage of this class
# as a Sprite, but useful for internal purposes of setting the position
# of the rectangle within the .update() method:
self.x_velocity = tplVelocity[0]
self.y_velocity = tplVelocity[1]
#:def
# .update() method is used by the pGobjGroup hosting the sprite in its
# .update() command, so it is necessary to define it here in order to
# make this Sprite useful:
def update( self ):
self.rect.move_ip( (self.x_velocity, self.y_velocity) )
if(self.rect.left <= 0 or self.rect.right >= mPGvIntScreenWidth):
self.x_velocity = -(self.x_velocity)
#:if
if self.rect.top <= 0 or self.rect.bottom >= mPGvIntScreenHeight:
self.y_velocity = -(self.y_velocity)
#:if
#:def
#:class
pygame.init()
# pGobjSCREENsurface = pygame.display.set_mode(mPGvTplScreenSize, pygame.constants.DOUBLEBUF)
pGobjSCREENsurface = pygame.display.set_mode(mPGvTplScreenSize) # faster than with DOUBLEBUF
pGobjGroupOfSprites_spriteDot = pygame.sprite.RenderClear()
pGobjImage_SCREENsurfaceBackground = pygame.image.load(
r'rect-640x480_yellow.bmp'
) # picture shows yellow background
intLstLength = 0
dctTplVelocity = {}
import random
while(intLstLength < 5):
tplVelocity = (random.randint( 1, 5 ), random.randint( 1, 5 ))
if(tplVelocity in dctTplVelocity): pass
else:
intLstLength+=1
dctTplVelocity[tplVelocity] = intLstLength
#:if/else
#:while
lstTplVelocity = list(dctTplVelocity.keys())
# pGspriteRedDot = pGobjCustomizedSprite_Dot('red' ,lstTplVelocity[0])
# pGspriteWhiteDot = pGobjCustomizedSprite_Dot('purple',lstTplVelocity[1])
# pGspriteGreyDot = pGobjCustomizedSprite_Dot('grey' ,lstTplVelocity[2])
# pGspriteBlueDot = pGobjCustomizedSprite_Dot('blue' ,lstTplVelocity[3])
# pGspriteOrangeDot = pGobjCustomizedSprite_Dot('orange',lstTplVelocity[4])
pGobjGroupOfSprites_spriteDot.add(pGobjCustomizedSprite_Dot('red' ,lstTplVelocity[0]))
pGobjGroupOfSprites_spriteDot.add(pGobjCustomizedSprite_Dot('purple',lstTplVelocity[1]))
pGobjGroupOfSprites_spriteDot.add(pGobjCustomizedSprite_Dot('grey' ,lstTplVelocity[2]))
pGobjGroupOfSprites_spriteDot.add(pGobjCustomizedSprite_Dot('blue' ,lstTplVelocity[3]))
pGobjGroupOfSprites_spriteDot.add(pGobjCustomizedSprite_Dot('orange',lstTplVelocity[4]))
print
print( ' target frame rate [frames/second] == 100.0 : ' )
intLoopCounter = 0
import time
clock = time.clock
sleep = time.sleep
import sys
fltTime=clock()
fltStartTime = clock()
blnExitMainGameLoop = False
fltFrameRate = 100.0
fltTimeToNextFrame = 1.0 / fltFrameRate
import time
while True:
time.sleep(0.03)
# ---
# -----------------------------------------------------
# Processing of user input:
pGlstObjEvent = pygame.event.get()
for pGobjEvent in pGlstObjEvent:
if pGobjEvent.type == pygame.constants.QUIT:
blnExitMainGameLoop = True
#:if
if pGobjEvent.type == pygame.constants.KEYDOWN:
if pGobjEvent.key == pygame.constants.K_ESCAPE:
blnExitMainGameLoop = True
if pGobjEvent.key == pygame.constants.K_s:
time.sleep(21)
blnExitMainGameLoop = False
#:if
#:if
#:for
if(blnExitMainGameLoop):
pygame.display.quit()
break
#:if
# ---
# -----------------------------------------------------
# output of texts with infos to console window:
if(intLoopCounter%100 == 99):
print( ' %5.2f '%(fltFrameRate/(clock()-fltTime),), )
fltTime=clock()
#:if
intLoopCounter += 1
# print intLoopCounter,
# ---
# -----------------------------------------------------
# preparing and drawing graphic output to screen:
pGobjGroupOfSprites_spriteDot.update()
pGobjGroupOfSprites_spriteDot.clear(pGobjSCREENsurface, pGobjImage_SCREENsurfaceBackground)
pGobjGroupOfSprites_spriteDot.draw( pGobjSCREENsurface)
# ---
# -----------------------------------------------------
# adjusting frame rate to 100 frames/second:
# fltFrameRate = 100.0
# fltTimeToNextFrame = 1.0 / fltFrameRate
fltTargetTime = fltTimeToNextFrame*intLoopCounter
fltTimeDiff = fltTargetTime-(clock()-fltStartTime)
if(fltTimeDiff > 0.8*fltTimeToNextFrame): sleep(0.8*fltTimeToNextFrame)
fltTimeDiff = fltTargetTime-(clock()-fltStartTime)
if(fltTimeDiff > 0.1*fltTimeToNextFrame): sleep(0.1*fltTimeToNextFrame)
fltTimeDiff = fltTargetTime-(clock()-fltStartTime)
while(fltTimeDiff > 0):
fltTimeDiff = (fltTimeToNextFrame*intLoopCounter)-(clock()-fltStartTime)
#:while
# ---
# -----------------------------------------------------
# displaying prepared graphic output:
pGdefUpdateScreenWithDataStoredIn_pGobjSCREENsurface()
#:while
#:def pygCodeSample_UsageOfGroupsAndSprites()
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
if __name__ == '__main__':
print( strPythonScriptHeader )
pygCodeSample_UsageOfGroupsAndSprites()
import sys
if int(sys.version[0]) < 3 : # Python 2 :
raw_input('(exit with ENTER) #> OK? ')
sys.exit()
else: # Python 3 :
input('(exit with ENTER) #> OK? ')
sys.exit()
#:if
pygameConstantsForUsageInKeyEVENTS = ['ACTIVEEVENT', 'ANYFORMAT', 'ASYNCBLIT', 'AUDIO_S16', 'AUDIO_S16LSB', 'AUDIO_S16MSB', 'AUDIO_S16SYS', 'AUDIO_S8', 'AUDIO_U16', 'AUDIO_U16LSB', 'AUDIO_U16MSB', 'AUDIO_U16SYS', 'AUDIO_U8', 'BIG_ENDIAN', 'BLEND_ADD', 'BLEND_MAX', 'BLEND_MIN', 'BLEND_MULT', 'BLEND_PREMULTIPLIED', 'BLEND_RGBA_ADD', 'BLEND_RGBA_MAX', 'BLEND_RGBA_MIN', 'BLEND_RGBA_MULT', 'BLEND_RGBA_SUB', 'BLEND_RGB_ADD', 'BLEND_RGB_MAX', 'BLEND_RGB_MIN', 'BLEND_RGB_MULT', 'BLEND_RGB_SUB', 'BLEND_SUB', 'BUTTON_X1', 'BUTTON_X2', 'DOUBLEBUF', 'FULLSCREEN', 'GL_ACCELERATED_VISUAL', 'GL_ACCUM_ALPHA_SIZE', 'GL_ACCUM_BLUE_SIZE', 'GL_ACCUM_GREEN_SIZE', 'GL_ACCUM_RED_SIZE', 'GL_ALPHA_SIZE', 'GL_BLUE_SIZE', 'GL_BUFFER_SIZE', 'GL_DEPTH_SIZE', 'GL_DOUBLEBUFFER', 'GL_GREEN_SIZE', 'GL_MULTISAMPLEBUFFERS', 'GL_MULTISAMPLESAMPLES', 'GL_RED_SIZE', 'GL_STENCIL_SIZE', 'GL_STEREO', 'GL_SWAP_CONTROL', 'HAT_CENTERED', 'HAT_DOWN', 'HAT_LEFT', 'HAT_LEFTDOWN', 'HAT_LEFTUP', 'HAT_RIGHT', 'HAT_RIGHTDOWN', 'HAT_RIGHTUP', 'HAT_UP', 'HWACCEL', 'HWPALETTE', 'HWSURFACE', 'IYUV_OVERLAY', 'JOYAXISMOTION', 'JOYBALLMOTION', 'JOYBUTTONDOWN', 'JOYBUTTONUP', 'JOYHATMOTION', 'KEYDOWN', 'KEYUP', 'KMOD_ALT', 'KMOD_CAPS', 'KMOD_CTRL', 'KMOD_LALT', 'KMOD_LCTRL', 'KMOD_LMETA', 'KMOD_LSHIFT', 'KMOD_META', 'KMOD_MODE', 'KMOD_NONE', 'KMOD_NUM', 'KMOD_RALT', 'KMOD_RCTRL', 'KMOD_RMETA', 'KMOD_RSHIFT', 'KMOD_SHIFT', 'K_0', 'K_1', 'K_2', 'K_3', 'K_4', 'K_5', 'K_6', 'K_7', 'K_8', 'K_9', 'K_AMPERSAND', 'K_ASTERISK', 'K_AT', 'K_BACKQUOTE', 'K_BACKSLASH', 'K_BACKSPACE', 'K_BREAK', 'K_CAPSLOCK', 'K_CARET', 'K_CLEAR', 'K_COLON', 'K_COMMA', 'K_DELETE', 'K_DOLLAR', 'K_DOWN', 'K_END', 'K_EQUALS', 'K_ESCAPE', 'K_EURO', 'K_EXCLAIM', 'K_F1', 'K_F10', 'K_F11', 'K_F12', 'K_F13', 'K_F14', 'K_F15', 'K_F2', 'K_F3', 'K_F4', 'K_F5', 'K_F6', 'K_F7', 'K_F8', 'K_F9', 'K_FIRST', 'K_GREATER', 'K_HASH', 'K_HELP', 'K_HOME', 'K_INSERT', 'K_KP0', 'K_KP1', 'K_KP2', 'K_KP3', 'K_KP4', 'K_KP5', 'K_KP6', 'K_KP7', 'K_KP8', 'K_KP9', 'K_KP_DIVIDE', 'K_KP_ENTER', 'K_KP_EQUALS', 'K_KP_MINUS', 'K_KP_MULTIPLY', 'K_KP_PERIOD', 'K_KP_PLUS', 'K_LALT', 'K_LAST', 'K_LCTRL', 'K_LEFT', 'K_LEFTBRACKET', 'K_LEFTPAREN', 'K_LESS', 'K_LMETA', 'K_LSHIFT', 'K_LSUPER', 'K_MENU', 'K_MINUS', 'K_MODE', 'K_NUMLOCK', 'K_PAGEDOWN', 'K_PAGEUP', 'K_PAUSE', 'K_PERIOD', 'K_PLUS', 'K_POWER', 'K_PRINT', 'K_QUESTION', 'K_QUOTE', 'K_QUOTEDBL', 'K_RALT', 'K_RCTRL', 'K_RETURN', 'K_RIGHT', 'K_RIGHTBRACKET', 'K_RIGHTPAREN', 'K_RMETA', 'K_RSHIFT', 'K_RSUPER', 'K_SCROLLOCK', 'K_SEMICOLON', 'K_SLASH', 'K_SPACE', 'K_SYSREQ', 'K_TAB', 'K_UNDERSCORE', 'K_UNKNOWN', 'K_UP', 'K_a', 'K_b', 'K_c', 'K_d', 'K_e', 'K_f', 'K_g', 'K_h', 'K_i', 'K_j', 'K_k', 'K_l', 'K_m', 'K_n', 'K_o', 'K_p', 'K_q', 'K_r', 'K_s', 'K_t', 'K_u', 'K_v', 'K_w', 'K_x', 'K_y', 'K_z', 'LIL_ENDIAN', 'MOUSEBUTTONDOWN', 'MOUSEBUTTONUP', 'MOUSEMOTION', 'NOEVENT', 'NOFRAME', 'NUMEVENTS', 'OPENGL', 'OPENGLBLIT', 'PREALLOC', 'QUIT', 'RESIZABLE', 'RLEACCEL', 'RLEACCELOK', 'SCRAP_BMP', 'SCRAP_CLIPBOARD', 'SCRAP_PBM', 'SCRAP_PPM', 'SCRAP_SELECTION', 'SCRAP_TEXT', 'SRCALPHA', 'SRCCOLORKEY', 'SWSURFACE', 'SYSWMEVENT', 'TIMER_RESOLUTION', 'USEREVENT', 'USEREVENT_DROPFILE', 'UYVY_OVERLAY', 'VIDEOEXPOSE', 'VIDEORESIZE', 'YUY2_OVERLAY', 'YV12_OVERLAY', 'YVYU_OVERLAY', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
The code above is equipped with voluminous comments. As I have written it years and years ago starting on pygame and trying to understand sprites, the goal was to write it so clear that it will be easy to understand. Now you can judge yourself how successful I was with my style of self-explanatory variable naming and the provided explanations and informations.
Related
How can I tell if my game works at other resolutions? [duplicate]
So in every Pygame example I've seen, there seems to be a resolution the game is designed to be played on and that's it. No options for setting higher or lower resolutions. If you do change the resolution via display.set_mode, then the scale of the game textures/graphics get out of whack and the game becomes unplayable. In all the examples I've seen, it looks like you would have to actually create different sized texture sets for each target resolution... Which seems ridiculous. That leads me to my question. If I design a game based on a standard resolution of 480x270, is it possible to simply scale the output of that surface to 1080p (or 1440p, 4k, etc) while still utilizing the game assets built for 480x270? If so, can anyone post an example of how this can be accomplished? Any tips or pointers would also be appreciated.
You could make a dummy surface at the design resolution and scale it to the resolution the game is running at: window = pygame.set_mode([user_x, user_y]) w = pygame.Surface([design_x, design_y]) def draw(): frame = pygame.transform.scale(w, (user_x, user_y)) window.blit(frame, frame.get_rect()) pygame.display.flip() Draw everything on the dummy surface and it will be scaled to the screen. This requires a fixed screen height to screen width ratio.
Spent some time tinkering and put together a demo showcasing how you could go about tackling this problem. Find yourself an image for testing and put the string path to that image in the script for the value of imagePath. The functionality of this script is simple. As you hit either left or right on the arrow keys, the screen resolution cycles through a tuple of acceptable resolutions and the screen resizes accordingly while scaling your test image to the new resolution. import pygame,sys from pygame import * from pygame.locals import * displayIndex = 0 pygame.init() ##standard 16:9 display ratios DISPLAYS = [(1024,576),(1152,648),(1280,720),(1600,900),(1920,1080),(2560,1440)] screen = pygame.display.set_mode(DISPLAYS[displayIndex]) screen.fill((0,0,0)) ### change image path to a string that names an image you'd like to load for testing. I just used a smiley face from google image search. ### Put it in the same place as this file or set up your paths appropriately imagePath = "Smiley-icon.png" class Icon(pygame.sprite.Sprite): def __init__(self,x,y): pygame.sprite.Sprite.__init__(self) self.smileyImage = pygame.image.load(imagePath) self.image = self.smileyImage.convert_alpha() ### need to assume a default scale, DISPLAYS[0] will be default for us self.rect = self.image.get_rect() self.posX = x self.posY = y self.rect.x = x self.rect.y = y self.defaultx = (float(self.rect[2])/DISPLAYS[0][0])*100 self.defaulty = (float(self.rect[3])/DISPLAYS[0][1])*100 ## this is the percent of the screen that the image should take up in the x and y planes def updateSize(self,): self.image = ImageRescaler(self.smileyImage,(self.defaultx,self.defaulty)) self.rect = self.image.get_rect() self.rect.x = self.posX self.rect.y = self.posY def ImageRescaler(image,originalScaleTuple): #be sure to restrict to only proper ratios newImage = pygame.transform.scale(image,(int(DISPLAYS[displayIndex][0]*(originalScaleTuple[0]/100)), int(DISPLAYS[displayIndex][1]*(originalScaleTuple[1]/100)))) return newImage def resizeDisplay(): screen = pygame.display.set_mode(DISPLAYS[displayIndex]) ## this is where you'd have'd probably want your sprite groups set to resize themselves ## Just gonna call it on icon here icon.updateSize() icon = Icon(100,100) while True: screen.fill((0,0,0)) for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() if event.type == KEYDOWN: if event.key == K_LEFT: displayIndex -=1 if displayIndex < 0: displayIndex = 5 resizeDisplay() elif event.key == K_RIGHT: displayIndex+=1 if displayIndex > 5: displayIndex = 0 resizeDisplay() screen.blit(icon.image,(icon.rect.x,icon.rect.y)) pygame.display.update()
The best way to go about this is by downscaling images, in order to preserve image quality. Here are two options: Option 1 This method is probably the fastest. Create images to be compatible with the largest resolution you are intending to support. Create a screen with the user's desired size. screen = pygame.display.set_mode((user_x, user_y)) Downscale images at load time image = pygame.image.load("a.png").convert_alpha() pygame.transform.scale(image, (screen.width() / your_max_width, screen.height() / your_max_height), DestSurface=image) Now, just blit them normally. It should run at normal speeds. Option 2 Create a screen with the user's desired size. screen = pygame.display.set_mode((user_x, user_y)) Next, create a pygame.Surface with the highest resolution you are intending to support. surface = pygame.Surface((1920, 1080)) Then, blit everything to that surface. surface.blit(image, rect) # Etc... After all blits are completed, downscale the surface (as necessary) to whatever resolution the user desires. Preferably, you would only allow resolutions with the same aspect ratio. pygame.transform.scale(surface, ((screen.width() / your_max_width, screen.height() / your_max_height), DestSurface=surface) Finally, blit the surface to the screen, and display it. screen.blit(surface, (0, 0)) pygame.display.update() This process (downscaling) allows you to preserve image quality while still allowing the user to choose their screen resolution. It will be slower because you are constantly manipulating images.
How to increase the visual quality of pygame [duplicate]
So in every Pygame example I've seen, there seems to be a resolution the game is designed to be played on and that's it. No options for setting higher or lower resolutions. If you do change the resolution via display.set_mode, then the scale of the game textures/graphics get out of whack and the game becomes unplayable. In all the examples I've seen, it looks like you would have to actually create different sized texture sets for each target resolution... Which seems ridiculous. That leads me to my question. If I design a game based on a standard resolution of 480x270, is it possible to simply scale the output of that surface to 1080p (or 1440p, 4k, etc) while still utilizing the game assets built for 480x270? If so, can anyone post an example of how this can be accomplished? Any tips or pointers would also be appreciated.
You could make a dummy surface at the design resolution and scale it to the resolution the game is running at: window = pygame.set_mode([user_x, user_y]) w = pygame.Surface([design_x, design_y]) def draw(): frame = pygame.transform.scale(w, (user_x, user_y)) window.blit(frame, frame.get_rect()) pygame.display.flip() Draw everything on the dummy surface and it will be scaled to the screen. This requires a fixed screen height to screen width ratio.
Spent some time tinkering and put together a demo showcasing how you could go about tackling this problem. Find yourself an image for testing and put the string path to that image in the script for the value of imagePath. The functionality of this script is simple. As you hit either left or right on the arrow keys, the screen resolution cycles through a tuple of acceptable resolutions and the screen resizes accordingly while scaling your test image to the new resolution. import pygame,sys from pygame import * from pygame.locals import * displayIndex = 0 pygame.init() ##standard 16:9 display ratios DISPLAYS = [(1024,576),(1152,648),(1280,720),(1600,900),(1920,1080),(2560,1440)] screen = pygame.display.set_mode(DISPLAYS[displayIndex]) screen.fill((0,0,0)) ### change image path to a string that names an image you'd like to load for testing. I just used a smiley face from google image search. ### Put it in the same place as this file or set up your paths appropriately imagePath = "Smiley-icon.png" class Icon(pygame.sprite.Sprite): def __init__(self,x,y): pygame.sprite.Sprite.__init__(self) self.smileyImage = pygame.image.load(imagePath) self.image = self.smileyImage.convert_alpha() ### need to assume a default scale, DISPLAYS[0] will be default for us self.rect = self.image.get_rect() self.posX = x self.posY = y self.rect.x = x self.rect.y = y self.defaultx = (float(self.rect[2])/DISPLAYS[0][0])*100 self.defaulty = (float(self.rect[3])/DISPLAYS[0][1])*100 ## this is the percent of the screen that the image should take up in the x and y planes def updateSize(self,): self.image = ImageRescaler(self.smileyImage,(self.defaultx,self.defaulty)) self.rect = self.image.get_rect() self.rect.x = self.posX self.rect.y = self.posY def ImageRescaler(image,originalScaleTuple): #be sure to restrict to only proper ratios newImage = pygame.transform.scale(image,(int(DISPLAYS[displayIndex][0]*(originalScaleTuple[0]/100)), int(DISPLAYS[displayIndex][1]*(originalScaleTuple[1]/100)))) return newImage def resizeDisplay(): screen = pygame.display.set_mode(DISPLAYS[displayIndex]) ## this is where you'd have'd probably want your sprite groups set to resize themselves ## Just gonna call it on icon here icon.updateSize() icon = Icon(100,100) while True: screen.fill((0,0,0)) for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() if event.type == KEYDOWN: if event.key == K_LEFT: displayIndex -=1 if displayIndex < 0: displayIndex = 5 resizeDisplay() elif event.key == K_RIGHT: displayIndex+=1 if displayIndex > 5: displayIndex = 0 resizeDisplay() screen.blit(icon.image,(icon.rect.x,icon.rect.y)) pygame.display.update()
The best way to go about this is by downscaling images, in order to preserve image quality. Here are two options: Option 1 This method is probably the fastest. Create images to be compatible with the largest resolution you are intending to support. Create a screen with the user's desired size. screen = pygame.display.set_mode((user_x, user_y)) Downscale images at load time image = pygame.image.load("a.png").convert_alpha() pygame.transform.scale(image, (screen.width() / your_max_width, screen.height() / your_max_height), DestSurface=image) Now, just blit them normally. It should run at normal speeds. Option 2 Create a screen with the user's desired size. screen = pygame.display.set_mode((user_x, user_y)) Next, create a pygame.Surface with the highest resolution you are intending to support. surface = pygame.Surface((1920, 1080)) Then, blit everything to that surface. surface.blit(image, rect) # Etc... After all blits are completed, downscale the surface (as necessary) to whatever resolution the user desires. Preferably, you would only allow resolutions with the same aspect ratio. pygame.transform.scale(surface, ((screen.width() / your_max_width, screen.height() / your_max_height), DestSurface=surface) Finally, blit the surface to the screen, and display it. screen.blit(surface, (0, 0)) pygame.display.update() This process (downscaling) allows you to preserve image quality while still allowing the user to choose their screen resolution. It will be slower because you are constantly manipulating images.
Overlay tiles onto sprite in pygame
I am creating a tile-based 2d overworld for a game - heavily influenced by Pokemon - using pygame/python, Tiled for .tmx files, and the tmx library by Richard Jones. The code I'm using is mostly based on this wonderful demo of Pallet Town in python. The game runs just fine, however, I am having problems with making tiles on the map (e.g. houses, trees) overlap the player sprite when it would make sense for the player sprite to disappear behind them. For example: in the image here, principles of depth perception would tell us that the house in the foreground should occlude the player in the background, but because the map is 2D there is no depth and therefore no occlusion. I would love to add depth, but seeing as I'm very new to pygame (and python in general), I'm at a loss at how to draw the relevant foreground objects over the sprite. Luckily I'm not alone in this problem and plenty of documentation on possible solutions exist. For example: this StackExchange question this LibGDX tutorial this Unity tutorial However, this code isn't typically written for python and I'm not sure how to implement it in my situation. Sorting/drawing by z position (or by a 'depth' property) seems like the most sensible thing to do, but looking at the tmx library I can only find x and y values mentioned. Adding the player sprite to an empty object layer in Tiled is also a solution, but once again I'm unsure of how to do this and all my attempts have led to error messages. (Attempts not detailed here because I honestly don't know what I did and it didn't work anyway.) My current code is as follows: class Player(pygame.sprite.Sprite): def __init__(self, location, collStart, orientation, *groups): super(Player, self).__init__(*groups) self.image = pygame.image.load('sprites/player.png') self.imageDefault = self.image.copy() self.rect = pygame.Rect(location, (26,26)) self.collider = pygame.Rect(collStart, (13,13)) self.orient = orientation self.holdTime = 0 self.walking = False self.dx = 0 self.step = 'rightFoot' # Set default orientation self.setSprite() self.speed = pygame.time.get_ticks() + 50 # slows down walking speed by .5 sec (current time + 50 ms) def setSprite(self): # this function contains information about where to find which sprite in the sprite sheet, probably not relevant here. def update(self, dt, game): key = pygame.key.get_pressed() if pygame.time.get_ticks() >= self.speed: self.speed = pygame.time.get_ticks() + 50 # Setting orientation and sprite based on key input, removed the #code here because it wasn't relevant #[....] # Walking mode enabled if a button is held for 0.1 seconds if self.holdTime >= 100: self.walking = True lastRect = self.rect.copy() lastColl = self.collider.copy() # collider covers the bottom section of the sprite # Code for walking in the direction the player is facing, not relevant here #[....] # Collision detection: # Reset to the previous rectangle if player collides # with anything in the foreground layer if len(game.tilemap.layers['triggers'].collide(self.collider, 'solid')) > 0: self.rect = lastRect self.collider = lastColl # Area entry detection, loads dialog screen from the dialog file: elif len(game.tilemap.layers['triggers'].collide(self.collider, 'entry')) > 0: entryCell = game.tilemap.layers['triggers'].find('entry')[0] game.fadeOut() run() pygame.quit() quit() return if self.dx == 16: # Makes the player appear to take steps w/ different feet, not relevant here #[....] # After traversing 32 pixels, the walking animation is done if self.dx == 32: self.walking = False self.setSprite() self.dx = 0 game.tilemap.set_focus(self.rect.x, self.rect.y) class Game(object): def __init__(self, screen): self.screen = screen def initArea(self, mapFile): """Load maps and initialize sprite layers for each new area""" self.tilemap = tmx.load(mapFile, screen.get_size()) self.players = tmx.SpriteLayer() self.objects = tmx.SpriteLayer() # In case there is no sprite layer for the current map except KeyError: pass else: self.tilemap.layers.append(self.objects) # Initializing player sprite startCell = self.tilemap.layers['triggers'].find('playerStart')[0] self.player = Player((startCell.px, startCell.py), (startCell.px, startCell.bottom-4), startCell['playerStart'], self.players) self.tilemap.layers.append(self.players) self.tilemap.set_focus(self.player.rect.x, self.player.rect.y) def main(self): clock = pygame.time.Clock() self.initArea('test tilemap.tmx') while 1: dt = clock.tick(30) for event in pygame.event.get(): if event.type == pygame.QUIT: return if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE: return self.tilemap.update(dt, self) screen.fill((0,0,0)) self.tilemap.draw(self.screen) pygame.display.flip() Once again, I'm using the tmx library found here. Maybe something needs to be changed there? Hopefully, someone can help me figure this out. It's definitely possible, as shown by this pokemon clone in python (no source code available, sadly). Also, first-time StackOverflow user so let me know if I'm committing any faux-passes :)
Figured it out! As suggested by Kingsley in the comments, the solution was to change the draw order of the layers. Layers were being drawn in order of a list in the Layers class, with the player sprite having the highest index and thus being drawn on top of everything else. Moving the player between the background and foreground layer in the list made it appear behind the foreground objects. To do this I added the following code to the initArea function in the Game class: def initArea(self, mapFile): """Load maps and initialize sprite layers for each new area""" self.tilemap = tmx.load(mapFile, screen.get_size()) self.players = tmx.SpriteLayer() self.objects = tmx.SpriteLayer() # Initializing player sprite startCell = self.tilemap.layers['triggers'].find('playerStart')[0] self.player = Player((startCell.px, startCell.py), (startCell.px, startCell.bottom-4), startCell['playerStart'], self.players) foregroundItem = self.tilemap.layers.__getitem__("foreground") # finds the layer called foreground foregroundIndex = self.tilemap.layers.index(foregroundItem) # finds the position of the foreground layer in the Layers list (Layers class specified in .tmx file) self.tilemap.layers.insert(foregroundIndex-1, self.players) # move the Player layer one layer below the foreground layer self.tilemap.set_focus(self.player.rect.x, self.player.rect.y) I'll experiment a bit more tonight, but for now this solution seems to work. Thanks for the help!
Pygame Inquiry- Generating Multiple Sprites in Same Class
Unimportant Preamble: Hello, I'm using Python and Pygame to create a game. This is for the purpose of improving my programming skills rather than a serious attempt at game creation. I've taken a break from Python lately for Objective C, but I'm now returning to it. This is a problem that I was having before I took a brief break, and I've returned to a question that was originally puzzling me. I've spent quite a while researching it, and I have a suspicion I've come across the solution and merely failed to understand it. I apologize for some of the bad naming conventions/lack of comments. I'm working on improving that. Substance of Question: Anyway, I've attached the four images I'm using. The program uses a simple function to position various Tiles on the screen. The mouse cursor is a sword. It is the entire image, but I'll be changing that later. I've made the program type "blue" in the shell whenever the cursor collides with a Tile. My goal is to have this happen when it collides with "ANY" tile of that color. Long-term, I want to be able to modify the properties of these tile sprites. Various game-pieces would interact, and I would need to save the state of each sprite. I'd also be setting interactions for the other sprites. Right now the sprites are all generating images, but my collision rectangle for the Tile is simply moving after each image is generated. I suppose that makes sense given the code, but I need a way to multiple sprites, each with a rectangle for collision. Thanks EDIT: I was unable to add images due to a new-user restriction. They are available enter link description here I think I read somewhere that people can (and do) edit posts here. So if anyone who the ability to move the images into this thread is welcome to do so. import random,math,sys,os import pygame from pygame.locals import * pygame.init() #Initializing Pygame #Colors black=(0,0,0) #Screen screen=pygame.display.set_mode((1200,800),0,0) pygame.display.set_caption("Nero's Sandbox") pygame.mouse.set_visible(False) clock=pygame.time.Clock() fps=40 #Game Functions: def terminate(): pygame.quit() sys.exit() def numgen(x,y): return random.randint(x,y) #Loop Variables tri=2 #Groups: allsprites = pygame.sprite.Group() alltiles = pygame.sprite.Group() allmice = pygame.sprite.Group() #Mouse Classes class Pointy(pygame.sprite.DirtySprite): def __init__(self): pygame.sprite.Sprite.__init__(self) self.image = pygame.image.load('redsword.png').convert() #31x32 image self.image.set_colorkey(black) self.rect=self.image.get_rect() self.set=pygame.sprite.Group() self.add(allmice, allsprites, self.set) pygame.sprite.RenderPlain((self.set,allmice,allsprites)) def update(self): screen.fill(black) alltiles.draw(screen) if event.type == pygame.MOUSEMOTION: pos = pygame.mouse.get_pos() self.rect.topright = pos self.set.draw(screen) #Tile Sprites - only one rect is being recognized. class Tile(pygame.sprite.Sprite): def __init__(self, graphic): pygame.sprite.Sprite.__init__(self) self.image = pygame.image.load(graphic).convert() self.image = pygame.transform.scale((self.image),(50,50)) self.rect=self.image.get_rect() self.add(alltiles, allsprites) self.set=pygame.sprite.RenderPlain((self)) def update(self, x, y): pos = (x,y) self.rect.topleft = pos #Micers pointy1=Pointy() #Game Loops while True: #Ensures all loops within program are constantly called when conditions are met. screen.fill(black) while tri==2: for event in pygame.event.get(): if event.type == QUIT: terminate() pygame.display.flip() x = 0 y = 50 w = 0 while x!=600: x=x+50 w = w+1 if w%2==0: purpletile1=Tile('purplesquare.png') purpletile1.set.update(x,y) purpletile1.set.draw(screen) else: c=numgen(1,2) if c==1: bluetile1=Tile('lightbluesquare.png') bluetile1.set.update(x,y) bluetile1.set.draw(screen) if c==2: redtile1=Tile('redsquare.png') redtile1.set.update(x,y) redtile1.set.draw(screen) if x>=600 and y!=450: if y<450: x = 0 y = y+50 w=w-1 if y>=450: tri=3 while tri==3: for event in pygame.event.get(): if event.type == QUIT: terminate() alltiles.draw(screen) pointy1.set.update() pointy1.set.draw(screen) pygame.display.flip() clock.tick(fps) if pygame.sprite.collide_rect(pointy1,bluetile1): print('blue')
I had this same problem myself! I did some debugging, and it appeared that all instances of my class shared the same instance of pygame.Rect() You may want to change the line: pygame.sprite.Sprite.__init__(self) to super.__init__(self) This way, pygame.sprite.Sprite's init will set attributes to your tile. I could be wrong, I'm not entirely familiar with python's inheritance syntax, but that is the way I do it. I could also be the get_rect that is causing the same rectangle to be used for all classes, but that doesn't seem to be likely. I hope that I was some help, and just remember that pygame.Rect is an object, so somehow you are instantiating only once.
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.