Why the object is not moving on constant speed? - c#

Not sure if it's got anything to this two methods but when the objectToThrow is closer to the target the speed is slower on ThrowObject and ThrowBack the speed is the same but slower when it's closer by distance to the target and if the target is far the speed of the objectToMove movement will be faster.
I want the speed to be the same if it's closer or far or at least ot be able to set the speed for closer targets and for far targets.
IEnumerator ThrowObject(Transform objectToMove, float duration, InteractableItem primaryTarget)
{
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
Vector3 currentPos = objectToMove.position;
float time = Vector3.Distance(currentPos, primaryTarget.transform.position) / (duration - counter) * Time.deltaTime;
objectToMove.position = Vector3.MoveTowards(currentPos, primaryTarget.transform.position, time);
yield return null;
}
primaryTarget.gameObject.SetActive(false);
var naviParent = GameObject.Find("Navi Parent");
StartCoroutine(ThrowBack(objectToMove, naviParent.transform.position , duration, primaryTarget));
}
And :
IEnumerator ThrowBack(Transform objectToMove,Vector3 originalPosition , float duration, InteractableItem primaryTarget)
{
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
Vector3 currentPos = objectToMove.position;
float time = Vector3.Distance(currentPos, originalPosition) / (duration - counter) * Time.deltaTime;
objectToMove.position = Vector3.MoveTowards(currentPos, originalPosition, time);
yield return null;
}
objectToMove.localPosition = new Vector3(0, 0, 0);
if (primaryTarget.name == "kid_from_space_helmet")
{
var helmetParent = GameObject.Find("Helmet Parent");
GameObject Helmet = helmetParent.transform.Find("kid_from_space_helmet").gameObject;
Helmet.SetActive(true);
}
IsAlreadyThrown = false;
primaryTarget.interactableMode = InteractableItem.InteractableMode.Description;
primaryTarget.distance = 0;
}

I'm not quite sure why you're using Vector3.MoveTowards(), and I definitely don't understand what you're doing with time. I don't have much — any — experience with it, as I favor linear interpolation, or lerping.
But I've encountered similar trouble before, so it's probably a problem with your time. You're modifying it each iteration because you're basing it on the distance difference, whereas the example in the docs uses a constant speed.
That said, instead of pointing out what's wrong with your code, it'd be easier to write down a solution I use in these cases that should work.
private IEnumerator Throw(Transform objectToMove, float duration, Vector3 targetPosition)
{
float counter = 0f;
var initialPosition = objectToMove.transform.position;
while (counter < duration)
{
counter += Time.deltaTime;
var currentPosition = Vector3.Lerp(initialPosition, targetPosition, counter / duration);
objectToMove.position = currentPosition;
yield return null;
}
// ...
}
From what I've been able to understand, MoveTowards() is preferable when you'd like to move an object at a given speed, whereas Lerp() is better when it's the duration you want to control.
Additionally, for less coupling and cleaner code, I'd suggest that you chain this one coroutine twice and add the necessary code once the 2nd ("back") throw is done, like so:
private IEnumerator ThrowBackAndForth(Transform objectToMove, float duration, InteractableItem primaryTarget)
{
Vector3 originalPosition = objectToMove.transform.position;
Vector3 targetPosition = primaryTarget.transform.position;
// Throw "forward".
yield return StartCoroutine(Throw(objectToMove, duration, targetPosition));
// Throw "back".
yield return StartCoroutine(Throw(objectToMove, duration, originalPosition));
// And then do whatever you were doing after the while loop concluded in ThrowBack().
}
Of course, you know your code best, so customize as needed.

Related

(Beginner) Coroutine crashing unity. Not sure why

