I am trying to have my enemy Follow my player when in range, otherwise the enemy is Wandering. I'm quite new to C-Sharp so have been piecing bits of other tutorials code together.
Currently the enemy Wanders back and forth between the trigger box collider I have set. The enemy switches state to Follow when the player is within range, however the enemy will only move towards the player if the player is on the left hand side of the enemy, if I am on the right side of the enemy it is stuck until I am out of range, then it resumes Wandering. Also the enemy does not flip to face the enemy when Following.
Any help would be appreciated.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum EnemyState
{
Wander,
Follow,
Die,
};
public class EnemyController : MonoBehaviour
{
GameObject player;
public EnemyState currState = EnemyState.Wander;
public Transform target;
Rigidbody2D myRigidbody;
public float range = 2f;
public float moveSpeed = 2f;
void Start()
{
player = GameObject.FindGameObjectWithTag("Player");
myRigidbody = GetComponent<Rigidbody2D>();
target = GameObject.FindGameObjectWithTag("Player").GetComponent<Transform>();
}
void Update()
{
switch (currState)
{
case (EnemyState.Wander):
Wander();
break;
case (EnemyState.Follow):
Follow();
break;
case (EnemyState.Die):
// Die();
break;
}
if(IsPlayerInRange(range) && currState != EnemyState.Die)
{
currState = EnemyState.Follow;
}
else if(!IsPlayerInRange(range)&& currState != EnemyState.Die)
{
currState = EnemyState.Wander;
}
}
private bool IsPlayerInRange(float range)
{
return Vector3.Distance(transform.position, player.transform.position) <= range;
}
bool isFacingRight()
{
return transform.localScale.x > 0;
}
void Wander()
{
{
if (isFacingRight())
{
myRigidbody.velocity = new Vector2(moveSpeed, 0f);
}
else
{
myRigidbody.velocity = new Vector2(-moveSpeed, 0f);
}
}
}
void OnTriggerExit2D(Collider2D collision) //this is to flip the sprite when it reaches the end of its path - a box 2d collider trigger
{
transform.localScale = new Vector2(-(Mathf.Sign(myRigidbody.velocity.x)), 1f);
}
void Follow()
{
if (isFacingRight())
{
myRigidbody.velocity = new Vector2(-moveSpeed, 0f);
transform.position = Vector2.MoveTowards(transform.position, new Vector2(target.position.x, transform.position.y), moveSpeed * Time.deltaTime);
}
/* else //this seems to have no effect on the code
{
myRigidbody.velocity = new Vector2(moveSpeed, 0f);
transform.position = Vector2.MoveTowards(transform.position, new Vector2(target.position.x, transform.position.y), -moveSpeed * Time.deltaTime);
}*/
}
}
ok I figured out my own issues, here is the working script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum EnemyState
{
Wander,
Follow,
Die,
};
public class EnemyController : MonoBehaviour
{
GameObject player;
public EnemyState currState = EnemyState.Wander;
public Transform target;
Rigidbody2D myRigidbody;
public float range = 2f;
public float moveSpeed = 2f;
// Start is called before the first frame update
void Start()
{
player = GameObject.FindGameObjectWithTag("Player");
myRigidbody = GetComponent<Rigidbody2D>();
target = GameObject.FindGameObjectWithTag("Player").GetComponent<Transform>();
}
// Update is called once per frame
void Update()
{
switch (currState)
{
case (EnemyState.Wander):
Wander();
break;
case (EnemyState.Follow):
Follow();
break;
case (EnemyState.Die):
// Die();
break;
}
if(IsPlayerInRange(range) && currState != EnemyState.Die)
{
currState = EnemyState.Follow;
}
else if(!IsPlayerInRange(range)&& currState != EnemyState.Die)
{
currState = EnemyState.Wander;
}
}
private bool IsPlayerInRange(float range)
{
return Vector3.Distance(transform.position, player.transform.position) <= range;
}
bool isFacingRight()
{
return transform.localScale.x > 0;
}
void Wander()
{
{
if (isFacingRight())
{
myRigidbody.velocity = new Vector2(moveSpeed, 0f);
}
else
{
myRigidbody.velocity = new Vector2(-moveSpeed, 0f);
}
}
}
void OnTriggerExit2D(Collider2D collision)
{
transform.localScale = new Vector2(-(Mathf.Sign(myRigidbody.velocity.x)), 1f);
}
void Follow()
{
if (transform.position.x > target.position.x)
{
//target is left
transform.localScale = new Vector2(-1, 1);
myRigidbody.velocity = new Vector2(-moveSpeed, 0f);
transform.position = Vector2.MoveTowards(transform.position, new Vector2(target.position.x, transform.position.y), moveSpeed * Time.deltaTime);
}
else if (transform.position.x < target.position.x)
{
//target is right
transform.localScale = new Vector2(1, 1);
myRigidbody.velocity = new Vector2(moveSpeed, 0f);
transform.position = Vector2.MoveTowards(transform.position, new Vector2(target.position.x, transform.position.y), moveSpeed * Time.deltaTime);
}
}
}
Related
I have a problem with an enemy on Unity2D.
The enemy should run to the player and if it's in the attackRange then it has to attack the enemy with a bullet, but the enemy follows my Player.
But there is a problem:
The enemy doesn't shoot with the bullet.
I changed the code, so when I press a button the enemy attacks too.
It worked only with the button.
Here's my code for the enemy:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Pathfinding;
public class Enemy2 : MonoBehaviour
{
public Transform target;
public Transform firePoint;
public float speed = 200f;
public float nextWaypointDistance = 3f;
public float range = 10f; // the range at which the enemy will start moving towards the player
public float attackRange = 8f; // the range at which the enemy will attack the player
float distance;
public Transform enemyGFX;
private Transform player; // reference to the player's transform
public GameObject EnemyWeapon;
Path path;
int currentWaypoint = 0;
bool reachedEndOfPath = false;
Seeker seeker;
Rigidbody2D rb;
public Animator animator;
bool Stop = false;
public static float Enemy2Direction;
// Start is called before the first frame update
void Start()
{
seeker = GetComponent<Seeker>();
rb = GetComponent<Rigidbody2D>();
animator = GetComponentInChildren<Animator>();
player = GameObject.FindGameObjectWithTag("Player").transform;
InvokeRepeating("UpdatePath", 0f, .5f);
}
void UpdatePath()
{
if (seeker.IsDone())
{
seeker.StartPath(rb.position, target.position, OnPathComplete);
}
}
void OnPathComplete (Path p)
{
if (!p.error)
{
path = p;
currentWaypoint = 0;
}
}
// Update is called once per frame
void FixedUpdate()
{
if (Pause.IsPause == false)
{
if (path == null)
{
return;
}
if (currentWaypoint >= path.vectorPath.Count)
{
reachedEndOfPath = true;
return;
}
else
{
reachedEndOfPath = false;
}
Vector2 direction = ((Vector2)path.vectorPath[currentWaypoint] -rb.position).normalized;
Vector2 force = direction * speed * Time.deltaTime;
rb.AddForce(force);
float distance = Vector2.Distance(rb.position, path.vectorPath[currentWaypoint]);
if (distance < nextWaypointDistance)
{
currentWaypoint++;
}
//You can make look it differently, if you delete 'rb.velocity' and add 'force' instead.
if (rb.velocity.x >= 0.01f)
{
enemyGFX.transform.localScale = new Vector3(-1f, 1f, 1f);
Enemy2Direction = 1f;
}
else if (rb.velocity.x <= -0.01f)
{
enemyGFX.transform.localScale = new Vector3(1f, 1f, 1f);
Enemy2Direction = 0f;
}
if (distance < attackRange)
{
if (Stop = false)
{
StartCoroutine(Attack());
}
}
}
distance = Vector2.Distance(transform.position, player.position);
if (distance > range)
{
Destroy(gameObject);
}
}
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Player")
{
animator.SetBool("Damage", true);
}
if (collision.gameObject.tag == "Bullet")
{
animator.SetBool("Damage", true);
}
}
void Update()
{
if (animator.GetBool("Damage"))
{
StartCoroutine(DamageAnimation());
}
if (Input.GetKeyDown(KeyCode.L))
{
StartCoroutine(Attack());
}
}
IEnumerator Attack()
{
Debug.Log("Attacked");
Stop = true;
yield return new WaitForSeconds(2.0f);
animator.SetBool("Attack", true);
Instantiate(EnemyWeapon, firePoint.position, firePoint.rotation);
yield return new WaitForSeconds(2.0f);
animator.SetBool("Attack", false);
StartCoroutine(Wait());
}
IEnumerator DamageAnimation()
{
yield return new WaitForSeconds(2.0f);
animator.SetBool("Damage", false);
}
IEnumerator Wait()
{
yield return new WaitForSeconds(2.0f);
Stop = false;
}
}
I don't know what's wrong with my code.
Can somebody please help me?
if(Stop == false)
Replace the = in when you check the Stop bool with ==, in C# you use == in order to check for equality
See https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/equality-operators for more detail.
Recently I have been Frankensteining code I've found online together and I am struggling to implement the dash correctly. In its current state, whenever my character jumps and then uses his dash, he gets caught in the air as if the dash didn't move him. Obviously this inst intended and I was wondering how I could fix this. Also, how could I include a cool down system to my dash ability. I've tried using various sources for cool down scripts but they always seem to have a downside.
Thanks in advance!
using System.Collections;
using System.Collections.Generic;
using System.Security.Cryptography;
using UnityEngine;
public class CharacterController : MonoBehaviour
{
//Player Movement
public float speed;
public float jumpForce;
public Transform feetPos;
public float checkRadius;
public LayerMask whatIsGround;
public float dashSpeed;
public float startDashTime;
private float timeStamp = 0;
public Animator animator;
private Rigidbody2D rb;
private float moveInput;
private bool isGrounded;
private float jumpTimeCounter;
public float jumpTime;
private bool isJumping;
private float dashTime;
public int direction;
void Start()
{
animator.GetComponent<Animator>();
rb = GetComponent<Rigidbody2D>();
dashTime = startDashTime;
}
void FixedUpdate()
{
moveInput = Input.GetAxisRaw("Horizontal");
if (direction < 1)
rb.velocity = new Vector2(moveInput * speed, rb.velocity.y);
}
void Update()
{
// Moving
isGrounded = Physics2D.OverlapCircle(feetPos.position, checkRadius, whatIsGround);
if (moveInput > 0)
{
transform.eulerAngles = new Vector3(0, 0, 0);
animator.SetBool("Moving", true);
}
else if (moveInput < 0)
{
transform.eulerAngles = new Vector3(0, 180, 0);
animator.SetBool("Moving", true);
}
else
{
animator.SetBool("Moving", false);
}
// Jumping
if (isGrounded == true && Input.GetKeyDown(KeyCode.Space))
{
animator.SetTrigger("IsJumping");
isJumping = true;
jumpTimeCounter = jumpTime;
rb.velocity = Vector2.up * jumpForce;
}
if (Input.GetKey(KeyCode.Space) && isJumping == true)
{
if (jumpTimeCounter > 0)
{
rb.velocity = Vector2.up * jumpForce;
jumpTimeCounter -= Time.deltaTime;
}
else
{
isJumping = false;
}
}
if (Input.GetKeyUp(KeyCode.Space))
{
isJumping = false;
}
if (isGrounded == false)
{
animator.SetBool("Grounded", false);
}
if (isGrounded == true)
{
animator.SetBool("Grounded", true);
}
// Dashing
if (direction == 0)
{
if (Input.GetKeyDown(KeyCode.LeftShift))
{
if ((transform.rotation.eulerAngles.y == 180))
{
Dashleft();
}
else
{
DashRight();
}
}
}
else
{
if (dashTime <= 0)
{
direction = 0;
dashTime = startDashTime;
rb.velocity = Vector2.zero;
}
else
{
dashTime -= Time.deltaTime;
}
}
void Dashleft()
{
direction = 1;
rb.velocity = Vector2.left * dashSpeed;
}
void DashRight()
{
direction = 1;
rb.velocity = Vector2.right * dashSpeed;
}
}
}
I am new to programming and was wondering how I could make my "dash" make me move forward. I watched an online tutorial on how to make a dash work but after trying, the dash would only push me at all if I had written rb.velocity = Vector2.up * dashSpeed;. Currently, when I press the dash key, my character's gravity slows down for a second but no movement is made. Can I please get some help in making it push my character?
If you need clarification, please ask!
PS: Sorry for the long script, all my player control is in here.
using System.Collections;
using System.Collections.Generic;
using System.Security.Cryptography;
using UnityEngine;
public class CharacterController : MonoBehaviour
{
//Player Movement
public float speed;
public float jumpForce;
public Transform feetPos;
public float checkRadius;
public LayerMask whatIsGround;
public float dashSpeed;
public float startDashTime;
public Animator animator;
private Rigidbody2D rb;
private float moveInput;
private bool isGrounded;
private float jumpTimeCounter;
public float jumpTime;
private bool isJumping;
private float dashTime;
private int direction;
void Start()
{
animator.GetComponent<Animator>();
rb = GetComponent<Rigidbody2D>();
dashTime = startDashTime;
}
void FixedUpdate()
{
moveInput = Input.GetAxisRaw("Horizontal");
rb.velocity = new Vector2(moveInput * speed, rb.velocity.y);
}
void Update()
{
// Moving
isGrounded = Physics2D.OverlapCircle(feetPos.position, checkRadius, whatIsGround);
if (moveInput > 0)
{
transform.eulerAngles = new Vector3(0, 0, 0);
animator.SetBool("Moving", true);
}
else if (moveInput < 0)
{
transform.eulerAngles = new Vector3(0, 180, 0);
animator.SetBool("Moving", true);
}
else
{
animator.SetBool("Moving", false);
}
// Jumping
if (isGrounded == true && Input.GetKeyDown(KeyCode.Space))
{
animator.SetTrigger("IsJumping");
isJumping = true;
jumpTimeCounter = jumpTime;
rb.velocity = Vector2.up * jumpForce;
}
if (Input.GetKey(KeyCode.Space) && isJumping == true)
{
if (jumpTimeCounter > 0)
{
rb.velocity = Vector2.up * jumpForce;
jumpTimeCounter -= Time.deltaTime;
}
else
{
isJumping = false;
}
}
if (Input.GetKeyUp(KeyCode.Space))
{
isJumping = false;
}
if (isGrounded == false)
{
animator.SetBool("Grounded", false);
}
if (isGrounded == true)
{
animator.SetBool("Grounded", true);
}
// Dashing
if(direction == 0)
{
if(Input.GetKeyDown(KeyCode.LeftShift))
{
if ((transform.rotation.eulerAngles.y == 180))
{
Dashleft();
}
else
{
DashRight();
}
}
}
else
{
if(dashTime <= 0)
{
direction = 0;
dashTime = startDashTime;
}
else
{
dashTime -= Time.deltaTime;
}
}
}
void Dashleft()
{
rb.velocity = Vector2.left * dashSpeed;
}
void DashRight()
{
rb.velocity = Vector2.right * dashSpeed;
}
}
The problem is that your horizontal velocity is being controlled in FixedUpdate()
It is being overriden in every physics frame, so when you change velocity in DashLeft() or DashRight() methods, velocity is being reset in this line rb.velocity = new Vector2(moveInput * speed, rb.velocity.y);
You can do something like this:
if (dashTime <= 0)
rb.velocity = new Vector2(moveInput * speed, rb.velocity.y);
So you control your velocity by Input only if player is not dashing
I am just starting out with unity and am also making a game for a school project. My 2d game character can do the walk animation when I press the "a" and "d" keys and the sprite flips, but it stays in the same position. Below is my PlayerController script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
private float speed = 3f;
private Animator anim;
private SpriteRenderer sr;
// Start is called before the first frame update
void Awake()
{
anim = GetComponent<Animator>();
sr = GetComponent<SpriteRenderer>();
}
// Update is called once per frame
void Update()
{
Move();
}
void Move()
{
float h = Input.GetAxisRaw("Horizontal");
Vector3 temp = transform.position;
if (h > 0)
{
temp.x += speed * Time.deltaTime;
sr.flipX = true;
anim.SetBool("Walk", true);
}
else if (h < 0)
{
temp.x -= speed * Time.deltaTime;
sr.flipX = false;
anim.SetBool("Walk", true);
}
else if (h == 0)
{
anim.SetBool("Walk", false);
}
}
}
You're setting 'temp' to equal transform.position, but that doesn't mean that transform.position equals 'temp'. Here, the below script should give you what you want
using UnityEngine;
public class PlayerController : MonoBehaviour
{
private float speed = 3f;
private Animator anim;
private SpriteRenderer sr;
void Awake()
{
anim = GetComponent<Animator>();
sr = GetComponent<SpriteRenderer>();
}
void Update()
{
Move();
}
void Move()
{
float h = Input.GetAxisRaw("Horizontal");
transform.Translate(Vector2.right * (h * speed * Time.deltaTime));
anim.SetBool("Walk", h != 0f);
if (anim.GetBool("Walk"))
Flip(h > 0f);
}
void Flip(bool facingRight)
{
sr.flipX = !facingRight;
}
}
I've got the following player controller:
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
[RequireComponent(typeof(NetworkIdentity))]
public class PlayerController : NetworkBehaviour {
public Vector3 size = new Vector3(1, 1, 1);
public float speed = 10;
public float rotateSpeed = 9;
private Transform _transform;
private Map _map;
private BoxCollider _collider;
private bool _active = false;
private Vector3 _lastPosition;
private bool _isGrounded = false;
void Start () {
_transform = transform;
Messenger.AddListener ("MAP_LOADED", OnMapLoaded);
_transform.localPosition = new Vector3 (-100, -100, -100);
gameObject.name = "Player " + gameObject.GetComponent<NetworkIdentity> ().netId.Value;
_collider = gameObject.GetComponent<BoxCollider> ();
_map = GameObject.Find ("Map").GetComponent<Map> ();
_collider.size = size;
}
void OnMapLoaded () {
if (isLocalPlayer) {
// Hook up the camera
PlayerCamera cam = Camera.main.GetComponent<PlayerCamera>();
cam.target = transform;
// Move the player to the it's spawn location
_transform.localPosition = _map.GetPlayerSpawn();
}
// Set the player as active
_active = true;
}
void Update () {
if (!isLocalPlayer || !_active) {
return;
}
_lastPosition = _transform.position;
float transAmount = speed * Time.deltaTime;
float rotateAmount = rotateSpeed * Time.deltaTime;
if (Input.GetKey ("up")) {
transform.Translate (0, 0, transAmount);
}
if (Input.GetKey ("down")) {
transform.Translate (0, 0, -transAmount);
}
if (Input.GetKey ("left")) {
transform.Rotate (0, -rotateAmount, 0);
}
if (Input.GetKey ("right")) {
transform.Rotate (0, rotateAmount, 0);
}
if (!_isGrounded) {
Vector3 down = _transform.TransformDirection(Vector3.down);
_transform.position += down * Time.deltaTime * _map.gravity;
}
}
void FixedUpdate () {
//
// Check what is below us
//
Vector3 down = _transform.TransformDirection(Vector3.down);
RaycastHit[] hits;
hits = Physics.RaycastAll(_transform.position, down, size.y + 0.001f);
_isGrounded = false;
foreach (RaycastHit hit in hits) {
if (hit.collider.gameObject.tag.ToUpper() == "GROUND") {
_isGrounded = true;
}
}
}
void OnTriggerEnter(Collider collider) {
Debug.Log ("Triggered by " + collider.gameObject.tag.ToUpper ());
if (collider.gameObject.tag.ToUpper () == "GROUND") {
transform.position = _lastPosition;
}
}
}
In Update I'm taking the user's input and moving the transform around. In FixedUpdate I'm checking to see if the player is on the ground and setting a flag appropriately. And finally in OnTriggerEnter I'm checking to see if I'm trying to go through anything.
If in OnTriggerEnter I hit something I try to set the player's position back to a good one, but that's not happening. My guess is because _lastPosition isn't what I'm expecting it to be.
Is there a better way to do this?
Use rigidbody.Move(position) instead of transform.Translate(position).
Note: Using transform.translate causing performance issues and you must handle collision detection manually.