Converting From .FLV to .MP4 and Other Formats using FFmpeg on Windows

After a hair-pulling couple of hours trying to figure out how to conveniently convert .flv files into a more generally-accepted video format, I decided to write this brief guide to help anyone who may be experiencing a similar problem.

By default, OpenBroadcastSoftware (OBS) encodes recorded video in .flv format. This is unfortunate to anyone wanting to edit their latest footage in Windows Movie Maker or other common video editors, as the .flv file type does not fare well with these programs. Finding reliable, free video converting software online is a shot in the dark, and can oftentimes infect your computer with bloatware if not careful. There is no need to purchase any unnecessary software, just follow the steps below and you can convert .flv, among other files, quite fast for free through the command line.

This tutorial is intended for Windows 10, but will work on older versions of the operating system at the expense of the screenshots not matching perfectly. Similarly, different software may yield slightly inconsistent labeling, buttons, and locations of elements found within the screenshots. In this tutorial we will need to extract a .zip file, so necessary software is required. I recommend either 7zip or winRAR as they are lightweight with free trial versions. I am using winRAR, personally. There is a good chance you already have this extraction software on your PC.

Let's get started!

Download FFmpeg from Zeranoe.

Download FFmpeg from Zeranoe.

Step 1 - Download FFmpeg

The software we will be using is called FFmpeg and it can be found by clicking here. Click the big, blue Download FFmpeg button to start downloading the latest version, which at the time of this writing is 20172014-8fa18e0. Either the 32-bit or 64-bit architecture should be automatically selected based off what computer you are using, but if you encounter issues later in this tutorial it is a possibility you downloaded the incompatible architecture. If you want to determine whether your system is 32-bit or 64-bit, reference Steps 7 through 11 and then come back.


Open the .zip when the download has finished.

Open the .zip when the download has finished.

Step 2 - Open the Download

Once the file has completed its download, open it. Where you find this completed download is dependent on which browser is being using and what preferences you have set. For Google Chrome, my browser of choice, completed downloads appear at the bottom of the screen. You can also find a list of all downloads by pressing Ctrl + J on the keyboard.


Extract the .zip contents.

Extract the .zip contents.

Step 3 - Extracting the Files

Next, we need to extract the contents of the .zip. To do this, locate an Extract To button either in the upper navigation of the window that appears or by right-clicking the ffmpeg... folder and selecting the option from the popup list.


Change the extraction destination path to the root directory.

Change the extraction destination path to the root directory.

Step 4 - Selecting the Extraction Destination Path

We want to change the file extraction destination path to the root directory of the PC. Type C:\ in the Destination path field. Click OK.


Navigate to the root directory via Run.

Navigate to the root directory via Run.

Step 5 - Navigating to the Root Directory

After the extraction has completed, press the Windows Key + R and a Run dialogue box should appear. Type C:\ into the Open field and click OK.


Locate the extracted contents in the root directory.

Locate the extracted contents in the root directory.

Step 6 - Inside the Root Directory

You will now be in the root directory of your computer. Locate the folder named, in part, ffmpeg. If you cannot find it, ensure that you are in the root directory (C:\) and that you indeed extracted the contents of the .zip to it.


Rename the folder to "ffmpeg".

Rename the folder to "ffmpeg".

Step 7 - Rename the Folder

For simplicity's sake, we are going to rename the lengthy folder name to something that's easier to type. Right-click the folder and select Rename. Type in ffmpeg and press enter.


Open the Control Panel via Run.

Open the Control Panel via Run.

Step 8 - Navigating to the Control Panel

Similar to Step 5, we are going to press Windows Key + R to bring up the Run window. This time, however, we will type in control panel and then click OK.


Click on System and Security.

Click on System and Security.

Step 9 - System and Security

Click on System and Security in the Control Panel.


Click on System.

Click on System.

Step 10 - System

Locate System in the list of options and click it.


Click on Advanced System Settings on the left side-panel.

Click on Advanced System Settings on the left side-panel.

Step 11 - Advanced System Settings

