I wrote my first line of code back in 2014, when I tried Unity 3D to create a simple game for fun. I didn't have any programming experience at the time, but I loved video games. I made a couple of them back then, nothing commercial, just some simple games I could build in 4-5 days to play with friends for a few hours before moving on.
I eventually got into programming professionally and landed a job as a Software Engineer, which meant putting game dev aside and, plot twist, focusing on work.
Twelve years have passed since then (time flies), and the other day I was reading a book about the video game industry. Once I finished it, I decided to try making a game again. I didn't want to use Unity or Godot, as they felt like "too much" for what I could realistically make. Instead, I leaned towards GameMaker, which helped limit any unrealistic scope of a "super insane 3D game" I might get tempted by later.
GameMaker was perfect for a simple 2D game (plus some of my favorite games are made in GM). I managed to put together a playable top-down shooter in just one evening. Since it is super focused on 2D development, everything is intuitive and easy to use. After spending a decade working as a full-stack dev doing hundreds of different things (writing code, optimizing SEO, CI/CD, DB migrations, analytics, scalability...), I fell in love with GameMaker. I could finally write code for fun again.
However, there were some trade-offs: GML.
GML is the programming language used in GameMaker. If I had to briefly describe it, I would say: "It's an archaic version of JavaScript". While I believe it is great for people who first start writing code, but for me there are some big no-go:
- Typesafety: there are no types, you can use comments similarly to JSDoc, but I don't think it is good.
- Built-in code editor: this is the biggest issue for me personally. It is eons behind VS Code and **JetBrains **products in terms of DX experience.
As a certified "**TypeScript **lover" I said to myself: I can fix her this.
The idea was simple: "In and out, 20 minute adventure." I would take TypeScript code, run it through a transpiler, and post-process whatever needed fixing (for example: thisdoes not exist in GML; it uses self).
Obviously, things weren't that simple. But after a weekend (and a sleepless night), I managed to pull it off. I was finally able to write TypeScript code and make it work in GameMaker.
My initial PoC used a script in watch mode to compile TS code on the fly, but an incredible coincidence happened: two days after I started working on this tool, GameMaker released a new version of their IDE. It added support for build hooks, which allowed me to get rid of the "watcher" and simply transpile the code the moment you hit the "Run" button.
The actual thing
What I came up with is a CLI tool (open source, NPM) that can be installed globally.
Setup
Install the package globaly (-g):
npm i -g @odemian/gamemaker-typescript
It has 2 commands:
-
setup: Prepares the GameMaker project you are currently in to use TypeScript (createstsconfig.json, links types, and configurespre_project_stepto automatically transpile). -
compile: Manually compiles the project. This is useful if you've added assets like sprites or sounds, as this command will generate their respective types.
Project configuration and the TS first object
After the library has been installed, open a GameMaker project in VS Code (or whatever code editor) and run:
gmts setup
Create an object:
This will create a file at objects/obj_player/obj_player.yy. The .yy file contains the metadata for the object. Place your script in the same folder and give it any name (I call mine code.ts):
You can think of objects/obj_player as folder-based routing: each object is a folder in the objects directory, and you write your code right there.
Here is the actual code if you want to copy it:
class Player extends GMObject {
_movement_speed: number;
// inside of the defineObject you can declare all of your events, like onCreate, onStep, onDraw etc...
onCreate() {
// use "this." keyword to access object properties in a safe fully typed way
this._movement_speed = 2;
}
onStep() {
var _hspd = keyboard_check(vk_right) - keyboard_check(vk_left);
var _vspd = keyboard_check(vk_down) - keyboard_check(vk_up);
if (_hspd != 0 || _vspd != 0) {
var _dir = point_direction(0, 0, _hspd, _vspd);
this.x = this.x + lengthdir_x(this._movement_speed, _dir);
this.y = this.y + lengthdir_y(this._movement_speed, _dir);
}
}
}
If you've ever used GameMaker, you might wonder: "Wait, a Player class?" Yes, OOP baby.
Most other game engines (e.g., Unity, Godot) use this approach where each object's code is encapsulated in a class. I experimented with different approaches, but I eventually settled on classes. They feel like the most natural way to write game code. In the ten years I've been working with TypeScript, I've almost never used the class keyword, it rarely feels right in modern front-end or back-end development. But for a game, it's a perfect match.
The class inherits all the standard events from GMObject:
- Create ->
onCreate - Step ->
onStep - Draw ->
onDraw - and so on, you get the idea
Run it
After creating the Player object and writing the code, we can run it (I added a sprite to my obj_player behind the scenes):
How GameMaker understands TS
If you are into GameMaker, you might wonder if it uses the newest GMRT runtime. The short answer is no. As of the time of writing, GMRT is still in beta, and it is not clear when it will be released or in what form.
What currently happens is:
- When you set up a project using
gmts setup, it configures apre_project_stepscript to rungmts compilebefore building the project. - When you press "Run", it looks for all script and object resources and compiles any
.tsfiles it finds in the resource folder. - Events like
onCreateandonStepare extracted intoCreate_0.gmlandStep_0.gml(scripts that GML understands). Other methods are then assigned to the instance in the Create event. - GameMaker builds the project by reading all of the generated
.gmlfiles.
The cool thing about this setup is that if you start your project with TS but at some point change the idea, you can simply remove .ts files, and keep .gml (they remain in readable form).



Top comments (0)