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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Teleporting inside walls!

Discussion in '2D' started by DkoiOP, Oct 27, 2022.

  1. DkoiOP

    DkoiOP

    Joined:
    Apr 26, 2022
    Posts:
    11
    I have initially tried getting help on Reddit and discord servers to no avail although many have tried. So this is my last ditch effort.

    I am trying to make a dash of sorts for my character in a top down 2d style.
    I am using a Rigidbody2D with the body type Kinematic.

    private void OnDash(InputAction.CallbackContext context)
    {
    Vector2 dashPos = rb.position + movement * dashDistance;
    RaycastHit2D dashCheck = Physics2D.Raycast(rb.position, movement, dashDistance, dashLayerMask);
    Debug.DrawRay(rb.position, movement * dashDistance, Color.red, 5f);
    if(dashCheck.collider != null)
    {
    dashPos = dashCheck.point;
    Debug.Log("Something hit.");
    Debug.DrawLine(rb.position, dashCheck.point, Color.green, 10f);
    }
    rb.position = dashPos;
    Debug.Log("Nothing hit.");
    Debug.Log(dashPos);
    }


    this function works as intended when NOT colliding with walls. When I try to dash at a wall, it teleports my 50% of my character into the wall.
    I know this is happening because I am setting my rb.position to dashCheck.point using the center of my character. But what I am trying to figure out is how to set an offset of some sort that is the distance of half of my character if not a tad more to the dashCheck.point so that half my character doesn't go into the wall.

    I have tried a ton of things such as seeing if there was a way to send a raycast back towards the direction that the first raycast was sent, and then just have the distance be half the length of my character. But this resulted in me teleporting randomly around. But nothing has worked.

    Can anyone help?
    Hope I explained my issue correctly here.

    Full Controller script:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.InputSystem;
    5.  
    6. public class PlayerController : MonoBehaviour
    7. {
    8.     //Input Action Map
    9.     PlayerActionMaps playerActionMaps; InputAction movementInput;
    10.  
    11.     //Player Related
    12.     Vector2 movement; float moveSpeed = 5f; Rigidbody2D rb; float dashDistance = 5f;
    13.  
    14.     //Cast Related
    15.     [SerializeField] private LayerMask dashLayerMask, moveLayerMask;
    16.     ContactFilter2D contactFilter; List<RaycastHit2D> hitResults = new List<RaycastHit2D>(); float collisionOffset = 0.1f;
    17.  
    18.     private void Awake()
    19.     {
    20.         playerActionMaps = new PlayerActionMaps();
    21.         rb = GetComponent<Rigidbody2D>();
    22.         contactFilter.layerMask = moveLayerMask;
    23.     }
    24.  
    25.     private void OnEnable()
    26.     {
    27.         playerActionMaps.Gameplay.Enable();
    28.         movementInput = playerActionMaps.Gameplay.Movement;
    29.         movementInput.performed += OnMove;
    30.         movementInput.canceled += OnMove;
    31.         playerActionMaps.Gameplay.Dash.performed += OnDash;
    32.     }
    33.  
    34.     private void OnMove(InputAction.CallbackContext context)
    35.     {
    36.         movement = context.ReadValue<Vector2>();
    37.     }
    38.  
    39.     private void FixedUpdate()
    40.     {
    41.         if (movement != Vector2.zero)
    42.         {
    43.             bool success = TryMove(movement);
    44.             if (!success)
    45.             {
    46.                 success = TryMove(new Vector2(movement.x, 0));
    47.                 if (!success)
    48.                 {
    49.                     success = TryMove(new Vector2(0, movement.y));
    50.                 }
    51.             }
    52.         }
    53.     }
    54.  
    55.     private bool TryMove(Vector2 movement)
    56.     {
    57.         int hitCount = rb.Cast
    58.             (movement,
    59.             contactFilter,
    60.             hitResults,
    61.             collisionOffset);
    62.         if (hitCount == 0)
    63.         {
    64.             rb.MovePosition(rb.position + movement * moveSpeed * Time.fixedDeltaTime);
    65.             return true;
    66.         }
    67.         else return false;
    68.     }
    69.  
    70.     private void OnDash(InputAction.CallbackContext context)
    71.     {
    72.         Vector2 dashPos = rb.position + movement * dashDistance;
    73.         RaycastHit2D dashCheck = Physics2D.Raycast(rb.position, movement, dashDistance, dashLayerMask);
    74.         Debug.DrawRay(rb.position, movement * dashDistance, Color.red, 5f);
    75.         if(dashCheck.collider != null)
    76.         {
    77.             dashPos = dashCheck.point;
    78.             Debug.Log("Something hit.");
    79.             Debug.DrawLine(rb.position, dashCheck.point, Color.green, 10f);
    80.         }
    81.         rb.position = dashPos;
    82.         Debug.Log("Nothing hit.");
    83.         Debug.Log(dashPos);
    84.     }
    85. }
     
  2. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,623
    Why are you using Raycast to represent your character when you have already discovered that you can use Rigidbody2D.Cast to find where it would hit exactly at the wall?

    This gives you not only the point at which you hit but also the distance to the point at which you'd hit therefore how far you can move in that direction?

    So, why can you not use Rigidbody2D.Cast ?
     
  3. DkoiOP

    DkoiOP

    Joined:
    Apr 26, 2022
    Posts:
    11
    I don't get how I would use rb.cast to dash/teleport my character though. And I was afraid that spending time to figure this out would result in the same issue of my character getting stuck in the wall. Since I think I could get it to maybe dash my character. But how would I detect if I had hit a wall on my dash and then to teleport me to said wall. But not in the wall.
     
  4. DkoiOP

    DkoiOP

    Joined:
    Apr 26, 2022
    Posts:
    11
    Okay, so after messing around trying to figure it out. I can't get the character to teleport to the object that I am colliding with in the cast without teleporting into the wall. And when I try to set an offset it just causes my dash not to work at all only moving me a smidge.
     
  5. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,623
    You can use raycast but you don't know how to use cast? They return the same thing except one is a line and the other is the results of checking all the colliders. I don't see how one can be confusing but the other isn't! :)

    Is there a specific part of this you don't follow? Cast, get the distance, move only that distance? The distance is how far you can move. I don't know specifically what the confusion is, sorry.
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,964
    Dash can be handled a bit simpler than this.

    When a successful "dash" is started:
    - set either a timer or a distance counter to the desired time or distance of the dash.
    - observe the last commanded move direction (call this the "dash direction")

    Each update / fixed upate, when dash is active:
    - possibly play a different animation (blur, tumble, etc)
    - disable directional control and use the dash direction.
    - increase speed of player movement
    - always move the player in the same way (same code) as when not dashing
    - when they hit an obstacle, cancel dash
    - when the time (or distance) reaches zero, finish dash (perhaps restore animation state)
     
  7. DkoiOP

    DkoiOP

    Joined:
    Apr 26, 2022
    Posts:
    11
    No, like. I get that, but I still have the same issue of it teleporting my character 50% in the wall or it just does little tiny dashes that still cause me to get stuck in walls anyways.
     
  8. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,623
    Then you're using it wrong if you're using cast to do so. It means you're moving the Rigidbody2D too far, probably moving it to the actual hit point rather than as I've said twice above, the distance along the direction so it moves to where it would hit. This, I feel, is your misunderstanding.

    Of course, I'm guessing at what you're doing. There's no fundamental problem with the query so what's left is you using it incorrectly. :)

    Whilst not exactly the same, see the following video which is using Rigidbody2D.Cast to move around. That doesn't end up "50% in the wall":
     
  9. DkoiOP

    DkoiOP

    Joined:
    Apr 26, 2022
    Posts:
    11
    If you look at my script, I am able to use cast to move around as well. And it works perfect. I don't end up in walls or anything. The only thing I had to do was add an offset depending on my speed, because sometimes if my speed shot up i'd get stuck in the wall ever so slightly.
    What I can't figure out no matter how hard I try is how to use the rb2d.cast to essentially teleport my character(dash) to a location without getting stuck in a wall. Say my dash is 50 steps, I dash 50 steps. That works. But now if I am looking at a wall that is 30 steps away, I don't want to dash the full 50 steps since it will teleport me on the other side of the wall. So using either a raycast or rb.cast I tried detecting the wall and setting that as the point at which I teleport too.
    It is preventing me from being able to dash through the wall, but as a result I get stuck 50% into the wall. What I have been banging my head trying to figure out is how to use my rb.cast or raycast, get the point of contact with the collider and then set it back the width of my character. I can do the first parts, but I don't know how to set it back or set an offset.
    All my attempts at setting an offset result it all sorts of messy movement that I don't want.
    There probably is some simple way of doing this, but I have been using Unity for less than a month and I've been learning C# for the same amount of time. And Asides from a few Udemy courses and YouTube tutorials, i've been browsing Unity API and kind of just feeling my way through things.
     
  10. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,623
    I've already looked at your script and as I've said above, it doesn't use "Rigidbody2D.Cast()" for the dash (it uses raycast) which is where you're having the problem. You're doing offsets and other hacks when you don't need to, as I've already stated. Also repeating myself, I don't know why you consider Rigidbody2D.Cast needed for movement but this "dash" which is just movement still, you can drop back to using raycast and hacking offsets.

    RaycastHit2D.centroid is where the object center is when it contacts at the RaycastHit2D.point. This is where the Collider2D or Rigidbody2D is positioned when it hits. For a raycast, the centroid is identical to the point because it's an infinitely small point being cast. For a Collider2D it'll be different because it's not a point but the collider shape, the same for a Rigidbody2D as it's offset by whatever colliders it has.

    Again, the RaycastHit2D returns the distance you moved. The position the collider/rigidbody is at is simply the start position + direction * distance.

    I keep repeating myself above so I won't do it again. Sorry if I sound harsh but you really need to read what has been put rather than repeating what you're doing incorrectly.

    I wanted to add that the code for movement is also incorrect. You want to be checking the distance you intend to move which is "moveSpeed" in your case. If you hit something or not, you can then always move the distance minus the collision offset. You shouldn't move by the collision-offset as you're doing, that makes little sense and doesn't even represent the move you want to take. Also, unless "movement" is always a unit-normal, you'll move differently than you try to detect too. If movement was "10,0" then the cast would use "1,0" for a distance of "collisionOffset" but then you'd move by "10,0" * moveSpeed. It just doesn't make sense despite you saying "it works".
    Code (CSharp):
    1.             int hitCount = rb.Cast
    2.                 (movement,
    3.                 contactFilter,
    4.                 hitResults,
    5.                 collisionOffset);
    6.             if (hitCount == 0)
    7.             {
    8.                 rb.MovePosition(rb.position + movement * moveSpeed * Time.fixedDeltaTime);
    9.                 return true;
    10.             }
    11.             else return false;
     
    Kurt-Dekker likes this.
  11. DkoiOP

    DkoiOP

    Joined:
    Apr 26, 2022
    Posts:
    11
    I have already tried using RB2D.Cast just like I have for my movement for my dash and found no success. It resulted in the exact same issue.
    Although I do appreciate your help, don't get agitated saying I am not reading what you have said.
    I have tried implementing what you have said.
    Like I have said above I have tried using the rigidbody2d.cast as well as raycasthit2d.point/centroid all to no avail. People keep telling me all sorts of "fixes" for this issue. But when I try to implement any of them, none work. Or the require me to use a Dynamic body type. Or requires doing a bunch of work around stuff that I don't understand and no one uses terms I can search in the Unity API. People just say "Oh, you teleport to the position and just go back a few steps" which is exactly what I want to do, and I know it's what I have to do, but it's not help at all from people saying this because I don't know how to do it.
    I've been trying to get this to work for almost a week now and it still will not work.
     
  12. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,623
    This simply means you're using it wrong, focus on correcting that rather than assuming that somehow Rigidbody2D.Cast doesn't work and you need to do something else.

    I'm not agitated at all. I'm highlighting that you're not responding to anything I'm saying but simply restating what you've already said. If you don't respond to clear examples of what you're doing wrong, you simply cannot be helped is all.

    In the post above, you've not asked a single question about what I wrote. You're again simply restating how hard it is for you to get this working, something you've already stated and the reason why I'm explaining stuff.

    Maybe respond and ask questions to what has been written by me that you don't understand. Only then will you understand how to move forward.

    After all, the reason I'm taking my time here at the weekend is to HELP you.
     
  13. DkoiOP

    DkoiOP

    Joined:
    Apr 26, 2022
    Posts:
    11
    I wasn't assuming that RB2D.cast doesn't work. What I am saying is, while using it, I don't know how to set an offset. I am getting identical results to when I use Raycasthit2D.
    And I am not trying to restate stuff, but what I am restating is still my issue. I don't know how to set an offset to the point at which I am grabbing. I can detect the wall. I can get the point of detection. I don't know how to set an offset though, since when I try it acts all weird. But even me saying this here is restating. But I don't know what else to say.
     
  14. DkoiOP

    DkoiOP

    Joined:
    Apr 26, 2022
    Posts:
    11
    Here is what I end up with.

    The triangle is something I want to be able to dash through, and not detect. The square is something i don't want to dash through and detect. I know why I get stuck in the wall, I just don't know how to fix it.
     
  15. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,623
    Again, you've not asked about a single thing I've posted above which is telling you how to correctly use it. You are asking for help and not replying to a single thing that is being stated that will help.

    Seeing a video of it not working doesn't move anyone forward. I already assume it fails as you've stated several times and I've tried my best to tell you why it's failing and what you're doing wrong.
     
  16. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,623
    This post here. And all you replied with was "I have already tried using RB2D.Cast just like I have for my movement for my dash and found no success. It resulted in the exact same issue." which has nothing whatsoever to do with what I wrote for you.

    In the end, it seems that you cannot understand what I'm telling you so I'll leave others to attempt it.
     
  17. DkoiOP

    DkoiOP

    Joined:
    Apr 26, 2022
    Posts:
    11
    "Cast, get the distance, move only that distance" "The distance is how far you can move." This makes no sense to me.
    How do I grab the distance? And when I have the distance and I set my rb.position to that distance. How do I make sure it doesn't teleport me into the wall? Also when getting the distance, how do I even use the distance?
     
  18. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,623
    Look at the docs: RaycastHit2D.distance

    I don't see why. Ignore your problem. Let's say you do a raycast in a direction, over a maximum distance. It returns a RaycastHit2D. You know this. You should then understand the "RaycastHit2D.distance" (as the docs state) is the distance moved before the hit happened. This will be zero to the maximum distance you specify. This is raycast 101.

    Cast 10 right, it might hit at a distance of (say) 3.5.

    You don't set a position to a distance.

    As I stated when I said the following:
    Rigidbody2D.Cast also returns a RaycastHit2D and you use the contents the same.
     
  19. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,623
    I'll post this separately. Ignore your project. Just try to use the Rigibdody2D.Cast or Raycast in an empty simple set-up so you can follow how to use the results.

    RaycastHit2D.point = point of contact
    RaycastHit2D.distance = distance to move to hit at that point with whatever you cast (point, rigidbody, collider) along the "direction" argument you passed.
     
  20. DkoiOP

    DkoiOP

    Joined:
    Apr 26, 2022
    Posts:
    11
    I have looked at the RaycastHit2D.distance API before. And when using rb.position = rb.position + direction * raycasthit2d.distance it still sticks me 50% into the wall. What I am trying to figure out is how to set an offset that works.
     
  21. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,623
    Yes but as I've already said above, this is with the Physics2D.Raycast. That distance is the distance to move the infinitely small point to the hit point (a ray is casting a small point). Your Rigidbody2D and its colliders are NOT an infinitely small point! If you do this with Rigidbody2D.Cast, the distance will be how far to move the Rigidbody2D position so that it just hits. You don't even need this, use RaycastHit2D.centroid as the position to move to as I've said! You don't need to be messing with hacky offsets.

    Use the attached test project, it does a cast then moves the Rigidbody2D to the RaycastHit2D.centroid.

    Before (moving right using a single cast):
    Before.png

    After (using a single cast and the RaycastHit2D.centroid):
    After.png

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3.  
    4. public class CastTest : MonoBehaviour
    5. {
    6.     public Vector2 StartPosition = new Vector2(-6f, 0f);
    7.     public Vector2 Direction = Vector2.right;
    8.     public float Speed = 10f;
    9.  
    10.     private Rigidbody2D m_Rigidbody;
    11.     private List<RaycastHit2D> m_Results = new List<RaycastHit2D>(1);
    12.  
    13.     private void Start()
    14.     {
    15.         m_Rigidbody = GetComponent<Rigidbody2D>();
    16.     }
    17.  
    18.     private void FixedUpdate()
    19.     {
    20.         m_Rigidbody.position = StartPosition;
    21.  
    22.         var resultCount = m_Rigidbody.Cast(Direction, m_Results, Speed);
    23.         if (resultCount > 0)
    24.         {
    25.             m_Rigidbody.MovePosition(m_Results[0].centroid);
    26.         }
    27.     }
    28. }
    29.  
     

    Attached Files:

  22. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,623
    In the above code, you can use the distance returned so replace with:
    Code (CSharp):
    1. m_Rigidbody.MovePosition(m_Rigidbody.position + Direction * m_Results[0].distance);
    As an example of continuous movement, you can use the attached project which does this:
    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3.  
    4. public class CastTest : MonoBehaviour
    5. {
    6.     public Vector2 Direction = Vector2.right;
    7.     public float Speed = 4f;
    8.  
    9.     private Rigidbody2D m_Rigidbody;
    10.     private List<RaycastHit2D> m_Results = new List<RaycastHit2D>(1);
    11.  
    12.     private void Start()
    13.     {
    14.         m_Rigidbody = GetComponent<Rigidbody2D>();
    15.     }
    16.  
    17.     private void FixedUpdate()
    18.     {
    19.         Direction.Normalize();
    20.  
    21.         // Calculate the distance we'd like to move.
    22.         var distance = Speed * Time.fixedDeltaTime;
    23.  
    24.         // Calculate the distance to hit something.
    25.         if (m_Rigidbody.Cast(Direction, m_Results, distance) > 0)
    26.             distance = m_Results[0].distance;
    27.  
    28.         // Move the body.
    29.         m_Rigidbody.MovePosition(m_Rigidbody.position + Direction * distance);
    30.     }
    31. }
    32.  
     

    Attached Files:

  23. DkoiOP

    DkoiOP

    Joined:
    Apr 26, 2022
    Posts:
    11
    So the issue of why it wasn't working for me is because i was using my collisionOffset instead of my moveSpeed.
    While it is now pretty much working... I still seem to clip into the wall just enough to get stuck. It's like 1 pixel. How can I fix this?
     
  24. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,623
    Yes, I said that above.

    Don't always move so you're touching. Find the distance to touching then subtract a small amount from that distance.

    You can also use the ContactFilter2D to always only return hits that in the direction you're moving. This results in it ignoring things "behind" you or opposite the direction of movement.