tl;dr
I made a quick Shiny app that generates randomised characters for your next roguelike game, made with parts designed by Kenney.
Standing on the shoulders of Kenney
Kenney makes free (CC0) assets for videogames, like the 16×16-pixel sprites I used in a previous post, where you could move a little character move around a procedural forest glade while being chased by a mouse.
Kenney also has a roguelike characters pack where you can create your own characters by mixing and matching bodies, clothes and weapons. I’ve had a stab at making a toy roguelike in R before, but the ‘art’ and interface were purely composed of text.
With a long-term goal of creating a ‘real’ roguelike game (maybe in R?), I made a small app to combine sprite parts randomly to create new characters. It’s a pretty simple implementation for now, but I’m recording its current state in case I never come back to it.
An ‘applike’
The app is made with Shiny, deployed for the browser with {shinylive} and served with GitHub Pages. The source for the app is on GitHub. I’ve embedded it below, but you can also visit it in a standalone window. It might take a moment to load.
Hit the reroll button to randomise the sprite parts. You can also download as PNG at 16×16 and 1024×1024 pixels or as an R-specific RDS file containing a nativeRaster representation of the sprite.
Going rogue
The basic approach was to:
- ‘Cut out’ and save each individual sprite part from a single ‘spritesheet’.
- Select sprite parts at random given some weighting (bodies are always selected, but weapons aren’t).
- Store the part names in a
reactiveValues()
object so they can be called by the draw method (to display the sprite onscreen) and via the download options.
- Use
image_mosaic()
from the {magick package} to print sprite parts on top of each other and store this as a nativeRaster object.
- Read the nativeRaster and use {grid} functions to draw it.
Why not just draw the image_mosaic()
object? Two reasons: I specifically want to download the nativeRaster version of the sprite (for later use in R) and because nativeRaster can then be used to produce an image at any size. The nativeRaster object is just a matrix of colour information, where each cell is a pixel of the image. That means you can re-plot it any size you want.
Of course, I could use {nara} by Mike (coolbutuseless) to ‘blit’ sprite parts to a single object, but I had some trouble using the package in a {shinylive} context as its not on CRAN and the GitHub repo doesn’t have any releases with the necessary WebAssembly binaries (yet?).
Expanding the inventory
As ever, there were some learning points, like how:
- I used
ignoreNULL = FALSE
in shiny::bindEvent()
on the step that chooses sprite parts, so that a sprite is created on startup without the need for the user to click the reroll button
- there’s a {shinylive} deployment GitHub Action that can save the step of having to
shinylive::export()
your app whenever you make changes
- the Chrome browser appears to have a problem downloading the files when the buttons are clicked, so there is a workaround at time of writing that’s used in one of the examples on the {shinylive} website
Even something this simple can be a learning experience.
Permadeath
Obviously this app is more of a proof-of-concept (as ever on this blog). It’s not particularly special or all that useful outside of my needs.
Next steps might involve letting the user select parts manually, styling and theming the app and giving the sprites a transparent background.
You can add a pull request to the GitHub repo if this interests you. But beware, adventurer, I can’t promise my code is less labyrinthine than a classic Rogue dungeon.
Environment
Session info
Last rendered: 2024-12-24 08:57:34 GMT
R version 4.4.2 (2024-10-31)
Platform: aarch64-apple-darwin20
Running under: macOS Ventura 13.2.1
Matrix products: default
BLAS: /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRblas.0.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRlapack.dylib; LAPACK version 3.12.0
locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
time zone: Europe/London
tzcode source: internal
attached base packages:
[1] stats graphics grDevices utils datasets methods base
loaded via a namespace (and not attached):
[1] htmlwidgets_1.6.4 compiler_4.4.2 fastmap_1.2.0 cli_3.6.3.9000
[5] tools_4.4.2 htmltools_0.5.8.1 rstudioapi_0.16.0 yaml_2.3.10
[9] rmarkdown_2.28 knitr_1.48 jsonlite_1.8.9 xfun_0.48
[13] digest_0.6.37 rlang_1.1.4 fontawesome_0.5.2 evaluate_1.0.1