A proposal for Javascript scripting in Construct 3

16
skymen's avatar
skymen
  • 28 May, 2019
  • 1,764 words
  • ~7-12 mins
  • 2,739 visits
  • 2 favourites

Scirra has recently announced and then released a new feature for Construct 3: Javascript coding in the editor.

I was extremely hyped by this announcement and could not wait getting my hands on that new juicy stuff.

I have made many tests on a project and I want to make a proposal to give my humble opinion on what I think would make that feature game changing.

Scripting files vs JS in events

I think scripting files and Javascript inside events are widely different, and in that case I think that we should treat them as two ways to write code and thus make them behave slightly differently.

If we were to look at what the competition does, we have two main competitors that each use one of these two methods of writing scripts: Game Maker Studio and Unity.

Game Maker's scripting relies on including scripts inside their eventing system and have the code snippets be ran on every step, or each premade events. This is very similar to JS inside events and event sheets.

Unity's scripting relies on per file scripting, and including these scripts inside the GameObject's Component system. Scripting files can also be used for many other things, like scriptable objects, and editor addons.

I will be discussing each of these things in the following parts.

A scripting environment

What I made

At the moment, any script is immediately ran.

In my own testing I've written a few files that I used as the base for a scripting environment.

For anyone interested in seeing this on their own, download the c3p file.

The base

First, a global base file, construct.js

class C {
 static Function (name, params){
 c3_callFunction(name, params);
 }
 static Init(){
 C._objects = []
 }
 static get Objects() {
 return this._objects;
 }
 static set Objects(val) {
 this._objects = val;
 }
 static AddObject (object){
 C.Objects.push(object)
 }
 static Tick(){
 for (let object of C.Objects)
 object.Tick()
 }
 static Tick2(){
 for (let object of C.Objects)
 object.Tick2()
 }
 static get Runtime() {
 return this._runtime;
 }
 static set Runtime(val) {
 this._runtime = val;
 this.runtimeAssigned = true;
 }
}

class CObject {
 constructor(object, data, runtime){
 this._object = object;
 this._rawData = data
 try {
 let dataJSON = (typeof data === "string"? JSON.parse(data) : data);
 let self = this
 Object.keys(dataJSON).map(function(objectKey, index) {
 let value = dataJSON[objectKey];
 self[objectKey] = value
 });
 } catch (e) {
 }
 C.AddObject(this)
 }
 
 Tick(){
 }
 
 Tick2(){
 }
 
 SaveToJSON() {
 return {
 
 }
 }
 
 LoadFromJSON(o){
 
 }
}

Here I made two classes.

C is a static class that would contain many global functions, list every Javascript Object and distribute the Tick event to every object.

CObject was designed to be like Unity's MonoBehavior, and acts as a base object that would automate most of the common code and give child classes access to common functions like Tick. It also does some basic JSON parsing but that's not really important and won't be useful anymore once we have access to better features.

The initialisation

I wrote another file dedicated to managing and initializing everything. It only contains a simple runOnStartup function

runOnStartup(async runtime =>
{
 C.Init()
 runtime.addEventListener("tick", C.Tick)
});

This isn't very important in itself but is required to make everything work.

Rotate: an example object class

Here comes the meat, what an object script should look like.

class Rotate extends CObject {
 constructor(object, data, runtime){
 super(object, data, runtime);
 //Additionnal stuff on data
 }
 
 Tick(){
 super.Tick()
 this._object.angleDegrees += this.speed
 }
 
 SaveToJSON() {
 let superJSON = super.SaveToJSON()
 let dataJSON = {
 // Any new data to add
 }
 return {
 ...superJSON,
 ...dataJSON
 }
 }
 
 LoadFromJSON(o){
 super.LoadFromJSON(o)
 // Any new loading needed
 }
}

SaveToJSON and LoadFromJSON aren't very important (and don't even work). This is a very simple script that just rotates the associated objects with a given speed. What it does isn't important, how it looks like and how it's structured is however.

The proposal

Now comes the proposal. I think it could be great to have something similar to Unity where creating different kinds of scripts would initialise a scripting file with a different base. This would allow scripts to be used for many different things. What I am proposing is a base for an object script. That object script would be a class associated to object instances and would run additional code for each instance.

Integrating this in the editor

First, having a way to create that type of script in the script folder's context menu.

And then having a way to include that script inside an object's properties tab.

At the moment, linking an instance to an object class requires this bit of code in the event sheet.

Having a new tab inside the object's properties designed for object scripts would be great and would remove the need for manually linking instances to scripts.

The object scripts

These should give us as much freedom over the object as possible. Access to other scripts, to behaviors, to instance variables, and any trigger like OnCollision events (and anything else that plugins can export to scripts).

Scriptable objects

A class that can be instantiated as standalone files, and can be used to store data that shouldn't require anything else. These have been used in Unity to make cards, enemy types, weapon types and many many other things.

Exporting parameters to the editor

I have not thought of a proper way to make this possible in Javascript. It would be a Unity-like properties detection and export them to be able to change them from within the editor.

Note

There are many more things that could be added to this and there is no point in just copying Unity's system, so I will stop there with the Unity comparisons. I still think however that the way Unity handles things at the moment would be an amazing base to build up a system unique to Construct. Thus I will leave this proposal here, and won't request for anything more.

Javascript in the events

This part will be much less concise as there are less things to ask for.

The proposal

Runtime and Objects

At the moment, what we can do is very basic. All we have access to is a primitive runtime and a trimmed down version of the WorldInstance object. Having access to instance variables, behaviors, object scripts and per plugin functions (SetAnimation for Sprite, SetTile for Tilemap etc) would be a great start. I'm sure this is already planned so there is no real need for me to discuss this further.

Access to the draw function

One thing however that is available in Game Maker Studio and is not in any way in Construct is access to the draw process.

I think it would be highly beneficial to be able to write code that allows users to modify the draw process for objects and layers.

At the moment, this is used in Game Maker to produce a wide variety of effects, like drop shadows, outlines, which are possible in Construct using webgl effects, but also silhouettes which are not.

Subscribe to Construct videos now

Basically this is made possible by drawing the sprite once, then waiting for the draw to be finished for every other object, and draw it again with a color tint and a blend mode.

This is something that is not currently possible in Construct 3. Well it is possible in theory, but it is very limited, requires a very specific setup and will not work if you have a game that has a dynamic Z order. How this is currently possible in Construct is to have the player on one layer, every object in front of him on another one, set that object layer to own texture, and then create a silhouette sprite that gets pinned to every sprite that need a silhouette and have them all set to destination out. This is pretty inefficient, does not work in many cases, and is much harder to get working than the way Game Maker does it.

This feature is also possible in Unity with the rendering modifier features.

Subscribe to Construct videos now

Conclusion

That is all I have to say about the new Javascript feature. Seeing how early it is in development made me think that feedback and requests for how I think it should be structured might have more chances to be listened to.

Structuring it like this also has many advantages.

Unity and Game Maker Studio already do it, so we know what to do and what not to do. Why build something brand new when this has already been explored?

Devs from these softwares will also have a much easier time getting into Construct as they will know how everything works already. A brand new environment would confuse many devs, and that would make them refrain from joining Construct as they would need to learn a brand new engine from scratch.

Announcing Javascript as part of the editor has already sparked interest in many game developers, and not only Construct users. It is only a matter of exploiting this to Construct's advantage.

For education, this will also allow students to easily port their knowledge from Construct to Unity and Game Maker but also the other way around. In many game development schools, Unity is the main engine, and students already know everything about Unity. Construct being similar will make it very easy and very interesting for these schools to integrate the software inside their courses. Construct being much more user friendly than Unity, it would act as another stepping stone to learn about game development, while still being somewhat similar to other engines that will be taught in further lessons.

For professional game developers and game companies, the same applies. Many are using Unity and Game Maker for 2D games, and they might be interested in using Construct 3, its new runtime and features to ease development a lot.

For community members, sharing ready made scripts will be much easier if every script respects a similar shape and can be integrated anywhere in any project by simply importing them. That will allow the community to share pre-made scripts that can be added to projects much like behaviors, but without the need to install a whole addon.

TL;DR

Add Unity and Game Maker like scripting pls that'd be great kthxbye

Note that this is all a proposal based on my own opinion and is nothing more than that. Scirra is free to listen to any of what I have to say or not, and the final decision is theirs to make.

Subscribe

Get emailed when there are new posts!

  • 34 Comments

  • Order by
Want to leave a comment? Login or Register an account!
  • Great proposal to take advantage of the possibilities of the new C3 JS feature. There is a good educational coding aspect of adding more JS availability to C3, which is very welcome. In addition, Scirra, please also look to the more advanced JS users who this feature also targets, empower them to use the JS feature for very creative gamedev use (keeping with the understandable boundaries of the documented SDK.) For example, the Scriptable Object, Editor Properties Integeration and exposing the Draw replacement function would be very welcome. The changes such as adding a connection between JS/editor props may also make it easier for people to share their JS work with others who are still learning, but could handle dealing with the properties in editor instead of adapting and changing JS code directly. This is similar to how we handle GLSL effects now. We supply parameters / uniforms that game devs can use to modify GLSL shader parameters without having to know the GLSL shading language!

  • The way Unity handles scripting is awesome! Would love to see something similar in Construct.

  • Why would you want to tick a js-object in the first place?

    I thought that a js-object stores data and functions that operate on that data. Data doesn't tick.

    What paradigm are you trying to implement?

    • [-] [+]
    • 1
    • Ashley's avatar
    • Ashley
    • Construct Team Founder
    • 1 points
    • (25 children)

    How does attaching a script to an object change how it's run or change what kind of code you can use? This doesn't appear to have been explained, and there is nothing like it in the JavaScript language itself. I'm pretty wary about diverging too far from how JavaScript normally works, because one of our key goals is to help people learn skills they can re-use elsewhere.

    • It doesnt change how it's run at all because the script is only a class getting defined. What it would do however would be to link an instance of that class to said object.

      If code were to be put outside the class it would just run on startup like any other script. The script also only needs to be ran once to define the class and not on every object it's linked to. It's really just replacing the need to write the one line of JS in event I wrote

      instance.Rotate = new Rotate(instance, data, runtime)