What is the Unity Input System equivalent of GetAxis? - c#

I'm trying to get input from my character using the Input System package when I press the left/right arrows keys, however when I read the value of the input it currently snaps between 0, -1, and 1. This is similar to the Input Manager's GetAxisRaw, however, what I want is to be able to do something more like GetAxis where it's not instantly 1, but goes through a couple float numbers between 0 and 1 up until 1. Same when it goes back down as well.
Preferably, I'd like to know if the current Input System has a way to manage that on its own, rather than needing to adjust the input myself through any mathematical formulas over time.
What I'm trying to achieve is smooth movement left and right, where for a split second or so the character picks up speed before they reach the max, and where letting go they also still slide for a split second before stopping. Since what I'm doing is setting the velocity equal to the multiplied value of the speed (rigidbody.velocity = new Vector2(//input value * movement speed, rigidbody.velocity.y)).
Of course I understand that if there is no way to create a sort of "ramping" effect to the snapping numbers, I could make it an acceleration speed instead (rigidbody.velocity += new Vector2(blah blah)). The issue being I then need to insert extra code to set a cap for the speed, and to slow down the velocity when I let go so my character doesn't slide like they're on ice.

No, there doesn't seem to be an equivalent to GetAxis for the Unity Input System package, however I did attempt to emulate it. The Unity Docs note that Pasted from Unity Docs "the Horizontal and Vertical ranges change from 0 to +1 or -1 with increase/decrease in 0.05f steps."
So I used Mathf.Lerp(a, b, t) (https://docs.unity3d.com/ScriptReference/Mathf.Lerp.html) where my starting value (a) was 0, and my ending value (b) was the input (whether it be 1 or -1). My interpolation value (t) starts at 0 and I would add 0.05 to it each frame. This was a bit too slow, so I multiplied it by an int that I called inputSensitivity.
For the visual learners:
[SerializeField] private float moveSpeed = 5f;
private InputMaster inputActions;
private Rigidbody2D rigidbody2D;
static float movementIncrementOverTime = 0;
[SerializeField] int inputSensitivity = 100;
private void Awake()
{
inputActions = new InputMaster();
}
private void OnEnable()
{
inputActions.Enable();
}
private void OnDisable()
{
inputActions.Disable();
}
private void Start()
{
rigidbody2D = GetComponent<Rigidbody2D>();
}
void Update()
{
//Read the movement value
float moveInput = Running();
inputActions.Player.Movement.canceled += Movement_canceled;
//Move the player
rigidbody2D.velocity = new Vector2(moveInput * moveSpeed, rigidbody2D.velocity.y);
}
private float Running()
{
float moveInput = inputActions.Player.Movement.ReadValue<float>();
switch(moveInput)
{
case 1:
moveInput = Mathf.Lerp(0, moveInput, movementIncrementOverTime);
movementIncrementOverTime += (0.05f * inputSensitivity) * Time.deltaTime;
break;
case -1:
moveInput = Mathf.Lerp(0, moveInput, movementIncrementOverTime);
movementIncrementOverTime += (0.05f * inputSensitivity) * Time.deltaTime;
break;
default:
break;
}
return moveInput;
}
private void Movement_canceled(InputAction.CallbackContext obj)
{
movementIncrementOverTime = 0;
}
The Running() function is the key here, it makes it so that instead of snapping to 1 and -1, it gradually moves up to those values. Then, in the Update() function the line of code inputActions.Player.Movement.canceled += Movement_canceled; checks for when I release the key and resets my static float movementIncrementOverTime to 0 so I can once again ramp up speed when I decided to move in the opposite direction, or if I stand still and start walking again.
The only issue, which I have yet to fix, is that I haven't figured out how to get the character to slide slightly when you let go of the key, instead of instantly stopping when you let go.

Related

Why isn't my cube's velocity changing with rb.velocity = new Vector3(sidewaysForce, 0, 0) in Unity?

I am new to Unity so forgive me if I just did a stupid mistake. I am currently watching a tutorial from Brackeys, but I wanted to challenge myself by figuring out the movement myself. I got the moving forward correct, but I couldn't do the sideways movement. Since I've been spending a long time on the sideways movement, I just decided to watch the tutorial, but even when I used their code, it still didn't work. Can someone tell me why this isn't working? (FYI, in the code below, I did set all the public variables to some type of value).
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Sides : MonoBehaviour
{
public KeyCode left;
public KeyCode right;
public float sidewaysForce;
Rigidbody rb;
Vector3 v3;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void FixedUpdate()
{
if (Input.GetKey(left))
{
Debug.Log("Left");
rb.velocity = new Vector3(-sidewaysForce, 0, 0);
}
if (Input.GetKey(right))
{
Debug.Log("Right");
rb.velocity = new Vector3(sidewaysForce, 0, 0);
}
}
}
try left and right between quotation marks:
if (Input.GetKey("left"))
{
Debug.Log("Left");
rb.velocity = new Vector3(-sidewaysForce, 0, 0);
}
if (Input.GetKey("right"))
{
Debug.Log("Right");
rb.velocity = new Vector3(sidewaysForce, 0, 0);
}
You can check how to get the different keys in:
InputManager is in: Edit -> ProjectSettings -> Input
Other option is to use KeyCodes, which I prefer instead of the string argument for the intellisense benefits:
Update()
{
if ( Input.GetKey(KeyCode.UpArrow) )
//move up
if ( Input.GetKey(KeyCode.DownArrow) )
//move down
if ( Input.GetKey(KeyCode.RightArrow) )
//move right
if ( Input.GetKey(KeyCode.LeftArrow) )
//move left
}
If you're not getting the logs you added in the console, then you may have forgotten either:
To set the values of variables left and right in the inspector.
To add the Sides component to your GameObject.
I will assume your KeyCodes and the float are configured in the Inspector.
Either way you shouldn't do it like that!
You are completely overwriting the forward/backward movement and also the up/down movement with a hard 0 => you won't have any gravity and might break the forward movement - depending on which script is executed last.
You didn't share the forwards movement code so I can only guess but I suspect that there you do the very same thing like e.g.
rb.velocity = new Vector3(0, 0, someValue);
and therefore erase any sidewards movement from this script ;)
I would
a) make sure to have all movement input related stuff in a single script. Everything else just makes it unnecessarily hard to maintain and debug
b) make sure that your inputs don't eliminate each other
So something like e.g.
public class Movement : MonoBehaviour
{
[Header("References")]
[SerializeField] private Rigidbody _rigidbody;
[Header("Config")]
[SerializeField] private KeyCode _forward = KeyCode.W;
[SerializeField] private KeyCode _backward = keyCode.S;
[SerializeField] private KeyCode _left = KeyCode.A;
[SerializeField] private KeyCode _right = KeyCode.D;
public float speed;
private void Awake()
{
if(!_rigidbody) _rigidbody = GetComponent<Rigidbody>();
}
private void FixedUpate()
{
// by default input is 0,0,0
var input = Vector3.zero;
// user input for forward
if(Input.GetKey(_forward)) input.z = 1;
else if(Input.GetKey(_backward)) input.z = -1;
// user input for sidewards
if(Input.GetKey(_left)) input.x = -1;
else if(Input.GetKey(_right)) input.x = 1;
// make sure both combined are not bigger then 1 (avoid faster diagonal movement)
input = Vector3.ClampMagnitude(input, 1);
// apply speed multiplier
var velocity = input * speed;
// preserve the Y velocity
velocity.y = _rigidbody.velocity.y;
// finally assign back to rigidbody
_rigidbody.velocity = velocity;
}
}
In general though way more flexible and without having to configure and check the KeyCodes manually you could rather use Input.GetAxis and do e.g.
public class Movement : MonoBehaviour
{
[Header("References")]
[SerializeField] private Rigidbody _rigidbody;
[Header("Config")]
public float speed;
private void Awake()
{
if(!_rigidbody) _rigidbody = GetComponent<Rigidbody>();
}
private void FixedUpate()
{
var input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
// make sure both combined are not bigger then 1 (avoid faster diagonal movement)
input = Vector3.ClampMagnitude(input, 1);
// apply speed multiplier
var velocity = input * speed;
// preserve the Y velocity for the gravity
velocity.y = _rigidbody.velocity.y;
// finally assign back to the rigidbody
_rigidbody.velocity = velocity;
}
}
You can configure the mappings in Edit → Project Settings → Input Manager but by default the
Horizontal already refers to both A/D as well as LeftArrow/RightArrow
Vertical already refers to both W/S as well as UpArrow/DownArrow
and additionally GetAxis slightly interpolates the input so it doesn't immediately jump from 0 to 1 and back but rather increases and decreases smoothly over some frames.
If the latter bothers you you can also use GetAxisRaw which does not do the smoothing.
I just realized that I did in fact made a stupid mistake. In my other script that makes the player move forward, it sets the velocity to (0, 0, 100) in the FixedUpdate() which meant that the x axis doesn't change. Sorry for wasting your time.

