Unity OnTriggerEnter2D - c#

Anyone can help me how to fix this problem i'm having?
*1st script:
public static bool attacking;
public Collider2D attackTrigger;
private void Awake()
{
attackTrigger.enabled = false;
}
private void Update()
{
if (attacking == true)
{
Debug.Log("Box Collider Enabled");
attackTrigger.enabled = true;
StartCoroutine(DisableCollider());
}
}
IEnumerator DisableCollider()
{
yield return new WaitForSeconds(1);
attacking = false;
attackTrigger.enabled = false;
Debug.Log("Box Collider Disabled");
}
*2nd script:
public float damage = .10f;
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.isTrigger!=true && collision.CompareTag("Enemy"))
{
Debug.Log("Enemy is Damaged");
EHealthBar.TakeDamage(damage);
}
}
i'm trying to get my player character to attack an enemy using collision, well it does work and the player does damage the enemy but it will only work if the enemy's box collider enters my attack again.
if the enemy is already in my area of attack which has a box collision 2d attached, the enemy doesn't get damaged and that's not what i was going for.
I can change it to OnTriggerStay2D but the enemy will keep on getting damaged till the collider is disabled again. care to help?

I think the problem is
IEnumerator DisableCollider()
{
yield return new WaitForSeconds(1);
attacking = false;
...
which causes that your IF statement in your update is still occuring for 1 second and starting a lot of coroutines. The easiest solution would be moving attacking = false; before yield, but i guess you want to use that variable to be aware if user is in attack state. So i suggest to use another bool variable.
if (invokeAttack)
{
attacking = true;
invokeAttack = false
Debug.Log("Box Collider Enabled");
attackTrigger.enabled = true;
StartCoroutine(DisableCollider());
}
Anyway, you didn't show us how you invoke attack, but maybe instead of changing variable (attacking/invokeAttack in your case) to true and checking it in Update you just invoke a method which enables collider and starts coroutine?

Related

While working on my Platformer game in Unity, my Update function as it's assigning "true" to a bool even when character is not Grounded

Firstly, the code:
private bool? _hasJumped = false;
private void Update()
{
Debug.Log("Checking the _hasJumped value in Update(): " + _hasJumped);
Debug.Log("Is the player Grounded?: " + IsGrounded());
if (IsGrounded())
{
_extraJumps = _extraJumpCount;
_coyoteTimer = _coyoteTimerVal;
_hasJumped = false; //The Variable that I am having issues with.
Debug.Log("This statement only runs when the player is on ground!");
}
else
{
_coyoteTimer -= Time.deltaTime;
}
}
public bool IsGrounded()
{
return Physics2D.OverlapCircle(_feetpos.position,_feetCheckRadius,_groundLayerMask);
}
public void GetJumpInput(InputAction.CallbackContext context)
{
if (context.started)
{
_jumpBuffer = Time.time;
}
if (_coyoteTimer > 0f && (Time.time - _jumpBuffer >= 0) && context.performed)
{
if(context.interaction is TapInteraction)
{
_myRigidBody.velocity += new Vector2 (0f, _jumpForce);
_whichInteraction = 0;
}
else if (context.interaction is HoldInteraction)
{
_myRigidBody.velocity += new Vector2 (0f, _jumpForce);
_whichInteraction = 1;
}
_jumpBuffer = null;
_hasJumped = true; //_hasJumped set to true when I first jump
Debug.Log("The Player Has Pressed Jump!: " + _hasJumped);
}
else if (_extraJumps > 0 && context.performed && hasJumped) //Double Jump should ONLY work when I have jumped, and not when I fall off a ledge.
{
Debug.Log(_hasJumped);
_extraJumps--;
if(context.interaction is TapInteraction)
{
_myRigidBody.velocity += new Vector2 (0f, _jumpForce*_secondJumpForceMult);
_whichInteraction = 0;
}
}
else
{
_coyoteTimer = 0f;
}
}
Sorry if the the code is confusing, I've tried to add comments to the variable in focus. But the issue that I'm facing is this:
Right now, when a player falls off the edge, the player is able to jump once because of the additional "extra jump" but this should not be happening. The "extra jump" should only come into effect when the player HAS already jumped.
To stop this, I decided to use a bool "_hasJumped" which (in my head works as follows):
As long as the player is touching the ground, it is false.
When the player first jumps, it is set to true.
Only when it is true can player perform the "extra jump".
If it isn't true, the player cannot perform the "extra jump".
However, the issue that I'm facing is that although _hasJumped is set to true when I jump, it is immediately set to false in the next update (even though I'm still in the air!). Video Evidence #1
I've checked that my IsGrounded() function is working completely fine [Video Evidence #2].
The only reason I can deduce is that it may have something to do with Unity's runtime order? I'm a newbie though so I could be wrong.
I am also using the new Input System.
Thank you for taking the time to read it/give advice/help! I really appreciate it!
The problem is how fast the update actually runs in relation to the way you structured your code.
To visualize, this is what happens when the player pressed the jump button:
First frame: Player presses jump button
Second frame: Character is slightly up on the air, but the overlap collision detector is still touching the ground
Solution
Only check for IsGrounded() when the player presses the jump button, not per-update.
This also saves some computing resource as doing Physics calculation per-frame can be demanding.
Use OnCollisionEnter2D or OnTriggerEnter2D to check if the character has just landed to set canExtraJump to false.
Looks something like this:
bool canExtraJump = false;
void Update(){
// Set extrajump when player inputs jump button, and char is grounded
if (Player_Input_Jump) {
if (IsGrounded()){
Jump();
canExtraJump = true;
}
}
}
void TriggerLandedOnGround(){
canExtraJump = false;
}
// ...
/// Ideally this function should be in the character's feet (as another gameObject)
/// As you do not want this to trigger when the side of a character touches the platform.
void OnTriggerEnter2D(Collider2D other){
if (other.CompareTag("platform")){
TriggerLandedOnGround();
}
}

