When I release the character control button, character itself continues to move for about half a second. I want the character to stop right after I release the control button. I’ve tried diffirent methods: AddForce and velocity, but it’s all in vain.
Also, I tried to adjust the mass and drag momentum in Inspector of the character, but it didn’t help.
public class CapsuleMovement : MonoBehaviour
{
Rigidbody rb;
Vector3 playerMovement;
[SerializeField] float speed = 50;
void Start()
{
rb = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
ProccessCapsuleMovement();
}
void ProccessCapsuleMovement ()
{
playerMovement = new Vector3 (Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
playerMovement.Normalize();
rb.velocity = playerMovement * speed * Time.deltaTime;
}
}
Don't normalize! If the Input magnitude is actually smaller than 1 you still normalize it always to a magnitude 1!
Rather use Vector3.ClampMagnitude which only limits the magnitude in the upper bound but allows it to be smaller
The other point might be that GetAxis is actually "smoothed" and not applied immediately! After releasing the button it is actually decreased over time. So since you normalized the vector it keeps having a magnitude of 1 for a while after releasing the buttons.
You might rather want to use GetAxisRaw for this.
Then when assigning a velocity you do not want to multiply by Time.deltaTime! This only is needed where you want to convert a value from a fixed value per frame into a frame-rate-independent value per second. A velocity already is a vector per second so remove the * Time.deltaTime.
so something like e.g.
playerMovement = Vector3.ClampMagnitude(new Vector3 (Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical")), 1f);
rb.velocity = playerMovement * speed;
Related
So I am making a small game in unity where you have to shoot the enemy. However, when I made the script for the bullet and enemy, it half worked and half didn't. Sometimes, the bullet would hit the enemy and destroy the enemy, however, sometimes, it would take multiple shots for it to work. But when I turn the speed of the bullet to 1 (the speed of the bullet was 500), the bullet always destroys the enemy. So this leads me to think that this has something to do with the speed of the bullet. Here is my script
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
private void OnTriggerEnter(Collider other)
{
Destroy(other.gameObject);
Destroy(gameObject);
Debug.Log("e");
}
For the movement of the bullet, I just used transform.Translate(Vector3.up * Time.deltaTime * speed). How can I fix this?
The problem is not that Destroy do not work with a certain speed, the problem is that with certain speed you are not triggering the "OnTriggerEnter".
This fenomenon is called "tunneling" it happens when the object goes too fast.
That provokes that in one frame the projectile is on one side of the collider, and in the next frame is on the other side of the collider, giving the sensation like a teleport, so that's the why it do not collide, cause in any frame the engine has detected the collide.
If you're having troubles with high speed stuff, try to set your rigidbody (the one that is moving) to Interpolate, or use raycasts to fake bigger projectile colliders.
In addition to Lotan's answer
I just used transform.Translate(Vector3.up * Time.deltaTime * speed)
whenever using physics you do NOT want to set anything via Transform as this bypasses the physics engine and thereby breaks collision detection etc as in your case.
You rather want to through the Rigidbody component and e.g. use Rigidbody.MovePosition
Rigidbody.MovePosition moves a Rigidbody and complies with the interpolation settings
like
private Rigidbody _rigidbody;
private void Awake()
{
_rigidbody = GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
_rigidbody.MovePosition(_rigidbody.position + _rigidbody.rotation * Vector3.up * Time.deltaTime * speed);
}
or why not simply set it only once
private void Start()
{
GetComponent<Rigidbody>().velocity = transform.up * speed;
}
Additionally you want to set the interpolation to dynamic.
A tricky way : bullet form A point to B point by one frame , so you can fake a point on a to b like
fake point: A+B/2
Point a ;
Point b ;
Update()
{
Point ab = (a+b)/2;
bullet.point = ab;
//check collider some .
bullet.point = b; // set it back.
a = b;
}
Not a good solution . but it have double hit rate.
I should say that I have very very little C# and Unity experience. I got most of this code from a Brackeys tutorial.
So, this code originally just made the player move at a set speed. So, I added a new public float set to a higher speed and set an if statement to change the value of the float that controls the speed (the float is literally named speed). I added a Debug.Log to display if the if statement is triggered/what the value of the speed float is. This log entry always displays once the Lshift key is pressed (default control axes have Fire3 set to Lshift inside of the unity project settings). I think my issue is that my speed is somehow decreasing before the player even moves. Additionally, this is probably the worst way to implement a sprint function, so if anyone has any better ideas, please let me know!
Clarification: The player can move. My intended effect is for the sprint key (Lshift) to be held down to achieve the sprinting effect. I did intend for the speed to reset every frame, so that as soon as the Lshift key is let go, the speed of the player will drop down to the normal state (12f). Currently I have a Debug.Log in place that displays the value of the "speed" float whenever the Lshift key is pressed. This always returns 18 in the console, which is the intended effect. However, even though this number is displayed in the console, there is no actual movement speed change in the player.
public CharacterController controller;
public float speed = 16f;
public float gravity = -9.81f;
public float jumpHeight = 3f;
public float sprintspeed = 18f;
public Transform groundCheck; //this is where the groundcheck empty object goes
public float groundDistance = 0.4f;
public LayerMask groundMask; //make sure to add a layer into the unity project called "ground"
Vector3 velocity;
bool isGrounded;
void Update()
{
isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask); //this just creates a sphere and checks it's collision state with any other objects
if(isGrounded && velocity.y < 0)
{
velocity.y = -2f; //sets velocity to a low static number.
}
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
speed = 12f;
if(Input.GetButtonDown("Fire3")) //theoretically increases the speed float only when left control is pressed down
{
speed = sprintspeed;
Debug.Log(speed);
}
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * speed * Time.deltaTime);
if(Input.GetButtonDown("Jump") && isGrounded)//jump function below
{
velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
}
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
}
Thanks in advance!
I would look to see what values are in the inspector since both speed and sprintspeed are public. There is a chance that sprintspeed might be set to 12f and therefore not change the speed. Also, keep in mind that with your implementation you can't change the speed variable from the inspector since you're updating it back to 12f every frame.
If that doesn't help with anything, perhaps some clarification is needed. Is your player moving at all? Is it just the sprinting that isn't working? What value does your Debug.Log give you? What are the values in the inspector?
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public float speed;
public float jump;
private Rigidbody2D rb;
private void Start()
{
rb = GetComponent<Rigidbody2D>();
}
private void Update()
{
rb.position += new Vector2(Input.GetAxis("Horizontal"), 0) * Time.deltaTime * speed;
if(Mathf.Abs(rb.velocity.y) < 0.001f && Input.GetKeyDown(KeyCode.W))
{
rb.AddForce(new Vector2(0, jump), ForceMode2D.Impulse);
}
}
So I have this code for my player movement. I am wondering how can I reduce my character from sliding that much. I don't want to stop instantly after I release the key.
You could add counter-movement to make the movement to feel more responsive, or you could change the friction by adding a physics material. Counter-movement makes the player stop by adding a force opposite to the wanted direction of the movement. It will stop the player from sliding too much. Another approach is to add a physics material and up the friction a bit. This will make the player stop faster. I hope you find this helpful!
Inside of the Input Manager, Edit->Project Settings->Input Manager, there is a property called gravity.
Gravity: Speed in units per second that the axis falls toward neutral when no input is present.
Decreasing this value will cause the input to fall quicker, resulting in less/no sliding.
You can debug your input value to confirm this. You should notice a ramp up from 0 to 1/-1 when you first hold the horizontal input. Once you let go of the input, you should see the value fall back down to 0.
var inputHorz = Input.GetAxis("Horizontal");
Debug.Log(inputHorz);
Lower the value until it feel correct. This can be changed while you are playing the game, but you will need to paste that value back in after pressing stop.
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.
I was looking for a way to set the Quaternions (x, y, z, w) through the inspector window. We get all these variables when we click on "Debug" mode in Unity. Through Unity docs, I got to know that these values are between 0-1. So how do we set for angles such as 90,-90,180,-180,270,.... MAIN THING here is that I want to set the target rotations in the script of this game object so that the gameObject moves from initial rotation to target rotation.
For example in "Normal" window, if I set the target rotation of x as 180 (shown as -5.008956e-06 in the inspector window), the gameObject moves from 0 to -180, instead of +180. That is the reason I moved to "Debug" window thinking it helps here to set it. But the values here range between 0-1, so does anyone have an idea of how to calculate this?
Moreover, for rotation I am using this one line:
transform.localRotation = Quaternion.Slerp(transform.localRotation, targetRotation, Time.deltaTime * RotationSpeed);
It sounds like you want to be able to adjust it via a Vector3 just how Unity does it in the Transform Inspector. You could do something like
public Vector3 targetRotationV3;
private Quaternion targetRotation;
private void Start()
{
targetRotation = Quaternion.Euler(targetRotationV3);
}
or if you need to be more flexible
private void Update()
{
targetRotation = Quaternion.Euler(targetRotationV3);
...
}
Then for my comment what I mean is that Slerp interpolates a value between the first and the second argument using the factor between 0 and 1.
Since you every frame use a new value as start point, namely the current rotation, this will get slower and slower to the end and depending on your given speed never reach the target rotation.
It makes little sense to use Time.deltaTime here which just divides your speed by about 60 (for 60 FPS). Usually you rather want a constant interpolation factor between 0 and 1. If the frame-rate goes up it might even rotate back since in this case the Time.deltaTime would get smaller!
So you either rather want a constant interpolation factor
[Range(0f,1f)] private float slerpFactor = 0.5f;
private void Update()
{
targetRotation = Quaternion.Euler(targetRotationV3);
transform.localRotation = Quaternion.Slerp(transform.localRotation, targetRotation, slerpFactor);
}
or if you want to rotate with a constant speed instead use Quaternion.RotateTowards
private void Update()
{
targetRotation = Quaternion.Euler(targetRotationV3);
transform.localRotation = Quaternion.RotateTowards(transform.localRotation, targetRotation, Time.deltaTime * RotationSpeed);
}
where your RotationSpeed is now in ° / second
As I said in my comment, don't set Quaternion directly ever, unless you are really confident in your understanding of them, as pointed out in the unity docs (emphasis mine).
They are based on complex numbers and are not easy to understand intuitively. You almost never access or modify individual Quaternion components (x,y,z,w); most often you would just take existing rotations (e.g. from the Transform) and use them to construct new rotations (e.g. to smoothly interpolate between two rotations). The Quaternion functions that you use 99% of the time are: Quaternion.LookRotation, Quaternion.Angle, Quaternion.Euler, Quaternion.Slerp, Quaternion.FromToRotation, and Quaternion.identity. (The other functions are only for exotic uses.)
Rather what you want to do is set the initial and target rotations as Vector3 (Eulerangles) from the inspector and use the build in Quaternion.Euler(); method to let Unity figure out the transformation from Eulerangles to Quaternions.
This would look something like this (Note that I am doing this in an update for the example, and using a float time that I update from the inspector to change the rotation, this is just done for ease of example and not the best way to do implement the t parameter of Quaternion.Slerp):
public Vector3 initialrotation;
public Vector3 targetRotation;
public float time;
void Update()
{
// Let Unity figure out what the appropriate Quaternions are for the given Eulerangles
// Note that this can better be done in Start if initialRotation and targetRotation never change. Just put it here for simplicity
var initialQuaternion = Quaternion.Euler(initialrotation);
var targetQuaternion = Quaternion.Euler(targetRotation);
var slerp = Quaternion.Slerp(initialQuaternion, targetQuaternion, time);
transform.rotation = slerp;
}