It cannot be done......or so it seems.
Hi All.
It would appear that getting a gradual increase in speed of an object, by a set amount, over a set period of time, in progressive stages based on key input, is nigh on impossible.
Scenario is I have a ship and It was planned to be a recreation of old school engine controls on an actual ship....stop, ahead 1/4, ahead full, etc.
Now I can achieve the changes in speed just fine....different key inputs will increase or decrease the speed by a set amount. Can go from zero to 3, and then to 6,9 and 12 with subsequent key inputs.
The problem is the speed change is instantaneous, and looks a bit ridiculous.
I am trying to get the speed to increase gradually over time with each key press, but it will just not work.
I am currently enrolled in a game dev course, so I asked for assistance.
A fellow student gave me a script that involved Lerp, and one of my tutors gave me one that used coroutines.
Problem is the result was pretty much the same regardless, the speed change is instant, though the ratios did change.
I know this is possible, as the same system is used in another game I play, but how is it achieved?
Have spent so many hours/days trying to figure this out it is not even remotely funny. I am almost at the point of sacrificing my firstborn to the coding gods to get a solution to this.
Codes are as below:
Mine. public class movement2 : MonoBehaviour {
public float turnSpeed;
public float velocity = 0f;
public float maxVelocity;
public float minVelocity;
public float AccelSpeed;
public float maxAccel;
public float minAccel;
void Update()
{
if (Input.GetKeyDown (KeyCode.W))
velocity += maxAccel;
if (Input.GetKeyDown (KeyCode.S))
velocity -= maxAccel;
if (Input.GetKey (KeyCode.A))
transform.Rotate (Vector3.up, -turnSpeed * velocity * Time.deltaTime);
if (Input.GetKey (KeyCode.D))
transform.Rotate (Vector3.up, turnSpeed * velocity * Time.deltaTime);
if (velocity >= maxVelocity)
velocity = maxVelocity;
if (velocity <= minVelocity)
velocity = minVelocity;
transform.Translate (Vector3.forward * velocity * Time.deltaTime);
}
}
Tutors: public class movement : MonoBehaviour { public float velocity = 0f; public float maxVelocity = 100f; public float minVelocity = 0f; public float AccelSpeed;
public float minAccel;
void Update()
{
if (Input.GetKeyDown(KeyCode.W))
{
StartCoroutine("UpdateVelocity", 3);
}
if (Input.GetKeyDown(KeyCode.S))
{
StartCoroutine("UpdateVelocity", -1);
}
transform.Translate (Vector3.forward * velocity * Time.deltaTime);
}
IEnumerator UpdateVelocity(float value)
{
float temp = velocity + value;
while (velocity < temp) {
velocity += 0.1f;
}
if (velocity >= temp)
{
velocity -= 0.1f;
}
{
yield return new WaitForSeconds(0.25f);
}
}
}
And fellow Students: public class movment3 : MonoBehaviour {
public float speed = 0f;
public float maxspeed;
public float minspeed;
public float AccelSpeed;
void Update ()
{
float maxAccel = Mathf.Lerp (minspeed, maxspeed, Time.deltaTime * 10f);
if (Input.GetKeyDown (KeyCode.W))
speed += maxAccel;
transform.Translate (Vector3.forward * speed * Time.deltaTime);
}
}
It shouldn't be this hard......should it?
Any solutions GRATEFULLY recieved.
Cheers Coldbeer
Hi Again.
Have had a chance to play around with this a bit, and unfortunately it turns out it is not quite what I was after.
The acceleration is fantastically smooth, but it varies depending on when I press the input key, and does not like reverse at all. If I try to reverse, it takes many key presses to get back to going forward(calculation obviously doesn't like negative numbers).
So I am back to the drawing board.
Need this sort of smooth Acceleration, but need it to be applied evenly with each input, either positively or negatively, depending on whether I am speeding up or slowing down.
To put what I am after in the simplest terms:
One forward key input will slowly accelerate the ships speed from 0 to 3. Subsequent forward inputs will increase the speed by the same ratio, from 3 to 6, 6 to 9 and 9 to 12.
The same needs to happen when decreasing speed, smoothly move from 12>9>6>3>0>-3(reverse).
And then there is the whole issue of "button mashing" straight to full speed or reverse.
If anyone is familiar with World of Warships, they will know what I am trying to achieve.
Will keep at it. Who knows I might luck onto the solution....one day.....
Cheers
Coldbeer
This will however cause problems when calling the coroutine several times (some kind of "race" where each coroutine try to set velocity to its own goal, one trying to increase the value while the other decreases it).
Use my code in my answer and add this to the update function:
if (Input.Get$$anonymous$$eyDown($$anonymous$$eyCode.S))
{
StopCoroutine("UpdateVelocity");
StartCoroutine("UpdateVelocity", -1);
}
Also change the coroutine: float temp = value;
This will stop any coroutine that is currently running (and thus stop the change in velocity towards that goal) and just set the goal directly when calling the coroutine (temp will not depend on your current velocity but will make you go towards say -1, or 3/9/12). You will have to use a counter to deter$$anonymous$$e how many times you've pressed a certain button and thus how much the target velocity should be. Just increase the counter when pressing W and decrease it when pressing S. Use $$anonymous$$athf.Clamp(counter,-1,4) to make sure the value doesn't go outside a certain bound.
I should also add: $$anonymous$$ultiply or divide Time.deltaTime to tweak the speed of transition. velocity = $$anonymous$$athf.Lerp(velocity, temp, Time.deltaTime);
Answer by Salmjak · Feb 08, 2017 at 12:57 PM
They are both kind of right. My version of the coroutine would look like this:
WaitForEndOfFrame waitFrame = new WaitForEndOfFrame(); //Create your YieldInstruction here so you dont allocate new memory every iteration
IEnumerator UpdateVelocity(float value)
{
float temp = velocity + value;
while (Mathf.Abs(velocity-temp) > float.Epsilon) {
velocity = Mathf.Lerp(velocity, temp, Time.deltaTime);
yield return waitFrame;
}
}
This will however cause problems when calling the coroutine several times (some kind of "race" where each coroutine try to set velocity to its own goal, one trying to increase the value while the other decreases it). So I would but "float temp" outside of the coroutine depending on the behaviour you want.
You are a Legend, THAN$$anonymous$$ YOU very much. This is EXACTLY what I was looking for.
Will now go and investigate $$anonymous$$athf.Abs and Epsilon......it's all well and good having something that works, but if I don't understand the "how", i am not really getting anywhere.
Thanks again
Coldbeer
Your answer
Follow this Question
Related Questions
Coroutine Error when Capturing 360 Video 0 Answers
Which is more efficient, Update() or a couroutine with while(true)? 1 Answer
What is Image.Start() ?? and How can i reduce this function's GC.Collect 0 Answers
Heavy use of Unity API & UI Thread 0 Answers
I need help creating some coding so that it loads the next level (2 Players) 0 Answers