How do I make it so if the player wants they can hold the animation but if they don't hold it, it completes.. (physics based)

so here is the scenario,
i want the player to be able to perform a trick, and then be able to hold the trick for a longer duration if they want to, but if they don't hold the input then they instead just continue the animation until completion as i haven't actually tried implementing too much and its not giving me the desired result i figured i'd just ask if people have already done it so i don't spend the next 2 hours down a rabbit hole, any and all help is appreciated thanks! :D
(Unity Script)
{
[Header("Trick Attributes")]
public string GroundTagName; // Tag used for all the platforms
public KeyCode stuntKey;
public float AnimationFreezeTime = 0.75f;
public SpriteAnimator anim; // Put the Sprite animator here
public Animator spriteAnimator; // Put the Animator here
private bool isGrounded;
public bool stunting = false;
private void Start()
{
}
private void Update()
{
if (Input.GetKeyDown(stuntKey) && !isGrounded)
{
anim.StartAnimation("FlyingSquirrel");
stunting = true;
Invoke("FreezeAnimation", AnimationFreezeTime);
}
if (Input.GetKeyUp(stuntKey))
{
stunting = false;
spriteAnimator.speed = 1;
}
}
void FreezeAnimation() {
spriteAnimator.speed = 0;
}
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.tag == GroundTagName)
{
isGrounded = true;
}
else
{
isGrounded = false;
}
if (stunting && collision.gameObject.tag == GroundTagName)
{
Destroy(gameObject);
}
}
private void OnCollisionExit(Collision collision)
{
isGrounded = false;
}
}
FlyingSquirrel needs to be broken up into 3 different animations. This can be done in Maya, Blender, uMotion, or even Unity's animation importer:
You need 3 Animator states:
Start_FlyingSquirrel // Player moves into position
Idle_FlyingSquirrel // Player is "holding" the trick
End_FlyingSquirrel // Player let go, return to normal
When the player presses button to start the trick, play the Start animation. For the transition, use "has exit time" to proceed to the Idle State. The Idle state will be true (use a boolean Animator parameter) while the trick is "held". Uncheck "has exit time" for the transition to the End state- you want this to happen instantly.

Reset IgnoreCollision in Unity

I'm trying to make my character do a drop attack in Unity where I want to ignore the collision detection of boxCollider2D for the moment when character & an enemy collide, then undo the ignore. So far the ignore collision is working but it's not reverting back to detect. So after the first successful drop attack my character can pass through the enemy instead of colliding. Here's my code below,
private Rigidbody2D rb;
private Animator anm;
private Collider2D coll;
private enum State {idle, running, jumping, attacking, falling, hurt}
private void OnCollisionEnter2D(Collision2D other) {
if (other.gameObject.tag == "Enemy") {
Enemy enemy = other.gameObject.GetComponent<Enemy>();
if (state == State.falling && anm.GetBool("dropAtk")) { // hurt enemy if drop attack
enemy.Hurt();
Physics2D.IgnoreCollision(other.gameObject.GetComponent<Collider2D>(), GetComponent<Collider2D>(), true);
} else { // take damange
if (other.gameObject.transform.position.x > transform.position.x) {
PlayerHurt("right");
} else {
PlayerHurt("left");
}
}
}
}
private void OnCollisionExit2D(Collision2D other) {
if (other.gameObject.tag == "Enemy") {
if (coll.IsTouchingLayers(ground)) {
Physics2D.IgnoreCollision(other.gameObject.GetComponent<Collider2D>(), GetComponent<Collider2D>(), false);
}
}
}
How can I re-enable collision detection by turning off the IgnoreCollision when character hits the ground? I'm a novice in unity so any suggestion will be really helpful. Thank you!
hope you get the idea
// Update is called once per frame
void FixedUpdate()
{
if(state==dropattck)
{
ChangeTrigger(player, true);
if(player.transform.position.y<floorvalue/*check your floor value*/)
{
transform.position.y = floorvalue;
}
}else
{
ChangeTrigger(player, false);
}
}
public void ChangeTrigger(GameObject obj,bool tf)
{
if(obj.GetComponent<Collider2D>().isTrigger!=tf)
obj.GetComponent<Collider2D>().isTrigger = tf;
}
a solution that comes to mind is to bound the position on the y axis and then temporarily set your collider to trigger. you bound the y so the player doesn't fall through the floor
otherwise you can check when your animation ends and set the collider back to detecting

