New Game Engine

While Motionmelody has been fun to make, the one thing that I regret every single day is the fact that I have to use this shitty, slow, bug-prone garbage collected game engine. You may have heard of it, it’s called Unity.

In the future, after Motionmelody is released and we officially start production of our next game, I will be writing an article about everything I hate about Unity and why we’re using and maintaining our own game engine for the foreseeable future.

I’ve started writing the new engine a couple months ago, and it’s going well so far. It currently has hot-reloading of data assets, it can render text, basic 3d models, what isn’t there to like? It makes me wonder why we didn’t do this in the first place…

Of course, we aren’t planning on writing our games in C++, so we need to have a proper “scripting language” to script our games in. I chose Beef, the only acceptable language for this.

A big part of this process is calling C++ from Beef, and calling Beef from C++. So while that’s easy enough to do manually for a few functions, as our engine gets bigger and bigger, it’s not feasible with the amount of stuff we’ll eventually want to bind.

APIGen

I wrote a tool called APIGen. It allows you to define API functions, which then creates a transition layer between C++ and Beef. It looks like this:

API_FUNCTION(APITable_Drawing_Clear)
void Clear(const Color &color);

API_FUNCTION(APITable_Drawing_DrawLine)
void DrawLine(float x1, float y1, float x2, float y2, const Color &color);

APIGen will read these files and generate .json files, which, inside of them is just a list of functions and information about them.

{
    "functions": [
        {
            "name": "Clear",
            "returnType": "void",
            "tableIndex": 5,
            "params": [
            {
                "type": "Color",
                "name": "color",
                "const": true,
                "pointer": false,
                "reference": true
            }
            ],
            "pointer": false,
            "reference": false,
            "private": false
        },
        {
            "name": "DrawLine",
            "returnType": "void",
            "tableIndex": 6,
            "params": [
                {
                    "type": "float",
                    "name": "x1",
                    "const": false,
                    "pointer": false,
                    "reference": false
                },
                {
                    "type": "float",
                    "name": "y1",
                    "const": false,
                    "pointer": false,
                    "reference": false
                },
                {
                    "type": "float",
                    "name": "x2",
                    "const": false,
                    "pointer": false,
                    "reference": false
                },
                {
                    "type": "float",
                    "name": "y2",
                    "const": false,
                    "pointer": false,
                    "reference": false
                },
                {
                    "type": "Color",
                    "name": "color",
                    "const": true,
                    "pointer": false,
                    "reference": true
                }
            ],
            "pointer": false,
            "reference": false,
            "private": false
        }
    ]
}

The next part after this is generating bindings for Beef using these .json files. This is technically a different tool but for simplicity’s sake, I’m just gonna call it “APIGen Stage 2”.

I wrote another C# script that reads these .json files and generates the appropriate code files for us to use in our scripting library, which we then use in our game!

/// ===========================================================
/// Auto-generated by the Trinket API generator. Do not modify!
/// ===========================================================
using System;

using internal Trinket.EngineAPI;

namespace Trinket;

public static class Drawing
{
	[Inline]
	public static void Clear(Color color)
	{
		function void(in Color color) func = (.)EngineAPI.callTable[5];
		func(color);
	}
	
	[Inline]
	public static void DrawLine(float x1, float y1, float x2, float y2, Color color)
	{
		function void(float x1, float y1, float x2, float y2, in Color color) func = (.)EngineAPI.callTable[6];
		func(x1, y1, x2, y2, color);
	}
}

This system might change a bit as we develop the engine more but I’m proud of how it works right now. It’s probably better than how Unity manages it, for sure.

Motionmelody Plus Lua

In Motionmelody, I’ve been designing a system for loading and writing Lua scripts that can be executed in-game. It includes a fully-featured script editor so you won’t have to use Visual Studio Code or anything.

In the future I’m planning on adding a debugger and maybe an intellisense-like system.

I was mostly inspired by NotITG and the amount of bullshit you’re able to do in that game. Hopefully you’ll be able to make some cool stuff with it.

Here’s the full example script as just an example of what the Lua API is like:

local drawLines = {}
local currentLine = {}
local drawingLine = false
local drawColor = { 0, 0, 0 }
local drawColorIndex = 9;

local function isPointInRectangle(px, py, x, y, w, h)
    -- Check if the point is within the bounds of the rectangle
    if px >= x and px <= (x + w) and py >= y and py <= (y + h) then
        return true
    else
        return false
    end
