So I want to check if there is ground under my character
here is the code
public class move2d : MonoBehaviour
{
public float moveSpeed = 7f;
public float distanceGround;
public bool isGrounded = false;
// Start is called before the first frame update
void Start()
{
distanceGround = GetComponent<Collider2D>().bounds.extents.y;
}
// Update is called once per frame
void Update()
{
if (Physics2D.Raycast(transform.position, -Vector2.up, distanceGround + 0.1f))
{
}
else
{
isGrounded = true;
Jump();
}
Vector3 movement = new Vector3(Input.GetAxis("Horizontal"), 0f, 0f);
transform.position += movement * Time.deltaTime * moveSpeed;
}
void Jump()
{
if (Input.GetButtonDown("Jump") )
{
gameObject.GetComponent<Rigidbody2D>().AddForce(new Vector2(0f, 8f), ForceMode2D.Impulse);
}
}
}
but it doesn't work and I don't understand why it never enter else statement even there my character is on ground.
When dealing with rigidbody you shouldn't use transform.position not for getting and not for setting values. Rather use Rigidbody2D.position for getting and Rigidbody2D.MovePosition in FixedUpdate for setting.
Also shouldn't it be the other way round? You most probably are grounded if the raycast hits something .. not if it doesn't?
// Already reference these via the Inspector
[SerializeField] private RigidBody2D rigidBody2D;
[SerializeField] private Collider2D _collider2D;
public float moveSpeed = 7f;
public float distanceGround;
private Vector2 movement;
private bool jumped;
private void Awake ()
{
// Alternately get it once on runtime
if(! rigidBody2D) rigidBody2D = GetComponent<RigidBody2D>();
if(!_collider2D) _collider2D = GetComponent<Collider2D>();
}
private void Start()
{
// Micro performance improvement by calculating this only once ;)
distanceGround = _collider2D.bounds.extents.y + 0.1f;
}
// Get user Input every frame
private void Update()
{
// Directly check for the button
if (Input.GetButtonDown("Jump"))
{
// if button presed set the jump flag
jumped = true;
}
// store user input
movement = Vector3.right * Input.GetAxis("Horizontal");
}
// Apply physics in the physics update
private void FixedUpdate()
{
// If jump flag is set
if(jumped)
{
// You are grounded when you hit something, not the other way round
if (Physics2D.Raycast(rigidBody2D.position, Vector2.down, distanceGround))
{
rigidbody2D.AddForce(Vector2.up * 8f, ForceMode2D.Impulse);
}
// reset the flag
jumped = false;
}
// apply the normal movement without breaking the physics
rigidBody2D.MovePosition(rigidBody2D.position + movement * Time.deltaTime * moveSpeed);
}
Related
I have a player in unity with the movement controlled by a rigidbody. The movement on the Z axis is kept contstant by the game to keep the player moving forward. However, this means that the rigidbody keeps speeding up so its speed at the start of the game is much slower than the speed at the end of the game. Here is my script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;
public class Controller : MonoBehaviour
{
[Tooltip("Rigidbody component attached to the player")]
public Rigidbody rb;
public float forwardMax;
public float slowBy;
private float movementX;
private float movementY;
private float gravity = -9.81f;
private bool isJumping = false;
private bool isSlowing = false;
private bool isSpeeding = false;
private float speedX = 100;
private float speedY = 150000;
private float speedZ = 60;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
}
void Update()
{
// if(!controller.isGrounded)
// {
// Vector3 gravityVector = new Vector3(0.0f, gravity, 0.0f);
// rb.AddForce(gravityVector * Time.deltaTime);
// }
}
void OnCollisionEnter(Collision collision)
{
// SceneManager.LoadScene(1);
}
// Update is called once per frame
void OnMove(InputValue movementValue)
{
Vector2 movementVector = movementValue.Get<Vector2>();
movementX = movementVector.x;
movementY = movementVector.y;
}
void OnJump()
{
isJumping = true;
}
void CalculateMovement()
{
if(rb.velocity.z > 20)
{
rb.drag = 20;
}
else
{
rb.drag = 0;
}
Vector3 movement = new Vector3(movementX * speedX, 0.0f, speedZ);
if(isJumping)
{
movement.y += Mathf.Sqrt(speedY * -3.0f * gravity);
isJumping = false;
}
rb.AddForce(movement);
Debug.Log("Speed is " + rb.velocity.z);
}
void FixedUpdate()
{
CalculateMovement();
}
}
Is there a way to keep the forward velocity constant? The problem is worse when the player jumps.
First I tried clamping the forward (z-axis) vector but that had no effect. Then, I tried adding a backward vector onto the total when the forward velocity was above a certain number but this led to it speeding up and slowing down all the time. Then I tried the same thing with the drag on the rigidbody but that had the same effect.
You can use rigidbody.velocity and set it to constant value or whatever you want instead of adding force. By adding force, your character's speed increases.
Also you can use AddForce but you have to tune the force value dynamically according to the current velocity.
rigidbody velocity
How about directly setting the z value you want at the end of FixedUpdate() ?
......
void FixedUpdate()
{
CalculateMovementWithoutZMovement();
rb.velocity = new Vector3 (rb.velocity.x, rb.velocity.y, ConstantZValue);
}
I am trying to take the current position of something once when collided and im not sure how to
do that could anyone help me out please?
The problem i have is that the script is grabbing the playerLocation constantly i need it so it grabs it once it collides.
basically when my object colides with something it moves towards a diferent object but i dont want it to follow it constantly i want it to get the position of that 1 object only once it has collided.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CircleMover : MonoBehaviour
{
public float speed = 10.0f;
public Transform playerLocation;
public float redirectSpeed;
public bool isCurrentlyColliding;
Vector3 playerposition;
private Rigidbody2D rb;
private Vector2 screenBounds;
// Start is called before the first frame update
void Start()
{
rb = gameObject.GetComponent<Rigidbody2D>();
rb.velocity = new Vector2(-speed, 0);
screenBounds = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, Camera.main.transform.position.z));
}
// Update is called once per frame
void Update()
{
//if (transform.position.x < -11.6f)
//{
// Destroy(this.gameObject);
//}
if (isCurrentlyColliding)
{
transform.position = Vector2.MoveTowards(transform.position, playerposition.transform.position, redirectSpeed * Time.deltaTime);
}
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("CopyCat"))
{
playerposition = (playerLocation.transform.position);
isCurrentlyColliding = true;
}
}
}
The reason it grabs the playerLocation constantly because you are using isCurrentlyColliding which you are never making false and also using it in Update() method which calls every frame.
You can do two things (choose whichever goes well with your code) :
by making isCurrentlyColliding false inside Update() method like this:
void Update()
{
if (isCurrentlyColliding)
{
transform.position = Vector2.MoveTowards(transform.position, playerposition.transform.position, redirectSpeed * Time.deltaTime);
isCurrentlyColliding = false;
}
}
by removing isCurrentlyColliding and setting transform.position directly inside OnTriggerEnter2D() method like this:
void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("CopyCat"))
{
playerposition = (playerLocation.transform.position);
transform.position = Vector2.MoveTowards(transform.position, playerposition.transform.position, redirectSpeed * Time.deltaTime);
}
}
I have a character, he can move and jump. I need to check if it is grounded, so I made a trigger box collider as a characters component, and I use OnTriggerEnter and OnTriggerExit to check if it is grounded, but the exit of collision with object that the character is standing on is detected one physics update late, and when it is detected, the upward velocity appears to become 0, and the character starts falling. Here is my code:
using System.Collections;
using UnityEngine;
public class Player : MonoBehaviour
{
public float speed = 4.0f;
public float jumpSpeed = 8.0f;
private bool doJump = false;
private Rigidbody rb;
bool isGrounded = false;
private float x, z;
private void Awake()
{
rb = GetComponent<Rigidbody>();
}
private void Update()
{
x = Input.GetAxis("Horizontal");
z = Input.GetAxis("Vertical");
if (Input.GetKeyDown(KeyCode.Space))
{
StartCoroutine(Jump());
}
}
void FixedUpdate()
{
if (isGrounded)
{
//this is movement
rb.velocity = (transform.right * x) * speed * Time.fixedDeltaTime + (transform.forward * z) * speed * Time.fixedDeltaTime;
}
if (isGrounded && doJump)
{
doJump = false;
rb.AddForce(0, jumpSpeed, 0, ForceMode.VelocityChange);
}
}
IEnumerator Jump()
{
//need the coroutine to make the player jump even if space pressed a bit earlier than
//the character landed
doJump = true;
yield return new WaitForSeconds(0.15f);
doJump = false;
}
private void OnTriggerStay(Collider other)
{
if (other != this.gameObject)
{
isGrounded = true;
}
}
private void OnTriggerExit(Collider other)
{
if (other != this.gameObject)
{
isGrounded = false;
}
}
}
I tried to not use coroutine for jumping but it doesn't help. What helps is deleting the movement line.
I think you are trying to implement "Pre_Update" function, I have found this solution maybe you can use it:
https://answers.unity.com/questions/614343/how-to-implement-preupdate-function.html
I am not sure but I think if you initialize your physics component in the Start function instead of the Awake function then it might work.
I dont know what to do, when I click play, I click Space for Jump and sometime it jump but sometime it not. What do I do wrong?
i got it from https://www.youtube.com/watch?v=I_mjYhwSsS8
this is my PlayerController Script.
// Start is called before the first frame update
void Start()
{
this.rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
private void FixedUpdate()
{
Grounded();
Jump();
Move();
}
private void Jump()
{
if(Input.GetKeyDown(KeyCode.Space) && this.grounded)
{
this.rb.AddForce(Vector3.up * 4, ForceMode.Impulse);
}
}
private void Grounded()
{
if(Physics.CheckSphere(this.transform.position + Vector3.down, 0.2f, layerMask))
{
this.grounded = true;
}
else
{
this.grounded = false;
}
this.anim.SetBool("jump", !this.grounded);
}
private void Move()
{
float verticalAxis = Input.GetAxis("Vertical");
float horizontalAxis = Input.GetAxis("Horizontal");
Vector3 movement = this.transform.forward * verticalAxis + this.transform.right * horizontalAxis;
movement.Normalize();
this.transform.position += movement * 0.04f;
this.anim.SetFloat("vertical", verticalAxis);
this.anim.SetFloat("horizontal", horizontalAxis);
}
In general you want to get single time event user input within Update!
FixedUpdate is called in fixed time intervals, not every frame which might cause that if you press a button down exactly in a frame where FixedUpdate is not called you miss that event.
Secondly as soon as there is a rigidbody involved you should not use Transform.position to move it but rather go through the Rigidbody component either setting velocity or using MovePosition
private bool isJumpPressed;
private void Start()
{
if(!rb) rb = GetComponent<Rigidbody>();
}
private void Udpate()
{
GetJumpInput();
}
private void GetJumpInput()
{
// it is slightly more efficient to first check the simple bool flag
if(grounded && (Input.GetKeyDown(KeyCode.Space))
{
// set the jump flag that is later handled in FixedUpdate
isJumpPressed = true;
}
}
private void FixedUpdate()
{
Grounded();
HandleJump();
Move();
}
private void HandleJump()
{
// since the grounded might have changed in the meantime check it again
// then check and handle the jump flag
if(grounded && isJumpPressed)
{
rb.AddForce(Vector3.up * 4, ForceMode.Impulse);
}
// after having handled the input always reset it
isJumpPressed = false;
}
private void Grounded()
{
grounded = Physics.CheckSphere(this.transform.position + Vector3.down, 0.2f, layerMask);
anim.SetBool("jump", !this.grounded);
}
private void Move()
{
// you might want to consider to only allow a change of this while on the ground
// so midair you can't change the direction
//if(!grounded) return;
var verticalAxis = Input.GetAxis("Vertical");
var horizontalAxis = Input.GetAxis("Horizontal");
// store the current velocity
var velocity = rb.velocity;
// most importantly store the Y velocity in order to maintain that one
var y = velocity.y;
// instead of normalize you rather want to Clamp the magnitude
// otherwise you also get magnitude 1 even though the input is actually smaller
velocity = Vector3.ClampMagnitude(rb.rotation * Vector3.forward * verticalAxis + rb.rotation * Vector3.right * horizontalAxis, 1) * 0.04f;
// finally maintain the Y velocity so you can't fly ;)
velocity.y = y;
// and assign the velocity back to the rigidbody
rb.velocity = velocity;
this.anim.SetFloat("vertical", verticalAxis);
this.anim.SetFloat("horizontal", horizontalAxis);
}
I'm learning Unity, and I'm doing a character move, but the animation of the jump has a delay for the character to literally jump, a 0.30s until it picks up, how do I add this delay in the code?
Like, I thought of doing somehow that when you hit "Space" release the animation, count 0.20s and make the jump. it's viable? How can I do this?
In short, the character jumps before the animation.
Animation Video:
https://imgur.com/a/LgzkSKi
public class PlayerController : MonoBehaviour {
public float moveSpeed;
public float jumpForce;
public CharacterController controller;
private Vector3 moveDirection;
public float gravityScale;
public Animator animator;
void Start() {
controller = GetComponent<CharacterController>();
}
void Update() {
float yStore = moveDirection.y;
moveDirection = (transform.forward * Input.GetAxis("Vertical")) + (transform.right * Input.GetAxis("Horizontal"));
moveDirection = moveDirection.normalized * moveSpeed;
moveDirection.y = yStore;
jump();
controller.Move(moveDirection * Time.deltaTime);
animator.SetFloat("Speed", (Mathf.Abs(Input.GetAxis("Vertical"))));
}
void jump() {
if (controller.isGrounded) {
moveDirection.y = 0f;
if (Input.GetButtonDown("Jump")) {
moveDirection.y = jumpForce;
Debug.Log("jump");
}
}
moveDirection.y = moveDirection.y + (Physics.gravity.y * gravityScale * Time.deltaTime);
animator.SetBool("isGrounded", controller.isGrounded);
}
}
In your jump() function don't apply the jump force. Instead, set the next time the character is supposed to jump.
float nextJumpTime;
bool todoJump;
void jump() {
if (!todoJump && Input.GetButtonDown("Jump")) {
// Remember we have to jump and when
nextJumpTime = Time.time + 0.2f;
todoJump = true;
}
// Execute the jump
if (todoJump && Time.time >= nextJumpTime) {
todoJump = false;
moveDirection.y = jumpForce;
}
}
Either that or read on coroutines. Start a coroutine on input, yield return new WaitForSecond(0.2f); in the coroutine and then execute the jump.
To what One Man Mokey Squad suggest I think you should also consider to get a new jump animation or trim the current animation.
I think it's not a great idea to create a custom code for that broken jump animation because you will not be able to reuse your script.