Acceleration problem

Hello,

sorry for the not easy question, but 'm having an hard time with an acceleration script.

My question, basically is almost identical to this:
http://answers.unity3d.com/questions/583863/move-player-via-button-press-with-including-accele.html

that’s not me though and it was left without answers :(. Also I use my own controls.

First, let me explain how my game works: basically you have a control stick on the left of your screen, but that’s ONLY to rotate the player.
I’m using ControlFreak’s sticks and buttons, and here’s the script:

var grades : float = 90; 
var rotaz :float;

then, in a function Update, wrapped in the stick part: 

   rotaz = CFInput.GetAxis("Horizontal");
    transform.Rotate(0, rotaz * grades * Time.deltaTime, 0);

this works perfectly so far, the player can turn around to decide the direction with the stick while he’s going.

To move the player I need a script that moves him when I press a touch button on release (game is for mobile).
Technically I already have the button ready:

movingZone.JustUniReleased())
			{
			var movingUniRelStartPos	: Vector2	= movingZone.GetReleasedUniStartPos();
			var movingUniRelEndPos	: Vector2	= movingZoneGetReleasedUniEndPos();
			var movingUniRelStartBox	: int		= TouchZone.GetBoxPortion(2, 2, movingZone.GetReleasedUniStartPos(TouchCoordSys.SCREEN_NORMALIZED)); 
			var movingUniRelEndBox	: int		= TouchZone.GetBoxPortion(2, 2, movingZone.GetReleasedUniEndPos(TouchCoordSys.SCREEN_NORMALIZED)); 

			var movingUniRelDragVel	: Vector2	= movingZone.GetReleasedUniDragVel();
			var movingUniRelDragVec	: Vector2	= movingZone.GetReleasedUniDragVec();
					

//I probably need my script to be here
       		}

I had no problem accomplishing that at the beginning, as even a simple transform.Translate(Vector3.forward * speed) was sufficient.

The problem is that I need the player to accelerate AND decelerate but, like in that Unity Answers question it can’t be based on “how much the player holds down the button”.
It has to be that, the more you press (technically, release) the button, the more you keep accelerating, but in the moments you’re not pressing it it slowly decelerates. So if, say, you press it 20 times quickly and then stop, it still keeps going for a bit.

So far I’ve had two main problems:

The first is that my player, who’s also a rigidbody, does Not move only on the z axis so I guess I still have to use either Vector3.forward in some form or some rigidbody forces?

the second is that I’m not sure if I can use Time.deltaTime in my case, so I’m a bit lost here.

Thank you. If anyone can really help me and make this work, I’d make sure to give a small reward.

Vector3 input = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical"));
currentVelocity = Vector3.MoveTowards(currentVelocity,input,acceleration * Time.deltaTime);

Hey, thanks a lot for your help. Thought nobody wanted to hit his head on this.

Unfortunately Unity gives me an " UCE0001: ‘;’ expected "on the first row, but I’m not sure what’s wrong. Usually I fix this kind of errors quickly but I’m not familiar with the code you gave me so maybe you can see it.

It seems like you’re using JS while this code is in C#. The second line remains the same but the first one changes I believe, this should fix it:

var input : Vector3 = Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical"));

oh, damn, I’m not familiar with C#. Thanks, I’m sorry…

Unfortunately, while I tried to make the change you suggested, Unity says Unknown identifier ‘currentVelocity’
Should I set it somewhere? How?