How can I move a character from one point to a specific point at any desired speed?

I used from this code but it does not do it:
public Transform[] points;
public float speed;
private void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "door1")
{
transform.position = Vector3.Lerp(transform.position, points[1].position, speed * Time.deltaTime);
}
}
That is, I want the pig to go to a higher point on the ground at a desired speed when it hits the trigger (see the photo attached to see it)
Two problems here:
you are calling that line exactly once when you enter the collider so the movement is applied for I single frame!
Lerp interpolates linear between two positions using a factor between 0 and 1. You every time use the current position as start point so what would happen if if you called this continuously is approximating the position getting slower and slower every frame which is not what you describe. You want to move with a constant speed.
You most likely would rather use a Coroutine and MoveTowards for that
private void OnTriggerEnter2D(Collider2D other)
{
// Better use CompareTag here
if (other.CompareTag("door1"))
{
// Start a routine for the continuous movement
StartCoroutine(MoveTo(points[1].position, speed);
}
}
private IEnumerator MoveTo(Vector3 targetPosition, float linearSpeed)
{
// This uses an approximation of 0.00001 for equality
while(transform.position != targetPosition)
{
// with a constant speed of linearSpeed Units / second
// move towards the target position without overshooting
transform.position = Vector3.MoveTowards(transform.position, targetPosition, linearSpeed * Time.deltaTime);
// Tell Unity to "pause" the routine here, render this frame
// and continue from here in the next frame
yield return null;
}
// to be sure to end up with exact values set the target position fix when done
transform.position = targetPosition;
}
Alternatively a bit more complex looking but more powerful would be to rather calculate the required time depending on the speed but still adding some smoothing like e.g.
private void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("door1"))
{
StartCoroutine (MoveTo(points[1].position, speed);
}
}
private IEnumerator MoveTo(Vector3 targetPosition, float averageSpeed)
{
// store the initial position
var from = transform.position;
// Get the expected duration depending on distance and speed
var duration = Vector3.Distance(from, targetPosition) / averageSpeed;
// This is increased over time
var timePassed = 0;
while(timePassed < duration)
{
// This linear grows from 0 to 1
var factor = timePassed / duration;
// Adds some ease-in and ease-out at beginning and end of the movement
factor = Mathf.SmoothStep(0, 1, factor);
// linear interpolate on the smoothed factor
transform.position = Vector3.Lerp(from, targetPosition, factor);
// increase by time passed since last frame
timePassed += Time.deltaTime;
// Tell Unity to "pause" the routine here, render this frame
// and continue from here in the next frame
yield return null;
}
// to be sure to end up with exact values set the target position fix when done
transform.position = targetPosition;
}
OnTriggerEnter2D is only called once on every collider enter. Given you are only moving it by speed * Time.deltaTime with Time.deltaTime being in the order of 0.008 - 0.100 it may only move slightly.
Depending on what you want, are you sure you don't want to completely move the object or alternatively set a flag that starts moving it in the update() method?

