How do i make enemy AI face the player - c#

I'm trying to find a way to rotate the enemy towards the player. Currently it follows/stops and shoots the player. Have not figured out how to make it rotate. Please help if you can. I've tried couple of different things on here but they dont seem to work.
I've tried creating a Flip function with isFacingRight.
private bool facingRight;
public float speed;
public float stoppingDistance;
public float retreatDistance;
private Animator enemyAnimation;
private bool isDead;
private float direction;
public static bool enemyShoot = false;
private float timeBtwShots;
public float startTimeBtwShots;
public GameObject projectile;
private Transform player;
[SerializeField]
private Stat health;
private void Awake()
{
health.Initialize();
}
// Start is called before the first frame update
void Start()
{
//Player tag
player = GameObject.FindGameObjectWithTag("Player").transform;
enemyAnimation = GetComponent<Animator>();
isDead = false;
}
// Update is called once per frame
void Update()
{
if (Vector2.Distance(transform.position, player.position) > stoppingDistance)
{
transform.position = Vector2.MoveTowards(transform.position, player.position, speed * Time.deltaTime);
timeBtwShots = startTimeBtwShots;
if(player.position.x > transform.position.x && !facingRight) //if the target is to the right of enemy and the enemy is not facing right
Flip();
if(player.position.x < transform.position.x && facingRight)
Flip();
}
else if (Vector2.Distance(transform.position,player.position) <= stoppingDistance && Vector2.Distance(transform.position,player.position)>retreatDistance)
{
transform.position = this.transform.position;
}
else if (Vector2.Distance(transform.position, player.position) > retreatDistance)
{
transform.localScale = new Vector2(-1f, 1f);
transform.position = Vector2.MoveTowards(transform.position, player.position, -speed * Time.deltaTime);
timeBtwShots = 100;
enemyAnimation.SetTrigger("Attack");
}
if (enemyShoot == true)
{
//Stops animation Loop
enemyShoot = false;
enemyAnimation.SetTrigger("Shoot");
}
if (timeBtwShots <= 0)
{
Instantiate(projectile, transform.position, Quaternion.identity);
timeBtwShots = startTimeBtwShots;
}
else
{
timeBtwShots -= Time.deltaTime;
}
if (health.CurrentVal == 0)
{
timeBtwShots = 100;
FindObjectOfType<SoundsScript>().Play("SaibaDeath");
isDead = true;
enemyAnimation.SetBool("Dead", isDead);
Destroy(gameObject, 2f);
}
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "ProjectileEnem")
{
health.CurrentVal -= 50;
enemyAnimation.SetTrigger("Hurt");
}
}
//Disables enemeies when off screen
private void OnBecameInvisible()
{
GetComponent <Enemy> ().enabled = false;
}
//Re enables enemies once they are visible
private void OnBecameVisible()
{
GetComponent<Enemy>().enabled = true;
}
void Flip(){
Vector3 scale = transform.localScale;
scale.x *= -1;
transform.localScale = scale;
facingRight = !facingRight;
}
}

Try this:
spriteRenderer.flipX = transform.position.x < player.position.x;
SpriteRenderer contains the flipX and flipY property, you do not need to create your own Flip() function.
You need a reference to the SpriteRenderer on your GameObject.
The above code is assuming that your sprite faces left on default. If your sprite faces right normally, change the < to >.

Related

How to flip character when moving left unity 2D

