How to Show Explosion Image When Collision Happens

How can I show explosion image when collision happens?

The explosion is just shown for a short moment. Use pygame.time.get_ticks() to return the number of milliseconds since pygame.init() was called. Calculate the point in time after that the explosion image has to be removed. Add the coordinates of the explosion and the end time point to the head of a list (explosionList ). Draw the explosion(s) in the main application loop. Remove the expired explosions from the tail of the list:

explosionList = []

while run:
current_time = pygame.time.get_ticks()
# [...]

for missile_idx, missile_val in enumerate(missiles)
# [...]

if missile_rect.colliderect(rock_rect):
explosion_sound.play()

explosion_pos_x = missile_pos_x
explosion_pos_y = missile_pos_y
end_time = current_time + 2000 # 2000 milliseconds = 2 seconds
explosionList.insert(0, (end_time, explosion_pos_x, explosion_pos_y))

# [...]

for i in range(len(explosionList)):
if current_time < explosionList[i][0]:
screen.blit(explosion, (explosionList[i][1], explosionList[i][2]))
else:
explosionList = explosionList[:i]
break

# [...]

With this algorithm it is possible to manage multiple explosions.

See Time, timer event and clock


Minimal example

Sample Image

import pygame
pygame.init()
window = pygame.display.set_mode((210, 210))

def create_rectangles():
global rectangles
w, h = window.get_size()
rectangles = []
for x in range(0, w - 60, 60):
for y in range(0, h - 60, 60):
rectangles.append(pygame.Rect(x + 30, y + 30, 30, 30))

create_rectangles()
hit_list = []
fade_out_time = 3000

run = True
while run:
current_time = pygame.time.get_ticks()

for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False

point = pygame.mouse.get_pos()
collideindex = pygame.Rect(point, (1, 1)).collidelist(rectangles)
if collideindex >= 0:
end_time = current_time + fade_out_time
hit_list.insert(0, (end_time, rectangles[collideindex].center))
del rectangles[collideindex]
if not hit_list and not rectangles:
create_rectangles()

window.fill(0)
for r in rectangles:
pygame.draw.rect(window, (255, 0, 0), r)
for i in range(len(hit_list)):
delta_time = hit_list[i][0] - current_time
if delta_time > 0:
radius = round(30 * delta_time / fade_out_time)
pygame.draw.circle(window, (255, 255, 0), hit_list[i][1], radius)
else:
hit_list = hit_list[:i]
break
pygame.display.flip()

Loading image of explosion when collision with character sprite in Pygame

