Have a simple AI thats follows the player when in range and randomly moves the ai around when it's not in the player range. When the AI hits a wall and is out of the players range it starts to spin all the time. Can't work out why it keeps doing so.
I may be missing a simple thing...
Many thanks for any help.
void Update()
{
Target = GameObject.FindGameObjectWithTag("Player");
if (Vector3.Distance(Target.transform.position, transform.position) < 25)
{
followPlayer();
}
else
{
randomMovement();
}
}
public void followPlayer()
{
if (Vector3.Distance(transform.position, Target.transform.position) >= MinDist)
{
transform.position += transform.forward * MoveSpeed * Time.deltaTime;
transform.LookAt(Target.transform);
if (Vector3.Distance(transform.position, Target.transform.position) <= MaxDist)
{
}
}
else
{
}
}
public void randomMovement()
{
transform.position += transform.forward * MoveSpeed * Time.deltaTime;
transform.Rotate(RandomDirection * Time.deltaTime * 10.0f);
}
void OnCollisionEnter(Collision col)
{
bool hasTurned = false;
if (col.transform.gameObject.name != "Terrain")
{
if(hasTurned == false)
{
RandomDirection = new Vector3(0, Mathf.Sin(TimeBetween) * (RotationRange / 2) + OriginalDirection, 0);
randomMovement();
hasTurned = true;
}
else
{
randomMovement();
hasTurned = false;
}
Debug.Log("Hit");
}
The reason it is continuously spinning is because you are continuously calling randomMovement() in your Update() which continously applies a rotation to your object with Rotate(). It sounds like what you are instead trying to do is have the object wander aimlessly every few seconds. You could do this by implementing on timer on your randomMovement() so that every few seconds, it generates a new rotation(similar to what you have in the onCollision). Example below.
float t = 0;
public void randomMovement()
{
transform.position += transform.forward * MoveSpeed * Time.deltaTime;
t += Time.deltaTime;
if (t > 3f) // set to a new rotation every 3 seconds.
{
t = 0; // reset timer
RandomDirection = new Vector3(0, Random.Range(0f, 360f), 0); // turn towards random direction
transform.Rotate(RandomDirection);
}
}
Related
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 have a fish which is swimming across the screen. When it gets within 10% of the edge of the screen, I want it to turn begin turning around until it has completely reversed and is now swimming in the opposite direction. This should be more gradual like a fish would swim. I don't want it to rotate on its own axis.
I'm not having any luck getting it to turn around. It only turns partially.
Update
Here's the fish
public class FishSwim : MonoBehaviour
{
public enum Direction { LeftToRight, RightToLeft };
public Direction moveDirection;
[SerializeField]
private float speedMin = 0.5f;
[SerializeField]
private float speedMax = 1f;
[SerializeField]
private bool useOnlySpeedMax = false;
private float speed;
[HideInInspector]
public float removeBeyond;
private void Start()
{
var dist = (transform.position - Camera.main.transform.position).z;
if (moveDirection == Direction.RightToLeft)
removeBeyond = Camera.main.ViewportToWorldPoint(new Vector3(1, 0, dist)).x;
else
removeBeyond = Camera.main.ViewportToWorldPoint(new Vector3(1, 0, dist)).x + FindObjectOfType<SkinnedMeshRenderer>().bounds.size.x;
}
private void OnEnable()
{
speed = Random.Range(speedMin, speedMax);
if (useOnlySpeedMax)
{
speed = speedMax;
}
if (moveDirection == Direction.RightToLeft)
{
speed = -speed;
}
}
// Update is called once per frame
void Update()
{
float realSpeed = speed * Time.deltaTime;
transform.position += Vector3.right * realSpeed;
if (moveDirection == Direction.RightToLeft && transform.position.x < -Mathf.Abs(removeBeyond))
{
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.right), Time.deltaTime * 40f);
moveDirection = Direction.LeftToRight;
}
else if (moveDirection == Direction.LeftToRight && transform.position.x > Mathf.Abs(removeBeyond))
{
}
}
}
I would use Coroutines to do this. Explanation in comments:
bool isTurning = false;
[SerializeField] float turnPeriod = 0.5f; // how long it takes to turn, smaller=faster
private void OnEnable()
{
speed = Random.Range(speedMin, speedMax);
if (useOnlySpeedMax)
{
speed = speedMax;
}
// Get rid of negative speed
}
void Update()
{
float realDistance = speed * Time.deltaTime;
transform.position += transform.right * realDistance;
// Surely there is a better way than calculating
// Mathf.Abs(removeBeyond) every frame... but that is off-topic
if (!isTurning && (
(moveDirection == Direction.RightToLeft
&& transform.position.x < -Mathf.Abs(removeBeyond)
) || (moveDirection == Direction.LeftToRight
&& transform.position.x > Mathf.Abs(removeBeyond)
) ) )
{
// If we aren't already turning and we should be, start turning:
StartCoroutine(Turn());
}
}
IEnumerator Turn()
{
isTurning = true;
Vector3 startForward = transform.forward;
// find end speed and direction
Direction endDirection = moveDirection == Direction.RightToLeft ?
Direction.LeftToRight:Direction.RightToLeft;
// keep track of how much of our turning time has elapsed
float elapsedTimePortion = Time.deltaTime/turnPeriod;
// turn until you've spent enough time turning
while (elapsedTimePortion < 1f)
{
// by whatever portion we've spent time turning, turn starting forward
// 180 degrees around up axis
float angle = 180f * elapsedTimePortion;
Vector3 newForward = Quaternion.AngleAxis(angle, Vector3.up) * startForward;
transform.rotation = Quaternion.LookRotation(newForward);
yield return null;
// next frame - update how long we've been turning
float newElapsedTimePortion = elapsedTimePortion + Time.deltaTime/turnPeriod;
if (newElapsedTimePortion >= 0.5f && elapsedTimePortion < 0.5f)
{
// If we've just passed 50% of the turn,
// make fish move opposite direction
moveDirection = endDirection;
}
elapsedTimePortion = newElapsedTimePortion;
}
// we're done turning, just set the rotation to the end rotation.
isTurning = false;
transform.rotation = Quaternion.LookRotation(-startForward);
// Does removeBeyond need to be updated here? There are a few lines in Start()
// which would suggest that.
}
I want to make my player object move right when a touch happens and move left on following touch and so on.
bool isTurn = true;
[SerializeField] float horizonMoveSpeed = 20f;
Vector3 targetPosWhenTrue;
Vector3 targetPosWhenFalse;
private void Start()
{
targetPosWhenTrue = new Vector3(3f, 0, 0);
targetPosWhenFalse = new Vector3(-3f, 0, 0);
}
private void Update()
{
if(Input.touchCount > 0)
{
if (isTurn == true)
{
transform.position += targetPosWhenTrue * Time.deltaTime * horizonMoveSpeed;
isTurn = false;
}
if (isTurn == false)
{
transform.position += targetPosWhenFalse * Time.deltaTime * horizonMoveSpeed;
isTurn = true;
}
}
}
There is no error message but it is not working. Does nothing.
Since update is called every frame, isTurn is being reset every single frame. It appears that nothing is happening because it's going by so fast. In order to achieve what you're looking for you're going to have to check the position of the target and set the isTurn variable in the else statement. This may not be exactly what you're looking for, but should get you started.
if(Input.touchCount > 0)
{
if (isTurn == true)
{
if(transform.position != targetPosWhenTrue)
transform.position += targetPosWhenTrue * Time.deltaTime * horizonMoveSpeed;
else
isTurn = false;
}
if (isTurn == false)
{
if(transform.position != targetPosWhenTrue)
transform.position += targetPosWhenFalse * Time.deltaTime * horizonMoveSpeed;
else
isTurn = true;
}
}
I want to make my character to move smoothly when I tilt my phone. How I can make it to move smoothly and the velocity and the speed increases as the slope of the phone?
void AccelerometerMove()
{
float x = Input.acceleration.x;
Debug.Log("X = " + x);
if (x < -0.1f)
{
MoveLeft();
}
else if (x > 0.1f)
{
MoveRight();
}
else
{
SetVelocityZero();
}
}
public void SetVelocityZero()
{
rb.velocity = Vector2.zero;
}
public void MoveLeft()
{
rb.velocity = new Vector2(-speed, 0);
//transform.Translate(Vector2.right * speed * Time.deltaTime);
transform.eulerAngles = new Vector2(0, 180);
}
public void MoveRight()
{
rb.velocity = new Vector2(speed, 0);
//transform.Translate(Vector2.right * speed * Time.deltaTime);
transform.eulerAngles = new Vector2(0, 0);
}
Check out this answer about using a moving average/low-pass filter here
You could achieve a similar effect by using the Vector3.Lerp function.
I wrote a script for the player movement in c#. Whenever the player presses A or D, it moves him/her to the left or right by 12 units and when the player presses W or S, it moves him/her up or down by 12 units. My script works fine, but if a person starts to spam all of the keys at once, it glitches out and the player object is not in line with the level anymore. I want to have the script check if there a movement is currently happening before executing the movement on the keypress. Here is my script:
void Update () {
transform.Translate(Vector3.forward * ForwardSpeed * Time.deltaTime);
if (Input.GetKeyDown (KeyCode.A) && side > maxSideLeft) {
MoveObjectTo(this.transform, new Vector3(this.transform.position.x - 12, this.transform.position.y, this.transform.position.z + 10), movementSpeed);
side -= 1;
} else if (Input.GetKeyDown (KeyCode.D) && side < maxSideRight) {
MoveObjectTo(this.transform, new Vector3(this.transform.position.x + 12, this.transform.position.y, this.transform.position.z + 10), movementSpeed);
side += 1;
}
if (Input.GetKeyDown (KeyCode.W) && level < maxLevelHeight) {
MoveObjectTo(this.transform, new Vector3(this.transform.position.x, this.transform.position.y + 12, this.transform.position.z + 10), movementSpeed);
level += 1;
} else if (Input.GetKeyDown (KeyCode.S) && level > minLevelHeight) {
MoveObjectTo(this.transform, new Vector3(this.transform.position.x, this.transform.position.y - 12, this.transform.position.z + 10), movementSpeed);
level -= 1;
}
if (Input.GetKeyDown (KeyCode.R) || Input.GetKeyDown(KeyCode.Space)) {
SceneManager.LoadScene ("Scene1");
Time.timeScale = 1;
}
}
private void MoveObjectTo(Transform objectToMove, Vector3 targetPosition, float moveSpeed)
{
StopCoroutine(MoveObject(objectToMove, targetPosition, moveSpeed));
StartCoroutine(MoveObject(objectToMove, targetPosition, moveSpeed));
}
public static IEnumerator MoveObject(Transform objectToMove, Vector3 targetPosition, float moveSpeed)
{
float currentProgress = 0;
Vector3 cashedObjectPosition = objectToMove.transform.position;
while (currentProgress <= 1)
{
currentProgress += moveSpeed * Time.deltaTime;
objectToMove.position = Vector3.Lerp(cashedObjectPosition, targetPosition, currentProgress);
yield return null;
}
}
You can use a simple isMoving variable for this. Don't move if the variable is true. Move if it is false. When the coroutine is done running set isMoving back to false. Also, I can't tell why MoveObject function is static. I removed the static keyword from it.
bool isMoving = false;
void Update()
{
transform.Translate(Vector3.forward * ForwardSpeed * Time.deltaTime);
if (Input.GetKeyDown(KeyCode.A) && side > maxSideLeft)
{
MoveObjectTo(this.transform, new Vector3(this.transform.position.x - 12, this.transform.position.y, this.transform.position.z + 10), movementSpeed);
side -= 1;
}
else if (Input.GetKeyDown(KeyCode.D) && side < maxSideRight)
{
MoveObjectTo(this.transform, new Vector3(this.transform.position.x + 12, this.transform.position.y, this.transform.position.z + 10), movementSpeed);
side += 1;
}
if (Input.GetKeyDown(KeyCode.W) && level < maxLevelHeight)
{
MoveObjectTo(this.transform, new Vector3(this.transform.position.x, this.transform.position.y + 12, this.transform.position.z + 10), movementSpeed);
level += 1;
}
else if (Input.GetKeyDown(KeyCode.S) && level > minLevelHeight)
{
MoveObjectTo(this.transform, new Vector3(this.transform.position.x, this.transform.position.y - 12, this.transform.position.z + 10), movementSpeed);
level -= 1;
}
if (Input.GetKeyDown(KeyCode.R) || Input.GetKeyDown(KeyCode.Space))
{
SceneManager.LoadScene("Scene1");
Time.timeScale = 1;
}
}
private void MoveObjectTo(Transform objectToMove, Vector3 targetPosition, float moveSpeed)
{
//Don't do anything Object is already moving
if (isMoving)
{
return;
}
//If not moving set isMoving to true
isMoving = true;
StartCoroutine(MoveObject(objectToMove, targetPosition, moveSpeed));
}
public IEnumerator MoveObject(Transform objectToMove, Vector3 targetPosition, float moveSpeed)
{
float currentProgress = 0;
Vector3 cashedObjectPosition = objectToMove.transform.position;
while (currentProgress <= 1)
{
currentProgress += moveSpeed * Time.deltaTime;
objectToMove.position = Vector3.Lerp(cashedObjectPosition, targetPosition, currentProgress);
yield return null;
}
//Done moving. Set isMoving to false
isMoving = false;
}
It sounds like you want to check to see if the user has completed their lerp before lerping to some new position. In that case, you will want to prevent your player from moving again until your currentProgress variable is equal to 1. Thus, use a simple boolean callback and prevent calling the Corouting until that callback is true. Your MoveObject function:
public static IEnumerator MoveObject(Transform objectToMove, Vector3 targetPosition, float moveSpeed, System.Action<bool> callBack)
{
float currentProgress = 0;
Vector3 cashedObjectPosition = objectToMove.transform.position;
while (currentProgress <= 1)
{
currentProgress += moveSpeed * Time.deltaTime;
objectToMove.position = Vector3.Lerp(cashedObjectPosition, targetPosition, currentProgress);
yield return null;
if (currentProgress >= 1)
callBack(true);
}
}
Then you can change your MoveObjectTo function to read this callback:
private void MoveObjectTo(Transform objectToMove, Vector3 targetPosition, float moveSpeed)
{
if (canCallCoroutine)
{
canCallCoroutine = false;
StartCoroutine(MoveObject(objectToMove, targetPosition, moveSpeed, finished =>
{
if (finished != null)
{
if (finished)
canCallCoroutine = true;
}
}));
}
}