Run, Doodleguy! Theme Creation Guide

Themes can be used to replace the game’s visuals and sounds.
Multiple can be applied at once to stack their effects, like combining one that replaces the player, one that replaces the background, and one that replaces the music.

Themes are controlled by a file called config.json. Using JSON Syntax, it organizes assets in an easy-to-read manner and allows you to change them just as easily.
You can open up config.json in any text editor you’d like, though I personally recommend Notepad++ since it recognizes JSON formatting which can help you avoid some headaches (it’s also just generally a good text editor).

Have any suggestions for how to make this guide better? Email me at mcherndon03🧠gmail.com!
(🧠 = @)

Before You Start…

Before creating a Theme, there are some very important things you should keep in mind.

  • JSON Formatting
    • Having a solid understanding of JSON Syntax will help greatly in creating Themes, though it isn’t strictly required.
    • If the config.json file isn’t formatted correctly, the game will show an error in the Themes menu.
    • If your JSON Syntax is invalid, you can use jsonlint to see where it went wrong. Simply copy and paste all of config.json’s contents into the textbox and click "Validate JSON".
  • Theme Stacking
    • Themes are made to be combined together, with the assets and values of higher priority Themes replacing the equivalent assets and values of the ones below them.
    • When working from the sample Theme, be sure to remove any values that your Theme doesn’t change. This will allow it to work well with other Themes.
    • Themes will always be applied on top of the game’s default Theme.
  • Data Types
    • Number / Integer
      • A number.
      • Numbers can have decimal places, but integers can’t.
      • Number ex. 643.8
      • Integer ex. 72
      • Also supports negative numbers.
      • As per JSON syntax, numbers with decimal places cannot start with a period.
    • String
      • A line of text, starting and ending with quotation marks.
      • ex. "water"
      • With the exception of metadata, strings are usually used to point to the Theme’s files.
      • To insert a line break in a string, you can use "\n".
    • Boolean
      • A simple True or False (written as either true or false)
    • Array
      • A collection of multiple values in an ordered list, separated by commas.
      • Most often used for representing colors.
      • ex. [43, 72, 12]

Getting Started

Download Sample Theme

  • Download the Sample Theme.
  • Extract the included folder into the Themes folder
    • You can find it by clicking "Open Themes Folder" in the game's Themes menu.
  • Mess around and see what you can make!
Now, here's the stuff you can replace!

Metadata

  • ["name"] (string)
    • The name of the Theme
  • ["bio"] (string)
    • A short description
  • ["author"] (string)
    • The name of the Theme’s author. Only displays in the offline Themes menu.
  • ["icon"] (string, file location)
    • A path to the Theme’s icon (image file).
    • Will be stretched to fit a 128x128 square pixel area.
    • If an Icon isn't provided (or the file the path points to doesn't exist) the game will use a default icon.

Visuals

Drawings

["drawings"]

Themes allow you to replace the Drawings used in the game’s menus.
Drawings only have one attribute, ["filepath"], which is a path that leads to a Drawing file (.drw2). Your custom Drawings can be found in the game’s save folder under /custom.

  • ["logo"]
    • The logo that appears on the title screen
    • Default: "drawings/logo.drw2"
  • ["btn_play"]
    • The title screen’s Play button
    • Default: "drawings/btn_play.drw2"
  • ["btn_custom"]
    • The title screen’s Custom button
    • Default: "drawings/btn_custom.drw2"
  • ["btn_network"]
    • The title screen’s Network button
    • Default: "drawings/btn_network.drw2"
  • ["game_over"]
    • The text that displays when you get a Game Over
    • Default: "drawings/gameover.drw2"
  • ["btn_retry"]
    • The Game Over screen’s Retry button
    • Default: "drawings/btn_retry.drw2"
  • ["btn_menu"]
    • The Game Over screen’s Menu button
    • Default: "drawings/btn_menu.drw2"
  • IMPORTANT: All of the above Drawings are subject to changes by the game's localizations. Changing them in a Theme may harm readability for some players.
    The below Drawings are not subject to localization changes.
  • ["icon_drawings"]
    • The icon for Drawings in the Network menu
    • Default: "drawings/icon_drawings.drw2"
  • ["icon_collections"]
    • The icon for Collections in the Network menu
    • Default: "drawings/icon_collections.drw2"
  • ["icon_themes"]
    • The icon for Themes in the Network menu
    • Default: "drawings/icon_themes.drw2"

Colors

["colors"]

The game’s colors are represented by arrays with 3 numbers, each corresponding to a color’s Red, Green, and Blue values.

For example, [213, 65, 37] would be this color.
Red=213, Green=65, Blue=37

