Search Unity

Help with collision

Discussion in 'Scripting' started by Jsmucker, Oct 24, 2014.

  1. Jsmucker

    Jsmucker

    Joined:
    Sep 5, 2014
    Posts:
    48
    I am trying to check if an object is colliding with another object, then if it is, play a function. Here's what I have so far. I keep getting errors after running the game and am lost.
    Code (CSharp):
    1. void OnTriggerEnter(Collider other) {
    2.         transform.Find("Hero").SendMessage("Damage", 1.0f, SendMessageOptions.DontRequireReceiver);
    3. //Where Hero, is the player game object
    4.     }
    Also when it says "OnTriggerEnter(Collider other)", do I have to enter what collider I am checking for?
     
  2. Deleted User

    Deleted User

    Guest

    Could you post the error report? Is this script on the object that you want to do some thing OnTriggerEnter and does that object have a trigger IE a collider that is set to trigger?
     
  3. Sykoo

    Sykoo

    Joined:
    Jul 25, 2014
    Posts:
    1,394
    Would it not be "other.transform.Find..." ?
     
  4. GarthSmith

    GarthSmith

    Joined:
    Apr 26, 2012
    Posts:
    1,240
    Most likely, your transform does not have a child named exactly "Hero" and so you are getting a NullReferenceException when trying to run SendMessage on a null object.
    Code (csharp):
    1. void OnTriggerEnter(Collider other) {
    2.     Transform heroTransform = transform.Find("Hero");
    3.     if (heroTransform == null)
    4.         Debug.LogWarning("Cannot find a child named 'Hero' under " + name);
    5.     else
    6.         heroTransform.SendMessage("Damage", 1.0f, SendMessageOptions.DontRequireReceiver);
    7. }
     
  5. Jsmucker

    Jsmucker

    Joined:
    Sep 5, 2014
    Posts:
    48
    Thanks for the help, The gameobject hero that I am looking for is not a child of anything. I don't know if that changes anything, but I was wondering how I can get the function to find "Hero" and apply the rest of the script. That object does have a capule collider, and it is set to istigger
     
  6. GarthSmith

    GarthSmith

    Joined:
    Apr 26, 2012
    Posts:
    1,240
    Ohhh okay. Instead of using transform.Find("NameOfObject") which only returns children of *that* specific transform, try capital 'G' GameObject.Find("NameOfObject")
    http://docs.unity3d.com/ScriptReference/GameObject.Find.html

    GameObject.Find() is a bit time consuming so it is suggested to start thinking about other ways to hold on to a reference to your hero.
     
  7. GarthSmith

    GarthSmith

    Joined:
    Apr 26, 2012
    Posts:
    1,240
    Even better, if you are expecting Hero to be triggering the OnTriggerEnter. This way we can avoid using GameObject.Find() as well!
    Code (csharp):
    1. void OnTriggerEnter(Collider other) {
    2.     if (other.name == "Hero")
    3.         other.SendMessage("Damage", 1.0f, SendMessageOptions.DontRequireReceiver);
    4. }
     
    Jsmucker likes this.
  8. Jsmucker

    Jsmucker

    Joined:
    Sep 5, 2014
    Posts:
    48
    Wow, thanks, that worked!
     
  9. Jsmucker

    Jsmucker

    Joined:
    Sep 5, 2014
    Posts:
    48
    One more thing, is there a way to delay the function?
     
  10. GarthSmith

    GarthSmith

    Joined:
    Apr 26, 2012
    Posts:
    1,240
    There's something called Coroutines that would be the easiest way to do this. I would start a new coroutine in your Hero and yield return new WaitForSeconds(1); before causing damage.
     
  11. Jsmucker

    Jsmucker

    Joined:
    Sep 5, 2014
    Posts:
    48
    Maybe thats not exactly what I want. Is there a way to check if you are x seconds into an animation, then apply the damage function?
     
  12. GarthSmith

    GarthSmith

    Joined:
    Apr 26, 2012
    Posts:
    1,240
    There are a few methods of doing this.
    1. Wait for the right time in Update() before doing anything. This involves recording the time the Hero triggered OnTriggerEnter(), then checking against Time.time every frame.
    2. Put an Animation Event in your animation that triggers your Damage() function.
    3. I tend to use Finite State Machines for characters. You could split the animation into two states, and apply damage when the second animation clip plays.
     
  13. Jsmucker

    Jsmucker

    Joined:
    Sep 5, 2014
    Posts:
    48
    Ok, I am trying your second method, but I guess I have to define the damage function and am having trouble doing so. I wrote:
    public void TakeDamage();
    in the beginning of the script. Then I wrote TakeDamage(); in the damage function, but I get an error saying that TakeDamage must have a body.
     
  14. GarthSmith

    GarthSmith

    Joined:
    Apr 26, 2012
    Posts:
    1,240
    Code (csharp):
    1. // Your animation event should call this.
    2. public void TakeDamage()
    3. {
    4.   health -= 1;
    5. }
    6.  
    7. public void Damage(float damageAmount)
    8. {
    9.   futureDamage = damageAmount;
    10.   animation.Play(); // if default animation has an animation event.
    11. }
    12.  
    13. float futureDamage;
    What should happen is that the animation plays a clip, and that clip will call TakeDamage() at some point while it is playing. You will need to open the animation clip in Unity and add the animation event to it.
    http://docs.unity3d.com/Manual/animeditor-AnimationEvents.html
     
  15. Jsmucker

    Jsmucker

    Joined:
    Sep 5, 2014
    Posts:
    48
    So this is what my script looks like now. I know I am messing something up as I am getting errors.
    Code (CSharp):
    1. public void TakeDamage()
    2. {
    3.     health -= 1;
    4. }
    5.  
    6. public void Damage(float damageAmount)
    7. {
    8.     futureDamage = damageAmount;
    9.     animation.Play();
    10. }
    11.  
    12. float futureDamage;
    13. public class PlayerDamage : MonoBehaviour {
    14.    
    15.     void OnTriggerEnter(Collider other) {
    16.         if (other.name == "Hero")
    17.             other.SendMessage("Damage", 1.0f, SendMessageOptions.DontRequireReceiver);
    18.     }
    19. }
     
  16. GarthSmith

    GarthSmith

    Joined:
    Apr 26, 2012
    Posts:
    1,240
    Unfortunately, my time is a bit tight right now and this topic is a pretty complex one. Those lines of code were intended to be in your Hero class.

    Before that though, I suggest just trying to play an animation and detect the animation event on the same object that plays the animation. See if you can get a Debug.Log() statement to print from the AnimationEvent.
    http://docs.unity3d.com/Manual/animeditor-AnimationEvents.html

    You should try to get AnimationEventTriggerTest() to run from the AnimationClip.
    Code (csharp):
    1. using UnityEngine;
    2.  
    3. public class AnimTest : MonoBehaviour
    4. {
    5.   void Start()
    6.   {
    7.     if (animation == null)
    8.       Debug.LogError(name + " is missing an animation!");
    9.     else
    10.       animation.Play();
    11.   }
    12.  
    13.   void AnimationEventTriggerTest()
    14.   {
    15.     Debug.Log("Detected animation event correctly!");
    16.   }
    17. }
     
  17. Jsmucker

    Jsmucker

    Joined:
    Sep 5, 2014
    Posts:
    48
    Sorry, I am not catching on :/. I moved the code from your second to last post to the enemy's script and have made an animation event on the animation. I don't know if it makes any difference, but the Enemy AI, which all this code is put into has animations played through an extension gotten off the Asset store. I don't tell the script to play animations specifically, but use this other extension. I no longer get any errors and the game runs, but the Hero doesn't receive damage for some reason.
     
  18. Jsmucker

    Jsmucker

    Joined:
    Sep 5, 2014
    Posts:
    48
    I am lost, Can we take a few steps back? I just need the reference TakeDamage(); to contain the damage function right? And then have the animation call for TakeDamage? Would there be any other simpler way?
    Why won't this work?
    Code (CSharp):
    1. public void TakeDamage()
    2. {
    3. if (other.name == "Hero")
    4.     other.SendMessage("Damage", 1.0f, SendMessageOptions.DontRequireReceiver);
    5. }
     
  19. GarthSmith

    GarthSmith

    Joined:
    Apr 26, 2012
    Posts:
    1,240
    Yup. A few steps back sounds good.

    So I'm assuming you can deal damage just fine, but the main goal here is to delay by a second, right?

    I'm going to show two methods. The Update() method and the Coroutine() method. The reason is because I can put all the code in one class and you can see how everything works at once without having to mess with animation clips.

    One (not both) of these scripts should be attached to your Hero. Might need to edit them to play the animation or remove health properly.

    Update() way:
    Code (csharp):
    1. using UnityEngine;
    2.  
    3. public class Hero : MonoBehaviour
    4. {
    5.   public float m_delayDamageTime = 1f;
    6.  
    7.   // Should get called by SendMessage in your original code.
    8.   public void Damage(float damageAmount)
    9.   {
    10.     animation.Play(); // however you do this
    11.     m_damageAmount = damageAmount;
    12.     m_waitingToApplyDamage = true;
    13.     m_damageTime = Time.time + m_delayDamageTime;
    14.   }
    15.  
    16.   void Update()
    17.   {
    18.     if (m_waitingToApplyDamage && Time.time >= m_damageTime)
    19.     {
    20.       // Replace this line with however you apply damage.
    21.       health -= m_damageAmount;
    22.       m_waitingToApplyDamage = false;
    23.     }
    24.   }
    25.  
    26.   float health = 10f; // example
    27.   bool m_waitingToApplyDamage;
    28.   float m_damageAmount;
    29.   float m_damageTime;
    30. }
    The coroutine way:
    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class Hero : MonoBehaviour
    5. {
    6.   public float m_delayDamageTime = 1f;
    7.  
    8.   // Should get called by SendMessage in your original code.
    9.   public void Damage(float damageAmount)
    10.   {
    11.     animation.Play(); // however you do this
    12.     StartCoroutine(DamageInSeconds(m_delayDamageTime, damageAmount));
    13.   }
    14.  
    15.   IEnumerator DamageInSeconds(float delayTime, float damageAmount)
    16.   {
    17.     yield return new WaitForSeconds(delayTime);
    18.     health -= damageAmount;
    19.   }
    20.  
    21.   float health = 10f; // example
    22. }
    Not tested. If this doesn't work, let me know what the compile errors are or what is happening instead.
     
  20. Jsmucker

    Jsmucker

    Joined:
    Sep 5, 2014
    Posts:
    48
    Thanks, So what all do I attach to the enemy's AI script? The script you made above didn't show any errors when attached to the Hero. I used the Update() way. Is it just this?
    1. void OnTriggerEnter(Collider other) {
    2. if (other.name == "Hero")
    3. other.SendMessage("Damage", 1.0f, SendMessageOptions.DontRequireReceiver);
    4. }
    When I did this, in the animation editor, the animation event says that the function is not supported. Also, I don't want the Hero to play an animation on death, but the enemy.
     
    Last edited: Oct 30, 2014
  21. A.Killingbeck

    A.Killingbeck

    Joined:
    Feb 21, 2014
    Posts:
    483
    What does your class structures look like? It may be possible to solve this in a much simpler way if we know how you have set up your code.
     
  22. Jsmucker

    Jsmucker

    Joined:
    Sep 5, 2014
    Posts:
    48
    Ok, I'm starting to try something a little different, I am trying your third method where you split the animations into two and apply the TakeDamage function when the second animation plays. How though would I do that? I've tried to find a way to play the function when checking if an animation is playing, but I had no luck. How would I do that, or something else?