Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

help noob: "open door on collision example" though

Discussion in 'Scripting' started by gameproducer, Feb 3, 2010.

  1. gameproducer

    gameproducer

    Joined:
    Jan 23, 2010
    Posts:
    25
    I've watched the video tutorials by Will goldstone and bought the Unity game dev book which I'm now reading.

    In the book, there is an example code (similar to the online tutorials):

    Code (csharp):
    1.  
    2. function OnControllerColliderHit(hit: ControllerColliderHit) {
    3.    if (hit.gameobject.tag == "outpostDoor"  doorIsOpen == false) {
    4.       OpenDoor();
    5.    }
    6. }
    7.  
    I started pondering that it seems a bit silly to have "player" to handle door opening. In my opinion, player gameobject shouldn't need to know anything about "opening a door". I think "door opening" should belong to DOOR game object instead.

    Therefore... If I write some sort of "interaction.js" that I could attach to Door object.

    Couldn't I then use something like this:
    Code (csharp):
    1.  
    2. // this in player controller script thing:
    3. function OnControllerColliderHit(hit: ControllerColliderHit) {
    4.    hit.gameobject.parent.interaction.openMe();
    5. }
    6.  
    And then this in the interaction.js:
    Code (csharp):
    1.  
    2. function openMe() {
    3.    if (doorIsOpen == false) {
    4.       // do stuff to open door...
    5.    }
    6. }
    7.  
    I'm very new to this thing and thought that I would first watch the tutorials read the book and then dive into actual programming... where I could actually test this. :D

    Anyway... am I missing something?

    Also a question (again - I'm new to Unity) what if the object that got hit (let's say it's another player or whatever) and it wouldn't have this "interaction.js" attached. How could I prevent error from happening? Can I make something like this:
    Code (csharp):
    1.  
    2. // this in player controller script thing:
    3. function OnControllerColliderHit(hit: ControllerColliderHit) {
    4.    if (hit.gameobject.parent.interaction) {
    5.       hit.gameobject.parent.interaction.openMe();
    6.    }
    7. }
    8.  
    Thanks in advance.
     
  2. Sycle

    Sycle

    Joined:
    Nov 24, 2009
    Posts:
    446
    This is kind of an architecture question, one approach isn't really sillier than the other.

    For instance, if you have a large number of doors in your game, it would make sense to keep the logic all on the player and avoid having extra script instances running. But if you had a large number of varied interactable objects, it might make more sense to put the code on the objects themselves and keep the player more generic.

    As for using something like :

    hit.gameobject.parent.interaction.openMe();

    No, you can't, in programming it's generally frowned upon to just make up the syntax :) You could use something like this though :

    Code (csharp):
    1. hit.gameObject.getComponent("interaction").openMe();
    But like you say, it will cause an error if you try to interact with a collidable not running an 'interaction' script, like a lampshade, or your neighbours dog.

    To be honest checking tags is a good idea, this way it'll only try if it's an object tagged with "outpostDoor".

    Code (csharp):
    1. if (hit.gameobject.tag == "outpostDoor") {    hit.gameObject.getComponent("interaction").openMe();
    2. }
    Alternatively you could use a send message, which will just try to run a function on all scripts on that object :

    Code (csharp):
    1. hit.gameObject.SendMessage("openMe", null, SendMessageOptions.DontRequireReceiver);
    But then why do things half way? Why not make the door detect the collision and open itself? That way the player script doesn't even need to know about it.
     
  3. gameproducer

    gameproducer

    Joined:
    Jan 23, 2010
    Posts:
    25
    Thanks for the reply.

    It simply makes things more... modular if things are done in proper places. For example, if player script takes care of "checking doors" and you put that player script in some other game where's no doors, you can see that the player script does bit more than it "should".

    The "checking tag" perhaps works and seems to be used in the examples, but it means that the player object has dependencies for tons of objects. Now instead of "interact.open" (whether it's Door, Car Door, Can of Food, Bank Account...) I need to make if statements "if hit.door => openDoor, elseif hit.carDoor => openCarDoor, elseif hit.CanOfFood => openFoodCan" etc. If I add another object - let's say Trashcan, I need to edit the player script and create "openTrashCan()" script in player script, which does not seem logical place.

    Maybe I'm missing something, maybe I just need to do some testing and see how things go. From my own background and dealing with objects patterns it just feels that IF-tag-dosomething feels quite inflexible way to do things.

    That's why I was pondering if it's possible to consider having something like.
    Code (csharp):
    1. hit.gameobject.getComponent("interaction").use()
    (no need to do IFs or "find tags")

    Then each object - when added to game - would need interaction script where they could (perhaps?) act differently depending on the nature of the object.

    I haven't yet read about Unity JS classes, so it might be enlightening to get there as well.
     
  4. Sycle

    Sycle

    Joined:
    Nov 24, 2009
    Posts:
    446
    Be careful not to fall into the trap that there is an "ideal" way to do things, these systems don't exist in a vacuum and at the end of the day the most sensible one is one that is easy to maintain and reduces permutations.

    I'll say it again though, if you want to get door opening logic away from the player, why do you stop there? Why not make the door responsible for detecting the player has collided with it and open itself? Isn't that even more modular?

    And your suggested approach will attempt to find a function called Use on a script called "interaction" on EVERY collidable in your world, and will generate errors when it doesn't find one. Using tags (comparing the tag, not finding the tag, big difference) and if statements is just basic validation.

    If having a tag called "outpostDoor" makes you feel all weird inside, make it "interactable" or something. Then you can tag up objects that have that script on them.
     
  5. gameproducer

    gameproducer

    Joined:
    Jan 23, 2010
    Posts:
    25
    Well, if door needs to know about player - then door has a dependency on it.

    Maybe what I'm asking here is that... is it possible to do something like to first check if the object can be interacted with, and then proceed with the usage:
    Code (csharp):
    1.  
    2. if (hit.gameobject.getComponent("interact")) {
    3.    hit.gameobject.getComponent("interact").use()
    4. }
    5.  
    This way the player does not need to know if there's door, car or wallet he is trying to use - and then the target object also has no dependancy on player. Target just knows that "I can be used".

    I'm not trying to do anything too fancy, and I'm unity n00b but have decade or two programming experience with me and I have a pretty good feeling what kind of swamp big number of IF statements can be in terms of keeping the code clear (in certain cases) coupled with dependencies...

    ---

    And your last point:
    Yeh, something like this. Like said, I'm very new to unity so I'm not so familiar how the things are done here. Of course this would mean that when I add "interactable" script to an object I also need to add a tag... :roll:

    I guess this approach is something I can live with. :)

    Thanks again, I really appreciate this.
     
  6. gameproducer

    gameproducer

    Joined:
    Jan 23, 2010
    Posts:
    25
    Forgot to add:

    Code (csharp):
    1. hit.gameObject.SendMessage("openMe", null, SendMessageOptions.DontRequireReceiver);
    I gotta check how this "sendMessage" works.
     
  7. Sycle

    Sycle

    Joined:
    Nov 24, 2009
    Posts:
    446
    This seems rhetoric, but sure. That approach is making the assumption that your interaction script is called interact.js though so it's not like it's completely independent.

    As for tags, have you played around with the Unity interface? They're a dropdown box that once you've populated it you can just pick one off the list. They're very simple to to use, especially if you're using prefabs. (since you'll only need to assign the tag once)

    SendMessage will try to call a function on all script components attached to a gameObject. So you can call a function without knowing what script is attached. This is handy if you wanted to have a script that does doors and another that does teapots - as long as they both have an openMe function they'll both get called.
     
  8. gameproducer

    gameproducer

    Joined:
    Jan 23, 2010
    Posts:
    25
    Yes, naturally there would be dependency to interaction script, but it's step better than having player to know tag of every object.

    Yeh, I have played with the tag system a bit. It is indeed pretty clear. (I just need to adjust my brain a bit to see the big picture :D )

    I just read the sendMessage, and it seems exactly what I was after. (Any idea if it has performance issues or some pitfalls?)
     
  9. andeeeee

    andeeeee

    Joined:
    Jul 19, 2005
    Posts:
    8,768
    Using SendMessage is slower than a direct function call, but it is unlikely to make any real difference unless you are using it in a tight loop.