Accelerate/decelerate towards moving target and hitting it

I'm trying to create a smooth camera movement in 2D. The target I want to hit can potentially move a large distance in a single frame, and is not like a character moving smoothly from A to B.
I'm aware of possible solutions like using Vector2.Lerp(), but that approach only slows down nicely but speeds up abruptly.
_position = Vector2.Lerp(_position, target, 0.5f * Time.deltaTime);
I've tried implementing the "arrive" steering behaviour, but cannot make it work nicely together with acceleration - especially when the target is close to the current position.
I managed to make it work pretty well in one axis, but that approach didn't work when repeated in a second axis.
var decelerateRadius = GetDistanceFromAcceleration(acceleration, Mathf.Abs(_velocity));
var direction = target - _position;
var distance = Mathf.Abs(direction);
var a = acceleration * Time.deltaTime;
if (distance > 0.0005f)
{
if (distance < decelerateRadius.x)
{
_velocity *= distance / decelerateRadius.x;
}
else
{
_velocity += direction.x >= 0.0f ? a : -a;
}
}
else
{
_velocity = 0.0f;
}
// move tracker
_position += _velocity * Time.deltaTime;
And my method for calculating the distance based on acceleration:
private Vector2 GetDistanceFromAcceleration(float a, float vx, float vy)
{
// derived from: a = (vf^2 - vi^2) / (2 * d)
return new Vector2(vx * vx / (2.0f * a), vy * vy / (2.0f * a));
}
My last attempt was making a rolling average of the target, but it suffered the same issue as lerping.
To summarize the requirements:
Must accelerate
Must decelerate and stop at target
Must not "orbit" or in other ways swing around the target, before stopping
Target must be able to move
May be limited by a maximum velocity
Any tips, pointers og solutions on how to achieve this?
I've also asked the question over at game dev
https://gamedev.stackexchange.com/questions/170056/accelerate-decelerate-towards-moving-target-and-hitting-it
The problem with your lerp is also that you actually never reach the target position you just get very very close and small.
I thought about something like this
as long as you are already at the targets position don't move. Enable orbit mode
while not within a certain targetRadius around the target position accelerate from to maxVelocity
if getting within a certain targetRadius around the target position decelerate depending on the distance / radius will be a value between 1 and 0
To get the distance there is already Vector2.Distance you could/should use.
For the movement I would recommend Vector2.MoveTowards which also avoids overshooting of the target.
something like
public class SmoothFollow2D : MonoBehaviour
{
[Header("Components")]
[Tooltip("The target this will follow")]
[SerializeField] private Transform target;
[Header("Settings")]
[Tooltip("This may never be 0!")]
[SerializeField] private float minVelocity = 0.1f;
[SerializeField] private float maxVelocity = 5.0f;
[Tooltip("The deceleration radius around the target.\nThis may never be 0!")]
[SerializeField] private float targetRadius = 1.0f;
[Tooltip("How much speed shall be added per second?\n" +
"If this is equal to MaxVelocity you know that it will take 1 second to accelerate from 0 to MaxVelocity.\n" +
"Should not be 0")]
[SerializeField] private float accelerationFactor = 3.0f;
private float _currentVelocity;
private float _lastVelocityOutsideTargetRadius;
private bool _enableOrbit;
public bool EnableOrbit
{
get { return _enableOrbit; }
private set
{
// if already the same value do nothing
if (_enableOrbit == value) return;
_enableOrbit = value;
// Whatever shall be done if orbit mode is enabled or disabled
}
}
private void Update()
{
if (target == null) return;
var distanceToTarget = Vector2.Distance(transform.position, target.position);
// This is the threshold Unity uses for equality of vectors (==)
// you might want to change it to a bigger value in order to
// make the Camera more stable e.g.
if (distanceToTarget <= 0.00001f)
{
EnableOrbit = true;
// do nothing else
return;
}
EnableOrbit = false;
if (distanceToTarget <= targetRadius)
{
// decelerate
// This will make it slower
// the closer we get to the target position
_currentVelocity = _lastVelocityOutsideTargetRadius * (distanceToTarget / targetRadius);
// as long as it is not in the final position
// it should always keep a minimum speed
_currentVelocity = Mathf.Max(_currentVelocity, minVelocity);
}
else
{
// accelerate
_currentVelocity += accelerationFactor * Time.deltaTime;
// Limit to MaxVelocity
_currentVelocity = Mathf.Min(_currentVelocity, maxVelocity);
_lastVelocityOutsideTargetRadius = _currentVelocity;
}
transform.position = Vector2.MoveTowards(transform.position, target.position, _currentVelocity * Time.deltaTime);
}
// Just for visualizing the decelerate radius around the target
private void OnDrawGizmos()
{
if (target) Gizmos.DrawWireSphere(target.position, targetRadius);
}
}
The MinVelocity is actually necessary for the edge case when the target is moved not further than TargetRadius and lastVelocityOutsideTargetRadius si still 0. In that case no acceleration takes place so lastVelocityOutsideTargetRadius is never updated.
With the values you have to play a bit ofcourse ;)
It might not be perfect yet but I hope it is a good start point to develop that further (only looks laggy due to 15 FPS for the Gif ;) )

