How to make a vector2 slowly go to a number [0]? - c#

I have a ship which is moving around with rigidbody velocity, and when the player has no input I want the ship to slow down to 0 and then stay at 0 until there's more input. What I have now is:
if(yAxis == 0)
{
StartCoroutine (StopRoutine());
}
IEnumerator StopRoutine()
{
yield return new WaitForSeconds (0.5f);
Vector2 force = rb.velocity;
rb.velocity = (new Vector2(0.2f,0.2f)) - force;
}
and all that does is make the ship go backward which I understand is due to it subtracting forever with no clamp, I just don't know how to make it go to 0 and STAY at 0, I also don't know how I would do it the other way too (making a vector2 which is in the negatives go to 0)

Actually you able to do many different ways. If want a use coroutines for this sample. Try this at update method in your script...
First of all you need to declare a variable to check have any input? (I think this is wind, fuel etc.)
bool isInput = true;
IEnumerator stopRoutine(float targetX, float targetY, float valueX, float valueY, float t) //targetX and targetY your target speed values, valueX and valueY is your acceleration or deceleration values and t is waitTime for while loop...
{
if (isInput)
{
isInput = !isInput; // You've to do this because unity update function re-runs every deltaTime.
Vector2 firstVelocity = rb.velocity;
if (firstVelocity.x < 0f && firstVelocity.y < 0f)
{
while (rb.velocity.x < targetX || rb.velocity.y < targetY)
{
rb.velocity += new Vector2(valueX, valueY);
yield return new WaitForSecondsRealtime(t);
}
}
else
{
while (rb.velocity.x > targetX || rb.velocity.y > targetY)
{
rb.velocity -= new Vector2(valueX, valueY);
yield return new WaitForSecondsRealtime(t);
}
}
}
}
I don't want to do it for you completely, but I want you to understand the logic in the event. Ok try this method in update()...

Related

Why the speed variable have no factor when changing in the inspector the value in run time ? How to update positions and make nonstop moving?

