I need to instantiate an object with a collider, check its collisions, then if its not touching anything, delete it.
is the following code always guaranteed to execute it the right order? (ie: collisions THEN lateUpdate?)
public class RemoveIfColliding : MonoBehaviour
{
bool touching = false;
private void OnCollisionStay(Collision collision)
{
print("I am touching " + collision.other.name);
touching = true;
}
private void LateUpdate()
{
if (!touching)
{
Destroy(gameObject);
}
}
}
Have a look at Order of Execution for Event Functions:
I would intepret it as all messages from the Physics block are called before the GameLogic. It states
The physics cycle might happen more than once per frame if the fixed time step is less than the actual frame update time.
but they should still be done before the Update and LateUpdate calls.
Even if LateUpdate is called first, the object will be destroyed in its next iteration. So basically at seemengly the same time.
But I don't think this is the best way to do it. You can simplify the code by not using the late update and the boolean at all. Just do it like this:
public class RemoveIfColliding : MonoBehaviour
{
private void OnCollisionStay(Collision collision)
{
print("I am touching " + collision.other.name);
Destroy(gameObject);
}
}
Hope this helps!
Related
I am trying to make a check point, I want to make it so when the character hits the checkpoint, the collider turns off (I am open to using a raycast but that wasn't the current plan). I am still new to unity and I can't get the code to compile. I think my issue may be just not calling objects properly???
public class checkpoint : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
cp = GetComponent<Collider>();
cp.isTrigger = true;
object = GameObject.Find("Check Point");
}
private void OnTriggerEnter(cp)
{
cp.GetComponent(BoxCollider).isTrigger = false;
}
}
Not sure what is wrong
When you look at the code in your IDE, you will see red outlines underneath the exact points in your code that have issues. When you hover your cursor over these points the IDE will tell you what the issue is.
You can even use the Show potential fixes functionality and the IDE can often fix the issue for you automatically.
Here is the code modified so that it compiles.
public class checkpoint : MonoBehaviour
{
Collider cp;
// Start is called before the first frame update
void Start()
{
cp = GetComponent<Collider>();
cp.isTrigger = true;
GameObject gameObject = GameObject.Find("Check Point");
}
private void OnTriggerEnter(Collider other)
{
cp.isTrigger = false;
}
}
You were missing declarations for your cp and object variables. Additionally object is a reserved keyword in C#, so you can't use it as a variable name.
On the OnTriggerEnter function declaration the cp parameter was missing its type Collider.
In the body of the method the GetComponent call has incorrect syntax.
UPDATE: You can use the Destroy method to destroy the Collider component when something enters inside its bounds for the first time.
[RequireComponent(typeof(Collider))]
public class Checkpoint : MonoBehaviour
{
void Reset()
{
var collider = GetComponent<Collider>();
collider.isTrigger = true;
}
private void OnTriggerEnter(Collider other)
{
var collider = GetComponent<Collider>();
Destroy(collider);
}
}
If you want to destroy the whole GameObject that contains the checkpoint component and the Collider component you can use Destroy(gameObject);.
I am new to Unity2D. I am trying to make castle defense game. When the spawners start to inheritance the enemies overlap (they should), but when the archer arrow collides whit the enemies it kills them all. I searched everywhere for the answer of this but nothing...
My questions is:
Is there a way to only hit one target at time?
Arrow script:
void Start()
{
target = GameObject.FindGameObjectWithTag("Enemy").GetComponent<Transform>();
}
// Update is called once per frame
void Update()
{
transform.position = Vector2.MoveTowards(transform.position, target.position, speedProjectile * Time.deltaTime);
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.CompareTag("Enemy"))
{
Destroy(gameObject);
}
}
Enemy script:
void Start()
{
target = GameObject.FindGameObjectWithTag("target3").GetComponent<Transform>();
}
// Update is called once per frame
void Update()
{
transform.position = Vector2.MoveTowards(transform.position, target.position, speed * Time.deltaTime);
}
private void OnTriggerEnter2D(Collider2D col)
{
if (col.gameObject.CompareTag("arrow"))
{
EHealth -= HDamage;
}
if (EHealth <= 0)
{
Destroy(gameObject);
}
Your enemies are all taking damage because destroying a GameObject isn't something that happens immediately (for good reason). Because of this, in a single frame, any number of enemies can be hit by the same arrow.
If you'd like to rely on these collision methods, I'd suggest controlling the damage from the arrow instead of from the enemy, so you can be sure it only hits something once:
private bool dealtDamage = false;
private void OnTriggerEnter2D(Collider2D col) {
// Only deal damage once
if (dealtDamage) {
return;
}
// Does the thing I hit have this "EnemyScript" ?
var enemy = col.gameObject.GetComponentInChildren<EnemyScript>();
if (enemy == null) {
return;
}
// Make this "DealDamage()" method public on your EnemyScript
enemy.DealDamage();
dealtDamage = true;
Destroy(gameObject);
}
Then get rid of the Enemy Script's OnTriggerEnter2D because the arrow is handling it all. I don't know what the name of the script is on your enemies so I just called it EnemyScript. This is also calling a DealDamage() method that you'd have to make (which would probably look a lot like the one you current have listed in your "Enemy Script.")
its hard to say with the information given, based on what you have said it seems that all the entity's are just 1 entity(so when you kill 1 enemy you kill the only enemy which is all of them). you can have them run independently from each other.
Maybe you can use collision enter function to check bullet is hitting enemy's body.
You can use this code below...
private bool isEntered = false;
void OnCollisionEnter(Collision collision)
{
if(isEntered) return;
if(collision.gameObject.tag == "enemy") isEntered = true;
....
....
}
I hope it will work for you.
Finally figured it out. First of all the script is dealing damage so I should hit the target specific number of times, so I can't stop the collision of the arrow without turning it back on. So I made a method, that is being Invoked (InvokeRepeating()) after the arrow collides, that turns a bool back to false. Which method (function) of course should be in Update();
I am not sure if I am saying it right bc I am a beginner but I hope this helps somebody. :D
And here is the code:
Arrow Script:
private void DealDamage()
{
if (hit == true)
return;
eHealth.EHealth -= hDamage.HDamage;
hit = true;
}
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.CompareTag("enemy"))
{
DealDamage();
Destroy(gameObject); //Destroying arrow on collision
}
}
private void HitSetter()
{
hit = false;
}
void Update()
{
InvokeRepeating("HitSetter", 0f, 1.1f);
}
Enemy Script:
void Update()
{
if (EHealth <= 0)
{
anim.SetBool("EnemyDie", true);
Destroy(gameObject, 0.833f);
}
Also thanks to #Foggzie
I wanted to create an collider on my players sword, that if he attacks that he would detect and via an animation event turn off/on an gameobject called (Damage Point) who was an script attached that would subtract the enemys health.
But somewhere it does not detect correctly. I tried to add the OnDrawGizmos function to see my sphere collider but even after making it bigger it does not detect.
The most strange thing about my issue is that im using the same code for my monster chest and my fantasy player, but for the chest it works but for the player it does not.
I created a class called PlayerDamage that is attached on an empty gameobject to the tip of my sword.
{
public class PlayerDamage : MonoBehaviour
public int damageAmount = 2;
public LayerMask enemyLayer;
void Update()
{
Collider[] hits = Physics.OverlapSphere(transform.position, 1.7f, enemyLayer);
if (hits.Length > 0)
{
if (hits[0].gameObject.tag == MyTags.ENEMY_TAG)
{
print("COLLIDED WITH ENEMY");
}
}
}
private void OnDrawGizmos()
{
Gizmos.DrawWireSphere(transform.position, 1.7f);
}
In another script called PlayerScript that is attached directly to the player I have a function called Attack:
void Attack()
{
if (Input.GetKeyDown(KeyCode.K))
{
if (!anim.GetCurrentAnimatorStateInfo(0).IsName(MyTags.ATTACK_ANIMATION) || !anim.GetCurrentAnimatorStateInfo(0).IsName(MyTags.RUN_ATTACK_ANIMATION))
{
anim.SetTrigger(MyTags.ATTACK_TRIGGER);
}
}
}
Also in the PlayerScript class there are two functions called ActivateDamagePoint and DeactivateDamagePoint, these are assigned to animation events for the attack animations.
void ActivateDamagePoint()
{
damagePoint.SetActive(true);
}
void DeactivateDamagePoint()
{
damagePoint.SetActive(false);
}
I double checked that everything is on his layer and that the tags are okay, but that did not solve my problem.
Like I said before for the chest I use the same code and it works, but unfortunately it does not work with my player. I also have the Activate and Deactivate DamagePoint functions in there and these are also called by the animation event for my chest attack animation.
Okay after 1hour of debugging I finnaly found the solution:
It was in the MyTags class (a helper class for all the string variables), the error was:
public static string ENEMY_TAG = "Enemey";
instead of:
public static string ENEMY_TAG = "Enemy";
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FirePistol : MonoBehaviour {
public GameObject TheGun;
public GameObject MuzzleFlash;
public AudioSource GunFire;
public bool IsFiring = false;
void Update () {
if (Input.GetButtonDown("Fire1"))
{
if (IsFiring == false)
{
StartCoroutine(FiringPistol());
}
}
}
IEnumerator FiringPistol ()
{
IsFiring = true;
TheGun.GetComponent<Animation>().Play("PistolShot");
MuzzleFlash.SetActive(true);
MuzzleFlash.GetComponent<Animation>().Play("MuzzleAnim");
GunFire.Play();
yield return new WaitForSeconds(0.5f);
IsFiring = false;
}
}
I am writing a gun mechanic And
I wonder why we need
yield return new WaitForSeconds(0.5f); .What is the difference without this command .It's really unnecessary to write this code coz the time is short .Afterall , will it be an error like scene crash after i deleting this code ?Any help is greatly appreciated !
In general: Every method returning IEnumerator has to contain at least one yield statement. In Unity you have to use StartCoroutine to run an IEnumerator as Coroutine.
In your specific case: So you can delay your code by 0.5 seconds!
It is short but 0.5 is about 30 frames!
Someone using e.g. something like AutoClicker could jam the fire key each frame so he would cause significantly more damage then someone playing "normal" (due to physical limitations of your keyboard and finger ;) )
You are just avoiding that and limit down firing to a maximum of 2x per second.
In general - as usual - there are multiple ways to achieve that and you could go without Coroutines entirely but it makes coding so much cleaner and easier to maintain then doing everything in Update!
As some alternative examples for simple delays as here you could also either do a simple timer in Update
private float timer;
void Update ()
{
if(timer > 0)
{
// reduce the timer by time passed since last frame
timer -= Time.deltaTime;
}
else
{
if(Input.GetButtonDown("Fire1"))
{
FiringPistol();
timer = 0.5f;
}
}
}
void FiringPistol()
{
TheGun.GetComponent<Animation>().Play("PistolShot");
MuzzleFlash.SetActive(true);
MuzzleFlash.GetComponent<Animation>().Play("MuzzleAnim");
GunFire.Play();
}
or you can also use Invoke with a given delay.
bool canFire;
void Update ()
{
if (Input.GetButtonDown("Fire1") && canFire)
{
FiringPistol();
}
}
void FiringPistol()
{
TheGun.GetComponent<Animation>().Play("PistolShot");
MuzzleFlash.SetActive(true);
MuzzleFlash.GetComponent<Animation>().Play("MuzzleAnim");
GunFire.Play();
Invoke(nameof(AfterCooldown), 0.5f);
}
void AfterCooldown()
{
canFire = true;
}
In general btw you should store the Animation references to not use GetComponent over and over again:
// if possible already reference these via the Inspector
[SerializeField] private Animation theGunAnimation;
[SerializeField] private Animation muzzleFlashAnimation;
private void Awake()
{
// as fallback get them on runtime
// since this is a fallback and in best case you already referenced these via the Inspector
// we can save a bit of resources and use GetComponent
// only in the case the fields are not already set
// otherwise we can skip using GetComponent as we already have a reference
if(!theGunAnimation) theGunAnimation = TheGun.GetComponent<Animation>();
if(!muzzleFlashAnimation) muzzleFlashAnimation = MuzzleFlash.GetComponent<Animation>();
}
then later you reuse them
theGunAnimation.Play("PistolShot");
MuzzleFlash.SetActive(true);
muzzleFlashAnimation.Play("MuzzleAnim");
GunFire.Play();
In World Scale AR, I want to move to an object I placed using SpawnOnMap. When I touch that object, I want that object to be erased. I made the Player have a script called PlayerController. I made a tag for the object so that when it touches, it should destroy or set it active. When I walk towards the object, nothing happens. I've tried OnTriggerEnter and OnCollisionEnter already. Is there some kind of method I'm missing that Mapbox provides?
public class PelletCollector : MonoBehaviour
{
public int count = 0;
// Start is called before the first frame update
void Start()
{
}
private void OnCollisionEnter(Collision other)
{
if (other.gameObject.tag == "Pellet")
{
count++;
Debug.Log("Collected Pellets: " + count);
other.gameObject.SetActive(false);
//Destroy(other.gameObject);
}
}
}