Jase00's Forum Posts

  • ...in the case of "has tags", in one case you just compare a string which is a very simple and quick operation for a CPU. On the other hand "has tags" has to split the given string by spaces to extract individual tags, and then verify that all the provided individual tags are in the set of tags for the given instance. It's probably at least 10x as much work as just comparing a string. It's not that it's slow...It's just that you've used a feature which necessarily includes more complex steps. So if you make a benchmark that absolutely hammers that specific feature, you will probably see something like a 10x difference.

    This is a good example of understanding "under the hood" a bit more so that we can make decisions on what to use/how to structure things.

    Typical usage for any structure is going to perform great, but using "loops" or "many instances", it can be desirable to explore a different approach or understanding as many optimisation tips as possible (many small optimisations for a loop will add up).

    I too went to use tags as a form of identifying many instances within loops, did measurements, and opted not to (not really understanding why it gave higher CPU than other methods). With your insights, had I wrote a bug report about Tag performance, it would have cost my time and Scirra's time to do the bug report process to ultimately end with "this is by design".

    Without insights, I assumed tags was like a small dictionary hidden within instances and "has tags" automates a dictionary "has key" loop and picks objects; Didn't know that it always splits the string when using "has tags" (I've come to find heavy loops with "tokenat()" can be heavy, and try to use "Array Split String" outside of a loop when possible).

    I suppose this means it's ideal to not put "Has tags" in a heavy loop, and if wanting to do different things to many instances with various tags, keep it base level with a handful of "Has tags X", "Has tags Y", and each event can have For Each after this condition, if required (often not required).

  • So I got unhealthily curious about CPU power management.

    [Note: I oversimplify CPU talk here. It's not all about CPU Speeds/GHz, it's also about type of CPU, generation, age, etc.]

    Turns out you can control your processor speed in Windows. Not explaining how. Your own risk if you explore this.

    Doing this stabilised my CPU's clock speed. At worst, only changing +-0.01, but frequently staying completely static.

    This gave far more stable C3 readings, bit jumpy with unlocked framerate but less range with the jumpy numbers.

    This guarantees CPU readings aren't affected by CPU power shenanigans, but is NOT a real world scenario, just an experiment to rule-out CPU power management.

    Maxing out CPU caused unpredictable CPU speed, doing tests that were 10%, 30%, etc, all affected CPU speed. Indeed, CPU is affecting our results.

    The way we measure now, if you let your CPU do it's own thing and fluctuate as workload gets higher, then:

    1. Running one test at a time, your CPU would reach differing speeds during each test. Less accurate results. (I did this method for many months).

    2. Doing tests with "2 Groups enabled and compare CPU Profiler", CPU gets even higher than doing singular tests (The difference in CPU will also be less clear, explained below).

    Notably, the higher your CPU speed, the harder it is to observe a CPU difference between two tests. No use fluffing up both tests with a "For" loop, as CPU will just boost its speed and further decrease the CPU difference. If you had an imaginary 100GHz CPU, none of our measurements would even show a 0.1% difference and we wouldn't know there was a difference that could affect the average player with a 2.5GHz CPU. (Always test on lower-powered devices if you aim to target them).

    Measurements with fixed CPU speed

    I took the same example I posted in this topic: "Dummy Return function" vs "For Each loop", 1000 objects every tick, one test at a time.

    Had to approximate measurements still, since it's never a perfectly-stable number (esp with higher GHz).

    I also tested various clock speeds, in all 3 Framerate modes (VSync, Ticks Only, Full Frames) and noted down the CPU % for VSync, TicksPerMin for Ticks Only, and FPS for Full Frames.

    Why did I do this? I don't know. Maybe someone finds it interesting or can extract some information from it, knowing it's a fixed stable CPU speed on every measurement.

    --- No CPU Restriction ---

    (Idle with preview open using 2% CPU, GHz floats between 4.15GHz to 4.21GHz).

    (Very approximate as CPU speed changes often).

    For Each

    CPU: 13%

    TPM: 1000 [~4.24GHz]

    FPS: 940 [~4.22GHz]

    Return Fn

    CPU: 8%

    TPM: 2250 [~4.24GHz]

    FPS: 1840 [~4.22GHz]

    --- 2.28GHz ---

    For Each

    CPU: 20%

    TPM: 560

    FPS: 545

    Return Fn

    CPU: 12%

    TPM: 1220

    FPS: 1000

    --- 1.48GHz ---

    For Each

    CPU: 30%

    TPM: 360

    FPS: 345

    Return Fn

    CPU: 17%

    TPM: 760

    FPS: 670

    --- 0.98 GHz ---

    For Each

    CPU: 43%

    TPM: 225

    FPS: 218

    Return Fn

    CPU: 25%

    TPM: 475

    FPS: 420

    The kicker? This is all absolutely pointless lol. Regardless of the CPU doing its thing and adapting, or a fixed CPU speed, I still have the same lesson I learned from when I whipped up the original c3p file in 5 minutes.

    With my ignorance these past months, I measured mainly by comparing CPU, running 1 test at a time, re-running a few times to see if any sudden differences occur.

    Any test I have done, has benefited me in my C3 journey:

    - Placing conditions in ideal ways

    - Knowing when/when not to use conditions/actions

    - Knowing the expensive actions

    - Finding that "comparing longer strings can increase CPU insigificantly, but can add up qucikly if reading many long strings in large loop, so opt for a number or shorter string".

    - Learning that getting a value from a deeply-nested JSON gets expensive quickly, not for 1 "Get" but observable in a loop - but keeping nests minimal with hundreds of keys is fine.

    - JSON performs the worst in many read/write/iterate tests when compared with Array/Dictionary/JSON, but is is still powerful and an ideal way to store complex relational data.

    - Understanding "Pick by UID" or "Pick Child/Parent" is the categorical optimal way to pick an object, again, especially if in a long loop - Instance Variables could eat away at CPU... (But now there's exploring "Evaluate expression" which may render Instance Variables more ideal to use in a busy loop

    These all feel accurate, and if inaccurate or misremembering... Can go and measure.

  • Thank you Ashley.

    I spent a lot of time measuring, I kept in mind the very quote in official performance advice: "Measure measure measure"...

    I thought this thread was great. No guessing, folks sharing screenshots and project files. I've remade some tests and observed similar results.

    FWIW even if some things learned here aren't ideal in most cases, like usage of "evaluate expression", it's now another direction to consider if looking to optimise. I knew of things, but didn't expect the results in the specific cases shown.

    It's unfortunate we couldn't learn some confusingly-interesting insights about some mysteries of results; never an obligation, just strong curiosity from us all. I feel the strong pushback against measurements was surprising considering project files were shared for people to check themselves, but presumably you are considering general users that see this topic/the thread seemed too authoritative on how to design events when they could trip up others.

    ... and all modern processors have advanced power management. This means it's possible to make a benchmark and slowly increase the amount of work, and then at some point the CPU/GPU measurement suddenly drops a lot. You might think "OMG! Adding more work made it faster, WTF?" but all that happened is you created enough work for the processor to step up from slow, low-power mode to fast, high-power mode.

    I am not as familiar with CPU power management (is it just the GHz reported by your CPU or is there more to it?), but observed my CPU GHz during tests and see it rise and lower relative to CPU usage of C3, although flickers +-0.2 whether at 10% or 100% CPU with FPS dropping to 10%

    Regardless of, looking at two tests running, the difference in CPU between the two tests maintains a fairly consistent distance, even if inflating tests fairly with a For loop. It feels logical to explore the winning test, even if 5% or 10% difference (ofc depending on the initial test). Turning off VSync and running 1 test at a time would be further confirmation, surly?

    Admittedly I often go with just CPU comparisons that don't max out, it tends to work for me when implemented into real world project. E.G. the "dummy return function" I implemented for a system I've been trying to solve for months, from 10% down to 2% CPU (setting opacity of about 200 objects with a wacky equation pulling from various places related to that specific instance - For Each was my go-to method most of the time).

    It's a presumption but I figured the types of optimisations being looked at in this thread are more for event systems that can "grow" (i.e. wanting more enemies active or something, the more the better). Sure it's working great with 50 or 100 enemies maybe at 30% CPU (knowing you need to keep things lite for other gameplay CPU tasks), but then to find that using a different order of events/different condition could grant an extra 100 enemies or more without affecting anything, is curious to learn about.

    Granted some benchmarks are overkill, and it's easy to be quick to think "HEY this 10000 iteration loop is slow!". But I think there's been some realistic examples in this thread.

    I'd also note running events once with N instances picked is almost always more efficient than running events N times with one instance picked, because the latter repeats the overhead of the event engine.

    Can't deny I'm curious why the "dummy return function" is more optimal than a single event with a "For Each" loop (I posted earlier on this thread), though I'm certain it relates to what you noted here. I presumed calling a return function when many instances of a Sprite are picked, would be calling a function many times, plus running the return function's "pick by UID" and such (altho fast to pick by UID, I thought it's still "more work" dealing with a function and an extra condition). It's not like functions behave like custom actions and pass the SOL, so... No clue!

    Even Construct's event blocks, with the overhead of the event system, is so fast that one benchmark we did a few years ago showed Construct events being 5x faster than GameMaker Language in VM mode, and still nearly as fast as GML when compiled to C++!

    This was a cool benchmark and never left my mind!

    Causing people to do contrived, inconvenient things to their projects that are entirely unnecessary.

    It'd be a shame to not see this type of thread pop up if folks worry about leading people down the wrong path - it's really engaging, but I understand the concern if that's the case. Hopefully if anyone shares something, they write a warning, and I'd like to think newcomers wouldn't be racing to learn about optimisations and ignore the overwhelming confusion of this thread until they've learned more

    This kind of thing comes up so much and seems to mislead people so consistently, even after explaining the pitfalls, that I do wonder if it would be better just to get rid of Construct's timer-based CPU/GPU readings. However they are still useful for general "is it low or high" type readings, for example knowing whether you've maxed it out or not, so I don't think we should get rid of them.

    I know you said you won't remove it, but - I LIVE in that profiler!! It has guided me to mistakes so often. I've optimised 16k event project from 40% CPU down to 17%. It's helped tremendously with measurements. Even if I'm doing it wrong, whatever I measure and feel is optimal, I implement, CPU goes down - This helps reassure me my game will run well on low-end devices, even if a little bit more. (Gotta get the old integrated graphics laptop out - worked moderately well before, must be great now!)

  • Jase00 The nesting of the events doesn't seem to affect the speed significantly.

    So far the fastest way to do things is to have the loop as the last thing after picking stuff, or you can use compares after the loop as long as they are in sub-events.

    Thank you for measuring. I was so certain I've read in the past about nesting subevents being a crux, but maybe it was a very long time ago... or I imagined it... Cramming in so many tidbits, maybe misremembering a few!

    It's great to know, glad I don't need to investigate and refactor things related to this!

  • What are the chances that the way the system handles these comparisons under the hood, which currently leads to these results, could change—so that another approach becomes more efficient, or that what is efficient now simply turns into the least efficient? What are the patterns? Do they exist?

    I feel it's less likely to change, unless Scirra find ways that provide exact same results but with boosted performance (but imagine the risk behind that - In a rare case someone uses events that gives a different result, could break many projects).

    Or as I noted somewhere before - Say there's ways to boost these comparisons under the hood, but requires a tiny change to the base of the conditions (e.g. "Sprite - Compare Instance Variable" needs a tiny change to check if a "For Each" is running, or to check if only 1 object is picked, so that it can skip picking checks (assuming this is what is currently happening)), it could bring down performance "generally" now that all "Sprite - Compare Instance Variable" actions are now doing this quick check, which could add up quickly, especially when it's such a common condition used probably 1000s of times in a large project where this "quick check" may not be relevant since those events do need to fire in the current way (rendering that "quick check" useless in most cases, yet still has to do the check and use a tiny bit of CPU).

    With the above scenario, personally, I'd love to see more Actions/Conditons added to cater towards both scenarios. I wouldn't see it as bloat, I'd see it as more pathways to cooperate with how C3's runtime works. Whether it means a new "For Each (No SOL)" (as a random nonsensical example) or various new actions/conditions to be able to keep things readable (it'd look at lot less readable in events with many "evaluate expressions" with no Sprite icons and such), then I'd be all for that.

    Of course, if Scirra discover any performance boosts they could implement, I'd imagine they'd race to do that, especially if it's not a fundamental change, or if the boost is significant enough to risk breaking older projects then they may trial it in a beta.

    But I wouldn't fear much of this information suddenly changing overall. And if it did, it could only be for the better - But I see your point, should you sift through your project (or the particular systems within your project that you wish to get maximum efficiency out of), and start replacing things with "evaluate expression" where possible... or wait it out in case Scirra hatch an idea?...

    I suppose waiting for some insight would be ideal, there's plenty to work on elsewhere in a project! But if desperate for those CPU gains, can implement things in this thread. Even if Scirra made some alternative boost in an unexpected way (e.g. a new conditions/action/event block type), then we would migrate anyway if we seek those gains!

  • Thank you R0J0, I will study these results and your explanations deeply, it's very useful to myself and clearly many others.

    It's interesting the best performing result is a nested subevent - Whilst I haven't measured it exactly, I seem to have it in mind to "try to avoid nesting subevents where possible" (although I barely follow this since the gains of subevents almost always outweigh trying to lower subevent nests).

    Perhaps there's truth to trying to avoid too much nesting with subevents, but maybe in the case of a loop, less important since it's tackling the specific nest and subsequent nests? I presume Sub-Events function similarly to JSON nesting, where the more you nest, the more CPU usage needed to get the deeper nest (i.e. getting a JSON key at A.B.C.D.E.F is more expensive than getting the key from A.B, even if there's many keys present at A.B and just 2 keys present at A.B.C.D.E.F), plus event blocks have the added complexity with storing SOLs at various levels and reloading SOLs and such.

    ...More things to measure!

    Imagine having a 6,000-line project and needing to refactor a bunch of things that could have been done right from the start, just by realigning items.

    ... heh yeah, imagine! My project's at 16,000 events, no big deal!...

    ...

    ...send help... possibly send help to Je Fawk and a number of others too...

  • Another tip - Again, measure, but this one I know is true, and measured before writing this post, and have provided c3p to share.

    You can get a higher-performant For Each by using... Return functions! (Again MEASUREEEEE for your case!).

    Explaination

    Much like how you can do "Sprite n > 5, set Sprite opacity", and it applies to each Sprite without needing a "For Each", you can utilise this by using return functions, as they too run for each Sprite, without needing "For Each".

    I measured:

    1000 sprites, 500 have n = 0, 500 have n = 10

    Two test keyboard keys to hold:

    A Key = Sprite n > 5 , For Each , Pick Child , Set child opacity to 55

    S Key = Sprite n > 5 , Set Sprite dummyVar to Functions.DummyFunction(Sprite.UID)

    DummyFunction: Pick Sprite by UID , Pick Sprite Child , Set child opacity to 55

    Now, this seems wild - The "dummy return function" seems way more busy, and has to run a function, and pick the sprite via UID...

    ... But the results?:

    For Each - CPU 13% (Unlocked Frames: 900fps)

    Dummy Return - CPU 8% (Unlocked Frames: 1800fps)

    MAGIC.

    C3P below:

    drive.google.com/file/d/17CTtsNiO59uh5h8pjgY_4kTNPj6ooRQI/view

    (I would have included some clearer checks like a "Children updated" counter, but wanted it to be raw performance, but I quickly checked debugger for values being updated only for the last 500 children, and indeed it seems correct.)

    (Note: Be careful before rushing to implement this everywhere. Whilst I am using this in places and haven't had issue, I have a fear of "reaching call stack", which you may have seen when you run a function too many times in 1 tick - Perhaps this gets reached if over-using functions per tick, although I am not sure.)

  • Imagine having someone who really understands how things work under the hood, to explain it…

    But I guess the answer would be: "Don’t worry about that."

    Lol it be that way occassionally, but I find it goes that way when it's random fluff and filler. I find that Ashley often posts insightful stuff - Often I am Googling a random specific action, and encounter all sorts of insights throughout old forum posts. Sometimes it's interesting to Google something vague, like "Construct 3 Wait" or "Construct 3 Tween Performance", you find lots of valuable information.

    My slight fear of a reply would be "measure" or "would this affect real world scenarios", but I feel the difference here is:

    Measuring IS helping, but it's opening a lot of "why" questions - Why "this" order of events (especially with For Each sometimes being performant when higher-level). So long as we know why we are choosing to use "evaluate expression" and such, even if it's an odd quirk, then we can adapt our thinking to work with C3, and become powerhouses in optimisation.

    "Real World" I like to hope this thread is showing it, particularly with Je Fawk, hopefully my own case, too. I spent a lot of time to eek out performance where it is needed (and I avoid micro-optimisations like "Should I use Bool or Number for my one-off tiny return function" or something). I still hope to find new methods to optimise, and indeed this thread has taught me a lot.

    "For Each", whilst I understand a lot tend to avoid this, you sorta find that you need it for specific cases, thus end up using it a lot "at the end of a list of conditions", or just before the end - Such as: an event block evaluating a bunch of stuff for a Sprite, and there comes a point where you must pick this sprite's child in a hierarchy, so you must use For Each - Totally fine, since all this "For each" is doing, is picking a child (rather than expensively evaluating the entire event block).

    Thanks to this thread, I will be hunting down those "for each" blocks, seeing if I do indeed have some subevents with a quick "Sprite Inst Var = 5", and change this to "Evaluate Expression", measure, see how much improvement is gained.

  • > BTW fun fact, if you set opacity to a decimal number, I recall it eating CPU far more. Makes sense in some ways, C3 gotta convert it to int, but I recall it was a measurable different in cpu. Worth noting if you make your own opacity fading systems, better to wrap your opacity result with int() instead.

    Very interesting, thanks for sharing!

    You too quick to reply!! - I wrote my post, then went to measure just to fact-check and report back here, and I can't repro - I am certain I encountered this about a month or two ago, and kept flicking back between "int()" and not, and seeing a noticeable difference, hence why I posted this with confidence. I measured:

    1000 objects, setting opacity to decimal or not, seems to give same CPU either way. For each, or not... visible, or not... all same result.

    ... Either I'm going crazy, or it's a project-specific setting (WebGPU/Worker/Something), but hey, worth keeping in mind, if you ever wonder why your huge opacity system seems CPU-hungry; somehow this stuck to my mind after long experimenting/measuring a while ago. I'll update if I figure out what the exact scenario was in my main project.

  • Very insightful read, learned a lot! But also feel not 100% in tune.

    I suppose in some ways, it makes sense - when doing "Sprite inst var check", even if it's a For Each, it may be doing the typical Picking stuff, even though it's within a For Each loop and therefore only 1 is picked.

    Whereas "evaluate expression", doesn't do any picking, so using it during a For Each loop, it's taking away the task of picking, whilst still indeed cycling each sprite in order to get the correct variable for the evaluate.

    If this is true, perhaps C3 could be designed to take into account whether a For Each loop is active for an object type, therefore prevent the redundant picking and save some cpu... Although I wonder if that type of check could slightly worsen performance overall for any For Each loop, which wouldn't be ideal... Perhaps it's better to fully understand the exact mechanisms and work with it - but after a decade, never thought Evaluate would be extremely useful here.

    I seek to eek as much performance as possible in certain areas of my project, where the more performance, the more this system can be utilised (e.g. Event-based particle system, UI). Indeed a small 5% cpu gain could seem pointless in real-world scenarios, but that is a major difference for specific systems a dev might be trying to design.

    EDIT: Measured but this may be incorrect. BTW fun fact, if you set opacity to a decimal number, I recall it eating CPU far more. Makes sense in some ways, C3 gotta convert it to int, but I recall it was a measurable different in cpu. Worth noting if you make your own opacity fading systems, better to wrap your opacity result with int() instead.

  • Interestingly, there may be bleeding-edge support for direct tcp udp connections, via Direct Socket API, although I have 0 knowledge on this, and unsure on the concept of "isolated Web apps" (does this count when C3 is exported to exe, or when using "install as app", or neither?)

    developer.chrome.com/release-notes/131

  • Thank you for your understanding reply!

    We marked it deprecated in r421 back in January

    ...oops, I missed that. Not an abrupt suprise after all. I seemed to have confused with the various community chatter where some of it is "What if X gets removed, prepare!" and some is "X will be getting removed, prepare!". As with r450, indeed a lot was going on at once.

    This also came up on Reddit and I explained in much more detail why we've done this in this comment. I won't repeat what I wrote there here, so do go and read that for the background on this.

    Thank you, and I fully read both reddit posts. I suppose a number of points are echoed in what I had written originally, like the experimental flag thats not recommended - It's just a shame we can't "choose" to risk it for a possible outcome we desire. I wonder about "--in-process-gpu" - Isn't it a Chrome flag, so should work in any Chrome engine, or is it that WebView2 is just built different for that one specific case? If there were a way for WebView2 to have this flag, and devs can choose to run the risk, then, that'd be a major incentive imo

    Broadly speaking it is time to move everyone over to our own desktop export options. The maintenance work is a key consideration as we are a small company with limited resources, and maintaining two options for desktop exports is a significant drain on resources, particularly given how tricky NW.js is to maintain and customize.

    Completely understandable - One query: LTS is supporting NWJS for another year or so, does this mean build server gets new NWJS versions, Steam plugin gets new SDK update still, and any bugs to C3's NWJS plugin introduced from a new NWJS version would be looked at? Or is LTS frozen in time with no added NWJS build versions, no fixes if C3 reacts buggy to a newer NWJS version? If the former (which I'm thinking is not the case lol), wouldn't it be good to keep NWJS until the LTS version has finished, since workload remains the same and is just excluding the fixes added to LTS from main C3?

    Oops, that shouldn't happen - fixed that for the next beta.

    ... I gotta quit with walls of text, I apologise. This was the main crux - I presumed too much from this error, I thought it was a strict "yep we have ripped it out, you gotta remove NWJS plugin if you want your projects to remain alive", which was painful, as mentioned with the desire to test my project with r450 but unable to. Roll on Tuesday!!

    I am well aware how difficult these transitions can be, and I can assure you we absolutely plan to stick with WebView2 (and our other desktop export options) permanently, and would only change technology again in extraordinary circumstances.

    I hear your commitment and it is reassuring. Your community cheered loud in that Steam thread for you (inc myself and I argued with someone being unhelpful there, too bad it was more of a public-facing forum and not the dev area which is restricted), we do believe in you.

    I do hold opinion that bundling the browser engine is preferable. I have nightmares back in C2 where Audio broke in all exports in latest browsers, and Scirra fixed this, but everyone had to re-export their projects. I like the possibly-ill-perceived stability of a bundled browser (its why I mention Fixed version of WebView2 being desirable within C3, rather than a separate guide to build it), where at least things like that old C2 Audio issue won't strike (but I am also aware a simple windows update can have a similar affect. Can't win really. I suppose if had to choose, I'd go the "blame windows update" path, as it feels like it's more likely to have affected other games due to a win update).

    It was different in the past, but at this point, the entire node.js component of NW.js is basically redundant. It provides a large API surface, but geared towards server-side development.

    I find this interesting and will take it as a warning. I have explored node modules and found various useful things that help with communication to the desktop, to other apps, etc - Though I am relying on existing npm modules whom have done the work of coding this and prob used C++ and such - and maybe most are indeed Server-related usage, and I only found the odds and ends that do light communication with desktop.

    Our new wrapper extension model basically means you can write C++ code that directly interacts with JavaScript via a simple messaging system, which makes it much easier to integrate custom native code.

    I really hate to ask, but I'd love to see a guide/blogpost on writing a C++ addon for WebView2, even if you're not explaining the C++ fundamentals, but just the process. Inb4 you already done this and I missed this, like I missed the year-long warning of NWJS deprecation. FWIW I read all blog posts, even some chapters of your latest Typescript entries, when I'm a noob at JS and not even planning to touch TypeScript, and yet I pick up a number of things that I then learn and find useful.

    I do apologize for the inconvenience of these technology switches, but in the long term they are absolutely necessary. I know they are painful and these are not decisions we take lightly. As I mentioned in the r450 release notes, Construct 3 has been around for about 8 years now, and we need to think about where we'll be in another 8 years and longer. These kinds of decisions are difficult but important to ultimately ensure Construct is still a great product that keeps improving and remains competitive.

    It's ok, it is less painful after learning that preview error is a mistake, I again apologise for being so reactionary. I was a week late to checking the update out due to stupid life issues, then see I can't begin to preview at all, and just thought here we gooooo another fork in the road, nobody else reported it it seems, so it must be fact that we gotta nuke any projects we wish to upgrade beyond r449

    I feel I will pursue NWJS still. I keep an eye on literally everything that goes on and I drift to things that unlock further functionality or are more optimal, so I will turncoat as soon as I find WebView2 appealing, even if it's due to some other new features that make file handling/management/listing/creation easier (these are things I had been exploring with NWJS, accessing further "List Item" parameters for subfolders or "list only specific extensions".

  • Use "Add key" instead of "Set key" for the dictionary.

  • Try Construct 3

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

    Try Now Construct 3 users don't see these ads
  • EDIT: Rewrote to be more concise, despite being a wall of text still.

    Is NWJS "fully" removed on r450?

    If I save a project that has the NWJS plugin, then upgrade to r450, then preview, I see error along the lines of:

    runtime.js:1 Uncaught (in promise) TypeError: this._runtime.IsNWjs is not a function

    I presumed that NWJS was deprecated, so that you can still use your project with NWJS if you had the plugin added, but most stuff is "hidden away", so no exports and no adding the plugin. But judging by this, I take it that NWJS is removed from the core of C3 in whatever way C3 used to detect NWJS? This would mean all past projects with NWJS plugin cannot be run on r450 until it is removed?

    I also presumed that we could still choose to use NWJS, but no support provided from Scirra, so we would be on our own to export via HTML5 and mash it together with NWJS if we wish, or if a new NWJS version broke the deprecated NWJS plugin, then Scirra won't support this.

    If it is intended that existing projects with NWJS plugin must remove the plugin entirely to use their project:

    This feels rough, my choice to use NWJS has backfired and stalled me. I abruptly need to remove NWJS plugin, which is deeply ingrained into my project. Maybe there were warnings (or maybe it is unintentional that projects are erroring), but oh mann I was eager to try the new Text improvements in r450 (which deeply affect my project when I unexpectedly encountered this when migrating my project to WebGPU-only effects. I had worked around it for a month or two, with poor results, but didn't want to pester Scirra, and assumed it was a Chrome bug, but then got excited to see the update).

    Repeating what is known - WebView2 is not quite there yet. Very close but not equal to NWJS. Steam Overlay (fallback not ideal), supress the "fullscreen/Mouse pointer lock" popups. WebView2 feels more restrictive whereas NWJS feels more adaptable. I liked how C3 integrated fixed builds for NWJS, but WebView2 we must follow a guide externally. An advanced user may enjoy other aspects of NWJS, such as using Node Modules for further enhancement of their game (e.g. enabling Toast notifications on desktop).

    NWJS's Steam Overlay relies on an experimental Chrome flag, a risk that some players cannot play or get crashes. Personally? No hesitation, I'd enable that flag, it's important for my case. I'd rather my game behave like a standard game and not split into many processes, I want Steam Overlay/Achievement popups, I want OBS to behave as it does with any other game that is a single-process (Yes you can still record a window, but, if there's a choice to make it consistent with how OBS detects other games, I'd choose that). I don't care for any performance/stability reasons to avoid that flag, it is an optional flag that we can choose to use, it works fine for most, but if it didn't, you can offer a 2nd build on Steam with the flag disabled. All doable with NWJS, but WebView2 prevents all of this, there is no choice, and that sucks bad.

    I feel distrust towards WebView2 and slight distrust on following the Scirra path of EXE solution (again, only if this NWJS plugin issue was intentional and expects us to remove the plugin to preview our projects). Had I used Electron years ago, I wouldn't be suddenly panicked about what to do to update to the next beta. I'm thinking "what if" since this is now occurring: What if Scirra decide WebView2 is not the path to go with, due to slow rate of Microsoft response to WebView2 bugs/requests (such as the slow response to the "fullscreen" popups), or if Microsoft/Valve both say "No" to resolving the Steam Overlay issue and C3 cannot ever have Steam Overlay unless changing EXE solution - Some plausible situation like this happens, then Scirra move on to the next EXE solution, repeat this loop of killing projects until they remove the old plugin, etc. Such a time-sink for us trying to work on our games (and I empathise with the headache this causes Scirra to deal with). Where I mention distrust to Scirra's EXE solution, this means that I currently seek to find a way to continue using NWJS and am opposed to moving to WebView2, at least as of now. Still love practically every other decision Scirra makes, I am not going anywhere.

    Another point of distrust to WebView2, was what we saw with WebView2 being cross-platform - a promise then a rejection, so it's not far-fetched to distrust the future of WebView2.

    I also struggle to find "real-world" examples of WebView2 games on Steam, I think I encountered one months ago, but it's so rare to find, whereas NWJS has indeed had success out there with big named titles from RPG Maker and such. I can't peek at reviews to gauge what players experiences are like with WebView2, I can't Google info about it, it's way too new/experimental.

    I think I've missed the point of why NWJS is no longer viable. It had been for years, since C2 days. I recall FileSize being the downside, but surly I am mistaken - FileSize matters for browser-based games, but rarely for Steam games.

    I want to crack on and work on my game, but I'm rewriting stuff to fit in with SDKV1, WebGPU, and now WebView2. I'm happy to, and really try to recognise Scirra's goal and follow the philosophy. SDKV1 feels necessary, WebGPU provides a tangible benefit, but WebView2 is a downgrade as of now.

    LTS will support NWJS still and have build server and presumably NWJS gets new versions added and bugs that arise will be fixed for LTS, the workload for Scirra still happens for the duration of LTS to support NWJS but the primary version of C3 won't benefit from this work.

    I could use LTS version, it's a great addition, but what awful timing for my case with the WebGPU Text improvements, it's vital I can utilise that, especially after all the months of effort of mangling my project to not use WebGL effects.

    Last lesser point, it feels antithetical for WebView2 to rely on C++ addons. I don't believe there's many addons for over a year, besides one companion addon? I imagine it's more difficult than the typical addon to create, and may have less chance of attracting community members with the skillset to make these. I'd have thought encouragement of Node.JS would be the path to promote - More JS shenanigans to mess with and unlock more power for desktop builds (not to mention the huge repo of nwjs modules that exist now).

    The community has aided me to understand that, you can still use NWJS, just do it as JS event blocks. This is viable, but must I really have to re-interpret every NWJS action (as I made essentially full-use of NWJS plugin) into a JS event block/function, and figure out the more complex ones like loading into a Binary Data object (which again, heavily used in my case). Whyyy when it was working so fine just one version ago. :(

    My apologies for the reactionary post. I fear the workload of having to move to FileSystem plugin, which may not be a simple swap-n-replace since NWJS was sync and FileSystem is async - And it feels like work that will just result in a downgraded end result when it comes to exporting.

  • Say you have array that is 10x3 (e.g. Each X is Y0 = Apple, Y1 = Food, Y2 = £5... And then 8 other foods in the array).

    Have your array be 10x4 instead, and the first Y0 is now a number (then Y1 = Apple, Y2 = Food, etc).

    For each X, set Y0 to random number.

    Sort via X.

    Random shuffle that keeps relationship!

    ... I think, just guessing.