You may notice some system information on the right of the window. Here, you can find out whether your system is 32-bit or 64-bit.

On the left side-panel, you will see Advanced system settings beside a shield. Click it.


Click on Environment Variables...

Click on Environment Variables...

Step 12 - System Properties

Towards the bottom of the new window that appear, click the button labeled Environment Variables...


Select Path in upper pane, User Variables, and then click Edit...

Select Path in upper pane, User Variables, and then click Edit...

Step 13 - Environment Variables

In the upper pane, User variables, you will see Path. Click that line and then Edit... beneath. If you cannot find Path, click New... 


Modify the variable value with the bin directory of ffmpeg.

Modify the variable value with the bin directory of ffmpeg.

Step 14 - Editing the User Variables

If you could not locate Path in the previous step, simply type Path into the top field, Variable name. In the second field, Variable value, type ;C:\ffmpeg\bin. Then, click OK. You can click the OK button on System Properties window as well.


Open Command Prompt via Run.

Open Command Prompt via Run.

Step 15 - Opening the Command Prompt

Similar to Steps 5 and 8, you will be opening Run again by pressing the Windows Key + R. Type cmd and click OK. Command prompt will open.


Change the directory to where your video is located.

Change the directory to where your video is located.

Step 16 - Command Prompt and Changing Directories

We want the command prompt to be able to locate the video we want to convert. Type cd ("change directory") and then the directory where your video is located, presumably Videos.

cd C:\Users\USER\Videos would bring us to the video folder, assuming USER is replaced with your user name, which is found to the left of the > on the line you are typing on right now.


Convert your .flv video to .mp4 using this command.

Convert your .flv video to .mp4 using this command.

Step 17 - Converting the Video

This is the last step, converting the video! You will want to type (or copy-paste) the code below, replacing input.flv with the name of the video you want to convert (input), and output.mp4 with what you want the converted video file to be named.

ffmpeg -i input.flv -c:v libx264 -crf 23 -strict experimental output.mp4

Press enter and wait for the video to convert. The new video will appear in the same directory as the input video.

To be more specific with what exactly what the command does, let's break down it down. ffmpeg allows us to conveniently reference the path to the program we set up in Step 14. Next, we have the input file, input.flv, followed by referencing the codec (data encoder and decoder), -c:v libx264-crf determines the level of output video quality, where the smaller the number the better the quality. 23 is the default quality level, and the common range is between 18 and high 20s. Finally, before the output name, we include -strict experimental which, in short, allows for us to use a sandboxed encoder to get this working.

FFmpeg is powerful and can be used to convert to file formats other than mp4, though the command will differ outside of simply changing the .mp4 extension, unfortunately. Luckily, it has rich documentation on all its functionalities.


Finishing Up and Troubleshooting

Hopefully you were able to successfully convert the video. If not, it may be a good idea to go back ensure you followed all the steps; it is easy to overlook something small! If you need additional help, you can check out the FFmpeg forums.

Thanks for reading this tutorial. If you have any questions, comments, or critiques, feel free to leave them below as always.

Depth-first Search (DFS) Maze Generation in GameMaker: Studio

The final result will generate the entire maze almost immediately, but to get a better sense of how DFS works (and for fun), this is the maze being carved out step-by-step.

The final result will generate the entire maze almost immediately, but to get a better sense of how DFS works (and for fun), this is the maze being carved out step-by-step.

In today's tutorial, I will be showing you how to make a random maze generator using a depth-first search (DFS) algorithm in GameMaker: Studio, which can be adapted with minor changes to work with GM:S2. This is a fairly simple concept to grasp, and can serve as a solid introduction into several data systems, like grids and stacks.

I feel it's important to preface the tutorial by stating that this particular maze generation technique is nothing new nor special, and that I'm essentially walking you through a GM:S port of Daniel Shiffman's JavaScript tutorial of the same topic. Shiffman is a wonderful personality and his Coding Train series on YouTube is engaging content for anyone who wants to learn about JavaScript (p5.js) and Java (Processing) through fun, digestible projects. Further, if something in the code below does not make sense to you even with my - albeit brief - comments, be sure to check out Shiffman's video as he explains in-depth what the code he's writing does. However, remember that this tutorial is in GML and Shiffman's is in JavaScript, so using his code verbatim will not compile correctly in GM:S.

