So I've created crouching in Unity 2D game, everything goes well except when there is something above the character after exiting crouching, what it does is that it returns to idle animation and decreases Y-axis position, I know that is normal but how can I keep the character crouched when there is something above him or when I hold the ctrl button? Check the script down below if necessary. Thanks!
private Rigidbody2D rb2d;
private float h = 0.0f;
public float Speed, Jump;
private bool canJump;
private Animator anim;
private BoxCollider2D bc2d;
// Start is called before the first frame update
void Start()
{
rb2d = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
bc2d = GetComponent<BoxCollider2D>();
}
// Update is called once per frame
void Update()
{
h = Input.GetAxisRaw("Horizontal");
anim.SetFloat("Speed", Mathf.Abs(h));
transform.Translate(Vector3.right * h * Speed * Time.deltaTime);
if (h != 0.0f)
{
transform.localScale = new Vector2(h, transform.localScale.y);
}
if (Input.GetKeyDown(KeyCode.Space) && canJump == true)
{
rb2d.AddForce(new Vector2(rb2d.velocity.x, Jump));
}
if (Input.GetKeyDown(KeyCode.LeftControl))
{
bc2d.enabled = false;
Speed = Speed / 2;
anim.SetBool("Crouch", true);
}
else if (Input.GetKeyUp(KeyCode.LeftControl))
{
bc2d.enabled = true;
Speed = Speed * 2;
anim.SetBool("Crouch", false);
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.collider.name == "Ground")
{
canJump = true;
anim.SetBool("Jump", false);
}
}
private void OnCollisionExit2D(Collision2D collision)
{
if (collision.collider.name == "Ground")
{
canJump = false;
anim.SetBool("Jump", true);
}
}
Use RaycastHit2D. Basically before "standing" cast a ray Vector2.up from the character and if it hits, check to make sure there's enough room to stand. Something like:
private Rigidbody2D rb2d;
private Transform t;
private float h = 0.0f;
public float Speed, Jump;
private bool canJump;
private Animator anim;
private BoxCollider2D bc2d;
// Variable to check if character is attempting to stand
private bool tryingToStand = false;
// Keep crouching state
private bool isCrouching = false;
// Start is called before the first frame update
void Start()
{
rb2d = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
bc2d = GetComponent<BoxCollider2D>();
t = GetComponent<Transform>();
}
// Update is called once per frame
void Update()
{
h = Input.GetAxisRaw("Horizontal");
anim.SetFloat("Speed", Mathf.Abs(h));
transform.Translate(Vector3.right * h * Speed * Time.deltaTime);
if (h != 0.0f)
{
transform.localScale = new Vector2(h, transform.localScale.y);
}
// Make sure we can jump and we're not crouching before jumping
if (Input.GetKeyDown(KeyCode.Space) && canJump == true && !isCrouching)
{
rb2d.AddForce(new Vector2(rb2d.velocity.x, Jump));
}
if (Input.GetKeyDown(KeyCode.LeftControl))
{
bc2d.enabled = false;
anim.SetBool("Crouch", true);
// Make sure we haven't already halved the speed
if (!tryingToStand)
{
Speed = Speed / 2;
}
// Set tryingToStand to false in case player holds ctrl before character has stood up
tryingToStand = false;
// Set crouching state to true
isCrouching = true;
}
else if (Input.GetKeyUp(KeyCode.LeftControl))
{
tryingToStand = true;
}
if (tryingToStand && CanStand())
{
tryingToStand = false;
// Set crouching state to false;
isCrouching = false;
bc2d.enabled = true;
Speed = Speed * 2;
anim.SetBool("Crouch", false);
}
}
bool CanStand()
{
RaycastHit2D hit = Physics2D.Raycast(t.position, Vector2.up);
if (hit.collider != null)
{
// Check the distance to make sure the character has clearance, you'll have to change the 1.0f to what makes sense in your situation.
if (hit.distance <= 1.0f)
{
return false;
}
}
return true;
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.collider.name == "Ground")
{
canJump = true;
anim.SetBool("Jump", false);
}
}
private void OnCollisionExit2D(Collision2D collision)
{
if (collision.collider.name == "Ground")
{
canJump = false;
anim.SetBool("Jump", true);
}
}
Related
I don't know how to make my character dash without it keeps ignoring the cooldown and the dash distance is small. I have been stumped on this for a long time and i want to continue with other things in my game but this has been stopping me. I use Unity and C#.
Here's my code:
public class PlayerMove : MonoBehaviour
{
private Rigidbody2D rb;
private BoxCollider2D bc;
private static bool isRunning;
private static bool isMoving;
private static bool isDashing;
private static bool isOnWall;
private static bool cooldown;
private static bool jumpCooldown;
private float time = 4f;
private float dashingspeed = 14f;
private float timewhiledashing = 2f;
private Vector2 dashingDir;
// Start is called before the first frame update
private void Start()
{
rb = GetComponent<Rigidbody2D>();
bc = GetComponent<BoxCollider2D>();
cooldown = false;
}
// Update is called once per frame
private void Update()
{
bool IsGrounded()
{
float extraHeightText = .01f;
RaycastHit2D raycastHit = Physics2D.Raycast(bc.bounds.center, Vector2.down, bc.bounds.extents.y + extraHeightText);
Color rayColor;
if (raycastHit.collider != null)
{
rayColor = Color.green;
}
else
{
rayColor = Color.red;
}
Debug.DrawRay(bc.bounds.center, Vector2.down * (bc.bounds.extents.y + extraHeightText));
return raycastHit.collider != null;
}
float dirX = Input.GetAxisRaw("Horizontal");
rb.velocity = new Vector2(dirX * 4f, rb.velocity.y);
isMoving = true;
float dirY = Input.GetAxisRaw("Vertical");
if (Input.GetKey("left ctrl"))
isRunning = true;
else
{
isRunning = false;
}
if (Input.GetKeyDown("left shift") && cooldown == false)
isDashing = true;
else
{
isDashing = false;
}
if (IsGrounded() && Input.GetButtonDown("Jump"))
{
rb.velocity = new Vector2(rb.velocity.x, 8f);
}
if (isRunning == true && isMoving == true)
{
rb.velocity = new Vector2(dirX * 6f, rb.velocity.y);
}
if (isDashing == true && cooldown == false)
{
dashingDir = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
if (dashingDir == new Vector2(0f,0f))
{
dashingDir = new Vector2(transform.localScale.x, 0f);
}
}
if (isDashing == true)
{
rb.velocity = dashingDir.normalized * dashingspeed;
return;
}
if (isDashing == true)
{
timewhiledashing -= 1 * Time.deltaTime;
}
if (timewhiledashing == 0f)
{
cooldown = true;
}
if (IsGrounded())
{
cooldown = false;
}
if (cooldown == true)
{
isDashing = false;
time -= 1 * Time.deltaTime;
}
if (time == 0f)
{
cooldown = false;
time = 4f;
timewhiledashing = 2f;
}
}
}
Please help me with this.
The reason your code is not dashing is because it detects if you get the key down, and if not it will be true again. So if you press the key, it will be true, then next frame it will be false.
This way, time while dashing will only go down for one frame.
Instead, you should use
if (Input.GetKey(KeyCode.LeftShift))
I've got my player sprite that has a blank "Front Check" object nested underneath it. The nested item is used to check if the front of the sprite is touching a wall. the problem I'm having is the Front Check object does not flip with the sprite when I move in the opposite direction. it was working fine earlier today and then out of the blue just quit. This is my first unity project and I'm just not quite sure what's missing. Any help is much appreciated. here is an image of the project and my movement script.
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
private Rigidbody2D rb;
private BoxCollider2D coll;
private SpriteRenderer sprite;
private Animator anim;
[SerializeField] private LayerMask jumpableGround;
public float checkRadius;
bool isTouchingFront;
public Transform frontCheck;
public float wallSlidingSpeed;
bool wallSliding;
bool wallJumping;
public float xWallForce;
public float yWallForce;
public float wallJumpTime;
private float dirX = 0f;
[SerializeField] private float moveSpeed = 7f;
[SerializeField] private float jumpForce = 14f;
[SerializeField] private float airControl;
private enum MovementState { idle, running, jumping, falling }
[SerializeField] private AudioSource JumpSoundEffect;
// Start is called before the first frame update
private void Start()
{
rb = GetComponent<Rigidbody2D>();
coll = GetComponent<BoxCollider2D>();
sprite = GetComponent<SpriteRenderer>();
anim = GetComponent<Animator>();
}
// Update is called once per frame
private void Update()
{
dirX = Input.GetAxisRaw("Horizontal");
if (dirX < 0 && IsGrounded())
{
rb.velocity = new Vector2(moveSpeed * -1, rb.velocity.y);
}
else if (dirX > 0 && IsGrounded())
{
rb.velocity = new Vector2(moveSpeed, rb.velocity.y);
}
else if (dirX == 0 && IsGrounded())
{
rb.velocity = new Vector2(0, rb.velocity.y);
}
if (Input.GetButtonDown("Jump") && IsGrounded())
{
JumpSoundEffect.Play();
rb.velocity = new Vector2(rb.velocity.x, jumpForce);
}
UpdateAnimationState();
}
private void UpdateAnimationState()
{
MovementState state;
if (dirX > 0f && IsGrounded())
{
state = MovementState.running;
}
else if (dirX < 0f && IsGrounded())
{
state = MovementState.running;
}
else
{
state = MovementState.idle;
}
if (rb.velocity.y > .1f)
{
state = MovementState.jumping;
}
else if (rb.velocity.y < -.1f)
{
state = MovementState.falling;
}
//Wall Jump
isTouchingFront = Physics2D.OverlapCircle(frontCheck.position, checkRadius, jumpableGround);
if (isTouchingFront == true && IsGrounded() == false && dirX != 0)
{
wallSliding = true;
}
else
{
wallSliding = false;
}
if (wallSliding)
{
rb.velocity = new Vector2(rb.velocity.x, Mathf.Clamp(rb.velocity.y, -wallSlidingSpeed, float.MaxValue));
}
if (Input.GetButtonDown("Jump") && wallSliding == true)
{
wallJumping = true;
Invoke("SetWallJumpingToFalse", wallJumpTime);
}
if (wallJumping == true)
{
rb.velocity = new Vector2(xWallForce * -Input.GetAxisRaw("Horizontal"), yWallForce);
}
anim.SetInteger("state", (int)state);
}
void SetWallJumpingToFalse()
{
wallJumping = false;
}
public bool IsGrounded()
{
return Physics2D.BoxCast(coll.bounds.center, coll.bounds.size, 0f, Vector2.down, .1f, jumpableGround);
}
public void FixedUpdate()
{
if (dirX > 0f)
{
sprite.flipX = false;
}
else if (dirX < 0f)
{
sprite.flipX = true;
}
}
}
sprite.flipX only flips the rendering not the actual object scaling.
You rather want to use e.g.
var scale = transform.localScale;
scale.x = Mathf.Sign(dirX);
transform.localScale = scale;
Note that it needs to be UnityEngine.Mathf.Sign and not System.Math.Sign since the latter returns 0 for parameter 0 which would totally break your colliders etc. while Mathf.Sign returns only -1 or 1.
so i'm trying to achieve a player thats stops whenever the user lets go of the key, i don't want it to slide, i have a vertical variable and a horizontal variable that stores the axis, if these values are less or more than zero, than the bool isMoving is set to true, else it's set to false, this works wonderful on the vertical azis but not on the horizontal axis, i tried even making if statements checking when the keys are pressed and when they are not
like this
if(Input.GetKeyDown(KeyCode.A))
{
isMoving = true;
}
if(Input.GetKeyDown(KeyCode.D))
{
isMoving = true;
}
if(Input.GetKeyUp(KeyCode.A))
{
isMoving = false;
}
if(Input.GetKeyUp(KeyCode.D))
{
isMoving = false;
}
this also did not work,
this is my script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
[Header("Player Values")]
[SerializeField] private float playerSpeed = 50f;
[SerializeField] private float jumpHeight = 7f;
[Header("Ground Check")]
[SerializeField] private Transform groundCheck;
[SerializeField] private LayerMask ground;
private float checkLenght = 0.1f;
private Rigidbody rb;
private float horizontal;
private float vertical;
// Crouch Scales
private Vector3 initialScale;
private Vector3 crouchScale;
// Counter Movement
private bool isMoving;
private bool isOnGround;
private float counterDrag = 10f;
private void Start()
{
rb = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
// Crouch Scales
initialScale = new Vector3(1f, 1f, 1f);
crouchScale = new Vector3(1f, 0.5f, 1f);
// Player Movement
MovePlayer();
// Counter Movement
ApplyCounterMovement();
// Check Ground
isOnGround = Physics.CheckSphere(groundCheck.position, checkLenght, ground);
}
private void Update()
{
CheckInput();
}
private void MovePlayer()
{
horizontal = Input.GetAxis("Horizontal") * playerSpeed;
vertical = Input.GetAxis("Vertical") * playerSpeed;
Vector3 movement = transform.forward * vertical + transform.right * horizontal;
rb.AddForce(movement, ForceMode.Acceleration);
}
private void CheckInput()
{
if(Input.GetKeyDown(KeyCode.LeftShift))
{
getDown();
}
if(Input.GetKeyUp(KeyCode.LeftShift))
{
getUp();
}
// Check Counter Movement
if (horizontal > 0f || horizontal < 0f)
{
isMoving = true;
}
else
{
isMoving = false;
}
if (vertical > 0f || vertical < 0f)
{
isMoving = true;
}
else
{
isMoving = false;
}
// Check Jump
if(Input.GetKeyDown(KeyCode.Space))
{
Jump();
}
}
// Crouch
private void getDown()
{
transform.localScale = crouchScale;
transform.position = new Vector3(transform.position.x, transform.position.y - 0.5f, transform.position.z);
}
// Get Up From Crouching
private void getUp()
{
transform.localScale = initialScale;
transform.position = new Vector3(transform.position.x, transform.position.y + 0.5f, transform.position.z);
}
// Apply Counter Movement
private void ApplyCounterMovement()
{
if(isMoving == true)
{
rb.drag = 0f;
return;
}
if(isMoving == false)
{
rb.drag = counterDrag;
}
}
private void Jump()
{
if (isOnGround == false)
{
return;
}
if(isOnGround == true)
{
rb.AddForce(Vector3.up * jumpHeight, ForceMode.VelocityChange);
}
}
}
Your if checks for the vertical into overrule the ones before for the horizontal since both of them set the same field isMoving.
Rather combine them into e.g.
isMoving = horizontal != 0f || vertical != 0f;
For whatever reason, my player movement stutters every second or every other second, and I'm sure it's to do with my player movement code being in the wrong update method.
Here's my player movement code
[SerializeField] private LayerMask groundLayerMask;
public float speed;
public float Jump;
public sword swordScript;
public GameObject swordSprite;
private float move;
private Rigidbody2D rb;
private BoxCollider2D boxCollider2d;
private bool facingRight;
public SpriteRenderer spr;
public Animator PlayerAnims;
public bool movementAllowed;
public float knockback;
void Awake()
{
boxCollider2d = GetComponent<BoxCollider2D>();
rb = GetComponent<Rigidbody2D>();
facingRight = true;
spr = GetComponent<SpriteRenderer>();
}
// Start is called before the first frame update
void Start()
{
boxCollider2d = GetComponent<BoxCollider2D>();
rb = GetComponent<Rigidbody2D>();
facingRight = true;
spr = GetComponent<SpriteRenderer>();
}
// Update is called once per frame
void FixedUpdate()
{
if(movementAllowed == true)
{
rb.velocity = new Vector2(move * speed, rb.velocity.y);
move = Input.GetAxisRaw("Horizontal");
rb.velocity = new Vector2(move * speed, rb.velocity.y);
if (isGrounded() && Input.GetButtonDown("Jump"))
{
rb.AddForce(new Vector2(rb.velocity.x, Jump));
}
}
}
void Update()
{
if (movementAllowed == true)
{
Flip(move);
if (move == 0)
{
PlayerAnims.SetBool("isRunning", false);
}
else
{
PlayerAnims.SetBool("isRunning", true);
}
}
}
private bool isGrounded()
{
float extraHeightText = .1f;
RaycastHit2D raycasthit2d = Physics2D.BoxCast(boxCollider2d.bounds.center, boxCollider2d.bounds.size, 0f, Vector2.down, extraHeightText, groundLayerMask);
Color rayColour;
if (raycasthit2d.collider != null)
{
rayColour = Color.green;
PlayerAnims.SetBool("isJumping", false);
}
else
{
rayColour = Color.red;
PlayerAnims.SetBool("isJumping", true);
}
Debug.DrawRay(boxCollider2d.bounds.center + new Vector3(boxCollider2d.bounds.extents.x, 0), Vector2.down * (boxCollider2d.bounds.extents.y + extraHeightText), rayColour);
Debug.DrawRay(boxCollider2d.bounds.center - new Vector3(boxCollider2d.bounds.extents.x, 0), Vector2.down * (boxCollider2d.bounds.extents.y + extraHeightText), rayColour);
Debug.DrawRay(boxCollider2d.bounds.center - new Vector3(boxCollider2d.bounds.extents.x, boxCollider2d.bounds.extents.y + extraHeightText), Vector2.right * (boxCollider2d.bounds.extents.x), rayColour);
return raycasthit2d.collider != null;
}
private void Flip(float move)
{
if (move > 0 && !facingRight || move < 0 && facingRight)
{
facingRight = !facingRight;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
if (swordScript.isFollowing == true)
{
Vector3 swordScale = swordSprite.transform.localScale;
swordScale.x *= -1;
swordSprite.transform.localScale = swordScale;
}
}
}
I tried out your script and I think I barely see that stutter, but one thing seems to fix it.
You have this particular line twice in the code for no reason
rb.velocity = new Vector2(move * speed, rb.velocity.y);
Just remove the first one.
As a rule of thumb:
Calling rb.AddForce: This interacts with Physics - place it in FixedUpdate
Setting rb.velocity: This only sets a value. You are not triggering physics interactions with this - place it in Update
So, at first you should move your code which sets the velocity to the Update function. Also you are setting the velocity twice. Just remove the first line.
As an example here ist the Update and FixedUpdate function:
void FixedUpdate()
{
if(movementAllowed == true)
{
if (isGrounded() && Input.GetButtonDown("Jump"))
{
rb.AddForce(new Vector2(rb.velocity.x, Jump));
}
}
}
void Update()
{
if (movementAllowed == true)
{
float move = Input.GetAxisRaw("Horizontal");
rb.velocity = new Vector2(move * speed, rb.velocity.y);
Flip(move);
if (move == 0)
{
PlayerAnims.SetBool("isRunning", false);
}
else
{
PlayerAnims.SetBool("isRunning", true);
}
}
}
Since you are not using move anywhere else than Update you can just declare it as a local variable and remove the private float move; at the top of the script.
Last thing which should be mentioned:
You are getting component references both in Start or Awake. Generally you should get your references in Start and only get them in Awake when needed.
I have problem when making a 2d platformer game with Unity 5 about cannot perform double jump and just perform one jump for my playable character. Please check out what's wrong from my script below:
Player Movement.cs
using UnityEngine;
using System.Collections;
public class PlayerMovement : MonoBehaviour {
public float forwardSpeed = 1f;
public float jumpHeight;
bool doJump = false;
bool canJumpAgain;
bool isGrounded;
float deathCooldown;
Animator animator;
Rigidbody2D myRigidbody;
void Awake () {
myRigidbody = GetComponent<Rigidbody2D> ();
}
// Use this for initialization
void Start () {
animator = transform.GetComponentInChildren<Animator>();
if(animator == null) {
Debug.LogError("Didn't find animator!");
}
}
// Update is called once per frame
void Update () {
isGrounded = true;
//for movement with infinite run to right
myRigidbody.AddForce( Vector2.right * forwardSpeed );
transform.rotation = Quaternion.Euler(0, 0, 0);
}
void OnCollisionStay2D (Collision2D col) {
if (col.gameObject.tag == "Ground") {
if (Input.GetKeyDown (KeyCode.Space) || Input.GetMouseButtonDown (0) && isGrounded && !doJump) {
myRigidbody.velocity = new Vector2 (GetComponent<Rigidbody2D>().velocity.x, jumpHeight);
animator.SetTrigger("DoJump");
isGrounded = false;
canJumpAgain = true;
doJump = true;
}
//perform double jump but failed
if (Input.GetKeyDown (KeyCode.Space) || Input.GetMouseButtonDown (0) && doJump && !isGrounded) {
myRigidbody.velocity = Vector2.zero;
myRigidbody.velocity = new Vector2 (GetComponent<Rigidbody2D>().velocity.x, jumpHeight);
animator.SetTrigger("DoJump");
Debug.Log ("yes");
canJumpAgain = true;
doJump = false;
}
if (canJumpAgain) {
//myRigidbody.velocity = Vector2.zero;
myRigidbody.AddForce(Vector2.up * jumpHeight);
animator.SetTrigger("DoJump");
Debug.Log ("yes1");
}
}
}
void OnCollisionExit2D (Collision2D col) {
if (col.gameObject.tag == "Ground") {
isGrounded = false;
GetComponent<CircleCollider2D>().isTrigger = false;
}
transform.parent = null;
}
void OnTriggerEnter2D(Collider2D Other)
{
}
}
I'm pretty sure this will fix the problem you're facing. Do not set isGrounded to true every Update() call. On the OnTriggerEnter2D() you wan't to have something like this: if (other.gameObject.tag == "ground") isGrounded = true; On the OnTriggerExit2D() you need to have this: if (other.gameObject.tag == "ground") isGrounded = false; Set the floor tag in the Unity inspector to ground. Explained: Before each double jump you're checking if isGrounded is false, however this will be always true since you're making it true every frame.
Finally I can resolved with my way please check out my script below:
using UnityEngine;
using System.Collections;
public class PlayerMovement : MonoBehaviour {
public float forwardSpeed = 1f;
public float jumpHeight;
bool doJump;
bool canJumpAgain;
bool isGrounded;
float deathCooldown;
Animator animator;
Rigidbody2D myRigidbody;
private bool canJump = false;
private int availableJumps = 1;
float jumpForce = 0f; // Force added when the player jumps.
public float firstJumpForce = 0f;
public float secondJumpForce = 0f;
void Awake () {
myRigidbody = GetComponent<Rigidbody2D> ();
}
// Use this for initialization
void Start () {
animator = transform.GetComponentInChildren<Animator>();
if(animator == null) {
Debug.LogError("Didn't find animator!");
}
}
// Update is called once per frame
void Update () {
isGrounded = true;
//for movement with infinite run to right
myRigidbody.AddForce( Vector2.right * forwardSpeed );
transform.rotation = Quaternion.Euler(0, 0, 0);
if (Input.GetKeyDown (KeyCode.Space) || Input.GetMouseButtonDown (0) && isGrounded) {
doJump = true;
}
}
void FixedUpdate () {
if(doJump && canJump)
{
// Add a vertical force to the player.
myRigidbody.velocity = new Vector2 (GetComponent<Rigidbody2D>().velocity.x, jumpHeight);
animator.SetTrigger("DoJump");
// Can the player jump again??
availableJumps--;
if (availableJumps <= 0){
canJump = false;
} else {
//I modify the second jump force
jumpForce = secondJumpForce;
}
doJump = false;
}
}
void OnCollisionStay2D (Collision2D col) {
if (col.gameObject.tag == "Ground") {
canJump = true;
availableJumps = 1;
jumpForce = firstJumpForce;
}
}
void OnCollisionExit2D (Collision2D col) {
if (col.gameObject.tag == "Ground") {
isGrounded = false;
GetComponent<CircleCollider2D>().isTrigger = false;
}
transform.parent = null;
}
void OnTriggerEnter2D(Collider2D Other)
{
}
}
I cannot remove "isGrounded = true;" on void update section because cannot perform first jump although already tried from TuukkaX's solution