Ding! Sound effects in {r.oguelike}

gamedev
r
r.oguelike
sonify
videogames
Author
Published

January 4, 2023

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:

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

Hex sticker design for the 'r.oguelike' R package. Black background with bright green font, reminiscent of old computer terminal output. In the centre, a three-by-ten arrangement of hashmarks and periods, along with a single at symbol and dollar sign, which looks like a classic ASCII tile-based roguelike game. The text 'r.oguelike' is underneath.

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.

Environment

Session info
Last rendered: 2023-08-11 19:25:13 BST
R version 4.3.1 (2023-06-16)
Platform: aarch64-apple-darwin20 (64-bit)
Running under: macOS Ventura 13.2.1

Matrix products: default
BLAS:   /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRblas.0.dylib 
LAPACK: /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.11.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.2 compiler_4.3.1    fastmap_1.1.1     cli_3.6.1        
 [5] tools_4.3.1       htmltools_0.5.5   rstudioapi_0.15.0 yaml_2.3.7       
 [9] rmarkdown_2.23    knitr_1.43.1      jsonlite_1.8.7    xfun_0.39        
[13] digest_0.6.33     rlang_1.1.1       evaluate_0.21    

Footnotes

  1. Meanwhile, I’m looking forward to what Mike (coolbutuseless) is up to with audio for games in R.↩︎

  2. Find some actual real R games in this list I’ve put together.↩︎

Reuse

CC BY-NC-SA 4.0