As always, the source code can be found at the end of this tutorial.


What is Depth-First Search?

This particular depth-first search prioritizes exploring to the left, gradually making its way to the right. How you choose to traverse is up to you.

This particular depth-first search prioritizes exploring to the left, gradually making its way to the right. How you choose to traverse is up to you.

Before we jump into the coding, I should shine some light on DFS, the backbone of this generator. DFS is an algorithm largely used when writing a program that will need to traverse through a tree data structure, reaching each and every node in all connecting branches. When a dead-end is reached - defined by no adjacent nodes that have not yet been visited - the algorithm traverses backwards until it is able to penetrate new territory.

Our two-dimensional maze may not appear to be a tree with branches upon first glance, but take a closer look at the generation animation at the beginning of this tutorial. Each square is considered a cell. Dark gray cells are unvisited, meaning that they have not been carved out by the green, active cell yet. Blue squares are the opposite - they are officially a hallway in the maze. When the active cell is adjacent to an unvisited cell, it will go to it. In the event there are multiple unvisited, adjacent cells, the active cell will randomly select one to visit. In the event there are no unvisited neighbors, the active cell will begin backtracking through its route until it meets a new, unvisited neighbor. As a result of this backwards movement, the active cell will make its way back to its starting position when its job generating the maze is completed.

Neighbors (blue) are defined as adjacent cells to the right, left, top, and bottom of an active cell (green), not corner cells.

Neighbors (blue) are defined as adjacent cells to the right, left, top, and bottom of an active cell (green), not corner cells.

There are multiple ways to structure a DFS, some more computationally efficient than others. For the sake of simplicity in an inherently "easy" task of generating a random maze, this tutorial will present DFS in its most raw form, with (sometimes) excessive backtracking. Assuming you are not generating enormous mazes, lag spikes and slowdowns should not be a problem with this code.


Overview of The Project

The expanded project resource tree.

The expanded project resource tree.

Unless you want foresight into how this project will be structured and what resources we will need to create, you can skip over this section of the tutorial.

Our maze generator will consist of only two objects, oMaze and oCell. The former is the control object which oversees the entire maze, used to execute commands on each of the cells, oCell. oMaze will utilize the Create Event and Step Event. oCell will utilize the Create Event and Draw Event. We are not using any sprites in this tutorial, and anything rendered is being drawn using built-in GM:S functions, most notably draw_line() and draw_rectangle().

We will be writing three custom scripts, index()checkNeighbors(), and removeWalls(). I will explain what these scripts do when we get to them. There will also be one room.

Remember that you can name your resources however you would like (e.g. obj_maze instead of oMaze), though be sure you modify their references in the code to suit your preferences.

Let's get started!


Preparing the Maze

We should begin development in the Create Event of object oMaze. Here, we are initializing variables and generating cells which will soon become the corridors of the maze. Using basic algebra, we are able to figure out how many cells we are able to fit inside our defined area, the current room size (320, 240) in this example. Further, we define the starting location of the maze generator at (0, 0). Be sure to create the room and place object oMaze in it.

/// CREATE EVENT of object oMaze

random_set_seed(randomize()); // set a random seed to produce unique mazes every time

width = room_width; // px horizontal span of the maze (width of room) (320)
height = room_height; // px vertical span of the maze (height of room) (240)

w = 10; // px width of each cell (applied to height also to produce square cells)

// calculate the number of cells in rows and columns of the maze (total cells = columns * rows)
cols = floor(width / w); // columns
rows = floor(height / w); // rows

