Skip to content

Instantly share code, notes, and snippets.

@oeloeloel
Last active July 22, 2020 18:32
Show Gist options
  • Save oeloeloel/c6acbbf783dc92a4cd829ad6516a6a24 to your computer and use it in GitHub Desktop.
Save oeloeloel/c6acbbf783dc92a4cd829ad6516a6a24 to your computer and use it in GitHub Desktop.
$gtk.reset seed: Time.now.to_i
#puts "++++++++++++++++++ RESET +++++++++++++++++++++"
=begin
Creates a few different effects using particles
explode: fly apart dramatically and burn with flaming colours
melt: fall apart in a downward direction while turning toxic green
disintegrate: break apart less dramatically without burning colours
to do
particles should allow init of step and direction
so an exploding sprite would appear to continue in the same
direction as it explodes rather than suddenly stop
=end
def tick args
# black background
args.outputs.solids << [0, 0, 1280, 720, 0, 0, 0]
# create the objects at the start rather than slow things down later
args.state.explode ||= ParticleEffect.new(args, ExplodeStrategy.new)
args.state.melt ||= ParticleEffect.new(args, MeltStrategy.new)
args.state.disintegrate ||= ParticleEffect.new(args, DisintegrateStrategy.new)
# calculate next moves etc.
args.state.explode.update
args.state.melt.update
args.state.disintegrate.update
# perform the effects when spacebar is pressed
# or the mouse is clicked
if args.inputs.keyboard.key_down.space || args.inputs.mouse.click
args.state.explode.activate(640, 640)
args.state.melt.activate(640, 440)
args.state.disintegrate.activate(640, 240)
end
# some useful stuff
debug args
end
# tells ParticleEffect how to behave in case of an explode effect
# blasts apart with a burning red colour palette
class ExplodeStrategy
# how big is it
def num_particles
256
end
# what colours each particle will cycle through
# (same colour repeated keeps it going for longer)
def colours
[
[194, 195, 199], [194, 195, 199], [194, 195, 199], [194, 195, 199], # light grey
[255, 236, 39], # yellow
[255, 163, 0], [255, 163, 0], # orange
[255, 0, 77], [255, 0, 77], [255, 0, 77], # red
[126, 37, 83], # dark red
]
end
# calculates the next move for each particle in this effect
# this is only called once and the particle will use the same
# movement each frame while accounting for gravity
# this one spreads the explosion out horizontally
def step
dir = normalize({x: (3*rand) - 1, y: (3*rand) - 1})
{x: (1.5/rand) * dir.x, y: (1.5*rand) * dir.y}
end
end
# tells ParticleEffect how to behave in case of an melt effect
# melts and drops down with a toxic green colour palette
class MeltStrategy
def num_particles
64
end
# toxic greens and greys. twinkle yellow at the end.
def colours
[
[194, 195, 199], [194, 195, 199], # light grey
[0, 228, 54], # light green
[0, 135, 81], # dark green
[95, 87, 79], # dark grey
[0, 228, 54], # light green
[0, 135, 81], # dark green
[95, 87, 79], # dark grey
[0, 135, 81], [0, 135, 81], # dark green
[95, 87, 79], # dark grey
[0, 135, 81], [0, 135, 81], # dark green
[95, 87, 79], # dark grey
[255, 236, 39], # yellow
]
end
# calculates the next move for each particle in this effect
# mostly fall down, slightly spread out
def step
dir = normalize({x: (0.3*rand) - 0.15, y: (1.5*rand) - 1.5})
{x: dir.x, y: (1.5*rand) * dir.y}
end
end
# tells ParticleEffect how to behave in case of a disintegrate effect
# blasts apart in a round shape without a burning red effect.
# colours can be swapped out for the colours in the sprite
class DisintegrateStrategy
def num_particles
64
end
def colours
[
[41, 173, 255], [41, 173, 255], # light blue
[194, 195, 199], [194, 195, 199], [194, 195, 199], [194, 195, 199], # light grey
[95, 87, 79], [95, 87, 79], # dark grey
]
end
def step
dir = normalize({x: (5*rand) - 3, y: (5*rand) - 3})
{x: (1.5*rand) * dir.x, y: (1.5*rand) * dir.y}
end
end
# parts that are common to all the effects
class ParticleEffect
GRAVITY = 0.05
# set up the bounds beyond which particles can be marked as inactive
# this is probably always going to be the left, bottom, width and height of
# the window
# since we have gravity, we won't test for particles going off the top
# because they will come back down
BOUNDS = [0, 0, 1280, -1]
def initialize args, strategy
@args, @strategy = args, strategy
# get effect specific values from supplied strategy
@num_particles=@strategy.num_particles
@colours = @strategy.colours
# initialize particles
@particles ||= @num_particles.map do
p = Particle.new(
@args,
@colours,
#@gravity,
GRAVITY,
BOUNDS
)
end
end
# called when it's time to make the effect happen
# supply x and y coordinates
def activate x, y
# get the movement for each particle from the strategy
# and activate the particle
@particles.map { |p|
p.activate x, y, @strategy.step.x, @strategy.step.y
}
end
# tell particles its a new frame
def update
@particles.map {|p| p.update}
end
# the small print
def serialize
{}
end
def inspect
serialize.to_s
end
def to_s
serialize.to_s
end
end
# individual particle
class Particle
# instances will be fed to args.outputs.sprites
attr_sprite
# ParticleEffect will get or set these
attr_accessor :step_x, :step_y, :r, :g, :b, :active
# do everything that can be done early to go faster later
def initialize args, colours, gravity, bounds
@args = args
@colours = colours
@gravity = gravity
@bounds = bounds
@x, @y, @w, @h = x, y, 4, 4
@path = 'sprites/white.png'
@active = false
end
# time to dance
def activate x, y, step_x, step_y
# active will stay true as long as the particle is inside bounds
# and we haven't run out of colours to display
@active = true
@x, @y = x, y
@step_x, @step_y = step_x, step_y
@colour_count = 1
@r = @colours[0][0]
@g = @colours[0][1]
@b = @colours[0][2]
end
def update
return if @active != true
# update position
@x += @step_x
@y += @step_y
@step_y -= @gravity
# 1 in 5 chance of colour shifting to next.
# Room for improvement here
if rand(5)==0
if check_still_alive == false
@active = false
else
@r = @colours[@colour_count][0]
@g = @colours[@colour_count][1]
@b = @colours[@colour_count][2]
@colour_count+=1
end
end
# sacrifice self to the beast
@args.outputs.sprites << self
end
# check to see if particle should remain active
def check_still_alive
return false if @colour_count >= @colours.count
return false if @bounds[0] != -1 && @x < @bounds[0]
return false if @bounds[1] != -1 && @y < @bounds[1]
return false if @bounds[2] != -1 && @x > @bounds[0]+@bounds[2]
return false if @bounds[3] != -1 && @y > @bounds[1]+@bounds[3]
return true
end
def serialize
{ step_x: step_x, step_y: step_y, r: r, g: g, b: b }
end
def inspect
serialize.to_s
end
def to_s
serialize.to_s
end
end
# a fun bit of geometry, used by the strategies
def normalize v
theta = Math.atan2(v.y, v.x)
{x: Math.cos(theta), y: Math.sin(theta)}
end
def debug args
# reset DR: press 1
if args.inputs.keyboard.key_down.one
$gtk.reset seed: Time.now.to_i
end
# crude instructions and FPS display
args.outputs.labels << [10, 30,
"Click or hit the spacebar. FPS: #{args.gtk.current_framerate.to_i}",
255, 255, 255, 255]
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment