2D Jumping System not working correctly - Unity 2D - c#

I was working on my 2D game in unity and i made this system to set the character to jump, and move around, but for some reason, i can only move mid air when i was moving on the ground, but if i stand still and then jump, i can not move mid air
Further Explaination: imagine you want to pick a coin in the air, and there is an obstacle under the coin, thus you have to jump while moving forward to pick it up, my problem is that you can only move mid air, if you were moving on the floor originally, but if you jump and THEN move right or left, the system just ignores it
My code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(CapsuleCollider2D))]
public class CharacterController2D : MonoBehaviour
{
// Move player in 2D space
public float maxSpeed = 3.4f;
public float jumpHeight = 6.5f;
public float gravityScale = 1.5f;
public Camera mainCamera;
bool facingRight = true;
float moveDirection = 0;
bool isGrounded = false;
Vector3 cameraPos;
Rigidbody2D r2d;
CapsuleCollider2D mainCollider;
Transform t;
// Use this for initialization
void Start()
{
t = transform;
r2d = GetComponent<Rigidbody2D>();
mainCollider = GetComponent<CapsuleCollider2D>();
r2d.freezeRotation = true;
r2d.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
r2d.gravityScale = gravityScale;
facingRight = t.localScale.x > 0;
if (mainCamera)
{
cameraPos = mainCamera.transform.position;
}
}
// Update is called once per frame
void Update()
{
// Movement controls
if ((Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D)) && (isGrounded || Mathf.Abs(r2d.velocity.x) > 0.01f))
{
moveDirection = Input.GetKey(KeyCode.A) ? -1 : 1;
}
else
{
if (isGrounded || r2d.velocity.magnitude < 0.01f)
{
moveDirection = 0;
}
}
// Change facing direction
if (moveDirection != 0)
{
if (moveDirection > 0 && !facingRight)
{
facingRight = true;
t.localScale = new Vector3(Mathf.Abs(t.localScale.x), t.localScale.y, transform.localScale.z);
}
if (moveDirection < 0 && facingRight)
{
facingRight = false;
t.localScale = new Vector3(-Mathf.Abs(t.localScale.x), t.localScale.y, t.localScale.z);
}
}
// Jumping
if (Input.GetKeyDown(KeyCode.W) && isGrounded)
{
r2d.velocity = new Vector2(r2d.velocity.x, jumpHeight);
}
// Camera follow
if (mainCamera)
{
mainCamera.transform.position = new Vector3(t.position.x, cameraPos.y, cameraPos.z);
}
}
void FixedUpdate()
{
Bounds colliderBounds = mainCollider.bounds;
float colliderRadius = mainCollider.size.x * 0.4f * Mathf.Abs(transform.localScale.x);
Vector3 groundCheckPos = colliderBounds.min + new Vector3(colliderBounds.size.x * 1f, colliderRadius * 0.9f, 0);
// Check if player is grounded
Collider2D[] colliders = Physics2D.OverlapCircleAll(groundCheckPos, colliderRadius);
//Check if any of the overlapping colliders are not player collider, if so, set isGrounded to true
isGrounded = false;
if (colliders.Length > 0)
{
for (int i = 0; i < colliders.Length; i++)
{
if (colliders[i] != mainCollider)
{
isGrounded = true;
break;
}
}
}
// Apply movement velocity
r2d.velocity = new Vector2((moveDirection) * maxSpeed, r2d.velocity.y);
// Simple debug
Debug.DrawLine(groundCheckPos, groundCheckPos - new Vector3(0, colliderRadius, 0), isGrounded ? Color.green : Color.red);
Debug.DrawLine(groundCheckPos, groundCheckPos - new Vector3(colliderRadius, 0, 0), isGrounded ? Color.green : Color.red);
}
}```

if ((Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D)) && (isGrounded || Mathf.Abs(r2d.velocity.x) > 0.01f))
In this line you check if you are grounded isGrounded or have some horizontal velocity Mathf.Abs(r2d.velocity.x) > 0.01f.
If you have any of those you give horizontal control.
So if you are moving horizontal en then jump Mathf.Abs(r2d.velocity.x) > 0.01f is true and gives you control in the air.
But if you are standing still and the jump isGrounded is false bequase you are in the air and Mathf.Abs(r2d.velocity.x) > 0.01f is false so you cant control your jump in the air.
If you always want control in the air remove the (isGrounded || Mathf.Abs(r2d.velocity.x) > 0.01f) check

Related

Allow swipeing only to upwards unity

My code below works for detecting swipes and adding force to my game object. However, my aim is to let the user swipe only upwards(Can be up left, up right but never to down). I mean only if user swipes up, AddForce method will be called. When user swipes down nothing will happen. Is it possible to do that, if yes how?
public class SwipeScript : MonoBehaviour
{
Vector2 startPos, endPos, direction;
float touchTimeStart, touchTimeFinish, timeInterval;
[Range(0.05f, 1f)]
public float throwForse = 0.5f;
void Update()
{
if (Input.touchCount > 0 && Input.GetTouch (0).phase == TouchPhase.Began)
{
touchTimeStart = Time.time;
startPos = Input.GetTouch(0).position;
}
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Ended)
{
touchTimeFinish = Time.time;
timeInterval = touchTimeFinish - touchTimeStart;
endPos = Input.GetTouch(0).position;
direction = startPos - endPos;
GetComponent<Rigidbody2D>().AddForce(-direction / timeInterval * throwForse);
}
}
}
Why wouldn't you just clamp the direction to be nonnegative? For instance, before the last line add something like:
This would be for upward and right:
direction = Vector2.Max(direction, Vector2.Zero);
and this would be for all upward:
direction = new Vector2(direction.x, Math.Max(direction.y, 0));
and I suggest you use
direction = endPos - startPos;
instead of the other way as in your original code, as this way it represents that movement vector correctly. Then remove the negative sign in AddForce in front of the direction as well. Thus:
void Update()
{
if (Input.touchCount > 0 && Input.GetTouch (0).phase == TouchPhase.Began)
{
touchTimeStart = Time.time;
startPos = Input.GetTouch(0).position;
}
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Ended)
{
touchTimeFinish = Time.time;
timeInterval = touchTimeFinish - touchTimeStart;
endPos = Input.GetTouch(0).position;
Vector2 direction = endPos - startPos;
direction = new Vector2(direction.x, Math.Max(direction.y, 0));
GetComponent<Rigidbody2D>().AddForce(direction / timeInterval * throwForse);
}
}

Unity new Input-System and double jumping local multiplayer

So I have some trouble using the new Input System on a game I'm currently working on.
The game is going to be some type of an Smash game and I already had implemented double jump with the old Input System and it worked but now I've changed it to the new one and I can just jump one time in Air again I know it must be some kind of an easy fix problem but im a beginner and I don't really understand the new I- System. Here's my Code:
I would really appreciate if you could help me out with this and eventually I can learn something of ur comment. (Btw. sorry for my english I'm not native lol)
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerController : MonoBehaviour
{
public float speed;
public float jumpForce;
public float moveInput;
public float checkRadius;
private int extraJumps;
public int extraJumpsValue;
private Rigidbody2D rb;
private bool facingRight = true;
private bool isGrounded;
public Transform groundCheck;
public LayerMask whatIsGround;
private Vector2 movementInput = Vector2.zero;
private bool jumped = false;
//OnMove Input to later set as Event in Input Manager
public void OnMove(InputAction.CallbackContext context)
{
movementInput = context.ReadValue<Vector2>();
}
//OnJump Input to later set as Event in Input Manager
public void OnJump(InputAction.CallbackContext context)
{
jumped = context.action.triggered;
}
//start
private void Start()
{
extraJumps = extraJumpsValue;
rb = GetComponent<Rigidbody2D>();
}
private void FixedUpdate()
{
//check if isGrounded
isGrounded = Physics2D.OverlapCircle(groundCheck.position, checkRadius,
whatIsGround);
moveInput = movementInput.x;
//Move Right/Left relative to moveInput Value (-1 till 1)
rb.velocity = new Vector2(moveInput * speed, rb.velocity.y);
//rotate Player Sprite relative to a / d Input
if(facingRight == false && moveInput > 0)
{
Flip();
}
else if(facingRight == true && moveInput <0)
{
Flip();
}
}
private void Update()
{
// if grounded set extra jumps u can take to a preset value
if(isGrounded == true)
{
extraJumps = extraJumpsValue;
}
//if extrajumps is equal to 0 & player tries to jump and is grounded add upwards velocity
if (extraJumps == 0 && jumped && isGrounded == true)
{
rb.velocity = Vector2.up * jumpForce;
}
//subtract extra jump -1 and "jump"
else if (extraJumps > 0 && jumped)
{
rb.velocity = Vector2.up * jumpForce;
extraJumps--;
}
}
//rotate player
void Flip()
{
facingRight = !facingRight;
transform.Rotate(0f, 180f, 0f);
}
}
I think the problem is in the condition to execute the first jump :
//When isGrouded then extraJumps equal 1 (in the case extraJumpsValue is 1)
if(isGrounded == true)
{
extraJumps = 1;
}
//The next condition is always false
//Because when isGrouded then extraJumps is never 0
if (isGrounded == true && extraJumps == 0 && /*Some other condition*/)
...
I don't untertand why extraJumps is checked to do the first jump. I think you can remove this operand :
if (/*extraJumps == 0 &&*/ jumped && isGrounded == true)
{
rb.velocity = Vector2.up * jumpForce;
}
Full code :
private void Update()
{
// if grounded set extra jumps u can take to a preset value
if(isGrounded == true)
{
extraJumps = extraJumpsValue;
}
//if player tries to jump and is grounded add upwards velocity
if (jumped && isGrounded == true)
{
rb.velocity = Vector2.up * jumpForce;
}
//subtract extra jump -1 and "jump"
else if (extraJumps > 0 && jumped)
{
rb.velocity = Vector2.up * jumpForce;
extraJumps--;
}
}

JumpCount resets immediately after jumping 2D platformer

I'm pretty new to all of this so sorry for rookie mistakes. I'm making a 2D platformer
I am checking for ground via RayCast and right after the player jumps, the jumpCounter resets to 0, so I get a bonus jump. I tried adding a 0.2s timer before it can reset but that caused trouble when jumping multiple times in a row (bunny hopping). Any help?
private void FixedUpdate()
{
GroundCheck();
if (state != State.hurt)
{
Movement();
DoubleJump();
StateMachine();
}
HurtCheck();
anim.SetInteger("state", (int)state);
}
private void Movement()
{
//Input.GetAxis returns a value between -1 up to 1
//Edit> Project Settings> Input> Axis> Horizontal
float hDirection = Input.GetAxisRaw("Horizontal");
//holding down "D" makes the value positive and vice versa
if (hDirection < 0)
{
rb.velocity = new Vector2(-speed, rb.velocity.y);
transform.localScale = new Vector2(-1, 1);
}
else if (hDirection > 0)
{
rb.velocity = new Vector2(speed, rb.velocity.y);
transform.localScale = new Vector2(1, 1);
}
else
{
}
if (Input.GetButtonDown("Jump") && isGrounded == true)
{
Jump();
}
}
private void Jump()
{
rb.velocity = new Vector2(rb.velocity.x, jumpforce);
state = State.jumping;
jumpCount += 1;
}
private void GroundCheck()
{
Debug.DrawRay(transform.position, Vector2.down * hitDistance, Color.green);
RaycastHit2D hit = Physics2D.Raycast(transform.position, Vector2.down, hitDistance, ground);
if(hit.collider != null)
{
isGrounded = true;
jumpCount = 0;
}
else
{
isGrounded = false;
}
}
private void DoubleJump()
{
if (Input.GetButtonDown("Jump") && isGrounded == false && jumpCount < 2)
{
rb.velocity = new Vector2(rb.velocity.x, jumpforce);
jumpCount += 1;
}
}
Hey Have you made sure your player's layer isn't ground layer or set ground layermask properly or maybe the hitDistance for the raycast is too high it should be very low like 0.1 or less or does jumpCount start from 0 or 1 try and alternate that or maybe have it start from two to lose the extra bonus jump...These would be where I would look, Good luck!

how can i use a for loop so my character can double jump?

I am trying to code a platforming game as right now all my character model can do is either jump infinitely or doesn't jump at all. I want to use a for loop so my character can jump one when grounded and once more when he is in the air but I cant figure out how to make it stop after double jumping once and resetting when the character hits the floor again. please help!!
public class SimplePlatformController : MonoBehaviour
{
[HideInInspector]
public bool facingRight = true;
[HideInInspector]
public bool jump = false;
public float moveForce = 365f;
public float maxSpeed = 5f;
public float jumpForce = 1000f;
public Transform groundCheck;
private bool grounded = false;
private Animator anim;
private Rigidbody2D rb2d;
// Use this for initialization
void Awake()
{
anim = GetComponent<Animator>();
rb2d = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
for (int i = 0; i <= 1; i++)
{
grounded = Physics2D.Linecast(transform.position, groundCheck.position, 1 << LayerMask.NameToLayer("Ground"));
if (Input.GetButtonDown("Jump") && grounded)
{
i = 0;
jump = true;
}
if (Input.GetButtonDown("Jump") && !grounded)
{
jump = true;
i = i + 1;
}
}
}
void FixedUpdate()
{
float h = Input.GetAxis("Horizontal");
anim.SetFloat("Speed", Mathf.Abs(h));
if (h * rb2d.velocity.x < maxSpeed)
rb2d.AddForce(Vector2.right * h * moveForce);
if (Mathf.Abs(rb2d.velocity.x) > maxSpeed)
rb2d.velocity = new Vector2(Mathf.Sign(rb2d.velocity.x) * maxSpeed, rb2d.velocity.y);
if (h > 0 && !facingRight)
Flip();
else if (h < 0 && facingRight)
Flip();
if (jump)
{
anim.SetTrigger("Jump");
rb2d.AddForce(new Vector2(0f, jumpForce));
jump = false;
}
}
void Flip()
{
facingRight = !facingRight;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
}
A for loop is inappropriate for this, because you only want to advance the counter if you jump. and you need to leave the loop when it's time for the frame to end. You could in theory make it happen with a while loop in a Coroutine but that is unnecessarily complicated.
A better alternative is to just keep a counter as a class field and update it appropriately, according to the double jump state.
Also, since the if statement is being reached on every frame, you have to check if you have any more air jumps before you double jump.
If you want to be able to double-jump after you simply walk off a platform, you'll want to set the jump counter to 0 anytime grounded is set to be true.
Combining all of these suggestions might look like this:
public class SimplePlatformController : MonoBehaviour
{
// ...
private int airJumpCount = 0; // Add this counter
// ...
// Update is called once per frame
void Update()
{
grounded = Physics2D.Linecast(
transform.position, groundCheck.position,
1 << LayerMask.NameToLayer("Ground"));
if (grounded) airJumpCount = 0; // reset the counter when grounded
if (Input.GetButtonDown("Jump") && grounded)
{
jump = true;
}
// Only enter the air jump block if we still have more air jumps
if ( Input.GetButtonDown("Jump") && !grounded && airJumpCount < 1)
{
airJumpCount++;
jump = true;
}
}
// ...
}

Unity 5.1.1 skips "Input.GetKeyDown" line sometimes

I use Unity 5.1.1 on a Win 64 bit machine, more than capable to run what i'm creating of games. In the making of a 2D sidescroller, i found my character sometimes doesn't jump when prompted. Here is the code:
public float speed;
public float MomentAcc;
private float moveVertical;
private float score;
private float scoreP;
public GameObject wallRight;
public GUIText scoreText;
public bool touching;
void Start() {
MomentAcc = 10;
score = 0;
}
//Jump limiter
void OnCollisionStay2D() {
touching = true;
}
void OnCollisionExit2D() {
touching = false;
}
void Update() {
if (Input.GetKeyDown(KeyCode.W) && touching == true || Input.touchCount > 0 && Input.GetTouch (0).phase == TouchPhase.Began && touching == true) {
moveVertical = 29;
} else {
moveVertical = 0;
}
}
void FixedUpdate () {
scoreP = GameObject.Find ("Ball").GetComponent<Rigidbody2D> ().position.x + 11;
if(scoreP > score) {
score = score + 10;
}
UpdateScore ();
if(GetComponent<Death>().startGame == true) {
float moveHorizontal = 5;
Vector2 forward = new Vector2 (moveHorizontal, 0);
Vector2 jump = new Vector2 (0, moveVertical);
//Maxspeed limit
GetComponent<Rigidbody2D> ().AddForce (moveVertical * jump);
speed = moveHorizontal * MomentAcc * Time.deltaTime * 5;
if (GetComponent<Rigidbody2D> ().velocity.x < 7.000000) {
GetComponent<Rigidbody2D> ().AddForce (Vector2.right * speed);
if(GameObject.Find ("wallRight").GetComponent<wallRightScript>().wallJumpRight == true) {
GetComponent<Rigidbody2D> ().AddForce (new Vector2 (-420, 300));
}
if(GameObject.Find ("wallLeft").GetComponent<wallLeftScript>().wallJumpLeft == true) {
GetComponent<Rigidbody2D> ().AddForce (new Vector2(420, 150));
}
}
}
}
void UpdateScore() {
scoreText.text = "Score: " + (score );
}
}
(Sidenote: wallLeft/wallRight are for walljumping)
Here is your problem!
you are using Input.GetKeyDown(KeyCode.W) && touching == true in this case your jump is dependent on touching variable which can be false at the time when you press the "W" key. You are using rigidbody you cannot expect it to always colliding with ground when it is being dragged in horizontal. So you may need to change your implementation for ground check.
This Tutorial is good for learning about 2D character.
And one more advice! try to store reference of objects/components in some variables to access them easily. Using GetComponent<>/GameObject.Find() in Update()/FixedUpdate() is not efficient so not a good practice.

Categories