Skip to content

Commit

Permalink
Added particle effects
Browse files Browse the repository at this point in the history
  • Loading branch information
Philipp Sehmisch committed Sep 20, 2020
1 parent 40e6331 commit 49060d4
Show file tree
Hide file tree
Showing 10 changed files with 155 additions and 96 deletions.
Binary file modified build/img/explosion.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added build/img/spark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const CONST = {
EXPLOSION_MIN_RADIUS: 64, //radius in pixels from within the maximum explosion force is applied
EXPLOSION_MAX_RADIUS: 640, //radius in pixels from without explosions are completely ignored
PLAYER_GROUND_COLLISION: -46, //distance in pixels between player center of mass and feet
PLAYER_ACCEL_GROUND: 2000, // player acceleration on ground
PLAYER_ACCEL_GROUND: 0, // player acceleration on ground
PLAYER_FRICTION_GROUND: 0.999, // fraction of speed the player loses per second while grounded
PLAYER_ACCEL_AIR: 1000, // player acceleration while airborne
PLAYER_FRICTION_AIR: 0.5, // fraction of speed the player loses per second while airborne
Expand Down
37 changes: 29 additions & 8 deletions entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,16 @@ class FlyForwardBehavior extends Behavior {

private velocity: Point;

constructor(entity: Entity, pos: Point, direction: Point, initialVelocity: Point) {
constructor(entity: Entity, direction: Point, velocity: number, initialVelocity: Point) {
super()

let distx = direction.x
let disty = direction.y
let dist = Math.sqrt(distx * distx + disty * disty)

let directionNormalized = { x: distx / dist, y: disty / dist }
this.velocity = { x: directionNormalized.x * CONST.ROCKET_VELOCITY + initialVelocity.x,
y: directionNormalized.y * CONST.ROCKET_VELOCITY + initialVelocity.y }
this.velocity = { x: directionNormalized.x * velocity + initialVelocity.x,
y: directionNormalized.y * velocity + initialVelocity.y }
entity.sprite.rotation = Math.atan(directionNormalized.y / directionNormalized.x)
if (directionNormalized.x < 0) entity.sprite.rotation += Math.PI
}
Expand Down Expand Up @@ -152,26 +152,47 @@ class ExplodeOnImpactBehavior extends Behavior {

state.addEntity(new Particle(entity.pos,
explosionSprite,
CONST.EXPLOSION_FX_LIFETIME));
CONST.EXPLOSION_FX_LIFETIME,
false,
0));
state.player.explosion(entity.pos)
}
}


class SelfDestructBehavior extends Behavior {
private initialLifetime: number;
private lifetimeLeft: number;
private initialLifetime: number
private lifetimeLeft: number
private fade: boolean

constructor(lifetime: number) {
constructor(lifetime: number, fade: boolean) {
super()
this.lifetimeLeft = lifetime
this.initialLifetime = lifetime
this.fade = fade
}

update(entity:Entity, deltaTime: number, state: State) : void {
this.lifetimeLeft -= deltaTime;
if (this.lifetimeLeft < 0) {
state.removeEntity(entity)
}
if (this.fade) {
entity.sprite.alpha = this.lifetimeLeft / this.initialLifetime
}
}
}
}