// cycle through all columns and rows to fill the board will cells
for (var i = 0; i < cols; i++) {
    for (var j = 0; j < rows; j++) {
        /*
        Create a cell at (i, j) and multiply the coordinates by
        the cell size to properly position them in the room.
        If your maze should not start at (0, 0), add an offset to the
        x and y spawning position (be sure width and height vars take into
        account this repositioning).
        e.g. obj = instance_create(100 + (i * w), 100 + (j * w), oCell);
        */
        obj = instance_create(i * w, j * w, oCell);

        // pass variables from the generator into the cell
        obj.i = i; // pass horizontal index
        obj.j = j; // pass vertical index
        obj.w = w; // pass width of cell
        obj.cols = cols; // pass number of columns
        obj.rows = rows; // pass number of rows rows
        grid[i, j] = obj; // set index of 2d array to the cell
    }
}
/*
We've just filled a 2d array, spanning the entire room, with cells
(room_width/w) * (room_height/w) = 32 * 24 = 768 cell objects
*/

// cell from which the maze begins generating. [0, 0] is top-left
current = grid[0, 0];

// create a stack to previously visited cells, used for DFS backtracking
stack = ds_list_create();

Creating the Cells

If you run the code now without creating oCell, you will receive an error indicating that no such object exists. To fix this, let's go ahead and create the oCell object. The Create Event is much less lengthy than oMaze's. We are merely defining the four walls the cell possess and stating that it has not yet been visited - it is not a cooridore of the maze yet.

/// CREATE EVENT of object oCell

var w, cols, rows, i, j; // define vars. Values are passed in through object oMaze when created

/*
Each cell has four walls associated with it
    wall[0] = top wall, wall[1] = right side wall, wall[2] = bottom wall, wall[3] = left side wall
All four walls drawn together will produce an outline of a square
If wall[i] = false, the wall will not be drawn. By default, all four should be
*/
for (var i = 0; i < 4; i++) {
    walls[i] = true;
}

// whether or not the cell has been visited yet and is a part of the maze
visited = false;

Drawing the Cell

You can run the project now and not get an error, but all you will see is an empty room. We already have a lot of code written but not much to show for it. I like seeing progress, and I am sure you do as well, so next let's draw the cells to at least have something! The next block of code will go in the Draw Event of object oCell. For this tutorial, I have each cell start out dark gray, become blue once visited, and green when it is the current, active cell. This code draws the colored cell and its four walls.

/// DRAW EVENT of object oCell

draw_set_color(make_color_rgb(42, 42, 42)); // set color to dark gray/black

if (visited) { // if the cell has been visited by the generator
    draw_set_color(make_color_rgb(0, 0, 255)); // set color to blue
    if (id == oMaze.current) { // set color to green if the cell is currently active
        draw_set_color(make_color_rgb(0, 255, 0));
    }
}
// draw rectangle over span of cell, colored based off the scenerios reached above
draw_rectangle(x, y, x + w, y + w, false);

// set the color to white to draw the walls
draw_set_color(c_white);

if (walls[0]) { // draw top wall
    draw_line(x, y, x + w, y);
}

if (walls[1]) { // draw right wall
    draw_line(x + w, y, x + w, y + w);
}

if (walls[2]) { // draw bottom wall
    draw_line(x + w, y + w, x, y + w);
}

if (walls[3]) { // draw left wall
    draw_line(x, y + w, x, y);
}

A grid of empty cells. Soon, they'll be a part of a maze!

A grid of empty cells. Soon, they'll be a part of a maze!

Generating the Maze

Now when running the game, you should be presented with a static, grid-like pattern. Sweet! It's not much, but it shows that we are able to properly position and size our cells across the room.

The next thing we should do is transition over to the Step Event of object oMaze where will finally start writing the maze generation using DFS.

The upcoming chunk of code is intimidating to look at, especially now that we're introducing the custom scripts, but it's much more comprehensive once we break it down. You'll notice the code is wrapped around a do... until() loop. This means that all the code inside of the do will be executed until a condition is met. In our case, the condition is that there are no unvisited cells on the board. This means that the entire maze will be generated in one game step - wow! If your game is running at 30 steps per second (room_speed), don't always expect the maze to generate in 1/30 of a second, especially if the maze you want to generate is large. The bigger the maze, the longer the hang-time. If you want to see the maze generate cell by cell, simply remove the do and until-related lines of code.

