Ruskul's Recent Forum Activity

  • To add, in a practical setting, if the code for collision handling and movement is completely authored in the event sheet, using no functions or custom actions, Lets say it uses an arbitrary 20%cpu time per tick.

    Wrapping it up into custom actions might increase that to 25%. Which imo is worth it because it is creating good architecture for adding complexity. If you wrap everything that was written more than once into a function (as the rule typically goes in any other dev environment), then the cpu time can be as high as 40%. But thats wrapping things like calculating the dot product or other one-off math functions not covered by construct.

    The function wrapping does nothing except to make it easier for the programmer to make changes and adjustments, and to avoid repetition. So I don't use them in this project, though I do in others where performance isn't so constrained.

  • Doing it via events, I'd think of adding an array per instance, with strings that then call mapped functions per string. If a string doesn't exist, nothing is called or acted upon, so no further overhead.

    That does seem to be the only way. In vanilla c3, there are 2 concerns for a project with high object count:

    1.) The simple act of calling a Function has overhead. It does in any programming context, but the overhead in construct is significant at scale, (a function call that simply sets a variable from a parameter will take 3-5x longer than simply running that event outside of a function. Since you can't call a mapped function without a router function, (if you want to use parameters), every dynamic function calls becomes 2 function calls (6x-10x) the time of simply running the simple event, requires a way to manage tokens, etc... So the net result is that dynamic function routing when used liberally comes at a huge cost.

    2.) Managing per instance arrays, or dictionaries requires an amount of repetitive boiler plate every time you use it. For each object, pick array by uid, for each element, function call .... This is itself a perfect thing to abstract away, and you could use functions to do it, but now you have functions inside of foreach loops. The performance loss of foreach loops is sizable when object counts are high, as it duplicates the overhead of simply running a blank event. So things get messy where you prefer the for each loop inside of the function and not the other way around.

    The solution is as you say, put as much of the nec looping in javascript and eliminate all that you can editor side. Overboy's UID to anything is helpful, but it can only go straight to common expressions (like variables, position, etc, and can't hook into a behavior. I often create simple behaviors simply to hold data, so that iterating with that data, etc can happen in in the behavior instead of events. You can also create getters and setters that run at a tiny fraction the cost of doing that with a eventsheet function. Overboy's Data+ essentially does that as well and is a fine tool - much better than authoring it yourself.

    Overboy's Advanced signals, also function like the old c2 functions, so you don't need to call two functions to route a signal. They also function per instance in lovely fashion, allow for polymorphism with out setting up boilerplate code. And he has custom expressions. Working in c3 with Overboy's tools makes it much more scalable and easy to work with. But, if performance is still the issue, Js is probably the only solution, either plugin sdk or the editor side JS.

    I have a post about it, but the takeaway is that using editor js has a lot of restrictions in how you use it, or you might end up with worse performance than not using it at all. For example, I see many people say calling a function from js is easier, than using mapped functions. I agree. But the overhead of a JS block is huge compared to basic actions or c3 function calls. Like 300x worse. Again, this isn't a problem if your JS contains more than one line of code. But putting JS blocks in any kind of loop destroys any performance gained from the logic being JS instead of ACES.

    Basically, every ACE has an overhead. But a JS block does too, just way more. So in a way it's much like the function. If the JS block is eliminating the need for editor side loops via event sheets, then you are winning, but if you are using JS more like fancy functions to wrap utilities in, you are probably going to have worse performance. Behaviors on the other hand, for whatever reason, do not invoke the same expensive overhead when they are called. Ashley seemingly brushed off the concern with the usual, "but no real game..." He isn't wrong, so long as the valid selection of real games are only those that aren't object heavy in the way a vampire survivor game is. He also isn't particularly wrong to be unconcerned about it, as its not a bottleneck many users will face. But imo, using eventside js, is worse than behavior side, though it is easier to author.

  • One thing I could see is having a good SDK (both editor and runtime) to go along with Flowcharts. This would all 3rd party devs to extend it using additional addons or behaviors.

    One addon area I would focus on is Behavior Trees, which would work well with a flowchart layout.

    I mean, being able to extend any behavior or plugin would be nice.

  • To add, in many cases, in pursuit of an eventual solution, it is nice to be able to

    a.) decide how far the hard coded "failure distance" is and,

    b.) move the object that distance anyway because, overtime, a solution might be found.

    Consider Super Mario Bro 3. (If anyone hasn't played it, and make games of the platforming variety, I'd argue some "research" is in order :) . If Mario becomes entrapped (which can occur for a number of reasons), the collision ejection routine will eventually successfully eject Mario from whatever wall he is stuck in. He ejects laterally at 1 pixel per frame. Construct ejects at an all or nothing 100pixels, or speedx3, whichever is greater.

  • I’d say the more common use case is for the push out actions to work successfully no matter what. Being able to specify a give up distance seems to needlessly make things more complex. And really if I wanted to do that I’d rather do it manually from the starting position and the pushed out position.

    That said, I stopped using the push out actions after finding they weren’t as accurate as I would have liked. Construct likely leans on its very good collision detection functions, but probably does some kind of repeated move and overlap check. So limiting the distance sounds like a way to limit huge loops. Just a guess though.

    There are other good solutions for resolving collisions though. A common one is utilizing overlap checks and iteratively moving stuff. If you treat the object you’re pushing out as a circle you can utilize sdfs. Then there’s SAT, GJK, or MPR if you want more deluxe solutions.

    That's the thing though, c3 pushouts fail if they are an arbitrary hard coded distance from the starting point if no solution has been found, and the user doesn't get to decide how far that point is... As you say, I am sure its for performance reasons, and unless things have change in the last 7 years, the old pushout routine was indeed simply moving and testing, and looping, so it obviously has to have a break point, but I'd rather set that myself.

    For tilemaps, given a limited index of possible tile shapes, I use some linear algebra to solve push outs. Character on character is treated softer. But my current project is also retro in feel and I actually don't want perfect mathmatically accurate collisions. I like that corner slipping feel you get from a highly calibrated and personal collision engine (like you had in gb link's awakening or mario 3).

    One day, I'll have a top down project where I treat everything as a circle and I'll be happy. lol.

  • It stopped me from replying. It warns you, then after proceeding, just stuffs you in a loop, afaik.

  • If you just have a single sprite for each for the eight directions you can simply store them in one animation with frame 0 pointing right.

    Do some math to take the angle of the player 0-360 and remap it to 0-7, rounded to the nearest whole number. That would be the index to set the frame to. Otherwise, if you have whole animations for each direction, you are going to have to get the heading angle, do the same math, then have some conditionals to set the animation.

  • I always like to start here, since if no one else sees the need, Scrirra probably won't either.

    I was hopeful, that in the case of the current custom movement behavior, nothing would actually have to change, except just adding one more property with ACE to expose it, and not hard coding the pushout max distance. Conveniently, I created a standalone behavior that has just that one purpose (pushout at angle, max distance). In that case, not hard to maintain, and no sweat to recreate. But ultamately, if it doesn't happen, that's probably okay, because I live on 90% custom behaviors anyway.

    What bothers me most in c3 compared to other engines, is that fact that we have to completely reinvent the wheel when you need a little change like that (since official plugins are kept private), and with no official way to extend behaviors or plugins. Each bit of code, however nice to reuse, can't be. Tediously made duplicate behaviors are also a pain to keep updated. If c3 ever makes its internals protected, hopefully it is at the point where it has a robust, and complete official api to hook in. As it stands, I break all the rules, since I consider Official plugins do to. If a built in behavior uses a call to c3.something... I do to :D

  • Try Construct 3

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

    Try Now Construct 3 users don't see these ads
  • Inside the custom behavior, the PushOutSolid action has a max distance it will attempt to resolve the collision before giving up. It is something like: max(3xSpeed, 100).

    This is simply hard coded in. For one thing, I would prefer if it was a smaller number in many cases.

    It would be very nice if this was exposed to the user. In addition, it would also be nice if there was a way to go ahead and have the object move to that max distance, even if the collision isn't resolved.

    There are many reasons why you would want to only partially resolve a collision.

    The best workaround is to ->

    1.Test overlap. If colliding, save position, push from solid and compare the distance from the saved position to the new position.

    2.If it hasn't moved, then move it manually whatever max distance you want. If it has moved, and if it has moved more than you want it to, move it back whatever distance is desired.

    An alternate is ->

    1. Test overlap, if colliding, move the object the desired max and test overlap again. If it is still overlapping (assuming things about level design and max distance to be moved), leave it as it is, if it isn't overlapping any more, reset the object and use the push from solid behavior to move it perfectly from collision.

  • Thanks for your interest! I'll let you know.

    I fully committed my current project to c3, so one way or another, I intend to find a solution or compromise. I headed into this knowing that. :D

    Part of the reason to stay in construct for me was that I wanted the scope of the project to stay limited, even if that was because construct was the limitation. I make WAY too many frameworks and abstrations in unity, instead of just making the blasted game. You can go down some deep, deep, rabbit holes in programming if you aren't careful... and while those deep dives are fun, and would be useful in some constexts, they can be time sucking over-reaches for solo devs.... cab be and usually are usually every time (for me at least).

  • ---Solution---

    You can't via officially sanctioned methods - but I did anyway... As an aside, an api should exist for this, but doesn't, so what choice do I have! ITS NOT MY FAULT!!!! THEY FORCED ME!!!! </derangement>

    *ehem* When declaring variables for the behavior, inside the constructor, I added this:

    this._sprite = null;

    If I cared, I would add error checks to handle the plugin not being of type sprite, but I don't care. Because I only need this behavior on sprites- that are not even drawn to screen. Later on, when I need to change frames, I do this:

    	if(this._sprite === null) this._sprite = this._inst.GetSdkInstance();
    	this._sprite._SetAnimFrame(2);
    

    As far as I can tell, nothing is happening here that should be of concern, and the path to frame change is exactly as it would be if you called it via events or the scripting api (not included for the plugin sdk).

    However, I have special needs. I don't want to be referencing a frame by tag, and I don't want c3 to do a bunch of error checks I don't need. and I definitely don't care about updating the image texture or other nonsense related to sprite. I just want the cold hard collision poly handed over to the collision system. Done:

    const change = 2;
     const changeFrame = this._sprite._currentAnimation.GetFrameAt( change );
    	 const wi = this.GetWorldInfo();
    			//wi.SetOriginX(changeFrame.GetOriginX());
    	 //wi.SetOriginY(changeFrame.GetOriginY());
    	 wi.SetSourceCollisionPoly(changeFrame.GetCollisionPoly());
    	 wi.SetBboxChanged(); 

    This gets to the meat, and it bypasses the following:

    • Error checking for the index number passed in, no use of tags
    • Errors resulting from frame being out of bounds
    • It runs regardless if you are changing to the same frame
    • It doesn't update the texture or change the renderer
    • The sprite plugin doesn't accurately know which frame you are on
    • It doesn't trigger On Frame Changed
    • It bypasses the c3 event system
    • It doesn't change any variables in the sprite anim system

    TLDR: As far as the sprite is concerned, you didn't change frames. Only world info knows a change happened. It black magic, and breaks the intended behavior of the sprite - which is exactly what I want >.>

    What does this mean? You can bloody change collision polys whenever you want without alot of useless overhead. Savings can range from 20%-45% depending on the "real world" application. For me, that falls on the higher side and has a huge impact on overall performance.

    ---Original Post---

    Hi, I tried to revive someone else's old post on this, but the website told me to make a new post... So here it is.

    Ashley followed up with info about how animation frames are tied to a sprite, and behaviors can be applied to more than just sprites - all makes sense. Then Ashley asked what was the use case was but the original poster never answered.

    Given chribbe never described the use case, maybe I can describe why I am looking to do the same.

    I am creating a retro platformer behavior that resolves collisions the same way nes era games would (not by means, just results). To do so, you need collision points for sides, head, toe, and a few others, because each collision point will resolve differently.

    In an event sheet, I would manage these different collision zones by means of animation frames. (for example, 0 = full collider, 1 = right side, 2 = down, so on. You simply set the frame, test for an overlap, do the resolve, then move on. The advantage is that you can easily set the collision poly for each frame in a visual way in the editor, and afiak, this is how many c3 users do things like this. The only alternative is to have multiple objects (with all the picking problems and foreach loop requirements (which hurts performance), or hierarchies with even worse performance still. Don't get me wrong, hierarchies are great, but not in my case. (enter feature suggestion: allow user to define multiple collision poly's without the need for it to be attached to animations).... The leanest, most performant solution within the editor (for my use case) is the frame based one.

    Anyway, I have shoveled most of this logic into a behavior, but I have to still externally set the animation frames and then tell the behavior to then test and resolve the collision - This is boilerplate required in every project that shoudl clearly be handled by the behavior. Not only that, every event I have to interact with the behavior is performance I can't spare. The ehavior can't do its job correctly without a user knowing how to work with it. Great for an api, but behaviors are nice when they are plug and play. I would much prefer for it to handle changing the collision poly to the different positions and testing for collisions and resolving them without help. Outside of doing that, I could have the behavior have a bunch of variables representing the collisions points and have it reposition and resize the object to match them, one by one, but entering the data is tedious, easy to mess up, hard to visualize and kinda silly when we already have collision poly editor..

    The way I envision my behavior, is that there would be an action/property that sets/holds an index of a animation frame for each collision point. I feed it that via a property or action, and let 'er rip - at that point without ever having to interact with it except to disable it.

    Why put it in a behavior? Performance most of all, and ease of use across projects as a close second, etc...

    If there was a better way (which there could be) for handling multiple collision boxes that still allow the user to set them visually via the editor.... Ideally, collision stuff wouldn't be paired so tightly (or at all) with the sprite object, as they really aren't the same responsibility. Everybody be making projects with two sprites for each object, because one is the collider the other the visual model is basically proving that point... but that aside, is there an obvious solution I missed?

  • And I should make clear: Wrapping up functionality from the event sheet and putting it into a single Action call in a behavior can save alot of performance, especially where eliminating event based for each loops and functions calls.

    But what I don't like is that creating behaviors to do these sort of things often involve duplicating portions of pre-existing behaviors.

    My stat system is basically a dictionary, etc...

    Worst part is, the interfacing between c3 events and complex behaviors will also end up with overhead. For example, there is no way to dynamically trigger an Action from an action without passing in a token and parsing it. In c#, you could simply save any function into an action variable, and the line actionVariable?.Invoke(); would essentially call any functions assigned to it via actionVariable += anyObject.someFunction(); Sure, there are also methods in javascript for doing this to, but communicating from the event sheet into the world of javascript has limitations.

    Now, delegates in c# do have overhead as well, but its negligible at the scale I am talking about. My unity project has a dynamic system with 20+ action variables that all invoke however many functions are set to them for every object.

Ruskul's avatar

Ruskul

Member since 23 Nov, 2013

Twitter
Ruskul has 2 followers

Trophy Case

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

18/44
How to earn trophies