-
-
Save ab5tract/ff53731543df4cedd1c5f15fb0599c93 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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