Ashley's Forum Posts

  • If it works in Chrome, it probably means Construct is working correctly - if the problem was with Construct then it wouldn't work in Chrome either.

    I think Brave has very strict privacy settings by default. This may mean it intentionally deletes local storage (confusingly also referred to as "cookies", although the term means any locally saved data). If it does that, then all you can do is change the browser settings to not do that.

  • I'm not sure, possibly - but games tend to redraw every frame anyway, so it's probably not that significant. Either way it's fixed in r364+.

  • Then you could add "unpredictable tick order", "single timer only mode", etc. all to make up the performance difference... and now what's meant to be a simple feature is full of complicated configuration options, which makes it harder to use, unfriendly to beginners, makes the code much more complicated, and usually ends in lots more bugs. It's not worth it. It's pretty much the approach we took with Construct Classic years ago, and it ended in development hell.

  • I tried this out and was surprised to find it is indeed the case - adding a Text object in the editor increases the GPU work, even if you are not doing anything.

    It turned out to be a regression from fixing a bug a few releases ago (#7358). It's just causing the Text to keep redrawing. I fixed it for the next release so it won't use any extra GPU.

  • Try Construct 3

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

    Try Now Construct 3 users don't see these ads
  • I think WebView2 doesn't currently support these window positioning methods out of the box. I filed an issue about it a while back.

    Can I ask why you need this though? It seems a weird thing to do. If you're trying to set a specific window size, it should default to the viewport size of your project.

  • Ashley do you think there is a chance that one day the Addon SDK would supports new "function-like True Triggers" for Timers or any plugin/behavior that wants to implement some kind of "Self-Functions" ?

    The triggers system is not designed to be used for performance-sensitive cases. Basically it's quite slow, but for things like input events, that doesn't matter. The built-in functions are specifically designed to be used in performance-sensitive cases, but they are deeply integrated in to the event engine and so aren't easily extendable. So I'd say if you really need maximum performance, use the built-in functions, but as I was saying before, probably the vast majority of cases don't need extreme performance, so some other solution is probably usually fine anyway.

    I did not expect timers to be this complex :V

    A good rule of thumb is everything is more complicated than you think! For example, want to know how long a string is? It's complicated.

  • It's not 10x the overhead of the entire behavior again, it's just the timer complete check, which is a very small and simple check. If you use triggers it still does that check but internally, and then has to fire a trigger which could well be 100x slower or more than the simple check, which would make rapidly firing timers much slower.

    As I say, it's all tradeoffs, and there is usually no perfect solution. Changing A for B usually means you tip the scales another way and some other case becomes worse. A general-purpose engine usually tries to avoid making any one case particularly bad.

  • I'd really caution against kneejerk reaction to performance numbers like this. It can really send you on a wild goose chase and end up in giving people bad advice.

    To illustrate how subtle performance is as a topic, you can take the "quad issue performance" benchmark, add the Solid behavior to the main sprite being created, and the benchmark drops about 3%. "OMG! Is the Solid behavior slow? Scirra should delete the extra code it's running to ensure maximum performance!"

    The thing is it is literally not running a single line of extra code. How could it be slower then? It uses a tiny bit more memory per instance. That means the hardware memory caching systems are a tiny bit less effective. So it takes a bit longer to run identical code. This goes further: you'll be able to measure bigger differences the more features you add, solely due to the increase in memory usage and caching effects. This is just a fact of computing. Advising people to avoid the Solid behavior due to this overhead is firstly pretty much useless advice, and secondly irrelevant to 99.9% of projects anyway, which don't have such demanding performance requirements.

    I can indeed measure a fairly big performance difference between the Timer behavior and using an instance variable. This is for three reasons:

    1. The Timer behavior uses the behavior ticking system, which for compatibility reasons must preserve a specific ordering. This means internally it iterates a special data structure which has a small overhead compared to iterating a simpler data structure like an array.
    2. The Timer behavior does not merely subtract from a variable. If you do that, you will find the timer drifts off from a real clock over time, because floating point precision errors accumulate. The solution to this is to use the Kaham summation algorithm, which improves precision enough to be suitable for practical purposes. Since it tracks both the current and total time, it in fact does kahan summation twice per tick.
    3. The Timer behavior has more features, like being able to dynamically add timers with a string expression for its tag.

    You can start by subtracting from an instance variable for your timer. But you may well find over time you run in to these same problems. If you want accuracy over time, then you need kahan summation; if you want a predictable timer finish order, you'll need to sequence things somehow; if you want dynamic timers, you'll need a dictionary or some other such feature to track state by string.

    All of those things add a small amount of performance overhead. When you do that with tens of thousands of sprites, the difference can add up and show up as significant results. However JavaScript is still super fast, the Timer behavior really is already about as minimal and optimal as it can be given its features, and I'm sure for 99% of projects it's perfectly fine. I don't think it's right to say "the timer behavior is slow": if you did all the same things it does in event sheets, you'd probably find your events are actually slower still.

    Sure, if you want a buggy and limited version of a timer solely to maximize performance at all costs, then you can do the instance variable thing. I suppose a few particularly intensive games might need that. But the Timer behavior still performs well given its feature set. There is no obvious way to further optimize it without deleting features. No, a "true trigger" won't help, because internally it still has to do the same checks, and that just means it fires the timer a different way. In fact the trigger infrastructure has a high performance overhead (as it's designed for things like input events, not performance critical stuff) and so making it a real trigger may well degrade performance further. In that case you can view the fact it's not a real trigger as an optimization.

    All software is about tradeoffs, and there is usually a fundamental choice: do you want it to have lots of features and work correctly, or maximize performance? If you improve one it will come at the cost of the other. We try to get a good balance with Construct, but if you have a specific situation, maybe you want a different tradeoff. That's OK but it doesn't mean the built-in stuff ******

  • As often as not, I find my use of arrays and dictionary to be tied to objects. Dropping them in a container is great... but I always use families.

    Thus... object picking is required for every object pair...

    Containers are meant to be the way this works - it gives you one Array/Dictionary/etc per instance. Does that not work with families? I guess that might be an oversight in the engine. Could you file an issue for that so I can take a look? Ideally this would just work and not need workarounds if you use families.

  • It looks like the TypeScript compiler removes unused imports by default, which doesn't work for importsForEvents.js, because it adds code after the import later on but the TypeScript compiler doesn't know about that.

    It looks like adding the option "verbatimModuleSyntax": true in tsconfig.json fixes this - I'll add that to the next beta so it's a default setting.

  • I tried it and it looks fine to me. Always share a project! More often than most people expect, it depends on exactly what you're doing.

    I'd guess it's some kind of mipmapping artefacts you're seeing, but that's only a guess without knowing what's really happening.

  • It's hard to comment because it depends on the details of what you're doing. If there's a 1:1 relationship between objects, you can use containers and everything should "just work" without needing extra picking. Even if not, if you can rearrange it to use such a system it may be more efficient. Nested loops tend to be pretty inefficient (as the algorithmic efficiency can be poor - a fundamental mathematical limitation that tends to make it slow in any tool). So if you can do anything that avoids nested loops it should help a lot. For example if instead of "for each bullet - for each effect in list", you can instead precompute the state for the bullet only when it changes and store it on an instance variable, then you don't need to do all that intensive CPU work every tick.

    If you have an extreme performance requirement and the event system just isn't cutting it, there is also the scripting feature - I know writing JS isn't for everyone but at least the option is there for raw code to make things as fast as possible if necessary.

  • There is also a developer who improved this weapp-adapter: github.com/finscn/weapp-adapter/blob/master/README_EN.md

    That link lists 5 known bugs in the WeChat game library, some look really nasty and difficult to work around, and I bet if we ported Construct we'd find a lot more. So I think my intuition was correct - supporting it would be a nightmare!

    There are two kinds of browser tech: real browsers that are tested against the industry-standard Web Platform Tests, and adapters that try to fake it and end up broken in all sorts of difficult ways.

  • is foreach simply duplicating the overhead of a single event, in this case 40k times, while the former is only the overhead of 1 event and the resulting cpu usage is the internal time to generate the sol based on the condition?

    Yep.

    The event engine has some overhead (surprisingly small - enough to perform close to GML compiled to C++). 'For each' requires the event engine to repeat the engine overhead for each instance - it will re-run all following conditions, actions and expressions once per instance. However normal events run all conditions, actions and expressions a single time, but with a single long list of all the picked instances.

    It's analogous to this in code:

    const instancesArray = [ /* 40,000 entries */ ];
    
    // Normal action
    runAction(instancesArray);
    
    // With 'For each'
    for (const instance of instancesArray)
    {
    	const singleInstanceArray = [instance];
    	runAction(singleInstanceArray);
    }
    

    The first approach has the overhead of runAction once, passing 40k items, and the second approach has the overhead of runAction 40k times, passing one item each time.

    So yeah, don't use 'For each' unnecessarily.

  • Nice work! Happy to see a companion plugin in the works. It looks like you've used pretty much exactly the approach I intended.

    To pass more complicated data structures, so far I am passing a JSON string back from the wrapper.

    Yeah, I think that's the best solution. Passing complex data types over a DLL boundary gets very complicated, and JS already has built-in support for JSON, so just sending a string of JSON data is probably the best thing to do for more complex data types. If you need more advanced JSON support in C++ I can recommend this C++ JSON library.

    Some of the commands require handles which cannot be passed back, so I keep them as statics in the wrapper (global now, but should be private in the class).

    Yep, that's fine. If you need multiple handles you can do something like assign them tags and use the tags in the event system to identify the handle, and look them up on the C++ side.

    I need to figure out ways to debug the DLL.

    In Visual Studio, you can set the debug command to run the wrapper app executable. Then make sure the .ext.dll is a debug build of your DLL (just copy paste it over the exported files if necessary). Then when you click the debug run button in Visual Studio, it should start up the app, detect your debug DLL, and then stop on breakpoints you've set so you can properly debug it. (There's also OutputDebugString() and other debug output methods for a console-like output if that helps.)

    It should be possible to set up a workflow where Visual Studio builds the debug DLL directly over the WebView export, so you can change some C++ code, start debug, and it automatically builds, runs and then debugs your new code.