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

having an issue with transform.LookAt

Discussion in '2D' started by jakebaker865, Jul 4, 2020.

  1. jakebaker865

    jakebaker865

    Joined:
    May 6, 2020
    Posts:
    36
    Hi All, Thanks in advance.

    I have a 2D platformer with patrolling enemies, but the issue is that you can be behind the enemy and they won't turn around to look at you, even when using transform.LookAt(target);

    Code below. Let me know what I'm doing wrong here.

    Code (CSharp):
    1. public int health = 100;
    2.  
    3.  
    4.     private Rigidbody2D rb;
    5.  
    6.     public float speed = 1f;
    7.     public float distance = 0.3f;
    8.  
    9.     private bool movingRight = true;
    10.  
    11.     public Transform groundDetection;
    12.     public float attackRange = 0.4f;
    13.     public float dashForce = 5f;
    14.     public Transform target;
    15.     private Animator enemyAnim;
    16.  
    17.     public bool canMove;
    18.     public bool isAttacking;
    19.  
    20.  
    21.     void Start()
    22.     {
    23.         rb = GetComponent<Rigidbody2D>();
    24.         enemyAnim = GetComponent<Animator>();
    25.         canMove = true;
    26.     }
    27.  
    28.     void Update()
    29.     {
    30.        
    31.  
    32.         if (rb.velocity.x > 0)
    33.         {
    34.             enemyAnim.SetBool("IsMoving", true);
    35.         }
    36.  
    37.  
    38.         if (!canMove)
    39.         {
    40.             transform.Translate(Vector2.zero);
    41.             enemyAnim.SetBool("IsMoving", false);
    42.         }
    43.         else
    44.         {
    45.             transform.Translate(Vector2.right * speed * Time.deltaTime);
    46.         }
    47.        
    48.  
    49.  
    50.         if(Vector2.Distance(target.position, transform.position) <= attackRange)
    51.         {
    52.             enemyAnim.SetBool("IsAttacking", true);
    53.         }
    54.         else if (Vector2.Distance(target.position, transform.position) > attackRange)
    55.         {
    56.             enemyAnim.SetBool("IsAttacking", false);
    57.         }
    58.  
    59.  
    60.         RaycastHit2D groundInfo = Physics2D.Raycast(groundDetection.position, Vector2.down, distance);
    61.        
    62.         if (groundInfo.collider == false)
    63.         {
    64.             if (movingRight == true)
    65.             {
    66.                 transform.eulerAngles = new Vector3(0, -180, 0);
    67.                 movingRight = false;
    68.             }
    69.             else
    70.             {
    71.                 transform.eulerAngles = new Vector3(0, 0, 0);
    72.                 movingRight = true;
    73.             }
    74.         }
    75.  
    76.      
    77.  
    78.     }
    79.  
    80.     void FixedUpdate()
    81.     {
    82.        
    83.     }
    84.  
    85.  
    86.  
    87.  
    88.     public void TakeDamage(int damage)
    89.     {
    90.         health -= damage;
    91.  
    92.         if (health <= 0)
    93.         {
    94.             Die();
    95.         }
    96.     }
    97.  
    98.     void Die()
    99.     {
    100.         Destroy(gameObject);
    101.     }
    102.  
    103.     public void DashAttack()
    104.     {
    105.         if(movingRight == true)
    106.         {
    107.            
    108.             rb.velocity = Vector2.zero;
    109.             rb.AddForce(new Vector2(dashForce, 0f), ForceMode2D.Impulse);
    110.         }
    111.  
    112.         if (!movingRight)
    113.         {
    114.            
    115.             rb.velocity = Vector2.zero;
    116.             rb.AddForce(new Vector2(dashForce * -1, 0f), ForceMode2D.Impulse);
    117.         }
    118.        
    119.     }
    120.  
    121.     public void IsAttacking()
    122.     {
    123.        
    124.         canMove = false;
    125.     }
    126.  
    127.     public void IsNotAttacking()
    128.     {
    129.         canMove = true;
    130.     }
     
  2. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,256
    I avoid using LookAt in 2D. I instead get the angle and rotate the sprite that way.

    Code (CSharp):
    1. Vector2 dir = target.position - transform.position;
    2. //We get the square magnitude because it is cheaper to use than Vector2.Distance(uses sqrt which isn't a cheap calculation). We also cache the value so we avoid calculating it more than once in the same frame.
    3. float sqrDistance = dir.sqrMagnitude;
    4. //Since we are getting the square distance we need to use the square of our range. Still cheaper than the sqrt, we can cache this value so we don't calculate it every frame too making it even cheaper.
    5. float sqrRange = attackRange * attackRange;
    6. dir.Normalize();//Normalize the direction so we can get the angle.
    7. float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;//Atan2 gives the angle in radians so we convert it to degrees.
    8. rb.rotation = angle;
    9. if (sqrDistance <= sqrRange)
    10. {
    11.     enemyAnim.SetBool("IsAttacking", true);
    12. }
    13. else
    14. {
    15.     enemyAnim.SetBool("IsAttacking", false);
    16. }
    17.  
    I also recommend to change the movement to use the Rigidbody2D instead of the transform. If you are going to use physics, don't use the transform to move or rotate.
     
  3. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,256
    Just realized that this is a for a platformer. The above code is for top down.

    You can replace the rotation portion with this.
    Code (CSharp):
    1. if (dir.x > 0)
    2. {
    3.     //Right
    4.     transform.localScale = new Vector3(1f, 1f, 1f);
    5. }
    6. else if (dir.x < 0
    7. {
    8.     //Left
    9.     transform.localScale = new Vector3(-1f, 1f, 1f);
    10. }
     
  4. jakebaker865

    jakebaker865

    Joined:
    May 6, 2020
    Posts:
    36

    Hey Chris, Thanks alot for the help. Have any thoughts on how to use Rigidbody and not tranform.Translate? I'd rather use it in order to have more control on wall detection.

    I looked all over and couldn't figure out how to keep in the raycasthit2d section in order to keep it on the platform.
     
  5. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,256
    You can do it a few ways.

    Code (CSharp):
    1. rb.velocity = moveDir * speed;
    2.  
    3. rb.MovePosition(rb.position + moveDir * speed * Time.fixedDeltaTime);
    4.  
    5. //We can also use AddForce but I only use it for jumping, knock back and other single force type movement like a dash.
    One little caveat with MovePosition. It will only accept the last position passed in. This means if you use it in Update it might not give you consistent movement, because Update and FixedUpdate(Physics) are not the same frequency. FixedUpdate is slower, 50 fps or every 0.02s. This is why I used Time.fixedDeltaTime and it should be in FixedUpdate.

    There are also a few ways to stop them from walking off the edge of the platforms.
    1. A raycast that is slightly in front of the enemy so it can "predict" the edge when the raycast comes back with no platform.
    2. Empty triggers at the ends of the platforms, once the enemy enters the trigger it should turn around.
    3. Way points that the enemy moves too and turns around once they reach it setting it to the next way point.
    There are probably more, its programming after all.

    1 is probably the least efficient, raycasting isn't bad, doing it every frame when there are more efficient ways to me is bad.

    2 is the easiest. It only needs the triggers to have a tag that the enemy checks for in OnTriggerEnter2D so it can turn around. These can be attached to each platform and positioned easily.

    3. Is probably the most efficient but more time consuming to build. The pay off probably isn't worth it compared to 2.
     
  6. jakebaker865

    jakebaker865

    Joined:
    May 6, 2020
    Posts:
    36
    This is a bit of a mess, but this is what I have so far.

    The main idea that's missing is that the enemy should patrol the platform (still haven't gotten that to work, since I'm not sure how to flip them once they OnTriggerEnter2D and make them walk the other way. whjat I have right now works if enemy is standing still and player gets in their attackRange, the attack will at least go toward the player.

    Any thoughts on implementing them having them patrol and turn once OnTriggerEnter2D? (I started something in FixedUpdate)


    Code (CSharp):
    1.     public int health = 100;
    2.  
    3.  
    4.     private Rigidbody2D rb;
    5.  
    6.     public float speed = 1f;
    7.     public float distance = 0.3f;
    8.  
    9.     private bool movingRight = true;
    10.  
    11.     public Transform groundDetection;
    12.     public float attackRange = 0.4f;
    13.     public float dashForce = 5f;
    14.     public Transform target;
    15.     private Animator enemyAnim;
    16.  
    17.     public bool canMove;
    18.     public bool isAttacking;
    19.  
    20.     private float nextAttackTime = 0;
    21.     public float attackCooldownTime = 0.5f;
    22.  
    23.  
    24.     void Start()
    25.     {
    26.         rb = GetComponent<Rigidbody2D>();
    27.         enemyAnim = GetComponent<Animator>();
    28.         canMove = true;
    29.     }
    30.  
    31.     void Update()
    32.     {
    33.        
    34.  
    35.  
    36.         Vector2 targetDir = target.position - transform.position;
    37.         Vector2 dir = transform.position;
    38.         //We get the square magnitude because it is cheaper to use than Vector2.Distance(uses sqrt which isn't a cheap calculation). We also cache the value so we avoid calculating it more than once in the same frame.
    39.         //float sqrDistance = dir.sqrMagnitude;
    40.         //Since we are getting the square distance we need to use the square of our range. Still cheaper than the sqrt, we can cache this value so we don't calculate it every frame too making it even cheaper.
    41.         //float sqrRange = attackRange * attackRange;
    42.         dir.Normalize();//Normalize the direction so we can get the angle.
    43.         targetDir.Normalize();              
    44.         //float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;//Atan2 gives the angle in radians so we convert it to degrees.
    45.  
    46.         if (Vector2.Distance(target.position, transform.position) <= attackRange)
    47.         {
    48.             enemyAnim.SetBool("IsAttacking", true);
    49.             if (targetDir.x > 0)
    50.             {
    51.                 //Right
    52.                 transform.localScale = new Vector3(1f, 1f, 1f);
    53.             }
    54.             else if (targetDir.x < 0)
    55.             {
    56.                 //Left
    57.                 transform.localScale = new Vector3(-1f, 1f, 1f);
    58.             }
    59.         }
    60.         else
    61.         {
    62.             enemyAnim.SetBool("IsAttacking", false);
    63.         }  
    64.     }
    65.  
    66.  
    67.  
    68.  
    69.     void FixedUpdate()
    70.     {
    71.         /*Vector2 dir = transform.position;
    72.         dir.Normalize();
    73.         if (dir.x > 0)
    74.         {
    75.             enemyAnim.SetBool("IsMoving", true);
    76.             rb.velocity = new Vector2(speed, 0);
    77.         }
    78.         else if (dir.x < 0)
    79.         {
    80.             enemyAnim.SetBool("IsMoving", true);
    81.             rb.velocity = new Vector2(speed * -1, 0);
    82.         }*/
    83.  
    84.     }
    85.  
    86.  
    87.     void OnTriggerEnter2D()
    88.     {
    89.  
    90.     }
    91.  
    92.  
    93.  
    94.     public void TakeDamage(int damage)
    95.     {
    96.         health -= damage;
    97.  
    98.         if (health <= 0)
    99.         {
    100.             Die();
    101.         }
    102.     }
    103.  
    104.  
    105.     void Die()
    106.     {
    107.         Destroy(gameObject);
    108.     }
    109.  
    110.  
    111.     public void DashAttack()
    112.     {
    113.         Vector2 dir = target.position - transform.position;
    114.         //We get the square magnitude because it is cheaper to use than Vector2.Distance(uses sqrt which isn't a cheap calculation). We also cache the value so we avoid calculating it more than once in the same frame.
    115.         float sqrDistance = dir.sqrMagnitude;
    116.         //Since we are getting the square distance we need to use the square of our range. Still cheaper than the sqrt, we can cache this value so we don't calculate it every frame too making it even cheaper.
    117.         float sqrRange = attackRange * attackRange;
    118.         dir.Normalize();//Normalize the direction so we can get the angle.
    119.         float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;//Atan2 gives the angle in radians so we convert it to degrees.
    120.         if (dir.x > 0)
    121.         {
    122.             //Right
    123.             rb.velocity = Vector2.zero;
    124.             rb.AddForce(new Vector2(dashForce, 0f), ForceMode2D.Impulse);
    125.         }
    126.         else if (dir.x < 0)
    127.         {
    128.             //Left
    129.             rb.velocity = Vector2.zero;
    130.             rb.AddForce(new Vector2(dashForce * -1, 0f), ForceMode2D.Impulse);
    131.         }
    132.     }