I have a game in a 3D space, and rigidbody comp. to a player. I have implemented a jump mechanic that is not working at intended, with this code:
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public bool Travel;
public Rigidbody rb;
public Transform tf;
public bool Grounded;
public float speed;
int OnRail = 2;
void Update ()
{
float speed = 1f * Time.deltaTime;
if (Input.GetKeyDown("d") && OnRail < 3)
{
tf.Translate(5.4f, 0f, 0f);
OnRail++;
}
else if (Input.GetKeyDown("a") && OnRail > 1)
{
tf.Translate(-5.4f, 0f, 0f);
OnRail--;
}
}
void FixedUpdate ()
{
if (Travel)
{
rb.velocity = new Vector3(0, 0, speed * Time.deltaTime);
}
if (Input.GetKeyDown("space") && Grounded)
{
Grounded = false;
rb.AddForce(new Vector3(0, 500000f * Time.deltaTime, 0));
}
if ((tf.position.y == 2f || tf.position.y == 10f) && rb.velocity.y == 0 && !Grounded)
{
Grounded = true;
}
}
}
The player is supposed to jump into the air, but only jumps almost no distance. The 2 and 10 Y positions are preset floors (I really don't need these 2 Y-coordinates. unless someone has a substitution for floor detection).
This is because GetKeyDown only occurs for one frame, meaning you are only applying a force upwards for that single frame. Additionally, Time.deltaTime is the amount of time it took to complete the last frame (a very small value), meaning you are applying a very small force upward. If you only want a single force applied on spacebar, there is no need to use Time.deltaTime here. Just do:
rb.AddForce(Vector3.up * 100f); // or some other reasonable multipliier
Hi for those that land on this looking for more information on their rigidbodies not behaving the way that they expected, I extracted bit of relevant information from Unity ForceMode's. Happy Developing.
//Here, switching modes depend on button presses in the Game mode
switch (m_ModeSwitching)
{
//This is the starting mode which resets the GameObject
case ModeSwitching.Start:
//This resets the GameObject and Rigidbody to their starting positions
transform.position = m_StartPos;
m_Rigidbody.transform.position = m_StartForce;
//This resets the velocity of the Rigidbody
m_Rigidbody.velocity = new Vector3(0f, 0f, 0f);
break;
//These are the modes ForceMode can force on a Rigidbody
//This is Acceleration mode
case ModeSwitching.Acceleration:
//The function converts the text fields into floats and updates the Rigidbody’s force
MakeCustomForce();
//Use Acceleration as the force on the Rigidbody
m_Rigidbody.AddForce(m_NewForce, ForceMode.Acceleration);
break;
//This is Force Mode, using a continuous force on the Rigidbody considering its mass
case ModeSwitching.Force:
//Converts the text fields into floats and updates the force applied to the Rigidbody
MakeCustomForce();
//Use Force as the force on GameObject’s Rigidbody
m_Rigidbody.AddForce(m_NewForce, ForceMode.Force);
break;
//This is Impulse Mode, which involves using the Rigidbody’s mass to apply an instant impulse force.
case ModeSwitching.Impulse:
//The function converts the text fields into floats and updates the force applied to the Rigidbody
MakeCustomForce();
//Use Impulse as the force on GameObject
m_Rigidbody.AddForce(m_NewForce, ForceMode.Impulse);
break;
//This is VelocityChange which involves ignoring the mass of the GameObject and impacting it with a sudden speed change in a direction
case ModeSwitching.VelocityChange:
//Converts the text fields into floats and updates the force applied to the Rigidbody
MakeCustomForce();
//Make a Velocity change on the Rigidbody
m_Rigidbody.AddForce(m_NewForce, ForceMode.VelocityChange);
break;
}
Related
My movement is working fine without Time.deltaTime but when I add it to my player movement I can move left to right fine but I fall slowly and I cant jump
using Newtonsoft.Json.Bson;
using System;
using UnityEngine;
using UnityEngine.SceneManagement;
public class PlayerMovement : MonoBehaviour
{
[SerializeField] private float horSpeed;
[SerializeField] private float vertSpeed;
private Rigidbody2D rb;
private Boolean isJumping;
private void Start()
{
rb = GetComponent<Rigidbody2D>();
}
private void Update()
{
// we store the initial velocity, which is a struct.
var v = rb.velocity * Time.deltaTime;
if (Input.GetKey(KeyCode.Space) && !isJumping)
{
vf.y = vertSpeed;
isJumping = true;
}
if (Input.GetKey(KeyCode.A))
{
vf.x = -horSpeed;
}
if (Input.GetKey(KeyCode.D))
{
vf.x = horSpeed;
}
if (Input.GetKey(KeyCode.S))
{
vf.y = -vertSpeed;
}
rb.velocity = vf;
if(Input.GetKey(KeyCode.Escape))
SceneManager.LoadScene(0);
}
private void OnCollisionEnter2D(Collision2D collision)
{
isJumping = false;
}
}
I've added it to my enemy script and it works fine so im not sure whats different with player movement to using transform.position
Why would you multiply the velocity by Time.deltaTime?
This is used to convert a value from a "certain unit per frame" into a value "certain unit per second".
A velocity already IS a value in units per second.
Linear velocity of the Rigidbody in units per second.
=> It makes no sense to apply Time.deltaTime here at all.
For your X axis movement it doesn't make much difference since you anyway apply that velocity continuously every frame. Here you just would probably want to handle the case where neither left nor right is pressed and set vf.x = 0.
For the Y axis however you apply the jump only once, just to divide it by about 60 (or whatever your frame rate is) right in the next frame => you never give it the chance to fall or jump.
In general you should modify the Rigidbody in FixedUpdate. In your specific case it is fine to do this in Update, it is just redundant work ;)
I tested and this script cause lags in physic. It seems to me that it call physic not per 0,2 seconds, but much-much more rarely and because of this object falling much more slower.
Here's a gif to demonstrate what happens: The purple one without script, the blue one with script.
So how can I fix that?
public class PlayerMovement : MonoBehaviour
{
Rigidbody2D rb;
Vector2 movement = new Vector2(0, 0);
public float movementSpeed = 10f;
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
private void Update()
{
movement = new Vector2(Input.GetAxisRaw("Horizontal"), 0);
}
void FixedUpdate()
{
rb.MovePosition(rb.position + movement * movementSpeed * Time.fixedDeltaTime);
}
}
As said MovePosition overrules the movement => don't use it if you want physics (force) based movements and reaction to collisions etc.
You want to use the input only for the horizontal movement but use physics for the vertical. MovePosition affects both directions.
Try directly setting the Rigidbody.velocity instead
private void Update()
{
// Get the current velocity
var velocity = rb.velocity;
// Only overwrite the x velocity
// since the velocity is only applied in the FixedUpdate anyway
// there is no problem setting this already in Update
// And since it is the velocity which is already frame-rate independent
// there is no need for Time.deltaTime
velocity.x = Input.GetAxisRaw("Horizontal") * movementSpeed;
// Assign back the changed velocity
rb.velocity = velocity;
}
There is an issue with the logic of your code if I understand what you want to do.
You seem to be moving in a vertical fashion yet only have the x component of your vector2 from the horizontal input.
new Vector2(Input.GetAxisRaw("Horizontal"), 0);
If the GIF you attached, the object is just in freefall, so that would be a vertical drop, or the y component of the vector2, which you are setting to 0. Think of this as (x,y) or (move horizontally (left/right), move vertically (up/down)).
I would change the above line to:
movement = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
You can now move vertically using vertical input. Something else which would not cause your issue, but is mentioned on the MovePosition docs is to mark your Rigidbody2D as Interpolate. It is the bottom field in the Rigidbody2D component labeled Interpolate, set it from None to Interpolate.
The reason your object is moving slowly without any input is because you still have your Gravity Scale on the Rigidbody set to 1 instead of 0. If you are trying to simulate gravity with this script, add the vertical component to your input vector and set the Gravity Scale field on the Rigidbody to 0. The MovePosition is fighting the current gravity attempting to affect it.
I have a simple movement that relvolves around moving foward depending on where I look. The camera is made to follow the player. The problem I am havaing is that whenever I hit an object, my character(along with the camera) start spinning crazily all over the place. (I am new to coding)
Here is the code to the movement of where my character is facing :
public class Player : MonoBehaviour {
public float movementSpeed = 10;
public float turningSpeed = 60;
public Rigidbody rb;
void Start()
{
rb = GetComponent<Rigidbody> ();
}
void Update() {
float horizontal = Input.GetAxis("Mouse X") * turningSpeed *
Time.deltaTime;
transform.Rotate(0, horizontal,0);
float vertical = Input.GetAxis("Mouse Y")* turningSpeed * Time.deltaTime;
transform.Rotate(vertical, 0, 0);
float movement = Input.GetAxis("Foward") * movementSpeed *
Time.deltaTime;
transform.Translate(0, 0,movement);
}
}
(Sorry for bad format)
The Mouse X just makes it so that it rotates on the x axis of mouse(same with y axis). The Foward is just the vertical input preset in unity.
Here is the code to the lookAt player :
public class LookAtCamera : MonoBehaviour {
public GameObject target;
void LateUpdate() {
transform.LookAt(target.transform);
}
}
Answer
I believe your rigidbody is being more heavily affected than what you desire by the bounce force occuring as you hit the object.
Possible solutions
Depending on which collider type acts as your legs, the weight of the objects involved, what behaviour you intend and more the solution can vary.
Possible solution 1:
One option is to freeze the rotation of your object on it's Rigidbody component, you can decide which axes to freeze rotation on. If freezed they can't be pushed by outside forces.
Possible solution 2:
If you wish the player to only be affected by your script you can set the rigidbody kinematic, meaning that it only is affected by scripts and animation. This is a contrast to being set to dynamic where other objects can affect it.
Possible solution 3:
Play around with the angular drag and mass of the rigidbody to slow down rotation.
I am having an issue with my character which is not jumping at all. I am new to Unity, but I made sure to apply the script to the player and adjust the speed, I did not touch the Rigidbody 2D. If any one can help me figure our the issue, it will be much appreciated.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour {
public float moveSpeed;
public float jumpSpeed;
public bool grounded = false;
private Rigidbody2D rb;
void Start() {
rb = GetComponent<Rigidbody2D>();
}
void Update () {
transform.Translate (Input.GetAxisRaw ("Horizontal") * moveSpeed * Time.deltaTime, 0, 0);
if (grounded)
{
if (Input.GetButtonDown ("Jump"))
{
rb.AddForce (Vector2.up * jumpSpeed);
grounded = false;
}
}
}
void OnCollisionEnter2D (Collision2D coll){
if (coll.transform.tag == "Ground")
{
grounded = true;
}
}
}
Inspector window of the Player GameObject
Inspector window of the Ground GameObject
Your problem is you haven't tag the Ground GameObject as so. So in the OnCollisionEnter2D the character detects the collision, but the if (coll.transform.tag == "Ground") will never be true. So it means the character can't be grounded
Since to be grounded is the first condition to check if the player pressed the Jump key. It is impossible it will ever jump
if (grounded)
{
if (Input.GetButtonDown ("Jump"))
{
rb.AddForce (Vector2.up * jumpSpeed);
grounded = false;
}
}
To solve this issue: You need to tag the Ground GameObject as so. In case you are not sure how to do that, on the Tag menu, create (if it doesnt exist already) a new tag called Ground. Then assign in that same menu the Ground Tag to the Ground GameObject. Here you can learn how in case you need a visual reference:
https://docs.unity3d.com/Manual/Tags.html
Edit: You can try this script if everything fails. It should work. I used my self some time ago, I cleaned the code so to leave only what you need to move the character in the x and y axis. Hope to have included everything you need:
public class CharacterController2D : MonoBehaviour {
// LayerMask to determine what is considered ground for the player
public LayerMask whatIsGround;
// Transform just below feet for checking if player is grounded
public Transform groundCheck;
// store references to components on the gameObject
Transform transform;
Rigidbody2D rigidbody;
bool isGrounded = false;
float vy;
float vx;
public float jumpForce = 600f;
void Awake () {
transform = GetComponent<Transform> ();
rigidbody = GetComponent<Rigidbody2D> ();
}
void Update()
{
// determine horizontal velocity change based on the horizontal input
vx = Input.GetAxisRaw ("Horizontal");
vy = rigidbody.velocity.y;
// Check to see if character is grounded by raycasting from the middle of the player
// down to the groundCheck position and see if collected with gameobjects on the
// whatIsGround layer
isGrounded = Physics2D.Linecast(transform.position, groundCheck.position, whatIsGround);
if(isGrounded && Input.GetButtonDown("Jump")) // If grounded AND jump button pressed, then allow the player to jump
{
DoJump();
}
// Change the actual velocity on the rigidbody
rigidbody.velocity = new Vector2(_vx * MoveSpeed, _vy);
}
//Make the player jump
void DoJump()
{
// reset current vertical motion to 0 prior to jump
vy = 0f;
// add a force in the up direction
rigidbody.AddForce (new Vector2 (0, jumpForce));
}
}
So things to take into account:
Instead of tag the ground, you create a layer with everything you
consider ground. That will include possible platforms the character
may jump over. Pass as a parameter this layer to the script in the
inspector
You need to place an empty GameObject in the feet of the character.
You will drag and drop that GameObject in the editor onto the
groundCheck public variable.
Instead of OnTriggerEnter, you will use Physics2D.Linecast which
will trave a line from the position of the character to under its
feet (where you should have place the Transform mentioned in the
previous step) and if in the middle there is an element of the
groundLayer, it means the character will be grounded.
Let me know if anything is not clear or if you find some bug.
As mentioned your problem is deffinetly that your missing to tag your ground object :)
A tip: What i like to do when i have problems like this is to use the unitys Debug.Log() to locate where the problem is it. It will let you know easily in the console what code is run and which is not. Try do the following:
void Update () {
transform.Translate (Input.GetAxisRaw ("Horizontal") * moveSpeed * Time.deltaTime, 0, 0);
if (grounded)
{
Debug.Log("Is grounded");
if (Input.GetButtonDown ("Jump"))
{
Debug.Log("Jump clicked");
rb.AddForce (Vector2.up * jumpSpeed);
grounded = false;
}
}
}
I'm making a 2D platformer and I followed a tutorial to build my character. This character works fine except that when jumping, it doesn't allow for changing direction midair. How can I add to this to make my character able to change direction mid-jump?
The code used for the basic movement is below:
void Update()
{
CharacterController controller = GetComponent<CharacterController>();
float rotation = Input.GetAxis("Horizontal");
if(controller.isGrounded)
{
moveDirection.Set(rotation, 0, 0);
moveDirection = transform.TransformDirection(moveDirection);
//running code
if(Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) //check if shift is held
{ running = true; }
else
{ running = false; }
moveDirection *= running ? runningSpeed : walkingSpeed; //set speed
//jump code
if(Input.GetButtonDown("Jump"))
{
jump();
}
}
moveDirection.y -= gravity * Time.deltaTime;
controller.Move(moveDirection * Time.deltaTime);
}
EDIT: Forgot to include the code for jump(), that's probably kind of important...
void jump()
{
moveDirection.y=jumpHeight;
transform.parent=null;
}
You are updating the moveDirection vector only when the character is grounded:
if(controller.isGrounded)
when player jumps isGrounded is set to false, so moveDirection won't be updated (except for gravity).
I suppose you don't want to affect the velocity of the character while is jumping (unless it is supposed to fly or walk in the air). I guess you want to change its orientation while it's jumping, so you can directly modify transform making it rotate accordingly to your input.
Something like:
else
{
float amountOfRotation = Input.GetAxis("...") * rotationScaleFactor;
transform.RotateAround(transform.position, transform.up, Time.deltaTime * amountOfRotation);
}
EDIT
I've never used it but I think CharacterController is designed for being moved only when the object is grounded. So if you want to move it while it's on the air, don't use the Move method but edit directly the transform of your GameObject:
else
{
float additionalForwardSpeed = Input.GetAxis("...") * speedScaleFactor;
transform.Translate(transform.forward * Time.deltaTime * additionalForwardSpeed ); //translate
}
the code above should increase the speed of your object in local forward direction.