end

local function drawPalette(r, g, b, i)
	local rectX = -640 + ((32 + 16) * i) - 32;
	local rectY = 360 - 32 - 16;
	local rectWidth = 32;
	local rectHeight = 32;
	
	if (i == drawColorIndex) then
		melody.graphics.setColor(0, 0, 0);
		melody.graphics.drawRectangle(rectX - 4, rectY - 4, rectWidth + 8, rectHeight + 8, 12);
	end
	
	if (isPointInRectangle(melody.player.getX(), melody.player.getY(), rectX, rectY, rectWidth, rectHeight)) then
		drawColor = { r, g, b, 1 }
		drawColorIndex = i;
	end
	
	melody.graphics.setColor(r, g, b);
	melody.graphics.drawRectangle(rectX, rectY, rectWidth, rectHeight, 8);
end

function OnDraw()
	melody.graphics.setZLayer(4);

	for i, line in ipairs(drawLines) do
		local color = line[5];
		melody.graphics.setColor(color[1], color[2], color[3]);
		melody.graphics.drawLine(line[1], line[2], line[3], line[4]);
	end
	
	if (drawingLine) then
		melody.graphics.setColor(drawColor[1], drawColor[2], drawColor[3]);
		melody.graphics.drawLine(currentLine[1], currentLine[2], currentLine[3], currentLine[4]);
	end
	
	drawPalette(1, 0, 0, 1);
	drawPalette(1, 0.5, 0, 2);
	drawPalette(1, 1, 0, 3);
	drawPalette(0.8, 1, 0, 4);
	drawPalette(0, 1, 0, 5);
	drawPalette(0, 0.5, 1, 6);
	drawPalette(0, 0, 1, 7);
	drawPalette(0.5, 0, 1, 8);
	drawPalette(0, 0, 0, 9);
	drawPalette(1, 1, 1, 10);
	
	-- reset button
	local resetX = 640 - 166 - 16;
	local resetY = 360 - 64 - 16;
	local resetWidth = 166;
	local resetHeight = 64;
	
	if (isPointInRectangle(melody.player.getX(), melody.player.getY(), resetX, resetY, resetWidth, resetHeight)) then
		if (melody.input.isMouseButtonPressed(0)) then
			drawLines = {}
		end
		melody.graphics.setColor(0.6, 0.6, 0.6, 0.5);
	else
		melody.graphics.setColor(0.2, 0.2, 0.2, 0.5);
	end
	melody.graphics.drawRectangle(resetX, resetY, resetWidth, resetHeight, 16);
end

function OnUpdate()
	if (melody.input.isMouseButtonPressed(0)) then
		currentLine = { melody.player.getX(), melody.player.getY(), melody.player.getX(), melody.player.getY(), drawColor }
		drawingLine = true;
	end
	
	if (melody.input.isMouseButtonDown(0)) then
		currentLine[3] = melody.player.getX();
		currentLine[4] = melody.player.getY();
	end
	
	if (melody.input.isMouseButtonReleased(0)) then
		table.insert(drawLines, currentLine);
		currentLine = {}
		drawingLine = false;
	end
end
Depression Is Sucks

Happy anti-gay month.

For the past couple years, I’ve been dealing with a huge depression. Mostly caused by my overwhelming cynicism about Humanity. It’s not fun to make video games when you feel this way. Constantly waking up every single day to spend 12 hours working on a game and then going back to sleep. It’s not that I hate making games, I really do love it, but it’s extensively obvious that this is not what humans were designed to do.

Suicide is a constant thought I have every day, it’s almost a joke at times. Very pathetic.

Happiness is overrated to be honest, comfort is more important right now. I just need the comfort to finish this game, and then we’ll see what happens next…

In my efforts to distract myself from the dishonorable state of my mind, I’ve decided to buy play some new games, not really a thing I do anymore.

Mini-Praise of Animal Well

Animal Well might be one of the greatest games of all time. Period. It’s no Pizza Tower but it’s definitely up there. The art, sound design, music, level design, it’s just a mastapeece. It goes to show that there are still people willing to make good and high quality stuff out there. Excellent.

My only problem with the game is the save system, it pretty much only exists to facilitate nostalgia and nothing else. It’s more of a checkpoint than an actual save. The game would’ve been better without it.

Hats off to Billy Basso.

How to Setup raylib with BeefLang

