Sponsored By

Visualizing StarCraft Replays with R

Using the R programming language to create interesting summarizations of expert StarCraft gameplay.

Ben Weber, Blogger

July 9, 2018

6 Min Read
Game Developer logo in a gray background | Game Developer

It's been awhile since I blogged for Gamasutra, but I'm excited to get back into the games industry! I recently joined the central data organization at Zynga, and wanted to share some of the tools that I've been using for data science. I've also written about this new experience on Medium. These visualizations are based on old research, but the release of the DeepMind API provides renewed interest in StarCraft as a research environment. 

Almost a decade since I started data mining professional replays for StarCraft: Brood War, I'm still exploring the data sets. After recently trying out new animation packages in R, I wanted to revisit this data set and see if I could make an interesting visualization using some of these replays with the gganimate and tweenr packages. The result is the video below:
 


This blog post walks through the R script I authored to create this video. All of the data and code used to generate this visualization is available on Github. I generated the data set by viewing replays in StarCraft and using BWAPI to output a snapshot of the game state every 5 seconds. It’s also possible to use the data sets available here. The result of this process is that I ended up with CSV files in the following format:

frame,pID,unitID,x,y
0,0,12,3792,864
0,0,19,3744,858
0,0,30,3704,864
0,0,34,3808,834
0,0,64,3768,864


The file specifies the game frame, or tick count, the ID of the player controlling the unit, the in-game identifier for tracking the unit, and the position of the unit. While it would be useful to also include unit types and status variables such as health, this data provides a starting point for visualizing the movement of armies. The data set includes both buildings and mobile units, but excludes neutral units and structures.

# libraries
library(ggplot2)
library(gganimate)
library(ggforce)
library(tweenr)
library(dplyr)


As mentioned, I’m using the gganimate and tweenr libraries to create the visualization. gganimate is useful for creating animated plots in R and tweenr provides methods for interpolating between different snapstops in the animation. ggplot2 is used to create the actual plot, ggforce is used for styling the plot, and dplyr is used for setting up the data.

# video settings
fps <- 24
nframes <- 5000


I defined two parameters for video playback. The first, fps, is the framerate for the output video, and the second, nframes, is the total length of the video. The length of the video will be nframes/fps seconds.

# load the replay data
df <- read.csv("PvT.csv") 

# lag the x and y coordinates to plot lines of movement
df <-  df %>%  group_by(unitID) %>% 
    mutate(x1 = dplyr::lag(x, n = 1, default = NA),        
           x2 = dplyr::lag(x, n = 2, default = NA),
           y1 = dplyr::lag(y, n = 1, default = NA),
           y2 = dplyr::lag(y, n = 2, default = NA))


The next step is to load and transform the data. The loaded dataframe includes the x and y position of each unit at a specific game time, but I also wanted to lag these values in order to draw lines between coordinates. I used dplyr and mutate to lag the x and y positions by 5 and 10 seconds.

# set up graphics
df$colour <- ifelse(df$pID == 0, 'RED', 'GREEN')
df$time <- df$frame / fps
df$ease = 'linear'
df$size = 5
df$alpha = 1 

# Use tweenr
dt <- tween_elements(df, 'time', 'unitID', 'ease', nframes=nframes)


I then set additional parameters for the plot including the color and size of the units, and the type of interpolation to use for the animation. The last step uses tweenr to build a dataframe that interpolates between the different states in the input dataframe.

# animate with gganimate
p <- ggplot(data = dt) + 
    geom_point(aes(x=0, y=0, size=10, alpha=0, frame = .frame)) +  
    geom_segment(aes(x=x1, y=y1, xend=x2, yend=y2, size=1,
                 alpha=0.5, frame = .frame, color = colour)) +  
    geom_segment(aes(x=x, y=y, xend=x1, yend=y1, size=1, alpha=1,
                 frame = .frame, color = colour)) +  
    geom_point(aes(x=x, y=y, size=size, alpha=alpha, frame = .frame,
                 color = colour)) +  
    scale_size(guide = 'none') +  
    scale_colour_identity() +  
    scale_alpha(range = c(0, 1), guide = 'none') +    
    ggforce::theme_no_axes()


The next step is to create a plot of the data, which includes lines and dots. The first geom_point is used to scale the size of the dots and the second geom_point is used to draw the units. The two geom_segment calls are used to draw lines between current and past locations of the units. The older trails are colored a lighter color (alpha = 0.5) than the more recent trails. The remainder of the code block modifies the style of the plot.

# output the video
animation::ani.options(interval = 1/fps)
gganimate(p, "output.mp4", ani.width=640, ani.height=640
    ,title_frame = FALSE)


The final step is to create a video of the animated plot. I set the video size to 640 by 640 pixels and disabled the frame counter at the top of the video. It’s also possible to output GIFs and web pages. The result of this script is shown in the sample video below. If may take quite a bit of time to render.


The video shows troop movements for a Protoss (red) vs Terran (green) game. The start of the video shows the scouting phase and initial skirmishes. Later on, you can see where the Terran player lays spider mines throughout the map. The match ends after the Protoss player takes out an expansion and gains control of the middle of the map.


Ben Weber is a principal data scientist at Zynga. We are hiring!

Read more about:

Featured Blogs

About the Author

Daily news, dev blogs, and stories from Game Developer straight to your inbox

You May Also Like