Unity2D how to limit player jump - c#

So im making a clicker game and here is my code. What i want to ask is how to limiting button to click so it can't be clicked by multiple time, because if i clicked it multiple time the speed became too fast
public float downForce;
public float speed;
public int playerHp;
public Text healthText;
Rigidbody2D rb;
CharacterController controller;
void Awake()
{
rb = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
healthText.text = playerHp.ToString();
if (Input.GetMouseButtonDown(0))
{
Jump();
}
if (playerHp < 0)
{
Destroy(this.gameObject);
SceneManager.LoadScene("GameOver");
}
}
public void Jump()
{
rb.AddForce(Vector2.up * downForce + Vector2.right * speed, ForceMode2D.Impulse);
rb.isKinematic = false;
}

public Button BTN;
public float btnDelay = .5f;
this to get the Button reference and specify the duration
coroutine = ButtonDelayed(btnDelay);
StartCoroutine(coroutine);
this after you call Jump(); in your update or in Jump()
IEnumerator ButtonDelayed(float delay)
{
BTN.interactable = false;
yield return new WaitForSeconds(delay);
BTN.interactable = !BTN.interactable;
}
this somewhere.
Just a quick mockup. Not sure if you will get an exception. If you have a problem just hit me up.
EDIT: I forgot to tell you to change the color of the disabled state in the inspector to the color you have when the Button is interactable. Otherwise you will see the Button change colors.
EDIT2: Full script updated
public float downForce;
public float speed;
public int playerHp;
public Text healthText;
Rigidbody2D rb;
CharacterController controller;
public Button BTN;
public float btnDelay = .5f;
void Awake()
{
rb = GetComponent<Rigidbody2D>();
}
void Update()
{
healthText.text = playerHp.ToString();
if (Input.GetMouseButtonDown(0))
{
Jump();
}
if (playerHp < 0)
{
Destroy(this.gameObject);
SceneManager.LoadScene("GameOver");
}
}
public void Jump()
{
coroutine = ButtonDelayed(btnDelay);
StartCoroutine(coroutine);
rb.AddForce(Vector2.up * downForce + Vector2.right * speed,
ForceMode2D.Impulse);
rb.isKinematic = false;
}
IEnumerator ButtonDelayed(float delay)
{
BTN.interactable = false;
yield return new WaitForSeconds(delay);
BTN.interactable = !BTN.interactable;
}

I still can't reply to comments so I'll post it here
To use IEnumerator is just like any other function
you put it somewhere in your code and then call it
the difference is when you call it you add
StartCoroutine(MethodName()) ;
and it will run the first part of the code - then Wait For the amount of time you specified, then it will run the second part of the code

Related

Player now does not jump after adding health in Unity

so first time learning C# for my team studio class. I found this video which I was able to get my player moving and jumping perfectly, https://www.youtube.com/watch?v=3GtQ2yQX2kU&t=569s however after I added a health/can die system to the script, now the player refuses to jump no matter what I do. Since I am a newbie at this, I am just going to post the whole thing here if anyone can point me in the right direction. I've played with the box colliders, added the layers to the platforms like the tutorial said... please, if anyone can help. I know I'm a newbie to this :(
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public HealthBar healthBar; //Communicates with the healthbar script
public float moveSpeed;
public float jumpForce;
public int jumpsAmount;
int jumpsLeft;
public Transform GroundCheck;
public LayerMask GroundLayer;
public int maxHealth = 3;
public int currentHealth;
bool isGrounded;
float moveInput;
Rigidbody2D rb2d;
float scaleX;
// Start is called before the first frame update
void Start()
{
healthBar.SetMaxHealth(maxHealth); //Sets healthbar to max health
currentHealth = maxHealth;
rb2d = GetComponent<Rigidbody2D>();
scaleX = transform.localScale.x;
}
public void TakeDamage(int damage)
{
currentHealth -= damage;
if (currentHealth <= 0) //When Health is zero the player is destroyed
{
Destroy(gameObject);
}
healthBar.SetHealth(currentHealth); //Updates Healthbar when damgage is taken
}
// Update is called once per frame
void Update()
{
moveInput = Input.GetAxisRaw("Horizontal");
Jump();
}
private void FixedUpdate()
{
Move();
}
public void Move()
{
Flip();
rb2d.velocity = new Vector2(moveInput * moveSpeed, rb2d.velocity.y);
}
public void Flip()
{
if (moveInput > 0)
{
transform.localScale = new Vector3(scaleX, transform.localScale.y, transform.localScale.z);
}
if (moveInput < 0)
{
transform.localScale = new Vector3((-1) * scaleX, transform.localScale.y, transform.localScale.z);
}
}
public void Jump()
{
if (Input.GetKeyDown(KeyCode.Space))
{
CheckIfGrounded();
if (jumpsLeft > 0)
{
rb2d.velocity = new Vector2(rb2d.velocity.x, jumpForce);
jumpsLeft--;
}
}
}
public void CheckIfGrounded()
{
isGrounded = Physics2D.OverlapCircle(GroundCheck.position, GroundCheck.GetComponent<CircleCollider2D>().radius, GroundLayer);
ResetJumps();
}
public void ResetJumps()
{
if (isGrounded)
{
jumpsLeft = jumpsAmount;// jumpsAmount =2;
}
}
}
I put the groundcheck collider where the video wanted me to, even played around having different locations too. Nothing is working... tried changing the jump force too to see if it needs to be higher. I am at a complete loss.

