library(tidyverse)
library(raster)
library(gganimate)
Sprites
A sprite is a 2d bitmap often used by games to represent objects. A number of sprites displayed in sequence creates an animation.
Puzzle bobble sprite extraction
- Grab a sprite sheet for your favourite game from spriters resource
- I will be using a Bubble Bobble dinosaur
- Work out how to cut out sprites by subsetting the sheet of sprites
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Not going to directly link in to script to avoid hitting server
# https://www.spriters-resource.com/resources/sheets/12/12593.png
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sprite_sheet <- png::readPNG("12593.png")
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Set up the extraction parameters
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Nframes <- 16 # number of frames to extract
width <- 28 # width of a frame
sprite_frames <- list() # storage for the extracted frames
# Not equal sized frames in the sprite sheet. Need to compensate for each frame
offset <- c(3, 4, 6, 7, 7, 6, 5, 5, 6, 6, 7, 7, 7, 6, 7, 8)
# Manually extract each frame
for (i in seq(Nframes)) {
sprite_frames[[i]] <- sprite_sheet[13:40, (width*(i-1)) + (1:width) + offset[i], 1:3]
}
# Test plot
plot(as.raster(sprite_frames[[15]]), interpolate=FALSE)
Convert sprites to data.frames
- Convert the sprite to a data.frame of values for
geom_tile()
- Remove the background colour
- Calculate a lookup table of colours to use with
scale_fill_manual
- Stack all the individual frames together
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Function to convert a sprite frame to a data.frame
# and remove any background pixels i.e. #82ABE7
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sprite_frame_to_df <- function(frame) {
plot_df <- data_frame(
fill = as.vector(as.raster(frame)),
x = rep(1:width, width),
y = rep(width:1, each=width)
) %>%
filter(fill != '#82ABE7')
}
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Convert all sprite frames to sprite data.frames
# and set the 'idx' column to the frame number
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sprite_dfs <- sprite_frames %>%
map(sprite_frame_to_df) %>%
imap(~mutate(.x, idx=.y))
Warning: `data_frame()` is deprecated, use `tibble()`.
This warning is displayed once per session.
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Work out the spirte colours for scale_fill_manual
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
fill_manual_values <- unique(sprite_dfs[[1]]$fill)
fill_manual_values <- setNames(fill_manual_values, fill_manual_values)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Stack all the sprites into a mega data.frame
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
mega_df <- dplyr::bind_rows(sprite_dfs)
ggplot - facet-in-space
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Create the basic ggplot object
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
p <- ggplot(mega_df, aes(x, y, fill=fill)) +
geom_tile(width=0.9, height=0.9) +
coord_equal(xlim=c(1, width), ylim=c(1, width)) +
scale_fill_manual(values = fill_manual_values) +
theme_bw() +
theme(legend.position = 'none')
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Facet by time
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
p +
facet_wrap(~idx)
gganimate - facet-in-time
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Facet by time with gganimate
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
panim <- p +
transition_manual(idx, seq_along(sprite_frames)) +
labs(title = "gganimate bubble bobble")
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Create and save animation
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gganimate::animate(panim, fps=8, width=400, height=400)
nframes and fps adjusted to match transition
# gganimate::anim_save("bubble_bobble.gif")