Unity 2D Platforms Show on Collision

How can I make a group of platforms appear when colliding with it's trigger?
I would like to walk into the trigger, which will turn on the 'StarPlatforms' in the inspector, revealing every thing that is a child of it. I have only got it to do the exact opposite:
Before collision
After Collision
Children of StarPlatfroms
The script on the StarPlatforms:
public class AppearTrigger : MonoBehaviour {
private GameObject StarPlatforms;
void Start() {
StarPlatforms = GameObject.Find("StarPlatforms");
StarPlatforms.gameObject.SetActive (true);
}
void OnTriggerStay2D (Collider2D col) {
if (col.gameObject.tag == "Player") {
StarPlatforms.gameObject.SetActive (false);
}
}
}
I have tried swapping the .SetActives too false then true and it unchecks in the inspector on Play but stays hidden on collision with the trigger.
Thank you for your time.
ps: this is my first go at Unity & C#
SetActive just sets whether it is part of the main game loop or not. To make a platform invisible or visible use it's Renderer component and deactivate that instead.
gameObject.GetComponent<SpriteRenderer>().enabled = false;
I would like to walk into the trigger, which will turn on the
'StarPlatforms' in the inspector. I have tried swapping the .SetActives too false then true
You seem to be guessing which one works. This is how SetActive work:
To turn on or activate an object, pass true to SetActive. To turn it off or deactivate it, pass false to it. Also, this should be done in the OnTriggerEnter2D function not OnTriggerStay2D.
The OnTriggerStay2D function is called when collision each frame collision is still touching. The OnTriggerEnter2D function is called when there is a collision. OnTriggerExit2D is called when there is no longer a collision.
void OnTriggerEnter2D (Collider2D col)
{
if (col.CompareTag("Player"))
{
StarPlatforms.gameObject.SetActive (true);
}
}
If you also want to turn it of or de-activate it when they no longer touch, pass false to the SetActive function in the OnTriggerExit function.
void OnTriggerExit2D(Collider2D col)
{
if (col.CompareTag("Player"))
{
StarPlatforms.gameObject.SetActive (false);
}
}
Not related to your issue but it's a good practice to use CompareTag instead of gameObject.tag.
This is what worked:
public class AppearTrigger : MonoBehaviour {
GameObject[] StarPlatfromArray;
void Start() {
StarPlatfromArray = GameObject.FindGameObjectsWithTag("Cave");
for (int i = 0; i < StarPlatfromArray.Length; i=i+1) {
StarPlatfromArray [i].gameObject.GetComponent<BoxCollider2D> ().enabled = false;
StarPlatfromArray [i].gameObject.GetComponent<SpriteRenderer> ().enabled = false;
}
}
void OnTriggerEnter2D (Collider2D col) {
StarPlatfromArray = GameObject.FindGameObjectsWithTag("Cave");
for (int i = 0; i < StarPlatfromArray.Length; i++) {
StarPlatfromArray [i].gameObject.GetComponent<BoxCollider2D> ().enabled = true;
StarPlatfromArray [i].gameObject.GetComponent<SpriteRenderer> ().enabled = true;
}
}
}

Calling Transform component of an object in another class C#