We mark the active cell as visited and set our destination cell, a neighboring cell, to undefined. The generator has not yet made up its mind on what adjacent cell it wants to move to next. The (unwritten) checkNeighbors() script will return us an unvisited, neighboring cell assuming one exists. If not, our destination cell will remain undefined.

A visual representation of a stack data structure

A visual representation of a stack data structure

Each time the generator moves the active cell to an unvisited neighbor, we add its position to a stack. A stack is a data structure that is comparable to a deck of playing cards. Each time you want to pull something from a stack, you take the top card off. The first elements (cards) in the stack are the last ones to leave. The last elements (cards) in the stack are on the top and the first to leave. This is commonly referred to LIFO: last-in, first-out. If there are no unvisited neighbors, we begin backtracking through the maze until we encounter one, going through the stack, removing the elements until it is empty - until there are no cards left. We can simulate a stack function by storing elements in a ds_list and removing the latest element from it. In a ds_list, each new element is appended to the end of the list, meaning that if we wanted the newest item in the list, we would have to pull from the end of it. We can use ds_list_size() to get the number of elements in a list. Using this, we know the desired index of the element we want to pull. Once pulled, we simply remove it from the list.

EDIT: Due to an oversight, I forgot GM:S comes with a built-in stack data structure, ds_stack, which can be used in lieu of our ds_list workaround.
Neighboring walls are removed

Neighboring walls are removed

If the currently active cell can move to an unvisited neighboring cell, we mark the neighboring cell as visited in anticipation of the move, add our position to the stack, and remove the walls between the two neighboring cells. Finally, we update the currently active cell to the neighbor. The neighbor now has control and will go through the entire process we just covered.

/// STEP EVENT of object oMaze

// the statement within the brackets will be executed until the stack is empty (until)
do {
    current.visited = true; // the current, active cell has been visited

    // the destination cell, where we head next, has yet to be determined
    next = undefined;

    // check the current cell's neighbors to determine if we can visit one
    with(current) {
        other.next = checkNeighbors();
    }

    // if the cell can move adjacently; if we have an unvisited neighbor
    if (next != undefined) {
        next.visited = true; // mark our neighbor as visited in preparation of the move
        ds_list_add(stack, current); // add the current, active cell to the stack
        removeWalls(current, next); // remove the walls between us and our neighbor
        current = next; // update active cell to the neighboring cell
    } else if (!ds_list_empty(stack)) { // if we have no unvisited neighbors and the stack isn't empty
        // pop a cell off the top of the stack and visit it
        current = ds_list_find_value(stack, ds_list_size(stack) - 1);
        ds_list_delete(stack, ds_list_size(stack) - 1); // remove the cell from the stack
    }
}
until(ds_list_empty(stack)); // the stack is empty; all cells have been visited

// reset the room by pressing the "R" key
if (keyboard_check_pressed(ord("R"))) {
    room_restart();
}

Checking for Neighbors

Most of the logic is in the game now! If you run the program, you'll get an error because we haven't written the custom scripts checkNeighbors(), removeWalls(), and index() yet. Let's do just that right now.

The first script we'll be writing is checkNeighbors(). This script will return a random, unvisited neighboring cell if one should exist. If not, it will return undefined. To randomize which neighbor the active cell should visit next, all neighbors are placed into a list which is shuffled. We reference another script, index(), which we'll look at afterwards. This script simplifies both the math and edge cases when referencing adjacent cells.

Clock-wise (above, right, below, left), we check for neighbors and add them to the list if unvisited.

///checkNeighbors();

/*
    This script is called by a cell to get reference to its
    neighboring cells and whether or not they have been visited.
    If possible, it will return a random unvisited, neighboring cell.
*/

var neighbors, n; // declare temp vars
neighbors = ds_list_create(); // create a list to hold the cell's neighbors
n[0] = index(i, j - 1); // get the neighbor above us
n[1] = index(i + 1, j); // get the neighbor to the right of us
n[2] = index(i, j + 1); // get the neighbor below us
n[3] = index(i - 1, j); // get the neighbor to the left of us

