pygame - particle effects - python

I'm making a 2-d game using Pygame.
I want to add particle effects into the game I'm working on. I want to do things like spawn smoke, fire, blood, etc. I'm curious is there an easy way to do this? I don't really know even where to start.
I just need a base case I could expand upon..
Pls Help.

You might want to just make a class made of rects that go up and randomly go to the right or left each time it is updated for the smoke. Then make a ton of them whenever you want it. I'll try to make an example code below, but I can't guarntee it will work. You can make similar classes for the other particle effects.
class classsmoke(pygame.Rect):
'classsmoke(location)'
def __init__(self, location):
self.width=1
self.height=1
self.center=location
def update(self):
self.centery-=3#You might want to increase or decrease this
self.centerx+=random.randint(-2, 2)#You might want to raise or lower this as well
#use this to create smoke
smoke=[]
for i in range(20):
smoke.append(classsmoke(insert location here))
#put this somewhere within your game loop
for i in smoke:
i.update()
if i.centery<0:
smoke.remove(i)
else:
pygame.draw.rect(screen, GREY, i)
Another option would be to make the class just a tuple, like this:
class classsmoke():
'classsmoke(location)'
def __init__(self, location):
self.center=location
def update(self):
self.center[1]-=3
self.center[0]+=random.randint(-2, 2)
#to create smoke
smoke=[]
for i in range(20):
smoke.append(classsmoke(insert location here))
#put inside game loop
for i in smoke:
i.update()
if i.centery<0:
smoke.remove(i)
else:
pygame.draw.rect(screen, GREY, (i.center[0], i.center[1], 1, 1))
Or, to avoid classes completly:
#to create smoke:
smoke=[]
for i in range(20):
smoke.append(insert location here)
#put within your game loop
for i in smoke:
i[1]-=3
i[0]+=random.randint(-2, 2)
if i[1]<0:
smoke.remove(i)
else:
pygame.draw.rect(screen, GREY, (i[0], i[1], 1, 1))
Pick your preference, and do something similar for other particle effects.

Check the Library for particle effects PyIgnition

Related

Get any objects (Rect, Surfaces...) at position / coordinates X, Y

