Search Unity

  1. Unity 2019.2 is now released.
    Dismiss Notice

LineCasting: Making GameObject Face the EndPoint of Raycast

Discussion in '2D' started by MisterSkitz, Nov 4, 2019.

  1. MisterSkitz

    MisterSkitz

    Joined:
    Sep 2, 2015
    Posts:
    623
    Okay, so I've tried multiple things but cannot achieve what I want. Currently what I have breaks the enemy and makes him behave retardedly. Not cool!

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class PlayerDetection : MonoBehaviour
    4. {
    5.     public Transform origin, end, player;
    6.     public float radarSpd;
    7.     public bool playerDetected;
    8.  
    9.     public static bool playerIsDetected;
    10.  
    11.     private int playerLayer = 1 << 8;
    12.     private Rigidbody2D enemyRb;
    13.     private Vector3 facePlayer;
    14.     private float angleToFace;
    15.  
    16.     private void Start()
    17.     {
    18.         enemyRb = GetComponentInParent<Rigidbody2D>();
    19.         playerIsDetected = false;
    20.     }
    21.  
    22.     private void Update()
    23.     {
    24.         PlayerDetector();
    25.         if (playerDetected == false)
    26.         {
    27.             Radar();
    28.         }
    29.         else { PlayerIsDetected(); }
    30.                
    31.     }
    32.  
    33.     void PlayerDetector()
    34.     {
    35.         Debug.DrawLine(origin.position, end.position, Color.red);
    36.         playerDetected = Physics2D.Linecast(origin.position, end.position, playerLayer);
    37.     }
    38.  
    39.     void Radar()
    40.     {
    41.         end.RotateAround(origin.position, Vector3.forward, radarSpd * Time.deltaTime);
    42.     }
    43.  
    44.     void PlayersPosition()
    45.     {
    46.         facePlayer = player.position - enemyRb.transform.position;
    47.         angleToFace = Mathf.Atan2(facePlayer.y, facePlayer.x) * Mathf.Rad2Deg;
    48.         enemyRb.transform.GetChild(0).eulerAngles = new Vector3(0,0,angleToFace);
    49.  
    50.     }
    51.  
    52.     void PlayerIsDetected()
    53.     {
    54.         if(playerDetected == true)
    55.         {
    56.             playerIsDetected = true;
    57.             end.position = player.position;
    58.             PlayersPosition();
    59.         }
    60.     }
    61. }
    62.  
    Okay, now that you can see what I'm working with, lemme provide a walk through of what I have going on:

    upload_2019-11-3_18-12-58.png
    (Above) Enemy is an empty GameObject that stores everything relevant to the enemy. The most important thing to note is that the rigidbody2D component resides on the Enemy not the EnemyObj(which is my sprite with collider)

    upload_2019-11-3_18-15-33.png
    (Above) I made a "radar" out of the line cast where the end point revolves around the origin point. When the player is detected, this function stops and it does focus on the player as it is designed (Below)
    upload_2019-11-3_18-18-24.png


    Ideally I'd like to have the enemy face the end point but I can't seem to get a rotation value out of it. So I tried this:(Below)
    upload_2019-11-3_18-20-24.png

    This code doesn't even remotely work. This causes the enemy to lose all senses and pop to a different position and refuses to look at the player. Now note how I am getting the child object of Enemy which is the EnemyObj(See Hierarchy image above)



    What I really want is to be able to make the enemy LookAt() the end point position. I have tried this but it makes the enemy disappear. I don't quite understand why. Does anyone know how to make either this code work or preferably how to make the enemy face towards the end point of the raycast?
     
  2. vakabaka

    vakabaka

    Joined:
    Jul 21, 2014
    Posts:
    1,134
    try something like:
    //up, down, -up, - down; depended how the sprite is painted
    enemy.transform.up = facePlayer;

    for something as selfguided rocket I have used:
    Code (CSharp):
    1. if (enemy != null && enemy.activeSelf)
    2.         {
    3. //direction
    4.             Vector2 dir = enemy.transform.position - transform.position;
    5. //- 1 or +1 to choose the rotation direction
    6.             angle = Mathf.Sign(Vector2.SignedAngle(transform.right, dir));
    7. //disable rotation if angle is to small
    8.             if (Mathf.Abs(Vector2.Angle(transform.right, dir)) < 5f)
    9.             {
    10.                 angle = 0;
    11.             }
    12.         }
    13.  
    14.         if (angle != 0)
    15.         {
    16. //rotate left or right
    17.             rb.MoveRotation(rb.rotation + rotateSpeed * angle * Time.fixedDeltaTime);
    18.         }
    lookAt is for the 3d world. The z-direction from one object will be rotated to the the target. In the 2d world then you will see the sprite from the side and sprite has zero thickness from the side, also you can't see it.
     
    MisterSkitz likes this.
  3. MisterSkitz

    MisterSkitz

    Joined:
    Sep 2, 2015
    Posts:
    623
    My enemy faces downwards by design. I'll have to figure out the correct alignment but I appreciate the code! I will give this a try as soon as I get the chance :)

    I'm pretty stuck on this!

    I tried using Raycast2D but it doesn't work nearly as well as the line cast for what I'm doing. I cannot seem to find anything even remotely close to what I'm trying to do on the interwebz either. Since the "end point" of my ray doesn't actually have a rotation, I cannot use its transform to make the enemy face the player with eulerAngles. Using Psuedo:

    1) Enemy spots player (Works)
    2) End point of ray sticks to player (Works)
    3) Enemy faces direction of end point (or player) - (Doesn't work)
     
  4. vakabaka

    vakabaka

    Joined:
    Jul 21, 2014
    Posts:
    1,134
    try just
    Code (CSharp):
    1. -transform.up = directionToTarget;
     
  5. MisterSkitz

    MisterSkitz

    Joined:
    Sep 2, 2015
    Posts:
    623
    This is backwards and confusing. Please elaborate, amigo :)
     
  6. MisterSkitz

    MisterSkitz

    Joined:
    Sep 2, 2015
    Posts:
    623
    Okay, so I'm certain that the cause of my problem is due to the fact my Rigidbody2D is on the empty and not the actual sprite (EnemyObj).

    You would think the two elements would be lined up on the same plane of transform but they are not. So here is my actual dilemma, to clarify.

    I'm needing to use the rotation of the Rigidbody2D, however, my sprite doesn't actually have a RB component(The parent does). That's why I'm using eulerAngles to rotate my sprite to face a particular direction(Which works in the movement script). What I need is the RB's z-rotation to become the sprites eulerAngles z-coordinate. So I'm going to need to store the rotation of the enemyFaceRotation = enemyRb.rotation.z and translate that information into the enemyRb.transform.GetChild(0).eulerAngles = new Vector3(0,0, enemyFaceRotation);

    My facePlayer variable will need to store the enemies z-rotation and enemyFaceRotation will need to store the facePlayer.z information.

    I'm basically taking notes for tomorrow.

    @vakabaka - I sincerely appreciate your advice, amigo! I wasn't able to implement your code style into this; it's more of a lack of knowledge on my part about linecasting/raycasting than anything. I've successfully designed A.I. movement with linecasting but it's easy to read true/false. What I lack understanding on is how to obtain the data from the cast, at the end point and use that information to determine a rotation. I'm getting close though! I'm learning :)
    Do not think I have ignored your advice, for I did try :)
     
    vakabaka likes this.
  7. vakabaka

    vakabaka

    Joined:
    Jul 21, 2014
    Posts:
    1,134
    sorry, I have spoken about something like:

    Code (CSharp):
    1.  void PlayersPosition()
    2.     {
    3.         facePlayer = player.position - enemyRb.transform.position;
    4. //if the child has other position as parent object, then
    5. //facePlayer = player.position - enemyRb.transform.GetChild(0).position;
    6.         //angleToFace = Mathf.Atan2(facePlayer.y, facePlayer.x) * Mathf.Rad2Deg;
    7.         enemyRb.transform.GetChild(0).up = - facePlayer;
    8.  
    9.     }
    enemyRb.transform.GetChild(0).up = - facePlayer;

    the line should just rotate a gameobject ( downwards) in the facePlayer direction (from the enemyRb to the player). The gameobject can be empty, with SpriteRenderer or RB2D. With transform.up = direction you are rotating just the needed transform.
     
    Last edited: Nov 6, 2019
    MisterSkitz likes this.
  8. MisterSkitz

    MisterSkitz

    Joined:
    Sep 2, 2015
    Posts:
    623
    I did take the time to try your suggestion but it didn't work. Not sure why though. Been a long time since I've been this stumped! However, I tried something and it almost works! (Note - I am using -enemyRot at the moment, an idea inspired by you)

    upload_2019-11-6_17-18-12.png

    Now I added an empty game object to Enemy and made my playerObj a child of that. I then rotated the z-axis 90 degrees to make the enemy face-right from the beginning.

    upload_2019-11-6_17-58-0.png upload_2019-11-6_17-58-41.png

    Ideally, rotating the Rigidbody2D isn't a difficult task. I am aware that I could make this way simpler on myself by making the enemyObj the RB. However, I'm accepting the challenge to learn something new. This is annoying, but fun at the same time :p

    There must be a minor tweak that I can make to get the enemyObj eulerAngle to work properly.

    So, my enemy now faces to the right. Do you see any way to implement your code you suggested into this? @vakabaka
    I feel as though your enemyRb.GetChild(0).GetChild(0).up = -facePlayer; may be the final recipe ingredient I'm missing. However, I cannot figure out how to implement this. I do appreciate your help, amigo!
     
  9. vakabaka

    vakabaka

    Joined:
    Jul 21, 2014
    Posts:
    1,134
    sorry, I have no idea now :eek:

    I mean, if I am not wrong, atan2 calculates the angle between world x-axis and the direction axis. Also if the object was rotated, then you should add the object-rotation to the atan2 rotation.
     
  10. vakabaka

    vakabaka

    Joined:
    Jul 21, 2014
    Posts:
    1,134
    as example both parts in the statements do the same rotation (look at mouse). Try it.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class LookAtMouse : MonoBehaviour
    6. {
    7.  
    8.     public bool useAtan2;
    9.     private void Update()
    10.     {
    11.         if (!useAtan2)
    12.         {
    13.             Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
    14.             Vector2 direction = mousePosition - (Vector2)transform.position;
    15.             transform.up = direction;
    16. //you can take the right axis
    17. //transform.right = direction;
    18.         }
    19.         else
    20.         {
    21.             Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
    22.             Vector2 direction = mousePosition - (Vector2)transform.position;
    23.             float angle =  Mathf.Atan2(mousePosition.y, mousePosition.x) * Mathf.Rad2Deg;
    24.             transform.eulerAngles = new Vector3(0, 0, angle - 90f);
    25. //then here is no need to add extra rotation
    26. //transform.eulerAngles = new Vector3(0, 0, angle);
    27.         }
    28.     }
    29. }
     
    Last edited: Nov 7, 2019
  11. MisterSkitz

    MisterSkitz

    Joined:
    Sep 2, 2015
    Posts:
    623
    upload_2019-11-6_18-53-28.png

    This seems to be pretty close. Lemme make a video.
    Your code is exactly like what I have researched. You're right 100%.
    However, I haven't seen anything online about what I'm trying to do. I lack the mathematical skills to define the formula. What I do know is that I'm missing one step. In your example here, you use angle but I'm going to need another calculation to convert the Mathf.Atan2 * MathfRad2Deg into, I dunno, perhaps a magnitude? Maybe I just need to divide something by the facePlayer?

    I am set on discovering the answer. And when I find out how blatantly obvious it actually is, I'll be embarrassed lol
     
  12. MisterSkitz

    MisterSkitz

    Joined:
    Sep 2, 2015
    Posts:
    623
    upload_2019-11-10_12-54-46.png

    Ok, okay you were right lol
    Took me awhile to process this information! I do appreciate your help greatly!

    Final solution creds @vakabaka
    Thanks amigo!
     
    vakabaka likes this.
  13. vakabaka

    vakabaka

    Joined:
    Jul 21, 2014
    Posts:
    1,134
    :eek: there are many children (or childs ? i don't know) in the game
     
  14. MisterSkitz

    MisterSkitz

    Joined:
    Sep 2, 2015
    Posts:
    623
    upload_2019-11-10_14-56-50.png

    Yes, The Enemy is the Parent, EnemyRotationController is Child(0), and the EnemyObj is the GrandChild(0).

    The EnemyRotationController is used to make the enemy face the proper direction because I designed it facing downwards. I was trying to use the Atan2() and it required the object facing a standard direction. So technically, using the code you provided, I no longer need this. However, it works fine so there isn't much point in changing it.

    Also note that I was having such a hard time with this because the Enemy parent is the Rigidbody2D. That way when I move it, all the children stay with the object and isn't rotating with my EnemyObj when it changes direction face. I have rays casting from top,left, right, and down. I use these rays to detect walls and the enemy will only move where walls are not detected. So if these raycast were a child of my EnemyObj, the rays would rotate with the enemy. So down would become right and it would appear as though right was open and the enemy would move in the wrong directions.

    Player Detection holds the script for this. OriginPoint and EndPoint are supposed to be childs of that.. I'll fix that now lol

    WallDetectionAndEnemyDetection holds the script for wall detection named EnemyDetection. This is where all of the A.I. code occurs for movement.

    I'm doing this as a tutorial I'm working on, so I appreciate you helping me solve this issue! I'll be sure to give you an honorable mention in the video! :)
     
  15. MisterSkitz

    MisterSkitz

    Joined:
    Sep 2, 2015
    Posts:
    623
    upload_2019-11-10_15-19-12.png

    My Enemy contents are supposed to look like this.I don't remember why I moved them out.