I have implemented various enemies in my game but for now, I'm confronted with a problem when I want to add pre-defined movement to all enemies.
Firstly, the code managing enemies worked fine. The enemies just waited and if the players was too close to him, he attacked.
[SerializeField]
private float rotationSpeed = 180;
[SerializeField]
private float movementSpeed = 1f;
[SerializeField]
private float meshRadius = 1f;
private IEnumerator turnTowardsPlayerCoroutine;
private IEnumerator moveTowardsPlayerCoroutine;
private bool isDead = false;
public float speed = 1;
private int maxLife = 3;
void OnTriggerEnter(Collider collider)
{
if (collider.gameObject.tag == "Player")
{
float playerDistance = Vector3.Distance(collider.transform.position, transform.position);
if (playerDistance >= 2f * meshRadius)
{
turnTowardsPlayerCoroutine = TurnTowardsPlayer(collider.transform);
moveTowardsPlayerCoroutine = MoveTowardsPlayer(collider.transform);
StartCoroutine(turnTowardsPlayerCoroutine);
StartCoroutine(moveTowardsPlayerCoroutine);
}
}
}
void OnTriggerExit(Collider collider)
{
if (collider.tag == "Player")
{
float playerDistance = Vector3.Distance(collider.transform.position, transform.position);
if (playerDistance >= 2f * meshRadius)
{
StopCoroutine(turnTowardsPlayerCoroutine);
StopCoroutine(moveTowardsPlayerCoroutine);
}
}
}
void OnDeath()
{
if (isDead)
{
return;
}
maxLife--;
if (maxLife <= 0)
{
isDead = true;
GameManager.Instance.NumKilledEnemies++;
StopCoroutine(turnTowardsPlayerCoroutine);
StopCoroutine(moveTowardsPlayerCoroutine);
Destroy(gameObject);
}
}
private IEnumerator TurnTowardsPlayer(Transform player)
{
while (true)
{
Quaternion targetRotation = Quaternion.LookRotation(player.position - transform.position, Vector3.up);
targetRotation.x = 0f;
targetRotation.z = 0f;
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
yield return 0;
}
}
private IEnumerator MoveTowardsPlayer(Transform player)
{
while (true)
{
Vector3 playerDirection = transform.position - player.position;
playerDirection.y = 0;
playerDirection = playerDirection.normalized;
Vector3 deltaMovement = playerDirection * movementSpeed * Time.deltaTime;
int layermask = LayerMask.GetMask("Environment");
Vector3 movingTowards = transform.position - playerDirection * meshRadius + (new Vector3(0f, 0.1f, 0f));
if (Physics.Raycast(movingTowards, Vector3.down, 0.25f, layermask))
{
transform.position -= deltaMovement;
}
yield return 0;
}
}
But after that, I wanted to add a predefined movement of enemies if nobody is close to him so I did something like this :
private IEnumerator moveDefaultCoroutine;
void Start()
{
moveDefaultCoroutine = MoveToPosition(new Vector3(transform.position.x, transform.position.y, transform.position.z + 3), 5);
StartCoroutine(moveDefaultCoroutine);
}
private IEnumerator MoveToPosition(Vector3 newPosition, float time)
{
while (true)
{
float elapsedTime = 0;
Vector3 startingPos = transform.position;
while (elapsedTime < time)
{
transform.position = Vector3.Lerp(startingPos, newPosition, (elapsedTime / time));
elapsedTime += Time.deltaTime;
yield return null;
}
yield return new WaitForSeconds(2);
elapsedTime = 0;
while (elapsedTime < 0.8)
{
transform.eulerAngles = Vector3.Lerp(transform.eulerAngles, new Vector3(0, 180, 0), (elapsedTime / 0.8f));
elapsedTime += Time.deltaTime;
yield return null;
}
elapsedTime = 0;
while (elapsedTime < time)
{
transform.position = Vector3.Lerp(newPosition, startingPos, (elapsedTime / time));
elapsedTime += Time.deltaTime;
yield return null;
}
yield return new WaitForSeconds(2);
elapsedTime = 0;
while (elapsedTime < 0.8)
{
transform.eulerAngles = Vector3.Lerp(transform.eulerAngles, new Vector3(0, 0, 0), (elapsedTime / 0.8f));
elapsedTime += Time.deltaTime;
yield return null;
}
}
}
And here come the problem, when I'm adding this code the enemies disappear randomly... Did I did something wrong ?
Full code here : http://pastebin.com/tzGczv26
Thanks per advance !
PokeRwOw
Related
I am trying to implement a coroutine that moves an object to a specific point and then comes back to the origin (specified by me) as a coroutine in Unity, but the coroutine is not executed after returning to the origin. What's wrong?
public class LineManager : MonoBehaviour
{
public Vector3 positionToGo = new Vector3(0, -4, 0);
IEnumerator coroutine;
void Start()
{
coroutine = MoveToPosition(transform, positionToGo, 2f);
}
void Update()
{
if(transform.position.y == 4)
{
StartCoroutine(coroutine);
}
}
public IEnumerator MoveToPosition(Transform transform, Vector3 position, float timeToMove)
{
var currentPos = transform.position;
var t = 0f;
while (t < 1 && currentPos.y > position.y)
{
t += Time.deltaTime / timeToMove;
transform.position = Vector3.Lerp(currentPos, position, t);
yield return null;
}
transform.position = new Vector3(0, 4, 0);
StopCoroutine(coroutine);
}
}
You don't really need to restart your routine for that.
But first things first:
transform.position.y == 4
is risky as it is a direct float comparison and might fail. See Is floating point math broken?
Anyways, back to the coroutine restarting. Why not simply move without a routine at all
public Vector3 positionToGo = new Vector3(0, -4, 0);
public float timeToMove = 2f;
private Vector3 originalPosition;
private float factor;
private void Awake()
{
originalPosition = transform.position;
}
private void Update()
{
if(factor >= 1)
{
factor = 0;
transform.position = originalPosition;
}
else
{
factor += Time.deltaTime / timeToMove;
transform.position = Vector3.Lerp(originalPosition, positionToGo, factor);
}
}
As an alternative if you want to use the routine you can always simply make it loop
public Vector3 positionToGo = new Vector3(0, -4, 0);
public float timeToMove = 2f;
private IEnumerator Start()
{
var originalPosition = transform.position;
while(true)
{
for(var factor = 0f; factor < 1f; factor += Time.deltaTime / timeToMove)
{
transform.position = Vector3.Lerp(originalPosition, position, factor);
yield return null;
}
transform.position = originalPosition;
}
}
Call the Coroutine in that IEnumerator with WaitForSeconds
So, I made a basic movement script with wallrunning and wanted to add dashing to it, at first I made parameters for the character to dash and testing it with debug.log worked as intended, but the actual dash command, which was transform.translate(Vector3.forward), didn't work for some reason.
This is the code:
public class PlayerMovement : MonoBehaviour
{
public Transform player;
public CharacterController controller;
public float speed = 12f;
public float baseSpeed;
Vector3 velocity;
public float gravity = -9.81f;
public Transform groundCheck;
public float groundDistance = 0.4f;
public LayerMask groundMask;
public Transform wallCheckL;
public Transform wallCheckR;
public float wallDistanceL = -0.4f;
public float wallDistanceR = 0.4f;
public LayerMask wallMask;
public bool touchWallL;
public bool touchWallR;
public float SlideTime = 10f;
public float Ynormal;
public bool isGrounded;
public bool Sprinting;
public bool Crouching;
public bool Sliding;
public bool canDash;
// Start is called before the first frame update
void Start()
{
baseSpeed = speed;
canDash = true;
}
// Update is called once per frame
void Update()
{
isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
touchWallL = Physics.CheckSphere(wallCheckL.position, wallDistanceL, wallMask);
touchWallR = Physics.CheckSphere(wallCheckR.position, wallDistanceR, wallMask);
if (isGrounded && velocity.y < 0)
{
velocity.y = -2f;
}
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * speed * Time.deltaTime);
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
if ((isGrounded == true) && Input.GetKeyDown("space"))
{
velocity.y = 10f;
}
if (Input.GetKeyDown(KeyCode.LeftShift))
{
speed = baseSpeed * 1.5f;
Sprinting = true;
}
if (Input.GetKeyUp(KeyCode.LeftShift))
{
speed = baseSpeed;
Sprinting = false;
}
if (Input.GetKeyDown(KeyCode.LeftControl))
{
Crouching = true;
}
if (Input.GetKeyUp(KeyCode.LeftControl))
{
Crouching = false;
}
if (Crouching)
{
transform.localScale = new Vector3(1f, 0.5f, 1f);
} else if (Crouching == false)
{
transform.localScale = new Vector3(1f, 1f, 1f);
}
if (Sprinting && Crouching)
{
Sliding = true;
} else if (Sprinting == false && Crouching == false)
{
Sliding = false;
}
if (Sliding)
{
speed = baseSpeed * 2;
transform.localScale = new Vector3(1f, 0.25f, 1f);
}
Ynormal = transform.localEulerAngles.y;
if (touchWallL)
{
gravity = -4.4f;
transform.localRotation = Quaternion.Euler(0, Ynormal, -20f);
isGrounded = true;
} else if (touchWallR)
{
gravity = -4.4f;
transform.localRotation = Quaternion.Euler(0f, Ynormal, 20f);
isGrounded = true;
}
else
{
gravity = -9.81f;
transform.localRotation = Quaternion.Euler(0f, Ynormal, 0f);
}
if (touchWallR && Input.GetKeyDown(KeyCode.Space))
{
velocity.y = 10f;
transform.position += Vector3.left * Time.deltaTime * 100;
}
if (Input.GetKeyDown(KeyCode.E) && canDash)
{
Dash();
}
}
void Dash()
{
StartCoroutine(dashTimer());
player.transform.Translate(Vector3.forward * Time.deltaTime * speed);
}
IEnumerator dashTimer()
{
canDash = false;
yield return new WaitForSeconds(2f);
canDash = true;
}
}
The requirements themselves work, I did some testing, but the dash itself didn't. I tried controller.move, transform.position += transform.position, even making a game object empty called dash distance and trying to teleport my character there, but none of it worked, all of it resulted in my character just not doing anything when I tried to dash.
This is due that Transform.Translate actually needs to be updated each frame.
Try refactoring a bit like this:
// add another control flag
private bool isDashing = false;
void Update()
{
//...
if (Input.GetKeyDown(KeyCode.E) && canDash)
{
StartCoroutine(dashTimer());
}
if (isDashing)
{
Dash();
}
}
void Dash()
{
player.transform.Translate(Vector3.forward * Time.deltaTime * speed);
}
IEnumerator dashTimer()
{
canDash = false;
isDashing = true;
yield return new WaitForSeconds(2f);
canDash = true;
isDashing = false;
}
i want to multiply a float. in the unity editor everything is ok too, in the build it is going up slower all of a sudden.
i tried reimporting all assets and restart my pc but that does not help either.
I tried to build it again but that did not help.
I do not know what to do now, does anyone know how to fix this problem?
here is my build output: https://i.stack.imgur.com/ry5PI.png
code:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class PlayerController : MonoBehaviour
{
[Header("")]
[Header("Move Settings")]
[Header("")]
public float MovementSpeed = 5f;
public float SprintSpeed = 7f;
public float JumpForce = 5f;
public float minJumpForce = 3f;
[Header("")]
[Header("Stamina Settings")]
[Header("")]
public float fillStamina = 1f;
public float MaxStamina = 10000f;
public float Stamina = 10000f;
public Slider staminaBar;
public GameObject SliderFill;
public GameObject SliderB;
[Header("")]
[Header("Health Settings")]
[Header("")]
public float fillHealth = 1f;
public float MaxHealth = 100f;
public float Health = 100f;
public Slider HealthBar;
[Header("")]
[Header("Player Settings")]
[Header("")]
Rigidbody2D body;
public static PlayerController instance;
public GameObject DeathObj;
float horizontal;
float vertical;
private Rigidbody2D _rigidbody;
private bool Sprint = false;
//private bool ActivateLowStamina = false;
//private bool LowStamina = false;
private float move;
private void Awake()
{
instance = this;
}
void Start()
{
_rigidbody = GetComponent<Rigidbody2D>();
//body = GetComponent<Rigidbody2D>();
Stamina = MaxStamina;
staminaBar.maxValue = MaxStamina;
staminaBar.value = MaxStamina;
HealthBar.maxValue = MaxHealth;
HealthBar.value = MaxHealth;
//ActivateLowStamina = false;
//lowTxt.gameObject.SetActive(false);
}
void Update()
{
horizontal = Input.GetAxisRaw("Horizontal");
vertical = Input.GetAxisRaw("Vertical");
//transform.rotation = Quaternion.identity;
staminaBar.value = Stamina;
HealthBar.value = Health;
//var movement = Input.GetAxis("Horizontal");
if (Health < 1)
{
Die();
}
if (Stamina <= 0)
{
Stamina = 0;
}
if(Health <= 0)
{
Health = 0;
}
if (Stamina < 3000)
{
SliderFill.GetComponent<Image>().color = new Color32(213, 217, 0, 255);
SliderB.GetComponent<Image>().color = new Color32(11, 217, 0, 47);
}
else
{
SliderFill.GetComponent<Image>().color = new Color32(11, 217, 0, 255);
SliderB.GetComponent<Image>().color = new Color32(0, 255, 12, 47);
}
if (Input.GetKey("left shift"))
{
Sprint = true;
}
//if(Stamina < 100)
//{
// ActivateLowStamina = false;
//}
//else
//{
// ActivateLowStamina = true;
//}
//if(ActivateLowStamina == true)
// {
// lowTxt.gameObject.SetActive(true);
// }
if (Mathf.Abs(_rigidbody.velocity.x) == 0f && Stamina < MaxStamina && Mathf.Abs(_rigidbody.velocity.y) == 0f)
{
Stamina += fillStamina;
}
Health += fillHealth;
if (Input.GetButtonDown("Jump") && Mathf.Abs(_rigidbody.velocity.y) < 0.001f) {
if(Stamina > 10)
{
_rigidbody.AddForce(new Vector2(0, JumpForce), ForceMode2D.Impulse);
//_rigidbody.AddForce(Vector2.up * JumpForce);
UseStamina(700);
}
else
{
_rigidbody.AddForce(new Vector2(0, minJumpForce), ForceMode2D.Impulse);
//_rigidbody.AddForce(Vector2.up * JumpForce);
UseStamina(300);
}
}
}
public void UseStamina(float amount)
{
if (Stamina - amount >= 0)
{
Stamina -= amount;
}
}
private void FixedUpdate()
{
move = Input.GetAxis("Horizontal");
if (Sprint == true && Stamina > 10)
{
_rigidbody.velocity = new Vector2(move * SprintSpeed, _rigidbody.velocity.y);
if (Mathf.Abs(_rigidbody.velocity.x) != 0f)
{
UseStamina(10);
}
Sprint = false;
}
else
{
if (Stamina > 10)
{
_rigidbody.velocity = new Vector2(move * MovementSpeed, _rigidbody.velocity.y);
if(Mathf.Abs(_rigidbody.velocity.x) != 0f)
{
UseStamina(2f);
}
}
else
{
_rigidbody.velocity = new Vector2(move * 1f, _rigidbody.velocity.y);
}
}
//if(Mathf.Abs(_rigidbody.velocity.x) != 0f)
//{
//Stamina -= 1;
//}
}
public void Die()
{
if(Health < 1)
{
Time.timeScale = 0;
DeathObj.SetActive(true);
//SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
}
}
//_rigidbody.velocity = new Vector2(horizontal * MovementSpeed, vertical * SprintSpeed);
//transform.position += new Vector3(movement, 0, 0) * Time.deltaTime * SprintSpeed;
//transform.Translate(new Vector3(-3f, 0, 0) * Time.deltaTime);
//_rigidbody.velocity = new Vector2(MovementSpeed, 0);
//_rigidbody.AddForce(new Vector2(MovementSpeed, MovementSpeed));
//transform.position += new Vector3(movement, 0, 0) * Time.deltaTime * MovementSpeed;
//_rigidbody.velocity = new Vector2(horizontal * MovementSpeed, vertical * MovementSpeed);
//transform.position += new Vector3(movement, 0, 0) * Time.deltaTime * 3f;
//body.velocity = new Vector2(horizontal * MovementSpeed, vertical * MovementSpeed);
//var movement = Input.GetAxis("Horizontal"); ```
What you experience is the typical frame-rate dependent code.
Every frame you do
Stamina += fillStamina;
and
Health += fillHealth;
However, what if on one device you have 60 frames per second, on another weaker device only 30?
On the weaker device it will take twice as long to raise the value.
Therefore you should use Time.deltaTime which is the time passed since the last frame was rendered.
By multiplying
X * Time.deltaTime
you convert a value X from value per frame into a value per second which is now independent of the capacity of the device and the frame-rate. Across all devices it will now always take the same effective time in seconds.
You will of course have to tweak your values but in general you would do e.g.
Stamina += fillStaminaPerSecond * Time.deltaTime;
and accordingly
Health += fillHealthPerSecond * Time.deltaTime;
And accordingly also for the continous usages
UseStamina(10 * Time.deltaTime);
and
UseStamina(2f * Time.deltaTime);
as said you might have to adjust the values since now they are more or less decided by 60 (whatever the frame-rate is).
However, for the single event calls for the jump you do not want/need the Time.deltaTime since these are no continous calls.
Btw instead of using GetComponent over and over again rather do it once in Awake or Start and store the reference in a field and later reuse it!
And instead of
[Header("")]
you should probably rather use
[Space]
;)
I want to move the object up smooth slowly from it's current position on y 50.01 to new position 51.255
But the object keep moving up nonstop.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class RoboSphereWindowBreakInteraction : MonoBehaviour
{
public Transform target;
public AudioClip audioClip;
public float speed;
private bool hasStarted = false;
private Animator anim;
void Update()
{
if ((Input.GetKeyDown(KeyCode.B) || (hasStarted == true)))
{
float step = speed * Time.deltaTime; // calculate distance to move
transform.position = Vector3.MoveTowards(transform.position, target.position, step);
hasStarted = true;
}
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.name == "Square 1")
{
GetComponent<Rigidbody>().isKinematic = false;
hasStarted = false;
Destroy(GameObject.Find("Wall_Window_Long_03"));
}
}
public void ActivateRoboSphere()
{
foreach(Transform child in transform)
{
if(child.name == "Camera")
{
RepositionCamera(child);
}
}
anim = GetComponent<Animator>();
anim.enabled = true;
GetComponent<FPEInteractableActivateScript>().interactionString = "";
FPEInteractionManagerScript.Instance.BeginCutscene();
StartCoroutine(PlayAudio());
}
private void RepositionCamera(Transform camera)
{
var Eyes = GameObject.Find("eyeDome");
camera.position = Eyes.transform.position + Eyes.transform.forward;
camera.LookAt(Eyes.transform);
camera.GetComponent<Camera>().enabled = true;
}
IEnumerator PlayAudio()
{
AudioSource audio = GetComponent<AudioSource>();
audio.clip = audioClip;
audio.Play();
yield return new WaitForSeconds(audio.clip.length);
var rotation = Quaternion.LookRotation(target.position - transform.position);
StartCoroutine(Spin(3f, rotation, () =>
{
anim.SetBool("Roll_Anim", true);
}));
StartCoroutine(MoveFromTo(transform, transform.position, new Vector3(transform.position.x,
transform.position.y + 51.255f, transform.position.z), 3f));
}
IEnumerator Spin(float lerpTime, Quaternion rotation, Action whenDone)
{
float elapsedTime = 0f;
while (elapsedTime <= lerpTime)
{
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, elapsedTime / lerpTime);
elapsedTime += Time.deltaTime;
yield return null;
}
whenDone?.Invoke();
}
// 51.255
IEnumerator MoveFromTo(Transform objectToMove, Vector3 a, Vector3 b, float speed)
{
float step = (speed / (a - b).magnitude) * Time.fixedDeltaTime;
float t = 0;
while (t <= 1.0f)
{
t += step; // Goes from 0 to 1, incrementing by step each time
objectToMove.position = Vector3.Lerp(a, b, t); // Move objectToMove closer to b
yield return new WaitForFixedUpdate(); // Leave the routine and return here in the next frame
}
objectToMove.position = b;
}
}
I did :
StartCoroutine(MoveFromTo(transform, transform.position, new Vector3(transform.position.x,
transform.position.y + 51.255f, transform.position.z), 3f));
But the object keep moving up nonstop. Or at least very high and not like I wanted from 50.01 to 51.255
And when the object reaching the height of 51.255 then I want it to move fast smooth forward to target.
In your code replace this part:
StartCoroutine(MoveFromTo(transform, transform.position, new Vector3(transform.position.x,
transform.position.y + 51.255f, transform.position.z), 3f));
with this:
StartCoroutine(MoveFromTo(transform, transform.position, new Vector3(transform.position.x,
51.255f, transform.position.z), 3f));
So I have written some code and everything works in it besides the AI following the player. I don't know what I did wrong so any help would be gladly appreciated!
public class SharkAI : MonoBehaviour
{
public float speed;
public Transform patrolPoints;
private float waitTime;
public float startWaitTime;
public float minX;
public float maxX;
public float minY;
public float maxY;
public float oldPosition;
public float newPosition;
private SpriteRenderer fishes;
public float eyeSightOfFish;
public float disToPlayer;
private Transform player;
// Start is called before the first frame update
void Start()
{
waitTime = startWaitTime;
patrolPoints.position = new Vector2(Random.Range(minX, maxX), Random.Range(minY, maxY));
fishes = GetComponent<SpriteRenderer>();
}
// Update is called once per frame
void Update()
{
player = GameObject.FindGameObjectWithTag("Player").GetComponent<Transform>();
oldPosition = transform.position.x;
newPosition = patrolPoints.position.x;
Debug.Log("Player distance " + disToPlayer);
//distToPatrolPoint = Vector3.Distance(transform.position, patrolPoints[whichPoint].transform.position);
disToPlayer = Vector2.Distance(transform.position, player.transform.position);
transform.position = Vector2.MoveTowards(transform.position, patrolPoints.position, speed * Time.deltaTime);
if (newPosition > oldPosition)
{
fishes.flipX = true;
}
else { fishes.flipX = false; }
if(disToPlayer <= eyeSightOfFish)
{
transform.position = Vector2.MoveTowards(transform.position, player.position, speed * Time.deltaTime);
}
else if (Vector2.Distance(transform.position, patrolPoints.position) <= 0.2f)
{
if (waitTime <= 0)
{
patrolPoints.position = new Vector2(Random.Range(minX, maxX), Random.Range(minY, maxY));
waitTime = startWaitTime;
}
else
{
waitTime -= Time.deltaTime;
}
}
}
}
so im not sure where this is wrong and ive tried a few things but I can't seem to make the ai follow the player.