I'm making a game in python with pygame.
I've made a parent class Items with 4 children.
As far as I can see, they should all be moving at speed 'dx' as defined in the Items class.
However, when I run the program, some of them are much faster than others. It seems to be inconsistent as to which colour items are faster, too.
import pygame
from random import randint
WIDTH, HEIGHT = 800, 600
WINDOW = pygame.display.set_mode((WIDTH, HEIGHT))
FPS = 60
clock = pygame.time.Clock()
gravity = 15
# Colours
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
CYAN = (0, 255, 255)
def draw_window():
WINDOW.fill(WHITE)
for item in items:
item.draw_item()
pygame.display.update()
def main():
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
clock.tick(FPS)
for item in items:
item.movement()
draw_window()
pygame.quit()
class Item:
def __init__(self, x):
self.x = x
self.radius = 10
self.y = randint(100, 500) + self.radius
self.dx = -2
def perform_action(self):
pass
def movement(self):
self.x += self.dx
if self.x < 0 - WIDTH:
self.x = WIDTH + randint(0, 300)
self.y = HEIGHT - 40 - randint(0, 400)
def draw_item(self, colour):
pygame.draw.circle(WINDOW, colour, (self.x, self.y), self.radius)
class GravUp(Item):
def __init__(self, x):
super().__init__(x)
def draw_item(self):
super().draw_item(RED)
def perform_action(self):
global gravity
gravity += 3
class GravDown(Item):
def __init__(self, x):
super().__init__(x)
def draw_item(self):
super().draw_item(GREEN)
class AgilUp(Item):
def __init__(self, x):
super().__init__(x)
def draw_item(self):
super().draw_item(CYAN)
item_types = {0: GravUp(WIDTH + randint(0, 500)),
1: GravDown(WIDTH + randint(0, 500)),
2: AgilUp(WIDTH + randint(0, 500))}
items = []
for i in range(10):
items.append(item_types[randint(0, 2)])
if __name__ == '__main__':
main()
When printing out the addresses of the created objects you will see why there are different speeds:
<__main__.AgilUp object at 0x0BD30E70>
<__main__.AgilUp object at 0x0BD30E70>
<__main__.GravUp object at 0x0BD30550>
<__main__.AgilUp object at 0x0BD30E70>
<__main__.GravUp object at 0x0BD30550>
<__main__.GravUp object at 0x0BD30550>
<__main__.GravDown object at 0x0BD306F0>
<__main__.GravDown object at 0x0BD306F0>
<__main__.AgilUp object at 0x0BD30E70>
<__main__.AgilUp object at 0x0BD30E70>
So you don't have 10 objects, but only three, and 10 random pointers to one of these. In this example, the movement instructions will call 5 times AgilUp, 3 times GravUp and 2 times GravDown.
Redesigning the object creation process should fix the issue.
The answer to your random speed behavior resides within the following lines of code in your program.
item_types = {0: GravUp(WIDTH + randint(0, 500)),
1: GravDown(WIDTH + randint(0, 500)),
2: AgilUp(WIDTH + randint(0, 500))}
items = []
for i in range(10):
items.append(item_types[randint(0, 2)])
What this bit of code is doing is indeed defining your three colored circles. Then, it is randomly assigning a number of times (one out of ten ) each circle will be moved and redrawn.
When I added in a print operation within the "draw_item" function and ran the program, I was able to determine the number of drawing/redrawing calls each circle was getting by printing out the item's RGB color value.
(0, 255, 0)
(255, 0, 0)
(255, 0, 0)
(255, 0, 0)
(0, 255, 0)
(0, 255, 0)
(0, 255, 255)
(255, 0, 0)
(0, 255, 0)
(0, 255, 255)
If you tally up the quantities, the green circle gets four calls to move and redraw, the red circle gets four calls to move and redraw, and the cyan circle gets two calls to move and redraw. Running this again would probably produce a different mix of calls for each color.
Reviewing your code, I am guessing what you ultimately wanted to do was create ten circles with random colors at random positions. If that is actually what you want to do, you will need to revise the block of code to derive a random number first, and then based upon that number, populate the item group with the random item type.
I hope that clarifies things.
Regards.
Related
I am trying to draw squares in random positions and random rgb values and I want 1000 of them to be created. The problem I'm facing is that everytime the loop for drawing occurs, it randomizes it all again, is there any way to make this not happen
import pygame
import sys
import random
pygame.init()
win = pygame.display.set_mode((800,600))
pygame.display.set_caption("Simulation")
def safeZone():
#Draws a top rectangle
pygame.draw.rect(win, (50,205,50), (0, 0, 800, 100))
def dot():
width = 10
height = 10
spawnX = random.randrange(1, 801)
spawnY = random.randrange(1, 601)
r = random.randrange(1, 256)
g = random.randrange(1, 256)
b = random.randrange(1, 256)
pygame.draw.rect(win, (r, g, b), (spawnX, spawnY, width, height))
def population(size):
for x in range(size):
dot()
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
win.fill((255, 255, 255))
safeZone() # Always draw dots after safe zone
population(1000)
pygame.display.update()
pygame.quit()
Create a dot collection, then just draw that dot collection. Now you can update the dot positions separately, and they will redraw in the new positions. Here, I'm having each dot move a random amount in every loop.
import pygame
import sys
import random
pygame.init()
win = pygame.display.set_mode((800,600))
pygame.display.set_caption("Simulation")
class Dot:
def __init__(self):
self.spawnX = random.randrange(0, 800)
self.spawnY = random.randrange(0, 600)
self.r = random.randrange(0, 256)
self.g = random.randrange(0, 256)
self.b = random.randrange(0, 256)
def safeZone():
#Draws a top rectangle
pygame.draw.rect(win, (50,205,50), (0, 0, 800, 100))
def drawdot(dot):
width = 10
height = 10
pygame.draw.rect(win, (dot.r, dot.g, dot.b), (dot.spawnX, dot.spawnY, width, height))
def population(dots):
for dot in dots:
dot.spawnX += random.randrange(-3,4)
dot.spawnY += random.randrange(-3,4)
drawdot(dot)
alldots = [Dot() for _ in range(1000)]
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
win.fill((255, 255, 255))
safeZone() # Always draw dots after safe zone
population(alldots)
pygame.display.update()
A worthwhile modification is to store the whole rectangle in the object:
...
class Dot:
def __init__(self):
self.location = [
random.randrange(0, 800),
random.randrange(0, 600),
10, 10
]
self.color = (
random.randrange(0, 256),
random.randrange(0, 256),
random.randrange(0, 256)
)
def move(self, dx, dy ):
self.location[0] += dx
self.location[1] += dy
def drawdot(dot):
pygame.draw.rect(win, dot.color, dot.location)
def population(dots):
for dot in dots:
dot.move( random.randrange(-3,4), random.randrange(-3,4) )
drawdot(dot)
...
You call a function dot() in which you have assigned randomization. You should introduce a piece of code that randomizes the values outside of the dot() function, and store them in a separate array, and then call the function.
Your description sounds like you aren't necessarily trying to store the result so much as you want the process to be the same every time, but still sort of random? You could just use a hard-coded seed?
import random
random.seed(10)
print(random.random())
See this link for more detail: Random Seed
In this program when the user types more text I want the rectangle to automatically get longer when the user types to keep the letters inside of the rectangle. However, it doesn't update the rectangle when the text gets longer. How do I fix this?
from pygame import *
init()
screen = display.set_mode((800, 600))
name_font = font.Font(None, 32)
name_text = ''
class Rectangle:
def __init__(self, x, y):
self.x = x
self.y = y
self.input_rect = Rect(x, y, 140, 32)
self.text_surface = name_font.render(name_text, True, (255, 255, 255))
color = Color('lightskyblue3')
draw.rect(screen, color, self.input_rect, 2)
self.input_rect.w = self.text_surface.get_width() + 10
screen.blit(self.text_surface, (self.input_rect.x + 5, self.input_rect.y + 5))
def naming():
global name_text
if events.type == KEYDOWN:
if keys[K_BACKSPACE]:
name_text = name_text[:-1]
screen.fill((0, 0, 0))
rect_1 = Rectangle(200, 200)
else:
name_text += events.unicode
while True:
rect_1 = Rectangle(200, 200)
for events in event.get():
keys = key.get_pressed()
naming()
if events.type == QUIT:
quit()
display.update()
time.delay(1)
The Rectangle.text_surface is a PyGame Surface. So you can easily get the precise width of the bounding box by simply calling self.text_surface.get_width().
But you start the size of the border-rect at 140, so this size has to be the maximum of 140 or whatever the new (longer) width is. Another problem is that when the rectangle re-sizes, the old rectangle is left behind. So whenever we now re-draw the rectangle, it erases the background to black.
This is all pretty easily encapsulated into the exiting __init__():
def __init__(self, x, y):
self.x = x
self.y = y
self.text_surface = name_font.render(name_text, True, (255, 255, 255))
rect_width = max( 140, 10 + self.text_surface.get_width() ) # Adjust the width
color = Color('lightskyblue3')
self.input_rect = Rect(x, y, rect_width, 32) # Use new width (if any)
draw.rect(screen, (0,0,0) , self.input_rect, 0) # Erase any existing rect
draw.rect(screen, color, self.input_rect, 2)
self.input_rect.w = self.text_surface.get_width() + 10
screen.blit(self.text_surface, (self.input_rect.x + 5, self.input_rect.y + 5))
I am developing a small target shooter game for school coursework. I have hit an an impasse after about an hour and a half of iteration and testing. If you see the code below, I have used lists to make it so that when bullets hit a target, they are removed from the list and are no longer printed with 'pygame.draw.rect' but it seems to be much more difficult for the targets as I have made these using OOP and not a one off function. I have tried replicating the bullet lists with the targets, putting the 'IF' statement in the same places etc. but I end up having the same outcome which is the target is hit and 'hit' is printed. The bullet disappears but the target doesn't. I have only been using the language for about a month so although I'm getting more used to it, I still am no expert and I really have just hit a dead end here. Any sort of help would be greatly appreciated. It may be a case of me having to completely rethink my approach and change my code drastically but perhaps theres something you can see which I can not. Thanks for any help given. (Sorry for the messiness of the code, it is after a lot of changes and iterations. Also please note that I have currently only coded to test it on target_1 to save time)
import pygame
#Setting window dimensions and caption. (Module 1)
pygame.init()
window = pygame.display.set_mode((800, 575))
pygame.display.set_caption("TARGET PRACTICE")
#Colour variables. (Module 1)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (200, 0, 0)
GREEN = (0, 200, 0)
BLUE = (0, 0, 200)
#py_clock tracks framerate of program for other 'pygame.time' commands. (Module 8)
py_clock = pygame.time.Clock()
#Target class created. (Module 5)
class Target:
def __init__(self, x, y, h, w, v):
self.x = x
self.y = y
self.h = h
self.w = w
self.v = v
def hit(self):
print('hit')
all_bullets_keep.remove(item)
all_targets.remove(all_targets[0])
pass
#Instantiation of targets. (Module 5)
target_1 = Target(0, 80, 60, 40, 0.05)
target_2 = Target(0, 100, 60, 40, 0.5)
target_3 = Target(0, 50, 60, 40, 0.5)
target_4 = Target(0, 75, 60, 40, 0.5)
target_5 = Target(0, 45, 60, 40, 0.5)
target_6 = Target(0, 85, 60, 40, 0.5)
#Instantiation of hitboxes. (Module 9)
target_hbx1 = Target(-5, 75, 70, 50, 0.05)
target_hbx2 = Target(-5, 95, 70, 50, 0.5)
target_hbx3 = Target(-5, 45, 70, 50, 0.5)
target_hbx4 = Target(-5, 70, 70, 50, 0.5)
target_hbx5 = Target(-5, 40, 70, 50, 0.5)
target_hbx6 = Target(-5, 80, 70, 50, 0.5)
#Declaring variables to be used in the while loop. (Module 5)
clock = 0
target_2_threshold = 500
target_3_threshold = 1000
target_4_threshold = 1500
target_5_threshold = 2000
target_6_threshold = 2500
#Setting player sprite dimension variables. (Module 6)
player_sprite_x = 357.5
player_sprite_y = 450
player_sprite_h = 125
player_sprite_w = 85
#all_bullets list to store bullets made by function inside loop. (Module7)
all_bullets = []
all_targets = []
all_targets.append(target_1)
all_targets.append(target_2)
all_targets.append(target_3)
all_targets.append(target_4)
all_targets.append(target_5)
all_targets.append(target_6)
#Variables to track and limit shooting function. (Module 9.5)
bullet_delay = 1500
next_bullet_time = 0
exec = True
while exec:
#current_time uses a pygame_time command to track ticks. (Module 9.5)
current_time = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == pygame.QUIT:
exec = False
#'IF' statement to trigger the shooting function. (Module 7)
if event.type == pygame.MOUSEBUTTONDOWN:
#Condition set to only trigger the below code if the current_time is greater than the next_bullet time. (Module 9.5)
if event.button == 1 and current_time > next_bullet_time:
next_bullet_time = current_time + bullet_delay
dx = event.pos[0] - (player_sprite_x+ player_sprite_w//2)
dy = event.pos[1] - player_sprite_y
direction = pygame.math.Vector2(dx, dy).normalize()
bullet = {'x': player_sprite_x+42, 'y': player_sprite_y, 'direction': direction}
all_bullets.append(bullet)
#Defines movement of targets and sets delay between drawings. (Module 5)
for item in all_targets:
target_1.x += target_1.v
target_hbx1.x += target_hbx1.v
if clock > target_2_threshold:
target_2.x += target_2.v
target_hbx2.x += target_hbx2.v
if clock > target_3_threshold:
target_3.x += target_3.v
target_hbx3.x += target_hbx3.v
if clock > target_4_threshold:
target_4.x += target_4.v
target_hbx4.x += target_hbx4.v
if clock > target_5_threshold:
target_5.x += target_5.v
target_hbx5.x += target_hbx5.v
if clock > target_6_threshold:
target_6.x += target_6.v
target_hbx6.x += target_hbx6.v
#all_bullets_keep list combined with FOR loop retains only bullets in the arena. (Module 7)
all_bullets_keep = []
for item in all_bullets:
item['x'] += item['direction'][0] # item['direction'][0] * 2
item['y'] += item['direction'][1] # item['direction'][1] * 2
if 0 < item['x'] < 800 and 0 < item['y'] < 575:
all_bullets_keep.append(item)
all_bullets = all_bullets_keep
#Fill the background (Module 5)
window.fill(RED)
#Redraw each target in every frame. (Module 5)
all_targets_keep = []
for item in all_targets:
pygame.draw.rect(window, BLUE, (target_1.x, target_1.y, target_1.h, target_1.w))
pygame.draw.rect(window, BLUE, (target_hbx1.x, target_hbx1.y, target_hbx1.h,target_hbx1.w), 2)
if 0 < target_1.x < 800 and 0 < target_1.y < 575:
all_targets_keep.append(target_1)
if clock > target_2_threshold:
pygame.draw.rect(window, BLUE, (target_2.x, target_2.y, target_2.h, target_2.w))
pygame.draw.rect(window, BLUE, (target_hbx2.x, target_hbx2.y, target_hbx2.h,target_hbx2.w), 2)
all_targets_keep.append(target_2)
if clock > target_3_threshold:
pygame.draw.rect(window, BLUE, (target_3.x, target_3.y, target_3.h, target_3.w))
pygame.draw.rect(window, BLUE, (target_hbx3.x, target_hbx3.y, target_hbx3.h,target_hbx3.w), 2)
all_targets_keep.append(target_3)
if clock > target_4_threshold:
pygame.draw.rect(window, BLUE, (target_4.x, target_4.y, target_4.h, target_4.w))
pygame.draw.rect(window, BLUE, (target_hbx4.x, target_hbx4.y, target_hbx4.h,target_hbx4.w), 2)
all_targets_keep.append(target_4)
if clock > target_5_threshold:
pygame.draw.rect(window, BLUE, (target_5.x, target_5.y, target_5.h, target_5.w))
pygame.draw.rect(window, BLUE, (target_hbx5.x, target_hbx5.y, target_hbx5.h,target_hbx5.w), 2)
all_targets_keep.append(target_5)
if clock > target_6_threshold:
pygame.draw.rect(window, BLUE, (target_6.x, target_6.y, target_6.h, target_6.w))
pygame.draw.rect(window, BLUE, (target_hbx6.x, target_hbx6.y, target_hbx6.h,target_hbx6.w), 2)
all_targets_keep.append(target_6)
all_targets = all_targets_keep
#Draw the player sprite. (Module 6)
pygame.draw.rect(window, BLUE, (player_sprite_x, player_sprite_y, player_sprite_w, player_sprite_h))
#Draw each item in all_bullets. (Module 7)
for item in all_bullets:
pygame.draw.rect(window, BLUE, (item['x']-5, item['y']-5, 10, 10))
b_hitbox = (item['x']-10, item['y']-10, 20, 20)
pygame.draw.rect(window, BLUE, b_hitbox, 2)
for item in all_bullets_keep:
if item['y']-30 < (target_hbx1.y) + (target_hbx1.h) and item['y']+30 > target_hbx1.y:
if item['x']+10 > target_hbx1.x and item['x']-30 < (target_hbx1.x) + (target_hbx1.w):
target_1.hit()
pygame.display.update()
#tick_busy_loop limits number of times the game can refresh per second. (Module 8)
py_clock.tick_busy_loop(120)
pygame.quit()
There's a few minor bugs in your code. I think the target is not disappearing because of something going on in with the all_targets_keep list. It looks like the code is re-adding the target to the "keep" list, whether it's been hit or not.
Your approach to using a class to hold all the Target code is a good idea. But all the targeting code is still spread throughout your main loop. This is causing code-clutter and making the task more difficult.
By moving this code into the class, it frees the code from having a "special case" for each of the targets. The target knows everything about itself, and can perform any test inside itself. For example, the drawing of the target to the screen.
class Target:
def __init__(self, x, y, h, w, v, threshold):
self.x = x
self.y = y
self.h = h
self.w = w
self.v = v
self.threshold = threshold
self.hit = False
def draw( self, window ):
# body
pygame.draw.rect( window, BLUE, ( self.x, self.y, self.w, self.h ), 0 )
# hit-box
pygame.draw.rect( window, BLUE, ( self.x-5, self.y-5, self.w+10, self.h+10 ), 1 )
When the target has a member function to draw itself, that whole-whack of drawing code goes away to become a single, simple function. Then the drawing of all targets becomes:
clock += 1
for item in all_targets:
if ( clock > item.threshold and not item.hit ):
item.draw( window )
The target's "hit box" is also just a function of the existing points, there's no need to keep these separate. So, similarly, using a PyGame rect, a target can check whether it has been hit by a bullet:
class Target:
...
def collidesWith( self, bullet_pos ):
# hit-box is 5 pixels offset from target
target_rect = pygame.Rect( self.x-5, self.y-5, self.w+10, self.h+10 )
self.hit = target_rect.collidepoint( bullet_pos )
return self.hit
Anyway, you're making good progress. But what commenter #importrandom says is true - it would really be easier (eventually) for you to use the PyGame built-in sprite classes. They already take care of a lot of the code you're writing yourself. If you don't want to, that's fine, it's your code.
I'm trying to build a game that involves a small red player (rectangle) being controlled by the arrow keys. There's a white grid on top of a black background, 2 different coloured objectives (rectangles), and several random red boxes (rectangles).
The part I need help with is moving the small red player. I can move it, but it seems to draw itself in the new position, but the version of the rectangle that was previously drawn stays there, forming a line. I want the entire rectangle to move and not leave any traces/previous versions of itself.
According to some other posts, I've heard that the only way to do this is to fill the screen with the background colour (in this case, black) and redraw the players on top of it; however, this is really hard in my case as I have the red boxes and objectives placed randomly, so every time I draw them again, they draw in a new random position, not in their old positions. I want the red boxes and objectives to stay in the same position, but have the player rectangles move around (while basically deleting the older versions of themselves).
Here's the code I currently have (I've excluded the basics, like defining colours, imports, and setting the screen height/width):
p1_velocity_x = 0
p1_velocity_y = 0
p2_velocity_x = 0
p2_velocity_y = 0
def grid():
for col in range(0, screen_width - 100, 10):
for row in range(0, screen_height, 10):
screen.set_at((row, col), white)
def red_boxes():
for i in range(100):
pygame.draw.rect(screen, red, (randrange(1, 1200), randrange(1, 470), 25, 25))
def blue_score_box():
pygame.draw.rect(screen, blue, (randrange(1, 1200), randrange(1, 470), 25, 25))
def yellow_score_box():
pygame.draw.rect(screen, yellow, (randrange(1, 1200), randrange(1, 470), 25, 25))
class Player:
color = (0, 0, 0)
def __init__(self, color):
self.x = 0
self.y = 0
self.color = color
p1 = Player(red)
p1.x = randrange(1, 1200, 10)
p1.y = randrange(1, 470, 10)
p2 = Player(yellow)
p2.x = randrange(1, 1200, 10)
p2.y = randrange(1, 470, 10)
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
p1.x += 10
screen.fill(black)
grid()
red_boxes()
yellow_score_box()
blue_score_box()
pygame.draw.rect(screen, p1.color, (p1.x, p1.y, 10, 10))
pygame.draw.rect(screen, p2.color, (p2.x, p2.y, 10, 10))
pygame.display.update()
The result of the above code is that I'm able to move the red rectangle as I want, but since I call the red_boxes(), yellow_score_box(), and blue_score_box() methods in the while loop, they keep calling indefinitely, drawing random red, blue, and yellow rectangles all over the screen almost every second. I want them to stay in one place while having the functionality of moving the player as I do now.
Create a pygame.Surface with the same size than the red player rectangle:
bkP1 = pygame.Surface((10, 10))
Backup the background (pygame.Surface.blit) and store the position of the player, before the player is drawn:
prevPos = (p1.x, p1.y)
bkP1.blit(screen, (0, 0), (*prevPos, 10, 10))
Draw the background on top of the player when the player has to be erased:
screen.blit(bkP1, prevPos)
The process may work as follows:
prevPos = None
bkP1 = pygame.Surface((10, 10))
while True:
# [...]
if prevPos:
screen.blit(bkP1, prevPos)
prevPos = (p1.x, p1.y)
bkP1.blit(screen, (0, 0), (*prevPos, 10, 10))
pygame.draw.rect(screen, p1.color, (p1.x, p1.y, 10, 10))
Of course it is possible to add the mechanism to the class Player:
class Player:
def __init__(self, color):
self.x = 0
self.y = 0
self.color = color
self. prevPos = None
self.bk = pygame.Surface((10, 10))
def draw(self, screen):
if self.prevPos:
screen.blit(self.bk, self.prevPos)
self.prevPos = (self.x, self.y)
self.bk.blit(screen, (0, 0), (*self.prevPos, 10, 10))
pygame.draw.rect(screen, self.color, (self.x, self.y, 10, 10))
while True:
# [...]
p1.draw(screen)
The code below draws a black background, a white box and text. However it seems that the drawing order is not determined and I sometimes have the text or the rectangle covered by the background rectangle. I'm trying to understand what the appropriate way to manage this is?
import sdl2.ext
import sdl2.sdlttf
from sdl2 import SDL_Color, SDL_Init
WHITE = SDL_Color(255, 255, 255)
class Entity(sdl2.ext.Entity):
def __init__(self, world, sprite, posx=0, posy=0):
self.sprite = sprite
self.sprite.position = posx, posy
sdl2.ext.init()
window = sdl2.ext.Window("PySDL2", size=(320, 240))
window.show()
world = sdl2.ext.World()
texture_renderer = sdl2.ext.Renderer(window)
spriterenderer = sdl2.ext.TextureSpriteRenderSystem(texture_renderer)
factory = sdl2.ext.SpriteFactory(sdl2.ext.TEXTURE, renderer=texture_renderer)
world.add_system(spriterenderer)
sdl2.sdlttf.TTF_Init()
font = sdl2.sdlttf.TTF_OpenFont('resources/Helvetica.dfont',32)
text_surface = sdl2.sdlttf.TTF_RenderText_Blended(font, 'test', WHITE).contents
sdl2.sdlttf.TTF_CloseFont(font)
bg = factory.from_color(sdl2.ext.Color(0, 0, 0), size = (320,240))
Entity(world, bg, 0, 0)
c = factory.from_color(sdl2.ext.Color(255, 255, 255), size = (50,50))
Entity(world, c, 100, 100)
text_sprite = factory.from_surface(text_surface)
text_entity = Entity(world, text_sprite, 50, 50)
def run():
running = True
while running:
events = sdl2.ext.get_events()
for event in events:
if event.type == sdl2.SDL_QUIT:
running = False
break
world.process()
run()
sdl2.ext.quit()
The default implementation of the TextureSpriteRenderSystem uses the depth attribute of Sprite object to determine the drawing order. If all sprites feature the same depth, the drawing order of them is undetermined, which results in your unwanted behaviour.
A Sprite with a depth value of 0 will be drawn below (or prior to) a Sprite with a higher depth value, e.g. 10. The default depth for each Sprite being created is 0. You could set your background's and rectangle's depth to a negative value in order to enforce the drawing order:
bg = factory.from_color(sdl2.ext.Color(0, 0, 0), size = (320,240))
bg.depth = -99 # always below everything
Entity(world, bg, 0, 0)
c = factory.from_color(sdl2.ext.Color(255, 255, 255), size = (50,50))
c.depth = -1
Entity(world, c, 100, 100)
You can read more about it in the PySDL2 sprite documentation.