How to make player stop infinite jump in unity 2d - c#

So I'm new to unity only 2 days, and I'm having this problem of my player jumping in air. I can keep clicking the spacebar key and my player will keep jumping and not stop, I'm trying make it so the player can only jump while on the ground not the air. I been trying a lot of tutorials and most of them just made my game freeze up and not work anymore. I also been trying to find out how to use ground check but I'm still not sure on how to do that.
This is my code that I have right know.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PM : MonoBehaviour
{
public float moveSpeed;
public float jumpHeight;
Rigidbody2D rb;
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
void Update() {
float moveDir = Input.GetAxis("Horizontal") * moveSpeed;
rb.velocity = new Vector2(moveDir, rb.velocity.y);
// Your jump code:
if (Input.GetKeyDown(KeyCode.Space))
{
rb.velocity = new Vector2(rb.velocity.x, jumpHeight);
}
}
}

Yes, you do need some sort of ground check. There are a lot of good tutorials online, for example, https://www.youtube.com/watch?v=c3iEl5AwUF8&t=17s ("3 ways to do a Ground Check in Unity").
To implement the simple "raycast down check", you need to assign the ground/platforms a layermask that is not the same as the player's layer. In this example, I have added a new layer named "Ground".
You can then use this simple code to do the test.
BoxCollider2D boxColliderPlayer;
int layerMaskGround;
float heightTestPlayer;
void Start()
{
rb = GetComponent<Rigidbody2D>();
// Get the player's collider so we can calculate the height of the character.
boxColliderPlayer = GetComponent<BoxCollider2D>();
// We do the height test from the center of the player, so we should only check
// halft the height of the player + some extra to ignore rounding off errors.
heightTestPlayer = boxColliderPlayer.bounds.extents.y + 0.05f;
// We are only interested to get colliders on the ground layer. If we would
// like to jump ontop of enemies we should add their layer too (which then of
// course can't be on the same layer as the player).
layerMaskGround = LayerMask.GetMask("Ground");
}
void Update()
{
float moveDir = Input.GetAxis("Horizontal") * moveSpeed;
rb.velocity = new Vector2(moveDir, rb.velocity.y);
// Your jump code:
if (Input.GetKeyDown(KeyCode.Space) && IsGrounded() )
{
rb.velocity = new Vector2(rb.velocity.x, jumpHeight);
}
}
/// <summary>
/// Simple check to see if our character is no the ground.
/// </summary>
/// <returns>Returns <c>true</c> if the character is grounded.</returns>
private bool IsGrounded()
{
// Note that we only check for colliders on the Ground layer (we don't want to hit ourself).
RaycastHit2D hit = Physics2D.Raycast(boxColliderPlayer.bounds.center, Vector2.down, heightTestPlayer, layerMaskGround);
bool isGrounded = hit.collider != null;
// It is soo easy to make misstakes so do a lot of Debug.DrawRay calls when working with colliders...
Debug.DrawRay(boxColliderPlayer.bounds.center, Vector2.down * heightTestPlayer, isGrounded ? Color.green : Color.red, 0.5f);
return isGrounded;
}
As you can see I expect the player character to have a BoxCollider2D - I guess you already have that since the character would otherwise fall through the ground). Be careful so you do not mix normal 3D colliders when you are doing 2D stuff.

Related

How do i assign left and right movement to my 2d game

So I'm new to Csharp, and I am working on this script for a game. It's a 2D game. Ive already assigned jump movement to the game, however, I'm stuck on fixing the movement along the x axis.
I'd really appreciate your help.
using UnityEngine;
// this code uses physics to make player jump
public class Movement2d : MonoBehaviour
{
private bool jumpKeyWasPressed;
private bool movementRight;
private CharacterController characterController;
private Rigidbody2D rigidbodyComponent;
private Vector3 moveSpeed;
// Start is called before the first frame update
void Start()
{
rigidbodyComponent = GetComponent<Rigidbody2D>();
characterController = GetComponent<CharacterController>();
}
// Update is called once per frame
void Update()
{
//Check if space key is pressed down
if (Input.GetKeyDown(KeyCode.Space))
{
jumpKeyWasPressed = true;
}
if (Input.GetKeyDown(KeyCode.RightArrow))
{
movementRight = true;
}
}
private void FixedUpdate()
{
if (jumpKeyWasPressed)
{
//jump action assigned to space key
rigidbodyComponent.AddForce(new Vector2(0, 10), ForceMode2D.Impulse);
jumpKeyWasPressed = false;
}
if (movementRight)
{
//moving right assigned to right arrow
Rigidbody.MovePosition();
}
}
}
If you're using RigidBody.MovePosition, you'll want to move the player according to your speed and the time elapsed since the last frame (to account for frame stutters).
Assuming your horizontal speed is stored in the x-coordinate of your speed vector, you'll want to write:
rigidBodyComponent.MovePosition(new Vector2(moveSpeed.x * Time.deltaTime, 0));
To perform left movement, use the same code, but use -moveSpeed.x instead. In both cases, don't forget to set the movement bool to false afterwards.
Keep in mind, however, that using RigidBody.MovePosition could cause your character to go through walls at high speeds. Consider using RigidBody.AddForce() as you did for jumping if you want to avoid that.
Sources:
https://docs.unity3d.com/ScriptReference/Rigidbody2D.MovePosition.html
https://docs.unity3d.com/ScriptReference/Rigidbody2D.AddForce.html