Change movement speed in Vector.Lerp Unity 5.6

I am using Vector3.Lerp in unity game to simply move a gameobject from one position to other smoothly. Below is my code:
public class playermovement : MonoBehaviour {
public float timeTakenDuringLerp = 2f;
private bool _isLerping;
private Vector3 _startPosition;
private Vector3 _endPosition;
private float _timeStartedLerping;
void StartLerping(int i)
{
_isLerping = true;
_timeStartedLerping = Time.time ; // adding 1 to time.time here makes it wait for 1 sec before starting
//We set the start position to the current position, and the finish to 10 spaces in the 'forward' direction
_startPosition = transform.position;
_endPosition = new Vector3(transform.position.x + i,transform.position.y,transform.position.z);
}
void Update()
{
//When the user hits the spacebar, we start lerping
if(Input.GetKey(KeyCode.Space))
{
int i = 65;
StartLerping(i);
}
}
//We do the actual interpolation in FixedUpdate(), since we're dealing with a rigidbody
void FixedUpdate()
{
if(_isLerping)
{
//We want percentage = 0.0 when Time.time = _timeStartedLerping
//and percentage = 1.0 when Time.time = _timeStartedLerping + timeTakenDuringLerp
//In other words, we want to know what percentage of "timeTakenDuringLerp" the value
//"Time.time - _timeStartedLerping" is.
float timeSinceStarted = Time.time - _timeStartedLerping;
float percentageComplete = timeSinceStarted / timeTakenDuringLerp;
//Perform the actual lerping. Notice that the first two parameters will always be the same
//throughout a single lerp-processs (ie. they won't change until we hit the space-bar again
//to start another lerp)
transform.position = Vector3.Lerp (_startPosition, _endPosition, percentageComplete);
//When we've completed the lerp, we set _isLerping to false
if(percentageComplete >= 1.0f)
{
_isLerping = false;
}
}
}
}
The code works fine and the gameobject moves smoothly between two points. But it takes about 1 sec to reach destination. I want to make it move faster. I have tried decreasing the value of float timeTakenDuringLerp but the speed isn't affected. I have followed this tutorial and the explanation there also says to change timeTakenDuringLerp variable in order to change speed but its not working here.
Any Suggestions please?
H℮y, thanks for linking to my blog!
Decreasing timeTakenDuringLerp is the correct solution. That reduces the time it takes for the object to move from start to finish, which is another way of saying "it increases the speed".
If there is a specific speed you want the object to move at, you'll need to make timeTakenDuringLerp a variable rather than a constant, and set it to distance/speed. Or better yet, don't use Lerp at all, and instead set the object's velocity and let Unity's physics engine take care of it.
Multiplying percentageComplete by a constant, as suggested by #Thalthanas, is incorrect. That causes the lerping updates to continue occurring after the lerping has completed. It also makes the code hard to understand because timeTakenDuringLerp is no longer the time taken during the lerp.
I've double-checked with my code and it does indeed work, so the problem you are experiencing must be elsewhere. Or maybe you accidentally increased the time, which would decrease the speed?
The solution is to
multiply percentageComplete value with a speed value like,
transform.position = Vector3.Lerp (_startPosition, _endPosition, percentageComplete*speed);
So when you increase speed, it will go faster.

