Skip to content

Instantly share code, notes, and snippets.

@ab5tract
Last active December 12, 2023 14:58
Show Gist options
  • Select an option

  • Save ab5tract/ff53731543df4cedd1c5f15fb0599c93 to your computer and use it in GitHub Desktop.

Select an option

Save ab5tract/ff53731543df4cedd1c5f15fb0599c93 to your computer and use it in GitHub Desktop.
use v6.*;
use Raylib::Bindings;
use OO::Monitors;
## Initialization
constant screen-width = 1024;
constant screen-height = 768;
init-window(screen-width, screen-height, "Snowfall Simulator");
my int32 $size = 48;
my $white = init-white;
my $black = init-black;
my Int $FRAME = 0;
## Implementation Classes
class Snowflake {
my $TEXTURE; # Only one needed
has Bool $.falling is rw = True;
has Vector2 $.pos is required is rw;
has Num() $.weight is required;
method initialize-texture {
my $image = image-text('*', 32, $white);
$TEXTURE = load-texture-from-image($image);
unload-image($image);
}
method unload-texture { unload-texture($TEXTURE) }
method texture { $TEXTURE }
}
constant BOUNDS-SCALE = 4e0;
constant LAYERS = 8;
monitor SnowfallAccumulator {
has @.pixels = [ "" xx screen-width ] xx screen-height;
has $.filled-pixels = 0;
has $.main-fill-line = 0;
has @!layer-fill-lines = 0 xx LAYERS;
method fill-pixel($x, $y, $idx, $layer, $scale) {
my $range = (BOUNDS-SCALE).Int;
for (-$range)..$range -> $x-offset {
for (-$range)..$range -> $y-offset {
next unless ($y + $y-offset) > 0 && ($x + $x-offset) > 0;
@!pixels[$y + $y-offset][$x + $x-offset] = "$layer-$idx"; # note: no longer used.
}
$!filled-pixels++; # just to slow down the rising snow
}
}
method layer-line(Int $layer) {
@!layer-fill-lines[$layer];
}
method check-pixel($x, $y) {
return if $y < 0;
@!pixels[$y][$x]
}
method compactable(Int $layer) {
my $snow-pack = ($!main-fill-line + 20) * screen-width;
if $!filled-pixels > $snow-pack {
@!layer-fill-lines[$layer]++;
$!main-fill-line = max |@!layer-fill-lines, $!main-fill-line + 1;
}
}
}
my SnowfallAccumulator $accumulator .= new;
class SnowfallLayer {
has Snowflake @.snowflakes;
has Num $.scale is required;
has Int() $.max-snowflakes = 16 * 1/$!scale;
has Int $.layer-number is required;
has Vector2 $.wind is rw = Vector2.init(1.001e0 + (2.5e0 * $!scale) * ((1..3).pick %% 2 ?? -1 !! 1), 1.02e0);
has Bool $.started = False;
has Supply $!update-supply = Supply.interval((($!scale + 0.5) / 30 + ($!layer-number / 1000)));
has Supply $!more-supply = Supply.interval($!scale, $!scale + ($!layer-number / 50));
submethod TWEAK() {
self!fill-snowflakes;
$!update-supply.act: {
$!started && @!snowflakes.grep(*.so, :kv).map: -> $idx, $flake {
if not $flake.falling {
@!snowflakes.splice($idx, 1);
next;
} else {
$flake.pos.y = $flake.pos.y + (($flake.weight + $!scale) * 10) + $!wind.y;
$flake.pos.x = $flake.pos.x + (1 + ($!wind.x + (0.05 * (1 .. 2).roll)));
if $flake.pos.x > screen-width {
$flake.pos.x = $flake.pos.x - screen-width;
}
if $flake.pos.x < 0 {
$flake.pos.x = $flake.pos.x + screen-width;
}
if $flake.pos.y >= screen-height - $accumulator.main-fill-line {
my $size = BOUNDS-SCALE * $!scale;
$flake.pos.y = screen-height - $accumulator.layer-line($!layer-number) - $size;
until not $accumulator.check-pixel($flake.pos.x.Int, $flake.pos.y.Int) {
$flake.pos.y = $flake.pos.y - $size;
}
$accumulator.fill-pixel($flake.pos.x.Int, $flake.pos.y.Int, $idx, $!layer-number, $!scale);
@!snowflakes.splice($idx, 1);
}
}
};
$accumulator.compactable($!layer-number) if $FRAME %% 111;
}
$!more-supply.act: { $!started && self!fill-snowflakes };
}
method !fill-snowflakes() {
if @!snowflakes <= $!max-snowflakes {
for (^screen-width).pick(((1 .. 8).roll).abs) -> $x {
@!snowflakes.push: Snowflake.new: :pos(Vector2.init($x.Num, (^16).roll.Num)), :weight((1 .. 5).roll * $!scale);
}
}
}
method render {
for @!snowflakes.grep(Snowflake:D) -> $flake {
draw-texture-ex($flake.texture, $flake.pos, 0e0, $!scale, $white);
}
if $accumulator.main-fill-line -> $fill-lines {
draw-rectangle(0, screen-height - $fill-lines, screen-width, $fill-lines, $white);
}
}
method start { $!started = True }
}
class SnowfallRenderer {
has @.layers;
submethod TWEAK {
constant scales = (0.1e0, 0.2e0, 0.25e0, 0.3e0, 0.33e0, 0.36e0, 0.4e0, 0.5e0);
@!layers = scales.kv.map: -> $layer-number, $scale { SnowfallLayer.new: :$scale, :$layer-number }
}
method start { @!layers.map: *.start }
method render { @!layers.map: *.render }
}
## Rendering
set-target-fps(60);
Snowflake.initialize-texture;
my $renderer = SnowfallRenderer.new;
$renderer.start;
while !window-should-close {
$FRAME++;
begin-drawing;
clear-background($black);
$renderer.render;
draw-fps(0,0);
end-drawing;
if $FRAME > 10_000 {
$FRAME = 0;
}
}
Snowflake.unload-texture;
close-window;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment