Draw Container and GMS Tasks

Part of creating a game requires developing tools and other functions that don’t necessarily effect the player, but help in the process of making the game. These tools can be range from art assets, such as this sprite generator from https://github.com/makrohn/Universal-LPC-spritesheet, or these utility functions from https://github.com/gmlscripts/scripts.  Either way, in creating Violet, we have borrowed so much from the help of others, that we decided to contribute some of our own works.  After all, we would not be as far as we are today without the help of the community.  So, we hope that this post, and these “few lines of code” will help make someone else’s project go from dream to reality.

Draw Container


Draw Container is a layout text engine we developed for Violet with Game Maker Studio 2.  Instead of managing ever-changing positions and sizes of the draw_text routines, we manage containers and their layout type.  Draw Container uses concepts similar to CSS Layout and Relative Layout.

Perhaps we need to draw some text to the screen.  Or perhaps we need to draw an advanced GUI.  Draw Container has been a huge help with the development in Violet.  We introduced a simple version of it on the Day 32 post.  However, that version quickly became obsolete as we need to render sprites and nested text.  With Draw Container, we can draw extensive GUI’s, character dialog, HUD elements and much more!

Let’s look at an example:

Draw Container Example
Draw Container Example

We can see that draw_container renders a root container called datadata has a size of 360x260, with a background color of light gray and is rendered in the middle of the screen.  It has a child container called middleChild, with a background color of modern black, a margin of 20px and a padding of 20pxmiddleChild has three children called cols with a grid layout of column.  This means that col's children will render by being “stacked” on each other.  The first child of cols is called row1row1 tells its children to render their sizes equally by using a property called flow and setting it to split.  Each child in row1 has a margin of 10px and a padding of 10px.  The other two children of cols, row2 and row3 and their cihldren are very similar to row1. and it’s children.

Here’s the code that generated the above picture:

var data = ds_map_create();
data[? "xx"] = obj_Global.GUI_WIDTH * .5;
data[? "yy"] = obj_Global.GUI_HEIGHT * .5;
data[? "width"] = 360;
data[? "height"] = 260;
data[? "fillColor"] = c_ltgray;
data[? "fillAlpha"] = .5;
data[? "radius"] = 20;
data[? "grid"] = "column";

var middleChildList = ds_list_create();
ds_map_add_list(data, "children", middleChildList);

  var middleChild = ds_map_create();
  middleChild[? "margin"] = 20;
  middleChild[? "padding"] = 20;
  middleChild[? "fillColor"] = c_modern_black;
  middleChild[? "fillAlpha"] = .5;
  middleChild[? "flow"] = "split";
  middleChild[? "grid"] = "column";
  ds_list_add_map(middleChildList, middleChild);
  var cols = ds_list_create();
  ds_map_add_list(middleChild, "children", cols);
    var row1 = ds_map_create();
    row1[? "grid"] = "row";
    row1[? "flow"] = "split";
    row1[? "fillColor"] = c_green;
    row1[? "fillAlpha"] = .5;
    ds_list_add_map(cols, row1);
      var children1 = ds_list_create();
      ds_map_add_list(row1, "children", children1);
      var topLeft = ds_map_create();
      topLeft[? "margin"] = 10;
      topLeft[? "padding"] = 10;
      topLeft[? "fillColor"] = c_orange;
      topLeft[? "fillAlpha"] = .5;
      topLeft[? "hAlign"] = fa_left;
      topLeft[? "vAlign"] = fa_top;
      topLeft[? "str"] = "Top Left";
      ds_list_add_map(children1, topLeft);
      var topMiddle = ds_map_create();
      topMiddle[? "margin"] = 10;
      topMiddle[? "padding"] = 10;
      topMiddle[? "fillColor"] = c_orange;
      topMiddle[? "fillAlpha"] = .5;
      topMiddle[? "hAlign"] = fa_center;
      topMiddle[? "vAlign"] = fa_top;
      topMiddle[? "str"] = "Top Middle";
      ds_list_add_map(children1, topMiddle);
      var topRight = ds_map_create();
      topRight[? "margin"] = 10;
      topRight[? "padding"] = 10;
      topRight[? "fillColor"] = c_orange;
      topRight[? "fillAlpha"] = .5;
      topRight[? "hAlign"] = fa_right;
      topRight[? "vAlign"] = fa_top;
      topRight[? "str"] = "Top Right";
      ds_list_add_map(children1, topRight);
    var row2 = ds_map_create();
    row2[? "grid"] = "row";
    row2[? "flow"] = "split";
    row2[? "fillColor"] = c_blue;
    row2[? "fillAlpha"] = .5;
    ds_list_add_map(cols, row2);
      var children2 = ds_list_create();
      ds_map_add_list(row2, "children", children2);
      var middleLeft = ds_map_create();
      middleLeft[? "margin"] = 10;
      middleLeft[? "padding"] = 10;
      middleLeft[? "fillColor"] = c_purple;
      middleLeft[? "fillAlpha"] = .5;
      middleLeft[? "hAlign"] = fa_left;
      middleLeft[? "vAlign"] = fa_middle;
      middleLeft[? "str"] = "Middle Left";
      ds_list_add_map(children2, middleLeft);
      var middleMiddle = ds_map_create();
      middleMiddle[? "margin"] = 10;
      middleMiddle[? "padding"] = 10;
      middleMiddle[? "fillColor"] = c_purple;
      middleMiddle[? "fillAlpha"] = .5;
      middleMiddle[? "hAlign"] = fa_center;
      middleMiddle[? "vAlign"] = fa_middle;
      middleMiddle[? "str"] = "Middle Middle";
      ds_list_add_map(children2, middleMiddle);
      var middleRight = ds_map_create();
      middleRight[? "margin"] = 10;
      middleRight[? "padding"] = 10;
      middleRight[? "fillColor"] = c_purple;
      middleRight[? "fillAlpha"] = .5;
      middleRight[? "hAlign"] = fa_right;
      middleRight[? "vAlign"] = fa_middle;
      middleRight[? "str"] = "Middle Right";
      ds_list_add_map(children2, middleRight);
    var row3 = ds_map_create();
    row3[? "grid"] = "row";
    row3[? "flow"] = "split";
    row3[? "fillColor"] = c_red;
    row3[? "fillAlpha"] = .5;
    ds_list_add_map(cols, row3);
      var children3 = ds_list_create();
      ds_map_add_list(row3, "children", children3);
      var bottomLeft = ds_map_create();
      bottomLeft[? "margin"] = 10;
      bottomLeft[? "padding"] = 10;
      bottomLeft[? "fillColor"] = c_olive;
      bottomLeft[? "fillAlpha"] = .5;
      bottomLeft[? "hAlign"] = fa_left;
      bottomLeft[? "vAlign"] = fa_bottom;
      bottomLeft[? "str"] = "Bottom Left";
      ds_list_add_map(children3, bottomLeft);
      var bottomMiddle = ds_map_create();
      bottomMiddle[? "margin"] = 10;
      bottomMiddle[? "padding"] = 10;
      bottomMiddle[? "fillColor"] = c_olive;
      bottomMiddle[? "fillAlpha"] = .5;
      bottomMiddle[? "hAlign"] = fa_center;
      bottomMiddle[? "vAlign"] = fa_bottom;
      bottomMiddle[? "str"] = "Bottom Middle";
      ds_list_add_map(children3, bottomMiddle);
      var bottomRight = ds_map_create();
      bottomRight[? "margin"] = 10;
      bottomRight[? "padding"] = 10;
      bottomRight[? "fillColor"] = c_olive;
      bottomRight[? "fillAlpha"] = .5;
      bottomRight[? "hAlign"] = fa_right;
      bottomRight[? "vAlign"] = fa_bottom;
      bottomRight[? "str"] = "Bottom Right";
      ds_list_add_map(children3, bottomRight);