(Continued from: http://forum.unity3d.com/threads/203823-Control-Freak-The-Ultimate-Virtual-Controller-for-Unity-Mobile/page20?p=1646833#post1646833 )

Hi,
I misunderstood what you wanted - I thought you were looking for something in style of a jet-pack, where holding the button will keep the object moving fast and short presses will result in slower, but still fairly smooth movement…
So you need something like a pinball-launcher - holding the button loads up the spring and on release, velocity is added to the object? But I think you were talking about rapid presses - this may result in choppy movement…
If you still need this, describe how you want the object to behave when the button is pressed and released in rapid succession.

Hi,

first of all thanks for caring yet again :slight_smile: - my promise to buy another license if I make this work is still here :smile:

Yes, maybe I failed to explain properly, I understand it’s not easy. I shouldn’t have wrote " I press a touch button on release" because it’s confusing.

I’ll try to explain the idea as best as I can (and after some tireless searching, I was able to find someone who did something very close to my idea, I put the video at the end):

basically, my character uses various man-riding-vehicles, like kick scooter, skateboards, bycicles, etc.
I wanted to replicate the “human push” needed to walk forward with this vehicles. Yes, even if it results in quite a tiring game to be played :smile:.

So my idea was: when you press the virtual button, a force (acceleration) is applied to the character (who’s one single entity with the vehicle) and he moves.
If you keep the button pressed, nothing happens: the character slowly decelerates.
So until you press again the button, the character will just decelerate and then stop. As you said, the risk is “choppy movement”: that’s why there has to be an acceleration and a deceleration. I mean, it can’t be that if you don’t press it in a second it immediately stops.

Last thing is, since it’s an acceleration, if you keep pressing the button quickly, as we said an acceleration will be added to the speed every time - which means you’ll go faster until you reach a max speed. In my game you race other guys, so basically you have to mash this button if you wanna win!

Now, to apply the force only when the virtual button is released, this was my original idea. But after watching this video and having tried this game on a NES emulator I noticed that here instead the force is applied immediately on press and not on release, and like in my game here also if you hold the button absolutely nothing happens. You have to press it again or the character will decelerate and then stop. That’s what I’m aiming at.

https://www.youtube.com/watch?v=m-RYJ7azOEk in the video you can heard the “bip bip bip” when the character is going forward: that’s when the player is pressing the button every time. You can also see the speed rising.

Sorry for the long post, but hopefully this is better!

Hi,
Here’s the modified version of my previous script. Now it simulates skateboard-like ‘kicking’…

#pragma strict

public var   rotSpeed   : float   = 90.0f;        // Degrees per second
public var   rotSmoothingTime   : float   = 0.1f;     // Smoothing time

private var   rotVel   : float;             // Internal var used for rotation smoothing
private var rotCur : float;               // Internal var used for rotation smoothing
private var rotTarget : float;             // Internal var used for rotation smoothing

public var motorMaxVel   : float   = 4.0f;     // Max velocity in units per second
public var motorAccelTime : float = 0.5f;     // Time for motor to reach max power
public var motorStopTime : float = 2.0f;     // Time for motor to stop
public var motorKickPower : float = 0.3f;       // How much power a single 'kick' adds


private var motorPowerTarget : float;
private var motorPowerCur   : float;   // Internal var used for motor power (0-1)
private var isKicking   : boolean;


function FixedUpdate()
   {
   // Control rotation...
   
   this.rotTarget += CFInput.GetAxis("Horizontal") * this.rotSpeed * Time.deltaTime;
   
   // Smooth rotation...
     
   var rotPrev : float = this.rotCur;
   this.rotCur = Mathf.SmoothDampAngle(this.rotCur, this.rotTarget, this.rotSmoothingTime, this.rotVel);
   
   this.transform.Rotate(0, (this.rotCur - rotPrev), 0);   // rotate by delta

   
   // 'Kick'
   
   if (CFInput.GetButtonDown("Fire1"))
     {
     this.isKicking = true;
     this.motorPowerTarget = Mathf.Clamp01(this.motorPowerCur + this.motorKickPower);
     }   
   
   // Accelerate forward when the "Fire" button is pressed...
   
   if (Time.deltaTime != 0)
     {
     if (this.isKicking)
       {
       if (this.motorAccelTime <= 0.0001)
         this.motorPowerCur = this.motorPowerTarget;
       else   
         this.motorPowerCur = Mathf.MoveTowards(this.motorPowerCur, this.motorPowerTarget, (Time.deltaTime / this.motorAccelTime));

       if (this.motorPowerCur >= (this.motorPowerTarget - 0.001f))
         this.isKicking = false;
       }
     else
       {
       if (this.motorStopTime <= 0.0001)
         this.motorPowerCur = 0;
       else   
         this.motorPowerCur = Mathf.MoveTowards(this.motorPowerCur, 0.0f, (Time.deltaTime / this.motorStopTime));

       this.motorPowerTarget = this.motorPowerCur;
       }
     }
           
   // Apply motor force...
   
   this.rigidbody.MovePosition(this.rigidbody.position + (this.transform.forward * (this.motorMaxVel * this.motorPowerCur * Time.deltaTime)));
   }

Dans,

thanks a lot for your help.
The script definitely works, although the standard parameters didn’t seem to work properly for some reason.
If you’re curious, right now I set it to:
MaxSpeed 50
Accel Time 0.5
Stop Time 5
Kick Power 0.2

This morning I bought another copy of your asset for a friend, you probably already got the paypal email =). You were more than helpful! I feel that even buying another copy of your asset isn’t enough, to be honest.