let SelfDestructIfOffscreen = new class extends Behavior {

update(entity:Entity, deltaTime: number, state: State) : void {
if (entity.pos.x - state.camera.pos.x > CONST.SCREEN_WIDTH ||
entity.pos.x - state.camera.pos.x < -CONST.SCREEN_WIDTH ||
entity.pos.y - state.camera.pos.y > CONST.SCREEN_HEIGHT ||
entity.pos.y - state.camera.pos.y < -CONST.SCREEN_HEIGHT
)
{
state.removeEntity(entity)
}
}
}
43 changes: 38 additions & 5 deletions particle.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,49 @@
class Particle extends Entity {


constructor(pos: Point, sprite: Sprite, lifetime: number) {
constructor(pos: Point, sprite: Sprite, lifetime: number, fade: boolean, velocity:number) {
super(pos)
this.sprite = sprite
this.sprite.drawOrder = CONST.LAYER_FX

this.behaviors.push(new SelfDestructBehavior(lifetime))
this.behaviors.push(new SelfDestructBehavior(lifetime, fade))
if ("setProgress" in this.sprite) {
this.behaviors.push(new AnimatedBehavior(lifetime, false))
}

if (velocity > 0) {
let angle = Math.random() * Math.PI * 2
this.behaviors.push(new FlyForwardBehavior(this, { x: Math.sin(angle), y:Math.cos(angle)}, velocity, { x:0, y:0}))
}
}

}

class ParticleEmitterBehavior extends Behavior {

private time: number = 0
private particleLifetime: number
private particleSpread: number
private particleSprite: ImageResource
public frequency: number

constructor(particleLifetime: number, particleSprite: ImageResource, frequency: number, spread:number) {
super()
this.particleLifetime = particleLifetime
this.particleSprite = particleSprite
this.particleSpread = spread
this.frequency = frequency
}

update(entity:Entity, deltaTime: number, state: State) : void {

this.time += deltaTime
while (this.time > this.frequency) {
state.addEntity(new Particle(
entity.pos,
new SimpleSprite(this.particleSprite),
this.particleLifetime,
true,
Math.random() * Math.random() * this.particleSpread)
)
this.time -= this.frequency
}
}
}
152 changes: 76 additions & 76 deletions player.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
class Player extends Entity {

private isGrounded = false
private timeSinceLastRocket = 0
public velocity: Point = {x: 0, y:0}
public airtime = 0
private rocketlauncher: Rocketlauncher
Expand All @@ -12,78 +10,83 @@ class Player extends Entity {
this.sprite.drawOrder = CONST.LAYER_PLAYER_FG
this.sprite.renderPivot = {x:30, y:50}
this.rocketlauncher = rocketlauncher
}

update(deltaTime: number, state: State) : void {
// apply gravity
this.velocity.y += CONST.GRAVITY * deltaTime

// apply input
let controlForce = deltaTime * ( this.isGrounded ? CONST.PLAYER_ACCEL_GROUND : CONST.PLAYER_ACCEL_AIR )
if(Input.keyDown("KeyA") || Input.keyDown("ArrowLeft")) {
this.velocity.x -= controlForce
this.sprite.flipped = true
}
if(Input.keyDown("KeyD") || Input.keyDown("ArrowRight")) {
this.velocity.x += controlForce
this.sprite.flipped = false
}

// apply friction
let frictionFraction: number;
if (this.isGrounded) {
frictionFraction = Math.pow((1 - CONST.PLAYER_FRICTION_GROUND), deltaTime )
} else {
frictionFraction = Math.pow((1 - CONST.PLAYER_FRICTION_AIR), deltaTime )
}
this.velocity.x *= frictionFraction
this.velocity.y *= frictionFraction

// apply velocity
this.pos.x += this.velocity.x * deltaTime
this.pos.y += this.velocity.y * deltaTime

// ground collision checking
if (this.pos.y > CONST.PLAYER_GROUND_COLLISION) {
this.pos.y = CONST.PLAYER_GROUND_COLLISION;
if (!this.isGrounded) {
this.velocity.x *= CONST.PLAYER_GROUND_BOUNCE
this.velocity.y *= -CONST.PLAYER_GROUND_BOUNCE
} else {
this.velocity.y = 0
this.behaviors.push(new class extends Behavior {
private isGrounded = false
private timeSinceLastRocket = 0

update(entity: Entity, deltaTime: number, state: State) : void {
// apply gravity
let player = entity as Player
player.velocity.y += CONST.GRAVITY * deltaTime

// apply input
let controlForce = deltaTime * ( this.isGrounded ? CONST.PLAYER_ACCEL_GROUND : CONST.PLAYER_ACCEL_AIR )
if(Input.keyDown("KeyA") || Input.keyDown("ArrowLeft")) {
player.velocity.x -= controlForce
player.sprite.flipped = true
}
if(Input.keyDown("KeyD") || Input.keyDown("ArrowRight")) {
player.velocity.x += controlForce
player.sprite.flipped = false
}

// apply friction
let frictionFraction: number;
if (this.isGrounded) {
frictionFraction = Math.pow((1 - CONST.PLAYER_FRICTION_GROUND), deltaTime )
} else {
frictionFraction = Math.pow((1 - CONST.PLAYER_FRICTION_AIR), deltaTime )
}
player.velocity.x *= frictionFraction
player.velocity.y *= frictionFraction

// apply velocity
player.pos.x += player.velocity.x * deltaTime
player.pos.y += player.velocity.y * deltaTime

// ground collision checking
if (player.pos.y > CONST.PLAYER_GROUND_COLLISION) {
player.pos.y = CONST.PLAYER_GROUND_COLLISION;
if (!this.isGrounded) {
player.velocity.x *= CONST.PLAYER_GROUND_BOUNCE
player.velocity.y *= -CONST.PLAYER_GROUND_BOUNCE
} else {
player.velocity.y = 0
}
this.isGrounded = true
player.airtime = 0
} else {
this.isGrounded = false
player.airtime += deltaTime
}


// make sure the rocket launcher follows
player.rocketlauncher.pos.x = player.sprite.flipped ? player.pos.x + 8 : player.pos.x - 8
player.rocketlauncher.pos.y = player.pos.y - CONST.ROCKET_START_HEIGHT
player.rocketlauncher.sprite.flipped = player.sprite.flipped

// set direction of rocket launcher
let direction = { x: state.cursor.pos.x - player.rocketlauncher.pos.x,
y: state.cursor.pos.y - player.rocketlauncher.pos.y }
player.rocketlauncher.sprite.rotation = Math.atan(direction.y / direction.x)
if (direction.x > 0 === player.rocketlauncher.sprite.flipped) {
player.rocketlauncher.sprite.rotation += Math.PI
}

// shooting
this.timeSinceLastRocket += deltaTime
if(Input.mouse.click && this.timeSinceLastRocket >= CONST.ROCKET_SHOOTING_COOLDOWN) {
this.timeSinceLastRocket = 0
state.addEntity(new Rocket(
player.rocketlauncher.pos,
direction,
player.velocity
))
}
}
this.isGrounded = true
this.airtime = 0
} else {
this.isGrounded = false
this.airtime += deltaTime
}


// make sure the rocket launcher follows
this.rocketlauncher.pos.x = this.sprite.flipped ? this.pos.x + 8 : this.pos.x - 8
this.rocketlauncher.pos.y = this.pos.y - CONST.ROCKET_START_HEIGHT
this.rocketlauncher.sprite.flipped = this.sprite.flipped

// set direction of rocket launcher
let direction = { x: state.cursor.pos.x - this.rocketlauncher.pos.x,
y: state.cursor.pos.y - this.rocketlauncher.pos.y }
this.rocketlauncher.sprite.rotation = Math.atan(direction.y / direction.x)
if (direction.x > 0 === this.rocketlauncher.sprite.flipped) {
this.rocketlauncher.sprite.rotation += Math.PI
}

// shooting
this.timeSinceLastRocket += deltaTime
if(Input.mouse.click && this.timeSinceLastRocket >= CONST.ROCKET_SHOOTING_COOLDOWN) {
this.timeSinceLastRocket = 0
state.addEntity(new Rocket(
this.rocketlauncher.pos,
direction,
this.velocity
))
}

})
}

explosion(point: Point) {
Expand All @@ -106,16 +109,13 @@ class Player extends Entity {
}
}

class Rocketlauncher extends Entity {

class Rocketlauncher extends Entity {

constructor() {
super( {x:0, y:0} )
this.sprite = new SimpleSprite(ImageResource.ROCKETLAUNCHER)
this.sprite.renderPivot = {x:30, y:8}
this.sprite.drawOrder = CONST.LAYER_PLAYER_BG
}

update(deltaTime: number, state: State) : void {
}
}
1 change: 1 addition & 0 deletions renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class Renderer {
}
this.ctx.translate( -r.sprite.renderPivot.x, -r.sprite.renderPivot.y)

this.ctx.globalAlpha = r.sprite.alpha
r.sprite.draw(this.ctx)
this.ctx.restore()

Expand Down
4 changes: 3 additions & 1 deletion resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ enum ImageResource {
UFO = "img/ufo.png",
CURSOR = "img/cursor.png",
ROCKET = "img/rocket.png",
EXPLOSION = "img/explosion.png"
EXPLOSION = "img/explosion.png",
SPARK = "img/spark.png",
PUFF = "img/puff.png"
}

class Preloader {
Expand Down
6 changes: 4 additions & 2 deletions rocket.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
class Rocket extends Entity {

static PARTICLE_SPRITE: Sprite

constructor(pos: Point, direction: Point, initialVelocity: Point) {
super(pos)

this.sprite = new SimpleSprite(ImageResource.ROCKET)
this.sprite.renderPivot = {x: 19, y: 9}
this.sprite.drawOrder = CONST.LAYER_MOBS
this.behaviors.push(new FlyForwardBehavior(this, pos, direction, initialVelocity))
this.behaviors.push(new FlyForwardBehavior(this, direction, CONST.ROCKET_VELOCITY, initialVelocity))
this.behaviors.push(new ExplodeOnImpactBehavior)
this.behaviors.push(new ParticleEmitterBehavior(0.25, ImageResource.SPARK, 0.01, 3))
this.behaviors.push(SelfDestructIfOffscreen)
}

}
6 changes: 3 additions & 3 deletions sprite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ abstract class Sprite {
public renderPivot: Point = { x:0, y:0 }
public isLoaded: boolean;
public flipped: boolean = false;
public alpha: number = 1.0
public drawOrder: number;

public image: CanvasImageSource;

public abstract draw(ctx: CanvasRenderingContext2D): void
}

class SimpleSprite extends Sprite {

private image: CanvasImageSource;

public constructor(src: ImageResource) {
super()
Expand All @@ -32,9 +34,7 @@ class SimpleSprite extends Sprite {
}

class AnimatedSprite extends Sprite {


private image: CanvasImageSource;
private framesize: Point;
private currentFrame = 0;

Expand Down

0 comments on commit 49060d4

Please sign in to comment.