I have a player character that can jump and, just as it lands, it has to check around a certain radius to see if there are any objects of the group "Interactive". My question is, how would be the best way to do this?
I tried adding an Area3D to the interactive objects so they emit a "body_entered" signal when the player enters, but when the player is in the air the signal is triggered, so by the time the player lands the signal has already been consumed. I have tried saving the interactive object to a temporary variable so that when the player lands he can check if something is stored there, but I don't like this method because then I have to carefully handle this reference in cases where other stuff can interrupt the player while it jumps.

Another solution I've thought is adding a 3D Area to the player and setting the monitoring property to false. Then, when the player lands, I just turn monitoring on, detect the interactive objects and turn monitoring off again.

And there are probably a couple better ways that I have no idea about as I'm an absolute Godot noob so...can you help me?

    correojon I'd just maintain the list of nearby objects all the time, using entered/exited signals. Then look at it when needed.

    correojon I do not know if GDScript has interfaces but here is how I'd do it.

    Place a sphere colider on the your character and every object that enters it you can add it to a list (I prefer a HashSet so thwt there are never duplicates and...faster/better optimized).

    Basically, all interactables would hold a script with interface "IInteractable" attached to it and every time your sphere triggers it it would add to/remove from a list.

    Where your list is, subscribe to the landed bool and when player has landed it fires a signal and does the interaction if the list is not empty.

    Sorry, I do only C# but I hope thst it at least gives you an idea on how to do it in GDScript! Best of luck!

      award
      This is just perfect, thanks a lot!
      BTW, I tried this but I found something a bit...curious. When you want to do a raycast with PhysicsDirectSpace3D.intersect_ray you first configure the query with PhysicsRayQueryParameters3D.create, which allows you to pass on all the parameters in one go. However, it looks like PhysicsShapeQueryParameters3D doesn't have a create method, so instead you have to set the query properties one by one. Is there any reason behind this? Would this be a nice suggestion to implement in the engine?

      PancakeGenie
      That's a good suggestion, but I don't like that I have to maintain the list all the time, which also forces me to check the state of each interactive object again once I launch the final interaction, as their state may have changed since the moment when I added them to the list.

        correojon

        It's because Godot's physics are old and bad 😂

        I think in particular the raycast functions are slow in part because of that PhysicsRayQueryParameters3D.

        Juan, one of the two founders, is aware of most of the issues to do with physics. They will be migrating to using Jolt Physics sometime in the near future, and hopefully then the interface will get sorted out too.

          correojon Really depends on what you are after. Doing the instant get all intersections sounds like it could drop some frames if there are many possible interactions.

          If you need to display "e" to interact...you kindof do need a list of interactables (of course, there are milions of ways to do it).

          I do not know what you mean that the state can change but if you found that your solution works for you, great! That is the most important part! Good luck on your journey!

          I'd still go with maintaining a list/hash. In realtime apps it's typically better to "stream" things than to do a lot at once in one frame. Besides, for best player experience you always want to ease things in and out for almost every kind of interactive feedback (animations, highlighting etc). So it could get handy to know those interactables in some time window before/after the actual moment of (inter)action. This may become more obvious as you polish the interaction more.

          As for checking the state; the list holds live references to objects, not their copies from the time they were put in the list. So there's no need for any extra checks that you wouldn't do otherwise.

            xyz Especially if Hash is used, slower on adding/removing but much more performant than Lists and ensures there are no duplicates inside. In my cases, usually, there is no need to have ordered list but you just need to do a fast lookup so I really like HashSet in these situations.

            You guys rise some good points. Let me give some more information: I'm making a Pikmin clone. You basically command a squad of units that follow you around and that you can pick up and toss at collectibles, enemies ad other stuff. So, when I pick one unit and toss it, it can either collide directly with an interactive object, or it can just land at the floor without colliding with anything. In this last case, I want the unit to check around some distance to see if there are any interactive objects and if so, it will walk right up to the closest one and perform the interaction (attack an enemy, pick-up a collectible, start digging a tunnel at a marked spot...).

            One important detail is that you can have up to 100 units, but you can only toss one at a time. This means that this detection process will only be active once at any given moment. However you can quickly toss units one after the other, so the process can be repeated as much as 100 times in a short time (just as fast as the player can repeatedly press the button to toss each unit).

            The detection radius is not too big, and per the game's design I think that there will only be something like 3-4 interactive objects inside the radius at most, with most times there being just 1. So if I go with a list it will be very small and easy to handle.

            So in this case, do you think that the list method would be better than using the intersect_shape() function?

            @xyz regarding easing, the units will have to walk to the interactive object they've detected so the process is not an instant transition from falling to interacting. I'm also using a finite state machine approach, so any changes in states go through some exit_state() and enter_state() functions to set transitions smoothly.
            HOWEVER this has given me an idea:

            1. Attach an Area3D with the detection radius to the player and disable it by default.
            2. Create a separate "landing" state that the unit enters just as it lands and just plays a landing animation.
            3. When entering the landing state, enable the Area3D.
            4. Then, the Area3D signals will detect all interactive objects and add them to a list.
            5. Back in the landing state, check the list in it's update function (update is just a redirection of _process()).
              This way the list can be cleared any time I enter or exit the landing state, the checking is done (almost) when landing and the list is handled almost automatically through signals.
            • xyz replied to this.

              correojon You can implement the described behavior by a single test, sure. But thinking a bit in advance, if you, going forward, decide to add new unit types with more complex behavior, for example; soft homing, mid air area attacks, attracting/repelling nearby objects and other stuff like this... maintaining a list would be the way to go. It's more scalable than a single check and lets you easily experiment and discover new mechanics possibilities. With all that in mind using a continuously updating list may actually result in much simpler code. Once implemented, it timely supplies proximity data for any situation, as opposed to an on-demand solution where you'd likely need to implement various checks for each newly added behavior.

                xyz Thanks for keeping up with this 🙂 Let me give you more details about the specifics:

                • My idea is to have an Area3D attached to each unit, disable it by default and only enable it for a brief moment when the unit lands after being thrown, use the area3D signals to detect all objects inside the interaction radius and process that list to find the closest one that can be interacted with, then disable the Area3D so no more signals are generated.
                • You propose to always have the Area3D active detecting units and adding/removing them to the list, so when the unit lands it already has the list of available objects (it will still need to find the closest one at this point, right?).

                Note that, by design, there will at most be 3 or 4 objects inside the interaction radius and normally there'll only be 1 or none at all:

                • If I run the check all the time, it will detect nothing at all 90% of the time.
                • If I run it just when landing it shouldn't create a big processing spike as at most it will just find 4 objects.

                Also, the squad will have 100 units so:

                • If I run the check and update the list all the time any calculations need to be done 100 times, 1 per unit.
                • If I run it just when landing I'm creating just 1/100 of the work and only at the landing moment, as I'm only updating the unit I've thrown.

                Interactions will only happen when the unit lands, the rest of the time units are "dumb" and are just following the player, completely oblivious to the world around them:

                • If I run the check all the time, the moment I throw the unit the list becomes obsolete. In essence, the list is completely useless until the unit lands and I'm updating it all the time for nothing.
                • If I run it just when landing then I'm creating no work at all outside of the landing moment.

                So, I understand that updating the list may be the best general approach, but with these specific considerations I think the "on demand" way may be more suited...but may be I'm missing something.

                • xyz replied to this.

                  correojon Not sure why you think it should be updated for all units at all times except for the active unit. That wouldn't make much sense, wouldn't it? It's exactly the opposite. You update only for the launched unit all the time while it's active (and if needed a little bit after that time).

                  Anyway, you now have two approaches, use the one that seems more fit. It's fairly easy to implement either so no big deal changing later. You can even implement both and have a switch flag.