Ruskul's Recent Forum Activity

  • Not at all! A memory leak in a managed language occurs when you have objects that can't be garbage collected.

    I said I wanted to avoid excessive garbage collection. "Accumulation", is a bit misleading, because you have memory that is either garbage, or it isn't, and it is basically defined as garbage by the collector. If the garbage collector marks something as garbage, it will be collected and so can't accumulate. You can create garbage, which will be collected, but a memory leak happens when the programmer has somehow mismanaged memory so that it can't be collected.

    Garbage collection (major/minor/whatever) takes time to operate, which is why I like to avoid it if I can. The more garbage you create, the more time it will take to collect. Long lived objects typically have a larger impact when removed, and there is an overhead per object that the garbage collector has to scan.

    A memory leak is a logical problem, in which the programmer has created references to objects that are no longer needed, but the references still exist on an object that is needed. A simple example would be if a character stored a reference to every bullet they shot in a list... Perhaps they have an ability that when triggered causes every bullet in the air to explode... Then when the bullet hits something, and is "destroyed" the programmer forgot to delete the reference in the list. Basically, you now have objects no longer in use, but they can't be garbage collected because the program still has a reference.

    In something like c++, a memory leak is way easier to accidentally create, because there is no garbage collector. C# is like js in that is has was to create leaks using action/delegates/lists/etc, where any function pointer can keep an object alive to which the function points, which in turn is keeping other objects alive, and so on.

    Memory leaks is simply memory that can't be released, despite no longer being in use. In managed languages, it just means that memory can't be garbage collected, though the collector will still scan it for opportunities to collect it.

  • Wait, now I'm super confused because this may indeed apply to me. This would be in instance.js

    I add properties like so:

    constructor(behInst, properties)
    {
    	super(behInst);
    			
    	//This is what the example on github shows:
    	//this._testProperty = 0;
    			
    	this._vx = 0;
    	this._vy = 0;
    	//etc...
    }
    

    I also do this as well, outside of the constructor obviously, but still inside the main instance class :

    get _someproperty() {
    	return this._somecoolmath(42) + this.somethingelse + this.etc;
    }
    _somecoolmath(n) {
    	return (n === 42? n : 0);
    }
    

    Later, I us a weakmap in the scripting interface to provide a way to hook in editor side like so:

    set vx(n) { map.get(this)._SetVx(n); }
    get vx() { return map.get(this)._GetVx(); }
    set vy(n) { map.get(this)._SetVy(n); }
    get vy() { return map.get(this)._GetVy(); }
    setVelocity(x,y) { map.get(this)._SetVelocity(x,y); }
    

    Is this not right? The example behavior on github doesn't really show anything else. I'm not sure what all constitutes a runtime class, but... ?

  • Try Construct 3

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

    Try Now Construct 3 users don't see these ads
  • I was asking about the lifespan of a construct object and how it is recycled behind scenes, if at all. Not so much worried about a leak, rather excessive garbage collection.

    Ashley addressed this in another forum recently, and as it turns out c3 does not use object pooling, Construct 2 used object pooling (to reduce the amount that the garbage collector would have to sort), but that is not the case with construct 3.

    As I understand it, Ashley chose not to bother with it because the advantages of pooling only really apply when 100s of objects are being created/destroyed every second; For those specific cases, its best to create low overhead objects and create a custom pooling solution yourself, as a generic one may not be as useful anyway.

    The advances in garbage collection in JS is vastly superior compared to 10 years ago when c2 used pooling.

    Garbage created on a frame to frame basis is handled with ease, typically in downtime between frames, and as such, it isn't critical to worry about creating JS objects to discard them each frame like it once was Major garbage collection is another beast and can cause some issues if a programmer is absolutely careless, but outside of creating large arrays and tossing them every few frames, there isn't too much to worry about. Even major GC is still effeciently handled these days in JS... My knowledge at the time of asking the question was about 10 years behind the times

    All that said, while object pooling isn't 100% necessary anymore, as it once was, I have found that creating and destroying a large amount of objects every tick still has a large enough impact to recommend recycling/pooling.

    Trying to manage object recycling via eventsheets, however, usually doesn't provide a large benefit as a consequence of increased event overhead. Therefore, I recommend either creating a behavior/plugin to handle it, or js module if you already have a lot of JS going on.

  • Ashley, I think you have made your position much more clear in the last few responses, so thank you for taking the time to do that.

    Out of respect for your position, I will do my best to shift over to only documented API.

    I don't think anyone here is arguing that encapsulation is bad.

    I think as is being pointed out, hacking always, eventually, inevitably, ends in disaster. I don't think anyone disagrees with that, and I don't think anyone is making tools thinking they will never have to update them at some point, maybe entirely!

    I think the point is this; and it isn't incompatible with your position:

    The current sdk api is too limited to realize certain types of projects. I would say it affects most projects, if you discount simple arcade games and prototypes, or learning projects.

    So long as the encapsulation occurs alongside (and preferably after the efforts to expand the api), that would be great! It would be welcome!

    But so long as you have to resort to internal engine hacks to achieve what official behaviors achieve, then the sdk is objectively too limited. The official behaviors, simple as some are, are demonstrative of the official sdk limitations.

    I understand there are some api that need to stay internal for flexibility reasons. But I also see many that just look like oversights - missing for no reason other than lack of priority.

    As you say, industry standard is too use encapsulation, but every other tool has a larger more robust api.

    Just! Please don't obfusicate the internals, just to enforce "good practices"! As I see it, at best that is like the fire authority deciding to torch every building not up to code, in order to prevent the eventual fires that could happen.

    The internals provide a very useful learning tool, even if not abused, I would still like that access. If I couldn't see the internals in c2, I never would have been able to make any behaviors. The same thing goes for c3. The current documentation and example list is just far too limited and the internals become a very useful learning ground. Being able to pop the lid and have a peak really helps to improve understanding and inform practices.

  • Doh! I totally missed that! Thank you kindly!

  • Thanks Ashley ! I think you mentioned using mixins in the past and I forgot about that feature.

    Incidentally I was looking at the latter approach you mentioned. Thanks for the insight.

  • I wrote a new timer behavior that uses real triggers https://www.construct.net/en/make-games/addons/1236/trigger-timer it outperforms the build in timer behavior in all cases I have tested and also only has one instance picked in the on timer trigger. (I also added some additional features)

    Sweet, I'll have to check it out. I'll probably stick with what I have at this point, but I may use yours in the future.

  • It's kinda funny that on almost every topic I read Ashley say "dont worry about performance. difference is not really noticable. 99.9% of projects doesnt need this" and so on. And then guess what? Peformance was literally the main problem i had with C3 through all those years. And i make pretty simple games! Some things in C3 cause absurdly bad performance and you will never read about that in docs, and the only way to figure out is to extensive testing and comparing different approaches. Without WebGL debugger and literal months of trial and error i would've never made my games playable on mobile or low end devices.

    In most cases i agree with Ashley, but...

    Ashley "dont worry about performance" Gullen

    Yeah, I struggle with his responses: "Usually this won't matter" -links article about preoptimization. The only reason I noticed it was a problem was because it was a problem, whether that's usual or not. I too moved many a project prototype from contruct to unity because of performance.

    In unity, I would just make the game and it would run well enough. Construct I always felt like a nes developer in the 90s squeezing everything I could out of the system.

  • I can't find in the manual if it tells you how to add an interface for a behavior.

    I did the following in the behavior instance.js:

    const map = new WeakMap;
    self.IToggleBehaviorInstance = class IToggleBehaviorInstance extends IBehaviorInstance {
     constructor() {
     super();
     map.set(this, IBehaviorInstance._GetInitInst().GetSdkInstance())
     }
     
    		getState(i) {
    			return map.get(this)._GetState(i);
    		}
    		setState(i, v) {
    			C3X.RequireFiniteNumber(i);
    			C3X.RequireFiniteNumber(v);
    			return map.get(this)._SetState(i,v);
    		}
    		
     };
    

    Later in an event sheet, I have the following:

    const inst = runtime.objects.Sprite.getFirstInstance();
    const t = runtime.objects.Text.getFirstInstance();
    
    const toggle = inst.behavior.Toggle;
    
    toggle.setState(0,3);
    t.text = toggle.getState(0);
    

    But I get an error saying: TypeError: Cannot read properties of undefined (reading 'Toggle')

    I've looked at the official behaviors, and I don't see what I am doing wrong (unless you just aren't allowed to define interfaces?)

  • Hey all,

    I have no issue sub classing an object type, but it seems the same thing doesn't work for families, unless I am doing something wrong.

    I want to be able to define a common behavior (in this case, some custom physics stuff), and apply it to many objects.

    Currently, as I understand it, I can only subclass a particular object:

    //In familyExtend.js
    export default class TestInstance extends globalThis.InstanceType.Tester
    {
    	constructor()
    	{
    		super();
    		this._balls = 2;
    	}
    }
    
    //In familyExtend2.js
    export default class TestInstance2 extends globalThis.InstanceType.qCollider
    {
    	constructor()
    	{
    		super();
    		
    		//add properties
    		this._balls = 2;
    	}
    }
    
    //In main.js
    import TestInstance from "./familyExtend.js";
    import TestInstance2 from "./familyExtend2.js";
    
    runOnStartup(async runtime =>
    {
    	//extend classes like so
    	runtime.objects.Tester.setInstanceClass(TestInstance);
    	runtime.objects.qCollider.setInstanceClass(TestInstance2);
    	//...
    	runtime.addEventListener("beforeprojectstart", () => OnBeforeProjectStart(runtime));
    });
    

    In the above case, Tester is a family, and qCollider is a object. I can subclass the object, but not the family. I imagine this is expected -...

    But what I don't understand then, is how do I add this functionality to every object in my game, short of redefining a subclass for every object type?

    I understand I am essentially wanting functionality that multiple object types share, but adding it on as a subclass is backwards. Like defining rats and bats and then creating subclasses for mammals.

  • Thanks, much appreciated!

  • Uh, I'm showing my js noobery here, but for the life of me, I don't understand this entry in the manual:

    *pickedInstances()

    Iterates over the instances that have been picked by the event's conditions. This is only useful with scripts in event sheets.

    How do I use it?

    For general instances I would do something like this:

    const enemies = runtime.objects.enemies.getAllInstances()
    
    enemies.foreach((i) => {
    
    	Utils.DoSomething(i, 60*runtime.dt);
    
    });
    
    //or
    
    for(let i = enemies.length(); i>= 0; --i) {
    	Utils.DoSomething("42");
    }
    

    I tried to look up iterators for js but didn't really get what construct is doing here. Any help is appreciated!

Ruskul's avatar

Ruskul

Member since 23 Nov, 2013

Twitter
Ruskul has 2 followers

Trophy Case

  • 10-Year Club
  • Forum Contributor Made 100 posts in the forums
  • Forum Patron Made 500 posts in the forums
  • x6
    Coach One of your tutorials has over 1,000 readers
  • Educator One of your tutorials has over 10,000 readers
  • Regular Visitor Visited Construct.net 7 days in a row
  • RTFM Read the fabulous manual
  • Email Verified

Progress

17/44
How to earn trophies