I am trying to print a million rectangles in 2-3 seconds, however, I can not understand how to use parallelization with GUI.
class Rectangle:
def __init__(self, pos, color, size):
self.pos = pos
self.color = color
self.size = size
def draw(self):
pygame.draw.rect(screen, self.color, Rect(self.pos, self.size))
I want to use something like open MP here to make this process faster
rectangles = []
for i in range(1000000):
random_color = (randint(0,255), randint(0,255), randint(0,255))
random_pos = (randint(0,639), randint(0,479))
random_size = (639-randint(random_pos[0], 639), 479-randint(random_pos[1],479))
rectangles.append(Rectangle(random_pos, random_color, random_size))
while True:
for event in pygame.event.get():
if event.type == QUIT:
exit()
screen.lock()
for rectangle in rectangles:
rectangle.draw()
screen.unlock()
pygame.display.update()
Here's an example that generates random rectangles in sub-processes. It's done this way, because it's not possible to draw to the main PyGame surface from anything other than the main thread/process. So if you want to generate items in real-time, do so, but then notify the main-thread somehow to include them. Perhaps by posting a PyGame Event.
The approach taken is to declare a shared subprocess.manager.list for the rectangles, then split the generation. The function rectGenerator() is given a position in the list from which to start, and a count to stop at.
With rectGenerator() as the target, /N/ sub-processes are created. We then loop waiting for the processes to all finish. A nicer approach might be to smarter about this and only paint the blocks that are complete.
Running on my workstation, it takes a while to paint one million rectangles (~10 seconds). To see for yourself, change BLOCK_SIZE to 100000. Probably this could be optimised (it was my first time using multiprocessing for some years), but I'd rather make the code illustrative.
So as I said in my comment, for simple rectangles it's much faster to just do it without sub-processes. But if each sub-process was doing some kind of CPU-bound complex rendering (fractal generating, image filtering, ray-tracing, etc. etc.) then this is a good way to spread the workload amongst your CPUs/cores.
Further reading: The python GIL.
#! /usr/bin/env python3
import pygame
import random
import multiprocessing
CHILDREN = 10 # processes to use
BLOCK_SIZE = 1000 # rectangles per process
RECT_COUNT = BLOCK_SIZE * CHILDREN # ensure an even factor / subprocess
# Window size
WINDOW_WIDTH = 800
WINDOW_HEIGHT = 800
def randomColour():
""" Generate a random "non dark" colour """
MIN = 80 # minimum brightness of colour channel
MAX = 255
return ( random.randint( MIN, MAX ),
random.randint( MIN, MAX ),
random.randint( MIN, MAX ) )
def randomRect( border=5 ):
""" Generate a random PyGame Rect, within the window bounds """
MAX_HEIGHT = WINDOW_HEIGHT - border
MAX_WIDTH = WINDOW_WIDTH - border
# Generate a rect that stays within the screen bounds
x1 = random.randint( border, MAX_WIDTH )
y1 = random.randint( border, MAX_HEIGHT )
x2 = random.randint( border, MAX_WIDTH )
y2 = random.randint( border, MAX_HEIGHT )
if ( x2 < x1 ):
xswap = x2
x2 = x1
x1 = xswap
if ( y2 < y1 ):
yswap = y2
y2 = y1
y1 = yswap
return pygame.Rect( x1, y1, x2-x1, y2-y1 )
class colourRect:
""" Simple class for holding components of a coloured rectangle """
def __init__( self ):
self.rect = randomRect()
self.colour = randomColour()
def draw( self, surface ):
pygame.draw.rect( surface, self.colour, self.rect, 1 )
def __str__( self ):
s = "rect[ %d,%d w=%d, h=%d ], colour[ %d, %d, %d ]" % ( self.rect.x, self.rect.y, self.rect.width, self.rect.height, self.colour[0], self.colour[1], self.colour[2] )
return s
###
### Multiprocessing Target Function
###
def rectGenerator( mp_list, index_from, count ):
""" Populate a section of the mp_list with randomly sized and coloured
rectangles, starting from the index, for the given count """
for i in range( count ):
mp_list[ index_from + i ] = colourRect()
###
### MAIN
###
pygame.init()
window = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), pygame.HWSURFACE )
pygame.display.set_caption("Plenty o' Rectangles")
# Make a *Huge* list of rectangles
# Note: this runs much faster than using multiprocessing
#all_rects = [ None for i in range( RECT_COUNT ) ] # pre-allocate list
#rectGenerator( all_rects, 0, RECT_COUNT )
print( "### Setting-up Generation" )
manager = multiprocessing.Manager()
all_rects = manager.list( range( RECT_COUNT ) ) # pre-allocate list
subprocesses = [] # keep the process handles so we can watch them
print( "### Starting Generation" )
for i in range( CHILDREN ):
# each subprocess populates a separate region of the rectangle array
p = multiprocessing.Process( target=rectGenerator, args=( all_rects, i*BLOCK_SIZE, BLOCK_SIZE ) )
p.start()
subprocesses.append( p )
print( "### Waiting for Generation" )
# Wait for the generation to complete
found_running = True
while ( found_running ):
found_running = False
for p in subprocesses:
if ( not p.is_alive() ):
p.join()
else:
found_running = True
print( "### Generation Complete" )
print( "--------------------------" )
# Main loop
clock = pygame.time.Clock()
running = True
while running:
time_now = pygame.time.get_ticks()
# Handle user-input
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
running = False
# Paint the window with rects, a lot of rects takes a while
print( "### Painting starts" )
window.fill( ( 0,0,0 ) )
for r in all_rects:
r.draw( window )
pygame.display.flip()
print( "### Painting ends" )
# Clamp FPS
clock.tick(2) #slow
pygame.quit()
Related
I don't know if my title was very clear, so I'm going to try to explain this more clearly here. So I have a Sprite in Pygame called spikes. I want there to be more than one spikes so I blit a second one. Problem is, my spike_collision box only works on the first one that I blit, and not the second one. Other than having to make a second collision box, how can I have the second spikes to have its own collision box as well?
Here's the blit code:
screen.blit(spikes, (spike_x, 500 - player_y + 476))
screen.blit(spikes, (spike_x + 200, 500 - player_y + 476))
Here's the collision-box code:
spike_collision = spikes.get_rect(topleft=(spike_x, 500 - player_y + 476))
Thanks.
I'm assuming when you write "sprite", you mean a bitmap-sprite, and not a pygame.sprite.Sprite.
It's probably best to maintain a sprite as a bitmap and a rectangle, then always draw the sprite at its rectangle, adjusting the rectangle to re-position the sprite, and using it for any collisions.
This could easily be done with list-pairs:
spike_image = pygame.image.load('spikes.png').convert_alpha()
spike_rect = spike_image.get_rect( )
spikes_a = [ spike_image, spike_rect( top_left=( 100, 100 ) )
spikes_b = [ spike_image, spike_rect( top_left=( 200, 200 ) )
# ...
screen.blit( spikes_a[0], spikes_a[1] )
screen.blit( spikes_b[0], spikes_b[1] )
# etc.
if ( spikes_a[1].colliderect( player_rect ) ):
print( "ouch!" )
However, it would behoove you to use a "proper" sprite object. Sure there's a bit of extra set-up, but it's repaid multiple times with ease of use:
class Spike( pygame.sprite.Sprite ):
def __init__( self, position=( 0, 0 ) ):
self.image = pygame.image.load('spikes.png').convert_alpha()
self.rect = self.image.get_rect( top_left = position )
def moveTo( self, position ):
self.rect = self.image.get_rect( top_left = position )
def moveBy( self, dx, dy ):
self.rect.move_ip( dx, dy )
spike_a = Spike( ( 100, 100 ) )
spike_b = Spike( ( 200, 200 ) )
spike_a.draw( window ) # get this for free
There's a whole lot of useful group & collision functionality that comes along with using Sprite objects, it's well worth a read: https://www.pygame.org/docs/ref/sprite.html
I'm checking missileGroup to see if any instances of missile collided with any instances enemy in enemyGroup. When run, it prints "Hit" for the first loop, but it ignores the second for loop. Why is that?
#### Imagine this is in a game loop ####
for missile in missileGroup:
if pygame.sprite.spritecollide(missile, enemyGroup, False) :
print("Hit")
for enemy in enemyGroup:
if pygame.sprite.spritecollide(enemy, missileGroup, False):
print("HI")
Update: #Rabbid76 stated that spritecollide wouldn't work because the spriteGroup enemyGroup is a list of sprites within a group(enemyGroup <- enemyList <- Enemy(sprite)) instead of a group of sprites(enemyGroup <- Enemy(sprite)). How would I access that?
Update 2 #paxdiablo stated that the first loop maybe emptying the group after iterating. I switched the places of the loops and 2nd loop ran, while the 1st did not.
Update 3 In the full code, .reset() method runs .kill() which removes the sprite from the group. Since the first loop removes the missile sprite before the second loop couldn't detect any collisions:
for missile in missileGroup:
if pygame.sprite.spritecollide(missile, enemyGroup, False) :
missile.reset()
for eachEnemy in enemyGroup:
if pygame.sprite.spritecollide(eachEnemy, missileGroup, False):
eachEnemy.reset()
See pygame.sprite.spritecollide():
Return a list containing all Sprites in a Group that intersect with another Sprite.
Therefore the arguments to spritecollide() must be a pygame.sprite.Sprite object and a pygame.sprite.Group object.
A list of pygame.sprite.Sprite objects instead of the Group does not work.
missileGroup = pygame.sprite.Group()
enemyGroup = pygame.sprite.Group()
for missile in missileGroup:
if pygame.sprite.spritecollide(missile, enemyGroup, False):
print("Hit")
for enemy in enemyGroup:
if pygame.sprite.spritecollide(enemy, missileGroup, False):
print("HI")
Furthermore read about kill()
The Sprite is removed from all the Groups that contain it.
Hence if you call kill() in the 1st loop, the 2nd loop won't work, because the sprite is removed from all Groups.
You call kill() in the reset methods. missile.reset() respectively eachEnemy.reset() causes the 2nd loop to fail.
There's no obvious reason, based on the information provided(a), why the second collision check should fail. If there's a collision between (for example) enemy #7 and missile #3, there should also be a collision between missile #3 and enemy #7.
You're not using any edge-case stuff like providing your own (possibly non-commutative) collision function, so it will simply use the sprite rectangle to detect this.
I'd be curious to see the behaviour when you reverse the order of the two loops in the code.
Also, you should specify the types of those group variables. If enemyGroup were something like an exhaustible generator rather than a list, it would be "emptied" by the first loop and then the second loop would have no items to iterate over(b) (the spritecollide call will be iterating over the group to check each item against the sprite).
That's about the only way, short of a bug in spritecollide itself, that you would see the effects you're describing.
By way of example, here's a piece of code that tries to iterate over a generator twice:
class gen3(object):
def __init__(self): self._num = 0
def __iter__(self): return self
def __next__(self):
if self._num == 3: raise StopIteration()
self._num += 1
return self._num - 1
gen = gen3()
print("A: ")
for i in gen: print(" ", i)
print("B: ")
for i in gen: print(" ", i)
The output shows that the second loop does nothing:
A:
0
1
2
B:
Lastly, a definitive way to check the state of the groups is to simply put the following code before each loop:
print("loop X enemy ", len(enemyGroup), enemyGroup)
print("loop X missile", len(missileGroup), missileGroup)
using a suitable value of X to distinguish between the two loops.
(a) Of course, there's always the possibility the information you've given is not fully accurate or complete (no malicious intent is supposed but sometimes people inadvertently skip what they consider to be unimportant details, that ends up being very important).
Example: there may be something happening between those two loops which is causing the issue. I'd prefer to give people the benefit of the doubt but you should probably let us know if that's the case.
(b) It would actually be emptied by the first iteration of the first loop so you'd find that it would probably only ever match for the first missile.
Here's a quick example that shows (in PyGame 1.9.6) this reported behaviour does not happen.
The example creates two sprite groups, then collides them in the exact same way as the OP's example code.
As well as printing, the sprites change from outline -> filled, depending on whether they believe they have participated in a collision. There's a 1:1 mapping of an Enemy colliding with a Missile, and vice-versa.
Apologies for the poor frame-rate...
import pygame
import random
# Window size
WINDOW_WIDTH = 800
WINDOW_HEIGHT = 800
DARK_BLUE = ( 3, 5, 54 )
RED = ( 200, 0, 0 )
YELLOW = ( 240, 250, 0 )
BLACK = ( 0, 0, 0 )
GREY = ( 200, 200, 200 )
GREEN = ( 250, 0, 0 )
TRANSPARENT=( 0,0,0,0 )
class Shape(pygame.sprite.Sprite):
def __init__(self, width=48, height=48):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface( ( width, height ), pygame.SRCALPHA)
self.rect = self.image.get_rect()
# Start position is randomly across the screen, and a little off the top
self.rect.center = ( random.randrange( 0, WINDOW_WIDTH ), random.randrange( 0, WINDOW_HEIGHT ) )
# Movement
self.dx = random.randrange( -2, 2 )
self.dy = random.randrange( -2, 2 )
# Looks like
self.filled = 2
self.last_fill = -1
self.render()
def setFilled( self, value ):
if ( value == True ):
self.filled = 0
else:
self.filled = 2
def update( self ):
if ( self.last_fill != self.filled ):
self.last_fill = self.filled
self.render()
self.rect.move_ip( self.dx, self.dy )
if ( self.rect.left > WINDOW_WIDTH ):
self.rect.x = -self.rect.width
elif ( self.rect.right < 0 ):
self.rect.left = WINDOW_WIDTH
if ( self.rect.y > WINDOW_HEIGHT ):
self.rect.y = 0
elif ( self.rect.y < 0 ):
self.rect.y = WINDOW_HEIGHT
class Square( Shape ):
def render( self ):
# Something to draw
if ( self.filled == 0 ):
self.image.fill( RED )
else:
border=3
x, y = border, border
width = self.rect.width - border -1
height = self.rect.height - border -1
self.image.fill( TRANSPARENT )
pygame.draw.rect( self.image, RED, (x,y,width,height), self.filled )
class Circle( Shape ):
def render( self ):
self.image.fill( TRANSPARENT )
pygame.draw.circle( self.image, YELLOW, (self.rect.width//2, self.rect.height//2), self.rect.width//2, self.filled )
### initialisation
pygame.init()
window = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ) )
### Some sprite groups
missileGroup = pygame.sprite.Group()
for i in range( 3 ):
new_missile = Circle()
new_missile.render()
missileGroup.add( Circle() )
enemyGroup = pygame.sprite.Group()
for i in range( 12 ):
new_enemy = Square()
new_enemy.render()
enemyGroup.add( Square() )
### Main Loop
clock = pygame.time.Clock()
done = False
while not done:
# Handle user-input
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
done = True
elif ( event.type == pygame.MOUSEBUTTONUP ):
# On mouse-click
pass
# Move * collide the sprites
missileGroup.update()
enemyGroup.update()
# Test Collisions
for missile in missileGroup:
if pygame.sprite.spritecollide(missile, enemyGroup, False) :
print("Missile " + str(missile) + " Hits Enemy")
missile.setFilled( True )
else:
missile.setFilled( False )
for enemy in enemyGroup:
if pygame.sprite.spritecollide(enemy, missileGroup, False):
print("Enemy " + str(enemy) + " Hits Missile")
enemy.setFilled( True )
else:
enemy.setFilled( False )
# Paint the window, but not more than 60fps
window.fill( DARK_BLUE )
enemyGroup.draw( window )
missileGroup.draw( window )
pygame.display.flip()
# Clamp FPS
clock.tick(60)
pygame.quit()
This question already has answers here:
How to load and play a video in pygame
(3 answers)
Closed 1 year ago.
I am looking to add videos in my game (such as cut scenes or an animated menu screen).
I looked around for a bit and it seems pygame doesn't support video playing anymore, so I was wondering if there was another way to get videos playing while being well integrated in my game, such as having the video playing in the background and having pygame elements (start button etc.) in the foreground.
There's an example somewhere (I was unable to find an original link) of using FFMPEG and another python module to decode the frames via a pipe and read these through into PyGame for displaying. I copied a code snippet (I thought from SO), and forgot about it.
I have now adapted that technique to make a VideoSprite. It uses FFMPEG to decode (and rescale) the video stream, where it's read during the sprites update() to get the next frame.
This is a very rough implementation, but I hope it gives you an idea of what's possible. While it would be nice if PyGame would just play the videos on its own, at least this method hands the video decoding and rescaling off to a subprocess where it hopefully runs on another CPU.
(EDIT: added handler for video ending, and proper FPS control)
import pygame
import subprocess
# Window size
WINDOW_WIDTH = 600
WINDOW_HEIGHT = 400
WINDOW_SURFACE = pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE
DARK_BLUE = ( 3, 5, 54)
### initialisation
pygame.init()
pygame.mixer.init()
window = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), WINDOW_SURFACE )
pygame.display.set_caption("Video Sprite")
class VideoSprite( pygame.sprite.Sprite ):
FFMPEG_BIN = "/usr/bin/ffmpeg" # Full path to ffmpeg executable
def __init__(self, rect, filename, FPS=25 ):
pygame.sprite.Sprite.__init__(self)
command = [ self.FFMPEG_BIN,
'-loglevel', 'quiet',
'-i', filename,
'-f', 'image2pipe',
'-s', '%dx%d' % (rect.width, rect.height),
'-pix_fmt', 'rgb24',
'-vcodec', 'rawvideo', '-' ]
self.bytes_per_frame = rect.width * rect.height * 3
self.proc = subprocess.Popen( command, stdout=subprocess.PIPE, bufsize=self.bytes_per_frame*3 )
self.image = pygame.Surface( ( rect.width, rect.height ), pygame.HWSURFACE )
self.rect = self.image.get_rect()
self.rect.x = rect.x
self.rect.y = rect.y
# Used to maintain frame-rate
self.last_at = 0 # time frame starts to show
self.frame_delay = 1000 / FPS # milliseconds duration to show frame
self.video_stop = False
def update( self ):
if ( not self.video_stop ):
time_now = pygame.time.get_ticks()
if ( time_now > self.last_at + self.frame_delay ): # has the frame shown for long enough
self.last_at = time_now
try:
raw_image = self.proc.stdout.read( self.bytes_per_frame )
self.image = pygame.image.frombuffer(raw_image, (self.rect.width, self.rect.height), 'RGB')
#self.proc.stdout.flush() - doesn't seem to be necessary
except:
# error getting data, end of file? Black Screen it
self.image = pygame.Surface( ( self.rect.width, self.rect.height ), pygame.HWSURFACE )
self.image.fill( ( 0,0,0 ) )
self.video_stop = True
### Create Video Area
video_sprite1 = VideoSprite( pygame.Rect( 100, 100, 320, 240 ), '1975_test_pattern.mp4' )
video_sprite2 = VideoSprite( pygame.Rect( 100, 100, 160, 90 ), '/home/kingsley/Videos/rocket.avi' ) # 640x360
#sprite_group = pygame.sprite.GroupSingle()
sprite_group = pygame.sprite.Group()
sprite_group.add( video_sprite1 )
sprite_group.add( video_sprite2 )
### Main Loop
clock = pygame.time.Clock()
done = False
while not done:
# Handle user-input
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
done = True
elif ( event.type == pygame.MOUSEBUTTONUP ):
# On mouse-click
pass
# Movement keys
keys = pygame.key.get_pressed()
if ( keys[pygame.K_UP] ):
video_sprite2.rect.y -= 10
if ( keys[pygame.K_DOWN] ):
video_sprite2.rect.y += 10
if ( keys[pygame.K_LEFT] ):
video_sprite2.rect.x -= 10
if ( keys[pygame.K_RIGHT] ):
video_sprite2.rect.x += 10
# Update the window, but not more than 60fps
sprite_group.update()
window.fill( DARK_BLUE )
sprite_group.draw( window )
pygame.display.flip()
# Clamp FPS
clock.tick_busy_loop(25) # matching my video file
pygame.quit()
Obviously, since it's just the video stream, there's no sound. But for all intents and purposes it's just another sprite. When the video runs out we catch the error and go black.
NOTE: Sometimes when I run this, it breaks the terminal echoing in Linux. I suspect it's something to do with the subprocess and/or pipe. Running reset fixes this. It seems a common problem with subprocesses.
I wrote a code which seems to work very well with moviepy, I was inspired by that of Kingsley.
import moviepy.editor
import moviepy.video.fx.all
import pygame
class VideoSprite(pygame.sprite.Sprite):
def __init__(self, rect, filename):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((rect.width, rect.height), pygame.HWSURFACE)
self.rect = self.image.get_rect()
self.rect.x = rect.x
self.rect.y = rect.y
self.video = moviepy.editor.VideoFileClip(filename).resize((self.rect.width, self.rect.height))
self.video_stop = False
def update(self, time=pygame.time.get_ticks()):
if not self.video_stop:
try:
raw_image = self.video.get_frame(time / 1000) # /1000 for time in s
self.image = pygame.image.frombuffer(raw_image, (self.rect.width, self.rect.height), 'RGB')
except:
self.video_stop = True
The advantage here is that the video is at the right speed
Code is running correctly and as I expected but after 5 seconds the display for graphics freezes forever. It shows no error, nothing, just stops responding.
This is a program to simulate a movement of a large group of objects. They have to move randomly while aimless like Brownian motion. To make this I used Pygame to draw any object as a rectangle of random location, to move them I remove everything and draw them again with their location randomly changed by 1.
I am using pygame to show graphics.
Could you please also suggest a better solution for this problem?
import pygame, random, sys, time, numpy
from pygame.locals import *
black = (0,0,0)
white = (255,255,255)
clock = pygame.time.Clock()
class people():
def __init__(self):
screen = pygame.display.get_surface()
self.x = random.randint(0, 800)
self.y = random.randint(0, 600)
def move(self):
self.x += random.randint(-1, 1)
self.y += random.randint(-1, 1)
if self.x < 0:
self.x = 0
if self.x > 800:
self.x = 800
if self.y < 0:
self.y = 0
if self.y > 600:
self.y = 600
def place(x, y):
screen = pygame.display.get_surface()
pygame.draw.rect(screen, black, [x, y, 10, 10])
def main():
# Initialise screen
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption('Test')
peoples = []
chosepopul = 1
while chosepopul == 1:
try:
population = abs(int(input("How many people would you like to have")))
chosepopul = 0
except:
print("Number, please")
for i in range(population):
peoples.append(people())
while True:
screen.fill(white)
for obj in peoples:
people.place(obj.x, obj.y)
people.move(obj)
pygame.display.update()
clock.tick(60)
if __name__ == '__main__':
main()
pygame.quit()
quit()
Everything is working as I expected but freezing inevitably.
UPDATE: If I change input script to constant number, everything is working correctly. So the problem is somehow linked with user interface interactions.
The program stops because the input() blocks the program flow. No further PyGame updates or events are sent & processed. Basically everything comes to a halt, waiting for the user to type.
The best way around this is to write code such that the user does some PyGame on-screen input, rather than in the console. Maybe make a slider or spinner-control to select the number, or plus/minus buttons, whatever.
Alternatively, the program can still use console input in a thread which uses the post() function to send the result to the main PyGame event-loop thread.
I must admit, this answer is of academic interest only, since using the console to input along with a PyGame window is pretty ugly!
Anyway, here is some code. The main python window simply changes colour every 0.5 seconds, while the user can input text in the console with the standard python input() function. The code uses it's own Event enumerated type for posting messages, but these could also just be plain numbers.
This works, as per the OP, because the input() function is called inside a sub-thread of execution. This leaves the main thread free to continually process the PyGame event queue, and paint updates to the window. It's important to only have a single event queue/loop (for reasons beyond the scope of this answer), so the sub-thread "posts" events back to the main thread, rather than acting on the window/events itself.
import threading
import pygame
import enum
# Window size
WINDOW_WIDTH = 200
WINDOW_HEIGHT = 200
DARK = ( 50, 50, 50 )
WHITE = ( 255,255,255 )
RED = ( 255, 55, 55 )
GREEN = ( 5,255, 55 )
BLUE = ( 5, 55,255 )
colour_cycle = [ DARK, WHITE, RED, GREEN, BLUE ]
class UserEvents( enum.IntEnum ):
CLIENT_NUMBER = pygame.USEREVENT + 1
CLIENT_QUIT = pygame.USEREVENT + 2
# ...
class ConsoleInputThread( threading.Thread ):
""" A thread that handles user input on the console.
Waits for user input, then posts messages
to the main PyGame thread for processing """
def __init__( self, prompt ):
threading.Thread.__init__(self)
self.daemon = True # exit with parent
self.done = False
self.prompt = prompt
def stop( self ):
self.done = True
def run( self ):
""" Loops until the user hangs-up """
while ( not self.done ):
# Get some input from the user
user_input = input( self.prompt ).strip()
new_event = None
if ( user_input == 'quit' ):
new_event = pygame.event.Event( UserEvents.CLIENT_QUIT, { } )
else:
try:
user_input = int( user_input )
new_event = pygame.event.Event( UserEvents.CLIENT_NUMBER, { "value":user_input } )
except:
print( "Syntax Error" )
# If we received valid input post it to the main thread
if ( new_event ):
pygame.event.post( new_event )
###
### MAIN
###
# Create the window
pygame.init()
pygame.display.set_caption("Socket Messages")
SURFACE = pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE
WINDOW = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), SURFACE )
# Start the console-input thread
input_thread = ConsoleInputThread( "How many people would you like to have: " )
input_thread.start()
# Main paint / update / event loop
done = False
clock = pygame.time.Clock()
colour_index = 0
while ( not done ):
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
done = True
elif ( event.type == UserEvents.CLIENT_QUIT ):
print("\nCLIENT ASKED TO QUIT " )
done = True
elif ( event.type == UserEvents.CLIENT_NUMBER ):
print( "\nVALUE WAS INPUT: %d " % ( event.value, ) )
WINDOW.fill( colour_cycle[colour_index] )
# rotate the colours, just so the screen changes
colour_index += 1
if ( colour_index >= len( colour_cycle ) ):
colour_index = 0
pygame.display.flip()
clock.tick_busy_loop(2) # NOTE: 2 frames per second, no flashy-flashy
input_thread.stop()
pygame.quit()
im having issues for loading an image to a certain location in which I choose to the gameboard
EDIT: OCR'd text from linked image.
def drawPieces (gameBoard):
global goblin1a
global goblin1b
global goblin2a
global goblin2b, goblin3a, goblin3b, goblin4a, goblin4b
global xcoordinate
global ycoordinate
global i
global j
for x in range (0,20):
for y in range (0,20):
xcoordinate.append(((margin+width) * x + margin+32)+xDistanceFromEdge)
ycoordinate.append((margintheight) * y + margin+33
#if gameBoard [x] [y]=="NormalBlack"
goblin1a=xcoordinate[2]#goblin 1
goblin1b=ycoordinate[2]#goblin 1
goblin2a=xcoordinate[3]#goblin 1
goblin2b=ycoordinate[3]#goblin 1
goblin3a=xcoordinate[7]#goblin 1
goblin3b=ycoordinate[5]#goblin 1
goblin4a=xcoordinate[9]#goblin 1
goblin4b=ycoordinate[2]#goblin 1
screen.blit(walkLeft, (goblin1a, goblin1b))
print (xcoordinate)
drawPieces (gameBoard)
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygameMOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
The code is getting bogged in all those co-ordinates!
What if there was some way of storing everything about a Goblin together, to keep it tidy, and allow it to be passed as a parameter or stored in a list.
So what do we know about a Goblin ... well it has:
an game-grid co-ordinate
an image (PyGame surface)
This could easily be put into a python list (like an array):
goblin1 = [ 0, 0, walkLeft ]
goblin2 = [ 1, 0, walkLeft ]
...
These can be kept in another list:
goblins = [ goblin1, goblin2, ... ]
Making the drawPieces() simpler:
# window border constants
X_BORDER = 40
Y_BORDER = 40
drawPieces( screen, goblin_list ):
global X_BORDER, Y_BORDER
# paint background white
screen.fill( ( 255,255,255 ) )
# draw each goblin in the list
for single_goblin in goblin_list:
# copy the three parts of the goblin's info from the list
board_x, board_y, bitmap = single_goblin
# convert game grid-position to screen-position
screen_x = X_BORDER + ( board_x * 32 )
screen_y = Y_BORDER + ( board_y * 33 )
screen.blit( bitmap, ( screen_x, screen_y ) )
# flush the updates to the display
pygame.display.flip()
There's also PyGame sprites, which are software objects, so they encapsulate both the variables for the object, and functions to work with it.
class GoblinSprite( pygame.sprite.Sprite ):
def __init__( self, bitmap, board_position ):
self.position = board_position
self.image = bitmap
self.rect = self.image.get_rect()
self.setScreenPosition()
def setScreenPosition( self ):
global X_BORDER, Y_BORDER
screen_x = X_BORDER + ( self.position[0] * 32 )
screen_y = Y_BORDER + ( self.position[1] * 33 )
self.rect.x = screen_x
self.rect.y = screen_y
return (screen_x, screen_y)
There's a short sprite tutorial on the PyGame Website.