I'm not sure what's going on but one of my coroutines is completely crashing unity. This is the coroutine in question.
IEnumerator attack()
{
currentState = state.attack;
pathFinder.enabled = false;
Vector3 origin = transform.position;
Vector3 attackPos = target.position;
float percent = 0;
float attackSpeed = 3;
while(percent <= 1)
{
percent += Time.deltaTime * attackSpeed;
float interpolation = (-Mathf.Pow(percent, 2) + percent) * 4;
transform.position = Vector3.Lerp(origin, attackPos, interpolation);
yield return null;
}
currentState = state.chase;
pathFinder.enabled = true;
}
A lot of code here might seem bad. I'm following a tutorial, probably heard that one a lot here, and on their end, it seems to be working fine but right when I press play it freezes unity forcing me to close it. Here's the entire script if that would give a better understanding of what could be going on
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
[RequireComponent(typeof(NavMeshAgent))]
public class Enemy : LivingEntity
{
public enum state { idle, chase, attack};
private state currentState;
private NavMeshAgent pathFinder;
private Transform target;
private float attackThreshhold = .5f;
private float timeBetweenAttacks = 1;
private float attackTimer;
private float myCollisionRadius;
private float targetCollisionRadius;
// Start is called before the first frame update
protected override void Start()
{
base.Start();
currentState = state.chase;
pathFinder = GetComponent<NavMeshAgent>();
target = GameObject.FindGameObjectWithTag("Player").transform;
myCollisionRadius = GetComponent<CapsuleCollider>().radius;
targetCollisionRadius = GetComponent<CapsuleCollider>().radius;
StartCoroutine(updatePath());
}
// Update is called once per frame
void Update()
{
if(Time.time > attackTimer)
{
float sqrDistanceToTarget = (target.position - transform.position).sqrMagnitude;
if (sqrDistanceToTarget < Mathf.Pow(attackThreshhold + myCollisionRadius + targetCollisionRadius, 2)) {
attackTimer = Time.time + timeBetweenAttacks;
StartCoroutine(attack());
}
}
}
IEnumerator attack()
{
currentState = state.attack;
pathFinder.enabled = false;
Vector3 origin = transform.position;
Vector3 attackPos = target.position;
float percent = 0;
float attackSpeed = 3;
while(percent <= 1)
{
percent += Time.deltaTime * attackSpeed;
float interpolation = (-Mathf.Pow(percent, 2) + percent) * 4;
transform.position = Vector3.Lerp(origin, attackPos, interpolation);
yield return null;
}
currentState = state.chase;
pathFinder.enabled = true;
}
IEnumerator updatePath()
{
float refreshRate = .25f;
while(target != null)
{
if(currentState == state.chase)
{
Vector3 dirToTarget = (target.position - transform.position).normalized;
Vector3 targetPosition = target.position - dirToTarget * (myCollisionRadius + targetCollisionRadius/2);
if (!dead) pathFinder.SetDestination(targetPosition);
yield return new WaitForSeconds(refreshRate);
}
}
}
}
Your problem is in the other routine in updatePath.
while(target != null)
{
if(currentState == state.chase)
{
Vector3 dirToTarget = (target.position - transform.position).normalized;
Vector3 targetPosition = target.position - dirToTarget * (myCollisionRadius + targetCollisionRadius/2);
if (!dead) pathFinder.SetDestination(targetPosition);
yield return new WaitForSeconds(refreshRate);
}
}
Problem: In the case your are not in the state state.chase you never yield => you have an infinite while loop!
It should probably rather be
while(target != null)
{
if(currentState == state.chase)
{
var dirToTarget = (target.position - transform.position).normalized;
var targetPosition = target.position - dirToTarget * (myCollisionRadius + targetCollisionRadius / 2);
if (!dead) pathFinder.SetDestination(targetPosition);
yield return new WaitForSeconds(refreshRate);
}
// In other cases wait one frame
else
{
yield return null;
}
}
Because at some point percent goes negative. It also never goes above 1. I would say your math is incorrect. Try removing the negative infront of Mathf.Pow
Math function has nothing to do with crash. Unity crashes because, #SOPMOD is running while loop infinetly. That causing a overflow. Your code tells it should break while loop when percent value is smaller or equal to 1 but you are yielding it null everytime inside the while loop. yield return null tells to compiler, coroutine will run in next frame. So, coroutine start again and percent value is set to 0 again. While loop run again and coroutine again will yield null. Infinite coroutine loop is crashing your app. Move
float percent = 0;
decleration to outside of function. Move it to class level and you will be fine. Because if it is in the class level, it will hold previous value and it will able to reach >1.
Here is How Coroutine yields work

How can I move the object back to his parent position as child?