This is definitely my last post on the matter, and the last time I ask for help, I promise. I have only 2 small problems:

  1. so far, no matter what parameters I tried to change I wasn’t able to make the single click work properly. Basically, if I clicked once when the character was not moving,he didn’t move at all. That is - even if motor power cur and motor power target were back to 0.
    Then, sometimes, seemingly at random (but of course there must be a motivation) sometimes instead he did.

I fixed this by changing FixedUpdate to Update. But, I’m not sure if it’s right to do so, maybe it’s Input.GetButbutton who’s not okay with FixedUpdate?
I had noticed by setting as public the var “iskicking” that indeed it often didn’t check , when I pressed just once.That is, with FixedUpdate.
With Update though, I can’t really tell if it’s getting every single click even if I go fast, it seems to be. IsKicking doesn’t seem to check-decheck literally every click but it still seems to be working, but I know Update is called each frame and I’m not sure if that’s right here.

Last thing, I’m not sure if it’s because the script is written a bit differently than I imagined - but hey, if I was able to do it alone, I would’ve finished it already. What I mean of course is just the logic behind the script.

In particular, maybe it’s MotorAccelTime and MotorMaxVel that confuse me a bit: I thought that Max Speed should have been a number that has nothing to do with time but I noticed that changing it seems to influence a bit “how much track you cover” even if I didn’t change the kicking value…
I noticed that even if I keep Motor Kick Power very very low, if I set a very high max speed, like 200, it starts off with a bang, but it doesn’t make sense… or, better, I don’t understand why it does.

In my mind, for example, max speed is set to 100, and every kick adds a “5” until it stops adding anything at 100.
Then, in general, the player keeps decelerating. So I’m a bit confused here.

I don’t really need “a fix” for this as in the end I managed to set up parameters that seem to do the job: what I’d really need if possible is to insert some kind of public float value as “Speed” in arbitrary units that tells me how fast the player is going, 'cause otherwise it’s extremely very hard to tell. Like, I had to put some AI racing near me haha. MotorPowerCur goes just from 0 to 1 so it’s very very hard :(.
Edit: I’ve done a GUI text based on rigidbody.velocity.magnitude, but it still would be useful to have a public var I can look at, too.

Again, I’m truly sorry for the bother. Hopefully it’s my last post on the matter :smile:

That’s probably because your world is bigger than my test one and the default ‘maxSpeed’ is too small in your case.

Good catch! You should never use GetButtonDown()/Up() inside FixedUpdate(). Sorry. I’ll post updated code at the end of my post.

The name of ‘isKicking’ private variable may be a bit misleading. It is set to true when the object in ‘Kick’ / ‘Acceleration’ phase - it’s duration is controlled by ‘motorAccelTime’. That’s why it’s set to true longer that one frame.

That’s my fault - I should better explain the roles of each of public variables.

‘motorMaxVel’ - maximum speed in units per second.

‘motorAccelTime’ - duration of FULL acceleration/kick phase in seconds (from 0 to full motor power)

‘morotStopTime’ - duration of stop phase - from max. motor power to zero.

‘motorPower’ - it’s a normalized (0-1) value, that later will be multiplied by ‘maxSpeed’ to get speed in world-units.

‘motorKickPower’ - (normalized) amount of power that will be added to ‘motorPowerTarget’ at the end of ‘kick’ phase. If you set it to 0.5 - it’s going to take just two kicks to reach max. motor power. If set to 0.1, player will need to kick 10 times quickly to reach max power and so on…

‘controlByInput’ - if true this object will be controlled by the player, if false - by AI. (It’s not the most elegant way to do this, but it’s just to open the way… )

To get current speed in world units, you must multiply ‘motorPowerCur’ by ‘maxSpeed’. I’ll add a public method to get that value.

Here’s the updated code:

#pragma strict

public var   rotSpeed   : float   = 90.0f;        // Degrees per second
public var   rotSmoothingTime   : float   = 0.1f;     // Smoothing time

