Problems with OnCollisionEnter2D & OnCollisionExit2D - c#

I'm trying to make the Player not jump continuously so I use isOnGrounded variable to check if the player is on the ground or not. Here is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class PlayerController : MonoBehaviour
{
//REFERENCES
private Rigidbody2D rb2D;
//VARIABLES
[SerializeField] float moveSpeed = 0;
private float moveX;
[SerializeField] bool isOnGrounded = true;
[SerializeField] float jumpY;
// Start is called before the first frame update
void Start()
{
rb2D = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
moveX = Input.GetAxis("Horizontal");
PlayerJump();
}
private void FixedUpdate()
{
PlayerMove();
}
void PlayerMove()
{
rb2D.velocity = new Vector2(moveX * moveSpeed * Time.fixedDeltaTime, rb2D.velocity.y);
}
void PlayerJump()
{
if (Input.GetKeyDown(KeyCode.Space) && isOnGrounded == true)
{
rb2D.AddForce(new Vector2(rb2D.velocity.x, jumpY));
}
}
private void OnCollisionEnter2D(Collision2D other)
{
if (other.gameObject.CompareTag("Ground"))
{
isOnGrounded = true;
}
}
private void OnCollisionExit2D(Collision2D other)
{
if (other.gameObject.CompareTag("Ground"))
{
isOnGrounded = false;
}
}
}
The problem is when the Player is standing on Platform01 so obviously isOnGrounded = true and when the Player moves out of Platform01 isOnGrounded = false, I suppose when move in Platform02 it will automatically check the Ground and isOnGrounded = true but it still false and everything just messing up.

This is because OnCollisionEnter2D(Platform02) is triggered before OnCollisionExit2D(Platform01).
You can remember the last hit collider and compare with it when leave a platform.
public class PlayerController : MonoBehaviour
{
private Collision2D ground;
public bool IsGround => (bool)ground;
private void OnCollisionEnter2D(Collision2D other)
{
if (other.gameObject.CompareTag("Ground"))
{
ground = other;
}
}
private void OnCollisionExit2D(Collision2D other)
{
if (other.gameObject.CompareTag("Ground") && other == ground)
{
ground = null;
}
}
}

Related

How to fix boolean value?

I am trying to get this jump/movement system and the bool for a ground check is not changing. When I try the code the value of the bool is its starting value. Here is my code. The code is c# and the engine I am using is unity.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MOVEMENT : MonoBehaviour
{
public float playerJumpHeight;
public GameObject player;
public float speed;
public float PlayerXPosition;
public bool isgrounded = false;
public GameObject data_to;
void fixedupdate()
{
PlayerXPosition = player.transform.position.x;
}
void OnCollisionEnter(Collision theCollision)
{
if (theCollision.gameObject.name == "floor")
{
isgrounded = true;
transform.Translate(0,0,1);
}
if (theCollision.gameObject.name == "Height_Check")
{
isgrounded = false;
transform.Translate(0,0,0);
}
}
//consider when the character is jumping .. it will exit collision.
void OnCollisionExit(Collision theCollision)
{
if (theCollision.gameObject.name == "floor")
{
isgrounded = false;
}
}
void Update()
{
Rigidbody2D rb = GetComponent<Rigidbody2D>();
if (Input.GetKeyDown(KeyCode.W) && isgrounded == true) {
rb.AddForce(transform.up * speed);
}
if (Input.GetKey(KeyCode.A))
rb.AddForce(Vector2.left);
if (Input.GetKey(KeyCode.D))
rb.AddForce(Vector2.right);
}
}
If your game is 2D, you must use OnCollisionEnter/Exit 2D.
void OnCollisionEnter2D(Collision theCollision)
{
if (theCollision.gameObject.name == "floor")
{
isgrounded = true;
transform.Translate(0, 0, 1);
}
// ...
}

How do I make my player speed up when collide with a game object in Unity?

