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);
}
Related
My problem is two-fold. Animation and a GameObject jumping off during Play. I don't know the exact phrases of what happens, I'll try to explain best I can.
I want to increase a float upon movement, which should trigger animation for my character. It is the SpeedBlend in image below. It's between 0 and 1.
The movement works fine and debugging works fine. But I cannot get the SpeedBlend to increase.
Here are the issues:
It says "NullReferenceException: Object reference not set to an instance of an object" when I press Play. I narrowed it down to the GameObject jumping off the public Animator when i press Play. I don't know the exact wording of what happens, but the "Animation (Animator)" is no longer attached to the Scripts' Animator. Does that make sense?
See below.
The "Animation (Animator)" jumps off on Play. If I declare the animator in Start function, it jumps off. If I don't declare it, it stays on during Play. See below.
private void Start()
{
Cursor.lockState = CursorLockMode.Locked;
controller = GetComponent<CharacterController>();
animator = GetComponentInChildren<Animator>();
}
If I re-apply the "Animation (Animator)" to the Scripts' Animator during Play, it changes the problem to
"NotImplementedException: The method or operation is not implemented."
My current solution for animating my character is:
//animation
if (direction != Vector3.zero)
{
animator.SetFloat("SpeedBlend", 0.5f);
Debug.Log("moving works fine");
}
The debugging works fine.
So my questions are two-fold:
Why is the "Animation (Animator)" no longer attached to the Script during Play?
My solution animator.SetFloat("SpeedBlend", 0.5f); doesn't work. Why not?
Just in case you want the read the entire code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ThirdPersonMovement : MonoBehaviour
{
public CharacterController controller;
public Transform cam;
//moving
public float speed = 6f;
public float turnSmoothTime = 0.1f;
float turnSmoothVelocity;
//https://www.youtube.com/watch?v=_QajrabyTJc&ab_channel=Brackeys
//gravity + jumping
public float gravity = -9.81f;
public Transform groundCheck;
public float groundDistance = 0.4f;
public LayerMask groundMask;
public float jumpHeight = 3f;
Vector3 velocity;
bool isGrounded;
//Animation
public Animator animator;
// Start is called before the first frame update
private void Start()
{
Cursor.lockState = CursorLockMode.Locked;
controller = GetComponent<CharacterController>();
animator = GetComponentInChildren<Animator>();
}
// Update is called once per frame
void Update()
{
Move();
/*
Idle();
*/
Run();
}
void Move()
{
//isgrounded
isGrounded = Physics.CheckSphere(transform.position, groundDistance, groundMask);
if (isGrounded && velocity.y < 0)
{
velocity.y = -2f;
}
//moving or walking
float horizontal = Input.GetAxisRaw("Horizontal");
float vertical = Input.GetAxisRaw("Vertical");
Vector3 direction = new Vector3(horizontal, 0f, vertical).normalized;
if (direction.magnitude >= 0.1f)
{
float targetAngle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg + cam.eulerAngles.y;
float angle = Mathf.SmoothDampAngle(transform.eulerAngles.y, targetAngle, ref turnSmoothVelocity, turnSmoothTime);
transform.rotation = Quaternion.Euler(0f, angle, 0f);
Vector3 moveDirection = Quaternion.Euler(0f, targetAngle, 0f) * Vector3.forward;
controller.Move(moveDirection.normalized * speed * Time.deltaTime);
}
//gravity
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
//jumping
if (Input.GetButtonDown("Jump") && isGrounded)
{
velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
}
//animation
if (direction != Vector3.zero)
{
animator.SetFloat("SpeedBlend", 0.5f);
Debug.Log("moving works fine");
}
}
/*
void Idle()
{
animator.SetFloat("SpeedBlend", 0);
}
*/
void Run()
{
//running
if (Input.GetKey(KeyCode.LeftShift))
{
speed = 12f;
Debug.Log("left shift presseed");
/*
animator.SetFloat("SpeedBlend", 1f);
*/
}
else
{
speed = 6f;
}
}
}
I tried changing movement scripts, changing animation types. Instead of the current Blend Tree, I tried using a bool parameter "isWalking". None of it works, and I can't for the love of me grasp why. If I manually increase the SpeedBlend during Play, the animation changes as it should.
If you cannot run your app in debug mode (which would show you the exception and its StackTrace):
Add a try-catch to your code
try
{
// the code that throws
}
catch (Exception ex)
{
// Set a break point or log the exception and its StackTrace.
Console.WriteLine(ex.ToString());
}
Try to log the StackTrace for example to a file or to the console (if you have no logging set up). That will show you what method throws the exception.
You can also get the whole exception as a string, with inner exception and all StackTraces via ex.ToString() and log that (instead of the StackTrace property only). See the code example above.
In case of the NotImplementedException: probably a base class has a virtual or abstract method that that you need to override.
So my problem here is that when the player holds the space button the addForce function is increased which I don't want.
So what I want is that if a player holds the space button they can keep jumping continuously if they're on the ground...
Here is my code:
private void Update()
{
GetComponent<Rigidbody2D>().velocity = new Vector2(1.0f * movementSpeed, GetComponent<Rigidbody2D>().velocity.y);
if (IsGrounded() && Input.GetKey("Jump"))
{
Debug.Log("IS JUMPING");
_rigidBody.AddForce(new Vector2(0, jumpForce), ForceMode2D.Impulse);
}
}
This is the IsGrounded Code:
private bool IsGrounded()
{
RaycastHit2D raycastHid2d = Physics2D.BoxCast(boxCollider2d.bounds.center, boxCollider2d.bounds.size, 0f, Vector2.down, .1f, layerMask);
//Debug.Log(raycastHid2d.collider);
return raycastHid2d.collider != null;
}
As said the issue is most probably that the rigibody doesn't move far enough away from the ground immediately so you might get multiple input frames before IsGrounded returns false.
In general for physics related things you should rather use FixedUdpate.
While Update is executed every frame, FixedUpdate uses a fixed time delta => There might be multiple Update calls before he physics are actually applied and your scene is updated accordingly!
While you will want to get one time event input (GetKeyDown) still in Update (otherwise you might miss it) I would move continuous user input into FixedUpdate
[Serializefield] Rigidbody2D rigidbody;
private void Awake()
{
if(!rigidbody) rigidbody = GetComponent<Rigigbody2D>();
}
private void FixedUpdate()
{
rigidbody.velocity = new Vector2(movementSpeed, rigidbody.velocity.y);
if (IsGrounded() && Input.GetKey("Jump"))
{
Debug.Log("IS JUMPING");
rigidbody.AddForce(Vector2.up * jumpForce), ForceMode2D.Impulse);
}
}
And as said just to be super sure you could still add some cooldown like e.g.
private float timer;
public float jumpCooldown = 0.1f;
private void FixedUpdate()
{
rigidbody.velocity = new Vector2(movementSpeed, rigidbody.velocity.y);
timer += Time.deltaTime;
if(timer < jumpCooldown) return;
if (IsGrounded() && Input.GetKey("Jump"))
{
Debug.Log("IS JUMPING");
rigidbody.AddForce(Vector2.up * jumpForce), ForceMode2D.Impulse);
timer = 0f;
}
}
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:)
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.
So basicy i need my character to go up when space or mousebutton are pressed. I added rigbody 2d and box colider 2d to my gameobject (player). The problem is when I hit space or mousebuttonit only jump not constantly moving up.
Here is my code:
using UnityEngine;
using System.Collections;
public class PixelMovement : MonoBehaviour {
Vector3 velocity = Vector3.zero;
public Vector3 PressVelocity;
public float maxSpeed = 5f;
public float fowardSpeed = 1f;
public float jumpForce;
bool didPress = false;
// Use this for initialization
void Start () {
}
//Do Graphic & Input updates
void Update()
{
if (Input.GetKey(KeyCode.Space) || Input.GetMouseButton(0))
{
didPress = true;
}
}
//Do physics engine updates here
void FixedUpdate()
{
velocity.x = fowardSpeed;
if (didPress == true)
{
didPress = false;
velocity += PressVelocity * Time.deltaTime *jumpForce;
}
velocity = Vector3.ClampMagnitude(velocity, maxSpeed);
transform.position += velocity * Time.deltaTime;
}
}
Ok this works for me. Thanks everyone for help :)
using UnityEngine;
using System.Collections;
public class PixelMovement : MonoBehaviour {
public float playerSpeed;
public Vector3 gravity;
public float maxSpeed = 5f;
Vector3 velocity = Vector3.zero;
public float fowardSpeed = 1f;
bool didPress = false;
void Update()
{
if (Input.GetKey(KeyCode.Space) || Input.GetMouseButton(0))
{
didPress = true;
}
else
{
didPress = false;
}
}
void FixedUpdate()
{
velocity.x = fowardSpeed;
transform.position += velocity * Time.deltaTime;
if (didPress == true)
{
float amntToMove1 = playerSpeed * Time.deltaTime;
transform.Translate(Vector3.up * amntToMove1);
}
velocity = Vector3.ClampMagnitude(velocity, maxSpeed);
transform.position += velocity * Time.deltaTime;
}
}
If you are using gravity and RigitBody2D's mass. I recomend you use the RigitBody2D.Addforce() method to jump your caracter, because if you just change the caracter position, the gravity will turn down him to the ground inmediately
This code creates the effect of a "rocketman" that takes off when the user keeps the spacebar pressed:
using UnityEngine;
using System.Collections;
public class jumpAntigravity : MonoBehaviour {
Vector2 upForce;
void Start () {
//Up force set to double of gravity
upForce = new Vector2(0, 9.8f*2);
}
void Update () {
if (Input.GetKey(KeyCode.Space))
{
print("Spacebar is down");
GetComponent<Rigidbody2D>().AddForce(upForce);
}
}
}
if (Input.GetKey(KeyCode.Space) || Input.GetMouseButton(0))
{
didPress = true;
}
else
{
didPress = false;
}
and remove didPress = false; from FixedUpdate() function
EDIT: Ok, wait, I just noticed you update transform.position in FixedUpdate() which is not really a good idea. When you do so you basically teleport your object from one position to another ignoring all kinds of physics (which probably is the reason you are using FixedUpdate()). With your script colliding with other objects will give you weird results. Find a tutorial on using RigidBodies to do proper physics-aware movement, or just move all your code to Update() because right now you might have some frames where your object doesn't move and other frames where it moves twice the distance. This will happen because FixedUpdate() may get executed any number of times (including 0) in a single frame, depending on a setup. FixedUpdate runs firmly 50 times per second by default, while Update runs roughly 60 times per second (on mobile 30) by default. And lastly, in FixedUpdate never use Time.deltaTime, use Time.fixedDeltaTime:
Time.deltaTime is equal to roughly 1 / 60th of a second (1 frame duration)
Time.fixedDeltaTime is roughly 1 / 50th of a second (1 physics update duration)
FixedUpdate can be called multiple times per frame in succession. This is based on the physics settings for your project. You can edit this to add more or less accuracy to your physics.
Update is called only 1 time a frame.
So you want to avoid doing logic in the FixedUpdate. If you want to move a rigid body use MovePosition.
From the Unity docs: If Rigidbody interpolation is enabled on the Rigidbody, calling Rigidbody.MovePosition results in a smooth transition between the two positions in any intermediate frames rendered. This should be used if you want to continuously move a rigidbody in each FixedUpdate.