private var   rotVel   : float;             // Internal var used for rotation smoothing
private var rotCur : float;               // Internal var used for rotation smoothing
private var rotTarget : float;             // Internal var used for rotation smoothing

public var motorMaxVel   : float   = 4.0f;     // Max velocity in units per second
public var motorAccelTime : float = 0.5f;     // Time for motor to reach max power
public var motorStopTime : float = 2.0f;     // Time for motor to stop
public var motorKickPower : float = 0.3f;       // How much power a single 'kick' adds


private var motorPowerTarget : float;
private var motorPowerCur   : float;   // Internal var used for motor power (0-1)
private var isKicking   : boolean;     // Object in in 'Kicking' phase

private var justKicked : boolean;     // intenal flag to start kicking phase
private var turnSpeed : float;       // internal normalzied turn speed variable used to control turning

public var   controlByInput : boolean = true; //

// ----------------
public function GetSpeed() : float
   {
   return (this.motorPowerCur * this.motorMaxVel);
   }
  
  
  
// -----------------
public function Kick()
   {
   this.justKicked = true;
   }

// ---------------------
public function SetTurnSpeed(normalizedTurnSpeed : float)
   {
   this.turnSpeed = Mathf.Clamp(normalizedTurnSpeed, -1.0f, 1.0f);
   }
  

// ----------------
function Update()
   {
   // Control this object by input...
   if (this.controlByInput)
     {
     if (CFInput.GetButtonDown("Fire1"))
       this.Kick();
    
     this.SetTurnSpeed(CFInput.GetAxis("Horizontal"));
     }
    
   // ... or by AI.
   else
     {
     // AI code here...
     }
   }
// ----------------
function FixedUpdate()
   {
   // Control rotation...
  
   this.rotTarget += this.turnSpeed * this.rotSpeed * Time.deltaTime;
  
   // Smooth rotation...
    
   var rotPrev : float = this.rotCur;
   this.rotCur = Mathf.SmoothDampAngle(this.rotCur, this.rotTarget, this.rotSmoothingTime, this.rotVel);
  
   this.transform.Rotate(0, (this.rotCur - rotPrev), 0);   // rotate by delta

  
   // 'Kick'
  
   if (this.justKicked)
     {
     this.justKicked = false;
    
     this.isKicking = true;
     this.motorPowerTarget = Mathf.Clamp01(this.motorPowerCur + this.motorKickPower);
     }  
  
   // Accelerate forward when in 'Kick' phase...
  
   if (Time.deltaTime != 0)
     {
     if (this.isKicking)
       {
       if (this.motorAccelTime <= 0.0001)
         this.motorPowerCur = this.motorPowerTarget;
       else  
         this.motorPowerCur = Mathf.MoveTowards(this.motorPowerCur, this.motorPowerTarget, (Time.deltaTime / this.motorAccelTime));

       if (this.motorPowerCur >= (this.motorPowerTarget - 0.001f))
         this.isKicking = false;
       }
     else
       {
       if (this.motorStopTime <= 0.0001)
         this.motorPowerCur = 0;
       else  
         this.motorPowerCur = Mathf.MoveTowards(this.motorPowerCur, 0.0f, (Time.deltaTime / this.motorStopTime));

       this.motorPowerTarget = this.motorPowerCur;
       }
     }
          
   // Apply motor force...
  
   this.rigidbody.MovePosition(this.rigidbody.position + (this.transform.forward * (this.motorMaxVel * this.motorPowerCur * Time.deltaTime)));
   }

Thanks, I understand everything perfectly now.

I’m not sure if I lost it but I didn’t see the method, but no worries :slight_smile: , I did this:

var kmh = (motorPowerCur*motorMaxVel).ToString("f0");
kmDisplay.text = kmh + " Km/h";

and it worked perfectly. Before, I was using rigidbody.velocity.magnitude but it stopped working after a few steps and I was getting crazy haha.

So, again, thank you a lot for this. ^_^. I may sound tedious, but seriously, without this I couldn’t go forward in making the game after months of work. So, it was that important, and it’s NOT easy to find people willing to go as far a preparing detailed code.

If you’d like to PM me your real name, or even a nickname, I’d be more than happy to put you in the credits of my game in the “thank you” section, if I manage to finish it as I hope :stuck_out_tongue: