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.
Related
I have two objects:
#Object1
enemigo=pygame.image.load("enemigo.png").convert_alpha()
enemigo=pygame.transform.scale(enemigo, (100, 100))
enemigo_rectangulo=enemigo.get_rect(center=(1000, 100))
#Object2
enemigo2=pygame.image.load("enemigo2.png").convert_alpha()
enemigo2=pygame.transform.scale(enemigo2, (140, 140))
enemigo_rectangulo2=enemigo2.get_rect(center=(1400, 50))
And I want to make an if statement if they collide with a third object. This code actually works but is too long:
if personaje_rectangulo.colliderect(enemigo_rectangulo) or personaje_rectangulo.colliderect(enemigo_rectangulo2):
And there's an error when I try to write:
if personaje_rectangulo.colliderect(enemigo_rectangulo, enemigo_rectangulo2):
So, what is wrong? Is there a shorter way to write it?
The first thing you can do is use shorter variable names, such as personaje and enemigo.
Additionally, break the statement into smaller pieces by using variables:
hit_enemy1 = personaje_rectangulo.colliderect(enemigo_rectangulo)
hit_enemy2 = personaje_rectangulo.colliderect(enemigo_rectangulo2)
if hit_enemy1 or hit_enemy2:
pass
Alternatively, if you store all of the enemies in a list, you can use any():
if any(personaje_rectangulo.colliderect(enemigo_rectangulo) for enemigo_rectangulo in todos_enemigos):
pass
Now this is also getting long so creating a function to help can shorten the line of code:
def collision(person, enemy):
return person.colliderect(enemy)
if any(collision(person, enemy) in todos_enemigos):
pass
Another solution is to create a Enemy class which encapsulates the data and behavior of an enemy. This is a bit more advanced, but definitely a good exercise to learn what classes are and how to use them.
Use collidelist():
Test whether the rectangle collides with any in a sequence of rectangles. The index of the first collision found is returned. If no collisions are found an index of -1 is returned.
if personaje_rectangulo.collidelist([enemigo_rectangulo, enemigo_rectangulo2]) >= 0:
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.
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!
I am trying to make an explosion appear and then disappear. My problem is it will either appear, and stay there, or not appear at all.
This is what I have so far:
#Throwing a grenade
if event.key == pygame.K_e and grenadeNum > 0:
Grenade = Explosive([Player.rect.centerx, Player.rect.centery])
for i in range(4, 30):
Grenade.move()
screen.fill([105, 105, 105])
screen.blit(Grenade.image, Grenade.rect)
screen.blit(Gun.image, Gun.rect)
screen.blit(Cliper.image, Cliper.rect)
screen.blit(Bullet.image, Bullet.rect)
screen.blit(Player.image, Player.rect)
screen.blit(BOOM.image, BOOM.rect)
screen.blit(ammo_text, textpos1)
screen.blit(clip_text, textpos2)
screen.blit(nade_text, textpos3)
pygame.display.update()
grenadeNum = grenadeNum - 1
explosion_sound.play()
hide = False
clock.tick(4)
BOOM = Explosion([Grenade.rect.centerx, Grenade.rect.centery])
screen.blit(BOOM.image, BOOM.rect)
hide = True
if hide == False:
BOOM = Explosion([Grenade.rect.centerx, Grenade.rect.centery])
else:
BOOM = Explosion([-100, -100])
You are blitting and waiting inside the event loop.
Any actions will be suspended while waiting.
The solution to this is to seperate the game logic from input.
Since you are throwing a grenade, you should only throw the grenade, and then later increment a counter for the grenade explosion. After enought time passes, you can then remove the grenade sprite from the game, and replace it with an explosion. I can see you already have a clock object, so just call tick, and accumulate that until you think it's enough. You could have a time field in the grenade class that will decide when the grenade will explode.
It's useful to keep all sprites in a list, so you can then call draw() and update() methods for all of them.
A little suggestion: A simple pygame module should look like this:
createObjects() #initialization, loading resources etc.
while(True):
delta = c.tick() #delta for the amount of miliseconds that passed since last loop
drawAll() #draws all active sprites
updateAll(delta) #moves, checks for collisions, etc
getInput() #changes the states of objects, calls functions like shoot,open etc.
So throwing a grenade would create a new sprite that will be drawn and updated like any other sprite.
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