IEnumerator ThrowObject(Transform objectToMove, Transform toPosition, float duration)
{
Vector3 originPos = objectToMove.position;
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
Vector3 currentPos = objectToMove.position;
float time = Vector3.Distance(currentPos, toPosition.position) / (duration - counter) * Time.deltaTime;
objectToMove.position = Vector3.MoveTowards(currentPos, toPosition.position, time);
yield return null;
}
var naviParent = GameObject.Find("Navi Parent");
StartCoroutine(ThrowBack(objectToMove, naviParent.transform.position , duration));
}
IEnumerator ThrowBack(Transform objectToMove,Vector3 originalPosition , float duration)
{
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
Vector3 currentPos = objectToMove.position;
float time = Vector3.Distance(currentPos, originalPosition) / (duration - counter) * Time.deltaTime;
objectToMove.position = Vector3.MoveTowards(currentPos, originalPosition, time);
//yield return null essentially means "return here every frame"
yield return null;
}
}
When the game start NAVI is child of Navi Parent. Navi Parent position is set to 0,0,0
Also NAVI is at position 0,0,0
Now NAVI is start moving to the target in the method ThrowObject and when NAVI reached the target a new Coroutine start and NAVI is moving back to his parent :
var naviParent = GameObject.Find("Navi Parent");
But when NAVI is reaching to Navi Parent back his position is not at 0,0,0 :
You can see NAVI is not back to the Navi Parent(The player hand) but he is back close to it.
Now NAVI position is : X 0.002350773 Y 0.0007914092 Z 0.002649077
And if I change it on my own the NAVI position to 0,0,0 now NAVI is in the player hand as it should be :
I want that when the NAVI is moving back in the ThrowBack that he will end at 0,0,0 and not that I will have to change it back to 0,0,0
And if I will change NAVI in the end after the second Coroutine ends it will looks like navi change position like jumping or changing position rude and I want it to move smooth back to 0,0,0 in the while loop.
I suspect your issue is with:
float time = Vector3.Distance(currentPos, originalPosition) / (duration - counter) * Time.deltaTime;
objectToMove.position = Vector3.MoveTowards(currentPos, originalPosition, time);
You are evaluating the currentPos every frame which then decreases time, eventually, due to also applying deltaTime, this time gets very small as it approaches the destination.
MoveTowards' third parameter is a maxDistanceDelta, which I assume is what you're trying to use time for, variable name notwithstanding. However, it is intended as a max speed limiter.
Try replacing the above two lones of code with:
objectToMove.position = Vector3.MoveTowards(currentPos, originalPosition, Speed * Time.deltaTime);
Or, at the very least, let time be no less than some small positive value (eg, time = Mathf.Max(time, 0.001f))
I think it happens because of your counter. Sometimes counter gets a bigger value than the duration and that might effect the calculations. Also using Vectore.Lerp is better. Check this out and see if your problem gets fixed.
private IEnumerator ThrowObject(Transform objectToMove, Transform toPosition, float duration)
{
Vector3 originPos = objectToMove.position;
float counter = 0;
while (objectToMove.position != toPosition.position)
{
counter += Time.deltaTime;
if(counter > duration)
{
counter = duration;
}
objectToMove.position = Vector3.Lerp(originPos, toPosition.position, counter / duration);
yield return null;
}
var naviParent = GameObject.Find("Navi Parent");
StartCoroutine(ThrowBack(objectToMove, naviParent.transform.position, duration));
}
private IEnumerator ThrowBack(Transform objectToMove, Vector3 originalPosition, float duration)
{
Vector3 originPos = objectToMove.position;
float counter = 0;
while (objectToMove.position != originalPosition)
{
counter += Time.deltaTime;
if (counter > duration)
{
counter = duration;
}
objectToMove.position = Vector3.Lerp(originPos, originalPosition, counter / duration);
yield return null;
}
}
My assumption is that you are trying to move the object back to the parent with a set time no matter the distance.
If I'm right the method you should use is this.
float duration = 1f;
float time = 0f;
while (time < 1)
{
time += Time.deltaTime / duration;
object.localPosition = Vector3.Lerp(object.localPosition, targetPosition, time);
yield return null;
}
That will move an object to a new position with a set duration.

Slow down Rigidbody