The main goal is to move the object between the positions with a delay. This is working fine but other things are not working.
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class Waypoints : MonoBehaviour
{
public GameObject objectPrefab;
public LineRenderer lineRenderer;
public List<Transform> pointsToMove;
public float speed;
public bool go = false;
private List<GameObject> objectsToMove;
private List<Vector3> positions;
void Start()
{
objectsToMove = new List<GameObject>();
//Spawn objects and start movement
}
private void Update()
{
if (go && lineRenderer.positionCount > 0 && CurvedLineRenderer.linesSet)
{
positions = GetLinePointsInWorldSpace();
foreach(Transform objToMove in pointsToMove)
{
positions.Add(objToMove.position);
}
for (int i = 0; i < 3; i++)
{
objectsToMove.Add(Instantiate(objectPrefab, transform.parent));
StartCoroutine(Move(i));
}
go = false;
}
}
private IEnumerator Move(int i)
{
//Wait interval
yield return new WaitForSeconds(i * 10);
bool stillTraveling = true;
int pointIndex = 0;
float threshold = 0.1f;
while (stillTraveling)
{
//Check if close to point
if (Vector3.Distance(objectsToMove[i].transform.position, positions[pointIndex]) < threshold)
{
//Check if can move next point
if (pointIndex + 1 > positions.Count - 1)
{
stillTraveling = false;
}
//If not stop moving
else
{
pointIndex++;
}
}
//Move towards point
objectsToMove[i].transform.position = Vector3.MoveTowards(objectsToMove[i].transform.position, positions[pointIndex], speed * Time.deltaTime);
yield return null;
}
}
List<Vector3> GetLinePointsInWorldSpace()
{
var pointsToMove = new Vector3[lineRenderer.positionCount];
//Get the positions which are shown in the inspector
lineRenderer.GetPositions(pointsToMove);
//the points returned are in world space
return pointsToMove.ToList();
}
}
I'm updating once the positions in the Update.
I'm using StartCoroutine once in the Update to send the objects to move between the waypoints with a delay.
private void Update()
{
if (go && lineRenderer.positionCount > 0 && CurvedLineRenderer.linesSet)
{
positions = GetLinePointsInWorldSpace();
foreach(Transform objToMove in pointsToMove)
{
positions.Add(objToMove.position);
}
for (int i = 0; i < 3; i++)
{
objectsToMove.Add(Instantiate(objectPrefab, transform.parent));
StartCoroutine(Move(i));
}
go = false;
}
}
It's working by sending the objects to move with delay between the waypoints, but now I have some other problems.
Because I'm using StartCoroutine for the delay and because I'm setting the go flag to false then the speed value have no affect on the moving objects at run time and I'm not sure how to solve it so the speed will have a factor affecting the moving objects.
Another problem or not a problem but not sure how to do it is how to Update in run time the positions List ? Here in the Update I'm updating the List once :
positions = GetLinePointsInWorldSpace();
foreach(Transform objToMove in pointsToMove)
{
positions.Add(objToMove.position);
}
but I want that if the amount of positions in the function GetLinePointsInWorldSpace or/and the amount of pointsToMove have changed to update the positions List at rune time. I could just call this part in the update nonstop but I'm not sure of it will be too expensive ?
Last problem or how to do is how to make the object to move to move between the waypoints non stop over again from the start or moving backward when getting the last waypoint ? Again because using StartCoroutine for the delay everything is happening once.
If you want to move an object from one point to another, you should use Mathf.Sin(). The is using the sine function, and you put in the amount of cycles times Mathf.PI times 2. Here is what you would write:
float period = 2f;
Vector3 a; //the first point the object goes to
Vector3 b; //the second point that the object goes to
void Update()
{
float cycles = period * Time.time * Mathf.PI;
float amplitude = Mathf.Sin(cycles);
Vector3 location = Vector3.Lerp(a, b, amplitude);
Transform.position = location;
}
I then use Vector3.Lerp to interpolate between point a and point b. This isn’t what you were doing, but it is a much simpler way you can do it.
Vector3 a; //the first point the object goes to
Vector3 b; //the second point that the object goes to
void Update()
{
float cycles = period * Time.time;
Vector3 location = Vector3.Lerp(a, b, cycles);
Transform.position = location;
}
This one is for bringing the object just strait to point b and not back.
If you want there to be a delay between when it moves, here is the script:
float period = 2f;
float waitTime;
bool AtoB = true;
Vector3 a; //the first point the object goes to
Vector3 b; //the second point that the object goes to
void Start()
{
StartCoroutine(“Oscillator”);
}
IEnumerator Oscillator
{
float amplitude;
if (AtoB)
{
amplitude += Time.deltaTime * period;
}
else
{
amplitude -= Time.deltaTime * period;
}
amplitude = Mathf.Clamp(amplitude, 0, 1);
Vector3 location = Vector3.Lerp(a, b, amplitude);
Transform.position = location;
if (amplitude == 1)
{
AtoB = false;
yield return new WaitForSeconds(waitTime);
}
if else (amplitude == 0)
{
AtoB = true;
yield return new WaitForSeconds(waitTime);
}
else
{
yield return false;
}
}

Playing animation with swipe controls and transform.position problem Unity

