Posts: 41
Threads: 7
Joined: Jun 2022
I have an Explosion class that spawns a bunch of Particles. But when it does, all the particles zip off in a clump, not in random directions.
class Explosion():
def __init__(self, game, pos):
self.game = game
self.pos = pos
for p in range(10):
rot = random.randrange(360)
dir = vec(1, 0).rotate(rot) * random.randrange(3, 6)
print(dir) #<------ see below
p = Particle(self.game, self.pos, dir)
self.game.effects.append(p)
class Particle():
def __init__(self, game, pos, dir):
self.pos = pos
self.game = game
self.dir = dir
self.image = pg.Surface((25, 25), pg.SRCALPHA)
self.image.fill((200, 100, 100))
self.rect = self.image.get_rect()
self.rect.center = self.pos
self.alpha = 255
def update(self):
self.alpha -= 4
if self.alpha < 0:
self.game.effects.remove(self)
self.image.set_alpha(self.alpha)
self.pos += self.dir
self.rect.center = self.pos
#### then...
for e in self.effects:
e.update()
for particle in self.effects:
screen.blit(particle.image, (vec(particle.rect.x, particle.rect.y) - self.camera.pos)) And the print(dir) produces:
Output: [-4.89074, 1.03956]
[0.899804, -3.89748]
[-0.927051, -2.85317]
[-0.261467, -2.98858]
[3.99318, -3.00908]
[1.69047, 3.62523]
[0.348623, -3.98478]
[-3.1466, 3.88573]
[-0.174497, 4.99695]
[-0.572427, 2.94488]
[-1.23607, -3.80423]
[-1.49843, 3.70874]
[-3.47329, 3.5967]
If I print the particle.dir from the update I get the same kind of information. They all get different vectors to move with, yet sail off together. But NOT perfectly. It's a clump, like they got a random vector for the first frame, then all moved together after that (total speculation). They fade and die like I want.
I'm certain this approach worded for me in the past, but if someone could point to any obvious error I would really appreciate the help.
Thank you all.
Posts: 544
Threads: 15
Joined: Oct 2016
First I would not use a keyword to store variables. _dir over dir.
Second print self.pos, self.dir out during update. Make sure data is right.
Other improvements.
pygame.Vector2 where made for this. Understanding the basic we take care of a lot math. They are always floats.
vector = pygame.Vector2()
vector.from_polar((1, angle)) To give more control over random. Create different list. Then use random choice.
explosion_data = [list(range(1, 30)), list(range(32, 60))]
# in explosion
for particle in explosion_data:
angle = random.choice(particle)
vector = pygame.Vector2()
vector.from_polar((1, angle)) pygame.sprite.Spirte and pygame.sprite.Group can handle your particles. They are a built in framework. They also work faster. I give example later.
99 percent of computer problems exists between chair and keyboard.
Posts: 41
Threads: 7
Joined: Jun 2022
(Jul-05-2022, 01:29 AM)Windspar Wrote: First I would not use a keyword to store variables. _dir over dir.
Second print self.pos, self.dir out during update. Make sure data is right.
Other improvements.
pygame.Vector2 where made for this. Understanding the basic we take care of a lot math. They are always floats.
vector = pygame.Vector2()
vector.from_polar((1, angle)) To give more control over random. Create different list. Then use random choice.
explosion_data = [list(range(1, 30)), list(range(32, 60))]
# in explosion
for particle in explosion_data:
angle = random.choice(particle)
vector = pygame.Vector2()
vector.from_polar((1, angle)) pygame.sprite.Spirte and pygame.sprite.Group can handle your particles. They are a built in framework. They also work faster. I give example later.
For some reason, I thought pygame groups were more resource intensive, I'll certainly change that. The .pos I use is already a pygame vector2, as is vec(), I just imported it as vec for brevity. I also really like the idea of storing the random data as a list rather than calculating it a million times.
I'll implement your suggestions later today and see what I find. Thank you.
Posts: 544
Threads: 15
Joined: Oct 2016
Jul-05-2022, 10:47 PM
(This post was last modified: Jul-05-2022, 10:47 PM by Windspar.)
Here an example.
import random
import pygame
class Scene:
def __init__(self, engine):
self.engine = engine
def on_draw(self, surface):
pass
def on_event(self, event):
pass
def on_update(self, delta, ticks):
pass
def on_quit(self):
self.engine.running = False
class DisplayEngine:
def __init__(self, caption, width, height, flags=0):
pygame.display.set_caption(caption)
self.surface = pygame.display.set_mode((width, height), flags)
self.rect = self.surface.get_rect()
self.clock = pygame.time.Clock()
self.running = False
self.gamespeed = 1
self.delta = 0
self.fps = 60
self._scene = Scene(self)
self._next_scene = None
def main_loop(self, scene=None):
if scene:
self._scene = scene
self.running = True
while self.running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
self._scene.on_quit()
else:
self._scene.on_event(event)
ticks = pygame.time.get_ticks()
self._scene.on_draw(self.surface)
self._scene.on_update(self.delta, ticks)
pygame.display.flip()
self.delta = self.clock.tick(self.fps) * 0.001
class Images:
def __init__(self):
self.particle = pygame.Surface((20, 20), pygame.SRCALPHA)
self.particle.fill((200, 100, 100))
class Particle(pygame.sprite.Sprite):
def __init__(self, image, pos, vector):
super().__init__()
self.center = pos
self.image = image.copy()
self.rect = self.image.get_rect(center=pos)
self.alpha = 255
self.interval = 40
self.vector = vector
self.next_tick = pygame.time.get_ticks() + self.interval
self.speed = random.randrange(30, 170) * 0.01
def update(self, ticks):
if ticks > self.next_tick:
if self.alpha > 0:
self.next_tick += self.interval
self.alpha -= 4
self.center += self.vector * self.speed
self.rect.center = self.center
self.image.set_alpha(self.alpha)
else:
self.kill()
class Game(Scene):
def __init__(self, engine):
super().__init__(engine)
self.particles = pygame.sprite.Group()
self.images = Images()
self.explode = []
for x in range(0, 320, 45):
self.explode.append(list(range(x, x + 45)))
def mouse_push(self, mouse_pos):
for part in self.explode:
angle = random.choice(part)
vector = pygame.Vector2()
vector.from_polar((1, angle))
particle = Particle(self.images.particle, mouse_pos, vector)
self.particles.add(particle)
def on_draw(self, surface):
surface.fill('black')
self.particles.draw(surface)
def on_event(self, event):
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
self.mouse_push(event.pos)
def on_update(self, delta, ticks):
self.particles.update(ticks)
if __name__ == "__main__":
pygame.init()
engine = DisplayEngine("Particles", 800, 600)
game = Game(engine)
engine.main_loop(game)
pygame.quit()
BashBedlam likes this post
99 percent of computer problems exists between chair and keyboard.
Posts: 41
Threads: 7
Joined: Jun 2022
(Jul-05-2022, 10:47 PM)Windspar Wrote: Here an example.
Your example is much more elegant than what I had. So I replaced the code I was using with yours, but then the same thing happens!
So here is my whole code. The state machine was lifted from the forum here, and remains basically unchanged. Same with what I use for tiling the background. The rest I just pretty much cobbled together. I took it out to make your changes, but I was also giving the enemies the ability to shoot, and that was messed up. They would shoot, and then the next frame zip away at a million miles per hour (I tracked their .pos). I don't know if that is part of the same mistake, because I couldn't tell why that was happening either. It's not in this code I'm posting, because I set it aside to fix the explosion problem, but alas....
This is a working example. The mistake must be in the Game() ? I'm lost. lol
import pygame as pg
import sys
from pygame import Vector2 as vec
import random
from os import path
pg.init()
class Images:
def __init__(self):
self.particle =pg.Surface((20, 20),pg.SRCALPHA)
self.particle.fill((200, 100, 100))
class Particle(pg.sprite.Sprite):
def __init__(self, image, pos, vector):
super().__init__()
self.center = pos
self.image = image.copy()
self.rect = self.image.get_rect(center=pos)
self.alpha = 255
self.interval = 40
self.vector = vector
self.next_tick = pg.time.get_ticks() + self.interval
self.speed = random.randrange(30, 170) * 0.01
def update(self, ticks):
if ticks > self.next_tick:
if self.alpha > 0:
self.next_tick += self.interval
self.alpha -= 4
self.center += self.vector * self.speed
self.rect.center = self.center
self.image.set_alpha(self.alpha)
else:
self.kill()
class Mini_map():
def __init__(self, game, pos):
self.game = game
self.pos = pos
self.background = pg.Surface((200, 200), pg.SRCALPHA)
self.bg = pg.Surface((200, 200), pg.SRCALPHA)
self.bg.set_alpha(80)
self.bg.fill((10, 250, 40))
self.background.blit(self.bg, (0,0))
pg.draw.rect(self.background, (10, 200, 40), (0,0, 200, 200), 2)
self.image = self.background.copy()
def update(self):
self.image = self.background.copy()
for sprite in self.game.all_sprites:
pg.draw.rect(self.image, (220, 20, 40), (sprite.pos.x/10 - self.game.camera.pos.x/10 + 60, sprite.pos.y/10 - self.game.camera.pos.y/10 + 60, 2, 2))
class Enemy(pg.sprite.Sprite):
def __init__(self, game, pos):
pg.sprite.Sprite.__init__(self)
self.game = game
self.original_img = pg.Surface((12, 30), pg.SRCALPHA)
self.original_img.fill((100, 255, 100))
self.rect = self.original_img.get_rect()
self.image = pg.transform.rotate(self.original_img, self.game.player.rot)
self.vel = vec(0,0)
self.rot = random.randrange(365)
self.state = "normal"
self.pos = vec(pos)
self.rect.center = self.pos
self.spawn_time = pg.time.get_ticks()
self.life_span = 1000
self.hit_points = 20
def update(self, ticks):
if self.hit_points <= 0:
self.kill()
self.rot = (self.game.player.pos - self.pos).angle_to(vec(1, 0))
self.image = pg.transform.rotate(self.original_img, self.rot)
self.acc = vec(1, 0).rotate(-self.rot)
self.avoid_mobs()
self.avoid_player()
self.vel *= .95
self.vel += self.acc/2
self.pos += self.vel
self.rect = self.image.get_rect()
self.image.set_colorkey((255, 255, 255))
self.rect.center = self.pos
# role = random.randrange(100)
# if role < 5:
# b = Enemy_Blaster(self, self.game, self.rot)
# self.game.all_sprites.add(b)
# print(self.pos)
def avoid_mobs(self):
for mob in self.game.enemies:
if mob != self:
dist = self.pos - mob.pos
if 0 < dist.length() < 60:
self.acc += dist.normalize()
def avoid_player(self):
dist = self.pos - self.game.player.pos
if 0 < dist.length() < 200:
self.acc += dist.normalize()
def hit(self):
self.hit_points -= 5
if self.hit_points <= 0:
for part in self.game.explode:
angle = random.choice(part)
vector =pg.Vector2()
vector.from_polar((1, angle))
particle = Particle(self.game.images.particle, self.pos, vector)
self.game.particles.add(particle)
self.kill()
class Camera():
def __init__(self, game):
self.game = game
self.pos = vec(0, 0)# vec(self.game.player.pos.x / 2, self.game.player.pos.y / 2)
self.state = "follow"
def update(self):
self.pos = vec(self.game.player.pos.x - 400, self.game.player.pos.y - 400)
def tile_image(game, surface, image):
width, height = 800, 800
i_width, i_height = image.get_size()
camera_x = int(game.camera.pos.x % i_width)
camera_y = int(game.camera.pos.y % i_height)
for x in range(-camera_x, width, i_width):
for y in range(-camera_y, height, i_height):
surface.blit(image, (x, y))
class Enemy_Blaster(pg.sprite.Sprite):
def __init__(self, source, game, dir):
pg.sprite.Sprite.__init__(self)
self.dir = vec(dir)*20 + source.vel
self.game = game
self.pos = source.pos
self.rot = source.rot
self.original_img = pg.Surface((7, 3), pg.SRCALPHA)
self.original_img.fill((200, 55, 255))
self.rect = self.original_img.get_rect()
self.image = pg.transform.rotate(self.original_img, self.rot)
self.vel = vec(1,0)
self.state = "normal"
self.rect.center = self.pos
self.spawn_time = pg.time.get_ticks()
self.life_span = 1000
def update(self, ticks):
now = pg.time.get_ticks()
if now - self.spawn_time > self.life_span:
self.kill()
self.pos += self.dir
self.rect.center = self.pos
hit = self.rect.colliderect(self.game.player.rect)
if hit:
self.game.player.hit()
print("hit")
self.kill()
class Blaster(pg.sprite.Sprite):
def __init__(self, game, pos, dir):
pg.sprite.Sprite.__init__(self)
self.dir = vec(dir)*20 + game.player.vel
self.game = game
self.original_img = pg.Surface((10, 4), pg.SRCALPHA)
self.original_img.fill((200, 55, 10))
self.rect = self.original_img.get_rect()
self.image = pg.transform.rotate(self.original_img, self.game.player.rot)
self.vel = vec(2,0)
self.rot = 0
self.state = "normal"
self.pos = vec(pos)
self.rect.center = self.pos
self.spawn_time = pg.time.get_ticks()
self.life_span = 1000
def update(self, ticks):
if ticks - self.spawn_time > self.life_span:
self.kill()
self.pos += self.dir
self.rect.center = self.pos
for sprite in self.game.enemies:
hit = self.rect.colliderect(sprite.rect)
if hit:
sprite.hit()
self.kill()
class Player(pg.sprite.Sprite):
def __init__(self, game, pos):
pg.sprite.Sprite.__init__(self)
self.game = game
self.game.player_img = pg.Surface((50, 50))
self.rect = self.game.player_img.get_rect()
self.image = self.game.player_img
self.vel = vec(0,0)
self.acc = vec(0,0)
self.rot = 0
self.angle = 0
self.state = "normal"
self.rot_speed = 4
self.pos = vec(400, 400)
self.firing_rate = 100
self.last_shot = pg.time.get_ticks()
self.blaster_offset = vec(0,0)
self.thrust = .5
def get_keys(self):
self.acc = vec(0, 0)
keys = pg.key.get_pressed()
if keys[pg.K_LEFT] or keys[pg.K_a]:
self.rot = (self.rot + self.rot_speed) % 360
if keys[pg.K_RIGHT] or keys[pg.K_d]:
self.rot = (self.rot - self.rot_speed) % 360
if keys[pg.K_UP] or keys[pg.K_w]:
self.acc = vec(self.thrust, 0).rotate(-self.rot)
if keys[pg.K_DOWN] or keys[pg.K_s]:
self.acc = vec(-self.thrust / 4, 0).rotate(-self.rot)
if keys[pg.K_SPACE]:
now = pg.time.get_ticks()
if now - self.last_shot > self.firing_rate:
self.last_shot = now
dir = vec(1, 0).rotate(-self.rot)
pos = self.rect.center #self.pos + self.blaster_offset.rotate(-self.rot)
b = Blaster(self.game, pos, dir)
self.game.all_sprites.add(b)
def update(self, ticks):
self.acc = vec(0, 0)
self.get_keys()
self.vel = self.vel * .99 + self.acc
self.image = pg.transform.rotate(self.game.player_img, self.rot)
self.rect = self.image.get_rect()
self.image.set_colorkey((255, 255, 255))
self.pos += self.vel
if abs(self.vel.x) < 0.1:
self.vel.x = 0
self.rect.center = self.pos
def hit(self):
print("ouch!!")
class Control:
def __init__(self):
self.done = False
self.fps = 60
self.screen = pg.display.set_mode((800,800))
self.screen_rect = self.screen.get_rect()
self.clock = pg.time.Clock()
def setup_states(self, state_dict, start_state):
self.state_dict = state_dict
self.state_name = start_state
self.state = self.state_dict[self.state_name]
def flip_state(self):
self.state.done = False
previous,self.state_name = self.state_name, self.state.next
self.state.cleanup()
self.state = self.state_dict[self.state_name]
self.state.startup()
self.state.previous = previous
def update(self, dt):
if self.state.quit:
self.done = True
elif self.state.done:
self.flip_state()
self.state.update(self.screen)
def event_loop(self):
for event in pg.event.get():
if event.type == pg.QUIT:
self.done = True
self.state.get_event(event)
def main_game_loop(self):
while not self.done:
delta_time = self.clock.tick(self.fps)/1000.0
self.event_loop()
self.update(delta_time)
pg.display.update()
class MenuManager:
def __init__(self):
self.selected_index = 0
self.last_option = None
self.selected_color = (255,255,0)
self.deselected_color = (255,255,255)
def draw_menu(self, screen):
'''handle drawing of the menu options'''
for i,opt in enumerate(self.rendered["des"]):
opt[1].center = (self.screen_rect.centerx, self.from_bottom+i*self.spacer)
if i == self.selected_index:
rend_img,rend_rect = self.rendered["sel"][i]
rend_rect.center = opt[1].center
screen.blit(rend_img,rend_rect)
else:
screen.blit(opt[0],opt[1])
def update_menu(self):
self.mouse_hover_sound()
self.change_selected_option()
def get_event_menu(self, event):
if event.type == pg.KEYDOWN:
'''select new index'''
if event.key in [pg.K_UP, pg.K_w]:
self.change_selected_option(-1)
elif event.key in [pg.K_DOWN, pg.K_s]:
self.change_selected_option(1)
elif event.key == pg.K_RETURN:
self.select_option(self.selected_index)
self.mouse_menu_click(event)
def mouse_hover_sound(self):
'''play sound when selected option changes'''
for i,opt in enumerate(self.rendered["des"]):
if opt[1].collidepoint(pg.mouse.get_pos()):
if self.last_option != opt:
self.last_option = opt
def mouse_menu_click(self, event):
'''select menu option '''
if event.type == pg.MOUSEBUTTONDOWN and event.button == 1:
for i,opt in enumerate(self.rendered["des"]):
if opt[1].collidepoint(pg.mouse.get_pos()):
self.selected_index = i
self.select_option(i)
break
def pre_render_options(self):
'''setup render menu options based on selected or deselected'''
font_deselect = pg.font.SysFont("arial", 50)
font_selected = pg.font.SysFont("arial", 70)
rendered_msg = {"des":[],"sel":[]}
for option in self.options:
d_rend = font_deselect.render(option, 1, self.deselected_color)
d_rect = d_rend.get_rect()
s_rend = font_selected.render(option, 1, self.selected_color)
s_rect = s_rend.get_rect()
rendered_msg["des"].append((d_rend,d_rect))
rendered_msg["sel"].append((s_rend,s_rect))
self.rendered = rendered_msg
def select_option(self, i):
'''select menu option via keys or mouse'''
if i == len(self.next_list):
self.quit = True
else:
self.next = self.next_list[i]
self.done = True
self.selected_index = 0
def change_selected_option(self, op=0):
'''change highlighted menu option'''
for i,opt in enumerate(self.rendered["des"]):
if opt[1].collidepoint(pg.mouse.get_pos()):
self.selected_index = i
if op:
self.selected_index += op
max_ind = len(self.rendered['des'])-1
if self.selected_index < 0:
self.selected_index = max_ind
elif self.selected_index > max_ind:
self.selected_index = 0
class States(Control):
def __init__(self):
Control.__init__(self)
self.done = False
self.next = None
self.quit = False
self.previous = None
class Menu(States, MenuManager):
def __init__(self):
States.__init__(self)
MenuManager.__init__(self)
self.next = 'game'
self.options = ['Play', 'Options', 'Quit']
self.next_list = ['game', 'options']
self.pre_render_options()
self.from_bottom = 200
self.spacer = 75
def cleanup(self):
print('cleaning up Main Menu state stuff')
def startup(self):
print('starting Main Menu state stuff')
def get_event(self, event):
if event.type == pg.QUIT:
self.quit = True
self.get_event_menu(event)
def update(self, screen, dt):
self.update_menu()
self.draw(screen)
def draw(self, screen):
screen.fill((255,0,0))
self.draw_menu(screen)
class Options(States, MenuManager):
def __init__(self):
States.__init__(self)
MenuManager.__init__(self)
self.next = 'menu'
self.options = ['Music', 'Sound', 'Graphics', 'Controls', 'Main Menu']
self.next_list = ['options', 'options', 'options', 'options', 'menu']
self.from_bottom = 200
self.spacer = 75
self.deselected_color = (150,150,150)
self.selected_color = (0,0,0)
self.pre_render_options()
def cleanup(self):
print('cleaning up Options state stuff')
def startup(self):
print('starting Options state stuff')
def get_event(self, event):
if event.type == pg.QUIT:
self.quit = True
self.get_event_menu(event)
def update(self, screen, dt):
self.update_menu()
self.draw(screen)
def draw(self, screen):
screen.fill((255,0,0))
self.draw_menu(screen)
class Game(States):
def __init__(self):
States.__init__(self)
self.next = 'menu'
self.effects = []
self.all_sprites = pg.sprite.Group()
self.enemies = pg.sprite.Group()
self.player = Player(self, vec(400, 400))
self.all_sprites.add(self.player)
self.camera = Camera(self)
self.bg = pg.Surface((800, 800))
self.bg.fill(pg.Color('grey4'))
for s in range(800):
self.bg.set_at((random.randrange(800), random.randrange(800)), (250, 200, 200))
self.img_dir = path.join(path.dirname(__file__), 'images')
self.snd_dir = path.join(path.dirname(__file__), 'snd')
self.load_images()
for i in range(10):
o = Enemy(self, (random.randrange(800), random.randrange(800)))
self.all_sprites.add(o)
self.enemies.add(o)
self.mini_map = Mini_map(self, (600, 0))
self.particles = pg.sprite.Group()
self.images = Images()
self.explode = []
for x in range(0, 320, 45):
self.explode.append(list(range(x, x + 45)))
def load_images(self):
img = pg.image.load(path.join(self.img_dir, "ship1.png")).convert()
self.player_img = pg.transform.scale(self.player_img, (40, 50))
self.player_img.set_colorkey((255, 255, 255))
self.player_img.fill((200, 101, 120))
def cleanup(self):
print('cleaning up Game state stuff')
def startup(self):
print('starting Game state stuff')
def get_event(self, event):
if event.type == pg.MOUSEBUTTONDOWN:
self.done = True
def update(self, screen):
ticks = pg.time.get_ticks()
self.all_sprites.update(ticks)
self.particles.update(ticks)
self.camera.update()
self.mini_map.update()
self.draw(screen)
def draw(self, screen):
if self.camera.state == "follow":
tile_image(self, self.screen, self.bg)
for sprite in self.all_sprites:
screen.blit(sprite.image, (vec(sprite.rect.x, sprite.rect.y) - self.camera.pos))
for particle in self.particles:
screen.blit(particle.image, (vec(particle.rect.x, particle.rect.y) - self.camera.pos))
self.screen.blit(self.mini_map.image, self.mini_map.pos)
app = Control()
state_dict = {
'menu': Menu(),
'game': Game(),
'options':Options()
}
app.setup_states(state_dict, 'game')
app.main_game_loop()
pg.quit()
sys.exit()
Posts: 544
Threads: 15
Joined: Oct 2016
It will help knowing your images sizes. For I can replace them in your code. Then I can see what going on.
Here part of my code updated. It is better to use delta time for really smooth movement. I use ticks last time.
class Particle(pygame.sprite.Sprite):
def __init__(self, image, pos, vector):
super().__init__()
self.center = pos
self.image = image.copy()
self.rect = self.image.get_rect(center=pos)
self.alpha = 255
self.interval = 40
self.vector = vector
self.next_tick = pygame.time.get_ticks() + self.interval
self.speed = random.randrange(8, 80)
def update(self, delta, ticks):
if ticks > self.next_tick:
if self.alpha > 0:
self.next_tick += self.interval
self.alpha -= 4
self.image.set_alpha(self.alpha)
else:
self.kill()
self.center += self.vector * self.speed * delta
self.rect.center = self.center
class Game(Scene):
def __init__(self, engine):
super().__init__(engine)
self.particles = pygame.sprite.Group()
self.images = Images()
self.explode = []
for x in range(0, 320, 45):
self.explode.append(list(range(x, x + 45)))
def mouse_push(self, mouse_pos):
for part in self.explode:
angle = random.choice(part)
vector = pygame.Vector2()
vector.from_polar((1, angle))
particle = Particle(self.images.particle, mouse_pos, vector)
self.particles.add(particle)
def on_draw(self, surface):
surface.fill('black')
self.particles.draw(surface)
def on_event(self, event):
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
self.mouse_push(event.pos)
def on_update(self, delta, ticks):
self.particles.update(delta, ticks)
99 percent of computer problems exists between chair and keyboard.
Posts: 41
Threads: 7
Joined: Jun 2022
(Jul-06-2022, 08:11 PM)Windspar Wrote: It will help knowing your images sizes. For I can replace them in your code.
Sorry, the image ship1.png isn't used. I forgot to comment it out so it would run. Take that line out and it should work... I think.
Posts: 544
Threads: 15
Joined: Oct 2016
Okay I get to that soon. Here I refactor my code more.
import random
import pygame
class Scene:
def __init__(self, engine):
self.engine = engine
def on_draw(self, surface):
pass
def on_event(self, event):
pass
def on_update(self, delta, ticks):
pass
def on_quit(self):
self.engine.running = False
class DisplayEngine:
def __init__(self, caption, width, height, flags=0):
pygame.display.set_caption(caption)
self.surface = pygame.display.set_mode((width, height), flags)
self.rect = self.surface.get_rect()
self.clock = pygame.time.Clock()
self.running = False
self.gamespeed = 1
self.delta = 0
self.fps = 60
self._scene = Scene(self)
self._next_scene = None
def main_loop(self, scene=None):
if scene:
self._scene = scene
self.running = True
while self.running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
self._scene.on_quit()
else:
self._scene.on_event(event)
ticks = pygame.time.get_ticks()
self._scene.on_draw(self.surface)
self._scene.on_update(self.delta, ticks)
pygame.display.flip()
self.delta = self.clock.tick(self.fps) * 0.001
class Images:
def __init__(self):
self.particle = pygame.Surface((20, 20), pygame.SRCALPHA)
self.particle.fill((200, 100, 100))
class Ticker:
def __init__(self, interval, next_tick=0):
self.interval = interval
if next_tick == 0:
self.next_tick = pygame.time.get_ticks()
else:
self.next_tick = next_tick
def tick(self, ticks):
if ticks > self.next_tick:
self.next_tick += self.interval
return True
else:
return False
def update(self, ticks=0):
if ticks > 0:
self.next_tick = ticks
else:
self.next_tick = pygame.time.get_ticks()
class Particle(pygame.sprite.Sprite):
def __init__(self, image, pos, vector, interval):
super().__init__()
self.center = pos
self.image = image.copy()
self.rect = self.image.get_rect(center=pos)
self.alpha = 255
self.vector = vector
self.speed = random.randrange(8, 80)
self.ticker = Ticker(interval)
def update(self, delta, ticks):
if self.ticker.tick(ticks):
if self.alpha > 0:
self.alpha -= 4
self.image.set_alpha(self.alpha)
else:
self.kill()
self.center += self.vector * self.speed * delta
self.rect.center = self.center
class ParticleEngine:
def __init__(self, image):
self.image = image
self.particles = pygame.sprite.Group()
self.explode = []
for x in range(0, 320, 45):
self.explode.append(list(range(x, x + 45)))
def create(self, mouse_pos):
for part in self.explode:
angle = random.choice(part)
vector = pygame.Vector2()
vector.from_polar((1, angle))
particle = Particle(self.image, mouse_pos, vector, 50)
self.particles.add(particle)
class Game(Scene):
def __init__(self, engine):
super().__init__(engine)
self.images = Images()
self.particle_engine = ParticleEngine(self.images.particle)
def on_draw(self, surface):
surface.fill('black')
self.particle_engine.particles.draw(surface)
def on_event(self, event):
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
self.particle_engine.create(event.pos)
def on_update(self, delta, ticks):
self.particle_engine.particles.update(delta, ticks)
if __name__ == "__main__":
engine = DisplayEngine("Particles", 800, 600)
game = Game(engine)
engine.main_loop(game)
pygame.quit()
99 percent of computer problems exists between chair and keyboard.
Posts: 544
Threads: 15
Joined: Oct 2016
I figure your problem out. It because you link the same vector. You can not pass the same vector without copying it.
particle = Particle(self.game.images.particle, vec(self.pos), vector)
99 percent of computer problems exists between chair and keyboard.
Posts: 41
Threads: 7
Joined: Jun 2022
(Jul-07-2022, 12:56 AM)Windspar Wrote: I figure your problem out. It because you link the same vector. You can not pass the same vector without copying it.
particle = Particle(self.game.images.particle, vec(self.pos), vector)
Amazing! Thank you, I would never, ever have figured that out.
|