Instead of the loadscreen eager-loading ALL level chunks to Go Images, only
load the chunks within the "LoadingViewport" - which is the on-screen
Viewport plus a margin of chunks off the screen edges.
During gameplay, every few ticks, reevaluate which chunks are inside or
outside the LoadingViewport; for chunks outside, free their SDL2 textures
and free their cached bitmaps to keep overall memory usage down. The
AzulianTag-Forest level now stays under 200 Textures at any given time
and the loadscreen goes faster as it doesn't have to load every chunk's
images up front.
The LoadUnloadChunk feature can be turned on/off with feature flags. If
disabled the old behavior is restored: loadscreen loads all images and
the LoadUnloadChunks function is not run.
Other changes:
* loadscreen: do not free textures in the Hide() function as this runs on
a different goroutine and may break. The 4 wallpaper textures are OK
to keep in memory anyway, the loadscreen is reused often!
* Free more leaked textures: on the Inventory frame and when an actor
calls Self.Destroy()
* Stop leaking goroutines in the PubSub feature of the doodad script
engine; scripting.Supervisor.Teardown() sends a stop signal to all
scripts to clean up neatly. Canvas.Destroy() tears down its scripting
supervisor automatically.
* Added to the F3 Debug Overlay is a "Texture:" label that counts the number
of textures currently loaded by the (SDL2) render engine.
* Added Teardown() functions to Level, Doodad and the Chunker they both use
to free up SDL2 textures for all their cached graphics.
* The Canvas.Destroy() function now cleans up all textures that the Canvas
is responsible for: calling the Teardown() of the Level or Doodad, calling
Destroy() on all level actors, and cleaning up Wallpaper textures.
* The Destroy() method of the game's various Scenes will properly Destroy()
their canvases to clean up when transitioning to another scene. The
MainScene, MenuScene, EditorScene and PlayScene.
* Fix the sprites package to actually cache the ui.Image widgets. The game
has very few sprites so no need to free them just yet.
Some tricky places that were leaking textures have been cleaned up:
* Canvas.InstallActors() destroys the canvases of existing actors before it
reinitializes the list and installs the replacements.
* The DraggableActor when the user is dragging an actor around their level
cleans up the blueprint masked drag/drop actor before nulling it out.
Misc changes:
* The player character cheats during Play Mode will immediately swap out the
player character on the current level.
* Properly call the Close() function instead of Hide() to dismiss popup
windows. The Close() function itself calls Hide() but also triggers
WindowClose event handlers. The Doodad Dropper subscribes to its close
event to free textures for all its doodad canvases.
* Clean up unused msgpack code for levels and doodads
* Fix the cosmetic bug where actors in your level would display wrongly
when scrolling off the top/left edges of the screen: they used to
anchor at their own 0,0 coordinate and crop their width/height leading
to a 'scrolling' effect that didn't happen on the right/bottom edges.
New feature: link a Start Flag to another doodad in your level
and you will play as that doodad instead of Boy. All Creatures
are designed to be playable. Playing as "other" doodads leads
to interesting effects, like not being able to activate buttons,
switches, or warp doors and not having an inventory to pick up
keys. The Anvil is fun: it can destroy other mobile doodads by
jumping on them.
If the actor does not specify that it has gravity, the gameplay
starts in antigravity mode. This will be the vast majority of
non-mobile doodads and the Bird.
Other changes:
* The Blue and Red Azulians now share a doodad script.
* The Azulians AI is still to walk back and forth, pickup keys and
press buttons. The Blue Azulian walks slower than the red one.
* The Blue Azulian is no longer hidden from the doodads list.
* Actor UUID values in levels are now V1 UUIDs (time-ordered).
This will help to reliably resolve conflicts in draw order
of overlapping doodads (newest added to level wins).
* Link Tool: clicking on a pair of already-linked doodads will
now unlink them, so you don't have to delete one to delete
the link.
* Actor Tool: deleting an actor immediately calls PruneLinks()
to clean up any links that the deleted doodad might have.
* pkg/loadscreen implements a global Loading Screen for loading heavy
levels for playing or editing.
* All chunks in a level are pre-rendered to bitmap before gameplay
begins, which reduces stutter as chunks were being lazily rendered on
first appearance before.
* The loading screen can be played with in the developer console:
$ loadscreen.Show()
$ loadscreen.Hide()
Along with ShowWithProgress(), SetProgress(float64) and IsActive()
* Chunker: separate the concerns between Bitmaps an (SDL2) Textures.
* Chunker.Prerender() converts a chunk to a bitmap (a Go image.Image)
and caches it, only re-rendering if marked as dirty.
* Chunker.Texture() will use the pre-cached bitmap if available to
immediately produce the SDL2 texture.
Other miscellaneous changes:
* Added to the Colored Pencil palette: Sandstone
* Added "perlin noise" brush pattern
Note: this commit introduces instability and crashes:
* New `asyncSetup()` functions run on a goroutine, but SDL2 texture
calls must run on the main thread.
* Chunker avoids this by caching bitmaps, not textures.
* Wallpaper though is unstable, sometimes works, sometimes has graphical
glitches, sometimes crashes the game.
* Wallpaper.Load() and the *Texture() functions are where it crashes.
Palette swatches gain a new property: Pattern.
Patterns are grayscale textures that the swatch color will sample
against when drawing pixels to the level, by taking the world coordinate
modulo a value inside the texture.
A few algorithms were tried (Screen, Overlay), this branch lands on one
that tries to cast the color from grayscale which comes out rather dark;
to get a patterned color to look black while still seeing the pattern,
the color needs to be as bright as #777 to get the effect.
* Start the program window maximized with the `-w maximized` CLI option.
* Move the Doodad Palette off the right-side dock of the Editor Scene and
into its own pop-up window: the DoodadDropper.
* Shrink the width of the Color Palette panel and show only the colors in
the buttons. The name of the swatch is available in the mouse-over tooltip.
* Added an "Edit" button to the Color Palette. It opens a Palette Editor
window where you can rename, change colors and attributes of existing colors
OR insert new colors into your palette. (Deleting colors not yet supported).
* level.Chunker gets a Redraw method: invalidates all cached textures of all
chunks forcing the level to redraw itself, possibly with an updated palette.
* New doodads: Switches.
* They come in four varieties: wall switch (background element, with
"ON/OFF" text) and three side-profile switches for the floor, left
or right walls.
* On collision with the player, they flip their state from "OFF" to
"ON" or vice versa. If the player walks away and then collides
again, the switch flips again.
* Can be used to open/close Electric Doors when turned on/off. Their
default state is "off"
* If a switch receives a power signal from another linked switch, it
sets its own state to match. So, two "on/off" switches that are
connected to a door AND to each other will both flip on/off when one
of them flips.
* Update the Level Collision logic to support Decoration, Fire and Water
pixel collisions.
* Previously, ALL pixels in the level were acting as though solid.
* Non-solid pixels don't count for collision detection, but their
attributes (fire and water) are collected and returned.
* Updated the MenuScene to support loading a map file in Play Mode
instead of Edit Mode. Updated the title screen menu to add a button
for playing levels instead of editing them.
* Wrote some documentation.
Since SDL2 is using in-memory bitmaps the same as Canvas engine, the
function names of the render.Engine interface have been cleaned up:
* NewTexture(filename, image) -> StoreTexture(name, image)
Create a new cached texture with a given name.
* NewBitmap(filename) -> LoadTexture(name)
Recall a stored texture with a given name.
* level.Chunk.ToBitmap uses simpler names for the textures instead of
userdir.CacheFilename file-like paths.
* Refactor texture caching in render.Engine:
* New interface method: NewTexture(filename string, image.Image)
* WASM immediately encodes the image to PNG and generates a JavaScript
`Image()` object to load it with a data URI and keep it in memory.
* SDL2 saves the bitmap to disk as it did before.
* WASM: deprecate the sessionStorage for holding image data. Session
storage methods panic if called. The image data is directly kept in
Go memory as a js.Value holding an Image().
* Shared Memory workaround: the level.Chunk.ToBitmap() function is where
chunk textures get cached, but it had no access to the render.Engine
used in the game. The `pkg/shmem` package holds global pointers to
common structures like the CurrentRenderEngine as a work-around.
* Also shmem.Flash() so Doodle can make its d.Flash() function
globally available, any sub-package can now flash text to the screen
regardless of source code location.
* JavaScript API for Doodads now has a global Flash() function
available.
* WASM: Handle window resize so Doodle can recompute its dimensions
instead of scaling/shrinking the view.
* Add RGBA color blending support in WASM build.
* Initial texture caching API for Canvas renderer engine. The WASM build
writes the chunk caches as a "data:image/png" base64 URL on the
browser's sessionStorage, for access to copy into the Canvas.
* Separated the ClickEvent from the MouseEvent (motion) in the WASM
event queue system, to allow clicking and dragging.
* Added the EscapeKey handler, which will abruptly terminate the WASM
application, same as it kills the window in the desktop build.
* Optimization fix: I discovered that if the user clicks and holds over
a single pixel when drawing a level, repeated Set() operations were
firing meaning multiple cache invalidations. Not noticeable on PC but
on WebAssembly it crippled the browser. Now if the cursor isn't moving
it doesn't do anything.
* Refactor the event system in the WASM render engine to serialize the
async JavaScript events into a channel, so that queued events are read
off serially in the main loop similar to SDL. This fixes keyboard
input issues, altho if you type really fast some input keys get lost.
* Add a Red Azulian as a test for mobile enemies.
* Its A.I. has it walk back and forth, changing directions when it
comes up against an obstacle for a few moments.
* It plays walking animations and can trigger collision events with
other Doodads, such as the Electric Door and Trapdoor.
* Move Gravity responsibility to the doodad scripts themselves.
* Call `Self.SetGravity(true)` to opt the Doodad in to gravity.
* The canvas.Loop() adds gravity to any doodad that has it enabled.
* Add some encoding/decoding functions for binary msgpack format for
levels and doodads. Currently it writes msgpack files that can be
decoded and printed by Python (mp2json.py) but it can't re-read from
the binary format. For now, levels will continue to write in JSON
format.
* Add filesystem abstraction functions to the balance/ package to search
multiple paths to find Levels and Doodads, to make way for
system-level doodads.