I've set up my swipe controls to transform my player left/right/up/down and I've restricted the movement on 3 lines; -1, 0, 1. Everything works fine but the movement is not smooth at all and the player seems to be "teleporting" from one position to another. I wanted to smooth the movement by playing the animation but the result was that the animation was being played after the player has changed his position.
Is there any way to play the animation while the player is changing his position or a way to smooth the movement so it looks right ?
I've tried everything and now I'm stuck with the problem, please help
Here's my code
public class SwipeControls : MonoBehaviour {
public float speed = 5.0f;
private Vector3 startpos; // start position
private Vector3 endpos; //end position
public int pozioma = 0;
public int pionowa = 0;
Animator anim;
void Start() {
GetComponent<Animator>();
}
void Update() {
foreach (Touch touch in Input.touches) {
Vector3 newPosition;
if (touch.phase == TouchPhase.Began) {
startpos = touch.position;
endpos = touch.position;
}
if (touch.phase == TouchPhase.Moved) {
endpos = touch.position;
}
if (touch.phase == TouchPhase.Ended) {
newPosition = transform.position;
if (Mathf.Abs(startpos.y - endpos.y) > Mathf.Abs(startpos.x - endpos.x)) {
if ((startpos.y - endpos.y) > 100 && pionowa > -1) //swipe down
{
pionowa--;
newPosition.y -= speed;
transform.position = newPosition;
anim.SetTrigger("Flydown");
}
if ((startpos.y - endpos.y) < -100 && pionowa < 1) //swipe up
{
pionowa++;
newPosition.y += speed;
transform.position = newPosition;
anim.SetTrigger("Flyup");
}
}
else {
if ((startpos.x - endpos.x) > 100 && pozioma > -1) //swipe left
{
pozioma--;
newPosition.z -= speed;
transform.position = newPosition;
anim.SetTrigger("Flyleft");
}
}
if ((startpos.x - endpos.x) < -100 && pozioma < 1) //swipe right
{
pozioma++;
newPosition.z += speed;
transform.position = newPosition;
anim.SetTrigger("Flyright");
}
}
}
}
}
Modify your code instead of using just
transform.position = newPosition;
in all position use
transform.position = Vector3.Lerp(transform.position, newPosition, smooth * Time.deltaTime);
where smooth is float variable assign it required value may be 0.5f or whatever you want
Note : What it actually does is that it makes game object to move little based on smooth value, every frame
Note that we do not know your animator setup. If in those animation clips there is anywhere a keyframe for the position then the animator will always overrule anything done by scripts!
Instead of setting the new position immediately you could use a Coroutine with StartCoroutine like (also fixing some coding styles)
public class SwipeControls : MonoBehaviour
{
// Flag for ignoring input until current animation finished
private bool routineRunning;
// Configure in the Inspector distance to swipe
// in unity units
[SerializeField] private float swipeDistance = 5;
// configure this in the Inspector
// How long should the swiping take in seconds
[SerializeField] private float swipeDuration = 1;
private Vector3 _startpos; // start position
private Vector3 _endpos; //end position
public int pozioma = 0;
public int pionowa = 0;
private Animator _anim;
private void Start()
{
GetComponent<Animator>();
}
private void Update()
{
// Ignore any Input while a routine is running
if (routineRunning) return;
foreach (var touch in Input.touches)
{
switch (touch.phase)
{
case TouchPhase.Began:
_startpos = touch.position;
_endpos = touch.position;
break;
case TouchPhase.Moved:
_endpos = touch.position;
break;
case TouchPhase.Ended:
if (Mathf.Abs(_startpos.y - _endpos.y) > Mathf.Abs(_startpos.x - _endpos.x))
{
if (_startpos.y - _endpos.y > 100 && pionowa > -1) //swipe down
{
pionowa--;
StartCoroutine(MoveSmooth(Vector3.up * -1, swipeDuration));
_anim.SetTrigger("Flydown");
}
else if (_startpos.y - _endpos.y < -100 && pionowa < 1) //swipe up
{
pionowa++;
StartCoroutine(MoveSmooth(Vector3.up, swipeDuration));
_anim.SetTrigger("Flyup");
}
}
else
{
if (_startpos.x - _endpos.x > 100 && pozioma > -1) //swipe left
{
pozioma--;
StartCoroutine(MoveSmooth(Vector3.forward * -1, swipeDuration));
_anim.SetTrigger("Flyleft");
}
else if (_startpos.x - _endpos.x < -100 && pozioma < 1) //swipe right
{
pozioma++;
StartCoroutine(MoveSmooth(Vector3.forward, swipeDuration));
_anim.SetTrigger("Flyright");
}
}
break;
}
}
}
private IEnumerator MoveSmooth(Vector3 direction, float duration)
{
routineRunning = true;
var currentPosition = transform.localPosition;
var targetPosition = currentPosition + direction * swipeDistance;
var timePassed = 0f;
do
{
// Additionally add some ease in and out to the movement to get
// even smoother movement
var lerpFactor = Mathf.SmoothStep(0, 1, timePassed / duration);
// Interpolate the position between currentPosition and targetPosition
// using the factor between 0 and 1
transform.localPosition = Vector3.Lerp(currentPosition, targetPosition, lerpFactor);
// increase by time since last frame
timePassed += Time.deltaTime;
// let this frame render and go on from
// here in the next frame
yield return null;
} while (timePassed <= duration);
// To avoid over or undershooting in the end set a fixed value
transform.localPosition = targetPosition;
routineRunning = false;
}
}
I don't know why you are using y+= and z+= .. it seems to me that it meeans the Camera is facing left in your scene e.g. with rotation = 0, -90, 0 and e.g. position = 10, 0, 0 than this should be the result (Note I copied the same code into if(GetMouseButtonDown(0)) etc in order to simulate the touch on the PC.)

