A Video Game? In R Shiny?
For the last two years, RStudio has been organizing a competition to showcase the power and flexibility of Shiny as a framework for creating applications. Lately I’ve been devoting my career to making Shiny apps more beautiful, and this year I decided to take part in the contest. However, I wanted to do something a little bit different…
At that moment a question popped up in my mind: Would it be possible to build a game using R Shiny, even as a prototype? Could it actually be playable and enjoyable?
Shiny has been historically used for data-driven applications and as a rapid development tool, but games have been done in all sorts of languages and frameworks before. Not only that, but the ingredients were all there: A way to build UI, something to keep game state, and the ability to read input from players and update the UI accordingly.
There have been attempts to make simple Shiny games as early as 2016, but I wanted to try something more ambitious. The more I joked around with the idea the more it actually seemed doable. Let’s find out if I was right.
My initial idea was simple. I wanted it to be a slow-paced game that you could play on desktop or mobile, since I also wanted to explore building responsive UIs in Shiny. I went with a card swiping game where swiping in different directions would affect the game in different ways. These choices would then influence some metrics, and the player would juggle decisions to keep those metrics at optimal levels.
I decided it could be fun to put the player in the shoes of a fictional world leader whose decisions would influence the state of the world. Your job would be to balance the economy, environment, and public opinion of your performance as an all-powerful decision maker. If any of those got to zero, game over!
I also decided it would be interesting to have a separate value to track how evil you are. Something that wouldn’t lose you the game but provide small benefits or hurdles depending on your behavior. Thus the Karma meter was born.
I imagined it with a retro feeling, so i went with NES.css for some of that 8 bit nostalgia. I also really wanted to show a map based on the metrics, `leaflet` was an easy choice in this case given how popular and easy to use it is.
With most of the research and a solid proof of concept behind me, it was time for implementation! I wanted to have a very clear and easy to follow project structure, while at the same time making it simple for anyone to pick up and expand upon.
The project structure
For the server components, I decided to structure them in their own individual `R6` objects, inside `modules`. If you aren’t familiar with R6, it’s an Object-oriented programming approach for encapsulating methods and values into classes that can then be instantiated as separate objects, meaning each object will have its own methods and state. It’s a different approach from functional programming, but very commonly used in other languages. Be sure to check out Marcin Dubel’s helpful post about R6 classes.
With an idea of what I wanted to work with, it was time to actually build the thing. So I started going over a list of the required components:
- Data Manager: Responsible for loading and providing data. Since I wanted to be able to create a lot of possible card combinations, I created a small template engine and used a google sheet as my database. This would allow me to change / add / remove cards or options whenever I wanted, since the data manager would always read the most recent data when a new game started. If you’re interested how the data is saved, you can check out the spreadsheet here: https://docs.google.com/spreadsheets/d/1LwIPKAxbKvuGyMKktcTVuYZbTda0WMQxmNMhxqaQhGg/edit
- Deck Manager: This would be responsible for generating cards and showing them to players. It ended up being responsible for the UI of the cards and for generating new cards based on game state, current karma values, and a bit of randomness making heavy use of `sample` to pick the card template, values and intensity (card intensity influences how much the decision affects the different metrics).
- Metrics Manager: Mostly responsible for the visual part of the current game state. It uses the state manager values as reactive values to keep the UI up to date with the current state of the game.
- State Manager: The heart of the game, being composed mostly of reactive lists and support functions for checking game state changes. For example, checking for game overs.
- Game Manager: Instantiates and brings all the other managers together. It’s responsible for starting, resetting and in general all high level changes to the game state.
- Map Manager: Manages a map that provides some visual feedback of the current state of the world. It wasn’t initially planned and It’s not required for the game but I thought it would be a nice touch to have something more than just a few progress bars to show the game state.
After a few weekends I got to a point where I was happy with it, and decided to enter my submission. The contest results aren’t out yet, so keep your fingers crossed for me!
I was shocked by how quickly the development went. Reactive values are AMAZING as an engine for managing the state of the game, and R6 made it super easy to create and destroy instances when needed. Using a spreadsheet for the database was also a really interesting concept that turned out better than I expected. The final result lets me fix typos and add new card types very easily.
Even mobile worked as expected thanks to some custom css styling:
Final thoughts: Games in shiny, yay or nay?
At this point we know that it’s possible to build a functioning video game in R Shiny. So…should you use Shiny to build your own game? To be honest, probably not. There are dozens of other frameworks and languages that are definitely more suitable for this type of project.
However…to me this project shows how easy it is to implement complex UX and UI elements in R Shiny. Of course this is not a commercial ready game, but as a proof of concept or a rapid prototype that can be set up in a couple of days/weeks and then iterated upon, Shiny once again managed to surprise me in a highly positive way. This bodes well for Shiny’s continued future as a go-to solution for complex enterprise dashboards.
If you want to play around with the code or find more information about the project, you can find everything on the github repository: https://github.com/pedrocoutinhosilva/shiny.decisions
Or maybe you just want to play with the final result! Make sure to let me know how many weeks you managed to get to!
Play the game here: https://sparktuga.shinyapps.io/ShinyDecisions/
- If you need help with your Shiny dashboards: https://appsilon.com/
- Project repo: https://github.com/pedrocoutinhosilva/shiny.decisions
- Deployed demo: https://sparktuga.shinyapps.io/ShinyDecisions/
- Modules: https://shiny.rstudio.com/articles/modules.html
- R6 classes: https://adv-r.hadley.nz/r6.html
- SASS: https://github.com/rstudio/sass
- RStudio’s 2020 Shiny Contest: https://community.rstudio.com/t/shiny-contest-2020-is-here/