I am doing a school project in Unity. My team and I decided to make an endless runner 2D game. However, because it is the first time I use C#, I don't know how to make my player's speed accelerate when collide with a game object in Unity. I only know how to destroy the player's health when a collision happens. Please help me! Thank you!
it's quite hard to give answers without seeing any code you've written, but as it's 2D and you've already got the collision damage working, you've probably used an OnCollisionEnter(), well it's very similar: you test if you've collided (which you've already done), then you add force to your player using a rigidbody2d, probably something along the lines of:
public Rigidbody2D rb;
void OnCollisionEnter2D(Collision2D collision)
{
rb.AddForce(direction * force, ForceMode2D.Impulse); // the ForceMode2D is
// optional, it's just so that
// the velocity change is sudden.
}
This should work.
If you have a GameManager that stores the game speed you can also do that too:
private float gameSpeedMultiplier = 0.5f;
void OnCollisionEnter(Collision collision)
{
if(collision.gameObject.CompareTag("Tag of the collided object")
{
GameManager.Instance.gameSpeed += gameSpeedMultiplier;
}
}
public class PlayerContoler : MonoBehaviour
{
public static PlayerContoler instance;
public float moveSpeed;
public Rigidbody2D theRB;
public float jumpForce;
private bool isGrounded;
public Transform GroundCheckPoint;
public LayerMask whatIsGround;
private bool canDoubleJump;
private Animator anim;
private SpriteRenderer theSR;
public float KnockbackLength, KnockbackForce;
private float KnockbackCounter;
public float bonceForce;
public bool stopImput;
private void Awake()
{
instance = this;
}
// Start is called before the first frame update
void Start()
{
anim = GetComponent<Animator>();
theSR = GetComponent<SpriteRenderer>();
}
// Update is called once per frame
void Update()
{
if(!PauseMenu.instance.isPause && !stopImput)
{
if (KnockbackCounter <= 0)
{
theRB.velocity = new Vector2(moveSpeed * Input.GetAxis("Horizontal"), theRB.velocity.y);
isGrounded = Physics2D.OverlapCircle(GroundCheckPoint.position, .2f, whatIsGround);
if (isGrounded)
{
canDoubleJump = true;
}
if (Input.GetButtonDown("Jump"))
{
if (isGrounded)
{
theRB.velocity = new Vector2(theRB.velocity.x, jumpForce);
AudioManager.instance.PlaySFX(10);
}
else
{
if (canDoubleJump)
{
theRB.velocity = new Vector2(theRB.velocity.x, jumpForce);
canDoubleJump = false;
AudioManager.instance.PlaySFX(10);
}
}
}
if (theRB.velocity.x < 0)
{
theSR.flipX = true;
}
else if (theRB.velocity.x > 0)
{
theSR.flipX = false;
}
} else
{
KnockbackCounter -= Time.deltaTime;
if(!theSR.flipX)
{
theRB.velocity = new Vector2(-KnockbackForce, theRB.velocity.y);
} else
{
theRB.velocity = new Vector2(KnockbackForce, theRB.velocity.y);
}
}
}
anim.SetFloat("moveSpeed", Mathf.Abs(theRB.velocity.x));
anim.SetBool("isGrounded", isGrounded);
}
public void Knockback()
{
KnockbackCounter = KnockbackLength;
theRB.velocity = new Vector2(0f, KnockbackForce);
anim.SetTrigger("hurt");
}
}

Jump/Throw Coding or Animation Issues

The Setup
I have a basketball player with an animator and 5 assigned animations to that animator as followed:
PlayerIdle
PlayerWalk
PlayerJump
PlayerBend
PlayerShoot
Setup_FruitHoops
Player Animation Window with Parameters
The Problem
In PlayMode, the player is able to move and jump successfully. The issue comes when the player attempts to grab the apple that is on the ground. The desired game design that I wanted was for the player to bend over and grab the apple by pressing the G key and then press G again to throw the apple towards the hoop to score a point. However, when I run it and after I first press G, the player can no longer jump, grab, or throw. The below section is the code for PlayerMovement and GrabberScript. Any advice is greatly appreciated.
PlayerMovement.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public float speed = 5f;
private Rigidbody2D rb;
private Animator anim;
private bool isGrounded, jumped;
private float jumpshotPower = 6f;
public Transform groundCheck;
public LayerMask groundLayer;
private void Awake()
{
rb = GameObject.Find("Player Parent").GetComponent<Rigidbody2D>();
anim = GameObject.Find("Player Parent").GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
CheckIfGrounded();
PlayerJump();
}
private void FixedUpdate()
{
PlayerWalk();
}
void PlayerWalk()
{
float h = Input.GetAxisRaw("Horizontal");
if (h > 0)
{
//going right
anim.SetBool("isWalking", true);
rb.velocity = new Vector2(speed, rb.velocity.y);
ChangeDirection(-1);
}
else if (h < 0)
{
//going left
anim.SetBool("isWalking", true);
rb.velocity = new Vector2(-speed, rb.velocity.y);
ChangeDirection(1);
}
else
{
//standing still
anim.SetBool("isWalking", false);
rb.velocity = new Vector2(0f, rb.velocity.y);
}
}
void ChangeDirection(int d)
{
Vector3 temp = transform.localScale;
temp.x = d;
transform.localScale = temp;
}
void CheckIfGrounded()
{
isGrounded = Physics2D.Raycast(groundCheck.position, Vector2.down, 0.1f, groundLayer);
if (isGrounded)
{
if (jumped)
{
jumped = false;
anim.SetBool("isJumping", false);
}
}
}
void PlayerJump()
{
if (isGrounded)
{
if (Input.GetKey(KeyCode.UpArrow))
{
jumped = true;
rb.velocity = new Vector2(rb.velocity.x, jumpshotPower);
anim.SetBool("isJumping", true);
}
}
}
}
GrabberScript.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GrabberScript : MonoBehaviour
{
private bool grabbed, throwObject;
private RaycastHit2D hit;
public float distance;
public Transform holdpoint;
public float throwForce;
public LayerMask notFruitLayer;
private Animator anim;
private void Awake()
{
anim = GameObject.Find("Player Parent").GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.G))
{
if (!grabbed)
{
//grab fruit
anim.Play("PlayerBend");
Physics2D.queriesStartInColliders = false;
hit = Physics2D.Raycast(transform.position, Vector3.down * transform.localScale.x, distance);
if(hit.collider != null && hit.collider.tag == "Apple")
{
grabbed = true;
}
}
else if(!Physics2D.OverlapPoint(holdpoint.position, notFruitLayer))
{
//throw fruit
grabbed = false;
if(hit.collider.gameObject.GetComponent<Rigidbody2D>() != null)
{
PlayerShooting();
}
}
if (grabbed)
{
hit.collider.gameObject.transform.position = holdpoint.position;
}
}
}
private void OnDrawGizmos()
{
Gizmos.color = Color.green;
Gizmos.DrawLine(transform.position, transform.position + Vector3.down * transform.localScale.x * distance);
}
void PlayerShooting()
{
if (grabbed == true)
{
if (Input.GetKey(KeyCode.G))
{
throwObject = true;
hit.collider.gameObject.GetComponent<Rigidbody2D>().velocity = new Vector2(transform.localScale.x, 1) * throwForce;
anim.Play("PlayerShoot");
}
}
}
}