/* if there is a neighbor, n, and it has not been visited, add
it to the list of avaliable neighbors. Repeat for right, left, above, below*/
for (var c = 0; c < array_length_1d(n); c++) {
    if (n[c] != undefined) {
        if (!n[c].visited) {
            ds_list_add(neighbors, n[c]);
        }
    }
}

// check if we added any neighbors to our list (it's not empty)
if (!ds_list_empty(neighbors)) {
    ds_list_shuffle(neighbors); // shuffle the list of neighbors
    var n = ds_list_find_value(neighbors, 0); // grab the top neighbor
    ds_list_destroy(neighbors); // destroy the list of neighbors
    return n; // return the randomly-selected neighbor
} else {
    ds_list_destroy(neighbors); // destroy the list of neighbors
    return undefined; // return undefined, we have no neighbors
}

Convenient Cell Referencing

Script index() accepts two arguments, an x-coordinate and a y-coordinate. It will return the cell at those coordinates if one should exist. Otherwise, it will return undefined. This script serves as more of an aid to cut down on repeating logic statements in checkNeighbors(). The edges of the maze do not have adjacent cells in all directions and we would get an "index out of bounds" error if we tried to reference one without the boundary checks.

///index(i,j);

/*
    This script allows us to pass in coordinates and access
    the appropriate cell at the given index, if it exists
*/

var i, j; // declare temp vars
i = argument0;
j = argument1;

// return undefined if the index is out of our array
if (i < 0 || j < 0 || i > cols - 1 || j > rows - 1) {
    return undefined;
}

return oMaze.grid[i, j]; // otherwise, return the necessary cell

Bringing it All Together: Removing Walls

Finally, the last code we need to write - the code which actually makes the maze look like a maze! Here, we remove the walls between visited, neighboring cells. Script removeWalls() is simple, though repetitive. There are ways to condense it, but this is passable. This script accepts two arguments, both of which are cells. First, we determine what direction the maze was just carved - what adjacent cell was visited. Then, we remove the walls. There are two walls to remove, one from each cell. If we translated to the right, we would remove cell A's right wall and cell B's left wall. The walls overlap, so both must be removed for it to visually make a difference.

///removeWalls(a, b);

/*
    This script is used to remove walls between the currently
    active cell and the neighboring cell about to be visited
*/

var a, b, _x, _y; // declare temp vars

a = argument0;
b = argument1;

/* subtract cell A's horizontal position from B's to determine whether
the active cell visited the left neighbor (-1), right neighbor (1),
or is parallel (0) */
_x = a.i - b.i;

if (_x == 1) { // if the previously-actived cell visited a neighbor to the right
    a.walls[3] = false; // remove left wall from cell A
    b.walls[1] = false; // remove right wall from cell B
} else if (_x == -1) { // if the previously-actived cell visited a neighbor to the left
    a.walls[1] = false; // remove right wall from cell A
    b.walls[3] = false; // remove left wall from cell B
}

/* subtract cell A's vertical position from B's to determine whether
the active cell visited the above neighbor (-1), below neighbor (1),
or is parallel (0) */
_y = a.j - b.j;

if (_y == 1) { // if the previously-actived cell visited a neighbor above
    a.walls[0] = false; // remove top wall from cell A
    b.walls[2] = false; // remove bottom wall from cell B
} else if (_y == -1) { // if the previously-actived cell visited a neighbor below
    a.walls[2] = false; // remove bottom wall from cell A
    b.walls[0] = false; // remove top wall from cell B
}

Closing

Maze generation with different dimensions

Maze generation with different dimensions

Whew! We're done! Run the program and you should get a random maze on the fly. Press R to generate a new one. If something's not working or you just want to get your hands on the source, click here.

Thanks for reading this tutorial! Again, I would like to give a huge shout-out to the wonderful Daniel Shiffman for the original code I've ported over to GM:S. If you have any comments, questions or critiques, feel free to leave them below.

