Tips and tricks for actually running events in the right order/right amount of times?

0 favourites
  • 7 posts
From the Asset Store
An educational game for Times Table. An easy to use template for developers to build larger games
  • I've been using construct for a long time now, but the largest headache I have is constantly having events trigger out of order/early/late/multiple times, causing days of rewrites sometimes just to try and accidentally find something that works.

    Then I can add one new thing, and everything breaks in an equally nonsensical way.

    Note: This all includes using wait:0's, wait signals, moving stuff off to functions/asynchronous functions and waiting for completion, etc.

    Does anyone that's experienced this have a workflow/consistent solution for this?

    For a recent point of reference, was trying to work on a recent game jam and spent straight 6 hours rebuilding an attempt to smoothly transition to a next turn of combat.

    I wanted all of a turn to resolve before allowing input again, so I did many variations like adding 1 to a global when a character attacked, then subtracting 1 at the end of their attack animation. And added 1 at the start of an enemies hit animation (triggered before the end of an attack), reduced 1 at the end of the hit animation before it was destroyed. Once it returned to 0 you would then be able to input again.

    Tried various waits, moving it around, signals, functions, etc adjacent to that idea, switched to/away from 'for each' in the on animation ends in-case there was a difference if 2 enemies triggered at the same time, but nothing worked. It would consistently, inconsistently have 1 left over and so stop working.

    This is only my most recent example, it's a struggle I constantly run into and can be the majority of my effort in small games just hoping to randomly stumble upon some combination that works.

    Sometimes wait 0's would fix something, other times it would break everything. Sometimes I end up with so many nested events just trying to hope they would properly run after another because they just didn't seem to even though nothing is meant to be parallel from what I've read.

    I can't really provide an example because it feels so illogical and inconsistent that I don't know how to properly reproduce it, besides waiting for it to break, but even providing that could be one of any countless variations of struggling with this problem I've had and not be of universal help.

    So I'd really appreciate if there's anyone who has developed a workflow to overcome these issues or found a common trick to resolve some of these cases that isn't just guess work.

  • You need to study game templates included in Construct (Kiwi Story and others).

    Trying many combinations of events in hope that some of them will work is a very wrong approach. You need to understand what you are doing if you want to create a working code.

    From what you described, you are using a "state machine" model. I understand why many beginners do this, at first it looks simple and logical, but as your project grows bigger, it becomes a minefield. You end up with dozens/hundreds of events checking and modifying all kinds of variables on every tick, lots of "trigger once" and "waits" in different placed. It's a delicate mess and any small change can potentially ruin everything.

    There is nothing wrong with having a few state variables, but building an entire game like this - I am strongly against it. In my games most of the events are triggers and functions. For example:

    On timer "Attack" -> Play "Attack" animation
    On "Attack" animation end 
    	-> Call function "DealDamageToEnemy"
    	-> Restart "Attack" timer
    etc.
    

    You can clearly see the sequence of events here, it's much easier to develop and debug this way.

  • I also don't advise using "Wait" in game mechanics, especially for long delays. You can do it for unimportant things, like create particles, wait 1 second, destroy particles. But imagine this code:

    In these 5 seconds the enemy may be killed, and you have no way of cancelling that delayed animation change, other than destroying the enemy.

    Another big mistake is having "Wait" in events that are running on every tick. Check out this comment.

  • Appreciate you sharing your experience, but it seems like you haven't run into my problems or at least have been able to avoid them from the start.

    I've been struggling on how to respond because I know I don't know everything, otherwise I would hope I'd be able to avoid these issues. But I want to at least affirm the context that I've used many different engines/visual scripting solutions for years and it's not basic mistakes like having any wait in an every tick that's happening. Though I also appreciate that was just meant to help as I didn't know how to word my experience in the first post and it could have been an issue.

    In a sense I was using a state machine, but only really for the natural use case of tracking the progress of a round and using that for tracking when logic should happen and players inputs can be used in a turn based structure.

    That is what I used as a recent reference, but I also frequently have these issues in start of layout sequences too, for example when I need to load json data to set up arrays and use that in some way at startup. Signals help most there, but often when trying to iterate events through that I run into various issues with having to wait for data to actually be loaded correctly, but then sometimes the event iteration breaks, and so many things seem to not be functioning in order, and what needs a wait to work/what putting a wait in front of breaks even worse becomes a complete guessing game for me. I had to rebuild the startup of a previous project so many times it was insane.

    Your response in general is helpful, but it's closer to how not to make mistakes 101, than best practices to utilize to avoid the above headaches. I'd love to be able to do something as simple as your attack animation example but that just doesn't ever function so smoothly for me so I end up with a messy string of functions/waits/embedded blank events trying to get things to work in the order I want and hoping nothing randomly happens more than it should.

    I looked through Kiwi Story, and Demonoire to try and find an example structure but they're just simple/clean/to the point. A good thing for sure, and especially Demonoire has a great deal of content, but like in the circumstances they use foreach/don't use it, or how in their enemy 'AI' they don't seem to need to try and cover potential overlap, because the game is built in a way that won't happen. So I can't really point at any tricky sequence of events like my above array example that is more representative of the cases where all this trips me up.

    Construct has some great advantages, but I always feel like I'm tripping over invisible code because of the nature of whatever logic it's using to determine action orders and event triggers. Where as in one of Unity's more complex visual scripting solutions like Bolt/Flowcanvas/Nodecanvas, or Unreal Blueprints, etc, I know exactly what something is doing and where/when it is doing it as I can follow it along more precisely.

    So I was hoping there might have been some more advanced tricks to keep things in order or have things run as much as they should/when they should with stronger certainty.

  • Try Construct 3

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

    Try Now Construct 3 users don't see these ads
  • Sorry for assuming that you are a beginner, but some things you've described and especially your troubleshooting methods (re-arranging events, adding waits etc.) are mistakes that I've seen many times on this forum.

    For example, adding 1 to a global variable when any enemy is attacking, and subtracting 1 at the end of their attack animation - with multiple enemies this is difficult to program correctly, and extremely difficult to debug when it's not working.

    It's much easier to set isUnderAttack=true when an enemy starts its attack, and then when an attack ends (or on every tick, or every 0.1s) check if there are any enemies still attacking. If not, set isUnderAttack=false.

    Using "Wait" in this logic will only cause problems! If you need to delay something, for example, player recovering from the attack, use Timer behavior.

    .

    About your other issue with the startup sequence - when you are starting many asynchronous requests at once, and then use many "On completed" events, it's difficult to figure out when all of them are completed. It's easier to execute them one by one.

    Unless the files are really big, these requests should be finished in a couple of ticks.

    .

    Can you please post screenshots of your code, which you are struggling with? It will be much easier to discuss with actual examples.

  • No worries, thanks a lot for the understanding and further advice.

    That being a problem is exactly why I end up feeling helpless a lot guessing in the dark. Logically it should work, but it feels like there are shortcuts/conversions/clashing interactions hidden behind the abstraction of the events/flow that I can't predict and keep tripping over.

    That could be an approach in some cases I'll have to keep in mind so thanks :), but my structure in this case and those where I have issues are more complex. I tried to build a separate example, but I was essentially making the whole project again to have the systems/interactions in play that are causing the issue, that are again just 1 of many encounters with this general issue each time I try and make something in Construct.

    Continuing the above thread though, to try and better discuss this games set of circumstances, here's an in-action visual of where it got - gyazo.com/015a79930be6b9e3814fd9e294643578

    The game loop is turn/round based:

    • [#1 (active)]: Player pressed 'w/a/s/d' and that sets that directional character to Attack1a that ends on final frame / Any Attack1a on final frame begins Attack1b to follow through. At the same time Any enemy at the left will step through Attack1a/Attack1b over 2 turns too (currently doing nothing else as I couldn't fix these issues at the player attack enemy stage, it's just visual)
    • [#2 (resolve)]: When Attack1b changes to its damage frame, the lowest enemy if any is in the left row (they advance from right to left in a sense each round) changes to 'hit' animation and will be deleted then removed from the row (the rows are 3 wide/x tall tracked in an array to move them and divide their location evenly as they reposition).
    • [#3 (turn end)]: Because enemies need to be deleted and taken out of the array before the next set move forward, I'm trying to during this step check at the final potential steps (did a character finish their attack but no enemy was being hit/did an enemy die and no remaining enemies are dieing or attacks are being triggered). Then enemies would progress and the round would go to the next so you could input again.

    I realize that it's probably as hard to give broad advice as it is to explain all the various circumstances I find myself in. I just have an unnatural talent to constantly make mechanics that Construct doesn't seem to agree with.

    Regarding the suggested startup sequence that's basically what I do, except I didn't find wait for previous actions consistent enough and I needed to do more with them so I was using signals - i.gyazo.com/6af17591ce1609dcdb19c3d209d7f943.png i.gyazo.com/a3953bfd8d3bccd04fce57e61886a685.png but it's reassuring to know I was on the right track with them so thanks for sharing your experience with them.

    Hopefully the above elaboration might give some insight into more potential methods I can try and take advantage of in this situation, but regrettably the awkward areas of code I still have needing wait 0+'s/signals/wait for completions/embedded blank events don't really have their context when shared at face value without me adding/removing them and seeing everything randomly break or randomly work during their implementation.

  • You are clearly a fan of "Wait for signal" action :). Which is not a bad thing, but I'm not sure if you are always using it correctly. For example if the two shuffling functions on your screenshot are completed immediately and don't have any "waits" or asynchronous requests inside, then you don't need the "shuffled" signal.

    Also, your whole GameStartup function is set as async, which means you can use "Wait for previous action to complete" in the parent event, from which you are calling this function. And yet you are signalling "StartupComplete" for some reason.

    And of course, you can optimize all those AJAX requests, get rid of "on completed" events and signals there.

    .

    I feel like your experience with other engines is actually doing you harm here, because Construct events system is quite different.

    Another thing you may be overusing is arrays. Of course, I can only make assumptions here, but I see 10 arrays on the screenshots you posted. In Construct many things where you normally need arrays, you can do much easier without them. If you have several instances of the some object or family - this is already a two dimensional array. And it's much easier to work with instances (sort, filter, group, modify, add, remove etc.), than manipulating an actual array. It's totally possible to make big complex games without a single array.

Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)