I am adding forward force based on GetAxis ("Vertical") and a speed modifier up to normal max speed.
I am also allowing "speed boost" that adds more force on top of the normal added force up to boosted max speed.
what i am having hard time with is slowing the object down once boost period is over
So basically if you were booster to a faster speed then normal max speed and you cannot speed boost no more, slow the object back to normal max speed.
this is what i got so far:
float velocity;
float magnitude = myRigidBody.velocity.magnitude;
float vertical = Input.GetAxis ("Vertical");
if (Input.GetKeyUp(KeyCode.LeftShift)) {
boost = false;
}
if (Input.GetKeyDown (KeyCode.LeftShift)) {
boost = true;
}
if (!boost) {
velocity = vertical * speed;
Vector3 force = myRigidBody.transform.forward * velocity;
if (magnitude + force.sqrMagnitude < normalMaxSpeed) {
myRigidBody.drag = 0f;
myRigidBody.AddForce (force, ForceMode.Impulse);
} else if (magnitude + force.sqrMagnitude >= maxTrust + 50) {
myRigidBody.drag = 1.5f;
}
} else if ( magnitude < boostedMaxSpeed) {
velocity = vertical * (speed + speedBoost);
myRigidBody.AddForce (myRigidBody.transform.forward * velocity, ForceMode.Impulse);
This some what working but there must be a better solution other then changing drag.
Other than changing the drag, there are still other ways to do this. You just have to experiment to see which one works best.
1.Change the drag
myRigidBody.drag = 20f;
2.Add force to the opposite velocity.
public Rigidbody myRigidBody;
void FixedUpdate()
{
Vector3 oppositeVelocity = -myRigidBody.velocity;
myRigidBody.AddRelativeForce(oppositeVelocity);
}
3.Decrement or Lerp the current velocity back to the normal force.
public Rigidbody myRigidBody;
Vector3 normalForce;
void Start()
{
normalForce = new Vector3(50, 0, 0);
//Add force
myRigidBody.velocity = new Vector3(500f, 0f, 0f);
//OR
//myRigidBody.AddForce(new Vector3(500f, 0f, 0f));
}
void FixedUpdate()
{
//Go back to normal force within 2 seconds
slowDown(myRigidBody, normalForce, 2);
Debug.Log(myRigidBody.velocity);
}
bool isMoving = false;
void slowDown(Rigidbody rgBody, Vector3 normalForce, float duration)
{
if (!isMoving)
{
isMoving = true;
StartCoroutine(_slowDown(rgBody, normalForce, duration));
}
}
IEnumerator _slowDown(Rigidbody rgBody, Vector3 normalForce, float duration)
{
float counter = 0;
//Get the current position of the object to be moved
Vector3 currentForce = rgBody.velocity;
while (counter < duration)
{
counter += Time.deltaTime;
rgBody.velocity = Vector3.Lerp(currentForce, normalForce, counter / duration);
yield return null;
}
isMoving = false;
}
4.Change the dynamic friction of the Physics Material. You can't use this method because it requires collider which you don't have.
//Get the PhysicMaterial then change its dynamicFriction
PhysicMaterial pMat = myRigidBody.GetComponent<Collider>().material;
pMat.dynamicFriction = 10;
It's really up to you to experiment and decide which one works best for you. They are useful depending on what type of game you are making. For example, #4 is mostly used when rolling a ball because it uses physics material which requires frequent collision to actually work.
The method used in #3 gives you control over how long the transition should happen. If you need to control that then that's the choice.

Move Object outside of Update() Function without teleporting [duplicate]

I want to move an object slowly from his original position to a little higher position but this code moves the object instantly to the highest position even when i use really slow speed like 0.0001f. I call LiftObj() inside another code 1 time only and i tell it run until it reaches the liftOffset. What is wrong with this code?
void LiftObj(GameObject Obj) {
float origianlPos = Obj.transform.position.y;
while (Obj.transform.position.y < origianlPos + liftOffset) {
Obj.transform.position += Vector3.up * 0.0001f;
float newPos = Obj.transform.position.y;
newPos = Mathf.Clamp (newPos, newPos, newPos + liftOffset);
Obj.transform.position += Vector3.up * 0.0001f;
}
but this code moves the object instantly to the highest position even
when i use really slow speed like 0.0001f.
You are not waiting at-all. The while loop will execute as fast as possible no matter how low your variable is.You should be doing this in a coroutine function then wait with the yield keyword yield return null.
IEnumerator LiftObj(GameObject Obj)
{
float origianlPos = Obj.transform.position.y;
while (Obj.transform.position.y < origianlPos + liftOffset)
{
Obj.transform.position += Vector3.up * 0.0001f;
float newPos = Obj.transform.position.y;
newPos = Mathf.Clamp(newPos, newPos, newPos + liftOffset);
Obj.transform.position += Vector3.up * 0.0001f;
yield return null;
}
Debug.Log("Done Moving Up!");
}
then move it with:
StartCoroutine(LiftObj(myGameObject));
Not even sure that would work as expected because you are missing Time.delta so the movement may not be smooth. If all you want to do is move from one position to another overtime, use the sample code below:
IEnumerator LiftObj(GameObject playerToMove, Vector3 toPoint, float byTime)
{
Vector3 fromPoint = playerToMove.transform.position;
float counter = 0;
while (counter < byTime)
{
counter += Time.deltaTime;
playerToMove.transform.position = Vector3.Lerp(fromPoint, toPoint, counter / byTime);
yield return null;
}
}
Move from current myGameObject position to Vector3(0, 5, 0) in 4 seconds:
StartCoroutine(LiftObj(myGameObject, new Vector3(0, 5, 0), 4));
You're using a while loop in an incorrect place here.
While loops continue to execute with in one frame as long as the statement is true.
This means that regardless of how low your magnitude multiplier is (the 0.001f) it will just take more iterations with in the same frame to reach the target.
You should check to see each frame whether the object has reached its goal yet, and if it hasn't apply the transformation.

Object moves instantly to new position even with very low speed

I want to move an object slowly from his original position to a little higher position but this code moves the object instantly to the highest position even when i use really slow speed like 0.0001f. I call LiftObj() inside another code 1 time only and i tell it run until it reaches the liftOffset. What is wrong with this code?
void LiftObj(GameObject Obj) {
float origianlPos = Obj.transform.position.y;
while (Obj.transform.position.y < origianlPos + liftOffset) {
Obj.transform.position += Vector3.up * 0.0001f;
float newPos = Obj.transform.position.y;
newPos = Mathf.Clamp (newPos, newPos, newPos + liftOffset);
Obj.transform.position += Vector3.up * 0.0001f;
}
but this code moves the object instantly to the highest position even
when i use really slow speed like 0.0001f.
You are not waiting at-all. The while loop will execute as fast as possible no matter how low your variable is.You should be doing this in a coroutine function then wait with the yield keyword yield return null.
IEnumerator LiftObj(GameObject Obj)
{
float origianlPos = Obj.transform.position.y;
while (Obj.transform.position.y < origianlPos + liftOffset)
{
Obj.transform.position += Vector3.up * 0.0001f;
float newPos = Obj.transform.position.y;
newPos = Mathf.Clamp(newPos, newPos, newPos + liftOffset);
Obj.transform.position += Vector3.up * 0.0001f;
yield return null;
}
Debug.Log("Done Moving Up!");
}
then move it with:
StartCoroutine(LiftObj(myGameObject));
Not even sure that would work as expected because you are missing Time.delta so the movement may not be smooth. If all you want to do is move from one position to another overtime, use the sample code below:
IEnumerator LiftObj(GameObject playerToMove, Vector3 toPoint, float byTime)
{
Vector3 fromPoint = playerToMove.transform.position;
float counter = 0;
while (counter < byTime)
{
counter += Time.deltaTime;
playerToMove.transform.position = Vector3.Lerp(fromPoint, toPoint, counter / byTime);
yield return null;
}
}
Move from current myGameObject position to Vector3(0, 5, 0) in 4 seconds:
StartCoroutine(LiftObj(myGameObject, new Vector3(0, 5, 0), 4));
You're using a while loop in an incorrect place here.
While loops continue to execute with in one frame as long as the statement is true.
This means that regardless of how low your magnitude multiplier is (the 0.001f) it will just take more iterations with in the same frame to reach the target.
You should check to see each frame whether the object has reached its goal yet, and if it hasn't apply the transformation.

Categories

Resources