Search Unity

(SOLVED) Need help with Triggers and UI Text

Discussion in 'Scripting' started by Denisowator, Sep 8, 2017.

  1. Denisowator

    Denisowator

    Joined:
    Apr 22, 2014
    Posts:
    918
    I've got a 2D 2-way stairs system, and there are two things wrong with it right now.

    • The triggers are a bit broken (not fully implemented)
    • The UI text doesn't have specific trigger detection.
    Here's what the setup looks like:
    upload_2017-9-8_20-8-26.png
    Right now, the player can enter either of the two triggers, which enables the polygon collider, and disables the collider of the above floor.

    Here's the code (feel free to use it for your game (even commercial), just don't licence it (the code))
    Code (CSharp):
    1.     public Movement playerMovement;
    2.     public PolygonCollider2D stairCollider;
    3.     public BoxCollider2D floorCollider;
    4.  
    5.     public Canvas privateCanvas;
    6.  
    7.     bool isInTrigger;
    8.  
    9.     void Update () {
    10.         if (isInTrigger == true) {
    11.             if (Input.GetKeyDown (KeyCode.W) || Input.GetKeyDown (KeyCode.S)) {
    12.                 stairCollider.enabled = true;
    13.                 playerMovement.OnStairs = true;
    14.                 floorCollider.enabled = false;
    15.             }
    16.         }
    17.  
    18.         else {
    19.             stairCollider.enabled = false;
    20.             playerMovement.OnStairs = false;
    21.             floorCollider.enabled = true;
    22.         }
    23.     }
    24.  
    25.     void OnTriggerEnter2D () {
    26.  
    27.         if (!stairCollider.enabled) {
    28.             privateCanvas.gameObject.SetActive (true);
    29.             isInTrigger = true;
    30.  
    31.         }
    32.  
    33.         else {
    34.             isInTrigger = false;
    35.         }
    36.     }
    37.     void OnTriggerExit2D () {
    38.         privateCanvas.gameObject.SetActive (false);
    39.     }
    Basically what's happening, is it checks for the player entering the trigger, and if the stairs' (polygon) collider isn't enabled, it sets the isInTrigger bool to true, which in turn activates the stairs (after checking for input). It's pretty much just creating a custom OnTriggerEnter check, which allows the system to loop back and produce a different result.

    So, I can enter a trigger, activating the stairs. And I can either walk to the other trigger to deactivate them, or I can walk back to the same trigger, which will also deactivate the stairs.

    Trigger problem (SOLVED, solution below)
    The problem comes when I deactivate the stairs. Basically what I want, is for the UI Text to appear and for the player to be able to activate the stairs, at any time they are in a trigger.

    However, because of the way this system works, if you enter a trigger while the stairs are on (to deactivate them), the "if" statement in OnTriggerEnter2D will never execute, since the stairs are enabled at the time. This results in the "else" statement executing, and the system thinking you are not in the trigger.

    Because of this, I have to leave the trigger, and return, for it (the stairs system) to be usable again.

    How can I fix this?


    UI Text problem (SOLVED, solution below)
    The UI Text is just a single letter following the player (above their head), to indicate what they have to press.
    The problem, is that I need some way for it to detect whether I'm at the top of the stairs, or the bottom, and for the letter visible to be based on that.

    So if I'm at the top of the stairs, I need it to say "S" for going down, and if I'm at the bottom, it should say "W" for going up.

    Currently for buttons work on both ends, but I can fix that myself once I get the text working.

    How do I do that? I was thinking of ray casting, where the cast goes from the center of the stairs to the player, and if it points up from its origin, "S" is displayed at the trigger, and "W" for when it's pointing down. But I don't know how I would do that.
     

    Attached Files:

    Last edited: Sep 21, 2017
  2. larswik

    larswik

    Joined:
    Dec 20, 2012
    Posts:
    312
    Not to answer all your questions but the UI Text problem why can't you just return the players position and check the y position to see if it is above or below a set value to display the "W" or "S"?

    for example
    Code (CSharp):
    1.     void howHighIsMyPlayer(){
    2.         if(gameObject.transform.position.y > 200f){
    3.             myText.text = "W";
    4.         }
    5.         else{
    6.             myText.text = "S";
    7.         }
    8.     }
    or my favorite, the 1 line If statement called a tierney statement.
    Code (CSharp):
    1. myText.text = (gameObject.transform.position.y > 200f) ? "W":"S";
    I'm trying to give back by answering what I can since I also am asking some question on here too. Hope that gives you some food for though.
     
  3. Denisowator

    Denisowator

    Joined:
    Apr 22, 2014
    Posts:
    918
    Is there a way I could do that by referencing the stairs object's y position? The game isn't going to be on a flat world, and I don't know exactly where or when I'll place this system in my game. So checking for a static y value wouldn't work.
     
  4. Denisowator

    Denisowator

    Joined:
    Apr 22, 2014
    Posts:
    918
    bump.

    I thought I had something with the Trigger problem, but it just caused other problems, so I removed it from the original post (the solution).
     
  5. Denisowator

    Denisowator

    Joined:
    Apr 22, 2014
    Posts:
    918
    TRIGGER PROBLEM SOLUTION

    Okay now I definitely solved it. My whole system basically consists of a make-shift trigger check, which goes as follows (for those who don't code).

    If the player enters the trigger, and the stairs (their collider) are disabled, it sets isInTrigger to true. It checks for that boolean being true, then for input to be made (pressing W or S), and it then enables the stairs.

    If the player enters the trigger, but the stairs are enabled (which is what happens the first time a trigger is entered), it disables them. This is basically the bare bones of the system.

    The solution I had before, worked. But it caused some further issues, which I identified as "unsolvable". What I later realized, if that anything can be restricted or disabled by code (duh). If you have an if statement that checks for something being true, you can counter that action by making an else statement.

    The "half-solution" I had, was adding the following two lines, to the "else" statement of the OnTriggerEnter2D:
    Code (CSharp):
    1. yield return new WaitForSeconds (0.01f);
    2. isInTrigger = true;
    I also had to change OnTriggerEnter2D's type from "void", to "IEnumerator".

    The reason I needed to add that, is because once I entered a trigger to disable the stairs, I wouldn't be able to instantly re-enable them. I would need to exit the trigger and re-enter it, for me to be able to activate the stairs again.

    Those two lines counter that, and give enough time for it to register that isInTrigger has been set to false (automatically disabling the stairs), before setting the boolean back to true, which allows you to instantly re-enable the stairs.

    The issue that those lines caused, was that after disabling the stairs you didn't need to be in the trigger, to re-enable them. Meaning you could be standing wherever on the second floor (not even anywhere close to the stairs), press either W or S, and fall through the floor.

    And all I needed to do in order to counter that, was make use of the actual "OnTrigger" functions.

    I just made an "OverrideTrigger" boolean, set it to true, inside OnTriggerEnter2D, and to false, inside OnTriggerExit2D. I then made a check for the boolean in Update, encapsulating all the other checks. This makes it so that you can only ever activate the stairs, when you are in one of the triggers.

    I think the reason I couldn't solve this problem before, was my variable naming. Specifically, naming the first boolean "isInTrigger". Which after using for a few days, caused me to subconsciously think it was actually checking for the player being in the trigger. Which caused me to completely neglect the actual functions.

    Here's the current version of the code:
    Code (CSharp):
    1.     public Movement playerMovement;
    2.     public PolygonCollider2D stairCollider;
    3.     public BoxCollider2D floorCollider;
    4.  
    5.     public Canvas privateCanvas;
    6.     public Text actionText;
    7.  
    8.     bool stairsToggle;
    9.     bool OverrideInTrigger;
    10.  
    11.     void Update () {
    12.         if (OverrideInTrigger == true) {
    13.             if (stairsToggle == true) {
    14.                 if (Input.GetKeyDown (KeyCode.W) || Input.GetKeyDown (KeyCode.S)) {
    15.  
    16.                     stairCollider.enabled = true;
    17.                     playerMovement.OnStairs = true;
    18.                     floorCollider.enabled = false;
    19.                 }
    20.             }
    21.  
    22.             else {
    23.                 stairCollider.enabled = false;
    24.                 playerMovement.OnStairs = false;
    25.                 floorCollider.enabled = true;
    26.             }
    27.         }
    28.     }
    29.  
    30.     IEnumerator OnTriggerEnter2D () {
    31.  
    32.         OverrideInTrigger = true;
    33.  
    34.         if (!stairCollider.enabled) {
    35.             privateCanvas.gameObject.SetActive (true);
    36.             stairsToggle = true;
    37.             actionText.text = "W";
    38.  
    39.         }
    40.  
    41.         else {
    42.             stairsToggle = false;
    43.             yield return new WaitForSeconds (0.01f);
    44.             stairsToggle = true;
    45.             privateCanvas.gameObject.SetActive (true);
    46.         }
    47.     }
    48.  
    49.     void OnTriggerExit2D () {
    50.         privateCanvas.gameObject.SetActive (false);
    51.         OverrideInTrigger = false;
    52.     }
    53. }
     
    Last edited: Sep 10, 2017
  6. Denisowator

    Denisowator

    Joined:
    Apr 22, 2014
    Posts:
    918
  7. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    I browsed this post.. So is it solved or partially solved or what's the story? :)
     
  8. Denisowator

    Denisowator

    Joined:
    Apr 22, 2014
    Posts:
    918
    Partially solved.

    The triggers are working fully. But I can't get the proper letter to appear in relation to the stairs, and for proper input to be checked.

    i.e. If the player is on top of the stairs (enters the top trigger) an "S" will appear (by using the "Text" UI element), and if he/she is on the bottom (enters the bottom trigger), a "W" will appear. It will also check for those letters on the keyboard, so that the opposite input won't be possible (pressing S to go up, or W to go down). This is what's currently not working.

    I'd prefer this to be done by some kind of position check only, as both triggers are BoxCollider2D's, and both are a component of the same object (so doing it another way might cause issues). I know I could just make them children, but that would mean altering the code and references, and I like it how it is (as long as it doesn't cause any further issues).

    If I understand the logic correctly, I could just make some kind of raycast, and check the player's position in relation to that. Or I could use the stairs object's transform in some way, to check for the player's Y position in relation to it, and if it's above a certain number, it will display "S", and if no, display "W".

    Problem is, I have no clue what I can check for (in very specific terms), in order to make this work.
     
    Last edited: Sep 12, 2017
  9. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Um.. Right.. Well, having separate objects for the colliders would have been my first suggestion.
    That way would be very 'neat' and organized. :) Then, you display the proper text and know where the player is , etc.. I would suggest you consider trying that.
    You could just check if you're closer to the bottom or top, provided these stairs are not like sideways?! lol

    If you really, really don't want to break up your current object.. I'd suggest adding 2 new empty objects with colliders, and use those references to determine where you are and what to show/accept. Unless those colliders are previously busy doing other other things (and even then, 2 new ones wouldn't break that)..

    I dunno - some options to consider :)
     
    Denisowator likes this.
  10. Denisowator

    Denisowator

    Joined:
    Apr 22, 2014
    Posts:
    918
    The last option is definitely the best one for me. I'm actually using "OnTrigger" events for the whole system, rather than variable references, so it would be really hard to reference them the same way, if they were separate objects.

    What I'll do in that case, is duplicate the two triggers, but as children of the stairs object. And have some kind of script and boolean interaction between them and the main system script. Most likely the triggers setting the booleans, and the main script checking for them being either true or false.

    Though I have no idea what would be the cleanest solution.

    I'm thinking of somehow defining the booleans in the main script, then altering them in the triggers' scripts. However everything I found so far on this, was really extensive (and mostly old) Answers posts, where someone puts in a huge block of code, followed by the answerers doing the same. Which just confuses me. Maybe I'm thinking about this the wrong way.
     
    Last edited: Sep 12, 2017
  11. Denisowator

    Denisowator

    Joined:
    Apr 22, 2014
    Posts:
    918
    UI Problem Solution
    Got the UI working finally.

    All I did was make an empty child for the stairs object, set its position at the half point (on the y axis) of the stairs, and checked the player's y position against that.

    Here's the full code:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. public class SwitchableSlopes : MonoBehaviour {
    7.  
    8.     public Transform positionCheck;
    9.     Transform playerPosition;
    10.     Movement playerMovement;
    11.     [HideInInspector]
    12.     public PolygonCollider2D stairCollider;
    13.     BoxCollider2D floorCollider;
    14.  
    15.     Text actionText;
    16.  
    17.     public SpriteRenderer stairs;
    18.     public SpriteRenderer table;
    19.     public SpriteRenderer phone;
    20.  
    21.     bool stairsToggle;
    22.     bool OverrideInTrigger;
    23.  
    24.     void Start () {
    25.         playerMovement = GameObject.Find("Player").GetComponent<Movement> ();
    26.         playerPosition = GameObject.Find ("Player").GetComponent<Transform> ();
    27.         stairCollider = GetComponent<PolygonCollider2D> ();
    28.         floorCollider = GameObject.Find("House (Inside)").GetComponent<BoxCollider2D> ();
    29.         actionText = GameObject.Find ("ActionLetterText").GetComponent<Text> ();
    30.     }
    31.  
    32.     void Update () {
    33.         if (OverrideInTrigger == true) {
    34.             if (stairsToggle == true) {
    35.                 if (playerPosition.position.y > positionCheck.position.y) {
    36.                     if (Input.GetKeyDown (KeyCode.S)) {
    37.  
    38.                         stairCollider.enabled = true;
    39.                         playerMovement.OnStairs = true;
    40.                         floorCollider.enabled = false;
    41.  
    42.                         stairs.sortingLayerName = "Sub-Foreground";
    43.                         table.sortingLayerName = "Sub-Foreground";
    44.                         phone.sortingLayerName = "Sub-Foreground";
    45.                     }
    46.                 }
    47.  
    48.                 else if (playerPosition.position.y < positionCheck.position.y) {
    49.  
    50.                     if (Input.GetKeyDown (KeyCode.W)) {
    51.  
    52.                         stairCollider.enabled = true;
    53.                         playerMovement.OnStairs = true;
    54.                         floorCollider.enabled = false;
    55.  
    56.                         stairs.sortingLayerName = "Sub-Foreground";
    57.                         table.sortingLayerName = "Sub-Foreground";
    58.                         phone.sortingLayerName = "Sub-Foreground";
    59.                     }
    60.                 }
    61.             }
    62.  
    63.             else {
    64.                 stairCollider.enabled = false;
    65.                 playerMovement.OnStairs = false;
    66.                 floorCollider.enabled = true;
    67.  
    68.                 stairs.sortingLayerName = "Midground";
    69.                 table.sortingLayerName = "Midground";
    70.                 phone.sortingLayerName = "Midground";
    71.             }
    72.         }
    73.     }
    74.  
    75.     IEnumerator OnTriggerEnter2D () {
    76.  
    77.         OverrideInTrigger = true;
    78.  
    79.         if (!stairCollider.enabled) {
    80.             stairsToggle = true;
    81.  
    82.             if (playerPosition.position.y > positionCheck.position.y) {
    83.                     actionText.text = "S";
    84.             }
    85.  
    86.             else if (playerPosition.position.y < positionCheck.position.y) {
    87.                 actionText.text = "W";
    88.             }
    89.  
    90.         }
    91.  
    92.         else {
    93.             stairsToggle = false;
    94.             yield return new WaitForSeconds (0.01f);
    95.             stairsToggle = true;
    96.  
    97.             if (playerPosition.position.y > positionCheck.position.y) {
    98.                 actionText.text = "S";
    99.             }
    100.  
    101.             else if (playerPosition.position.y < positionCheck.position.y) {
    102.                 actionText.text = "W";
    103.             }
    104.         }
    105.     }
    106.  
    107.     void OnTriggerExit2D () {
    108.         actionText.text = "";
    109.         OverrideInTrigger = false;
    110.     }
    111. }
    112.  
    There's an extra if statement inside the "stairsToggle" check within Update. Then inside that I put two if statements, each for a separate input key. This is for restricting the input possibilities.

    And two extra if statements inside the "!stairCollider.enabled" check within OnTriggerEnter2D. This is for restriction what letter shows up.

    The "table" and "phone" variable calls, are something specific to my game, and I will try to remove those from the code in the future, so it's more robust.
     
    Last edited: Oct 7, 2017