I am trying to flip my character sprite when moving left in my game, and I have followed multiple tutorials however my sprite does not seem to flip. It is always facing the same way.
Below is my code for my character's movement. I have created a Flip() function and 2 if statements used to call the function. The character can move left, right, up and down (no jumping).
I cannot seem to see where an error would be and why it is not flipping, so any help would be appreciated. thank you.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
// Start is called before the first frame update
private Animator animate;
public float moveSpeed = 6f;
bool facingRight = true;
public Rigidbody2D rb;
Vector2 movement;
private void Start()
{
animate = gameObject.GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
movement.x = Input.GetAxisRaw("Horizontal");
movement.y = Input.GetAxisRaw("Vertical");
animate.SetFloat("Speed", Mathf.Abs(movement.x));
if(movement.x < 0 && facingRight)
{
Flip();
}
else if (movement.x > 0 && !facingRight)
{
Flip();
}
}
void FixedUpdate()
{
rb.MovePosition(rb.position + movement * moveSpeed * Time.fixedDeltaTime);
}
void Flip()
{
Vector3 currentScale = gameObject.transform.localScale;
currentScale.x *= -1;
gameObject.transform.localScale = currentScale;
facingRight = !facingRight;
}
}
Updated code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
// Start is called before the first frame update
private Animator animate;
public float moveSpeed = 6f;
bool facingRight = true;
public Rigidbody2D rb;
Vector2 movement;
private void Start()
{
animate = gameObject.GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
movement.x = Input.GetAxisRaw("Horizontal");
movement.y = Input.GetAxisRaw("Vertical");
animate.SetFloat("Speed", Mathf.Abs(movement.x));
if (movement.x < 0 && facingRight)
{
GetComponent<SpriteRenderer>().flipX = true;
}
else if (movement.x > 0 && !facingRight)
{
GetComponent<SpriteRenderer>().flipX = false;
}
}
void FixedUpdate()
{
rb.MovePosition(rb.position + movement * moveSpeed * Time.fixedDeltaTime);
}
void Flip()
{
Vector3 currentScale = gameObject.transform.localScale;
currentScale.x *= -1;
gameObject.transform.localScale = currentScale;
facingRight = !facingRight;
}
}
Try to set the flipX property of the SpriteRenderer.
GetComponent<SpriteRenderer>().flipX = true;
You have an animator attached to the game object, if the scale value is controlled by the animation clip, you cannot change it. A typical workaround is give the game object an empty parent and change its scale.
Player <---- Change scale here
Model <---- Animator here
transform.Rotate(0f, 180f, 0f); you have to change y while fliping
will work better instead of using currentScale.x *= -1;
gameObject.transform.localScale = currentScale;
This code worked for me
private void Flip()
{
// Rotate the player
if (transform.localEulerAngles.y != 180 && !facingRight)
transform.Rotate(0f, 180f, 0f);
else if(transform.localEulerAngles.y != 0 && facingRight)
transform.Rotate(0f, -180f, 0f);
// player flip point of attck also flip is direction
//transform.Rotate(0f, 180f, 0f);
}
if (Input.GetKeyDown(KeyCode.LeftArrow))
{
GetComponent<SpriteRenderer>().flipX = true;
isLookingRight = false;
}
if (Input.GetKeyDown(KeyCode.RightArrow))
{
GetComponent<SpriteRenderer>().flipX = false;
isLookingRight = true;
}
Going to right
Going to left

I have a blank Unity object nested under the player. It's supposed to be tied to the front of the sprite but does not flip with the sprite