Slow down player's speed in air

I am working on a 3D game currently that is based on Unity's Roll-a-Ball tutorial. The problem that I have encountered, is that whenever my player jumps, its speed increases in the air and I don't want that to happen. I tried coming up with a solution but I just couldn't make it work the way I wanted it to. How would I prevent this from happening?
Here is my code if anyone is interested:
public class PlayerController : MonoBehaviour
{
private Rigidbody rb;
private float movementX;
private float movementY;
public int count;
public GameObject player;
bool isGrounded = true;
public TextMeshProUGUI countText;
public GameObject winTextObject;
public float speed = 0;
public float jumpSpeed = 0;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
count = 0;
SetCountText();
winTextObject.SetActive(false);
}
// Update is called once per frame
void Update()
{
}
void OnMove(InputValue movementValue)
{
Vector2 movementVector = movementValue.Get<Vector2>();
movementX = movementVector.x;
movementY = movementVector.y;
}
void Jump()
{
if (Input.GetKey(KeyCode.Space) && isGrounded)
{
rb.AddForce(Vector2.up * jumpSpeed, ForceMode.Impulse);
}
}
void FixedUpdate()
{
Jump();
Vector3 movement = new Vector3(movementX, 0f, movementY);
rb.AddForce(movement * speed);
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("PickUp"))
{
other.gameObject.SetActive(false);
count++;
SetCountText();
}
}
void SetCountText()
{
countText.text = "Score: " + count.ToString();
if (count >= 14)
{
winTextObject.SetActive(true);
}
}
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag("Ground"))
{
isGrounded = true;
Debug.Log("You are colliding with the ground!");
}
}
private void OnCollisionExit(Collision collision)
{
if (collision.gameObject.CompareTag("Ground"))
{
isGrounded = false;
Debug.Log("You are no longer grounded!");
}
}
}
Use drag to create air resistance:

Is that a good usecase of delegates and events in Unity?

I just started learning Unity and wanna try to use events/delegates in FlappyBird game.
Flappy bird screenshot
As on this pic, I need to replace text with current score + 1, when the bird triggers collider between pipes.
public class BirdController : MonoBehaviour
{
private SpriteRenderer sr;
private Rigidbody2D rigidBody;
private Animator anim;
[SerializeField]
private float movementXForce, movementYForce, rotationSpeedUp,
rotationSpeedDown;
private float lastPosY;
float rotation;
private string FLY_ANIMATION = "fly";
private void Awake() {
sr = GetComponent<SpriteRenderer>();
rigidBody = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
lastPosY = transform.position.y;
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
fly();
moveRight();
animFly();
}
void fly()
{
if (Input.GetButtonDown("Jump")) {
rigidBody.AddForce(new Vector2(0f, movementYForce),
ForceMode2D.Impulse);
lastPosY = transform.position.y;
}
if (rigidBody.velocity.y < 0 && rigidBody.rotation > -30) {
rotation = -1f * rotationSpeedDown;
transform.Rotate(Vector3.forward * rotation);
} else if (rigidBody.velocity.y >= 0 && rigidBody.rotation < 30) {
rotation = 1f * rotationSpeedUp;
transform.Rotate(Vector3.forward * rotation);
}
}
void moveRight()
{
transform.position += new Vector3(1f, 0f, 0f) * Time.deltaTime *
movementXForce;
}
void animFly()
{
if (lastPosY < transform.position.y) {
anim.SetBool(FLY_ANIMATION, true);
} else {
anim.SetBool(FLY_ANIMATION, false);
}
}
private void OnTriggerEnter2D(Collider2D other) {
if (other.CompareTag("Ground") || other.CompareTag("Trap")) {
Destroy(gameObject);
}
}
private void OnTriggerExit2D(Collider2D other) {
if (other.CompareTag("Score")) {
}
}
}
I am about to add extra field in BirdController, something like
public delegate void OnTriggerScoreLine();
public static event OnTriggerScoreLine onTriggerScoreLine;
Then in OnTriggerExit2D I will
if (onTriggerScoreLine != null) {
OnTriggerScoreLine();
}
After that I will create new script ScoreController and there I will subscribe onTriggerScoreLine on method that will change the score text on score + 1 and also static scoreValue variable
And I just wanted to ask if I correctly understood delegates and events. Is it a good example of its using? Thanks:)

Player not jumping. Unity RigidBody2D

