Unity Input triggered twice - c#

I created a small 2D sidescroller movement.
private Rigidbody2D rigid;
private BoxCollider2D playerCollider;
private bool isMovingRight = false;
private Vector2 movement;
private bool jumpPressed;
private const int MOVEMENT_SPEED = 5;
private const int JUMP_POWER = 5;
private const float GROUNDCHECK_TOLERANCE_SIDE = 0.05f;
private const float GROUNDCHECK_TOLERANCE_BOTTOM = 0.05f;
private void Start()
{
rigid = GetComponent<Rigidbody2D>();
playerCollider = GetComponent<BoxCollider2D>();
}
private void Update()
{
SetMovement();
}
private void FixedUpdate()
{
Move();
}
private void SetMovement()
{
float horizontalMovement = Input.GetAxis("horizontal") * MOVEMENT_SPEED;
if (Input.GetButtonDown("Jump"))
{
jumpPressed = true;
}
movement = new Vector2(horizontalMovement, rigid.velocity.y);
}
private void Move()
{
if (GroundCheck(true) || GroundCheck(false))
{
if (jumpPressed)
{
movement.y = JUMP_POWER;
jumpPressed = false;
}
}
rigid.velocity = movement;
}
private bool GroundCheck(bool checkLeftSide)
{
Bounds colliderBounds = playerCollider.bounds;
Vector2 rayPosition = colliderBounds.center;
float horizontalRayPosition = colliderBounds.extents.x + GROUNDCHECK_TOLERANCE_SIDE;
if (checkLeftSide)
{
rayPosition.x -= horizontalRayPosition;
}
else
{
rayPosition.x += horizontalRayPosition;
}
return Physics2D.Raycast(rayPosition, Vector2.down, (playerCollider.size.y / 2) + GROUNDCHECK_TOLERANCE_BOTTOM);
}
I register Inputs in Update and handle the Physics in FixedUpdate. When pressing the Jump Button the players jump works fine.
But when pressing jump multiple times, the player jumps up in the air, comes down and jumps one time again.
So if pressing the button more than 1 time the player will jump a second time after finishing the first jump.
How can I avoid this behaviour?

Using your code, I added 2 comments and an else statement to clarify what I mean in my comments. This should fix your issue.
private void SetMovement()
{
float horizontalMovement = Input.GetAxis("horizontal") * MOVEMENT_SPEED;
if (Input.GetButtonDown("Jump"))
{
// Only ever gets set to false if you are grounded and it makes you jump.
// Should only be set to true if you can jump. So you could add your groundChecks here...
// or use the method I am showing
jumpPressed = true;
}
movement = new Vector2(horizontalMovement, rigid.velocity.y);
}
private void Move()
{
if (GroundCheck(true) || GroundCheck(false))
{
if (jumpPressed)
{
movement.y = JUMP_POWER;
// This was the only place this every was set to false, and
// specific conditions were required to make this happen.
jumpPressed = false;
}
}
// fastest way to test
else
{
// yes they pressed jump but they can't jump so reset it, now
// they wont jump as soon as they land. just because they got
// trigger happy.
jumpPressed = false;
}
rigid.velocity = movement;
}

Related

How do remove speed decreasing after a jump that is used AddForce method (Unity)?