I've got my player sprite that has a blank "Front Check" object nested underneath it. The nested item is used to check if the front of the sprite is touching a wall. the problem I'm having is the Front Check object does not flip with the sprite when I move in the opposite direction. it was working fine earlier today and then out of the blue just quit. This is my first unity project and I'm just not quite sure what's missing. Any help is much appreciated. here is an image of the project and my movement script.
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
private Rigidbody2D rb;
private BoxCollider2D coll;
private SpriteRenderer sprite;
private Animator anim;
[SerializeField] private LayerMask jumpableGround;
public float checkRadius;
bool isTouchingFront;
public Transform frontCheck;
public float wallSlidingSpeed;
bool wallSliding;
bool wallJumping;
public float xWallForce;
public float yWallForce;
public float wallJumpTime;
private float dirX = 0f;
[SerializeField] private float moveSpeed = 7f;
[SerializeField] private float jumpForce = 14f;
[SerializeField] private float airControl;
private enum MovementState { idle, running, jumping, falling }
[SerializeField] private AudioSource JumpSoundEffect;
// Start is called before the first frame update
private void Start()
{
rb = GetComponent<Rigidbody2D>();
coll = GetComponent<BoxCollider2D>();
sprite = GetComponent<SpriteRenderer>();
anim = GetComponent<Animator>();
}
// Update is called once per frame
private void Update()
{
dirX = Input.GetAxisRaw("Horizontal");
if (dirX < 0 && IsGrounded())
{
rb.velocity = new Vector2(moveSpeed * -1, rb.velocity.y);
}
else if (dirX > 0 && IsGrounded())
{
rb.velocity = new Vector2(moveSpeed, rb.velocity.y);
}
else if (dirX == 0 && IsGrounded())
{
rb.velocity = new Vector2(0, rb.velocity.y);
}
if (Input.GetButtonDown("Jump") && IsGrounded())
{
JumpSoundEffect.Play();
rb.velocity = new Vector2(rb.velocity.x, jumpForce);
}
UpdateAnimationState();
}
private void UpdateAnimationState()
{
MovementState state;
if (dirX > 0f && IsGrounded())
{
state = MovementState.running;
}
else if (dirX < 0f && IsGrounded())
{
state = MovementState.running;
}
else
{
state = MovementState.idle;
}
if (rb.velocity.y > .1f)
{
state = MovementState.jumping;
}
else if (rb.velocity.y < -.1f)
{
state = MovementState.falling;
}
//Wall Jump
isTouchingFront = Physics2D.OverlapCircle(frontCheck.position, checkRadius, jumpableGround);
if (isTouchingFront == true && IsGrounded() == false && dirX != 0)
{
wallSliding = true;
}
else
{
wallSliding = false;
}
if (wallSliding)
{
rb.velocity = new Vector2(rb.velocity.x, Mathf.Clamp(rb.velocity.y, -wallSlidingSpeed, float.MaxValue));
}
if (Input.GetButtonDown("Jump") && wallSliding == true)
{
wallJumping = true;
Invoke("SetWallJumpingToFalse", wallJumpTime);
}
if (wallJumping == true)
{
rb.velocity = new Vector2(xWallForce * -Input.GetAxisRaw("Horizontal"), yWallForce);
}
anim.SetInteger("state", (int)state);
}
void SetWallJumpingToFalse()
{
wallJumping = false;
}
public bool IsGrounded()
{
return Physics2D.BoxCast(coll.bounds.center, coll.bounds.size, 0f, Vector2.down, .1f, jumpableGround);
}
public void FixedUpdate()
{
if (dirX > 0f)
{
sprite.flipX = false;
}
else if (dirX < 0f)
{
sprite.flipX = true;
}
}
}
sprite.flipX only flips the rendering not the actual object scaling.
You rather want to use e.g.
var scale = transform.localScale;
scale.x = Mathf.Sign(dirX);
transform.localScale = scale;
Note that it needs to be UnityEngine.Mathf.Sign and not System.Math.Sign since the latter returns 0 for parameter 0 which would totally break your colliders etc. while Mathf.Sign returns only -1 or 1.

Horizontal axis doesn't seem to have a value

so i'm trying to achieve a player thats stops whenever the user lets go of the key, i don't want it to slide, i have a vertical variable and a horizontal variable that stores the axis, if these values are less or more than zero, than the bool isMoving is set to true, else it's set to false, this works wonderful on the vertical azis but not on the horizontal axis, i tried even making if statements checking when the keys are pressed and when they are not
like this
if(Input.GetKeyDown(KeyCode.A))
{
isMoving = true;
}
if(Input.GetKeyDown(KeyCode.D))
{
isMoving = true;
}
if(Input.GetKeyUp(KeyCode.A))
{
isMoving = false;
}
if(Input.GetKeyUp(KeyCode.D))
{
isMoving = false;
}
this also did not work,
this is my script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
[Header("Player Values")]
[SerializeField] private float playerSpeed = 50f;
[SerializeField] private float jumpHeight = 7f;
[Header("Ground Check")]
[SerializeField] private Transform groundCheck;
[SerializeField] private LayerMask ground;
private float checkLenght = 0.1f;
private Rigidbody rb;
private float horizontal;
private float vertical;
// Crouch Scales
private Vector3 initialScale;
private Vector3 crouchScale;
// Counter Movement
private bool isMoving;
private bool isOnGround;
private float counterDrag = 10f;
private void Start()
{
rb = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
// Crouch Scales
initialScale = new Vector3(1f, 1f, 1f);
crouchScale = new Vector3(1f, 0.5f, 1f);
// Player Movement
MovePlayer();
// Counter Movement
ApplyCounterMovement();
// Check Ground
isOnGround = Physics.CheckSphere(groundCheck.position, checkLenght, ground);
}
private void Update()
{
CheckInput();
}
private void MovePlayer()
{
horizontal = Input.GetAxis("Horizontal") * playerSpeed;
vertical = Input.GetAxis("Vertical") * playerSpeed;
Vector3 movement = transform.forward * vertical + transform.right * horizontal;
rb.AddForce(movement, ForceMode.Acceleration);
}
private void CheckInput()
{
if(Input.GetKeyDown(KeyCode.LeftShift))
{
getDown();
}
if(Input.GetKeyUp(KeyCode.LeftShift))
{
getUp();
}
// Check Counter Movement
if (horizontal > 0f || horizontal < 0f)
{
isMoving = true;
}
else
{
isMoving = false;
}
if (vertical > 0f || vertical < 0f)
{
isMoving = true;
}
else
{
isMoving = false;
}
// Check Jump
if(Input.GetKeyDown(KeyCode.Space))
{
Jump();
}
}
// Crouch
private void getDown()
{
transform.localScale = crouchScale;
transform.position = new Vector3(transform.position.x, transform.position.y - 0.5f, transform.position.z);
}
// Get Up From Crouching
private void getUp()
{
transform.localScale = initialScale;
transform.position = new Vector3(transform.position.x, transform.position.y + 0.5f, transform.position.z);
}
// Apply Counter Movement
private void ApplyCounterMovement()
{
if(isMoving == true)
{
rb.drag = 0f;
return;
}
if(isMoving == false)
{
rb.drag = counterDrag;
}
}
private void Jump()
{
if (isOnGround == false)
{
return;
}
if(isOnGround == true)
{
rb.AddForce(Vector3.up * jumpHeight, ForceMode.VelocityChange);
}
}
}
Your if checks for the vertical into overrule the ones before for the horizontal since both of them set the same field isMoving.
Rather combine them into e.g.
isMoving = horizontal != 0f || vertical != 0f;

