Skip to content

Commit

Permalink
Add initial explosion particle effect
Browse files Browse the repository at this point in the history
  • Loading branch information
dylanlangston committed Jan 8, 2024
1 parent 272926c commit 0fba4e5
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/Build_raylib.zig
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,8 @@ pub fn linkWithEmscripten(
"-sMODULARIZE",
"-sEXPORT_NAME=emscriptenModuleFactory",
"-sEXPORT_ES6",
"-sINITIAL_MEMORY=4mb",
"-sSTACK_SIZE=1mb",
});
return emcc_command;
}
Expand Down
75 changes: 75 additions & 0 deletions src/Models/Explosion.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
const std = @import("std");
const raylib = @import("raylib");
const Shared = @import("../Shared.zig").Shared;
const Color = @import("../Colors.zig").Color;
const Particle = @import("Particle.zig").Particle;

pub const Explosion = struct {
active: bool,
position: raylib.Vector2,
lifeSpawn: f32,
particle: [PARTICLE_COUNT]Particle,
blastRadius: f32,

const ANIMATION_SPEED_MOD = 15;
const PARTICLE_COUNT = 50;

pub fn init(position: raylib.Vector2, color: Color, blastRadius: f32) Explosion {
var particles: [PARTICLE_COUNT]Particle = undefined;
for (0..PARTICLE_COUNT) |i| {
particles[i] = Particle.init(position, GetRandomColor(color), Shared.Random.Get().float(f32) * 4);
}

return Explosion{
.active = true,
.position = position,
.lifeSpawn = 0,
.particle = particles,
.blastRadius = blastRadius,
};
}

pub inline fn GetRandomColor(color: Color) raylib.Color {
switch (Shared.Random.Get().intRangeAtMost(u8, 0, 2)) {
0 => {
return color.Base;
},
1 => {
return color.Light;
},
else => {
return color.Dark;
},
}
}

pub inline fn Update(self: *@This(), screenSize: raylib.Vector2) void {
if (self.active) {
self.lifeSpawn += raylib.getFrameTime() * ANIMATION_SPEED_MOD;

var nonActiveCount: u32 = 0;
for (0..PARTICLE_COUNT) |i| {
var particle = self.particle[i];
particle.Update(screenSize, self.blastRadius * 5);
if (self.lifeSpawn < 5) {
if (!particle.active) {
particle.Reset(self.position);
}
} else {
if (!particle.active) nonActiveCount += 1;
}

self.particle[i] = particle;
}
self.active = nonActiveCount < PARTICLE_COUNT;
}
}

pub inline fn Draw(self: @This()) void {
if (self.active) {
for (0..PARTICLE_COUNT) |i| {
self.particle[i].Draw();
}
}
}
};
18 changes: 18 additions & 0 deletions src/Models/Meteor.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const Shared = @import("../Shared.zig").Shared;
const Player = @import("./Player.zig").Player;
const Shoot = @import("./Shoot.zig").Shoot;
const Alien = @import("./Alien.zig").Alien;
const Explosion = @import("./Explosion.zig").Explosion;