I'm starting to make my game and ran into a problem with movement. My movement is based on the new input system and .AddForce(). The problem is that when I jump, the character's speed drops after landing and then starts picking up again.
A Physic Material with a Dynamic Friction of 2 hangs on the player's Capsule Collider (if it affects the problem)
How to make the character move without slowing down?
Video demonstration (I hope you can see what the problem is).
Rigidbody and Player Mover settings.
Code (there is no more associated code):
using System;
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(CapsuleCollider))]
public class PlayerMover : MonoBehaviour
{
[SerializeField] private LayerMask _groundLayer = 6;
[SerializeField] private float _moveForce;
[SerializeField] private float _maxForce;
[SerializeField] private float _jumpForce;
[SerializeField] private float _mouseSensitivity;
private PlayerInput _input;
private Rigidbody _rb;
private CapsuleCollider _collider;
private Camera _camera;
private float _rotationX;
private const float MinAngle = -90f;
private const float MaxAngle = 90f;
private void Awake()
{
_input = new PlayerInput();
_rb = GetComponent<Rigidbody>();
_collider = GetComponent<CapsuleCollider>();
_camera = GetComponentInChildren<Camera>();
_rb.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ;
if (_groundLayer == gameObject.layer)
{
Debug.LogError("Сортувальний рівень гравця повинен відрізнятися від сортувального рівня землі!");
}
_input.Player.Jump.performed += context => Jump();
}
private void OnEnable()
{
_input.Enable();
}
private void OnDisable()
{
_input.Disable();
}
private void FixedUpdate()
{
Move();
}
private void LateUpdate()
{
Look();
}
private bool IsGrounded
{
get
{
var bottomCenterPoint = new Vector3(_collider.bounds.center.x, _collider.bounds.center.y,
_collider.bounds.center.z);
return Physics.CheckCapsule(_collider.bounds.center, bottomCenterPoint, _collider.bounds.size.x * 1.1f,
_groundLayer);
}
}
private Vector3 MovementVector
{
get
{
var cameraDirectionF = _camera.transform.forward;
var cameraDirectionR = _camera.transform.right;
cameraDirectionF.y = 0;
cameraDirectionR.y = 0;
cameraDirectionF = cameraDirectionF.normalized;
cameraDirectionR = cameraDirectionR.normalized;
var direction = _input.Player.Move.ReadValue<Vector2>();
return cameraDirectionF * direction.y + cameraDirectionR * direction.x;
}
}
private Vector2 MouseVector => _input.Player.Look.ReadValue<Vector2>() * _mouseSensitivity * Time.deltaTime;
private void Jump()
{
if (IsGrounded == false) return;
_rb.AddForce(new Vector3(0, _jumpForce, 0), ForceMode.Impulse);
}
private void Move()
{
_rb.AddForce(MovementVector * _moveForce, ForceMode.Impulse);
if (Math.Sqrt(Math.Pow(_rb.velocity.x, 2) + Math.Pow(_rb.velocity.z, 2)) < _maxForce) return;
var maxVelocity = _rb.velocity.normalized * _maxForce;
_rb.velocity = new Vector3(maxVelocity.x, _rb.velocity.y, maxVelocity.z);
}
private void Look()
{
_rotationX -= MouseVector.y;
_rotationX = Math.Clamp(_rotationX, MinAngle, MaxAngle);
_camera.transform.localEulerAngles = new Vector3(_rotationX, MouseVector.y, 0);
transform.Rotate(Vector3.up * MouseVector.x);
}
}
I tried changing the different .AddForce() values in the .Move() and .Jump() methods. Changed the value when assigning Vector3 to _rb.velocity in the .Move() method, where there is a check for the maximum allowable speed (without this check, .AddForce() will constantly increase the speed of movement).
I also thought that the problem was in the MovementVector property, but it is not so, it gives the correct values.
(The following statement is most likely incorrect)
In my opinion, after the jump, at the lowest point of the jump (almost near the ground), the character falls perpendicularly down and thus the speed is completely extinguished, so it needs to be regained.
Edit: I just modified the code to see the lowest velocity values in the console. Added the following: two variables and .Update(), one check in the MovementVector property, and changing the _flag in the .Jump() method.
private bool _flag;
private double _min = double.MaxValue;
private void Update()
{
if (!_flag) return;
var current = Math.Sqrt(Math.Pow(_rb.velocity.x, 2) + Math.Pow(_rb.velocity.z, 2));
if (current < _min) _min = current;
Debug.Log(_min);
}
ㅤ
private Vector3 MovementVector
{
get
{
....
var direction = _input.Player.Move.ReadValue<Vector2>();
if (direction.magnitude == 0)
{
_flag = false;
}
return cameraDirectionF * direction.y + cameraDirectionR * direction.x;
}
}
ㅤ
private void Jump()
{
_flag = true;
if (IsGrounded == false) return;
//_rb.AddForce(new Vector3(0, _jumpForce, 0), ForceMode.Impulse);
_rb.AddForce(transform.up * _jumpForce, ForceMode.Impulse);
}
Now, on landing, I will get accurate velocity values. With this, I noticed something, when changing _maxForce to a higher side, the landing velocity also changes. If for _maxForce equal to five the velocity dropped to zero, for _maxForce equal to 10 it drops already to 4, for _maxForce equal to 15 - to 9.
In Jump() Method you are adding the force of (0,_jumpforce,0) so this zeroes out the current movement. instead of zero you should addforce with the vector direction multiplied by jumpForce
Example:
_rb.AddForce(transform.up * _jumpForce, ForceMode.Impulse);
I hope this is fix works fine as per your requirement.

Need help in fixing a glitch with my Unity hiding script