Choppy Player Movement

For whatever reason, my player movement stutters every second or every other second, and I'm sure it's to do with my player movement code being in the wrong update method.
Here's my player movement code
[SerializeField] private LayerMask groundLayerMask;
public float speed;
public float Jump;
public sword swordScript;
public GameObject swordSprite;
private float move;
private Rigidbody2D rb;
private BoxCollider2D boxCollider2d;
private bool facingRight;
public SpriteRenderer spr;
public Animator PlayerAnims;
public bool movementAllowed;
public float knockback;
void Awake()
{
boxCollider2d = GetComponent<BoxCollider2D>();
rb = GetComponent<Rigidbody2D>();
facingRight = true;
spr = GetComponent<SpriteRenderer>();
}
// Start is called before the first frame update
void Start()
{
boxCollider2d = GetComponent<BoxCollider2D>();
rb = GetComponent<Rigidbody2D>();
facingRight = true;
spr = GetComponent<SpriteRenderer>();
}
// Update is called once per frame
void FixedUpdate()
{
if(movementAllowed == true)
{
rb.velocity = new Vector2(move * speed, rb.velocity.y);
move = Input.GetAxisRaw("Horizontal");
rb.velocity = new Vector2(move * speed, rb.velocity.y);
if (isGrounded() && Input.GetButtonDown("Jump"))
{
rb.AddForce(new Vector2(rb.velocity.x, Jump));
}
}
}
void Update()
{
if (movementAllowed == true)
{
Flip(move);
if (move == 0)
{
PlayerAnims.SetBool("isRunning", false);
}
else
{
PlayerAnims.SetBool("isRunning", true);
}
}
}
private bool isGrounded()
{
float extraHeightText = .1f;
RaycastHit2D raycasthit2d = Physics2D.BoxCast(boxCollider2d.bounds.center, boxCollider2d.bounds.size, 0f, Vector2.down, extraHeightText, groundLayerMask);
Color rayColour;
if (raycasthit2d.collider != null)
{
rayColour = Color.green;
PlayerAnims.SetBool("isJumping", false);
}
else
{
rayColour = Color.red;
PlayerAnims.SetBool("isJumping", true);
}
Debug.DrawRay(boxCollider2d.bounds.center + new Vector3(boxCollider2d.bounds.extents.x, 0), Vector2.down * (boxCollider2d.bounds.extents.y + extraHeightText), rayColour);
Debug.DrawRay(boxCollider2d.bounds.center - new Vector3(boxCollider2d.bounds.extents.x, 0), Vector2.down * (boxCollider2d.bounds.extents.y + extraHeightText), rayColour);
Debug.DrawRay(boxCollider2d.bounds.center - new Vector3(boxCollider2d.bounds.extents.x, boxCollider2d.bounds.extents.y + extraHeightText), Vector2.right * (boxCollider2d.bounds.extents.x), rayColour);
return raycasthit2d.collider != null;
}
private void Flip(float move)
{
if (move > 0 && !facingRight || move < 0 && facingRight)
{
facingRight = !facingRight;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
if (swordScript.isFollowing == true)
{
Vector3 swordScale = swordSprite.transform.localScale;
swordScale.x *= -1;
swordSprite.transform.localScale = swordScale;
}
}
}
I tried out your script and I think I barely see that stutter, but one thing seems to fix it.
You have this particular line twice in the code for no reason
rb.velocity = new Vector2(move * speed, rb.velocity.y);
Just remove the first one.
As a rule of thumb:
Calling rb.AddForce: This interacts with Physics - place it in FixedUpdate
Setting rb.velocity: This only sets a value. You are not triggering physics interactions with this - place it in Update
So, at first you should move your code which sets the velocity to the Update function. Also you are setting the velocity twice. Just remove the first line.
As an example here ist the Update and FixedUpdate function:
void FixedUpdate()
{
if(movementAllowed == true)
{
if (isGrounded() && Input.GetButtonDown("Jump"))
{
rb.AddForce(new Vector2(rb.velocity.x, Jump));
}
}
}
void Update()
{
if (movementAllowed == true)
{
float move = Input.GetAxisRaw("Horizontal");
rb.velocity = new Vector2(move * speed, rb.velocity.y);
Flip(move);
if (move == 0)
{
PlayerAnims.SetBool("isRunning", false);
}
else
{
PlayerAnims.SetBool("isRunning", true);
}
}
}
Since you are not using move anywhere else than Update you can just declare it as a local variable and remove the private float move; at the top of the script.
Last thing which should be mentioned:
You are getting component references both in Start or Awake. Generally you should get your references in Start and only get them in Awake when needed.