I am working on a tutorial and the "Side objectives" that they don't walk you through to try and get a feel for it.
So, the way things work at this time is that there is the Player object. The player object has the player script.
public class Player : MonoBehaviour {
private Animator anim;//reference for animator component
private Rigidbody rigidBody;//reference to component for rigidbody
private AudioSource audioSource;
[SerializeField] private float force = 100f;
[SerializeField] private AudioClip sfxJump;
[SerializeField] private AudioClip sfxDeath;
}
void Awake() {//these are assertions that will ensure when writing the cocde that you wont miss them. use for team work.
Assert.IsNotNull (sfxJump);
Assert.IsNotNull (sfxDeath);
}
private bool jump = false; //check for jump
// Use this for initialization
void Start () {//all these are getting components at the start to update them as the code goes onwards.
anim = GetComponent<Animator> ();
rigidBody = GetComponent<Rigidbody> ();
audioSource = GetComponent<AudioSource> ();
positionStart = GetComponent<Transform> ();
}
// Update is called once per frame
void Update () {
if (!GameManager.instance.GameOver && GameManager.instance.GameStarted) {
if (Input.GetMouseButtonDown (0)) {//if press mouse key
GameManager.instance.PlayerStarted ();
rigidBody.useGravity = true;//turn gravity on for component so it goes back down.
audioSource.PlayOneShot (sfxJump);
anim.Play ("jump");//play the animation jump
jump = true;
}
}
}
//Fixed update for physics
void FixedUpdate() {//use this for any physics due to frame rate. time.deltatime wont cut it.
if (jump == true) {//if we are jumping, turn the jump off.
jump = false;
rigidBody.velocity = new Vector2 (0, 0);//turn velocity to 0 so speed doesnt increase while falling
rigidBody.AddForce (new Vector2 (0, force), ForceMode.Impulse);//give a impulse upwards.
}
//print (rigidBody.velocity.y);//print velocity. turn this shit off.
}
//Code to create collision with obstacles and then die and fall through the floor.
void OnCollisionEnter (Collision collision) {//call collision component
if (collision.gameObject.tag == "obstacle") {//if you slap a tagged object called obstacle
rigidBody.AddForce (new Vector2 (-50, 20), ForceMode.Impulse);//add force to push back cause you ded
rigidBody.detectCollisions = false;//turn off the ability to detect collisions
audioSource.PlayOneShot (sfxDeath);//play ded noise
GameManager.instance.PlayerCollided ();
GameManager.instance.Restart ();
}
}
}
The game manager, of course exists in the camera to control the states of the game.
public static GameManager instance = null;//only one in memory. only one gamemanager ever.
[SerializeField] private GameObject mainMenu;
[SerializeField] private GameObject replayBtn;
[SerializeField] private GameObject playBtn;
private bool gameEnd = false;
private bool gameStarted = false;
private bool playerActive = false;
private bool gameOver = false;
//getters setters start
public bool PlayerActive {
get { return playerActive; }
}
public bool GameOver {
get { return gameOver; }
}
public bool GameStarted {
get { return gameStarted; }
}
//to create a state between gameover and main menu
public bool GameEnd {
get {return gameEnd; }
}
//getter setters end
void Awake(){
if (instance == null) {
instance = this;//this means the current instance. one instance of this class.
} else if (instance != this) {//if a seocnd one gets created destroy that bitch.
Destroy (gameObject);
}
DontDestroyOnLoad (gameObject);//allows a game object to persist between the scene. Dont need with one scene.
}
// Use this for initialization
void Start () {
replayBtn.SetActive (false);
}
// Update is called once per frame
void Update () {
}
public void PlayerCollided(){
gameOver = true;
}
public void PlayerStarted(){
playerActive = true;
}
public void EnterGame (){
mainMenu.SetActive(false);
gameStarted = true;
}
//When player dies start coroutine Hold.
public void Restart (){
StartCoroutine (Holdexit());
}
//The hole Coroutine waits 2 seconds then turns on the menu.
IEnumerator Holdexit (){
yield return new WaitForSeconds (2);
playBtn.SetActive (false);
replayBtn.SetActive (true);
mainMenu.SetActive (true);
//add character movement to location
}
}
So, When the player hits the object he dies, loses the ability to touch colliders and falls through the map, after 2 seconds the main menu comes back and the play button is replaced with a replay button. When I press replay, I need to reset the position, the state of the game, and the ability to collide.
I Tried all kinds of things. I did a get component for transform and tried to call it in the coroutine and then set it there, but I couldnt figure it out. I tried just changing the position after etc after the game managers state to restart gets called but the position change occurs before the main menu comes back on because its not being used in the coroutine.
Once thing I though would work, is i created a new method,
public void PlayerReset (){
if (GameManager.instance.Restart()){
//put new changes to player here.
}
}
The errors I came across here was I could not convert type void to bool, I assume its cause I was trying to say if the restart instance existed then function, but the way the restart function is created isn't true or false its just - is.
I really appreciate any help. I think what im going to try and do is make another script to the side and have it call the class of player to pull the components, and then manipulate them from there maybe. AUGH. So confusing. Lmao.
You can just set a public Transform variable on your object, and then in the inspector, drag the selected transform you want to call to that variable.
When that's done, you can use that transform variable in any way you want.
You can save the start position of the player in GameManager as it's a singleton. So, you set the position of the player to this saved position after restart.
You'll need to reinitialize all the variables(i.e. gameEnd,gameStarted, playerActive, gameOver etc.) on restart.
if(GameManager.instance.Restart()) will not work, as Restart() returns void not a boolean.

Categories