First load the images globally. (I've put them into a directory with the name 'Explosions'.)

EXPLOSION_IMGS = [pg.image.load(os.path.join('Explosions', img)).convert_alpha()
for img in os.listdir('Explosions')]

Then reduce the Explosions class to this version. It accesses the global constant EXPLOSION_IMGS list and then just increments the frame counter in its update method, after the required time has passed.

class Explosion(pg.sprite.Sprite):
def __init__(self, center):
pg.sprite.Sprite.__init__(self)
self.image = EXPLOSION_IMGS[0]
self.rect = self.image.get_rect(center=center)
self.frame = 0
self.last_update = pg.time.get_ticks()
self.frame_rate = 50

def update(self):
now = pg.time.get_ticks()
if now - self.last_update > self.frame_rate:
self.last_update = now
self.frame += 1
if self.frame == len(EXPLOSION_IMGS):
self.kill()
else:
self.image = EXPLOSION_IMGS[self.frame]
self.rect = self.image.get_rect(center=self.rect.center)

In the update method of the game class, create the expl instance and add it to the self.all_sprites group. Done.

class Game:

def update(self):
# ...
TrumpHits = pg.sprite.spritecollide(self.trump, self.projectiles, True)
for hit in TrumpHits:
print("TRUMP HIT!!!")
expl = Explosion(hit.rect.center)
self.all_sprites.add(expl)

how to display animation after sprites collide/overlap using p5.js/p5.play

When the collision happens, you could create a new object which contains the information about the collision, and how long you want the collision to take place for, and then add that object to a new explosions = []. Something like this:

 explosions.push({
x: towers[j].position.x,
y: towers[j].position.y,
time: 20
});

Of course, in your example, you'll want to create the object at the position of the bullet when the collision happens.

And then in your draw() you could simply iterate through that array backwards and display the animation:

function draw() {
...
// loop backwards so we can remove from array
for (let i = explosions.length - 1; i >= 0; i--) {

animation(explodeAnimation, explosions[i].x, explosions[i].y);
explosions[i].time--;

if (explosions[i].time <= 0) {
explosions.splice(i, 1);
}
}
}

You could also potentially use a setTimeout but this is probably clearer.

Here's an example I created (The animation is a bit dodgy, I just copied it from this animation reference):

let numAsteroids = 10;
let towers;
let rock;

let spriteSheet;

let explosions = [];
function setup() {
createCanvas(400, 400);
spriteSheet = loadSpriteSheet('spritesheet.png', 171, 158, 11);
explodeAnimation = loadAnimation(spriteSheet);

rock = Group();
towers = Group();
createTowers();
createAsteroids();
}

function draw() {
background(220);


drawSprites(rock);
drawSprites(towers);
updateAsteroids();

// loop backwards so we can remove from array
for (let i = explosions.length - 1; i >= 0; i--) {

animation(explodeAnimation, explosions[i].x, explosions[i].y);
explosions[i].time--;

if (explosions[i].time <= 0) {
explosions.splice(i, 1);
}
}

}

function createAsteroids() {
// code to spawn asteroids at random locations
for (let i = 0; i < numAsteroids; i++) {
let asteroid = createSprite(random(0, width), random(-50, -350), 40, 40);
asteroid.maxSpeed = random(1, 3);
rock.add(asteroid);

let tower = floor(random(3))

asteroid.attractionPoint(asteroid.maxSpeed, towers[tower].position.x, towers[tower].position.y);
}
}

function updateAsteroids() {
// draws asteroids moving down and checking collision
for (let i = rock.length - 1; i >= 0; i--) {
if (rock[i].position.y > height) {
rock[i].position.y = 0;
rock[i].position.x = random(0, width);
}

for (let j = towers.length - 1; j >=0; j--) {
if (rock[i].overlap(towers[j])) {
rock[i].position.y = 0;
rock[i].position.x = random(0, width);

explosions.push({
x: towers[j].position.x,
y: towers[j].position.y,
time: 20
});
}
}
}
}

function createTowers() {
towers.push(createSprite(40, height - 30, 40, 90));
towers.push(createSprite(width/2, height - 30, 40, 90));
towers.push(createSprite(width-40, height - 30, 40, 90));
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/addons/p5.sound.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/molleindustria/p5.play/lib/p5.play.js"></script>

<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />

</head>
<body>
<script src="sketch.js"></script>
</body>
</html>

how to replace picture and check for collision with new image and and a moving image in pygame

Look at pygame.time.Clock(). If you set an FPS rate (say 20), All you have to do is:

myClock = pg.time.Clock()
FPS = 20
bomb_placed = True
bomb_timer = 0
main_loop():
myClock.tick(FPS) # This will keep your FPS at 20 or below so you can estimate time
if bomb_placed:
bomb_timer += myClock.get_time()
if bomb_timer >= 3000:
# Draw the bomb explosion to the screen
if bomb_timer >= 5000:
bomb_timer = 0
bomb_placed = False # Stops displaying the bomb explosion

Hope that helped. Feel free to check out the pygame docs on the subject:

https://www.pygame.org/docs/ref/time.html#pygame.time.Clock.get_time

Example for multiple bombs:

myClock = pg.time.Clock()

FPS = 20

bombA, bombB, bombC = False, False, False
bombA_timer, bombB_timer, bombC_timer = 0, 0, 0
bombs = [bombA, bombB, bombC]
bomb_timers = [bombA_timer, bombB_timer, bombC_timer]

main_loop():
myClock.tick(FPS)
for i in len(bombs):
if bombs[i]:
bomb_timers[i] += myClock.get_time()
if bomb_timers[i] >= 3000:
draw_explosion()
if bomb_timer >= 5000:
bomb_timers[i] = 0
bomb_placed[i] = False

This example simply loops through each of the bomb variables that represent the possible bombs on the screen- in this case a max of 3.

For lots of bombs, first create two dictionaries to hold the values of the bombs (active or not active) and their respective timers. Then loop through each of the bombs and, if the bomb is active, update the timer accordingly:

bombs = {"bomb0": False}  # Create dictionaries for the bombs and their timers, with 0-1 entries. (I did one to show you what it looks like).
bombTimers = {bombTimer0: 0}
main_loop():
myClock.tick(FPS)
for i in len(bombs): # For each bomb you have created
bombName = "bomb" + str(i) # get the name of the bomb and corresponding timer
timerName = "bombTimer" + str(i)
if bombs[bombName]: # If the bomg has a value of True:
bombTimers[timerName] += myClock.get_time() # Update the timer
if bombTimers[timerName] >= 3000: # Check if you need to draw the explosion
draw_explosion()
if bomb_timer >= 5000: # Check if the bomb animation is over, and reset the bomb so that it can be used again in the future
bombTimers[timerName] = 0
bombs[bombName] = False

This code works exactly the same as the previous example, but it is much easier to work with. You can easily add more bombs while not starting with an unnecessary amount, like so:

bombCount = 1
bombs = {"bomb0": False}
bombTimers = {"bombTimer0": 0}
main_loop():
if player_placed_bomb(): # When the player drops a bomb in your game:
placed = False # Make a variable saying that you have not activaded the bomb and timer yet
for key, val in bombs: # For each bomb in the bombs dictionary:
if not val: # If the value of that bomb is False (Not being used):
val = True # Activates the bomb that was already created
placed = True # Updates the placed variable indicating that you set a bomb to active
break
if not placed: # After looping through the dictionary, if there were no inactive bomb variables:
bombs["bomb" + str(bombCounter)] = True # Create a new bomb in your dictionary, with a unique name.
bombTimers["bombTimer" + str(bombCounter)] = 0 # Created a new corresponding timer
bombCounter += 1 # Add 1 to the bombCounter, which is used to create the unique name of the next bomb you create.

# Rest of main_loop()

This code ensures that you are working with the smallest dictionaries possible to reduce unneeded computation power. When a bomb explodes, it will become false and it's matching timer reset. The first if loop in the code above ensures that all existing bombs are active, and if not reuses the old bomb and timer. If all bombs are being used, It creates new entries in the dictionaries to allow for more bombs.

I added an unnecesarry amount of comments, but I hope it makes it more understandable for you. Let me know if you have more questions on how this works. If you are unfamiliar with python dict() objects, there are lots of resources on the internet that explain them thoughroughly.
Disclaimer: I haven't actually ran the code, but it should function how it is supposed to. Let me know if it doesn't.

Here are some thoughts on the reformed code:

  1. You use "str(bag["bombs"])" quite a bit in your code- consider defining a variable: b = str(bag["bombs"]), and using that. It would make it much simpler.

  2. I don't know why you delete the "bombs_dict" and "explosions_dict" entries after you are finished with them. The way your loop is set up, I will result in an error. I would suggest rather than deleting the entries, you keep them and attempt to reuse them, as shown in the code snippet above.

  3. Or, if you like deleting them, you need to figure out a way to rename certain keys in your dictionaries so that the names are in order. (if you delete bomb2 from bombs_dict, and you have bomb1 and bomb3, you need to change bomb3 to bomb2 or the loop wont work as intended). You will also need to alter your bombCount variable accordingly.

Collision Detection between two images in Java

I think your problem is that you are not using good OO design for your player and enemies. Create two classes:

public class Player
{
int X;
int Y;
int Width;
int Height;

// Getters and Setters
}

public class Enemy
{
int X;
int Y;
int Width;
int Height;

// Getters and Setters
}

Your Player should have X,Y,Width,and Height variables.

Your enemies should as well.

In your game loop, do something like this (C#):

foreach (Enemy e in EnemyCollection)
{
Rectangle r = new Rectangle(e.X,e.Y,e.Width,e.Height);
Rectangle p = new Rectangle(player.X,player.Y,player.Width,player.Height);

// Assuming there is an intersect method, otherwise just handcompare the values
if (r.Intersects(p))
{
// A Collision!
// we know which enemy (e), so we can call e.DoCollision();
e.DoCollision();
}
}

To speed things up, don't bother checking if the enemies coords are offscreen.

trying to get sprite to disappear from screen after certain amount of time

You have to add another group for the explosions and to add the new Explosion object to this group, too:

class AlienInvasion:
# [...]

def __init__(self):
# [...]

self.explosions = pygame.sprite.Group()

# [...]

def _update_bullets(self):
# [...]

for collision in collisions:

expl = Explosion(collision.rect.center)
self.explosions.add(expl)
self.all_sprites.add(expl)

Get the current time (pygame.time.get_ticks()) when the Explosion object is constructed and kill() the object in the update method after a certain time span:

class Explosion(Sprite):

def __init__(self, center):
super().__init__()

self.image = pygame.image.load('images/explo.bmp')
self.rect = self.image.get_rect()
self.rect.center = center

self.start_time = pygame.time.get_ticks()

def update(self):

current_time = pygame.time.get_ticks()
timespan = 1000 # 1000 milliseconds = 1 second

if current_time > self.start_time + timespan:
self.kill()

Do not forget to invoke self.explosions.update() in AlienInvasion._update_bullets:

class AlienInvasion:
# [...]

def _update_bullets(self):
# [...]

self.explosions.update()


Related Topics



Leave a reply



Submit