Unity Ai GameObject Keeping increasing his speed when he moving

i did some code making an enemy search if the player exists or not and if the player exists go-to player to kill him the code is working fine but every moment the enemy move the speed keep increased and i do not want the speed increased at all so how to make it moving at the same starting speed without any increase
this is my code
void Update () {
if (target == null) {
if (!searchingForThePlayer) {
searchingForThePlayer = true;
StartCoroutine (searchForPlayer ());
}
return;
}
if (Vector3.Distance (target.position, transform.position) < 20) {
transform.position = Vector2.MoveTowards (transform.position, target.position, 1f * Time.deltaTime);
if (target.position.x < transform.position.x && !facingRight)
Flip ();
if (target.position.x > transform.position.x && facingRight)
Flip ();
} else {
}
StartCoroutine (UpdatePath ());
}
IEnumerator searchForPlayer () {
GameObject sRusult = GameObject.FindGameObjectWithTag ("Player");
if (sRusult == null) {
yield return new WaitForSeconds (0.5f);
StartCoroutine (searchForPlayer ());
} else {
target = sRusult.transform;
searchingForThePlayer = false;
StartCoroutine (UpdatePath ());
yield return null;
}
}
IEnumerator UpdatePath () {
if (target == null) {
if (!searchingForThePlayer) {
searchingForThePlayer = true;
StartCoroutine (searchForPlayer ());
}
yield return null;
} else {
if (Vector3.Distance (target.position, transform.position) < 20) {
transform.position = Vector2.MoveTowards (transform.position, target.position, 1f * Time.deltaTime);
if (target.position.x < transform.position.x && !facingRight)
Flip ();
if (target.position.x > transform.position.x && facingRight)
Flip ();
} else {
}
}
// Start a new path to the target position, return the result to the OnPathComplete method
yield return new WaitForSeconds (1f / 2f);
StartCoroutine (UpdatePath ());
}
void Flip () {
Vector3 scale = transform.localScale;
scale.x *= -1;
transform.localScale = scale;
facingRight = !facingRight;
}
Every frame, in your Update() method, you start the UpdatePath() coroutine. Then, in UpdatePath(), you start another UpdatePath() coroutine. In no situation do you don't start it, so this ensures that UpdatePath() keeps running forever and ever.
Since you also keep starting a new one in Update(), this means that you keep piling on more and more coroutines, which means the more the game runs, the more UpdatePath() gets called every frame.
in other words, your object's speed isn't technically increasing, it's just the number of time MoveTowards() is called that does, which does have the same end result.
As for the fix, I'd recommend restructuring your code. For example, I find it highly suspicious that Update() and UpdatePath() are near identical copies of each other. I also find it weird that UpdatePath() starts itself at the end of its run.

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.

Coroutines and while loop