So the question is simple:
Given a Surface, let's call it screen and x,y coordinates, can I get anything that lays at that coordinates on that Surface?
For example, let's say we have typical, Player attack, and if the attack reach the Enemy position x,y then enemy dies.
So given this simple app (is an example only not a real app)
import pygame as pg
from pygame.math import Vector2
# pygame constants
CLOCK = pg.time.Clock()
WIN_SIZE = (1280, 640)
# pygame setup
pg.init()
# screen
window = pg.display.set_mode(WIN_SIZE, 0, 32)
background = pg.Surface(WIN_SIZE)
player = pg.Surface(Vector2(12, 64))
player_rect = player.get_rect(topleft=Vector2(150, 150))
player_attack = False
player.fill((102, 255, 178))
player_attack_range = 20 # player can hit at min 20 pixel from target
enemy = pg.Surface(Vector2(12, 64))
enemy_rect = player.get_rect(topleft=Vector2(175, 150))
enemy.fill(pg.Color("green"))
while True:
background.fill((0, 0, 0)) # screen clear
# Render enemy
attacked = False
if player_attack:
# !!!!! HERE !!!!!
# Now we check if the playuer is close enough to the enemy, so we MUST know the enemy pos
distance_x = abs(player_rect.x - enemy_rect.x)
if distance_x > player_attack_range:
attacked = True
enemy.fill(pg.Color("red"))
if not attacked:
enemy.fill(pg.Color("green"))
background.blit(enemy, enemy_rect.topleft)
# Render player
background.blit(player, player_rect.topleft)
# Events
for event in pg.event.get():
if event.type == pg.QUIT or (
event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE): # x button and esc terminates the game!
exit(1)
# ............. Mouse ............. #
if event.type == pg.MOUSEBUTTONDOWN:
if event.button == 1:
player_attack = True
if event.type == pg.MOUSEBUTTONUP:
if event.button == 1:
player_attack = False
pg.display.update() # 2) Update the game
window.blit(background, (0, 0)) # 3) Repaint the screen
CLOCK.tick(60) # 4) Wait 60 Frames
When is attacked
Now I always seen it done this way more or less:
distance_x = abs(player_rect.x - enemy_rect.x)
if distance_x > player_attack_range:
attacked = True
enemy.fill(pg.Color("red"))
With this example, I'm not pointing out the code implementation but the fact that, the player must know the target position and then check whether or not the target is hit
But what I want to know, let's say I don't know the enemy position, and the player just attacks, is there a way that we can get what's currently on the surface at the attack range?
So do something like
attacked_area_x = abs(player_rect.x + player_attack_range) # only care of x coords
rects_or_surfaces_in_area = background.what_have_we_got_here(Vector(attacked_area, 0))
for r in rects_or_surfaces_in_area:
print("Hit!")
Update
So By checking MDN documentation of Game Development MDN I actually find a game algorithm / Technique that is similar (but concept is the same) of my solution.
Is called the Broad Phase
From the documentation:
road phase should give you a list of entities that could be colliding. This can be implemented with a spacial data structure that will give you a rough idea of where the entity exists and what exist around it. Some examples of spacial data structures are Quad Trees, R-Trees or a Spacial Hashmap.
So yes, it seems one of many good approach to solve this problem.
So, after some research and thanks to Rabbid76 and his answer here How do I detect collision in pygame? which covers in details the most common collisions in Pygame, it seems that what I was looking for natively is just not possible.
Maybe is normal, I'm also new to game development and maybe what I want to do just doesn't make any sense, but I bet it does.
The scenario I'm facing is, just one player with a sword hitting, so I asked my self, why should I need to know prior what objects lie on the sword path and check if is hit, instead, do the hit and request to the parent screen "what object are in the sword path"? , which, is for sure faster because we don't need to know what object that have collision are (and avoid a for loop and check for each react/surface).
So, let's say there are many many object that have collision and a player may hit it, it would be way faster do don't know what' there but request it instead to the surface, so basically the surface should be aware of its children / objects.
I tried a bit the Surface.subsurface() and the Surface.get_parent() but couldn't make it work, anyway still, in a surface area we may have many thinks like:
draws, Rect, Surfaces, Sprites, Imgs etc...
I have only 2 solutions in my mind:
Map the objects coordinates
This only really works if, the entities are static, if so, then we could create a dict with key x:y coordinates and then check if the sword is in within a certain x:y and exist in the dict, then the entity is hit.
But with entity then moves by themself, is a nigtmare and will have a worst problem than before, you would need to update the keys at each frame and so on..
Make it 'distance' sensible
So, this could apply to each entity that is moving and may or may not hit something that has a collision. But staying on the example context, let's say we are iterating thourgh entities / object that at each frame are updating their position.
We could check how distant are from the player, let's say 2 chunks away (or the sword lenght) and collect them in a list
Then we that list, we check if, once the sword is updated, is hitting anything close by.
Still, pretty sure there are better ways (without changing or extending pygame its self).
And maybe, by extending pygame, there may be a way to implement a 'surface aware' class that when we request a specific Rect area, it tells us what's there.

how would i check all adjacent cells for my minesweeper game?