I'm making a prototype for a horror game in Unity, and I'm trying to create a script that makes it so you can hide from the enemy in certain objects.
The intention is to make it that the camera lerps to the hiding spot in a smooth animation, and then will lerp the camera back to the player's position on exit.
I have it working so the player can enter and exit with a lerp, but the issue is that after exiting the hiding spot, interactions with any object cause the camera to sporadically rotate, rather than doing the intended interaction.
Here is my hide script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Hidey : MonoBehaviour, IInteractable
{
[SerializeField] private Camera mainCamera;
[SerializeField] private Camera hideCamera;
[SerializeField] private float hideSpeed;
private float timer;
private Vector3 endPosition;
private Quaternion endRotation;
private Vector3 endPosition2;
private Quaternion endRotation2;
private bool canToggle;
private bool togglingLerp;
private bool hidden;
private void Awake()
{
hideCamera.enabled = false;
mainCamera.enabled = true;
canToggle = true;
togglingLerp = false;
hidden = false;
endPosition = hideCamera.transform.position;
endRotation = hideCamera.transform.rotation;
}
public void Interaction()
{
if (canToggle && !hidden)
{
hideCamera.enabled = true;
hideCamera.transform.position = mainCamera.transform.position;
hideCamera.transform.rotation = mainCamera.transform.rotation;
mainCamera.enabled = false;
endPosition2 = mainCamera.transform.position;
endRotation2 = mainCamera.transform.rotation;
togglingLerp = true;
}
else return;
}
private void Update()
{
if(togglingLerp && !hidden)
{
canToggle = false;
timer = Time.deltaTime * hideSpeed;
HideAnimation(hideCamera.transform.position, endPosition, hideCamera.transform.rotation, endRotation, timer, true, hideCamera);
}
if(hidden && Input.GetKeyDown(KeyCode.E))
{
canToggle = false;
mainCamera.enabled = true;
mainCamera.transform.position = hideCamera.transform.position;
mainCamera.transform.rotation = hideCamera.transform.rotation;
hideCamera.enabled = false;
timer = Time.deltaTime * hideSpeed;
HideAnimation(mainCamera.transform.position, endPosition2, mainCamera.transform.rotation, endRotation2, timer, false, mainCamera);
}
}
private void HideAnimation(Vector3 startPos, Vector3 endPos, Quaternion startRot, Quaternion endRot, float lerpTimer, bool isHidden, Camera currentCamera)
{
currentCamera.transform.position = Vector3.Lerp(startPos, endPos, lerpTimer);
currentCamera.transform.rotation = Quaternion.Lerp(startRot, endRot, lerpTimer);
if (Quaternion.Dot(currentCamera.transform.rotation, endRot) > .9999f && Vector3.Dot(currentCamera.transform.position, endPos) > .9999f)
{
hidden = isHidden;
togglingLerp = false;
canToggle = true;
}
}
}
I have no idea what is causing this issue at the moment.

How to switch between the OnTriggerExit/Enter logic depending on the situation?

