new wr — r.oguelike any% tenkeyless noglitch
tl;dr
The {r.oguelike} package—a toy roguelike microadventure for the R console—now has little sound effects thanks to {sonify}. Pew pew!
The adventure continues?
Apparently this is part 5 of the {r.oguelike} devlog. You can read earlier posts about:
- its inception
- creating simple procedural dungeons
- making an enemy chase the player
- 3D dungeons and continuous keypress inputs
Alas, this is also probably the last installment.
Yes, the dungeons have been dank (cool, edgy), but also dank (cool, damp, claustrophobic). Time to unspelunk myself.
There may be time for a {r.oguelike2} in future. I’d like to try a class-based approach to help limit code spaghetti and make it more extensible. Perhaps it will even have a proper game loop! Call me when you’re ready, Kojima.
Until then, one more little feature to tie things up. Beeeeeeep. BOOOOOOOP.
Hi-Sonifi
So, yes: {r.oguelike} now has sound effects with quality as high as its graphics and gameplay. See all these in concert in the video embedded at the top of this page.
I used the {sonify} package to create a few little beeps and toots that I think fit the game’s retro aesthetic.1 These are fired when the player moves and interacts with things in the dungeon.
I’ve written about {sonify} before when I sonified data about COVID-19 infections and GitHub activity (incredible juxtaposition), which can offer a more interesting and accessible way of presenting data.
You can also demean {sonify} by making funny little honks and parps, which is what I’ve done for {r.oguelike}.
How did I arrive at the soundscape for {r.oguelike}? I did the bare minimum of fiddling around with the arguments in sonify::sonify()
until the noises amused me.
Demo cassette
Sounds are played in the code of the package via functions after each triggering event. The user can prevent these sounds from playing with the logical has_sfx
argument in the start_game()
function.
For example, here’s the function for the simplest sound effect:
.sfx_move <- function(has_sfx) {
if (has_sfx) sonify::sonify(1, 1, duration = 0.001)
}
The sonify()
outputs are {tuneR} objects. I’ve saved these as wav files with tuneR::writeWav()
so they can be embedded in this post.
Click for illustrative code to create the wav files.
library(sonify)
library(tuneR)
library(purrr)
sfx <- list(
move = sonify(1, 1, duration = 0.001),
bump = sonify(1, 1, duration = 0.01, flim = c(100, 110)),
gold = bind(
sonify(1, 1, duration = 0.05, flim = c(800, 800)),
sonify(1, 1, duration = 0.05, flim = c(1000, 1000))
),
apple = sonify(0:1, c(0, 1), duration = 0.05),
eat = sonify(0:1, c(1, 0), duration = 0.05),
win = bind(
sonify(0:1, rep(1, 2), duration = 0.1, flim = c(600, 600)),
sonify(0:1, rep(1, 2), duration = 0.1, flim = c(600, 600)),
sonify(0:1, rep(1, 2), duration = 0.1, flim = c(800, 800))
),
lose = bind(
sonify(0:1, rep(1, 2), duration = 0.1, flim = c(600, 600)),
sonify(0:1, rep(1, 2), duration = 0.1, flim = c(600, 600)),
sonify(0:1, rep(1, 2), duration = 0.1, flim = c(400, 400))
)
)
walk2(
.x = sfx,
.y = names(sfx),
~writeWave(.x, paste0(.y, ".wav"))
)
In reality, the sounds play a little slower in the game itself, but it was a bit fiddly to reproduce it for these clips. You’ll get the idea.
Move
Step onto unoccupied floor tile (.
) and you’ll hear the very quick tap of your boot.
Click to play the sound:
And here’s the corresponding code to reproduce it:
sonify(1, 1, duration = 0.001)
But bump into the dungeon wall (#
) and you’ll get a dull thud, you absolute clod.
sonify(1, 1, duration = 0.01, flim = c(100, 110))
Yes, flim
, as in: ‘this post is absolute flimflam’.
Food
Would you pick up an apple (a
) you found on the floor of a cave? Here’s what it might sound like as it pops into your inventory.
sonify(0:1, c(0, 1), duration = 0.05),
More importantly, would you eat an apple (a
) you found on the floor of a cave? Here’s how it would sound as it rolls down your gullet.
sonify(0:1, c(1, 0), duration = 0.05)
Gold
Collecting gold ($
) grants you a celebratory chirp of excitement. Although there’s not actually anything in the dungeon to spend it on, sorry.
sonify(1, 1, duration = 0.05, flim = c(800, 800))
sonify(1, 1, duration = 0.05, flim = c(1000, 1000))
Defeat enemy
A powerful victory ditty after you crush your enemies (E
).
sonify(0:1, rep(1, 2), duration = 0.1, flim = c(600, 600))
sonify(0:1, rep(1, 2), duration = 0.1, flim = c(600, 600))
sonify(0:1, rep(1, 2), duration = 0.1, flim = c(800, 800))
Lose
Conversely, a sad lament for being crushed by your enemies (E
) or running out of turns (T
).
sonify(0:1, rep(1, 2), duration = 0.1, flim = c(600, 600))
sonify(0:1, rep(1, 2), duration = 0.1, flim = c(600, 600))
sonify(0:1, rep(1, 2), duration = 0.1, flim = c(400, 400))
Echo echo echo
If you want to try out {r.oguelike}, you can install it from GitHub:
install.github("remotes") # if not yet installed
remotes::install_github("matt-dray/r.oguelike") # v0.1 currently
r.oguelike::start_game()
You can also run {r.oguelike} in an RStudio instance in your browser (!), thanks to the Binder project.
Free feel to highlight any bugs via the issues, or create a pull request that adds all the things that stop me from calling {r.oguelike} a proper ‘game’.2
Most importantly, don’t forget to wishlist me on Steam and remember that pre-order bonuses will include an apple that’s been left on a dungeon floor for a few months.
Session info
## ─ Session info ───────────────────────────────────────────────────────────────
## setting value
## version R version 4.2.0 (2022-04-22)
## os macOS Big Sur/Monterey 10.16
## system x86_64, darwin17.0
## ui X11
## language (EN)
## collate en_US.UTF-8
## ctype en_US.UTF-8
## tz Europe/London
## date 2023-01-04
## pandoc 2.19.2 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/ (via rmarkdown)
##
## ─ Packages ───────────────────────────────────────────────────────────────────
## package * version date (UTC) lib source
## blogdown 1.9 2022-03-28 [1] CRAN (R 4.2.0)
## bookdown 0.26 2022-04-15 [1] CRAN (R 4.2.0)
## bslib 0.3.1 2021-10-06 [1] CRAN (R 4.2.0)
## cli 3.3.0 2022-04-25 [1] CRAN (R 4.2.0)
## digest 0.6.29 2021-12-01 [1] CRAN (R 4.2.0)
## evaluate 0.15 2022-02-18 [1] CRAN (R 4.2.0)
## fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.2.0)
## htmltools 0.5.2 2021-08-25 [1] CRAN (R 4.2.0)
## jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.2.0)
## jsonlite 1.8.0 2022-02-22 [1] CRAN (R 4.2.0)
## knitr 1.39 2022-04-26 [1] CRAN (R 4.2.0)
## magrittr 2.0.3 2022-03-30 [1] CRAN (R 4.2.0)
## R6 2.5.1 2021-08-19 [1] CRAN (R 4.2.0)
## rlang 1.0.2 2022-03-04 [1] CRAN (R 4.2.0)
## rmarkdown 2.14 2022-04-25 [1] CRAN (R 4.2.0)
## rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.0)
## sass 0.4.1 2022-03-23 [1] CRAN (R 4.2.0)
## sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0)
## stringi 1.7.6 2021-11-29 [1] CRAN (R 4.2.0)
## stringr 1.4.0 2019-02-10 [1] CRAN (R 4.2.0)
## xfun 0.30 2022-03-02 [1] CRAN (R 4.2.0)
## yaml 2.3.5 2022-02-21 [1] CRAN (R 4.2.0)
##
## [1] /Library/Frameworks/R.framework/Versions/4.2/Resources/library
##
## ──────────────────────────────────────────────────────────────────────────────
Meanwhile, I’m looking forward to what Mike (coolbutuseless) is up to with audio for games in R.↩︎
Find some actual real R games in this list I’ve put together.↩︎