As is standard, these values can range from 0 to 255.

  • ["menu"]
    • ["text"]
      • The color used for most of the game’s text, as well as the menus.
      • Default: [0,0,0]
    • ["text_deselected"]
      • The color used for greyed-out options, either currently selected or unavailable.
      • Default: [127,127,127]
    • ["drawing_not_included"]
      • The color used in the Custom menu for drawings that aren’t being included.
      • Default: [255,0,0]
    • ["drawing_hover_outline"]
      • The color used in the Custom menu outlining the Drawing the mouse is currently hovering over.
      • Default: [192,192,192]
    • ["textbox_text"]
      • The color for text displayed inside the game’s textboxes (usually name + description)
      • Default: [0,0,0]
    • ["textbox_background"]
      • The color for the background of the game’s textboxes (usually name + description)
      • Default: [255,255,255]
    • ["textbox_selection"]
      • The color for when text is being highlighted in the game’s textboxes.
      • Default: [0,0,255]
      • ["textbox_blank"]
        • The color used for text in textboxes when displaying their “no text” messages.
        • Default: [192,192,192]
      • ["button_background"]
        • The color of the Custom Menu’s buttons (New, Edit, Upload, etc.)
        • Default: [255,255,255]
      • ["drawing_text_outline"]
        • The color of the outline around the tooltip text that displays while drawing a custom Obstacle or Hat.
        • Default: [255,255,255]
  • ["transparent_overlay"]
    • The color used for separating foreground menu elements from background menu elements. Also used when pausing gameplay.
    • Default: [37,37,37]
  • ["player"]
    • The color the player sprites will be blended with.
    • Set to white (or [255,255,255]) to not blend at all.
    • Default: [0,0,0]
  • ["pencil"]
    • The color of the lines drawn by the player.
    • Default: [30,30,30]
  • ["pen"]
    • The ["pen"] value is a special case, as it contains an array of color values.
    • During gameplay, these values will be randomly picked from for each Obstacle that spawns.
    • Default: [ [255,0,0], [0,255,0], [0,0,255] ]

Sprites

["sprites"]

All of the visuals from here on out are sprites, or images that can have multiple frames of animation.
Each sprite has these values:

  • ["filepath"] (string, file location)
    • A path to an image file with all of the sprite’s frames in a horizontal strip
    • There should be no horizontal space between the frames
    • .png is the suggested image file type, but .jpg/.jpeg and .gif work too (but only the first frame of .gif files will import)
  • ["origin"] (array, 2 values)
    • Adjusts where onscreen the sprite is drawn
    • The first value is the X coordinate, and the second value is the Y coordinate
    • 0,0 corresponds to the top left of the sprite
    • In this guide, suggestions for where to put the origin will be made, but do not have to be followed
  • ["frames"] (integer)
    • How many frames the sprite has
    • Source image files should be one long horizontal strip without separation between frames
    • The sprite’s width will be inferred from this value (strip width divided by frame count)
  • ["_fps"] (number)
    • Frames per second the animation runs at
  • ["scale"] (number)
    • How large (or small) to display the sprite
    • 1 = no change, <1 = smaller, >1 = larger
    • The sprite’s size will be multiplied by this number