The script is attached to two gameobjects.
One it's colliding area the collider is big enough so when the game start the player is already inside the collider area and then when he exit the area everything is working fine.
The problem is when I attached the object to another gameobject with collider but this time the collider s smaller and he is inside the bigger collider so now the player is entering the smaller collider and not first time exiting. but I want in both case to make the same effect.
If the player exit the collider he slow down wait then turn around and move inside back.
The same I want to make when the player getting closer to the fire flames slow down wait turn around and move back in it's just with the flames the player entering the collider area and then exit while in the bigger collider he first exit then enter.
Screenshot of the two colliders areas. The small one on the left is the one the player entering first and not exiting. The game start when the player is in the bigger collider area.
And the script :
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UnityStandardAssets.Characters.ThirdPerson;
public class DistanceCheck : MonoBehaviour
{
public Transform targetToRotateTowards;
public Transform colliderArea;
public float lerpDuration;
public float rotationSpeed;
[TextArea(1, 2)]
public string textToShow;
public GameObject descriptionTextImage;
public TextMeshProUGUI text;
public ThirdPersonUserControl thirdPersonUserControl;
private Animator anim;
private float timeElapsed = 0;
private float startValue = 1;
private float endValue = 0;
private float valueToLerp = 0;
private bool startRotating = false;
private bool slowOnBack = true;
private bool exited = false;
private Vector3 exitPosition;
private float distance;
void Start()
{
anim = transform.GetComponent<Animator>();
}
private void FixedUpdate()
{
if (startRotating)
{
transform.rotation = Quaternion.RotateTowards(transform.rotation,
Quaternion.LookRotation(targetToRotateTowards.position - transform.position),
rotationSpeed * Time.deltaTime);
}
if (exitPosition != new Vector3(0, 0, 0) && slowOnBack)
{
distance = Vector3.Distance(transform.position, exitPosition);
}
if (distance > 5 && slowOnBack)
{
slowOnBack = false;
StartCoroutine(SlowDown());
}
}
private void OnTriggerExit(Collider other)
{
if (other.name == colliderArea.name)
{
exited = true;
slowOnBack = true;
exitPosition = transform.position;
thirdPersonUserControl.enabled = false;
descriptionTextImage.SetActive(true);
text.text = textToShow;
StartCoroutine(SlowDown());
}
}
private void OnTriggerEnter(Collider other)
{
if (other.name == colliderArea.name)
{
exited = false;
startRotating = false;
text.text = "";
descriptionTextImage.SetActive(false);
}
}
IEnumerator SlowDown()
{
timeElapsed = 0;
while (timeElapsed < lerpDuration)
{
valueToLerp = Mathf.Lerp(startValue, endValue, timeElapsed / lerpDuration);
anim.SetFloat("Forward", valueToLerp);
timeElapsed += Time.deltaTime;
yield return null;
}
if (exited)
{
yield return new WaitForSeconds(3f);
startRotating = true;
StartCoroutine(SpeedUp());
}
if (slowOnBack == false)
{
thirdPersonUserControl.enabled = true;
}
}
IEnumerator SpeedUp()
{
timeElapsed = 0;
while (timeElapsed < lerpDuration)
{
valueToLerp = Mathf.Lerp(endValue, startValue, timeElapsed / lerpDuration);
anim.SetFloat("Forward", valueToLerp);
timeElapsed += Time.deltaTime;
yield return null;
}
}
}
Two problems I'm facing right now :
The smaller collider the player enter it then exit and the bigger collider the player exit it then enter so I need to change somehow the OnTriggerExit/Enter behavior logic in the script. but how to make the logic ?
Maybe it's better to make the script to be on the player object only and make it some how generic so I can drag to it many colliders and to make the effect in each one of the colliders the problem is how to make a text field for each collider area ? Now because I attach the script to each collider object I have one colliderArea variable but if I want to make the script only attached to the player I need to change the colliderArea variable to a List<Transform> collidersAreas and then how to create a text field/area to each collider area in the List ?
I think I would do this by creating two tags, NoExit and NoEntry. Once the tags are created you can set the tag on the GameObjects that hold your colliders. Then you can check for tags in the OnTriggerEnter and OnTriggerExit and act accordingly.
I can't tell for sure which functionality you've got written is for which case, or if it's both - everything looks muddled. Ultimately what you should be going for is a function like RepositionPlayer, that moves them back to before they're violating the no entry or no exit rules, and then some kind of OnPlayerRepositioned event that restores their control.
I'll leave it up to you to split out your functionality, but in general I'd look to do something like the following:
private void OnTriggerExit(Collider other)
{
if (other.tag == "NoExit")
{
RepositionPlayer();
}
else if(other.tag == "NoEntry")
{
OnPlayerRepositioned();
}
}
private void OnTriggerEnter(Collider other)
{
if (other.tag == "NoExit")
{
OnPlayerRepositioned();
}
else if(other.tag == "NoEntry")
{
RepositionPlayer();
}
}
And again here it's not clear to me what you're trying to do to reposition the player, but it looks like it's something like:
private void RepositionPlayer()
{
// Stuff that needs to happen to reposition the player
exited = true;
slowOnBack = true;
exitPosition = transform.position;
thirdPersonUserControl.enabled = false;
descriptionTextImage.SetActive(true);
text.text = textToShow;
StartCoroutine(SlowDown());
}
private void OnPlayerRepositioned()
{
// stuff you need to do to clear the "repositioning" status
exited = false;
startRotating = false;
text.text = "";
descriptionTextImage.SetActive(false);
}
Splitting up the logic like this makes it easier to both read and maintain.

Unity 2D - Jump Animation Reset