I have been trying to figure out a way to check each adjacent cell for my minesweeper game and am coming up short. I am a beginner in python and would also like to start using OOP. however before I can even get there, I need to rectify this. all the tutorials I have seen don't use basic python, but different versions to the IDLE i use, so I am struggling. can anyone help me? I need to be able to go around each adjacent cell and check if there is a bomb there. The value to check if there is a bomb there is 1 and will also turn red. Thank you all so much!
also if you could dumb it down a little for me, that would be lovely.
import random
import pygame
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
WIDTH = 20
HEIGHT = 20
MARGIN = 5
bombnum = 10
grid = []
for row in range(10):
grid.append([])
for column in range(10):
grid[row].append(0)
print(grid)
pygame.init()
WINDOW_SIZE = [255, 315]
screen = pygame.display.set_mode(WINDOW_SIZE)
pygame.display.set_caption("Array Backed Grid")
done = False
clock = pygame.time.Clock()
#class bomb:
#def revealed(self,pick):#this is the grid thats been picked
# self.shown = shown
def placebomb():
for i in range(bombnum):
while True:
row = random.randint(0,8)
column = random.randint(0,8)
if grid[row][column] == 0:
grid[row][column] = 1
break
placebomb()
# -------- Main Program Loop -----------
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
column = pos[0] // (WIDTH + MARGIN)
row = (pos[1]-50) // (HEIGHT + MARGIN)
grid[row][column] = 1
print("Click ", pos, "Grid coordinates: ", row, column)
screen.fill(BLACK)
for row in range(10):
for column in range(10):
color = WHITE
if grid[row][column] == 1:
color = RED
pygame.draw.rect(screen,
color,
[(MARGIN + WIDTH) * (column) + MARGIN,
50+(MARGIN + HEIGHT) * row + MARGIN,
WIDTH,
HEIGHT])
clock.tick(60)
pygame.display.flip()
pygame.quit()
First up, you definitely want to make an OOP project out of this. Minesweeper's probably near the complexity limit of what you can reasonably do without object-oriented programming, but if you want to take the basic Minesweeper concept and make it more interesting / complex, you're going to need better structure.
And even if you're not considering making it more complex, thinking about what sorts of complexities you could add in is helpful in planning your classes. (Perhaps you didn't realize there's a "planning your classes" step? I'll get to that.) My steps here should work for pretty much any beginning OOP project, since it seems Minesweeper's just a convenient example.
I realize this answer will be a giant diversion from OP's how-do-I-check-the-nearest-neighbors question, but OP was also asking about OOP and answering their question in an OOP context means getting a class model in place. Trying to retrofit OOP onto non-OOP code is possible, but is usually harder than doing OOP from scratch.
If you're new to OOP in Python, check these two tutorials. Both of them run through the basics, neither requires an IDE, and neither introduces complexity / features beyond what you need to start. The rest of my answer will assume you're familiar with how to write classes. I highly recommend typing out both tutorials on your own terminal and trying a couple variants on them before you go further.
Now that you know roughly what a class is, make a list of all the operations you're going to want in your Minesweeper program, and what sorts of things come up in it. The point here isn't to write code, and natural language will serve as pseudocode here. Things you'll probably want to include: "Add a mine to a cell on the grid," "check a cell on the grid," "display the number of spots near a given cell on the grid." You might also include some "future plans" like "resize the grid in the middle of a game", "limit number of moves," "get hint," "allow mines to move," "have people that move through minefield," multiple types of checks / mines / cells, hex grid as an alternative to a square grid, strangely-shaped and 3D grids, etc. Don't worry if you don't know how to code those things yet. If you've got an interesting idea, include it on the list.
Go through that list and make some notes about the main words / concepts that keep coming up. These will be your classes. For Minesweeper, I'd list "Cell," "Grid," "Game," "Check," and "Mine," but your mileage may vary. These are not necessarily the final class list. They're just a step toward organizing your thoughts so you can turn the high-level "Minesweeper game" into something concrete & extensible. You can add / remove classes later once you get a better feel for which ones you'll actually use.
Note the relationships between objects of the classes, including which ones need to be described for the others to make sense / be instantiated. This step is analogous to how you might plan a relational database: A Game has one and only one Grid, a Grid is a collection of Cells arranged in a fixed pattern, a Cell may or may not contain a single Mine, a Check reveals whether a Mine is in a given Cell and if not, reveals the number of Mines in Cells adjacent to the Checked Cell, etc.
Open up your IDE, and start blocking out classes in a file. The point here isn't so much to write code as to get all your classes and notes into the code for easy reference. If this file starts to get big, think about how the classes group together and split that one file into several, with import statements connecting them. By "blocking out classes," I mean "writing the simplest classes which will compile, but don't try to make them run." At this stage, you can / should have classes that look like this:
class Grid:
"""A Game has one and only one Grid, a Grid is a collection of Cells arranged in a fixed pattern"""
def __init__(self, game, **setup_params):
"""Initialize the grid given a game and possibly other parameters for shape and size"""
raise NotImplementedError('This code has not yet been written.')
def resize_grid(self, new_size):
raise NotImplementedError('This code has not yet been written.')
def check_cell_at(self, position):
raise NotImplementedError('This code has not yet been written.')
Things to check: This is completely legal Python and compiles fine. All of your notes from steps 2-4 should end up in docstrings. All of the target functionality you described in your notes corresponds to particular methods on classes that have something to do with those functions. Every class you've described is present, and has a docstring describing its purpose & structure. Every class's __init__() method takes the arguments that are necessary to instantiate the class. Every method takes some parameters that will likely be helpful. You can always edit the parameter lists later, but again, the point is to organize your code before you get too far into writing it, so that relationships and functionality are easy to track. If you're using Git or another version-tracking tool, make your first commit as soon as you're done with this step.
Now that you've got all the structure blocked out, figure out what the "main entry point" is going to be, instantiate that class (probably Game in my approach), and check that your code compiles, runs, and exits with a NotImplementedError coming with the line and message you expect. (If so, that's success!) If the project feels big, or if you're going to be sharing code with other developers, you may also want to add unit tests at this stage. The expectation is that every test will fail, but writing the tests helps to document the planned functionality.
Fill in the methods one by one, add unit tests to go with new or updated methods, and try running the code a bit at a time. You don't have to write the methods in any particular order, but it will be easier to test once all the __init__() methods are complete, and those are generally straightforward.
Now, how do you check the neighbors of a cell?
You should have methods on a Cell for "get list of my neighbors," and "get whether this cell contains a mine." You probably also have a method on Grid to "get Cell at position." You might have multiple types of Checks, but the base Minesweeper game only has the one, so Cell has a method like
def check(self):
"""Returns "BOMB" if this cell has a Bomb. Otherwise, returns the number of neighbors with bombs as an integer."""
if self.has_bomb:
return "BOMB"
neighboring_mines = 0
for cell in self.grid.get_neighbors_of(self.position):
if cell.has_bomb:
neighboring_mines += 1
return neighboring_mines
There are only 8([1,1],[1,0],[1,-1],[0,1],[0,-1],[-1,1],[-1,0],[-1,-1] relative to the clicked grid coordinate) adjacent squares, so it should be easy to do what you are asking only with "if" statements
anyway, just check the surrounding squares and if true add to a variable.
import random
import pygame
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
WIDTH = 20
HEIGHT = 20
MARGIN = 5
bombnum = 10
grid = []
for row in range(10):
grid.append([])
for column in range(10):
grid[row].append(0)
#print(grid)
pygame.init()
WINDOW_SIZE = [255, 315]
screen = pygame.display.set_mode(WINDOW_SIZE)
pygame.display.set_caption("Array Backed Grid")
done = False
clock = pygame.time.Clock()
#class bomb:
#def revealed(self,pick):#this is the grid thats been picked
# self.shown = shown
def placebomb():
for i in range(bombnum):
while True:
row = random.randint(0,8)
column = random.randint(0,8)
if grid[row][column] == 0:
grid[row][column] = 1
break
placebomb()
print(grid)
# -------- Main Program Loop -----------
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
column = pos[0] // (WIDTH + MARGIN)
row = (pos[1]-50) // (HEIGHT + MARGIN)
grid[row][column] = 1
print("Click ", pos, "Grid coordinates: ", row, column)
NBombs = 0
for i in range(-1,2):
for k in range(-1,2):
if (i!=0 or k!=0) and (row+i>=0) and (column+k>=0) and (row+i<len(grid)) and (column+k<len(grid[0])):#prevents from both being 0, or for the index to be negative or out of range
print(i,k)
if grid[row+i][column+k] == 1:
NBombs+=1
print("Number of bombs:",NBombs)
screen.fill(BLACK)
for row in range(10):
for column in range(10):
color = WHITE
if grid[row][column] == 1:
color = RED
pygame.draw.rect(screen,
color,
[(MARGIN + WIDTH) * (column) + MARGIN,
50+(MARGIN + HEIGHT) * row + MARGIN,
WIDTH,
HEIGHT])
clock.tick(60)
pygame.display.flip()
pygame.quit()
So basically like another answer in this thread, you can check the surrounding/adjacent tiles by adding -1, 0, or 1 to both your current x and y.
You could write a separate function that would return a list of the coordinates of the surrounding tiles with bombs and take the tile coordinates and grid as arguments like the following:
def check_surrounding_tiles(current_x, current_y, grid):
bomb_tiles = []
#Check starting from position (current_x - 1, current_y - 1)
for i in range(-1, 2):
for j in range(-1, 2):
#Ignore the current x and y, and do bounds checking
if (i != 0 or j != 0) and (current_x + i) > -1 and (current_y + j) > -1 and (current_x + i) < len(grid) and (current_y + j) < len(grid[0]):
if grid[current_x + i][current_y + j] == 1
bomb_tiles.append[(current_x + i, current_y + j)]
return bomb_tiles
This, like mentioned above, will return a list of coordinate pairs with bombs in them.
If you need to get the number of bombs, simply use the following:
adjacent_bomb_count = len(check_surrounding_tiles(<x position>,<y position>, grid))