OnGround Raycast not working after scaling fps character

I am trying to make a FPS Game. In the Movement.cs. The Raycast starts from the center of the Capsule (player's transform.position) and is sent towards Vector3.down, with the limit of playerHeight / 2 + 0.1f, where playerHeight is the height of the CapsuleCollider of the player (Capsule).
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class checkJump : MonoBehaviour
{
private Rigidbody rb;
private float playerHeight = 2f;
private bool isOnGround;
private float jumpSpeed = 400f;
void Start() {
rb = GetComponent<Rigidbody>();
}
void Update()
{
isOnGround = Physics.Raycast(transform.position, Vector3.down, playerHeight / 2 + 0.1f);
Jump();
Debug.Log(isOnGround);
}
void Jump() {
if (Input.GetKeyDown(KeyCode.Space) && isOnGround)
{
rb.AddForce(Vector3.up * jumpSpeed * Time.deltaTime, ForceMode.Impulse);
}
}
}
I disabled all other scripts except "checkJump.cs" one. And when I set the scale value to (4,4,3) or something else, it shows me false in the console and nor does the player Jump when I press Enter. But when I set the scale value to (1,1,1) it shows true. In both cases, the player is on the surface of the ground.
Also tried on another Capsule GameObject, doesn't work.
Here are the images :
https://forum.unity.com/attachments/onescale-png.931606/
https://forum.unity.com/attachments/otherscale-png.931609/
Your isOnGround variable is set to true only when the RayCast hits the ground. Since you've set it to transform.position, it's going to fire from the center of the character. You've also limited the distance it fires to playerHeight / 2 + 0.1, which is 1.1.
So basically, whenever your localScale.y is larger than 1.1, your RayCast no longer reaches the ground, and thus won't ever registering you being on the ground.
To solve this, multiply your RayCast distance by your localScale.y as well.

How can I lock the rotation of a player in Unity?

I'm currently making a 2D game as a beginner and I made a spinning platform. But when it's rotating the player's rotation (z-axis) also changes because it's a child of the platform. I need this when I use moving platforms. Now I want to lock the z-axis of the rotation of the player. I already tried it in 3 different ways, but none of them seems to be working. Does anybody know how to do this?
These are the three ways I tried:
// 1
PlayerTrans.transform.Rotate(
PlayerTrans.transform.rotation.x,
PlayerTrans.transform.rotation.y,
0);
// 2
PlayerTrans.transform.Rotate(
PlayerTrans.transform.rotation.x,
PlayerTrans.transform.rotation.y,
0,
Space.Self);
// 3
PlayerTrans.transform.localRotation = Quaternion.Euler(new Vector3(
PlayerTrans.transform.localEulerAngles.x,
PlayerTrans.transform.localEulerAngles.y,
0f));
and this is, what my code looks like for staying on the moving platforms. I used raycasting for this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Raycasting : MonoBehaviour
{
// Start is called before the first frame update
Transform PlayerTrans;
public float RayCastRange = 3;
void Start()
{
PlayerTrans = transform.parent;
}
// Update is called once per frame
void Update()
{
RaycastHit2D PlattformCheck = Physics2D.Raycast(transform.position, -Vector2.up, RayCastRange);
if (PlattformCheck.collider != null)
{
if (PlattformCheck.collider.gameObject.tag == "Platform")
{
PlayerTrans.transform.SetParent(PlattformCheck.collider.gameObject.transform);
}
else
{
PlayerTrans.transform.SetParent(null);
}
}
else
{
PlayerTrans.transform.SetParent(null);
}
}
}
There are 2 ways that might help you:
Just freeze the rotation from the inspector:
you can use some LookAt function (there is one for 3D but you can look and find ones for 2D) and just look at the camera.
(if you cant find it let me know and I will add it)
You should raycast directly down and then apply velocities to both objects (un-child the player from the platforms). You could do something like this for the player:
public LayerMask mask; //set ‘mask’ to the mask of the
//platform in the Unity Editor.
public Vector3 velocity;
void Update()
{
RaycastHit hit;
if (Physics.Raycast(transform.position, -Vector3.up, out hit, 0.1f, mask))
//0.1f is the distance to the platform to be able to be moved by the platform.
{
velocity = hit.collider.gameObject.GetComponent<Platform>().velocity;
}
float h = Input.GetAxis(“Horizontal”);
//this one is for CharacterController:
cc.Move(velocity);
//this one is for rigidbody:
rb.velocity = velocity;
velocity = 0;
}
It takes the velocity from the ‘Platform’ script and moves the player based on it. Now we should add the platform script. Call it ‘Platform’.
public Vector3 velocity;
public Vector3 a; //a and b are the two points you want the platform to go between.
public Vector3 b;
public float period = 2f; //the amount of seconds per cycle.
float cycles;
void Update()
{
cycles = Time.time / period;
float amplitude = Mathf.Sin(Mathf.PI * 2f * cycles);
Vector3 location = Vector3.Lerp(a, b, amplitude);
velocity = transform.position - location;
transform.position = velocity;
}
This script interpolates between the point a and b, then it finds the velocity and applies it. The player takes this velocity and it moves the player. Comment if you found an error.

My Unity 2D player controller script won't make my character flip when he moves to a different direction

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public float speed;
public float jumpForce;
private float moveInput;
private Rigidbody2D rb;
private bool facingRight = true;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
}
void FixedUpdate()
{
moveInput = Input.GetAxis("Horizontal");
rb.velocity = new Vector2(moveInput * speed, rb.velocity.y);
if(facingRight == false && moveInput > 0)
{
Flip();
}
else if(facingRight == true && moveInput < 0)
{
Flip();
}
}
void Flip()
{
facingRight = !facingRight;
Vector3 Scaler = transform.localScale;
Scaler.x *= -1;
transform.localScale = Scaler;
}
}
I'm made a c# 2D player controller script to use in unity, but when I hit play, my player won't flip and face a different direction when he moves a different direction. Can anyone spot what's wrong in my script?
Nevermind. Was just a dumb bug. I put an outdated AND updated script component into my player. Yes I know I'm stupid.
Unity seems a good choice to developp your game but due to the simplicity of your game, i think it will be easier to create it using Android Studio, it is more easier to handler Character Sprite and Animation. All depend of what kind of game you want to create.
"""For instances guys, i create a simple SkaterBoard game using simple gravity physics with Android Studio, I would really appreciate if you could left a review on it."""
(Android Link) Skater Soldier Game: https://play.google.com/store/apps/details?id=com.fight.exempleclass
Always make sure you double check settings, values, and where you attached scripts : )
I've heard a couple people advise against flipping via scale and instead prefer :
transform.Rotate(0f, 180f, 0f);
Or depending on the gameplay / utility needed using SpriteRenderer.flipX
In case you'd like alternatives.