If you use any of this code in projects of your own, I ask for no credit whatsoever. However, all animations in this post were created by me and I would appreciate attribution if used anywhere.

Lumberjerk Sneak Peek!

A mean lumberjack, the Lumberjerk

Earlier today I uploaded a video to my YouTube channel showcasing the menu of my gritty, upcoming indie game, Lumberjerk. I love making hideous-looking, trashy experimental games. This artistic mean allows me to be expressive through excessiveness and create something which would never make it as a triple-A title in today's gaming industry.

Lumberjerk is still in its prototyping stages, but I've been making swift progress amidst school and work. I won't reveal the gameplay mechanics until I'm satisfied showing them off, but what I have at the moment is different from any other game I've made before. It's exciting to branch (hah, tree puns) out and try something new, especially when I haven't been too off-kilter with my releases over the past several years.

I'm already thinking about the soundtrack in this game as it's something which is crucial to nail in a project like this one. I've been shopping around for artists who I feel can create sounds as beautifully ugly as the visuals.

It would be a lot of fun (and make for interesting content) if I were to document development of Lumberjerk here on my blog as it's something I've never really done before with a game. So, stay tuned for more! Also, I've been semi-active on YouTube lately, so subscribe to me on there if you wish to see random, mostly game dev-related, videos.

 

Super Mario Bros. Growing Vines in GameMaker: Studio

Since creating this blog - which has unfortunately seen more active days - I wanted to make tutorials exclusively for it. Providing Game Maker: Studio resources to developers is not a new ambition of mine as it's something I have been doing for nearly two years now on the YoYo Games Marketplace and Itchio. However, this tutorial-in-a-blogpost style is new to me, but I feel has potential as I'm not limiting myself to just code comments and can include images, .gifs, videos, and builds.

I decided to make this first tutorial a simple one suited towards beginners, more or less to test the waters of this platform. Today, I'll be showing you how to make growing vines as seen in the original Super Mario Bros. Below is a .gif of what we'll be making. Note that all graphics used in this tutorial are property of Nintendo and are used for educational purposes only.

tapping question mark blocks to make vines appear

We will be using three objects (vine, brick, question-mark block), each with corresponding graphics that are 16x16 pixels. You can download the graphics, and the background image, here. Before we jump into the code, let me explain exactly how we'll approach spawning and growing vines. 

When a user clicks on a question-mark block, we'll spawn a vine object behind it at its coordinates. The vine object moves upwards, and when it has moved 16px vertically (its sprite height), it spawns a new vine object below it (again, inside the block). When a vine comes in contact with a brick, it will stop moving as will any vines below.

First, let's import all the graphics.