Python3: Deleting enemy from dynamic array when colliding with the player

I am creating a sidescroller shooter game in which the enemies(drones)spawn contantly on the right side of the screen. The recently created enemies are appended to a list drones Each enemy goes towards the left of the screen. Once they are out of the screen, they get removed from the list.
I also want to remove the drones when they collide with the player. The following code works properly as long as there are multiple objects in the drones list, but when the list has only one item (so one drone is on the screen) that drone does not get deleted upon collision.
I have no idea why the first list item cannot be destroyed.
drones = []
class Drone
#other call methods
def hit(self):
del drones[drones.index(self)]
def generate_enemy():
global drones
if len(drones) < 20:
if (random.randint(1,100) == 1):
drones.append(Drone(screenWidth, random.randint(300,500))
def main():
global drones
while True:
#main loop stuff happening
if condition == True:
generate_enemy():
#main loop stuff happening
if player and (len(drones) > 0):
for i in range(len(drones)-1):
if drones[i].hitbox.colliderect(player.hitbox):
drones[i].hit()
main()
The "correct" way to do this in pygame is to let your classes inherit from Sprite, then use a Group instead of a simple list to store your enemies, and using pygame.sprite.spritecollide for collision detection with the dokill argument set to True.

Pyglet Image Rendering

I'm working on a sort of a 2D Minecraft clone for my first in depth Pyglet project and I've run across a problem. Whenever I have a decent number of blocks on screen, the frame rate drops dramatically.
Here is my rendering method:
I use a dictionary with the key being a tuple(which represents the coordinate for the block) and the item being a texture.
I loop through the entire dictionary and render each block:
for key in self.blocks:
self.blocks[key].blit(key[0] * 40 + sx,key[1] * 40+ sy)
P.S. sx and sy are coordinate offsets for screen scrolling
I would like to know if there is a way to more efficiently render each block.
I'm going to do my best to explain why and how to optemize your code without actually knowing what you code looks like.
I will assume you have something along the lines of:
self.blocks['monster001'] = pyglet.image.load('./roar.png')
This is all fine and dandy, if you want to load a static image that you don't want to do much with. However, you're making a game and you are going to use a hell of a lot more sprites and objects than just one simple image file.
Now this is where shared objects, batches and sprites come in handy.
First off, input your image into a sprite, it's a good start.
sprite = pyglet.sprite.Sprite(pyglet.image.load('./roar.png'))
sprite.draw() # This is instead of blit. Position is done via sprite.x = ...
Now, draw is a hell of a lot quicker than .blit() for numerous of reasons, but we'll skip why for now and just stick with blazing speed upgrades.
Again, this is just one small step towards successful framerates (other than having limited hardware ofc.. duh).
Anyway, back to pew pew your code with upgrades.
Now you also want to add sprites to a batch so you can simultaneously render a LOT of things on one go (read: batch) instead of manually pushing things to the graphics card. Graphic cards soul purpose was designed to be able to handle gigabits of throughput in calculations in one insanely fast go rather than handle multiple of small I/O's.
To do this, you need to create a batch container. And add "layers" to it.
It's quite simple really, all you need to do is:
main_batch = pyglet.graphics.Batch()
background = pyglet.graphics.OrderedGroup(0)
# stuff_above_background = pyglet.graphics.OrderedGroup(1)
# ...
We'll stick one with batch for now, you probably don't need more for this learning purpose.
Ok so you got your batch, now what? Well now we try our hardest to choke that living hell out of your graphics card and see if we can even buckle it under pressure (No graphic cars were harmed in this process, and please don't choke things..)
Oh one more thing, remember the note about shared objects? well, we'll create a shared image object here that we push into the sprite, instead of loading one new image every.. single... time.. monster_image we'll call it.
monster_image = pyglet.image.load('./roar.png')
for i in range(100): # We'll create 100 test monsters
self.blocks['monster'+str(i)] = pyglet.sprite.Sprite(imgage=monster_image, x=0, y=0, batch=main_batch, group=background)
Now you have 100 monsters created and added to the batch main_batch into the sub-group background. Simple as pie.
Here's the kicker, instead of calling self.blocks[key].blit() or .draw(), we can now call main_batch.draw() and it will fire away every single monster onto the graphics card and produce wonders.
Ok, so now you've optimized the speed of your code, but really that won't help you in the long run if you're making a game. Or in this case, a graphics engine for your game. What you want to do is step up into the big league and use classes. If you were amazed before you'll probably loose your marbles of how awesome your code will look once you've done with it.
Ok so first, you want to create a base class for your objects on the screen, lets called in baseSprite.
Now there are some kinks and stuff you need to work around with Pyglet, for one, when inheriting Sprite objects trying to set image will cause all sorts of iffy glitches and bugs when working with stuff so we'll set self.texture directly which is basically the same thing but we hook into the pyglet libraries variables instead ;D pew pew hehe.
class baseSprite(pyglet.sprite.Sprite):
def __init__(self, texture, x, y, batch, subgroup):
self.texture = texture
super(baseSprite, self).__init__(self.texture, batch=batch, group=subgroup)
self.x = x
self.y = y
def move(self, x, y):
""" This function is just to show you
how you could improve your base class
even further """
self.x += x
self.y += y
def _draw(self):
"""
Normally we call _draw() instead of .draw() on sprites
because _draw() will contains so much more than simply
drawing the object, it might check for interactions or
update inline data (and most likely positioning objects).
"""
self.draw()
Now that's your base, you can now create monsters by doing:
main_batch = pyglet.graphics.Batch()
background = pyglet.graphics.OrderedGroup(0)
monster_image = pyglet.image.load('./roar.png')
self.blocks['monster001'] = baseSprite(monster_image, 10, 50, main_batch, background)
self.blocks['monster002'] = baseSprite(monster_image, 70, 20, main_batch, background)
...
main_batch.draw()
How, you probably use the default #on_window_draw() example that everyone else is using and that's fine, but I find it slow, ugly and just not practical in the long run. You want to do Object Oriented Programming.. Right?
That's what it's called, I call it readable code that you like to watch all day long. RCTYLTWADL for short.
To do this, we'll need to create a class that mimics the behavior of Pyglet and call it's subsequent functions in order and poll the event handler otherwise sh** will get stuck, trust me.. Done it a couple of times and bottle necks are easy to create.
But enough of my mistakes, here's a basic main class that you can use that uses poll-based event handling and thus limiting the refresh rate to your programming rather than built in behavior in Pyglet.
class main(pyglet.window.Window):
def __init__ (self):
super(main, self).__init__(800, 800, fullscreen = False)
self.x, self.y = 0, 0
self.sprites = {}
self.batches = {}
self.subgroups = {}
self.alive = 1
def on_draw(self):
self.render()
def on_close(self):
self.alive = 0
def render(self):
self.clear()
for batch_name, batch in self.batches.items():
batch.draw()
for sprite_name, sprite in self.sprites.items():
sprite._draw()
self.flip() # This updates the screen, very much important.
def run(self):
while self.alive == 1:
self.render()
# -----------> This is key <----------
# This is what replaces pyglet.app.run()
# but is required for the GUI to not freeze.
# Basically it flushes the event pool that otherwise
# fill up and block the buffers and hangs stuff.
event = self.dispatch_events()
x = main()
x.run()
Now this is again just a basic main class that does nothing other than render a black background and anything put into self.sprites and self.batches.
Do note! we call ._draw() on the sprites because we created our own sprite class earlier? Yea that's the awesome base sprite class that you can hook in your own stuff before draw() is done on each individual sprite.
Anywho, This all boils down to a couple of things.
Use sprites when making games, your life will be easier
Use batches, your GPU will love you and the refreshrates will be amazing
Use classes and stuff, your eyes and code mojo will love you in the end.
Here's a fully working example of all the pieces puzzled together:
import pyglet
from pyglet.gl import *
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glEnable(GL_LINE_SMOOTH)
glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE)
pyglet.clock.set_fps_limit(60)
class baseSprite(pyglet.sprite.Sprite):
def __init__(self, texture, x, y, batch, subgroup):
self.texture = texture
super(baseSprite, self).__init__(self.texture, batch=batch, group=subgroup)
self.x = x
self.y = y
def move(self, x, y):
""" This function is just to show you
how you could improve your base class
even further """
self.x += x
self.y += y
def _draw(self):
"""
Normally we call _draw() instead of .draw() on sprites
because _draw() will contains so much more than simply
drawing the object, it might check for interactions or
update inline data (and most likely positioning objects).
"""
self.draw()
class main(pyglet.window.Window):
def __init__ (self):
super(main, self).__init__(800, 800, fullscreen = False)
self.x, self.y = 0, 0
self.sprites = {}
self.batches = {}
self.subgroups = {}
self._handles = {}
self.batches['main'] = pyglet.graphics.Batch()
self.subgroups['base'] = pyglet.graphics.OrderedGroup(0)
monster_image = pyglet.image.load('./roar.png')
for i in range(100):
self._handles['monster'+str(i)] = baseSprite(monster_image, randint(0, 50), randint(0, 50), self.batches['main'], self.subgroups['base'])
# Note: We put the sprites in `_handles` because they will be rendered via
# the `self.batches['main']` batch, and placing them in `self.sprites` will render
# them twice. But we need to keep the handle so we can use `.move` and stuff
# on the items later on in the game making process ;)
self.alive = 1
def on_draw(self):
self.render()
def on_close(self):
self.alive = 0
def render(self):
self.clear()
for batch_name, batch in self.batches.items():
batch.draw()
for sprite_name, sprite in self.sprites.items():
sprite._draw()
self.flip() # This updates the screen, very much important.
def run(self):
while self.alive == 1:
self.render()
# -----------> This is key <----------
# This is what replaces pyglet.app.run()
# but is required for the GUI to not freeze.
# Basically it flushes the event pool that otherwise
# fill up and block the buffers and hangs stuff.
event = self.dispatch_events()
# Fun fact:
# If you want to limit your FPS, this is where you do it
# For a good example check out this SO link:
# http://stackoverflow.com/questions/16548833/pyglet-not-running-properly-on-amd-hd4250/16548990#16548990
x = main()
x.run()
Some bonus stuff, I added GL options that usually does some benefitial stuff for you.
I also added sa FPS limiter that you can tinker and play with.
Edit:
Batched updates
Since the sprite object can be used to do massive renderings in one go by sending it all to the graphics card, similarly you'd want to do batched updates.
For instance if you want to update every objects position, color or whatever it might be.
This is where clever programming comes into play rather than nifty little tools.
See, everything i relevant in programming.. If you want it to be.
Assume you have (at the top of your code) a variable called:
global_settings = {'player position' : (50, 50)}
# The player is at X cord 50 and Y cord 50.
In your base sprite you could simply do the following:
class baseSprite(pyglet.sprite.Sprite):
def __init__(self, texture, x, y, batch, subgroup):
self.texture = texture
super(baseSprite, self).__init__(self.texture, batch=batch, group=subgroup)
self.x = x + global_settings['player position'][0]#X
self.y = y + global_settings['player position'][1]#Y
Note that you'd have to tweak the draw() (note, not _draw() since batched rendering will call upon draw and not _draw) function a little bit to honor and update position updates per rendering sequence. That or you could create a new class that inherits baseSprite and have only those types of sprite updated:
class monster(baseSprite):
def __init__(self, monster_image, main_batch, background):
super(monster, self).__init__(imgage=monster_image, x=0, y=0, batch=main_batch, group=background)
def update(self):
self.x = x + global_settings['player position'][0]#X
self.y = y + global_settings['player position'][1]#Y
And thus only call .update() on monster type classes/sprites.
It's a bit tricky to get it optimal and there's ways to solve it and still use batched rendering, but somewhere along these lines is probably a good start.
IMPORTANT NOTE I just wrote a lot of this from the top of my head (not the first time I've written a GUI class in Pyglet) and for whatever reason this *Nix instance of mine doesn't find my X-server.. So can't test the code.
I'll give it a test in an hour when I get off work, but this gives you a general Idea of what to do and what to think for when making games in Pyglet. Remember, have fun while doing it or you're quit before you even started because games take time to make ^^
Pew pew lazors and stuff, best of luck!

How would I create random locations for many groups of sprites?

I have tried a for loop with the blit and draw methods and using different variables for " PlayerSprite " and " Treegroup "
for PlayerSprite in Treegroup:
surface.blit(PlayerSprite,(random.randrange(100,500),random.randrange(100,600)))
also tried
SPRITES=[]
for Sprites in range(10):
Sprites= PlayerSprite
SPRITES.append(Sprites)
all I get are errors
screen=pygame.display.set_mode((640,480))
background1=pygame.image.load("C:\Pygame-Docs\examples\data\Random Map.bmp")
class Tree1(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image=pygame.image.load('C:\Pygame-Docs\examples\data\Tree 1.bmp')
self.image=self.image.convert()
self.rect=self.image.get_rect()
self.rect.centerx=random.randrange(10,100)
self.rect.centery=random.randrange(10,100)
# Makes a group of trees
Howmanytrees=random.randrange(5,10)
Trees=[]
for tree in range(Howmanytrees):
trees=Tree1()
Trees.append(trees)
# Howmany groups
for Treegroup in range(10):
Treegroup=Trees
# Places groups
PlayerSprite=pygame.sprite.Group(Treegroup)
# keeps loop ( game ) going until canceled
keepgoing=True
while keepgoing:
for event in pygame.event.get():
if event.type==pygame.QUIT:
keepgoing=False
# actually draws screen
screen.blit(background1,(0,0))
PlayerSprite.draw(screen)
pygame.display.flip()
This code only displays 5 to 10 trees " Trees=[] "
and nothing else. I have worked on this problem for over a week , read many tutorials, looked on many websites, nothing seems to work. I must be overlooking or missing somethig. I thought this would be easy!
Thanks so much!
As far as I understand what you want to achieve, the below code should help you. I kept it very very simple regarding python syntax, as you seems to be a newbie (for experienced programmers: yes, what I wrote below is horrible python code, but I believe the OP can understand it and it may help).
The key is that if you want to get several groups of trees, you must have a loop within a loop. The inner loop put the trees inside the group, and the outer loop put several groups. Of course you can (and should) certainly hide the inner loop behind some function or class.
# Howmany groups ? say 3
trees_groups = []
number_of_groups = 3
for _ in range(number_of_groups):
# Choose a base position for my group of trees
base_x = random.randrange(0,530)
base_y = random.randrange(0,370)
# Makes a group of trees
trees=[]
number_of_trees = random.randrange(5,10)
for _ in range(number_of_trees):
one_tree = Tree1()
trees.append(one_tree)
for tree in trees:
tree.rect.centerx += base_x
tree.rect.centery += base_y
trees_groups.append(tree)
# Places groups
PlayerSprite=pygame.sprite.Group(trees_groups)
Some after notes:
And as other posters said, you should not use capitalized variables as you do. The python usage is to keep them for classes
Also looping using range when the base variant is not used is bad practice. I emphasized this by using underline as a variable name for the loop variant when it is not used.
I would use randint and move_ip to get what you want. Here is a code snippet from my own game that works just as well:
self.rect.move_ip(random.randint(minX, maxX), random.randint(minY, maxY))
the four variables minX, maxX, minY, maxY form a rectangle where the sprite can be placed. In your case, the trees would be placed along the entire screen, but with a reduced max X and Y range so trees won't clip through the bottom of the screen.
Also, use a Group class to store your trees rather than a list. A list stops the spawning of multiple trees, while a Group does. Here is how to call it:
Treegroup = pygame.sprite.Group
and to add a sprite to the group:
Treegroup.add(Tree1(screen))
Make sure the class itself has screen in its init, like so:
def __init__(self, screen)
Once that's done, your code should look something like this:
for Treegroup in range(10):
Treegroup.add(Tree(screen))
[...]
class Tree(pygame.sprite.Sprite):
def __init__(self, screen):
pygame.sprite.Sprite.__init__(self)
self.image, self.rect = load_image('tree.png', -1)
self.rect.move_ip(random.randint(0, 800), random.randint(0, 600))
self.area = screen.get_rect()
It doesn't really make much sense to me.
for tree in range(Howmanytrees):
trees=Tree1()
Trees.append(trees)
Your loop here is doing nothing at all. tree should be a number between 0 and Howmanytrees. Again, the following block isn't indented so it's not part of the loop. Even so, the block still wouldn't work. Also, you're confusing yourself and us with variable names. Trees is the object? trees is the list? Don't do this. Seriously.
No idea what the following code is up to.
# Howmany groups
for Treegroup in range(10):
Treegroup=Trees
Create your SpriteGroup passing in the aforementioned trees list? Or am I missing something :) TreeGroup = Trees 10 times is just going to do that 10 times. You are not using the loop variant. The only thing that modifies during the loop. Even so, this entire block of code is useless.
while keepgoing:
for event in pygame.event.get():
if event.type==pygame.QUIT:
keepgoing=False
This is going to cause a nice infinite loop. It is evaluating the keepgoing variable constantly. This will never get set to false unless the user quits, but also it will never display anything on the screen. Lose this. Use this instead:
for event in pygame.event.get():
if event.type == QUIT:
return
This will not cause an infinite loop as there are only so many events to be processed per tick. Then the program will loop around and do the process or updating, rendering and getting input again.

Categories