pub const SpriteFrames = 5;
const MeteorSprite1 = Shared.Sprite.init(SpriteFrames, .Meteor1);
Expand All @@ -27,6 +28,7 @@ pub const Meteor = struct {
color: raylib.Color,
frame: f32,
meteorSprite: Shared.Sprite,
explosion: Explosion,

const ANIMATION_SPEED_MOD = 15;
pub const METEORS_SPEED = 3;
Expand Down Expand Up @@ -65,6 +67,7 @@ pub const Meteor = struct {
.color = Shared.Color.White,
.frame = 0,
.meteorSprite = GetSprite(),
.explosion = Explosion{ .active = false, .particle = undefined, .lifeSpawn = 0, .position = undefined, .blastRadius = radius },
};

return meteor;
Expand Down Expand Up @@ -166,6 +169,10 @@ pub const Meteor = struct {
}

pub inline fn Update(self: *@This(), player: Player, comptime shoots: []Shoot, comptime aliens: []Alien, comptime alien_shoots: []Shoot, screenSize: raylib.Vector2, shipHeight: f32, base_size: f32) MeteorStatus {
if (self.explosion.active) {
self.explosion.Update(screenSize);
}

// If Active
if (self.active) {
// Reset Frame
Expand All @@ -184,6 +191,7 @@ pub const Meteor = struct {
// Phase 2, check per pixel collision with player
if (PerPixelCollisionDetection(self.*, player, shipHeight, base_size)) {
self.active = false;
self.explosion = Explosion.init(self.position, Shared.Color.Green, self.radius);

Shared.Sound.Play(.Explosion);
return MeteorStatus{ .collide = true };
Expand Down Expand Up @@ -253,6 +261,7 @@ pub const Meteor = struct {
shoots[i].active = false;
shoots[i].lifeSpawn = 0;
self.active = false;
self.explosion = Explosion.init(self.position, Shared.Color.Green, self.radius);

return MeteorStatus{ .shot = shoots[i] };
}
Expand All @@ -269,6 +278,7 @@ pub const Meteor = struct {
alien_shoots[i].active = false;
alien_shoots[i].lifeSpawn = 0;
self.active = false;
self.explosion = Explosion.init(self.position, Shared.Color.Green, self.radius);

return MeteorStatus{ .shot = alien_shoots[i] };
}
Expand All @@ -281,6 +291,10 @@ pub const Meteor = struct {
}
self.frame = SpriteFrames - 1;

if (self.explosion.active) {
return MeteorStatus{ .animating = true };
}

return MeteorStatus{ .default = true };
}

Expand Down Expand Up @@ -388,6 +402,10 @@ pub const Meteor = struct {
const activeRadiusY = 375;

pub inline fn Draw(self: @This(), shipPosition: raylib.Vector2) void {
if (self.explosion.active) {
self.explosion.Draw();
}

if (self.position.x == inactivitePoint and self.position.y == inactivitePoint) return;
if (self.frame == SpriteFrames - 1) return;

Expand Down
92 changes: 92 additions & 0 deletions src/Models/Particle.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
const std = @import("std");
const raylib = @import("raylib");
const Shared = @import("../Shared.zig").Shared;

pub const Particle = struct {
position: raylib.Vector2,
speed: raylib.Vector2,
radius: f32,
lifeSpawn: f32,
active: bool,
color: raylib.Color,

const ANIMATION_SPEED_MOD = 100;

const PARTICLE_MAX_SPEED: f32 = 100;
const PARTICLE_MIN_SPEED: f32 = 20;

const LifeSpanLength = 50;

pub inline fn init(position: raylib.Vector2, color: raylib.Color, radius: f32) Particle {
const rotation = Shared.Random.Get().float(f32) * 360;
return Particle{
.position = position,
.speed = raylib.Vector2.init(
@cos((std.math.degreesToRadians(f32, rotation) * (PARTICLE_MAX_SPEED - PARTICLE_MIN_SPEED)) + PARTICLE_MIN_SPEED),
@sin((std.math.degreesToRadians(f32, rotation) * (PARTICLE_MAX_SPEED - PARTICLE_MIN_SPEED)) + PARTICLE_MIN_SPEED),
),
.radius = radius,
.active = true,
.lifeSpawn = Shared.Random.Get().float(f32) * LifeSpanLength,
.color = color.alpha((Shared.Random.Get().float(f32) * 0.2) + 0.55),
};
}

pub inline fn Reset(self: *@This(), position: raylib.Vector2) void {
self.position = position;
const rotation = Shared.Random.Get().float(f32) * 360;
self.speed = raylib.Vector2.init(
@cos((std.math.degreesToRadians(f32, rotation) * (PARTICLE_MAX_SPEED - PARTICLE_MIN_SPEED)) + PARTICLE_MIN_SPEED),
@sin((std.math.degreesToRadians(f32, rotation) * (PARTICLE_MAX_SPEED - PARTICLE_MIN_SPEED)) + PARTICLE_MIN_SPEED),
);
self.active = true;
self.lifeSpawn = Shared.Random.Get().float(f32) * (LifeSpanLength / 2);
}

pub inline fn Update(self: *@This(), screenSize: raylib.Vector2, endLifeSpan: f32) void {
if (self.active) {
self.lifeSpawn += raylib.getFrameTime() * ANIMATION_SPEED_MOD;

// Movement
self.position.x += self.speed.x;
self.position.y -= self.speed.y;

// Collision logic: particle vs walls
if (self.position.x > screenSize.x + self.radius) {
self.active = false;
self.lifeSpawn = 0;
} else if (self.position.x < 0 - self.radius) {
self.active = false;
self.lifeSpawn = 0;
}
if (self.position.y > screenSize.y + self.radius) {
self.active = false;
self.lifeSpawn = 0;
} else if (self.position.y < 0 - self.radius) {
self.active = false;
self.lifeSpawn = 0;
}

// Life of particle
if (self.lifeSpawn >= endLifeSpan) {
self.position.x = 0;
self.position.y = 0;
self.speed.x = 0;
self.speed.y = 0;
self.lifeSpawn = 0;
self.active = false;
}
}
}

pub inline fn Draw(self: @This()) void {
if (self.active) {
const color = if (self.lifeSpawn > 15) self.color else self.color.alpha((30 - self.lifeSpawn) / 30);
raylib.drawCircleV(
self.position,
self.radius,
color,
);
}
}
};

0 comments on commit 0fba4e5

Please sign in to comment.