Ashley's Forum Posts

  • 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.

  • Try Construct 3

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

    Try Now Construct 3 users don't see these ads
  • 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 sucks.

  • 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.

  • In fact, I noticed that in the development documentation of the mini game, there is an weapp-adapter which may be used to simulate the support of the browser environment.

    I had to use Google Translate to read the page, but the English translation mentions:

    The running environment of the mini game on iOS is JavaScriptCore, V8 on Android, they are all running environments without BOM and DOM, and there are no global document and window objects. Therefore, when you want to use the DOM API to create elements such as Canvas and Image, an error will occur.

    This suggests to me it's still not a real browser engine.

    You can have a go at it yourself if you want, but it will probably be a nightmare, because Construct assumes there is a real browser engine.

    Honestly by far the best direction forward is for them to just use a normal WebView.

  • I'd also add: if you choose to stay on an old version of Construct, and you run in to a showstopper bug, you still have a way out: update to the latest version of Construct. Even if there are regressions when you update, you can file issues with us and we'll provide official support and sort everything out.

    If you use engine hacks, that becomes impossible, and basically you're just screwed. That's why it's a uniquely worse situation.

  • Truth of the matter is i burnt myself once by using a lot of Rex' old add-ons back in the day and ever since then i stay away from 3rd party things, even though there's great stuff being done.

    If we were able to fully block all engine hacks like other engines, then it's unlikely anything would have gone wrong in the first place, and everyone could trust that third-party addons will work reliably. I think that would be a much better situation.

  • If in the future a disaster happens because of my own addons, please let me know and I will do my best to help in any way I can, as quickly as I can.

    I appreciate that, but my point is in the scenario I described previously, there is no solution. Nobody can help the person stuck in that situation: we can't, because they use third-party code, and you can't, because the hack has become impossible. They are on their own. The only way to prevent this is to avoid modifying the engine internals in the first place. I know software development is complicated and a bunch of difficult stuff happens over time anyway, but that situation I described is basically catastrophic. It ends in reviews like "I worked for a project for 5 years, a Construct update broke my project, and then they refused to help me". In that situation that's pretty much true, and is the kind of review that could cause major reputational damage to the company, and make other developers think they better not go anywhere near Construct.

    I'd point out most other game engines rely on compiled binaries which are not feasible to modify, so hacking the engine is off the cards. People just use workarounds instead and can still get a lot done that way. The problem of being able to hack the engine is basically unique to Construct because JavaScript has historically had poor encapsulation. It now has better features for that like private fields. I'd quite like to update the engine to use that to prevent the possibility of disaster, and force the use of workarounds instead, which would bring us in to line with how most other game engines work. But it would probably permanently break most engine hacks. That's something I've always tried to emphasize is a possible outcome, and it could happen at any time for any other reason too, including inadvertently. This is why the software industry invented encapsulation and if you ignore the lessons learned over decades of software development, you'll probably eventually learn them again the hard way.