I have been working on a object movement along a path Which i have been geting from Navmesh Unity3d
I am using coroutine in which i controled it with while loop as i can show
public void DrawPath(NavMeshPath pathParameter, GameObject go)
{
Debug.Log("path Parameter" + pathParameter.corners.Length);
if (agent == null || agent.path == null)
{
Debug.Log("Returning");
return;
}
line.material = matToApplyOnLineRenderer;
line.SetWidth(1f, 1f);
line.SetVertexCount(pathParameter.corners.Length);
allPoints = new Vector3[pathParameter.corners.Length];
for (int i = 0; i < pathParameter.corners.Length; i++)
{
allPoints[i] = pathParameter.corners[i];
line.SetPosition(i, pathParameter.corners[i]);
}
StartCoroutine(AnimateArrow(pathParameter));
//StartCoroutine(AnimateArrowHigh(pathParameter));
}
#endregion
#region AnimateArrows
void RunAgain()
{
StartCoroutine(AnimateArrow(Navpath));
}
IEnumerator AnimateArrow(NavMeshPath path)
{
Vector3 start;
Vector3 end;
while (true)
{
if (index > 0)
{
if (index != path.corners.Length - 1)
{
start = allPoints[index];
index += 1;
end = allPoints[index];
StopCoroutine("MoveObject");
StartCoroutine(MoveObject(arrow.transform, start, end, 3.0f));
yield return null;
}
else
{
index = 0;
RunAgain();
}
}
else if (index == 0)
{
start = allPoints[index];
arrow.transform.position = allPoints[index];
index += 1;
end = allPoints[index];
StopCoroutine("MoveObject");
StartCoroutine(MoveObject(arrow.transform, start, end, 3.0f));
yield return null;
}
}
}
IEnumerator MoveObject(Transform arrow, Vector3 startPos, Vector3 endPos, float time)
{
float i = 0.0f;
float rate = 1.0f / time;
journeyLength = Vector3.Distance(startPos, endPos);
float distCovered = (Time.time - startTime) * speed;
float fracJourney = distCovered / journeyLength;
while (i < 1.0f)
{
// Debug.Log("fracJourney In While" + fracJourney);
arrow.position = Vector3.LerpUnclamped(startPos, endPos, fracJourney);
yield return endPos;
}
Debug.LogError("Outside While");
}
But the problem is i have to move object on a constant speed but my object is gaining speed at every loop as i have to make movement in a loop so it tends to move until user wants to end it by input
guys plz help i dont understand what i am doing wrong in Coroutines that the speed of my objects is rising i wat it to stay constant but somehow its not working that way
thanks
As an alternative, you could utilize Unity's AnimationCurve class to map out all kinds of super smooth animation types easily:
You can define the curves in the inspector, or in code
public AnimationCurve Linear
{
get
{
return new AnimationCurve(new Keyframe(0, 0, 1, 1), new Keyframe(1, 1, 1, 1));
}
}
And you can define useage in a coroutine as such:
Vector2.Lerp (startPos, targetPos, aCurve.Evaluate(percentCompleted));
Where "percentCompleted" is your timer/TotalTimeToComplete.
A full example of lerping can be seen from this function:
IEnumerator CoTween(RectTransform aRect, float aTime, Vector2 aDistance, AnimationCurve aCurve, System.Action aCallback = null)
{
float startTime = Time.time;
Vector2 startPos = aRect.anchoredPosition;
Vector2 targetPos = aRect.anchoredPosition + aDistance;
float percentCompleted = 0;
while(Vector2.Distance(aRect.anchoredPosition,targetPos) > .5f && percentCompleted < 1){
percentCompleted = (Time.time - startTime) / aTime;
aRect.anchoredPosition = Vector2.Lerp (startPos, targetPos, aCurve.Evaluate(percentCompleted));
yield return new WaitForEndOfFrame();
if (aRect == null)
{
DeregisterObject(aRect);
yield break;
}
}
DeregisterObject(aRect);
mCallbacks.Add(aCallback);
yield break;
}
Check out this Tween library for more code examples: https://github.com/James9074/Unity-Juice-UI/blob/master/Juice.cs
while (i < 1.0f) will run forever because i is 0.0f and 0.0f is always < 1.0f and there is no place inside your while loop, where you increement i so that it will >= 1.0f. You need a way to exit that while loop. It should have looked like something below:
while (i < 1.0f){
i++ or i= Time.detaTime..... so that this loop will exist at some point.
}
Also your moving function is bad. The function below should do what you are trying to do:
bool isMoving = false;
IEnumerator MoveObject(Transform arrow, Vector3 startPos, Vector3 endPos, float time = 3)
{
//Make sure there is only one instance of this function running
if (isMoving)
{
yield break; ///exit if this is still running
}
isMoving = true;
float counter = 0;
while (counter < time)
{
counter += Time.deltaTime;
arrow.position = Vector3.Lerp(startPos, endPos, counter / time);
yield return null;
}
isMoving = false;
}
Also, in your AnimateArrow(NavMeshPath path) function, replace these three lines of code:
StopCoroutine("MoveObject");
StartCoroutine(MoveObject(arrow.transform, start, end, 3.0f));
yield return null;
with
yield return StartCoroutine(MoveObject(arrow.transform, start, end, 3.0f));
Doing this will wait the MoveObject function to finish before returning and running again in the while loop. You have to replace these inside if (index != path.corners.Length - 1) and else if (index == 0)
Maybe you could multiply the velocity by, say, 0.95f. This will make it accelerate, then stay at a constant speed, and then when you want it to stop it will gradually decelerate. Increasing the 0.95f will cause it to accelerate/decelerate faster.

Categories

Resources