For a good idea of how the game imports sprites, see sprite_add in the GameMaker documentation.
For an easy way to convert gifs into sprite strips, you can use ezgif.
The game takes place in an 800x600 game-units space (though the width can vary depending on aspect ratio). At a sprite scale of 1, 1 game-unit equates to 1 pixel.

  • ["bg"]
    • The game’s background.
    • Ideally should loop vertically and horizontally, but doesn’t have to.
    • Keep in mind the space’s 800x600 size.
    • Default:
      • ["filepath"]: "sprites/spr_bg.png"
      • ["origin"]: [0,0]
      • ["frames"]: 1
      • ["_fps"]: 0
      • ["scale"]: 1
  • ["eraser"]
    • Sprites for the Big Eraser that chases you during gameplay.
    • Should be facing right, and is split into 2 parts.
    • Has 2 different sprites and 1 attribute:
      • ["tip"]
        • The tip of the eraser that points into the playing field
        • Origin X should be on the right side of the sprite (equal or close to the width)
        • Origin Y should be in the middle of the sprite (half of the height)
        • Default:
          • ["filepath"]: "sprites/spr_eraser.png"
          • ["origin"]: [609,433]
          • ["frames"]: 1
          • ["_fps"]: 0
          • ["scale"]: 0.25
      • ["bottom"]
        • Default:
          • ["filepath"]: "sprites/spr_eraser_bottom.png"
          • ["origin"]: [33,433]
          • ["frames"]: 1
          • ["_fps"]: 0
          • ["scale"]: 0.25
      • ["stretch"] (boolean)
        • Whether to have the ["bottom"] sprite repeat until offscreen (false) or stretch until offscreen (true)
        • Default: false
  • ["line"]
    • The sprite for the lines that make up Drawings
    • Origin X should be on the left side of the sprite (0)
    • Origin Y should be in the middle of the sprite (half of the height)
    • Default:
      • ["filepath"]: "sprites/line.png"
      • ["origin"]: [0,3]
      • ["frames"]: 4
      • ["_fps"]: 7
      • ["scale"]: 1
  • ["pencil"]
    • Sprites for the Pencil you use to draw.
    • Should be facing up, and is split into 4 parts.
    • Has 4 different sprites and 2 attributes.
      • ["tip"]
        • The tip of the pencil.
        • Origin should be at the tip, where the Pencil would meet the mouse cursor.
        • Default:
          • ["filepath"]: "sprites/spr_pencil_tip.png"
          • ["origin"]: [23,0]
          • ["frames"]: 1
          • ["_fps"]: 0
          • ["scale"]: 1
      • ["tip_bottom"]
        • The end of the Pencil that extends offscreen.
        • Origin should be around the top center, where this sprite would match up to the tip.
        • Default:
          • ["filepath"]: "sprites/spr_pencil_tip_bottom.png"
          • ["origin"]: [23,0]
          • ["frames"]: 1
          • ["_fps"]: 0
          • ["scale"]: 1
      • ["tip_stretch"] (boolean)
        • Whether to have the ["bottom"] sprite repeat until offscreen (false) or stretch until offscreen (true)
        • Default: true
      • ["eraser"]
        • The tip of the Pencil’s eraser side.
        • Origin should be at the tip, where the Pencil’s eraser would meet the mouse cursor.
        • Default:
          • ["filepath"]: "sprites/spr_pencil_eraser.png"
          • ["origin"]: [23,0]
          • ["frames"]: 1
          • ["_fps"]: 0
          • ["scale"]: 1
      • ["eraser_bottom"]
        • The end of the Pencil’s eraser side that extends offscreen.
        • Origin should be around the top center, where this sprite would match up to the eraser side.
        • Default:
          • ["filepath"]: "sprites/spr_pencil_eraser_bottom.png"
          • ["origin"]: [23,0]
          • ["frames"]: 1
          • ["_fps"]: 0
          • ["scale"]: 1
      • ["eraser_stretch"] (boolean)
        • Whether to have the ["bottom"] sprite repeat until offscreen (false) or stretch until offscreen (true)
        • Default: true
  • ["player"]
    • The sprites used for the player.
    • Should be facing right (the sprites get flipped when facing left).
    • The origin for all player sprites should be where its feet would touch the ground.
    • IMPORTANT:
      When importing player sprites, you’ll most likely want to change the player color value (located in "colors") to white (or [255,255,255]) as it is set to black by default.
      You'll also probably want to set the player sprites' ["scale"] values to 1.
    • Has 4 different sprites (each with added hat attributes) and 1 attribute:
      • ["idle"]
        • Standing still
          • ["filepath"]: "player/idle.png"
          • ["origin"]: [130,575]
          • ["frames"]: 5
          • ["_fps"]: 7
          • ["scale"]: 0.2
          • ["hat"]
            • ["x"]: 0
            • ["y"]: -90
      • ["run"]
        • Running on the ground
          • ["filepath"]: "player/run.png"
          • ["origin"]: [130,575]
          • ["frames"]: 5
          • ["_fps"]: 17
          • ["scale"]: 0.2
          • ["hat"]
            • ["x"]: 0
            • ["y"]: -90
      • ["jump"]
        • Off the ground and moving upwards.
          • ["filepath"]: "player/jump.png"
          • ["origin"]: [130,575]
          • ["frames"]: 3
          • ["_fps"]: 14
          • ["scale"]: 0.2
          • ["hat"]
            • ["x"]: 0
            • ["y"]: -94
      • ["fall"]
        • Off the ground and moving downwards.
          • ["filepath"]: "player/fall.png"
          • ["origin"]: [130,575]
          • ["frames"]: 3
          • ["_fps"]: 14
          • ["scale"]: 0.2
          • ["hat"]
            • ["x"]: 0
            • ["y"]: -94
      • (added attribute) ["hat"]
        • Where in comparison to the sprite’s origin the player’s currently worn Hat should be drawn.
          • ["x"]
              The horizontal offset from the origin (-X = Left, +X = Right)
          • ["y"]
              The vertical offset from the origin (-Y = Up, +Y = Down)
      • ["hat_scale"] (number)
        • A scale multiplier for the player’s currently worn Hat
        • Works the same as the ["scale"] value in sprites
  • ["upvote_off"]
    • The sprite for the lines that make up Drawings
    • Origin X should be on the left side of the sprite (0)
    • Origin Y should be in the middle of the sprite (half of the height)
    • Default:
      • ["filepath"]: "sprites/upvote_off.png"
      • ["origin"]: [100,100]
      • ["frames"]: 1
      • ["_fps"]: 1
      • ["scale"]: 1
  • ["downvote_off"]
    • The sprite for the lines that make up Drawings
    • Origin X should be on the left side of the sprite (0)
    • Origin Y should be in the middle of the sprite (half of the height)
    • Default:
      • ["filepath"]: "sprites/downvote_off.png"
      • ["origin"]: [100,100]
      • ["frames"]: 1
      • ["_fps"]: 0
      • ["scale"]: 1
  • ["upvote_on"]
    • The sprite for the lines that make up Drawings
    • Origin X should be on the left side of the sprite (0)
    • Origin Y should be in the middle of the sprite (half of the height)
    • Default:
      • ["filepath"]: "sprites/upvote_on.png"
      • ["origin"]: [100,100]
      • ["frames"]: 1
      • ["_fps"]: 0
      • ["scale"]: 1
  • ["downvote_on"]
    • The sprite for the lines that make up Drawings
    • Origin X should be on the left side of the sprite (0)
    • Origin Y should be in the middle of the sprite (half of the height)
    • Default:
      • ["filepath"]: "sprites/downvote_on.png"
      • ["origin"]: [100,100]
      • ["frames"]: 1
      • ["_fps"]: 0
      • ["scale"]: 1
  • ["page_prev"]
    • The sprite for the lines that make up Drawings
    • Origin X should be on the left side of the sprite (0)
    • Origin Y should be in the middle of the sprite (half of the height)
    • Default:
      • ["filepath"]: "sprites/page_prev.png"
      • ["origin"]: [32,32]
      • ["frames"]: 1
      • ["_fps"]: 0
      • ["scale"]: 1
  • ["page_next"]
    • The sprite for the lines that make up Drawings
    • Origin X should be on the left side of the sprite (0)
    • Origin Y should be in the middle of the sprite (half of the height)
    • Default:
      • ["filepath"]: "sprites/page_next.png"
      • ["origin"]: [32,32]
      • ["frames"]: 1
      • ["_fps"]: 0
      • ["scale"]: 1