Raycast issue with chase AI

In my script, I have a patrol method and a chase method. My chase code using ray cast is not working properly. The only thing that works is my AI patrol code. When I hit a ray cast that I assign, it doesn't chase me. However, when I remove the patrol code in my script, my chase code works fine. What would be the problem? I want my AI to Patrol and Chase. If there is any collision and if the player is outside of the collision, the AI will start to patrol again
public class LineTrigger : MonoBehaviour {
private Transform targett;
public Transform sightStart, sightEnd;
public bool spotted = false;
public GameObject arrow;
public GameObject target;
public bool facingRight = false;
public float speed = 8;
[SerializeField]
float moveSpeed = 3f;
Rigidbody2D rb;
Vector3 localScale;
float dirX;
Vector3 directionToTarget;
void Start()
{
localScale = transform.localScale;
rb = GetComponent<Rigidbody2D> ();
dirX = -1f;
target = GameObject.Find ("Girl");
}
void Update()
{
if (transform.position.x < 1f)
dirX = 1f;
else if (transform.position.x > 20f)
dirX = -1f;
Raycasting ();
Behaviours ();
}
void Raycasting ()
{
Debug.DrawLine (sightStart.position, sightEnd.position);
spotted = Physics2D.Linecast (sightStart.position, sightEnd.position, 1 << LayerMask.NameToLayer ("Girl"));
}
void Behaviours()
{
if (spotted == true) {
arrow.SetActive (true);
directionToTarget = (target.transform.position - transform.position).normalized;
rb.velocity = new Vector2 (directionToTarget.x * moveSpeed, directionToTarget.y * moveSpeed);
}
else
{
arrow.SetActive (false);
}
}
void FixedUpdate()
{
rb.velocity = new Vector2 (dirX * moveSpeed, rb.velocity.y);
}
void LateUpdate()
{
CheckWhereToFace ();
}
void CheckWhereToFace()
{
if (dirX > 0)
facingRight = false;
else if (dirX < 0)
facingRight = true;
if (((facingRight) && (localScale.x < 0)) || ((!facingRight) && (localScale.x > 0)))
localScale.x *= -1;
transform.localScale = localScale;
}
}
I think you are missing a if(!spotted) in your FixedUpdate

Categories

Resources