Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Discussion An Innocent Fool Struggles With Triggering a Moving Floor Plate

Discussion in 'Scripting' started by sdtr443w, Aug 16, 2023.

  1. sdtr443w

    sdtr443w

    Joined:
    Nov 29, 2014
    Posts:
    23
    I have been spending the past week or two on-and-off trying to implement a floor plate. Rather than make multiple posts about it in a row, I figure people could sit back and enjoy my suffering. Here's what I'm trying to get done. I'm trying to set up a triggerable floor plate:

    When the player steps over it:
    1. The plate starts a tween (DOTween) to lower some given amount.
    2. It fires an event on the assigned GameObject that it has been activated.
    3. It stays down so long as the player keeps their butt on the plate.

    When the player steps off of it:
    1. The plate start a tween (DOTween) to reverse what it did.
    2. It fires an event on the assigned GameObject that it has been deactivated.
    3. It stays up so long as the player doesn't have their butt on the plate.

    The DOTween tween is moving on the position transform for the box. I don't think it's conveying any kind of physics directly.

    Shenanigans:
    1. If the player steps off while it's lowering, it will send the deactivate event and raise from wherever it is.
    2. If the player then steps back on it while it's raising, it will re-send the activate event and lower from wherever it is.

    The player object has some things relevant to all this:
    1. A Unity character controller
    2. A RigidBody
    3. A Capsule collider

    I am led to believe mixing the character controller with a RigidBody makes me a bad, bad person and I need to pick one or the other. I want video game physics, not real physics, but I want triggers to work. So what the heck do I do? When I take away the RigidBody and the independent capsule collider, no triggers happen any more. The world is dull and boring.

    The floor plate is a GameObject that has the MonoBehaviour doing all the main logic. It also has a RigidBody. Underneath it, it has three children:
    1. A GameObject with a BoxCollider that is not a trigger. It contains the box's profile. Could this be in the parent GameObject? Probably, but I was giving myself an object of enabling/disabling it and messing with it independently. Its main purpose is so the player doesn't go falling through the box.
    2. A GameObject with a BoxCollider representing an "outer bounding box" for triggering. It has a MonoBehaviour that exposes events to ferry onTriggerEnter and onTriggerExit to anything that cares (the parent object cares). The bounding box is above the physical box.
    3. Similar to #1, but an "inner bounding box." It also has a trigger bounding box above the physical box, but it fits inside the outer bounding box.

    By the way, I thought the correct term for this was "rectoid," but I googled that up and got lots of ... butt stuff. Rectal stuff. My college professor for 3d calculus might have been trolling all of us in the room. She seemed normal enough.

    I then set up a more particularly evil scenario to make sure it really, really works. The tile is a giant cube on top of a little staircase. This lets me screw around at the top step, run off of the trigger, and do a nice flying arc from the steps semi-over the trigger (whoooo!).

    Here's a picture:


    The player is Mr. Red there. It has activated the box, which turned it red and has lowered it partway down from the staircase. All relevant colliders and triggers are highlighted. The blue line is the activation target, which flashes when the floor plate is active. Notice how the player is ... not quite on the box. That's a problem I listed below.

    First problem: Bouncing events.
    I would get an onTriggerEnter and onTriggerExit within one frame of each other when I just had one bounding box. The solution was the inner/outer colliders I described. Now I only trigger the plate lowering when the inner collider is entered, and only start raising the plate when the outer trigger is exited.

    Technically, this is fixed, but I feel gross.

    Second problem: Flood of events.
    Assuming I'm filtering for just the player GameObject, I still get bombarded with all kinds of enter and exit events. I have since been told that I can't rely on just getting an enter/exit event once per a game object. I'll get one for multiple points of collision--especially with a capsule collider. So I started counting the number of inner entrances and outer exits. I would only fire entrance on the first inner entrance and only fire exit on when the count returns to zero.

    Technically, this is fixed, but I think there are side effects. Also, I feel gross.

    Third problem: Flying over the tile causing it to trigger down, but missing the outer exit.
    I can run my butt up the stairs and fly over the tile. I think the default character controller would have me retain some upward motion at the top of the steps. Weeeeee! If I did this, I could fool it into triggering inside but somehow not outside.

    Solution... maybe, I changed both the player and boxs' RigidBody detection method to continuous speculative. I'm surprised this worked. Disappointed, more likely. It's not like I'm firing shots around or moving particularly fast. The box's movement speed is one unit per second. So I feel gross about this too.

    Fourth problem: Disco party when I get the player juuust right on the platform.
    I can get the player on to the top step just enough to trigger the inner trigger, which starts to lower the tile, but then the player exits the outer trigger, so it raises, but then it hits the inner trigger again . . . and so on. To be honest, this is the most straightforward of all the problems I had and the one that's most clearly between the keyboard and the chair, but I thought I'd bring it up.

    No solution yet. I don't know what I want to do about this yet, but at least this one I can understand. I wish all problems were like this.

    Fifth problem: Fooling the box to triggering permanently.
    I found if I get up on top of the step but not on to the tile, and run perpendicular, I can fool it into triggering the inner trigger, but somehow the outer trigger never exits. So the tile things its permanently activated. It just somehow misses the exit trigger! I have no idea!

    Sixth problem: As the box lowers, the player is just kind falling onto it over and over again.
    If I had a player falling animation, it would probably be going into it in some kind of gross spasm. So I know I need to something to chain the player to the box until they leave it or jump or something. I think that's a later kind of thing but maybe adding that logic now would help me with the other problems. Like, if the center of the capsule isn't even on the box, maybe I have no business actually triggering yet.

    So here I am and I'd like to get some learnin' about all of this. Please feast on my misery. I don't know about sending a sample scene because I have DOTween and InControl doing a lot of my bidding right now. Mostly, I think I'm hitting problems that a lot of experienced people have hit so I just need some knowledge beyond Baby's First BoxCollider Trigger on YouTube. I hope everybody thinks I'm at least past that one.
     
    Yoreki likes this.
  2. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,326
    I think all of your problems can be solved by replacing OnTriggerEnter and OnTriggerExit with OnTriggerStay. Use a variable to store whether the player is standing on the button. Use timers to smooth-out all of these fiddly timing issues.
     
    xucian, Chubzdoomer, Yoreki and 2 others like this.
  3. sdtr443w

    sdtr443w

    Joined:
    Nov 29, 2014
    Posts:
    23
    Well . . . I gave it a go just now. It might be working? I have to screw with it some more here. But why is polling with OnTriggerStay (and probably with FixedUpdate) more special than reacting to the events? It kind of makes me a bit sad since I have a certain amount of aversion to just polling on stuff.
     
  4. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    As you mention here, it's best to have your own logic run "while" unity says it's in the trigger zone, and you handle "at first", "while", and "no longer". I mean you could still do an "enter", but you'd have to do some extra checks to make sure it is still "while" and if not, clear all references. So like you mentioned "the lift keeps going down with no one on it" type of problems don't exist.

    Nothing in code "technically" thinks for you, sadly, you have to do all the thinking yourself, and add in "what if" type checks constantly.
     
  5. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,326
    Well, you said your third and fifth problem was with missing the OnTriggerExit event. With OnTriggerStay it doesn't matter: If it fires one frame and not the next then you know the player has exited without having to catch the precise moment.

    For the rest of the problems that you mentioned the trick is don't respond to the player touching the button right away. Instead use a timer. Do not start the event until the player has been standing on the button continuously for 0.25 seconds or something like that (OnTriggerStay you can check every frame, so you'll know if the player is colliding continuously or not). Similarly, wait until there is no OnTriggerStay for 0.25 seconds before stopping the event. Since your tread-plate currently reacts instantly, that makes it too sensitive, which is causing all of your juddering problems.

    0.25 is just an arbitrary number. It's long enough to smooth-out the input but hopefully short enough that player won't really notice. You may have to adjust that number.
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563
    ^ ^ ^ This coupled with some latching states can generally solve a lot of this stuff. By latching states I mean things that once a platform is moving, it will continue moving regardless of any further messages, until it reaches the conditions necessary to stop.

    You'll get over it when you write your first game engine and realize that internally, that's all Unity is doing: polling.

    There is no magical "detect contact between two colliders" microchip in your computer. It's all code loops. :)
     
  7. sdtr443w

    sdtr443w

    Joined:
    Nov 29, 2014
    Posts:
    23
    I can concede Unity doing some polling but then it feels odd for me to just throw some of my own polling on top of it. But I can't argue polling on onTriggerStay appears to be working.

    It really does fire a lot. I get it multiple times a frame if I'm tracking it. I'm guessing it's part of the cycle that would call FixedUpdate for the physics.

    Something I think I need to do ultimately is fire a ray down from the player's center to see if the center is on the box. This may take care of the bouncing I get in problem #4 and is something I think I have to do towards problem six anyways. So I was going to try that instead of a delay. That'll be fun for the weekend.
     
  8. sdtr443w

    sdtr443w

    Joined:
    Nov 29, 2014
    Posts:
    23
    I did get time to do a raycast from the center of the player. It actually did reduce some of the disco party, but I could still get it juuuust right. If I used a time delay, it would just be . . . a more easygoing disco party. I have the player just parked there and it's flashing.

    At this point with onTriggerStay, I only have one trigger; I'm not doing the inner/outer colliders. I might switch back to that. Basically, if the inner trigger detects and entrance, then an exit doesn't happen until I exit the outer trigger. I don't think the raycast would do me any good with that any more. If I could figure out the proper way to tell if the capsule is fully on the box, I guess I could do that. Maybe take the radius and do a downward raycast from each cardinal direction? Is that a normal thing to do?

    A side effect of that would be I could be standing on the edge and still hit it, but I'd have to make the inner trigger small enough that the player collider can't hang on at all. Well, in practice, the tiles/plates that would drop would not drop far so it wouldn't be that noticeable. I'm just making my life a bit more hellish with a 1-unit drop like I have in the picture.

    At some point, I'd have to write logic to glue the player to platforms, but it doesn't have to be here.
     
  9. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    In the picture, I see 2 colliders, the bigger collider obviously must not be convex, as the players collider is able to go through it, since the player must have a Rigidbody. But the smaller/lower collider must be convex, because the player collider is stopping on it.

    The platform should just have one collider to detect like a trigger(not counting the convex collider so player doesn't fall through platform). Then on the platforms OnTriggerStay() method, you just need checks to ask things like: Is player in trigger zone? Are we powered on to move yet? If (yes,yes) move in direction specified.

    It should be as simple as that(should be, but code never is simple, lol).
     
  10. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563
  11. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,326
    The point of the timer is you should reset the timer whenever the player is not colliding with the button, so if collision detection is rapidly flailing back and forth then nothing should happen.
     
  12. sdtr443w

    sdtr443w

    Joined:
    Nov 29, 2014
    Posts:
    23
    I think a big wrinkle here is the rather short trigger on top of the tile and the bottom part of the capsule collider. If I nudge my way to the trigger, I can hit a spot where something like this happens:

    1. The capsule collider intersects with the trigger.
    2. The tween starts to play that lowers the box.
    3. The trigger no longer intersects with the capsule.
    4. The tween reverses and raises the box.
    5. Goto 1

    Some peace I've made is if I thought onTriggerExit was very symmetric to onTriggerEnter, then situations when I want to detect something fully enclosed in a trigger is trying to leave a box wouldn't work. So my assumptions for onTriggerEnter and onTriggerExit were flawed in the first place.

    If I read that correctly, then between steps 3 and 4, there would be the timer delay, but I would expect it would still end up looping.
     
  13. sdtr443w

    sdtr443w

    Joined:
    Nov 29, 2014
    Posts:
    23
    I wanted to verify that since the box is moved by a tween and no actual forces that I should have the RigidBody for it set to kinematic and that I should freeze the position and rotation axes.

    If I don't do that, sometimes the box will reach the ground, and then float some arbitrary amount up. I can see the RigidBody will have a velocity going upwards of some arbitrary amount.

    When I set the box's RigidBody to kinematic, it seems to go away. Should it be kinematic? Also, should I freeze the Y-axis position? I see it doesn't harm me to have it frozen. Since I'm using a tween to animation the block, I suspect I don't want any forces to be at play at all on the box.
     
  14. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,468
    If you don't want a collision response then yes, you don't use a Dynamic body but instead use a Kinematic body that doesn't have one and doesn't respond to any forces. As its name suggests, that's the whole point of using a "Kinematic" body i.e. movement only, forces are not involved. You are in control, you only tell it where to move and it does nothing else.