Sounds

["sound"]

Alongside visuals, Themes also allow you to replace the game’s music and sound effects. Due to engine limitations, all sound files must be in the .OGG file format.

Sound Effects

["sfx"]

Sound effects are simple, non-looping (typically) clips that play to give the game more flavor.
  • ["filepath"] (string, file location)
The game’s sound effects:
  • ["click"]
    • The clicking sound that plays in menus
    • Default:
      • ["filepath"]: "sfx/click.ogg"
  • ["page_turn"]
    • The sound that plays when changing pages in certain menus
    • Default:
      • ["filepath"]: "sfx/changepage.ogg"
  • ["screen_change"]
    • The sound that plays between some menu transitions
    • Default:
      • ["filepath"]: "sfx/changescreen.ogg"

Music

["music"]

["music"] The game’s music is able to play a loop after an intro segment, and it all works out of a single file. Your audio file should be structured like this:
  • Intro segment
  • Loop segment
  • Small clip of the loop segment (~1.5 seconds)

If the song loops perfectly, you don't need the intro segment or small repeated loop segment.

See Pixelated Pope’s music looping tutorial for a better idea of how to format your audio file (the game uses his solution for looping music).

Songs are formatted like this:
  • ["filepath"] (string, file location)
  • ["intro"] (number)
    • How long (in seconds) the song’s intro section is.
    • This can be 0 if the song loops perfectly.
  • ["loop"] (number)
    • How long (in seconds) the song’s looping section is.
    • Set this to -1 to have it play only once (like for the Game Over theme)
The game's music:
  • ["menu"]
    • Plays while not in gameplay.
    • Default:
      • ["filepath"]: "music/menu.ogg"
      • ["intro"]: 0
      • ["loop"]: 60
  • ["game"]
    • Plays during gameplay.
    • Default:
      • ["filepath"]: "music/game.ogg"
      • ["intro"]: 32
      • ["loop"]: 96
  • ["gameover"]
    • Plays at the end of gameplay.
    • Default:
      • ["filepath"]: "music/gameover.ogg"
      • ["intro"]: 0
      • ["loop"]: -1