I'm new to the Unity world (2D) and I've run into some problems.
I have a script in which i want to jump. When i jump, the jump animation is super fast. You can only see it in the animator.
I have two other animations (Idle, Run) that work without any problems.
Can you please help me? :(
public class Player : MonoBehaviour
{
private Rigidbody2D rigid;
[SerializeField]
private float jumpForce = 5.0f;
private bool resetJump;
[SerializeField]
private float speed = 5.0f;
private PlayerAnimation playerAnim;
private SpriteRenderer playerSprite;
// Start is called before the first frame update
void Start()
{
...
}
// Update is called once per frame
void Update()
{
Movement();
}
void Movement()
{
float move = Input.GetAxisRaw("Horizontal");
Flip(move);
if (IsGrounded())
{
playerAnim.Jump(false);
}
if (Input.GetKeyDown(KeyCode.Space) && IsGrounded() == true)
{
rigid.velocity = new Vector2(rigid.velocity.x, jumpForce);
StartCoroutine(ResetJumpNeededRoutine());
playerAnim.Jump(true);
}
rigid.velocity = new Vector2(move * speed, rigid.velocity.y);
playerAnim.Move(move);
}
void Flip(float move)
{
...
}
bool IsGrounded()
{
RaycastHit2D hitInfo = Physics2D.Raycast(transform.position, Vector2.down, 0.3f, 1 << 8);
if (hitInfo.collider != null)
{
if (resetJump == false)
{
return true;
}
}
return false;
}
IEnumerator ResetJumpNeededRoutine()
{
yield return new WaitForSeconds(0.1f);
resetJump = false;
}
}
The problem is in ur code just after space is pressed the resetjump is getting false and isGrounded() is returning true and hence jump anim is getting false. So what i insist is to set trigger instead of setting playerAnim.jump use playerAnim.SetTrigger("Jump").
Study about animation trigger we use trigger to play animations like shoot and jump as these animation are true and then at next instant false.

Trying to make a reload delay in c# yet running into a bug

So I have been trying to make a game recently and I have ran into a unexpected bug.
As I run the script I have wrote I can shoot 10 bullets out of my gun, then it plays a reload sound and waits two seconds using coroutines and it adds 1200 to my clip for no reason? So I added the if statement telling it that if the clip size goes over 10 then revert it to 10 but now it goes to 19 for a few seconds then back to 10.
I am unaware if I am being a idiot or something but would be glad for some help, also sorry if this is a duplicate but i was unable to find anything similar in c#.
EDIT: i have fixed the bug now, thanks for the help, ill share the new code after the old code for future reference!
My old code:
{
public float fireRate = 10;
public float damage = 15;
public int clipsize;
float timeUntilFire = 0;
public float ReloadSpeed = 2.5f;
float reloadtime;
public Transform firePoint;
public GameObject bullet;
public AudioSource gunshotaudio;
public AudioSource ReloadSound;
void Awake()
{
gunshotaudio = GetComponent<AudioSource>();
clipsize = 10;
}
// Update is called once per frame
void Update()
{
// if the button pressed is fire 1 and
if (Input.GetButtonDown("Fire1") && clipsize != 0)
{
timeUntilFire = Time.time / fireRate;
gunshotaudio.Play();
Shoot();
clipsize -= 1;
Debug.Log(clipsize);
}
if(clipsize <= 0)
{
ReloadSound.Play();
StartCoroutine(Wait());
StopCoroutine(Wait());
}
if(clipsize >= 11)
{
clipsize = 10;
}
}
IEnumerator Wait()
{
yield return new WaitForSecondsRealtime(2);
clipsize += 10;
}
void Shoot()
{
gunshotaudio.Play();
Instantiate(bullet, firePoint.position,firePoint.rotation);
}
}
** My New Code: **
{
public float fireRate = 10;
public float damage = 15;
public int clipsize;
float timeUntilFire = 0;
public float ReloadSpeed = 2.5f;
float reloadtime;
bool CanShoot;
bool IsReloading;
bool reloading;
public Transform firePoint;
public GameObject bullet;
public AudioSource gunshotaudio;
public AudioSource ReloadSound;
void Awake()
{
gunshotaudio = GetComponent<AudioSource>();
clipsize = 10;
}
void Start()
{
if (clipsize > 0)
{
CanShoot = true;
}
}
// Update is called once per frame
void Update()
{
// if the button pressed is fire 1 and
if (Input.GetButtonDown("Fire1") && clipsize != 0)
{
timeUntilFire = Time.time + fireRate;
gunshotaudio.Play();
Shoot();
clipsize--;
Debug.Log(clipsize);
}
// reloading
if(Input.GetKeyDown(KeyCode.R))
{
IsReloading = true;
ReloadSound.Play();
}
if (IsReloading)
{
StartCoroutine(Wait());
}
}
IEnumerator Wait()
{
if(reloading == false)
{
if (IsReloading)
{
reloading = true;
CanShoot = false;
yield return new WaitForSeconds(1);
clipsize = 10;
IsReloading = false;
Debug.Log(clipsize);
CanShoot = true;
reloading = false;
}
else
{
}
}
}
void Shoot()
{
gunshotaudio.Play();
Instantiate(bullet, firePoint.position,firePoint.rotation);
}
}
You aren't checking to see if a reload is already in process, so you are queuing up a bunch of reload events which all add +10 to the clip resulting in extras.
Add a reloading bool and check that before you initiate your coroutine and instead of adding 10 to the clip, set the clip to 10.

Categories