I said I would write another tutorial someday.

Honestly, Beef has been one of my most favorite languages to work with in the past few months, even more than C#!

Beef is compiled with no GC, and while I could probably write a whole post on how much I loathe garbage collectors, just know it makes the language really fast.

If you’re already familiar with C# (as I was), you can start using Beef pretty much instantly.

More info on it here.


Hoping to increase awareness of the language, I’ve created a very small and simple guide that demonstrates how to use one of my favorite game frameworks, Raylib, alongside one of my favorite languages.

I’m not going to go over the more advanced stuff. The Beef Documentation covers most questions you may have. You can also join the BeefLang Discord server and ask for help there.


Tutorial

First, Download and install Beef. If you’re reading before version 1.0, I would recommend using the Nightly Releases. They may already include fixes for bugs you could encounter and the latest features.

Now that you have Beef installed, open the Beef IDE, and you should see something that looks like this:

(Recent Projects blocked out for secrecy…)

Click the “Create Workspace” button, and select the folder you want to create your workspace in. Note: The name of the folder is what the initial startup project will be called.

A workspace in Beef is similar to a solution in C#, it contains all the projects inside your main project.

Press “Ctrl+Shift+S” to save your solution and everything in it.

Currently your project is empty. If you try to run your project by pressing “F5”, the IDE will ask if you want to auto-generate startup code because there is none. Select “Yes”.

If you press “F5” now, a console window will appear and quickly disappear. Congratulations, you have just created your first Beef program!

Let’s install Raylib so that we can use it with Beef.

Raylib is written in C, so to use it we’re going to have to write some bindings for it. Luckily, I already did that which you can clone from GitHub.

Extract the code to a folder and keep it in a safe place. We only need the “raylib-beef” folder for all purposes covered in this tutorial, so don’t worry about any of the other folders or files.

Right-click on “Workspace” (not the tab) and select “Add Existing Project”.

Go to the safe place where you stored the “raylib-beef” folder, and inside of it, and double-click on the “BeefProj.toml” file.

You should now see “raylib-beef” in your workspace panel.

Right-click on your main project and select “Properties…”.

From there, select “Dependencies” from under the “General” dropdown and click the checkbox next to “raylib-beef”.

You are now ready to use Raylib with Beef! I wrote a sample program that draws the raylib-beef logo to the window.

Copy and paste this into “Program.bf” under your main project and press “F5” to run.

using System;
using RaylibBeef;
using static RaylibBeef.Raylib;

namespace example; // Replace with your project name.

class Program
{
	public static int Main(String[] args)
	{
		InitWindow(800, 600, "Raylib Beef 4.5");

		var beefMain = Color(165, 47, 78, 255);
		var beefOutline = Color(243, 157, 157, 255);

		while (!WindowShouldClose())
		{
			BeginDrawing();
			
			ClearBackground(RAYWHITE);

			DrawRectangle(GetScreenWidth() / 2 - 128, GetScreenHeight() / 2 - 128, 256, 256, beefOutline);
			DrawRectangle(GetScreenWidth() / 2 - 112, GetScreenHeight() / 2 - 112, 224, 224, beefMain);

			DrawText("raylib", GetScreenWidth() / 2 - 44, GetScreenHeight() / 2, 50, beefOutline);
			DrawText("beef", GetScreenWidth() / 2 - 62, GetScreenHeight() / 2 + 46, 50, beefOutline);

			DrawRectangle(GetScreenWidth() / 2 + 54, GetScreenHeight() / 2 + 54, 42, 42, beefOutline);
			DrawRectangle(GetScreenWidth() / 2 + 62, GetScreenHeight() / 2 + 62, 26, 26, RAYWHITE);

			DrawCircle(GetMouseX(), GetMouseY(), 20, beefOutline);
			DrawCircle(GetMouseX(), GetMouseY(), 8, beefMain);

			DrawFPS(20, 20);

			EndDrawing();
		}
		CloseWindow();

		return 0;
	}
}

Have fun!

Motionmelody Update

Hello, It’s been quite a while, hasn’t it? Over a year since my last update on anything.

Let’s start with an update on the Motionmelody editor. It’s completely different now, with a waveform and full undo-redo support. We can finally make Darude - Sandstorm properly.

At this point, I’m about 60% done with the game. It’s taking me a little longer than I would’ve liked, but it’s definitely better than what I showed before.