When I press my jump key, the player doesn't jump but the Debug message I added does print in console.
My code:
using UnityEngine;
public class PlayerController : MonoBehaviour
{
// Start is called before the first frame update
private Transform transform;
private Rigidbody2D rb;
private bool onground = false;
public float speed;
public float momentum;
public float jumpForce;
void Start()
{
rb = gameObject.GetComponent<Rigidbody2D>();
transform = rb.transform;
}
// Update is called once per frame
void Update()
{
}
private void FixedUpdate()
{
float moveHorizontal = Input.GetAxis("Horizontal");
Vector3 movement = new Vector3(moveHorizontal, 0, 0);
transform.position += movement * Time.deltaTime * speed;
if (Input.GetButtonDown("Jump") && onground)
{
Jump(jumpForce);
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.collider.tag == "Floor")
{
onground = true;
Debug.Log("Player Is On Ground!");
}
}
private void OnCollisionExit2D(Collision2D collision)
{
if (collision.collider.tag == "Floor")
{
onground = false;
Debug.Log("Player Is Not On The Ground!");
}
}
private void Jump(float force)
{
rb.velocity = Vector2.up * force * Time.deltaTime;
Debug.Log("Player Has Jumped!");
}
}
the player can move but not jump and haven't found any posts anywhere with a similar issue, I might not be looking hard enough or searching the correct thing but I just cannot find a solution to my problem.
First of all, you move the player by transform component but trying to jump by physics (rigidbody), it isn't a good idea and I recommend you to work only with rigidbody in this case. Also the multiply jump force by Time.deltaTime doesn't have a sense because you call the method from FixedUpdate(), I assume that the power of jump is too week to see the result so try to remove Time.deltaTime and increase the jumpForce value.

Wait at waypoint for amount of time in unity C#

I have a waypoint system in a Unity 2D project where the GameObject will follow each waypoint in order and then repeat the process, i am wanting the GameObject to stop at each point for a fixed amount of time and i thought i could achieve this using a coroutine but not entirely sure of how to achieve this, what i have done so far is create a coroutine called WaitAtPoint and then call this on each waypoint movement but to no avail, not sure what i am doing wrong.
public class BrainBoss : MonoBehaviour {
[SerializeField]
Transform[] waypoints;
[SerializeField]
float moveSpeed = 2f;
int waypointIndex = 0;
// Start is called before the first frame update
void Start()
{
transform.position = waypoints[waypointIndex].transform.position;
}
// Update is called once per frame
void Update()
{
Move();
}
void Move()
{
transform.position = Vector2.MoveTowards(transform.position,
waypoints[waypointIndex].transform.position, moveSpeed * Time.deltaTime);
if(transform.position == waypoints[waypointIndex].transform.position)
{
StartCoroutine(WaitAtPoint());
waypointIndex += 1;
}
if(waypointIndex == waypoints.Length)
{
waypointIndex = 1;
}
}
IEnumerator WaitAtPoint()
{
yield return new WaitForSeconds(3f);
}
}
You are calling Move every update and you could also be calling that StartCoroutine multiple times so i suggest using a variable to see if you should even update the movement
public class BrainBoss : MonoBehaviour
{
[SerializeField]
Transform[] waypoints;
[SerializeField]
float moveSpeed = 2f;
int waypointIndex = 0;
private bool shouldMove = true;
// Start is called before the first frame update
void Start() {
transform.position = waypoints[waypointIndex].transform.position;
}
// Update is called once per frame
void Update() {
if (this.shouldMove) {
Move();
}
}
void Move() {
transform.position = Vector2.MoveTowards(transform.position,
waypoints[waypointIndex].transform.position, moveSpeed * Time.deltaTime);
if (transform.position == waypoints[waypointIndex].transform.position) {
StartCoroutine(WaitAtPoint(3));
waypointIndex += 1;
}
if (waypointIndex == waypoints.Length) {
waypointIndex = 1;
}
}
IEnumerator WaitAtPoint(int seconds) {
this.shouldMove = false;
int counter = seconds;
while (counter > 0) {
yield return new WaitForSeconds(1);
counter--;
}
this.shouldMove = true;
}
}
You can use a simble bool flag to know if you should move or not. In Move (or Update), check for that bool to know if you should move or not.
In WaitAtPoint, set the bool (like shouldWait) to true, then back to false after the WaitForSecond !
Well, your WaitAtPoint is not doing an awful lot at the moment. This is because it is waiting inside the IEnumerator, not where you are calling it.
There are various ways to tackle this, but I would suggest using a callback on your IEnumerator which is executed after the waiting time.
Like this:
private bool isWaiting;
void Update() {
if (!isWaiting) {
Move();
}
}
void Move()
{
transform.position = Vector2.MoveTowards(transform.position,
waypoints[waypointIndex].transform.position, moveSpeed * Time.deltaTime);
if(transform.position == waypoints[waypointIndex].transform.position)
{
StartCoroutine(WaitAtPoint(() =>
{
// All code that should be executed after waiting here.
waypointIndex += 1;
}));
}
if(waypointIndex == waypoints.Length)
{
waypointIndex = 1;
}
}
IEnumerator WaitAtPoint(Action callback)
{
isWaiting = true;
yield return new WaitForSeconds(3f);
callback.Invoke();
isWaiting = false;
}

Categories