The question-mark block sprite (which I've named sBlock out of preference) has two frames: the second of which will be shown after it has spawned a vine. Similarly, the vine (sVine) has two frames. The first indicates the top of the vine and the second is used for the remaining segments. Lastly, the brick (sBrick) gets only one frame.

Now, let's get coding!

Create a new object for the block sprite. I'll go ahead and name it oBlock for consistency. Because the sprite has two frames and we don't want it to be animated, we'll go ahead and set image_speed to 0 in the Create Event.

image_speed = 0;

In the Step Event, we'll want to create a vine when clicked, but only if the object hasn't spawned one already.

if (mouse_check_button_pressed(mb_left) && collision_point(mouse_x, mouse_y, id, false, false) && image_index == 0) {
    image_index = 1;
    instance_create(x, y, oVine);
}

The function mouse_check_button_pressed(...) returns whether or not its parameter mb_left (left mouse button) is pressed. We want to pair this with the collision_point(...) function which to determine if the mouse is over the instance (id). The last two parameters of this function determine whether or not we want to use precision-checking (not necessary as the block graphic is a square) and whether or not the instance calling this function should be excluded from the check (it shouldn't -- we want to click on the object itself). Conversely, one could use the function point_in_rectangle(...) to determine if the mouse is within the bounding box of the block. A mouse click and collision check together essentially replicate a Mouse Left Pressed Event. Finally, we add the check "image_index == 0" to only spawn the vine if the sprite's first frame is being rendered. If the criteria is met, the object draws the sprite's second frame (which now invalidates the spawn check) and creates a vine object, oVine, at its coordinates.

That's it for the block object

Now, let's make a brick object, oBrick, which will stop a vine if one collides into it. No code is necessary, but if you expand upon this tutorial to implement it into a platformer of yours, you may want to make it solid so the player can stand on it.

Last but not least, let's program the vine object. We'll need to set some variables in the Create Event.

image_speed = 0;
depth = 1;          // renders the vine object behind the block from which it spawns
can_move = true;    // whether or not the vine is allowed to move vertically
spawned = false;    // whether or not the vine has spawned another vine below it

The Step Event is a little more complex, but let's go through it step-by-step.

var above = instance_place(x, y - sprite_height, oVine);

Using the instance_place(...) function, we are able to reference a particular object based on their position as it returns the object id, if it exists. We set temporary variable above to this. If there is a vine object above us, as indicated by the "y - sprite_height", we are able to call it using above.

if (above != noone) {
    if (above.can_move == false) {
        can_move = false;
    }
}

Before we reference above we need to make sure we actually can, using a check to determine if it does not equal no one (or, if it equals someone). For instance, the top-most vine does not have any more vines above it, meaning that above will be undefined for that particular instance. If the vine object directly above us is not able to move as per can_move, stop us from moving as well.

if (place_meeting(x, y, oBrick) || y <= -sprite_height || (place_meeting(x, y, oBlock) && spawned)) { 
    can_move = false;
}

There are three circumstances in which a vine will be unable to move vertically: if it touches a brick (using place_meeting(...)), if it goes out of the room and its y value is 0 minus its sprite height or less, or if it collides into a question-mark block only after it has moved out of the one it was created in (and has thus spawned a new vine).

if (can_move) {
    y -= 2;
    if (y % sprite_height == 0 && !spawned) {
        spawned = true;
        var inst = instance_create(x, y + sprite_height, oVine);
        inst.image_index = 1;
    }
}

If can_move is set to true, the vine object is able to move vertically by 2px. Ensure that the sprite_height can be evenly divisible by this amount. Next, we check if the vine object has moved vertically its sprite_height (i.e. the object is 16px less than its vertical spawn position). This code will only work if objects are placed on a grid the size of the sprite. This line can be replaced with:

if (y <= ystart - sprite_height && !spawned) {

where ystart is a built-in GM variable which is set to the y-coordinate where the instance is created. Similar to the check in oBlock to determine if the image's index was 0, we want to check if we spawned a vine. If not, create a vine and set spawned to true. You'll also notice that we set variable inst to the function instance_create(...). Like spawninst is now equal to an object id. We can now set this instance's frame to 1.

invisible blocks showcase how exactly the vines spawn

By making the question-mark blocks invisible, it's much easier to comprehend what's going on and when exactly objects spawn.

Aaand that's it! You can download the source here. Thanks for reading my first blog post tutorial and let me know how I can improve. Hope you learned something!

LinkedIn and Resume Updates

This year is off to a fantastic start! I've been quite busy with college and miscellaneous freelance projects, alongside a startup company, Data Widow, with some colleagues of mine -- more on that later. Suffice it to say, I've had a whole bunch of new material to put on my resume and LinkedIn profile. Connect with me on there!

In other news, I have several higher-quality Game Maker: Studio assets in the works, including a full-fledged "Words With Friends" clone. In case you missed it, I've released several game engines since the Banack Bundle, which is going off sale soon (hurry!). You can check them out here.

11th Time's a Charm

This is the eleventh website design I've gone through since 2009 (back when I used the alias GroundZeroGamez), and the first one that I can say I'm truly happy with. It looks professional (enough) and has a blogging platform. While I can't make any promises, I hope to keep the blog active with news, rants, some programming insight, and everything else in-between.

For reference, the very first archive of my website can be found here. Gosh, I was one of those indie developers who referred to himself as "we" and "company".

What do you think of the new website design?