R0J0hound's Forum Posts

  • We already know a system compare or evaluate is faster than picking events, but here is a performance test of variations of looping with two picking events. Half the instance's "a" variable is 0 and the other is 1. I also made them all invisible so we are just measuring how long the events take.

    It measures and averages the timings of each event with 10,000 instances. Made in C2 but tested in C3 r449.

    dropbox.com/scl/fi/xd5mz5kofj7c67ftbrhvo/event_timing.capx

    We are mostly measuring the performance of the loop and how many instances are being copied in the SOL. "First" and "Last" are taking advantage of some optimizations whereas middle does not. I tried variations of using sub events but that doesn't appear to be significant.

    The for each appears to roughly do this under the hood:

    save SOL copy
    for each sprite
    -- if(following conditions or sub-events do picking)
    -- -- load SOL copy
    -- select nth sprite
    -- do stuff

    The if may be if the loop is the last condition and sub-events don't do picking but I didn't dig too deep

    The "load SOL copy" is the heavier part.

    With the "first" tests it's not heavy since the copy is just all the instances so construct sets a flag so nothing needs to be copied.

    The "last" tests are able to skip the loading since the SOL isn't being modified past that point.

    And finally, "middle" is the slowest since it has to load a copy of half the instances every iteration.

    I can almost see a way to avoid loading the list of picked instances every iteration, but it's not my code. I'd say it still may be worth filing a report in regards to how slow the middle case is.

  • Even after looking a the runtime's source in C2 I'm having trouble coming up with consistent rules. C2 and C3 run similarly but C3 is faster.

    Anyways by default the picking system duplicates object lists a lot. Think at the top of every event block, with every iteration of a loop, and probably with some other special events. That is slow with a high amount of instances.

    But there are two major optimizations being used:

    1. When all the instances are picked then copying just sets a flag so nothing needs to be copied which is faster. That is why having the "for each" as the first condition is faster in 3 and 4.

    2. When exporting it keeps track if any of the following sub-events modify the SOL. And if they don't there's no reason to copy the SOL after that point. That's why 2 is fast. So effectively the loop would need to be the last condition in a block, and the sub events would need to do no picking to take advantage of that.

    The short version that's easier to remember is you'd want the loop to be the first or last condition in a code block. You can have non-picking events after the loop but it needs to be in a sub-event.

    The high cpu usage in 1 is from the worst case with no optimized code paths. And oddly enough number two performs just as bad if the "system compare" was directly below the loop instead of in a sub-event.

  • That's strange for sure. Actually those hidden characters are in that "$‎¥‎" you posted. Using js I'm getting a length of 4 with that and the unicode value of each value is (gotten with the js method .charCodeAt(n)):

    36, 8206, 165, 8206

    36 is $ and 165 is ¥.

    So what is that 8206 (or in hex 0x200E) value? After searching a bit it might be this:

    "Control Character left to right mark"

    en.wikipedia.org/wiki/Implicit_directional_marks

    It's used to switch ordering from "right to left" languages to "left to right".

    Still it's not clear to me why that's added. In your posts it's after the ¥ and $ in quotes in your last post and only after one ¥ in your fourth post. It's not after your other uses of that symbol.

    One way to quickly check for invisible characters is with the browser console. When you type in in a quoted string the invisible characters are shown as circles you can hover the mouse over to see what they are.

    Alarmingly there is a lot of invisible characters.

    invisible-characters.com

    Guess you could bring this up as a suggestion to have a way to show hidden characters in construct to aid with debugging.

  • I figure that setting opacity is just setting a number so it’s light to set it twice. Else does a bit more than just run if the previous event was false. I tend to only use it in simple cases like you did since how it behaves with more complex picking isn’t entirely intuitive to me. However, it may be mostly a personal preference to do things in as few events as possible.

    I was almost going to write out some pseudocode for all that the events are functionally doing under the hood but I’m not sure that would be useful. I’ve only perused the source for the picking system in the C2 days, but mostly regard it as a black box so I don’t know all that’s going on other than basic behavior.

  • You’re probably just encountering overhead from the picking system. Picking involves juggling lists of picked object with one known optimization if all the instances are picked.

    So in your case it’s slower to filter 1000 objects down to 500 before looping over it for collision checks, instead of just looping over the 1000 objects to do collision checks. So I’m guessing one case is able to be optimized to be faster over the other?

    In that particular set of events you could try some other things to improve it. One could be to do “sprite overlaps sprite2” instead of “sprite2 overlaps sprite” since you are only dealing with sprite.

    You can also eliminate the else, and the loop for that matter. Loops are known to have a fair amount of overhead.

    Sprite: is visible
    — sprite: set opacity to 33
    — sprite: overlaps sprite2
    — — sprite: set opacity to 100
  • The lines may just be an artifact of the text renderer on Linux or in wine or something.

    To see if that’s an actual character you could open it in c3 and run a len() on that to see if it’s counted as a character. Theres probably other ways to check.

    Typing text is a fundamental feature of edit controls and really it’s controlled by the operating system that the browser and then construct uses. If it is adding extra characters it’s something outside of construct most likely.

  • You have options though.

    One way is to get a list of words (you can find and download lists of words online). Load that into a dictionary, and just check to see if the typed word is found in the dictionary or not.

    Another option is to find a js library to do it for you. Although attaching js libraries have their share of annoyances to use.

    Found this one that requires minimal boiler plate code. It will also offer some possible suggested corrections too.

    github.com/cfinke/Typo.js

    dropbox.com/scl/fi/cgdne40or7tx39wy518ih/spellcheckjs_c3.c3p

  • I just have a normal English keyboard so I used the window accessory “character map”, searched for yen and copied the character from there. But I imagine I could have copied it from anywhere and it would be fine?

    You could use \u{a5} instead of ¥ if the text input accepts escaped sequences.

    If you can consistently reproduce it then it would make it simple to make a bug report. But sounds very odd if something as simple as typing and deleting characters causes issues.

  • It’s working for me.

    I added a sprite font, added the yen to the character set, then drew it in on the image. Then the set character width action worked fine.

    Example:

    dropbox.com/scl/fi/owlta99xjl1kd5f761sbr/spritefont_spacing_test.c3p

  • I ended up taking a dive looking a lerping like this in a framerate independent way.

    In a frame dependent way we'd use:

    set a to lerp(a, b, f)

    Where f is a value from 0 to 1. The speed it moves will vary according to the framerate.

    The quick improvement is to just multiply it by dt. 60 is multiplied by it too to make it have the exact same motion as the non-dt version.

    set a to lerp(a, b, 60*f*dt)

    With that it's largely framerate independent and approximately follows the same motion path no matter the fps, but the path can vary a bit and overshoot.

    For full framerate independent version I've found two versions online.

    One is from Ashley:

    set a to lerp(a, b, 1-f^dt)

    and one from Freya Holmer:

    set a to b+(a-b)*exp(-decay*dt)
    or
    set a to lerp(a, b, 1-exp(-decay*dt))

    Where decay is a value from 1 to 25.

    But the rate of change is different between the two. Surprisingly if we tweak either of them to use a f value of 0 to 1 and shift it to match the f and f*60*dt version we get the same equation:

    set a to lerp(a, b, 1-(1-f)^(60*dt))

    Which will give the exact motion no matter the framerate.

  • 1) One way could be to create an invisible sprite at every grid location that isn't occupied and pick the closest one. Likely you could do that with collision detection and a double loop to loop over all the squares. It would give you an opportunity to understand some extra nuances to picking since you'd need wait or something to let all the newly created objects be pickable before picking the closest. Or just keep an invisible sprite at all the grid positions and not have to worry about it.

    Or if you prefer traditional programming logic you'd just loop over the center positions of all the empty squares, and calculate the distance. While doing it you'd keep track of the position with the closest distance, and update that if another is closer.

    2)

    It's mostly a matter of adding a condition i imagine. This is the power of events. If you want to do something conditionally you can add another condition.

  • You can look in the manual, namely the system expression section. But in general you can use any sort of math or combination of expressions as parameters. Construct supports expressions with text and numbers but you can convert between the two.

    There's even a tutorial about the uses of dt.

    construct.net/en/tutorials/delta-time-framerate-2

  • For the grid you can set the position to int(x/64)*64+32, int(y/64)*64+32 to center it in the grid. Notice the +32 which is half the grid size. I used int instead of round because it works better for this case, but you can try both to see what i mean.

    For the rest of your question I'd recommend utilizing families and\or representing things as instances of fewer types. Both would require less events.

    Consider a simple example with similarities with your setup. Each object has it's own object type: Bullet1, bullet2, enemy1 and enemy2. And both enemies have health variables. Events would look like:

    keyboard: on z key pressed
    -- create bullet1
    
    keyboard: on x key pressed
    -- create bullet2
    
    bullet1: collides with enemy1
    -- enemy1: subtract 1 from health
    -- bullet1: destroy
    
    bullet1: collides with enemy2
    -- enemy2: subtract 1 from health
    -- bullet1: destroy
    
    bullet2: collides with enemy1
    -- enemy1: subtract 2 from health
    -- bullet2: destroy
    
    bullet2: collides with enemy2
    -- enemy2: subtract 2 from health
    -- bullet2: destroy

    A quick fix would be to instead use families. Remove the health instance variables from enemy1 and enemy2, and then add enemy1 and enemy2 to a family. Call the family enemyFamily and give it a health instance variable. Similarly add bullet1 and bullet2 to a family and call it bulletFamily. Then add a variable called damage to that family. Your events then could reduce down to:

    keyboard: on z key pressed
    -- create bullet1
    -- bullet1: set damage to 1
    
    keyboard: on x key pressed
    -- create bullet2
    -- bullet2: set damage to 2
    
    bulletFamily: collides with enemyFamily
    -- enemyFamily: subtract bulletFamily.damage from health
    -- bulletFamily: destroy

    You can do something similar by just having different enemies be different animations on the same object type. But it just depends what you want to do.

  • Try Construct 3

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

    Try Now Construct 3 users don't see these ads
  • Manually in what way? In construct we deal with positions in layout coordinates, and construct converts those to canvas coordinates using scrollx/scrolly, layout scale, layout rotation, canvas size, and layer scale, parallax, etc… to draw.

    Well, maybe you are asking how viewportMidX(“ui”) is calculated… that could also be calculated with:

    ViewportLeft(“ui”)+viewportWidth(“ui”)/2

    Or

    (ViewportRight(“ui”)-ViewportLeft(“ui”))/2

    Or presumably if the ui layer has a parallax of 0,0 then you could just use:

    viewportWidth(“ui”)/2

    If the ui had a parallax of 100,100, and the layout had unbounded scrolling checked, then you could also just use:

    Scrollx

    With bounded scrolling the scrollx gets clamped but you can also clamp it manually if you’re so inclined:

    Clamp(scrollx, viewportWidth/2, layoutWidth-viewportWidth/2)

    But it would behave oddly if the view was smaller than the layout.

    If you set the canvas width the the window.innerWidth and had no layer or layout scaling then you could use:

    PlatformInfo.InnerWidth/2

    Of course all of those could be used with int() to round away the 0.5s from odd sizes. Also to manually handle the transformations that layers let you have then the formulas would get more involved and you may as well just use the expressions construct offers.

    Anyways, based on your previous posts you could get an unscaled pixel perfect viewport covering the window by setting the fullscreen scaling in your project properties to “none”, the set the canvas size in events to the innerWidth/height of the window. As long as the canvas is the only thing on the window there shouldn’t be any page scrolling. Then you’d just center with viewportMidX like you did above. There really isn’t a whole lot of benefit to calculating it more manually unless you encounter something that the engine doesn’t recalculate until drawing.

  • Construct supports JavaScript, typescript and events. No other languages are supported beyond that.

    You’d need to add support yourself or get someone else to do that. In theory you could compile the python interpreter into wasm to use via JavaScript, then add some kind of layer to call things from construct’s js api with python. But even doing all that would result in limits with what you can do. Aka you wouldn’t be able to run arbitrary python since within the web browser there are limits on things you can do or access. Generally working with supported languages are the easier way to go.

    On the bright side any programming concepts you learn often transfer to other programming languages. Or you can go down the python rabbit trail.