There are plenty of other examples provided in the repo link above, as well as a more in-depth look on all the available properties and how they behave with one another.  Hopefully creating text-based UI will become a much more manageable with draw_container in Game Maker Studio 2.

GMS Tasks


GMS Tasks adds some command line tasks for repetitive actions for Game Maker Studio 2.  Current tasks include merging instances in several rooms into one room, stitching a world map from images, exporting all sprites from your project to sprite strips, making Game Maker sprites (yy files) from strip files.

Some of these tasks, such as merging instances from several rooms into one room might be pretty niche to Violet with how we are developing our open world.  However, there might be other tasks in here that would be of benefit.

GMS Tasks currently has six tasks available:


The purpose of the build command is to take instances in several rooms and copy / paste them into the “main” room.

Let’s say we are building an open world game in Game Maker Studio 2.  We’ve found that after a while, using the room editor with thousands of instances makes the room editor slow and unusable. Our solution was to create “copies” of each room, in their full size, and only work on a section for each room. Then, when it comes time to “test” the game in the “main” room, we can run our script to merge these instances into the one room.


The purpose of the clean command is “clean” the main room from the build command — essentially reverting the main room to its former state.

Generate Map

The purpose of the generate-map command is generate (stitch) a map image from a open world game in Game Maker Studio 2.  In Violet, we are currently export all the things we want on our map to an image file, and then render that image file for the “map”.  This script will take images generated from Game Maker Studio 2, and stitch them together into a map.png file.

Export GM Sprites As Strips

The purpose of the export-gm-sprites-as-strips command is to take all sprites in a Game Maker Studio 2 project and export them as strips.  This can be very useful if we need to export are sprites as strip png files for an artist.

Make GM Sprites From Strips

The purpose of the make-gm-sprites-from-strips command is to take (very many) strip images and import them into Game Maker very quickly.  For example, let’s say we generated many characters from https://github.com/makrohn/Universal-LPC-spritesheet To import each individual row into Game Maker for many strips is very time consuming.  This task will speed up that process.


The purpose of the snap command is take all instances from specified rooms snap them to a grid. Sometimes when copying / pasting large volumes of instances in the room editor, the grouping does not conform to the instance grid it was copied into. This command will run and ensure all instances are placed correctly on a grid.


Leave a Reply