Ruskul's Forum Posts

  • ...What is a specific usecase that this plugin solves that isn't in the base engine? Most plugins already have vectors and I can use atan2 (or angle expression in construct) to get the angle of the vector and vice versa.

    It sounds like a barebones movement plugin like the build in custom movement behavior.

    Vector normals and projection are super cool and solve alot of issues very nicely in games. They aren't particularly complicated, but if you need to use them alot then it makes sense to have them wrapped up. A dot product is simply ax * bx + ay* by, but that can be verbose in a project - for example, to obtain a vector rotation, I might have to do something like this:

    localVarX = Sprite.instVarX * SomeotherSprite.InstVarX + Sprite.instVarY * SomeotherSprite.InstVarY;

    localVarY = Sprite.instVarX * SomeotherSprite2.InstVarX + Sprite.instVarY * SomeotherSprite2.InstVarY;

    (where SomeotherSprite represents a unit vector created earlier or replaced with sin/cos(a)

    Much better to simply have something like:

    Sprite.v2.TransformDirection(SomeOtherSprite.v2.x, SomeOtherSprite.v2.y);

    //Results stored in: Sprite.v2.x & Sprite.v2.y

    You can also do neat things with unitVectors (a vector whose length = 1) and the dot product and cross product. Also, multiplying a unit vector by a scaler is a simple oneliner: Sprite.v2.Scale(scaler); Since there is both expressions and actions that do the same thing, you can set x/y of the v2 while also retrieving the result. It just reduces verbosity, and imo improves clarity, because unlike the dot product shown above, used twice to calculate a rotation, the function is named what it does so no need to adda bunch of comments explaining what maths are being used.

    For example: If I had a boat, and I wanted to know it's velocity in the lateral direction in order to apply friction in that direction, I could do so with vector projection. Custom movement handlers are big candidates for needing vectors and vector math. But I even use it for input and a few other items. If you treat up,down,left,right input as a vector 2, you normalize it and then scale it by acceleration. Dealing with rotating gravity, identifying slope normals, and relative spaces/directions all need vector math.

    You are most correct though, with the last statement, as it is, many times the need involves custom movement.

    Oh, as an aside, you can use vectors to help infuluence, or steer, any other behavior. For example, using 8Dir, you could add up a bunch of influencing forces and calculate what direction to simulate to the behavior...

  • Good to know.

    The advantage of the behavior imo is often I'll be using it as a easy way to add a vector2d to the object. You can even add multiple ones.

    Since I'm already working with it, doing things like based on that vector means I only need 1 or two inputs, depending on the function.

    It also means, somethings, like get projection at angle, only takes one parameter, the angle. although I still have to read out the result property. Its all kinda of hassle no matter how you do it, but its more performant than construct functions, or js modules, since js blocks currently incur a huge overhead when used at volume.

  • Hey all,

    I have a behavior for vector2 support. Its not ready to share, but before I put the work in to make it ready, I wanted to know if people would have any use for it and in what ways they would see themselves wanting to use it.

    Also, is there already a vector 2 math utility plugin or behavior out there that people use? (making this irrelevant?)

    Mine is a behavior just adds an x and y property to the object with getters and setters for changing components or the whole vector. It also has magnitude, magnitudeSquared, and some simple logic to ensure magnitude (and other properties) are never computed more than needed. I don't create any new objects at any time.

    If you wanted to calculate the unit vector for a vector you would do something like this:

    action: Set Vector (x,y) //if needed

    action: Normalize

    expression: x

    expression: y

    I have expressions/actions for the following:

    .normalized

    .normal

    .dot

    .projection

    .rotatePoint

    .rotateDirection

    .RotateVector

    .scale

    Would people find this sort of thing useful? Would it be better as a utility plugin rather than a behavior? Any thoughts, or suggestions before I flesh it out?

  • one is for Edit Time, the other for Run Time.

    But I agree that it's super annoying to have to juggle a ton of different files for addons.

    If I recall, in c2, I remember only a few files. One for shared scripts, one for edit and one for runtime. I'm curious why there was a decision to parse them out like this.

    Adding the language support is nice, but that alone adds quite a bit of redundant typing and opportunities to misspell things - But I do like that the descriptions and such aren't wrapped up in those long calls to create new aces that we had in c2.

  • Try this one if you havent yet.

    https://www.construct.net/en/forum/construct-3/plugin-sdk-10/c3ide-construct-plugin-ide-141201

    Yes, thank you! This tool is amazing. I have used it a bit, but I prefer to make sure I understand all the things it is automating before really using it...

    I think the biggest issue is that it is really easy to forget to change one line here or there (or worse, misspell it lol). Obviously that is a personal problem I suppose, There are a lot of 't's to dot and 'i's to cross - erm... the other way around... and so the workflow of adding a new _function to an instance and having a complete set of ACEs and property for that function is alot. Especially when perhaps it might just be basically a property with a getter and setter.

  • Call me dumb, but for the life of me, I can't figure out wtf half these files are doing in the template and why they are arranged that way. The addon sdk doesn't really help either. Ideally there should be some more pictures and break down whats going on. Instead, the manual is like, "Make sure you change this line here, and here, and here, to match this line over here... What is this? American tax season? I shouldn't have to waggle around 12 files to follow what a SINGLE behavior is doing. Its ridiculous.

    You think you could just read them, and they would have comments explaining what they are doing, but not really, they more or less just warn you you can break a behavior just for doing things wrong... In the c3 runtime folder in behavior.js is this:

    	"use strict";
    
    {
    	const C3 = self.C3;
    
    	C3.Behaviors.MyCompany_MyBehavior = class MyBehavior extends C3.SDKBehaviorBase
    	{
    		constructor(opts)
    		{
    			super(opts);
    		}
    		
    		Release()
    		{
    			super.Release();
    		}
    	};
    }
    

    But one folder up is a file named the same thing and it has this:

    	"use strict";
    
    {
    	const SDK = self.SDK;
    
    	////////////////////////////////////////////
    	// The behavior ID is how Construct identifies different kinds of behaviors.
    	// *** NEVER CHANGE THE BEHAVIOR ID! ***
    	// If you change the behavior ID after releasing the behavior, Construct will think it is an entirely different
    	// behavior and assume it is incompatible with the old one, and YOU WILL BREAK ALL EXISTING PROJECTS USING THE BEHAVIOR.
    	// Only the behavior name is displayed in the editor, so to rename your behavior change the name but NOT the ID.
    	// If you want to completely replace a behavior, make it deprecated (it will be hidden but old projects keep working),
    	// and create an entirely new behavior with a different behavior ID.
    	const BEHAVIOR_ID = "MyCompany_MyBehavior";
    	////////////////////////////////////////////
    	
    	const BEHAVIOR_VERSION = "1.0.0.0";
    	const BEHAVIOR_CATEGORY = "general";
    	
    	const BEHAVIOR_CLASS = SDK.Behaviors.MyCompany_MyBehavior = class MyCustomBehavior extends SDK.IBehaviorBase
    	{
    		constructor()
    		{
    			super(BEHAVIOR_ID);
    			
    			SDK.Lang.PushContext("behaviors." + BEHAVIOR_ID.toLowerCase());
    			
    			this._info.SetName(self.lang(".name"));
    			this._info.SetDescription(self.lang(".description"));
    			this._info.SetVersion(BEHAVIOR_VERSION);
    			this._info.SetCategory(BEHAVIOR_CATEGORY);
    			this._info.SetAuthor("Scirra");
    			this._info.SetHelpUrl(self.lang(".help-url"));
    			this._info.SetIsOnlyOneAllowed(true);
    			
    			SDK.Lang.PushContext(".properties");
    			
    			this._info.SetProperties([
    				new SDK.PluginProperty("integer", "test-property", 0)
    			]);
    			
    			SDK.Lang.PopContext();	// .properties
    			
    			SDK.Lang.PopContext();
    		}
    	};
    	
    	BEHAVIOR_CLASS.Register(BEHAVIOR_ID, BEHAVIOR_CLASS);
    }
    

    There are also several other duplicate named files. and those duplicates have nothing going on in them other than the un-commented default. In total there are 12 files to edit in order to make a single behavior. Not to be the person who keeps saying, "Unity this, and unity that"... but in unity, you make one script, and maybe one more to define how properties are displayed in the editor, and that's it, not unlike many other engines out there.

    I mean, all this extra data should be generated by c3, and editable in editor. There is no reason to have to wade through 12 files, regardless of how 1337 your single ACE is. Imagine if you had to create 12 files in Unity, just to move a transform every tick. Rar,

    /rant

  • For those worried about 3rd party addons, I wanted to point out Overboy is right about stability in c3. For all my complaining about the engine, c3 is the most stable tool I have worked with, and though I do most my work in unity...

    I think 85% of the add-ons I have bought in unity are depreciated or irrelevant at this point, and those sometimes easily cost > $100. Was it a waste of money? Depends on how you look at it.

    If you can benefit from a tool now, the worst thing you can do is not buy a tool simply because you think it will be outdated in a few years. You'll find yourself under-productive, waiting for an off chance that something will change. Also, the standard workarounds for the tool's Overboy is selling aren't as performant or nearly as convenient.

    Get 'em while they're HOT!!!!

    If you work on medium/large projects I think you will find them indispensable.

  • drive.google.com/file/d/15kPU45uWAiLnM5aEOeMbDIgXqXpkL5e3/view

    I simply disable any events I don't want to run. and then look at the global variable timetocomplete to get the duration the test took. The majority of events to toggle are in the repeat loop. The loop runs one million times. A global variable is set to 50, and is the number of times the test runs. I probably made some mistake in counting so it might actually be 49 million times the test runs... or 51 lol.

    As per my other related post that you linked to, I am not arbitrarily inventing performance test to run, just for fun. I am running these because they are anticipated sticking points relevant to my project. You say "just make the game and worry about this later"? That's easy to say when you haven't already undergone the pain/time of porting projects to another engine multiple times because it wouldn't work in one or the other. Obviously you know how much work it took to change the engine from c++ to js. It pays in time and money to make sure you can feasibly create your project in the workspace and I am really limited on the former.

    Anyway, I think this brings up two major considerations for me, and any project:

    1. Calling a JS block is, for some reason, incredibly expensive, compared to basic event sheet ACES. This means that there is no reason to replace actions with JS, unless you are using a single JS block to replace many aces. Idealy, JS should be used to minimize event sheet loops and should never be used in an eventsheet loop.

    Obviously, All of this is only applicable when working with a large number of objects.... say more than a few hundreds, like my original tests were geared for and what my game is going to call for.

    But this really highlights the idea that you can use JS blocks to improve performance.

    2. Event sheet functions still are way bad for performance if you are structuring your project around them in a sensible and reasonable way... for example: Don't repeat yourself...

    I need a vector/math library for my game. I made a prototype project that uses event sheet function calls for this. They take 4 parameters (ax,ay,bx,by) and set globalVarables to handle multiple returns (rx,ry). Anyway, it isn't unreasonable to call a function from a function from a function... Function GetProjection is going to call function Normalize 2 times. If I really practice DRY, Normalize is going to call a function to get the length squared of a vector and then get the sqrt of that vector. TLDR, a simple function call to a library like this can easily call 6 other functions to do its job. You get 100 objects all wanting to get a vector projection, and all of a sudden you have 600 function calls and now you have already chewed through a decent chunk of your available cpu budget, and thats before you call functions for all those objects, like "seek" "arrive" and "avoid" which all will involve yet more vector math.

    JS seems like a good option here, but simply replacing event sheet function calls like normalize() with a JS blocks calling a module to do the same thing.... performance isn't particularly better. Its WAY better to take that library and stuff it in a plugin and use the plugin instead - because then at least it feels like the JS is married to the ACES, but this goes back to behaviors and plugins being tedious to make and having to write alot of stuff just to make one ACE. (Skymens plugin builder is amazing in this regard - As an aside, if I recall, one of the things that the c2 community really was hyped on for c3 was basically to be able to build extensible behaviors and package them up and share them, all from within construct, but it seems that never happened for whatever reason).

    Otherwise, the only other option is manually inline your function calls. That is tedious, unscalable, poor design, but saves a huge amount of performance where functions call other functions as in the example above.

  • Ashley

    I wrote such a long reply but I was logged out when I hit reply.

    TLDR: I fixed the test. I also made a few more.

    I am not arbitrary in these. I have had to move projects from construct to unity in the past due to performance reasons. Every construct project I have ever worked on beyond prototype has had performance bottlenecks. I am not simply making random tests for fun, but creating small prototypes that help me determine if c3 is the right engine for this project. I can achieve 80fps in unity with 100,000 objects normalizing a vector projection for movement.

    1000 was chosen because that is my design goal and is reasonable. But so for construct is unable to meet my desired performance goals.

    The issue is this: (JS blocks have a surprising overhead, functions unsurprisingly have overhead, and must be inlined when used frequently)

    JS blocks have significant overhead even if they are empty and do nothing. If a loop is being used to iterate through objects, it is best not to use JS blocks as actions work faster. This is a confusing result. I changed the JS so it would iterate through an array of data and calculate the results to another. So long as no loop was used in the event sheet, the JS ran super fast. So there is simply an overhead to each JS block that is accentuated in an eventsheet loop.

    I knew built in eventsheet functions had an overhead, and would perform worse... blah blah blah...

    But the fact is, I am manually inlining functions in order to achieve acceptable performance, and that is NEVER a good thing.

  • Hey all,

    I have a construct function for calculating the normal of a vector.

    I made an equivalent version in a js script and imported it. Here is the JS:

    export var x = 0;
    export var y = 0;
    
    export function Normalize (ax , ay) {
    	
    	var length = Math.sqrt(ax * ax + ay * ay);
    	
    	 if (length === 0) {
    	 	x = 1;
    		y = 0;
    	 } 
    	 else {
    	 	x = ax / length;
    		y = ay / length;
    	 }
    	
    }

    I should add that the eventsheet based method is optimized to use as few events as possible and uses expressions like set Variable x: length = 0? 1 : ax/length

    If I run Normalize(1,1) in a js block on an event "repeat" 1000 times, the cpu is running at around 22%, nothing else is going on.

    If I run the version I created in event sheet through functions, I get around 30% cpu. If I inline the function and eliminate the function call, I get around 8%.

    Basically, the eventsheet function call is ridiculously costly, which is already known, but what I don't understand is why the javascript version is performing worse that the event based one. Is there an "overhead" to a javascript block running that is junking up the performance when used with eventsheet loops?

    If I take away the eventsheet loop condition and replace it with a for loop in JS, the cpu time goes down to 3%, which is basically nothing. What am I missing here?

  • I'm not clear on how you are supposed to do this atm.

    I've looked through some of the examples, but they always seem super basic, or all JS is in the main.js file, instead of the event sheet.

    for (const inst of runtime.objects.Character.instances())
    {
    	inst.instVars.vX = m.x;
    	inst.instVars.vY = m.y;
    }
    

    For example, is the above selevting ALL instances of Character, or just the ones as picked by the eventsheet conditions at that point?

    Whatever the case, doing the above 1000 times with only 1 Character in the scene adds 10% cpu load, which makes it feel like I'm doing something terribly wrong with that.

  • I created a script file: "scripts" with the following:

    function CoolMath(x,y) {

    return x + y;

    }

    In my event sheet, I then have an javascript action:

    runtime.GlobalVars.SomeVar = CoolMath(1,1);

    Auto complete all brought up what I typed, so I figured it would be valid, but its not. SomeVar never gets changed. I am assuming I need to somehow import "scripts.js"?

    EDIT

    Found the problem - I didn't have the property set correctly for "importsforevents.js". It was set to none. I also added "export" keyword to my function name edited the function call to m.CoolMath to reflect the import call.

    Integration with scripts in events

    Unlike legacy "classic" mode scripts, modules have their own top-level scope. This means things like a top-level function declaration is not available in other script files.

    Instead you can add imports to the script file with the purpose Imports for events which then become available for scripts in events. See the section Using imports in Scripts in event sheets for more details.

    Another option is to write globals as explicit properties of globalThis, e.g. globalThis.myFunction = function () { ... } and call it via globalThis.myFunction(), but using modules is preferable.

  • I found a link to some images and simple explanation for those interested:

    openarena.ws/board/index.php

    As for the Velocity Verlet Integration, I can seemingly only find info on the version that infers velocity from current position and last position. I didn't know there was another, can you point me to it?

    I think I know of Explicit Euler (and semi-version), Midpoint, vk4, The german named one lol... something like rungakatta?, and Verlet (velocity infered by position)

  • This is documented actually

    https://www.construct.net/en/make-games/manuals/construct-3/scripting/scripting-reference/object-interfaces/iworldinstance#internalH1Link3

    But iirc it isn't available in the SDK.

    Gosh dang it, the addon sdk is different than the javascript blocks and assets???!!! So now you have two connections to the c3 runtime that differ?

    Hence the direct access to the collision engine which as far as I can tell, is undocumented. It's also bad because I think it actually skips most optimizations like collision cells, it's just a raw call of "Is instance X overlapping instance Y"

    What makes you think that? all the built in behaviors that call it would suffer but they seem performant enough, regardless of how many solids exist. I mean, I can set up some tests, but it would be absurd to me to have a call that bypassed the optimizations. atoh, If you are specifically testing 1 instance vs another then optimizations aren't going to matter a whole lot anyway (1st check being an aabb, I would think, which is super fast.) I haven't actually looked through this area of the engine since c2 so, this is as all hypothetical conjecture.

  • Try Construct 3

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

    Try Now Construct 3 users don't see these ads
  • Looks like that method is called leapfrog integration. It averages out changes to acceleration from frame to frame.

    Most behaviors use velocity verlet integration. Which handles constant acceleration motion perfectly no matter the timestep.

    verlet is appropriate for springs and the like, correct?

    I remember learning about the one I mentioned in order to correct maximum jump height in games running at different frame rates. I had the issue with my platformer behaviors having drastically different maximum jump heights at 30fps and 60fps