When scaling back to "original" scale after shrinking, it defaults to 1,1,1, which isn't what I want

The game object I'm scaling is simple. It shows the thrust for the ship being used (this last for 2 seconds as the bar decreases). Then for 3 seconds, the object scales back up to almost exactly where it was, but it needs to be exact or over time the object will not look correct after several uses (the scaling would get exponentially worse, and we know this is no good). I've researched this for the past hour or 2 with no avail in anything I try. This is the closest thing I've been able to mash up.
Overall problem: It needs to be EXACTLY 5 every time it scales back. I realize if I take off the last else if statement it works (poorly), but no matter what number I put in for the X it doesn't always scale to the exact same number, which is a problem, and why I need to return it's original scale.
tl;dr My object's X value scales from 5 to near 0, when trying to return it to its original scale (of 5) it scales up to near 5 then goes to 1,1,1.
using UnityEngine;
using System.Collections;
public class ShipPlayerController : MonoBehaviour {
[Header ("Camera(s)")]
public Camera CameraObject;
[Header ("Float(s)")]
public float fireRate = 0.3f;
public float fireTimer = 0.0f;
public float moveSpeedX = 12.0f;
public float moveSpeedY = 8.0f;
//counts from 0 to 5, so I can use the thrust again (starts at 5 so they can use it initially, this resets to 0 every time they press T and its been over 5 seconds)
public float thrusterTimer = 5.0f;
//the rate at which I can thrust, the lower the number the more often you can use it
public float thrusterRate = 5.0f;
//how long the player is actually thrusting
public float thrusterActive = 2.0f;
private Vector3 movement = Vector3.zero;
//supposed to be the original scale i've made for the game object???
private Vector3 originalScale; //(also have tried adding = Vector3.zero;)
[Header ("Script(s)")]
public Projectile bullet;
public HUD hudScript;
// Update is called once per frame
void Update () {
updatePosition ();
updateFiring ();
updateThrust ();
//calling the method when I push "t" for thrust
updateThrusterBar ();
}
//Skipping a bunch of unneeded code
........................................
//Gets called once I press "T"
public void updateThrusterBar () {
//Supposed to be the original scale???
originalScale = transform.lossyScale;
//When I press "T" the bar shrinks showing the player is using their thrust
if (thrusterActive > thrusterTimer) {
hudScript.thrustBar.transform.localScale += new Vector3 (-0.04187f, 0, 0);
//Fills the thrust back up so they can see when its full
} else if (thrusterTimer < thrusterRate) {
hudScript.thrustBar.transform.localScale += new Vector3 (0.029f, 0, 0);
//(Doesn't work): Logically, if the X scale isn't 5, then go back to your original scale i've made for you, which in the game is (5,0.3,1)
//What happens: defaults to (1,1,1) in the game, which is a square...
} else if (hudScript.thrustBar.transform.localScale.x != 5.0f) {
hudScript.thrustBar.transform.localScale = originalScale;
}
}
}

Categories