Tips for Construct 3 developers

Construct 3 project organization tips

I came up with / yoinked some practices I usually follow when working on Construct 3 projects. Warning: opinionated content. I'm not saying these are "best practices" in general, but maybe other C3 gamedevs might some of them useful too, so here goes.

Table of Contents

Sheet organization

I usually have 4 folders in every project: Actions, Events, Functions and Values.

The Actions folder contains sheets that have custom actions for a single object type or family. Sheets are named after the relevant object type or family, prefixed with the letter "a", eg. aPlayers, aEnemies, aPortals.

The Events folder contains sheets that only have certain kind of actual events (conditions + actions), with names prefixed with the letter "e". Some examples: eCreated (containing only "on X created" events), eCollisions (with only "on collision with" and "is overlapping" events), eInput (only Keyboard / Gamepad / Mouse / Touch events), eMovement (only events with conditions of movement behaviours), et cetera.

The Functions folder contains sheets that only have functions in them, their names are prefixed with the letter "f". Some examples: fUI (functions that update the user interface), fLevel (level initialization, ending, etc), fUtilities (full screen toggle function, various value returning math functions, etc), et cetera.

The Values folder contains exactly 3 sheets that only have global variables or constants in them: vState for global game state related variables, vConstants for global constants (I prefix those with "C_") and vMacros (more on that later).

A single event sheet at the root level named Main (sometimes Level) that includes all sheets in the Events folder (there's no need to include sheets from the other folders, custom actions, functions and global variables/constants are always available) and nothing else. This sheet is set as the "Event sheet" on all level layouts. For menu screens I usually use a separate layout with its own event sheet.

I suffix all sheets with emojis (pro tip for Windows devs: press Win + comma to open the emoji panel), so I can spot them quicker when I have a metric crapton of tabs open.

Tangential sidenote
Using emojis in names of C3 primitives, and project folders instead of single file (.c3p) projects means that you'll have file names on your filesystem that have emojis in them. NTFS on Windows 10 seems to work fine, but your mileage might vary on Linux or Mac. You might also have issues if you store project folders in Dropbox, as it will refuse to sync those files. Because of that, I went back to saving my projects in .c3p files and storing those in Dropbox. Dropbox saved my bacon a few times when I've accidentally saved or overwritten a project, as it keeps previous versions of files (as long as it's able to sync them to their server) which you can restore.

If you contemplate giving my organization system a go, you can download this template which I almost always use when starting new projects.

Using "macros"

I never use literal strings in conditions/actions when referencing animation names, layer names, effects, tags and such: instead, I use "macros", which are just constants I define in my vMacros sheet. Example: if I have a sprite that has an "Idle" and "Walking" animation, I create constants named "ANIM_Idle" and "ANIM_Walking" in the "vMacros" sheet with the values "Idle" and "Walking" respectively, and use those in conditions and actions instead of the string literals "Idle" and "Walking".

This has a couple of benefits:

  • You get autocompletion: just start typing the name of the constant wherever you want to use it's value in a condition or action and you don't have to worry about typos either.
  • You can use "Find all references" on the macro to find its occurrences
  • I you decide you want to rename "Walking" to "Running", you only have to make the change in two places: in the animation editor and the global constant. I almost never need use the search & replace function because of this.

I also use three special macros which have no prefixes:

  • NOTHING, with the numeric value of -1: whenever I have variables that store UIDs and want to clear them, I set their values to NOTHING. If I want to check if they have been assigned, I compare them to NOTHING.
  • YES and NO with the respective numeric values of 1 and 0. This stems from the fact that you can't pass a variable or calculated value to a function or custom action with a boolean parameter: when calling it, you have to use a checkbox to either pass true or false, you cannot write an expression or reference a variable. Because of this I refrain from using boolean parameters in functions and custom actions, instead I use numbers just in case I ever need to do the aforementioned. And when I don't, I pass the YES or NO macro.

Picky blindspots (sorry)

A significant amount of bugs I create are related to screwing up picking conditions: either picking something I don't want to pick, picking the wrong instance or failing to pick something. Here are some suggestions and known footguns related to object instance picking.

  • If you only want to pick a single object, make sure that the conditions only pick one. If unsure, add a "pick nth instance" condition with the parameter "0".
  • If you're storing UIDs in (instance) variables, make their default value is a negative number (eg. -1) and not 0, since 0 could be a valid UID since it's zero indexed. When you clear the variable, also reset it to a negative number.
  • Don't forget that the timer behaviour's "On timer" condition might pick multiple instances if those instances reach their time in the same tick and you might need to put in an extra "For each" to run actions on all of them. This is mentioned in a big yellow warning box in the docs by the way.
  • Functions and custom actions have a similarly named option: "copy picked" and "copy all picked". If you have a custom action for an object type or family "X" and call it from an event that already picked some instances of "X", those will be passed on to the custom action even if you don't check "copy all picked" (which is for passing other non-"X" instances to the custom action).

Keyboard shortcuts

In event sheets you can do almost everything without touching the mouse. Although I still find myself using it at times, I gained considerable speed by learning the handful of shortcuts for creating events, conditions, actions, functions, comments, inline scripts, parameters and variables alone. One thing that is missing at the moment is a keyboard shortcut for creating custom actions, but I have a feature request for that too (I'd sure appreciate some upvotes on it if you have a GitHub account, thanks ;).

You can select events, conditions and actions using the arrow keys (which will also require some practice, but it's totally worth it), toggle breakpoints, bookmarks and disabled state. Navigating the condition / action editor is also fully possible using keyboard shortcuts, and of course you can use the system-wide shortcuts like (Shift+)Tab to jump between fields, Ctrl + left / right arrows to move the cursor in text fields one word at a time and Enter to apply changes. The automatically focused search bar in the condition / action editor is also a godsend, you can just start typing what you're looking for, it's basically a 2D autocomplete that you can also navigate using the arrow keys.

Embrace the web platform

Construct 3 runs entirely in a browser and you can also install it as a PWA if you are using Chromium (my choice), Google Chrome, Edge or possibly other Chromium-derived browsers. This means you'll be able to start Construct like you start any other desktop app, use it in an almost full screen window (you can go full screen with F11 if you prefer) and have it associated with the .c3p file type.

Another great thing is that you can integrate JavaScript libraries in your projects fairly easily, which opens up a lot of cool possibilities. Some examples of libraries I've glued in previously:

And since your projects are JavaScript apps, you can also use the developer tools in your browser, including the console: the stock Browser plugin / object has actions that write to the console, but of course you can use console.log() and friends in code blocks or the code editor as well.

This article was updated on 2024-05-26 18:57