I'm making a 2D platformer in Unity, and made a patrolling enemy with code from a tutorial video. The enemy basically moves randomly to different spots in the scene. But how can I make the sprite turn around?
This is my code so far. I've tried with different approaches, but not getting the expected behavior.
public float speed = 10f;
private float waitTime;
public float startWaitTime = 1f;
public Transform[] moveSpots;
private int randomSpot;
private bool moving = true;
private bool m_FacingRight = true;
void Start()
{
waitTime = startWaitTime;
randomSpot = Random.Range(0, moveSpots.Length);
}
void Update()
{
transform.position = Vector2.MoveTowards(transform.position, moveSpots[randomSpot].position, speed * Time.deltaTime);
if (Vector2.Distance(transform.position, moveSpots[randomSpot].position) < 0.2f)
{
//Debug.Log(m_FacingRight);
// if (moving.x > 0 && !m_FacingRight)
// {
// Debug.Log("moving right");
// Flip();
// }
// else if (moving.x < 0 && m_FacingRight)
// {
// Debug.Log("moving left");
// Flip();
// }
if (waitTime <= 0)
{
//moving = true;
randomSpot = Random.Range(0, moveSpots.Length);
waitTime = startWaitTime;
}
else
{
//moving = false;
waitTime -= Time.deltaTime;
}
}
}
//private void Flip()
//{
//m_FacingRight = !m_FacingRight;
//transform.Rotate(0f, 180f, 0f);
//}
**********************************EDIT****************************************
I ended up with this for the enemy script movement
private bool facingRight = true;
private Rigidbody2D rigidBody2D;
private SpriteRenderer spriteRenderer;
public Vector2 speed = new Vector2(10, 0);
public Vector2 direction = new Vector2(1, 0);
private void Awake()
{
rigidBody2D = GetComponent<Rigidbody2D>();
spriteRenderer = GetComponent<SpriteRenderer>();
}
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.collider.CompareTag("Wall") || collision.collider.CompareTag("Player"))
{
direction = Vector2.Scale(direction, new Vector2(-1, 0));
}
if (!collision.collider.CompareTag("Ground"))
{
if (!facingRight)
{
Flip();
}
else if (facingRight)
{
Flip();
}
}
}
void FixedUpdate()
{
Vector2 movement = new Vector2(speed.x * direction.x, 0);
movement *= Time.deltaTime;
transform.Translate(movement);
}
private void Flip()
{
facingRight = !facingRight;
if (!facingRight)
{
spriteRenderer.flipX = true;
}
else if (facingRight)
{
spriteRenderer.flipX = false;
}
}
Rewrite your Flip function to
private void Flip()
{
m_FacingRight = !m_FacingRight;
GetComponent<SpriteRenderer>().flipX = true;
}
Also, if you're looking for a reference, I made a platformer in Unity that includes enemy patrolling https://github.com/sean244/Braid-Clone
In a sidescroller, an easy way to achieve this is to use SpriteRenderer.flipX.
For instance, if you want to flip X when the velocity is negative:
var sr = GetComponent<SpriteRenderer>();
sr.flipX = velocity.x < 0;
Related
I am trying to create an enemy that is 'smart', that changes a weapon if it has no ammo left and if all its weapons are depleted of ammo find the nearest weapon and change the weapon.
So far, I have an enemy that can patrol, find player in range, chase and attack the player.
Here is the code:
public class EnemyControler : MonoBehaviour
{
[Header("Attack")]
[SerializeField] float shootingDistance =10.0f;
[SerializeField] float shootDelay = 3.5f;
[Range(0,1.0f)][SerializeField] float shootingAccuracy =0.5f;
[SerializeField] int shootDamage =5;
[SerializeField] int ammo = 1;
[Header("User Interface")]
public Transform canvasTr;
public Slider Healthbar;
[Header("Health/Damage/Death")]
public float MaxHealth;
public float Damage;
public float AttackRange;
public int deathCounter;
public Transform ammoObject;
private NavMeshAgent navAgent;
private Collider enemycollider;
private Transform PlayerTr;
private Animator EnemyAnim;
float Health;
bool showingHealthBar, alive;
bool isPatrolling =false;
bool isInShootingRange =false;
bool canResumeIdleState =true;
bool isPreparingToShoot=false;
bool isDead =false;
bool isAlerted =false;
float shootTimer = Mathf.Infinity;
AIPatrolBehavior aIPatrolBehavior = null;
void Start()
{
Health = MaxHealth;
canvasTr.gameObject.SetActive(false);
navAgent = GetComponent<NavMeshAgent>();
EnemyAnim = GetComponent<Animator>();
enemycollider = GetComponent<Collider>();
PlayerTr = GameObject.FindGameObjectWithTag("Player").transform;
aIPatrolBehavior = GetComponent<AIPatrolBehavior>();
alive = true;
ammo = 100;
StartCoroutine(Idle());
// are you sure you want to randomly change MaxHealth after settting health = maxHealth?
//the slider gets weirdly bugged
// MaxHealth = Random.Range(50, 200);
Healthbar.maxValue = MaxHealth; // set the max value to MaxHelth
}
private void Update()
{
if(isDead)return;
if(Vector3.Distance(PlayerTr.transform.position, transform.position) > 20 && !isAlerted
|| PlayerTr.GetComponent<PlayerController>().HealthBar.value <= 0.0f){
StopAllCoroutines();
GetComponent<AIPatrolBehavior>().enabled =true;
return;
}
if(Vector3.Distance(PlayerTr.transform.position, transform.position) < 20 && isAlerted) isAlerted=false;
isInShootingRange = DistanceToPlayer() < shootingDistance &&
DistanceToPlayer() > AttackRange - 0.5f &&
PlayerTr.GetComponent<PlayerController>().HealthBar.value > 0;
if(isInShootingRange)
{
if(canResumeIdleState){
StopAllCoroutines();
canResumeIdleState=false;
}
ProcessShooting();
} else{
if(!canResumeIdleState){
isPreparingToShoot =false;
StartCoroutine(Idle());
canResumeIdleState =true;
navAgent.enabled =true;
}
}
}
private void ProcessShooting()
{
if(!isPreparingToShoot) shootTimer +=Time.deltaTime;
navAgent.enabled =false;
transform.LookAt(PlayerTr);
float randomProbability =Random.Range(0,1.0f);
if(shootTimer > shootDelay){
ShootAtPlayer();
}
EnemyAnim.SetFloat("MovmentSpeed", 0, 0.3f, Time.deltaTime);
}
private void ShootAtPlayer(){
if(isPreparingToShoot)return;
EnemyAnim.SetBool("PrepareAttack", false);
shootTimer =0.0f;
isPreparingToShoot = true;
EnemyAnim.SetTrigger("shoot");
}
public void ShootPlayerAnimationEvent(){
Debug.Log("Player got shot");
float randomAccuracy = Random.Range(0, 1.0f);
bool willHitTarget = randomAccuracy > 1.0f - shootingAccuracy;
if(willHitTarget && DistanceToPlayer() < shootingDistance){
PlayerTr.GetComponent<PlayerController>().DoDamage(shootDamage,true);
}
GetComponentInChildren<AIWeapon>().UseWeapon();
isPreparingToShoot =false;
}
IEnumerator Idle()
{
EnemyAnim.SetBool("PrepareAttack", false);
yield return new WaitUntil(() => Vector3.Distance(PlayerTr.transform.position, transform.position) < 20 || isAlerted);
StartCoroutine(RunToTarget());
}
IEnumerator RunToTarget()
{
aIPatrolBehavior.enabled =false;
if(navAgent.isOnNavMesh) { // save from error apperng
navAgent.isStopped = false;
}
EnemyAnim.SetTrigger("Attack");
while (Vector3.Distance(PlayerTr.transform.position, transform.position) > AttackRange - 0.5f)
{
if(navAgent.isOnNavMesh) { // save from error apperng
navAgent.SetDestination(PlayerTr.position);
}
// navAgent.SetDestination(PlayerTr.position);
EnemyAnim.SetFloat("MovmentSpeed", 1, 0.3f, Time.deltaTime);
yield return null;
}
StartCoroutine(Attack());
}
IEnumerator Attack()
{
EnemyAnim.SetBool("PrepareAttack", true);
navAgent.isStopped = true;
while (Vector3.Distance(PlayerTr.position, transform.position) < AttackRange)
{
EnemyAnim.SetTrigger("Attack");
float t = 0.5f;
while (t > 0)
{
Vector3 rotation = Vector3.RotateTowards(transform.forward, PlayerTr.position - transform.position, 5f * Time.deltaTime, 1f);
transform.forward = rotation;
t -= Time.deltaTime;
yield return null;
}
yield return null;
}
EnemyAnim.SetBool("PrepareAttack", false);
StartCoroutine(RunToTarget());
}
float DistanceToPlayer(){
return Vector3.Distance(transform.position,PlayerTr.position);
}
public void DoDamage(float damage)
{
Alert();
if (!showingHealthBar)
{
showingHealthBar = true;
StartCoroutine(ShowHealthBar());
}
Health -= damage;
Debug.Log("Health: " + Health + " of: " + MaxHealth);
Healthbar.value = Health;
if (Health <= 0)
{
StopAllCoroutines();
if(navAgent.isOnNavMesh) { // save from error apperng
navAgent.isStopped = true;
}
if (alive)
{
alive = true; // does this make sense?
StartCoroutine(Death());
}
}
}
I have tried searching through Google to find any tutorials, but without success. Does anyone have any ideas?
if the weapons placed on constant places all over the map, you can make the enemy stop patrolling or chasing the player when his ammo == 0 by a Boolean then use MoveTowrds() the weapon.
and u can include some way to calculate the distance between the enemy's position and all the weapons on the array, then it head for the closest.
you will use an array of vectors to store the locations of weapons.
now its your turn to try.
I am new to unity and am trying to take two scripts that I have written based on youtube tutorials and make a script that allows my character to have dynamic jump, coyote time, jump buffering, and dash. Everything is working except for the dash now. When I play, all of the features work, including the anti-gravity during the dash, but my character does not speed up; it remains at the same speed as when walking. I think the two IEnumerator may be conflicting, but I am unsure of how to resolve it. So far, everything I have tried has not worked. I appreciate the help! enter image description here
using System.Collections;
using UnityEngine;
public class PlayerMovementBendux : MonoBehaviour
{
private float horizontal;
public float speed = 8f;
public float jumpingPower = 16f;
private bool isFacingRight = true;
private bool isJumping;
private float coyoteTime = 0.2f;
private float coyoteTimeCounter;
private float jumpBufferTime = 0.2f;
private float jumpBufferCounter;
private bool canDash = true;
private bool isDashing;
public float dashingPower = 24f;
public float dashingTime = 0.2f;
public float dashingCooldown = 1f;
[SerializeField] private Rigidbody2D rb;
[SerializeField] private Transform groundCheck;
[SerializeField] private LayerMask groundLayer;
[SerializeField] private TrailRenderer tr;
private void Update()
{
if (isDashing)
{
return;
}
horizontal = Input.GetAxisRaw("Horizontal");
if (IsGrounded())
{
coyoteTimeCounter = coyoteTime;
}
else
{
coyoteTimeCounter -= Time.deltaTime;
}
if (Input.GetButtonDown("Jump"))
{
jumpBufferCounter = jumpBufferTime;
}
else
{
jumpBufferCounter -= Time.deltaTime;
}
if (coyoteTimeCounter > 0f && jumpBufferCounter > 0f && !isJumping)
{
rb.velocity = new Vector2(rb.velocity.x, jumpingPower);
jumpBufferCounter = 0f;
StartCoroutine(JumpCooldown());
}
if (Input.GetButtonUp("Jump") && rb.velocity.y > 0f)
{
rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * 0.5f);
coyoteTimeCounter = 0f;
}
if (Input.GetKeyDown(KeyCode.LeftShift) && canDash)
{
StartCoroutine(Dash());
}
Flip();
}
private void FixedUpdate()
{
rb.velocity = new Vector2(horizontal * speed, rb.velocity.y);
}
private bool IsGrounded()
{
return Physics2D.OverlapCircle(groundCheck.position, 0.2f, groundLayer);
}
private void Flip()
{
if (isFacingRight && horizontal < 0f || !isFacingRight && horizontal > 0f)
{
Vector3 localScale = transform.localScale;
isFacingRight = !isFacingRight;
localScale.x *= -1f;
transform.localScale = localScale;
}
}
private IEnumerator JumpCooldown()
{
isJumping = true;
yield return new WaitForSeconds(0.4f);
isJumping = false;
}
private IEnumerator Dash()
{
canDash = false;
isDashing = true;
float originalGravity = rb.gravityScale;
rb.gravityScale = 0f;
rb.velocity = new Vector2(transform.localScale.x * dashingPower, 0f);
tr.emitting = true;
yield return new WaitForSeconds(dashingTime);
tr.emitting = false;
rb.gravityScale = originalGravity;
isDashing = false;
yield return new WaitForSeconds(dashingCooldown);
canDash = true;
}
}
So I was trying to make a game just for fun and to learn for future use. So I encountered this problem in making the enemy NPC. I want it to follow me or chase me but I want the NPC to only move horizontal and vertical and I want the NPC to move per tile as well just like my Player.
Here's the video of how it looks
https://www.youtube.com/watch?v=CB_vdt1Z3nA
and here's the NPC script
public class ChaseScript : MonoBehaviour
{
public float speed;
private GameObject player;
private Transform player_transform;
void Start()
{
player = GameObject.Find("Player");
}
void Update()
{
player_transform = player.GetComponent<Transform>();
transform.position = Vector3.MoveTowards(transform.position, player_transform.position, speed * Time.deltaTime);
}
}
Here's my player controller
public void InputMove()
{
if (!isMoving)
{
input.x = Input.GetAxisRaw("Horizontal");
input.y = Input.GetAxisRaw("Vertical");
if (input.x != 0) input.y = 0;
if (input != Vector2.zero)
{
playerAnimation.SetParameterValue(animator);
var movePos = transform.position;
movePos.x += input.x;
movePos.y += input.y;
FacingForward.transform.position = movePos;
if (IsWalkable(movePos))
StartCoroutine(Move(movePos));
}
playerAnimation.SetParameterValueisMoving(animator);
}
if (Input.GetKey(KeyCode.LeftShift))
{
moveSpeed = 6f;
animator.speed = 1.5f;
}
else
{
moveSpeed = 4f;
animator.speed = 1f;
}
}
IEnumerator Move(Vector3 movePos)
{
isMoving = true;
while ((movePos - transform.position).sqrMagnitude > Mathf.Epsilon)
{
transform.position = Vector3.MoveTowards(transform.position, movePos, moveSpeed * Time.deltaTime);
yield return null;
}
transform.position = movePos;
isMoving = false;
}
private bool IsWalkable(Vector3 movePos)
{
if (Physics2D.OverlapCircle(movePos, 0.1f, SolidObjectLayer | NPC) != null)
{
return false;
}
return true;
}
What I did to my Player to move per tile is I just add 1 to transform so It'll be a constant movement but I don't know how to apply it on the NPC with the Vector3.MoveTowards but if it's not possible to do then it's fine
Check if this could work (you can adapt it to your 2D case)
using UnityEngine;
public class ChaseOrthoScript : MonoBehaviour
{
public float speed;
private GameObject player;
private Transform player_transform;
bool isMoving = false;
void Start()
{
player = GameObject.Find("Player");
player_transform = player.GetComponent<Transform>();
transform.LookAt(player_transform.position);
}
void Update()
{
if (transform.InverseTransformPoint(player_transform.position).z > 0) {
transform.position += transform.forward * speed * Time.deltaTime;
isMoving = true;
} else {
if (isMoving) {
float angle = Vector3.Angle(transform.forward, player_transform.position - transform.position);
transform.Rotate(Vector3.up, Mathf.Sign(angle) * 90);
isMoving = false;
} else if (transform.InverseTransformPoint(player_transform.position).z <= 0) { //player is back
transform.Rotate(Vector3.up, 180);
}
}
}
}
note that the player_transform = player.GetComponent<Transform>(); is moved to the Start(). Usually you dont want GetComponents in an update as you need to get it only once. ALso its much cleaner to have a public GameObject player; variable in the script and attach the reference in the editor that the player = GameObject.Find("Player");. Usually you dont want scene elements found by a hardcoded magic value in your code.
Hope that helps.
So I'm trying to add a dash mechanic to a game character im building. However for some reason i can't get the game objects velocity to actually change. I tried using addForce which worked, but i had to add a lot in order to get the desired effect and that behaved strangely sometimes!
Do i need to do anything else to the game objects velocity than i already doing?
Heres my script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public float dashSpeed;
private int dashDirection;
private float dashCoolDown;
public float startDashCoolDown;
private float dashTime;
public float startDashTime;
public GameObject dashEffect;
public float speed;
public float jumpForce;
private float moveInput;
private Rigidbody2D rb;
private bool isFacingRight = true;
private Animator anim;
private bool isGrounded;
public Transform groundCheck;
public float checkRadius;
public LayerMask whatIsGround;
private int extraJumps;
public int extraJumpsValue;
public GameObject dustEffect;
public GameObject trailEffect;
private void Awake()
{
// Setting up references.
isGrounded = transform.Find("GroundCheck");
anim = GetComponent<Animator>();
rb = GetComponent<Rigidbody2D>();
}
void Start()
{
extraJumps = extraJumpsValue;
rb = GetComponent<Rigidbody2D>();
dashTime = startDashTime;
dashCoolDown = startDashCoolDown;
}
void FixedUpdate()
{
isGrounded = false;
// Check to see if grounded
Collider2D[] colliders = Physics2D.OverlapCircleAll(groundCheck.position, checkRadius, whatIsGround);
for (int i = 0; i < colliders.Length; i++)
{
if (colliders[i].gameObject != gameObject)
{
isGrounded = true;
anim.SetBool("Ground", isGrounded);
}
}
// Check if movement is allowed
if (!GameMaster.disableMovement)
{
// Move character
moveInput = Input.GetAxis("Horizontal");
rb.velocity = new Vector2(moveInput * speed, rb.velocity.y);
Instantiate(trailEffect, groundCheck.position, Quaternion.identity);
anim.SetFloat("Speed", Mathf.Abs(moveInput));
}
// Flip character
if (isFacingRight == false && moveInput > 0)
{
Flip();
}
else if (isFacingRight == true && moveInput < 0)
{
Flip();
}
}
private void Update()
{
// Check if the player is grounded
if (isGrounded == true)
{
extraJumps = extraJumpsValue;
}
// Check if movement is allowed
if (!GameMaster.disableMovement)
{
// Check for jump
// If the player has more than one jump available
if (Input.GetKeyDown(KeyCode.Space) && extraJumps > 0)
{
isGrounded = false;
anim.SetBool("Ground", isGrounded);
rb.velocity = Vector2.up * jumpForce;
extraJumps--;
Instantiate(dustEffect, groundCheck.position, Quaternion.identity);
}
// If the player only has one jump available
if (Input.GetKeyDown(KeyCode.Space) && extraJumps == 0 && isGrounded == true)
{
isGrounded = false;
anim.SetBool("Ground", isGrounded);
rb.velocity = Vector2.up * jumpForce;
}
// Check for dash
if (dashCoolDown <= 0)
{
if (Input.GetKeyDown(KeyCode.LeftShift))
{
anim.SetBool("Dash", true);
Dash();
dashTime = startDashTime;
}
}
else
{
dashCoolDown -= Time.deltaTime;
}
}
}
void Dash()
{
if (dashTime <= 0)
{
dashCoolDown = startDashCoolDown;
anim.SetBool("Dash", false);
}
else
{
dashTime -= Time.deltaTime;
if (isFacingRight)
{
rb.velocity = Vector2.right * dashSpeed;
}
else if (!isFacingRight)
{
rb.velocity = Vector2.left * dashSpeed;
}
}
}
void Flip()
{
isFacingRight = !isFacingRight;
// Multiply the player's x local scale by -1.
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
}
If you dont get the desired dash effect with Rigidbody.velocity, you can try using Rigidbody.addForce(). I know you said you already used it, but there are multiple different force modes you can apply here. I would suggest using this one, because it applies full force from the start and then drops it off. You can read more about it yourself here.
So you could modify your code like so:
void Dash()
{
if (isFacingRight)
{
rb.AddForce(Vector2.right*dashSpeed, ForceMode.VelocityChange);
}
else if (!isFacingRight)
{
rb.AddForce(Vector2.left*dashSpeed, ForceMode.VelocityChange);
}
}
Note, that you also dont need to call dash repeatedly to falloff the velocity. this function should now be only called onse the button to dash (shift) has been pressed.
Hope this helped!
I want to make my player invulnerable from objects hitting him for a few seconds after he resets back to the center of the game, meaning I don't want anything to hurt him and I don't want the player to move for 5 seconds, but i'm not sure of how to do that! I searched it up but results that I found doesn't match up with my code. Anyway this is my playermovement script:
private Animator anim;
public float speed = 15f;
public static Vector3 target;
private bool touched;
private bool canmove;
Vector3 startPosition;
void Start () {
target = transform.position;
anim = GetComponent<Animator> ();
}
void Update () {
if (Input.GetMouseButtonDown (0)) {
Vector3 mousePosition = Input.mousePosition;
mousePosition.z = 10; // distance from the camera
target = Camera.main.ScreenToWorldPoint(mousePosition);
target.z = transform.position.z;
}
transform.position = Vector3.MoveTowards (transform.position, target, speed * Time.deltaTime);
var movementDirection = (target - transform.position).normalized;
if (movementDirection.x != 0 || movementDirection.y != 0) {
anim.SetBool ("walking", false);
anim.SetFloat("SpeedX", movementDirection.x);
anim.SetFloat("SpeedY", movementDirection.y);
anim.SetBool ("walking", true);
}
}
void FixedUpdate () {
float LastInputX = transform.position.x - target.x;
float LastInputY = transform.position.y - target.y;
if (touched) {
if (LastInputX != 0 || LastInputY != 0) {
anim.SetBool ("walking", true);
if (LastInputX < 0) {
anim.SetFloat ("LastMoveX", 1f);
} else if (LastInputX > 0) {
anim.SetFloat ("LastMoveX", -1f);
} else {
anim.SetFloat ("LastMoveX", 0f);
}
if (LastInputY > 0) {
anim.SetFloat ("LastMoveY", 1f);
} else if (LastInputY < 0) {
anim.SetFloat ("LastMoveY", -1f);
} else {
anim.SetFloat ("LastMoveY", 0f);
}
}
}else{
touched = false;
anim.SetBool ("walking", false);
}
}
}
And this is my player health script:
public int curHealth;
public int maxHealth = 3;
Vector3 startPosition;
bool invincible = false;
void Start ()
{
curHealth = maxHealth;
startPosition = transform.position;
}
void Update ()
{
if (curHealth > maxHealth) {
curHealth = maxHealth;
}
if (curHealth <= 0) {
Die ();
}
}
void Die ()
{
//Restart
Application.LoadLevel (Application.loadedLevel);
}
public void Damage(int dmg)
{
curHealth -= dmg;
Reset();
}
void Reset ()
{
transform.position = startPosition;
PlayerMovement.target = startPosition;
}
}
So to be more simple, I want to make my player invulnerable (once he reset back in the center of the screen) from getting hit from enemies for 5 seconds and people who are playing my player can not move for 5 seconds. Thank you! (My game is a topdown 2d click to move game)
I think this should do the trick. At least point you in the right direction.
Simple explanation, i changed invincible to be a time, a time in the future.
Then we simply add a time in the future to it at some event, and from that event onward to the point in the future we want him invincible.
So at every event where he would take damage we check, is now in the future of invincible or is invincible in the future of now. that is your bool.
DateTime invincible = DateTime.Now;
public void Damage(int dmg)
{
if(invincible <= DateTime.Now)
{
curHealth -= dmg;
Reset();
}
}
void Reset ()
{
transform.position = startPosition;
PlayerMovement.target = startPosition;
invincible = DateTime.Now.AddSeconds(5);
}
if you want a helper method like this
bool IsInvincible(){
return invincible <= DateTime.Now;
}
Then you can change out the if(invincible <= DateTime.Now) with if(IsInvincible()) in your public void Damage(int dmg)
In game development, you have to keep in mind that everything happens within a frame update. I like to use coroutines to implement invincibility frames in Unity. A coroutine is simply a method that runs in parallel to the main update loop and resumes where it left off in the next update.
float dmg = 100.0f;
bool isHitByEnemy = false
bool isInvulnerable = false
void Demage()
{
//some dmg logic
if (isHitByEnemy && isInvulnerable == false)
{
StartCoroutine("OnInvulnerable");
}
}
IEnumerator OnInvulnerable()
{
isInvulnerable = true;
dmg = 0.0f;
yield return new WaitForSeconds(0.5f); //how long player invulnerable
dmg = 100.0f;
isInvulnerable = false;
}