I’m trying to make the player move with “AddForce” but I’m constantly getting some unexpected results. I made a Physics thread recently, which is related to this.
So far this is what I have in terms of code:
public float force;
public Rigidbody rb;
void Start () {
rb = GetComponent<Rigidbody>();
}
void FixedUpdate () {
// If "W" key is pressed.
if (Input.GetKey(KeyCode.W)) {
// Add forward force to the player.
rb.AddForce(transform.forward * force);
//Debug.Log("going forward");
}
// If "S" key is pressed.
if (Input.GetKey(KeyCode.S)) {
// Add backwards force to the player.
rb.AddForce(-transform.forward * force);
//Debug.Log("going backwards");
}
// If "A" key is pressed.
if (Input.GetKey(KeyCode.A)) {
// Add leftwards force to the player.
rb.AddForce(-transform.right * force);
//Debug.Log("going left");
}
// If "D" key is pressed.
if (Input.GetKey(KeyCode.D)) {
// Add rightwards force to the player.
rb.AddForce(transform.right * force);
//Debug.Log("going right");
}
//
if (Input.GetKey(KeyCode.W) && Input.GetKey(KeyCode.A)) {
// Half player's forward-leftwards force by adding half the force to the opposite direction.
rb.AddForce(-transform.forward * force / 2);
rb.AddForce(transform.right * force / 2);
//Debug.Log("going forward-left");
}
if (Input.GetKey(KeyCode.S) && Input.GetKey(KeyCode.A)) {
// Half player's backwards-leftwards force by adding half the force to the opposite direction.
rb.AddForce(transform.forward * force / 2);
rb.AddForce(transform.right * force / 2);
//Debug.Log("going backwards-left");
}
if (Input.GetKey(KeyCode.S) && Input.GetKey(KeyCode.D)) {
// Half player's backwards-rightwards force by adding half the force to the opposite direction.
rb.AddForce(transform.forward * force / 2);
rb.AddForce(-transform.right * force / 2);
//Debug.Log("going backwards-right");
}
if (Input.GetKey(KeyCode.W) && Input.GetKey(KeyCode.D)) {
// Half player's forward-rightwards force by adding half the force to the opposite direction.
rb.AddForce(-transform.forward * force / 2);
rb.AddForce(-transform.right * force / 2);
//Debug.Log("going forward-right");
}
}
The first 4 “if” checks, is the 4-way movement along the positive/negative X and Z axis.
The following 4 “if” checks is the additional 4-way movement, which just halves the player’s speed so they don’t go faster when moving diagonally, as opposed to not diagonally.
Currently it seems to be working fine, except for the fact that when I constantly move around in a circle motion, I go really fast. So I want to somehow clip the maximum velocity along the X and Z axis. Also I’d like to somehow make it so the player reaches the maximum velocity almost instantly, since I don’t want to have it feel as if the player is dragging along the floor.
I know there’s most likely a much simpler and more efficient way of doing this, but my physics knowledge is very limited.
That might give you more of the effect you want. I would stay away from Transform.Translate as it causes physics problems if you want to do collisions.
Just converted everything to use MovePosition. And I can only move towards one direction at a time, and if I move both keys, the player goes to the direction the second line states. For example if I’m trying to take away half of the force to the forward-rightwards direction, I just go left (since the second line of the “forward-rightwards” if check, states “-position.right”).
OK, oops that didn’t work. I love making character controllers so I’ve made a test scene myself to tinker in
Generally, when you use that many “if” statements it’s a pretty noobish way to code (in some situations it’s appropriate, but I don’t think it is here).
I think you’ll be happier with this:
using UnityEngine;
public class Controller : MonoBehaviour {
public float Force;
public Rigidbody RB; //can be made private
public Vector3 InputVector; //can be made private
public Vector3 VelocityAmount; //can be made private
public Vector2 RBVelocityMaxLimits;
void Start()
{
RB = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
InputVector = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
VelocityAmount = new Vector3(Mathf.Clamp((InputVector.x * Force), -RBVelocityMaxLimits.x, RBVelocityMaxLimits.x) , 0,
Mathf.Clamp((InputVector.z * Force), -RBVelocityMaxLimits.y, RBVelocityMaxLimits.y));
//don't freak out about using Z and Y, RBVelocityMaxLimits is Vector2 so there's no Z for it.
RB.velocity = new Vector3(VelocityAmount.x, 0, VelocityAmount.z);
}
}
Just a quick question. How could I make the player’s start and stop take slightly longer. Currently the movement just feels too robotic, as in if I let go of the key, the player keeps going for a short bit, then instantly stops.
Would I be correct in trying to use Lerp or something similar? And using it to smoothly transition between the current speed, and the maximum/minimum?
For example, instead of doing:
RB.velocity = new Vector3(VelocityAmount.x, RB.velocity.y, VelocityAmount.z);
I could make a new variable called “smoothVelocityAmount”, assign that to be a lerp of the current/maximum, and current/minimum velocity amount.
Then do:
RB.velocity = new Vector3(smoothedVelocityAmount.x, RB.velocity.y, smoothedVelocityAmount.z);
Other than that, it seems to be working exactly how I wanted.
Here’s my solution, but it requires DotTween (I really suck at the standard Vector3.Lerp):
using System.Collections;
using UnityEngine;
using DG.Tweening; //MUST install DotTween: https://assetstore.unity.com/packages/tools/animation/dotween-hotween-v2-27676
//Documentation here: http://dotween.demigiant.com/documentation.php
public class Controller : MonoBehaviour {
public float Force;
public Rigidbody RB; //can be made private
public Vector3 InputVector; //can be made private
public Vector3 VelocityAmount; //can be made private
public Vector3 CurrentVelocity;
public Vector2 RBVelocityMaxLimits;
public float AccelerationTime;
void Start()
{
RB = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
InputVector = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
VelocityAmount = new Vector3(Mathf.Clamp((InputVector.x * Force), -RBVelocityMaxLimits.x, RBVelocityMaxLimits.x) , RB.velocity.y,
Mathf.Clamp((InputVector.z * Force), -RBVelocityMaxLimits.y, RBVelocityMaxLimits.y));
//don't freak out about using Z and Y, RBVelocityMaxLimits is Vector2 so there's no Z for it.
DOTween.To(TweenX, CurrentVelocity.x, VelocityAmount.x, AccelerationTime);
DOTween.To(TweenZ, CurrentVelocity.z, VelocityAmount.z, AccelerationTime);
RB.velocity = CurrentVelocity;
}
void TweenX(float InputFromTween)
{
CurrentVelocity = new Vector3(InputFromTween, RB.velocity.y, CurrentVelocity.z);
}
void TweenZ(float InputFromTween)
{
CurrentVelocity = new Vector3(CurrentVelocity.x, RB.velocity.y, InputFromTween);
}
}
It really takes only 30 seconds to install and set up and it’s pretty awesome, so I highly recommend you try it out.
Also, be careful with setting AccelerationTime too high as then it feels just like using AddForce.
EDIT/BUGFIX: Added RB.velocity.y for use of gravity/Y axis; previous code was bugged. If you don’t want gravity, change RB.velocity.y to 0.
Not yet. For now I’ll stick with the default way. From what I read about DOTween, it does seem much easier to handle, but I don’t want to plunge my head into something I’m not familiar with, very early on. Maybe once I’ve got a fair amount of progress done, I’ll have a go at converting it to use DOTween, and see how that goes.
And I did manage to make the smoothing effect for movement.