[Plugin] JavaScript (C2 and C3)

  • Toddler Damn, those are pretty high expectations)

    Yes, the main reason I made this plugin was that I wanted to have game objects in JS. With constructors and stuff, you know. It especially works well if you follow MVC paradigm: model goes to JS files, view and controller stay in Construct. There's a player object in JS with lives and points. This object doesn't know (and shouldn't know) anything about the player sprite and its current animation. View (construct event sheet) checks js object's state every tick and changes animations if needed. Actions like jumping don't change js object, they only affect player's sprite. And only if the sprite hits the enemy, Controller (event sheets) calls player object's methods like player.hit(). A method changes the object's state. The View checks the object's state and changes animations if needed. And so on.

  • Hi valerypopoff I am currently reading up on your site [VERY WELL DONE by the way, the amount of visual design work you put into explaining this plugin is...b.e.a.u.t.i.f.u.l. You are truly a designer at heart].

    I am seeing:

    var player =

    {

    bla bla bla

    }

    You...you are not using class ?

    Can I use class ? I really want to use class...it's more OOP like.

    Is that possible ?

  • Hi valerypopoff

    I analyzed your example *.js file to try and figure out why you didn't use class and I saw this:

    var Enemy = Object.assign( {}, Player );

    This is where I immediately understood why you didn't choose to do so.

    If Player were an instance of a class, instead of typing that simple line, we will be looking at:

    var Enemy = Object.assign( Object.create( Object.getPrototypeOf (Player) ) , Player);

    Which is a little less elegant I suppose.

    Is that your reason ?

  • Usage:

    Select the JS plugin, inside the script files section, enter the script files, example: script1.js;script2.js

    Import all the *.js files into the "Files" folder.

    Call JS function:

    Allows you to call a function from the js file.

    You can even call object functions like this: object1.function1.

    Return value can be accessed via JS.StoredReturnValue.

    Since StoredReturnValue contains only string, use JSON to transfer your data.

    To access a variable from a javascript file, you have to "Init alias" to set an alias for the variable.

    and then you can: js.AliasValue("enter the alias name here").

    Can we skip this step and straight away access the variable directly ?

    Like instead of js.AliasValue("enter the alias name here")

    we can just have js.variable_value("enter variable name here").

    So all in all I have three questions summarized:

    Question 1: Why is "All scripts loaded" continuously triggering ?

    It should trigger once when all the script is loaded and never trigger again ever.

    You don't see construct 2's On loader layout complete continuously triggering, it trigger once and stop.

    Question 2: Can we have js.variable_value("enter variable name here") instead of js.AliasValue("enter the alias name here") ?

    It's more direct, the alias just adds unnecessary levels of convolution.

    Question 3: Now this is difficult I think...can you somehow create a way for the custom javascript to have its own triggers ?

  • valerypopoff

    Oh thanks. I just saw your example files, and didn't think about checking the properties bar. I didn't see the 'Learn how' button at your site and missed it. Sorry for the error on my part.

  • Toddler

    Can I use class ? I really want to use class...it's more OOP like.

    Yes. You can use any valid JavaScript. I didn't use class because not all browsers support it. For example, some old Androids with webview 30- (or such).

    The only thing that the plugin doesn't see is global variables with const and let. Shouldn't be a problem.

    You can even call object functions like this: object1.function1.

    You can even call object functions like this: object1.propery['one']['two'].foo['bar'].function1

    Return value can be accessed via JS.StoredReturnValue.

    Or you can compare returned value right away with conditions like Compare Function return value or Compare alias call

    Since StoredReturnValue contains only string, use JSON to transfer your data.

    That's not true. StoredReturnValue contains any Construct data type. It means it can be string, integer or float (boolean will be converted to 0/1). There's no need to use JSON. If you have to transfer data to Construct in JSON format, you probably have a wrong architecture. The only valid reason for retrieving JSON from javascript is when you want to put it to JSON plugin. But in this case you're actually passing some string. It just happens to be JSON.

    Now the second question is, can you create a trigger from a javascript file ?

    Sure. You can call Construct functions from javascript like this:

    c2_callFunction(name, args_array)
    

    I came up with this wrapper:

    function ConstructCallback(name, args)
    {
    	if( name == "" )
    	return;
    	
    	args = (args === undefined) ? [] : args;
    	
    	if( c2_callFunction !== undefined )
    	{
    		return c2_callFunction(name, args);
    	}
    
    	if( c3_callFunction !== undefined )
    	{
    		return c3_callFunction(name, args);
    	}
    }
    

    ConstructCallback triggers Construct function and even gets the return value from Construct that you can use in JS if you like.

    Question 1: Why is "All scripts loaded" continuously triggering ?

    Because it's not a trigger. It's a simple condition. Don't confuse one with another. Once scripts are loaded, All scripts loaded is always true. If you want to make a trigger out of it, add Trigger once (see the example on the promo page). I just didn't want to make a trigger condition since it is so easy to do it yourself.

    Question 2: Can we have js.variable_value("enter variable name here") instead of js.AliasValue("enter the alias name here") ?

    You sure can. Just use Execute JS code action or JSCodeValue expression or Compare JS code completion value condition. You can pass pure javascript to them. The problem is that it's only possible because they work by eval'ing those strings. And this is not a very good practice.

    The alias system is there for a reason. When you do

    js.InitAlias "Alias" with javascript "foo"

    js.AliasValue("Alias.bar")

    It actually translates to window["foo"]["bar"] on runtime. This is 100 times faster than eval'ing the string "foo.bar".

    Getting back to your question. I could make actions, conditions and expressions that work exactly like Aliases but you don't have to init the alias first. But it would mean +10 ACEs the user would need to learn to use.

    If you have lots of global variables (which is weird) that you want to access from Construct, just do

    js.InitAlias "Global" with javascript "_window"

    And add this to your javascript:

    var _window = window;
    

    Now you can access all global variables with alias "Global": js.AliasValue("Global.foo")

    Question 3: Now this is difficult I think...can you somehow create a way for the custom javascript to have its own triggers ?

    What do you mean "its own triggers"? You mean make javascript code tick itself? Well you sure can use SetInterval function for that. But I can't even imagine why would you want your javascript code to have ticks. Javascript code is a model of your game objects. A model doesn't tick. A model stores data and makes actions (changes itself) when you ask it to. There should be only one ticking mechanism in the game and you already have it: it's event sheets. But you're still free to use any async things in your javascript.

  • Thank you so much valerypopoff !

    This is a truly powerful plugin.

    I have updated the core docs for my students and will be updating the curriculum to include this plugin of yours.

    Your resulting work is a very very powerful plugin and is going to change a lot of my C2 lessons to come as the complexity of projects can now include heavy duty object orientated programming concepts, these are the core Docs for your plugin to be included in my notes, hope it will help others here, I apologize that it can be a little too straight forward but as a teacher I know what my students will ask and be confused about so I rather lay it out in the notes:

    This is a powerful plugin that allows full utilization of JavaScript in Construct 2 !

    Step 1: Place plugin into your project & rename it to "JS" for convenience.

    Step 2: Import file *.js files (as many as you like) into construct 2's "Files" folder.

    Step 3: Select JS plugin & enter the file name(s) e.g. "src1.js;src2.js" into the script files section.

    Now you can start to use it !

    Only start using it when "All scripts loaded" is triggered.

    For some stubborn reason, the creator choose to make it so that this event triggers continuously.

    His reason is "because all scripts are indeed loaded and hence will always be true, always triggering."

    That would be like saying the "On loader layout complete" event is indeed completed and hence will always be triggering, common sense will tell you such events should only trigger once.

    Because of this pure stubbornness on the creator's side, please add in the "Trigger once" event to the "All scripts loaded" event so that it finally makes sense.

    Accessing Global or Object Function (with or without return data):

    Under Action, choose "Call JS function (stores return value)", enter function name inside example "function_name", for object just do "object1.function_name".

    Return values is contained in: JS.StoredReturnValue.

    Accessing Global or Object Variable:

    This is another point of stubbornness from the creator, you have to create some silly "alias" nonsense that will be treated as the variable and access it using the alias.

    Instead of allowing direct access just like the way functions are accessed [see top, notice you don't require an alias to access a function which actually makes sense],

    the creator's stubbornness insist on the alias nonsense, his round about solution to this nonsense is:

    Step 1: update your *.js file to include this: var _window = window;

    Step 2: Right after "All scripts loaded" is triggered, put in JS action "Init alias", set top textbox to "Global" and bottom textbox to "_window".

    Step 3: Now you can FINALLY access your global variables via: JS.AliasValue("Global.global_variable"). //Global.whatever_global_variable_name_you_choose.

    Accessing Object's value is the same: JS.AliasValue("Global.object1.name")

    Creating your own trigger:

    This is single handedly the most powerful feature of the plugin.

    To be honest, it doesn't "actually" create a custom trigger but merely calls a function set within Construct 2,

    but this is enough, it is all your need for back and forth communication and triggering.

    First, implement the following code into your *.js file:

    function execute_construct_function(function_name, args)

    {

    if (function_name == "")

    {

    return;

    }

    args = (args === undefined) ? [] : args;

    if ( c2_callFunction !== undefined )

    {

    return c2_callFunction(function_name, args);

    }

    }

    Now all you have to do in your JavaScript is call:

    execute_construct_function("Trigger"); //Assuming you have a Construct 2 function called "Trigger"

    and it will work ! AMAZING ! This officially make this plugin the most powerful and flexible plugin in all of Construct 2's plugin history !

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • Toddler I don't understand what you have against aliases. It's convenient, look:

    Even if you have a complicated structure of game objects, like:

    var MainObj = 
    {
    	players_arr = 
    	[ 
    		{
    			name: "Pacman",
    			health: 100
    		}, 
    
    		{
    			name: "Inky",
    			health: 100
    		},
    
    		{
    			name: "Blinky",
    			health: 100
    		},
    
    		{
    			name: "Pinky",
    			health: 100
    		},
    
    		{
    			name: "Clyde",
    			health: 100
    		}
    	]
    }
    

    You can do:

    JS.InitAlias "Pacman" with javascript "MainObj.players_arr[0]"

    JS.InitAlias "Inky" with javascript "MainObj.players_arr[1]"

    JS.InitAlias "Blinky" with javascript "MainObj.players_arr[2]"

    JS.InitAlias "Pinky" with javascript "MainObj.players_arr[3]"

    JS.InitAlias "Clyde" with javascript "MainObj.players_arr[4]"

    And later operate with "Pacman", "Inky", "Blinky", "Pinky" and "Clyde" without this long "MainObj.players_arr[n]".

    If you have game objects in JS, they should be represented with classes, not with global variables. Since you have game objects in classes, it's not a problem to make aliases for them. It's not like it's 100 of them. It's like 3, right?

    If you have a problem using aliases, you're probably doing it wrong.

    If you have lots of global variables that are not objects and you want access them from construct, you're doing it wrong. It makes no sense since there are variables in Construct. No need to store global variables in js when you can do this in Construct directly.

    Instead you're supposed to have objects in js — the thing that you can't have in Construct. If you have lots of global variables that are objects, you're doing it wrong. Even if you have a game with 30 different enemies, they shouldn't be stored as 30 global objects. They should either be stored as an array of 30 objects, or be scripted in such a way that you don't need to access them at all.

  • Hi valerypopoff,

    I see your view, I also notice you often execute the quote "You are doing it wrong" followed by put down speeches questioning the capability of the questioner when you were simply nicely asked a question.

    Not asking you to remove your love of using alias, just asking for a rather consistent outlet, you did not ask for functions to be alias didn't you ? And yet it is possible to have functions within functions in JavaScript, you knew how silly it will be if you were to implement alias to functions, but you insist on not having a non alias way of accessing variable data.

    I can understand that you are thinking your script will only hold objects and functions and some kind of a punishment is given [via your silly alias nonsense] if one decides to have top level variables in the js files, but can you imagine how ridiculous it is to explicitly declare all top level variables in Construct 2 though?

    You will have variables not used by anything in Construct 2 whose sole existence is to have the objects and functions in the js files interact with it, it is much preferable to have these variables tucked clean and neat inside the js file itself.

    It's ok, I see no resolution to this the same way I see that you are utterly insistent on "All scripts loaded" being by default a continuous trigger [which makes no sense to anyone besides you] and yet knowing that by your own logic, construct 2's internal "On loader layout complete" would be a continuous loop which of course, would be utterly retarded if it was so.

    I think you are doing some mental gymnastic compartmentalization here.

    However, it is a fact that despite these two flaws [yes yes I am doing it wrong because you are right I got it] your plugin is truly powerful.

    So I will use it as it is and appreciate the gift you have given the Construct 2 community :D

    Thank you valerypopoff .

    I really do wish you well because me and my students is going to benefit from this amazing work of yours.

  • Toddler Look.

    I want to make the plugin better. And I can't do this without asking people what they like or dislike about it. But implementing everything people are asking for is no way to do it, I think you know that.

    I keep telling you that you're probably using the plugin wrong hoping that you would either start using the plugin as intended and not have problems, ooooooor tell me what is it you're trying to achieve that is not possible to do the intended way.

    I can't implement a feature just because you think something is silly. But I can do it if I see that there's a way of using JS from Construct that I didn't think of.

    "Wanting to do something" is not a way of using, I can't accept this as a feature request. Can you tell me why is it that you need to have lots of global variables in JS and then access them from Construct? What are you trying to achieve by that? I need to understand that. Please cooperate.

    And yet it is possible to have functions within functions in JavaScript, you knew how silly it will be if you were to implement alias to functions, but you insist on not having a non alias way of accessing variable data.

    If you mean calling a function that has just been returned from another function, then it's not possible even with aliases and it wouldn't be possible even if I implemented no-aliases variable access that you're asking for.

    If you mean functions that are not global, you can still call them with no aliases: JS.CallFunction("Obj.functions_array[0]")

    you are utterly insistent on "All scripts loaded" being by default a continuous trigger [which makes no sense to anyone besides you] and yet knowing that by your own logic, construct 2's internal "On loader layout complete" would be a continuous loop which of course, would be utterly retarded if it was so.

    Why do you keep saying that "All scripts loaded" is a trigger? It's NOT a trigger, it's a condition. Triggers have a green arrow next to their name in the event sheet and their names start with "On".

  • Thank you for your respond valerypopoff .

    I will be re-writing my educational notes to incorporate your plugin so any changes at this point is actually detrimental to my current momentum.

    However since you are open to request, may I request this:

    JS.VariableValue()

    With this, one will be able to JS.VariableValue("top_level_variable") to retrieve "Hello, I am top level variable".

    one will also be able to JS.VariableValue("newCharacter.age") to retrieve 23.

    This is straight forward, direct and just plain makes sense.

    ========================================================================

    Example *.js File:

    var top_level_variable = "Hello, I am top level variable".

    var newCharacter = new main_character();

    class main_character

    {

    constructor()

    {

    this.name = "Kenny";

    this.age = "23";

    this.number_of_death = 45;

    }

    some_internal_function_whatever_doesnt_matter()

    {

    }

    }

    ========================================================================

    without this method, currently users have to:

    Step 1: update your *.js file to include this: var _window = window;

    Step 2: Right after "All scripts loaded" is triggered, put in JS action "Init alias", set top textbox to "Global" and bottom textbox to "_window".

    Step 3: Now you can FINALLY access your global variables via: JS.AliasValue("Global.top_level_variable").

    Accessing Object's value is the same: JS.AliasValue("Global.newCharacter.age")

    You see how this one new feature will run circles around the current "alias" nonsense ?

  • You see how this one new feature will run circles around the current "alias" nonsense ?

    Of course I can see that. The question is why would you want to declare a global variable in javascript and then access it from Construct when it's 100 times easier and more convenient to declare a global variable right in Construct. I cannot understand that. I can see lots of benefits of using Construct global variables:

    — You can declare them and see their start values right in Construct

    — You can access them directly, no need to use any additional expressions or actions

    — There's auto completion that helps you to write the name of the variable

    — You can edit it's name and it changes everywhere automatically

    I can't think of any benefits that you can get by declaring global variables in javascript. Can you explain why you want to do this? Why is it more convenient to you?

  • Hello,

    Is possible to load the JS inline instead of additional network request?

  • Not sure I understand what you mean or what it has to do with the plugin.

    Is possible to load the JS inline instead of additional network request?

  • Toddler With the new version of the plugin you can access js objects directly, with no aliases

Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)