How can I enable shooting only when the npc is in aiming animation?

I have a Animator. First the npc start walking then he stop slowly and aiming. Then with a script I did that when I click the mouse left button it will shoot. This is the last shooting state in the Animator.
Between the walk state and the aiming state I added a parameter and set it to true in the transition :
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Shooting : MonoBehaviour
{
[SerializeField]
private Transform[] firePoints;
[SerializeField]
private Rigidbody projectilePrefab;
[SerializeField]
private float launchForce = 700f;
[SerializeField]
private Animator anim;
[SerializeField]
private bool automaticFire = false;
[SerializeField]
private bool slowDownEffect = false;
private void Start()
{
anim.SetBool("Shooting", true);
}
public void Update()
{
if (Input.GetButtonDown("Fire1") && automaticFire == false)
{
if (anim.GetBool("Shooting") == true)
{
anim.Play("SHOOTING");
LaunchProjectile();
}
}
else
{
if (automaticFire == true)
{
anim.Play("SHOOTING");
LaunchProjectile();
}
}
}
private void LaunchProjectile()
{
foreach (var firePoint in firePoints)
{
Rigidbody projectileInstance = Instantiate(
projectilePrefab,
firePoint.position,
firePoint.rotation);
projectileInstance.AddForce(new Vector3(0, 0, 1) * launchForce);
projectileInstance.gameObject.AddComponent<BulletDestruction>().Init();
}
}
}
The problem is when I'm running the game if I will click the mouse left button "Fire1" it will jump/change from the walk animation to the shooting animation at once and will start shooting.
I want that first the npc will walk then it will change to aiming when he finished changing to aiming only then to be able to shoot "Fire1" .
If the npc is still walking or in the middle of changing to aiming don't allowed to shoot.
Enabling shooting should be only when the npc is in the aiming animation.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Shooting : MonoBehaviour
{
[SerializeField]
private Transform[] firePoints;
[SerializeField]
private Rigidbody projectilePrefab;
[SerializeField]
private float launchForce = 700f;
[SerializeField]
private Animator anim;
[SerializeField]
private bool automaticFire = false;
[SerializeField]
private bool slowDownEffect = false;
private void Start()
{
anim.SetBool("Shooting", true);
}
public void Update()
{
if (isAnimationStatePlaying(anim, 0, "AIMING") == true)
{
if (Input.GetButtonDown("Fire1") && automaticFire == false)
{
if (anim.GetBool("Shooting") == true)
{
anim.Play("SHOOTING");
LaunchProjectile();
}
}
else
{
if (automaticFire == true)
{
anim.Play("SHOOTING");
LaunchProjectile();
}
}
}
}
private void LaunchProjectile()
{
foreach (var firePoint in firePoints)
{
Rigidbody projectileInstance = Instantiate(
projectilePrefab,
firePoint.position,
firePoint.rotation);
projectileInstance.AddForce(new Vector3(0, 0, 1) * launchForce);
projectileInstance.gameObject.AddComponent<BulletDestruction>().Init();
}
}
bool isAnimationStatePlaying(Animator anim, int animLayer, string stateName)
{
if (anim.GetCurrentAnimatorStateInfo(animLayer).IsName(stateName))
return true;
else
return false;
}
}
I added the isAnimationStatePlaying method and using it in the Update and it's working very good now.