How to check ground for unity2d platformer game

I am trying to make a 2d plat-former where you see the player from the side. I want him to be continuously moving and you have to press space at the right time so he doesn't fall. Right now everything works but he doesn't collide with the ground. I want it to be like he's running behind a wall so I want to ignore a certain layer I have made and collide with the boxes below that. So far I have tried ray casting, watched multiple tutorials, and did box collisions. Box collisions worked but to get all the platforms counted as solid I'd need like 50 box colliders. Here is my current code:
public int playerSpeed = 10;
public int playerJumpPower = 1250;
public float moveX;
public float playerYSize = 2;
public LayerMask mainGround;
public float playerFallSpeed = 5;
void Awake(){
}
// Update is called once per frame
void Update()
{
RaycastHit2D hit = Physics2D.Raycast(transform.position, new Vector2(10, 0));
if(hit.distance < 0.7f){
print("hi");
}
Vector3 characterTargetPosition = new Vector3(transform.position.x + playerSpeed, transform.position.y, transform.position.z);
transform.position = Vector3.Lerp(transform.position, characterTargetPosition, playerSpeed * Time.deltaTime);
if(Input.GetKeyDown("space")){
// float playerTargetPosY = transform.position.y + playerJumpPower;
// Vector3 characterTargetPosition = new Vector3(transform.position.x, playerTargetPosY, transform.position.z);
// transform.position = Vector3.Lerp(transform.position, characterTargetPosition, playerJumpPower * Time.deltaTime);
gameObject.GetComponent<Rigidbody2D>().AddForce(Vector2.up * playerJumpPower);
}
//PlayerMove();
}
I have a rigidBody2D on my player so right now he just falls through the ground but the jump does work. If there is any easy way to do this. Like some script, a tutorial, or website I'm open for it. Please help.
Do you have a Rigidbody2D in your player? Things that will move usually have to have a RigidBody
(sorry for posting this as an answer. Cant comment yet)
EDIT:
try this:
Rigidbody2D rb;
void Awake()
{
rb = GetComponent<Rigidbody2D>();
}
//Physics usually are done in FixedUpdate to be more constant
public void FixedUpdate(){
if (Input.GetKeyDown("space"))
{
if(!rb.simulated)
//player can fall
rb.simulated = true;
rb.AddForce(Vector2.up * playerJumpPower);
}
else
{
//third argument is the distance from the center of the object where it will collide
//therefore you want the distance from the center to the bottom of the sprite
//which is half of the player height if the center is actually in the center of the sprite
RaycastHit2D hit = Physics2D.Raycast(transform.position, -Vector2.up, playerYSize / 2);
if (hit.collider)
{
//make player stop falling
rb.simulated = false;
}
}
}
If the player is the only thing that will collide with something you can just take out the colliders from the object that the player will not collide with.
Else you can check for the layer of the collided object with hit.collider.gameObject.layer and decide if the player will collide with that layer or not
(note that you have to compare with the index of the layer. If you want to get the index by its name you can use LayerMask.NameToLayer(/*layer name*/))
you will have to do rb.simulated = true everytime you want to do something with the RigidBody (like AddForce())
hope it helped :)

Categories