Soo, I'm trying to code a simple Platformer as a work for class, and I'm having problems with the jumping, the character actually jumps but it seems as a teleport since it instantly reaches the peak of the jump and then gradually falls, I'd like to make it smoother.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class playerMovement : MonoBehaviour {
public float speed;
public float jumpForce;
public bool grounded;
public Transform groundCheck;
private Rigidbody2D rb2d;
// Use this for initialization
void Start () {
rb2d = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update () {
}
private void FixedUpdate()
{
float moveVertical = 0;
float moveHorizontal = Input.GetAxis("Horizontal") * speed;
moveHorizontal *= Time.deltaTime;
grounded = Physics2D.Linecast(transform.position, groundCheck.position, 1 << LayerMask.NameToLayer("Ground"));
if (grounded && Input.GetKeyDown("space"))
{
moveVertical = Input.GetAxis("Jump") * jumpForce;
moveVertical *= Time.deltaTime;
}
Vector2 move = new Vector2(moveHorizontal, moveVertical);
transform.Translate(move);
}
}
The answer to the question is below, however, for future questions related to Unity ask on the Unity forum. You will get a better answer quicker as more people are focused on Unity itself.
To fix your jump, rather split your vertical and horizontal movement in two parts and use your rigidbody you assigned in in your Start() function to handle jumping. Use ForceMode.Impulse to get an instant burst of speed/acceleration.
private void FixedUpdate()
{
float moveHorizontal = Input.GetAxis("Horizontal") * speed;
moveHorizontal *= Time.deltaTime;
grounded = Physics2D.Linecast(transform.position, groundCheck.position, 1 << LayerMask.NameToLayer("Ground"));
if (grounded && Input.GetKeyDown("space"))
{
rb2d.AddForce(transform.up * jumpForce, ForceMode.Impulse);
}
Vector2 move = new Vector2(moveHorizontal, 0);
transform.Translate(move);
}
Instead of setting the position when you jump, add a vertical impulse to the character's rigidbody with:
if (grounded && Input.GetKeyDown("space"))
{
moveVertical = Input.GetAxis("Jump") * jumpForce;
moveVertical *= Time.fixedDeltaTime;
rb2d.AddForce(moveVertical, ForceMode.Impulse);
}
Vector2 move = new Vector2(moveHorizontal, 0f);
This will let Rigidbody2D handle the work of altering the vertical velocity on each frame.
Also, since you are doing all of these calculations in FixedUpdate, instead of Update, there is no sense in using Time.deltaTime. You should be using Time.fixedDeltaTime instead.
Related
How can I make this script work, or another alternative player movement script? This basic movement script in Unity doesn't do anything when i press WASD for some reason. I am a beginner at C# and unity and I kind of need an alternative player movement script that actually works.
void Update()
{
if (Input.GetKeyDown(KeyCode.W))
transform.Translate(Vector3.forward);
if (Input.GetKeyDown(KeyCode.S))
transform.Translate(Vector3.back);
if (Input.GetKeyDown(KeyCode.A))
transform.Translate(Vector3.left);
if (Input.GetKeyDown(KeyCode.D))
transform.Translate(Vector3.right);
I recommend using the GetAxis("Horizontal") and GetAxis("Vertical") methods which maps wasd and arrow keys by default to help slim down your big if tree.
Below is a small example from an old unversity project of mine.
Note: the "* speed * Time.deltaTime" as this is used to help translate as time/frames move on, speed is just a multiplier in this case.
public float speed = 10.0f;
private float translation;
private float straffe;
// Use this for initialization
void Start()
{
// turn off the cursor
Cursor.lockState = CursorLockMode.Locked;
}
// Update is called once per frame
void Update()
{
// Input.GetAxis() is used to get the user's input
// You can further set it on Unity. (Edit, Project Settings, Input)
translation = Input.GetAxis("Vertical") * speed * Time.deltaTime;
straffe = Input.GetAxis("Horizontal") * speed * Time.deltaTime;
transform.Translate(straffe, 0, translation);
if (Input.GetKeyDown("escape"))
{
// turn on the cursor
Cursor.lockState = CursorLockMode.None;
}
}
Input.GetKeyDown checks if you pressed the w key on that frame. You need Input.GetKey. That checks if you are holding
You should use GetAxis("Horizontal") & GetAxis("Vertical");
Here is my basic code for my projects default movement:
//movement
Rigidbody rb;
float xInput;
float yInput;
public float speed;
public float defSpeed;
public float sprint;
public bool isGrounded = true;
public float jump;
// Start is called before the first frame update
private void Awake()
{
rb = GetComponent<Rigidbody>();
}
void Start()
{
}
// Update is called once per frame
void Update()
{
xInput = Input.GetAxis("Horizontal");
yInput = Input.GetAxis("Vertical");
//Things is still can't understand
Vector3 input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
float cameraRot = Camera.main.transform.rotation.eulerAngles.y;
rb.position += Quaternion.Euler(0, cameraRot, 0) * input * speed * Time.deltaTime;
//sprint
if (Input.GetKeyDown(KeyCode.LeftShift))
{
speed = sprint;
}
else if (Input.GetKeyUp(KeyCode.LeftShift))
{
speed = defSpeed;
}
//jump
if (Input.GetKeyDown(KeyCode.Space) && isGrounded == true)
{
rb.AddForce(0, jump, 0);
//Debug.Log("Jump!");
}
Hope this helps:)
When I try to run this code on unity,everything works expect the jumping.Anyone know why?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public CharacterController controller;
public float speed = 12f;
public float gravity = -9.81f;
public float jumpHeight;
public Transform groundCheck;
public float groundDistance = 0.4f;
public LayerMask groundMask;
Vector3 velocity;
bool isGrounded;
// Update is called once per frame
void Update()
{
isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
if(isGrounded && velocity.y < 0)
{
velocity.y = -2f;
}
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * speed * Time.deltaTime);
if(Input.GetButtonDown("Jump") && isGrounded);
{
velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
}
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity *Time.deltaTime);
}
}
For jumping in unity you can just add a Vector3 to the object you want to make jump, if it is in movement unity calculate automatic the trajectory of the object.
Your problem is with this line here
velocity.y += gravity * Time.deltaTime;
Don't try add gravity yourself, unity will be handing this for you if you have a character controller
As suggested in the comment, First make the isGrounded a little more stable.
Use the character's RigidBody and AddForce(Vector3 force, ForceMode mode = ForceMode.Force); to implement jumps, as a Jump is an impulsive force applied on the character in the up direction. Directly using Move is not the best way of implementing a jump force.
Documentation for RigidBody.AddForce
using System.Collections;
using System.Collections.Generic; // <-- Since these are not utilized and not necessary for this code, defined by being grey in their Colour.
// Consider Removing them, To stop using unnecessary Data / Library calls in your code
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
/* THINGS TO KNOW */
// Private - only members of this class have access.
// Public -can be accessed outside of the script.
// Public Static - can be accessed in other scripts by using ( nameOfTheScript.VariableFromThatScript ) which are called Pointers.
// Protected - means it's only available in the class itself and any child-class.
// [SerializeField] private - Means that the variable is no longer publicly accessible, but can still be accessed inside the Editor (Inspector) of Unity
// Giving you the chance to drag / drop your character controller and modify values of other variables (while not overwriting your values outside of your other scripts)
// Always define if your variable is Public / Private or Protected, by default they will be Public.
[SerializeField] private CharacterController controller;
[SerializeField] private float speed = 12f;
[SerializeField] private float gravity = -9.81f;
[SerializeField] private float jumpHeight; // !! On Default this Element is going to be 0, if Not Defined Within the Inspector !!!
[SerializeField] private Transform groundCheck;
[SerializeField] private float groundRadius = 0.4f; // groundDistance -> groundRadius ( As it defines the size of the Sphere made from GroundCheck element)
[SerializeField] private LayerMask groundMask;
private Vector3 velocity;
[SerializeField] private bool isGrounded;
void Update() // Called once per frame.
{
// Defines if the character IsGrounded ( True or False )
// 1) Find object groundCheck (dragged in the inspector for the code)
// .position Getting its position where GroundCheck is at the current moment
//,2) Make a Sphere with a radius of ..
// 3) Define what LayerMask is going to be checked.
isGrounded = Physics.CheckSphere(groundCheck.position, groundRadius, groundMask);
if (isGrounded && velocity.y < 0) // !! Issue fixed " if (isGrounded && velocity.y < 0); " You cannot have " ; " - breaker before your method brackets.
{
velocity.y = -2f;
}
/* Move these function into their separate methods.*/
/* Makes it easier to read as well as Debug,
in order to find where the issue in your code is taking place*/
Move();
Jump();
controller.Move(velocity * Time.deltaTime);
}
private void Move()
{
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * speed * Time.deltaTime);
}
private void Jump()
{
// Y Axis
if (Input.GetButtonDown("Jump") && isGrounded)
{
velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
}
velocity.y += gravity * Time.deltaTime; // Applies gravity with (times) * Last updated frame to next update frame.
}
}
Make sure that you add : LayerMask to your object as well from which you are trying to jump from and check if you can jump. Found on this Picture ->
LayerMask Applied to The Floor Or Ground Object
On Player GameObject, Make sure that you have defined all variable values, as well as other GameObjects : such as " groundCheck " found on this Picture ->
Player Game Object Information In Inspector
Last put not least make sure you have defined JumpHeight in your Inspector of the Player
Though there are a lot of improvements still to make, this is what I would suggest, having provided you with commenting for Each Change or Explanation of a function.
the only problem that i face that the player only jump when they build momentum and i dont want that i want player jump by pressing space while standing or moving i tried velocity.y=jumphight which doesnt work too
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovment : MonoBehaviour
{
public CharacterController controller;
public float speed = 12f;
public float gravity = -9.81f;
Vector3 velocity;
public Transform groundCheck;
public float groundDistance = 0.4f;
public LayerMask groundMask;
bool isGrounded;
public float JumpHight = 3f;
void Update()
{
isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
if(isGrounded && velocity.y<0)
{
controller.slopeLimit = 45.0f;
velocity.y = -2f;//when player is grounded it just put some gravity to him all the time
}
float x = Input.GetAxis("Horizontal");//moves left and right
float z = Input.GetAxis("Vertical");//moves up and down
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move*speed*Time.deltaTime);
if(Input.GetButtonDown("Jump") && controller.isGrounded)
{
velocity.y = Mathf.Sqrt(JumpHight * -2f * gravity);
}
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
}
}
you could use an addforce on the rigidbody with a ForceMode force , and if you don't want your physic depending on the fps use FixedUpdate instead of update
GetComponent<Rigidbody>().AddForce(Vector3.up,ForceMode.Force);
Vector3 velocity;
public float gravity = -9.82f;
public float JumpHeight = 3f;
if(Input.GetButtonDown("Jump")
{
velocity.y = Matf.Sqrt(JumpHeight * -2f * gravity);
}
You could also do what the other person said, the reason this person's line of code might not have worked it due to how weak it is. Here is an updated version of said code.
GetComponent<Rigidbody>().AddForce(Vector3.up * 10f ,ForceMode.Impulse);
The reason I am using the * is to increase the "up" variable since it means Vector3(0f, 1f, 0f); and using the ForceMode.Impulse to make it more volatile and stronger.
Hope this code helps you in your game making,
Kind Regards. SB
Here's my code. Currently, the player moves forwards relative to a fix plane. Rotating the player/camera has no affect on which direction they travel when I press the "go forward" key.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MainController : MonoBehaviour
{
public float speed;
float jumpSpeed = 0.5f;
bool isGrounded;
private Rigidbody rb;
public GameObject player;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
}
void FixedUpdate()
{
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
if (Input.GetButtonDown("Jump") || Input.GetAxis("Right Trigger") > 0f && isGrounded)
{
rb.AddForce(Vector3.up * jumpSpeed, ForceMode.Impulse);
isGrounded = false;
}
if (isGrounded)
{
rb.AddForce(movement * speed * Time.deltaTime);
}
else
{
rb.AddForce((movement/4) * speed * Time.deltaTime);
}
}
void OnCollisionStay()
{
isGrounded = true;
}
void OnCollisionExit()
{
isGrounded = false;
}
}
How do I make it so that the player moves relative to the camera's direction?
It's because RigidBody.AddForce() adds force in absolute direction, no matter which direction the gameobject is looking.
You should try using RigidBody.AddRelativeForce(), like:
if (isGrounded)
{
rb.AddRelativeForce(movement * speed * Time.deltaTime);
}
I think there is a better way to do this, but see if it results in the expected behavior:
void FixedUpdate()
{
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
Vector3 looking = (transform.rotation * Vector3.forward).normalized;
Vector3 perpendicular = Vector2.Perpendicular(new Vector2(looking.x, looking.z));
perpendicular.z = perpendicular.y;
perpendicular.y = looking.y;
Vector3 vertical = moveVertical * looking;
Vector3 horizontal = moveHorizontal * perpendicular;
Vector3 movement = vertical - horizontal;
...
}
I am trying to make a script where i can move a ball, horizontal and vertical. I managed to get that working.
But now I want to make my ball "jump". I ended with script below, but now my ball just get launched like a rocket xD
Can anyone help me out
using UnityEngine;
using System.Collections;
public class PlayerController : MonoBehaviour
{
public float speed;
public float jumpSpeed;
public GUIText countText;
public GUIText winText;
private int count;
void Start()
{
count = 0;
SetCountText();
winText.text = " ";
}
void FixedUpdate()
{
float moveHorizontal = Input.GetAxis ("Horizontal");
float moveVertical = Input.GetAxis ("Vertical");
Vector3 movement = new Vector3 (moveHorizontal, 0, moveVertical);
Vector3 jump = new Vector3 (0, jumpSpeed, 0);
GetComponent<Rigidbody>().AddForce (movement * speed * Time.deltaTime);
if (Input.GetButtonDown ("Jump"));
GetComponent<Rigidbody>().AddForce (jump * jumpSpeed * Time.deltaTime);
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "PickUp") {
other.gameObject.SetActive(false);
count = count +1;
SetCountText();
}
}
void SetCountText()
{
countText.text = "Count: " + count.ToString();
if (count >= 10)
{
winText.text = "YOU WIN!";
}
}
}
Jumping does not work with adding a continuous force on an object. You will have to apply a single impulse to the object once when the jump button is first pressed. This impulse will also not include a time factor, because it is applied only once. So you would get something like this:
bool jumping;
if (Input.GetButtonDown ("Jump") && !this.jumping);
{
GetComponent<Rigidbody>().AddForce (jumpForce * new Vector3(0,1,0));
this.jumping = true;
}
Also note that in your example you are multiplying the upward unit vector by the jumpspeed twice. Once in the jump vector initialization and then once in the AddForce method.
Of course, you will also have to make sure that gravity applies to pull the object back down (and if the object hits the ground, reset the jump bool.
In general, depending on what kind of game you are making, it is easier to just set the velocity of the object yourself and don't work with the Unity physics engine to do some simple moving around.
There is an error in your code in the function FixedUpdate:
if (Input.GetButtonDown ("Jump"));
in that way you are applying a force on your object at every frame because the semicolon is excluding the row below from the condition. By removing the semicolon you will have a correct if implementation in case of a jump, as long as on your RigidBody component UseGravity is enabled.
if (Input.GetButtonDown ("Jump"))
GetComponent<Rigidbody>().AddForce (jump * jumpSpeed * Time.deltaTime);
Hope it helps.
Thanks everyone, it was a great help. I now have a jumping character. One who can only jump when grounded.
public bool IsGrounded;
void OnCollisionStay (Collision collisionInfo)
{
IsGrounded = true;
}
void OnCollisionExit (Collision collisionInfo)
{
IsGrounded = false;
}
if (Input.GetButtonDown ("Jump") && IsGrounded)
{
GetComponent<Rigidbody>().velocity = new Vector3(0, 10, 0);
}