How to change player speed with public float from another script

Hey I am trying to change a float when my player collides with a object. I tried many ways of reference but only got null when trying to debug I came up with this so far. I want to get the gameobject that contains the player script meaning the player and after I want to get the component script tankmovement to change the variable in it.
Getting the null reference error in the powerups script line 79 reset function Tank=GameObject.FindWithTag("Player")
using System.Collections.Generic;
using UnityEngine;
public class PowerUp : MonoBehaviour {
public bool boosting = false;
public GameObject effect;
public AudioSource clip;
public GameObject Tank;
private void Start()
{
Tank = GameObject.Find("Tank(Clone)");
TankMovement script = GetComponent<TankMovement>();
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("Player"))
{
if (!boosting)
{
clip.Play();
GameObject explosion = Instantiate(effect, transform.position, transform.rotation);
Destroy(explosion, 2);
GetComponent<MeshRenderer>().enabled = false;
GetComponent<Collider>().enabled = false;
Tank.GetComponent<TankMovement>().m_Speed = 20f;
//TankMovement.m_Speed = 20f;
boosting = true;
Debug.Log(boosting);
StartCoroutine(coolDown());
}
}
private IEnumerator coolDown()
{
if (boosting == true)
{
yield return new WaitForSeconds(4);
{
boosting = false;
GetComponent<MeshRenderer>().enabled = true;
GetComponent<Collider>().enabled = true;
Debug.Log(boosting);
// Destroy(gameObject);
}
}
}
void reset()
{
//TankMovement.m_Speed = 12f;
TankMovement collidedMovement = Tank.gameObject.GetComponent<TankMovement>();
collidedMovement.m_Speed = 12f;
//TankMovement1.m_Speed1 = 12f;
}
}
}
Trying to call on my m_Speed float in the player script to boost the speed of my player when he collides with it. How would you get a proper reference since my player is a prefab.
Tank script
using UnityEngine;
public class TankMovement : MonoBehaviour
{
public int m_PlayerNumber = 1;
public float m_Speed = 12f;
public float m_TurnSpeed = 180f;
public AudioSource m_MovementAudio;
public AudioClip m_EngineIdling;
public AudioClip m_EngineDriving;
public float m_PitchRange = 0.2f;
private string m_MovementAxisName;
private string m_TurnAxisName;
private Rigidbody m_Rigidbody;
private float m_MovementInputValue;
private float m_TurnInputValue;
private float m_OriginalPitch;
private void Awake()
{
m_Rigidbody = GetComponent<Rigidbody>();
}
private void OnEnable ()
{
m_Rigidbody.isKinematic = false;
m_MovementInputValue = 0f;
m_TurnInputValue = 0f;
}
private void OnDisable ()
{
m_Rigidbody.isKinematic = true;
}
private void Start()
{
m_MovementAxisName = "Vertical" + m_PlayerNumber;
m_TurnAxisName = "Horizontal" + m_PlayerNumber;
m_OriginalPitch = m_MovementAudio.pitch;
}
private void Update()
{
// Store the player's input and make sure the audio for the engine is playing.
m_MovementInputValue = Input.GetAxis(m_MovementAxisName);
m_TurnInputValue = Input.GetAxis(m_TurnAxisName);
EngineAudio();
}
private void EngineAudio()
{
// Play the correct audio clip based on whether or not the tank is moving and what audio is currently playing.
if (Mathf.Abs(m_MovementInputValue) < 0.1f && Mathf.Abs(m_TurnInputValue) < 0.1f)
{
if (m_MovementAudio.clip == m_EngineDriving)
{
m_MovementAudio.clip = m_EngineIdling;
m_MovementAudio.pitch = Random.Range(m_OriginalPitch - m_PitchRange, m_OriginalPitch + m_PitchRange);
m_MovementAudio.Play();
}
}
else
{
if (m_MovementAudio.clip == m_EngineIdling)
{
m_MovementAudio.clip = m_EngineDriving;
m_MovementAudio.pitch = Random.Range(m_OriginalPitch - m_PitchRange, m_OriginalPitch + m_PitchRange);
m_MovementAudio.Play();
}
}
}
private void FixedUpdate()
{
// Move and turn the tank.
Move();
Turn();
}
private void Move()
{
// Adjust the position of the tank based on the player's input.
Vector3 movement = transform.forward * m_MovementInputValue * m_Speed * Time.deltaTime;
m_Rigidbody.MovePosition(m_Rigidbody.position + movement);
}
private void Turn()
{
// Adjust the rotation of the tank based on the player's input.
float turn = m_TurnInputValue * m_TurnSpeed * Time.deltaTime;
Quaternion turnRotation = Quaternion.Euler(0f, turn, 0);
m_Rigidbody.MoveRotation(m_Rigidbody.rotation * turnRotation);
}
}
Since the TankMovement component you need to access is attached to the GameObject that is colliding with the power, you can get the TankMovement component you need to change by using other.gameObject.GetComponent<TankMovement>():
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("Player"))
{
if (!boosting)
{
// stuff
TankMovement collidedMovement = other.gameObject.GetComponent<TankMovement>();
collidedMovement.m_Speed = 20f;
// more stuff
}
}
}

Categories

Resources