Author Topic: Differentiating between Widget click, object click, and terrain click  (Read 11471 times)

smartman

  • Guest
Hi,

I'm building a tower-defense style game, and I have the scenario where a user can click on a gameobject (a tower), and when they click on it, I will display an NGUI widget that has some buttons (Such as upgrade, sell, etc).

The issue I'm running into, however, is I can't figure out how to differentiate between what I am clicking on, in order to determine if the user clicked on the gameObject, the widget I brought up, or the ground elsewhere on the screen (in which case, I should remove the widget)

The way I am detecting click is -
in an update loop on my camera, I check to see if I have an input mouse down. If I do, then I convert the mousePoint into a 3d point, and cast a ray, and look at what colliders I hit.
The things to keep in mind are:
  • On my tower, I have a sphere collider (which is the range of the tower) and a box collider (which is the size of the tower). If I hit a boxCollider, I know the user clicked a tower
  • The NGUI widgets don't have a collider on the, thus I can't tell if one was clicked

Does anyone know how to solve the problem? Fundamentally, I need to distinguish between the three clicks, and I essentially can't tell if the user is clicking on the ground or on the panel. Outside of adding a collider to the widget, I'm not sure how to solve this.

Thanks!

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Not sure I understand the issue. No collider means no click event. NGUI's events are only sent to objects with colliders on them. Differentiating between them? Your event will only be sent to the collider's game object... if it's a widget, the widget will receive it. If it's on a 3D object, the 3D object will. Why would you need to differentiate between them when you know which object gets the event? After all, the script handling it is ON that object?

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Edit: Oh you have your own event handling? Yeah, I can't support that. My suggestion -- use NGUI to drive your events. Your UI camera will handle events first. Any events that won't be handed will fall through to your game camera, and will end up on the 3D objects.

smartman

  • Guest
Sorry, can you please explain what you mean?

I'm happy with trying a different approach, but I need to also be able to detect when one of my gameobjects (towers) was clicked, and wasn't sure how to drive this through the NGU eventing model.

JRoch

  • Full Member
  • ***
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 140
    • View Profile
What he's saying is that UICamera will send events to down to any GameObject that has a Collider attached.  There is no difference between NGUI widgets and your custom GameObjects.   If they have a Collider, they receive UICamera events.  All you'd have to do is add a script to that GameObject that implements the type of event you want to respond to, such as OnClick, etc.

So for your tower, if it already has a Collider attached to it, just implement the correct event functions in a script attached to that tower GameObject, and you're good to go.

However, if you have a Collider that is a trigger, you might have to create a child GameObject that has another (identical perhaps) Collider attached to it that catches the NGUI events.  You can either handle them there knowing that you're doing so for the parent object, or turn around and fire off additional events to the parent.

ArenMook, did I get that right?

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Almost. NGUI will work fine with colliders marked as trigger as well. As long as the UICamera attached to the camera(s) has an event layer mask that covers the game object's layer and it has a collider, that game object's scripts will be receiving NGUI's events such as OnClick, OnPress, OnDrag, etc.

smartman

  • Guest
Sorry for the trouble, but it's still not clear to me how to solve the problem. Just a caveat - I'm still fairly new to Unity and learning my way.

To help clarify, let me say what experience I want, vs what I've done code wise -
I want a user to be able to select a tower. When they select a tower, I want to draw 2-3 buttons at the location of the tower, which will let them 'upgrade' 'sell' etc the tower. If they tap a button, I want to know which was tapped. If they tap the tower, it continues drawing the panel. If they tap the ground or elsewhere on the screen, it removes the widget.

Now, given the above, I already have a sphere collider on the tower which acts as a trigger when units come in range. Thus, I added an extra collider on the tower - a box collider - which I can detect as clicking the tower(I do a raycastAll from the mousepoint, and see if I collided with a box).

Besides that, I'm still lost. I could use the default unity events, such as mouseDown, etc (as you listed) but those check for a collision on a collider, thus I can't use them, since I have a large sphere collider which is larger than the object. That's why I chose to do it the 'hard' way by casting my own ray.

Again, any help is appreciated!

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Start by learning NGUI's event system. It's fully capable of doing everything you need. You won't need to do raycasts yourself. Want an event notification for when your tower was selected? OnSelect(true) function. Tapping on the ground? OnClick(), or (my suggestion) -- OnSelect(false).

Get a hang of how NGUI events work, and you'll find the rest to be simple. Follow the tutorials, and (more importantly) watch the videos. They explain everything.

smartman

  • Guest
Thanks for the help so far - still a bit stuck.

I've changed the game over to start using the NGUI events instead of doing it myself, and seem to have most parts fixed.

The issue I'm running into, is that I have a few items that, when clicked, I want to remove the panel that was placed fro the tower. For example, if I click a tower, I see the panel. When I then proceed to click on the terrain, I want to remove that tower.

Thus, this leads to the logical conclusion that I need to add an OnSelect to my terrain, and when selected, go remove any panels that may exist. It works fine if I write a script that does this, and attach it to the terrain.

The issue, however, is that I have several of these kind of objects - terrain, other UI objects, etc, that aren't really selectable. Thus I wanted to go in and create an empty game object I called 'noSelect', and then I set all the other objects, such as terrain, boxes, etc, to this newObject, and attach the noSelect script to this parent object. But when I do this, it doesn't work as expected.

Is there a way to do the above without me having to add the script to every single object that isn't selectable? I'd like to just add it to a parent container.

Thanks again!

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
No. The first time you click a tower, you will get OnSelect with a 'True' parameter. When you click on something else, you will get the same OnSelect call, but with a "False".

As I said, learn the NGUI event system please.

smartman

  • Guest
Re: Differentiating between Widget click, object click, and terrain click
« Reply #10 on: July 14, 2012, 02:44:13 PM »
Sorry, I did spend effort learning it - didn't realize the events would continue to fire.

Given that however, I'm still kind of back at square one -

When a tower is selected, I create a UI panel. When a tower is not selected, I destroy the associated UI panel if I've created one before.
The issue now, is that when I click the UI panel, the tower itself ends up destroying the UIpanel (even though I wanted to click on the UI panel), which defeats the purpose. I need the tower to know if the UI panel was clicked, and if so, to not destroy the panel - which I don't know how to do.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Differentiating between Widget click, object click, and terrain click
« Reply #11 on: July 14, 2012, 02:47:11 PM »
That's right, but then its up to you to do what you wish with the event. You can check to see what the new selected object is, and if it's the panel, do nothing. Likewise the panel itself can have a listener that watches for OnSelect(false) and acts accordingly. Use your imagination here, implementation can differ greatly, I can't tell you how to do things.

smartman

  • Guest
Re: Differentiating between Widget click, object click, and terrain click
« Reply #12 on: July 14, 2012, 03:24:07 PM »
Sure, but I'm still figuring out what NGUI provides.

What you said implies that from any onSelect, I can figure out what the object that was selected is.

I dug around the forums and documentation, and couldn't quite figure out how that can be done. I *think* I can look at UICamera.selectedObject to see who was selected, so in my case, in the tower OnSelect(false) case I would see if selectedObject is my widget/panel, and if so, don't destroy it.

But, selectedObject doesn't seem to be the other object - it is giving me the tower object itself, which isn't what was selected. Am I overlooking something somewhere?

Sorry for the bombardment of questions - I'm just a bit stuck.

On a side note - It'd be awesome if you had a tutorial focused purely on the eventing system you have in place :) (separate from the UI pieces). Similarly, I'd love to see a writeup that explains the camera system you have in place as well, since that isn't entirely clear to me.

JRoch

  • Full Member
  • ***
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 140
    • View Profile
Re: Differentiating between Widget click, object click, and terrain click
« Reply #13 on: July 15, 2012, 06:10:13 AM »
A gameobject that has a collider will receive appropriate messages as NGUI event states change.  It sounds like you're trying to catch all states in one script somewhere in your project, rather than having scripts attached to all of the gameobject with colliders.

I'm not 100% sure because I don't want to dig through the code right now, but UICamera likely generates a series of events for any given cycle, including mouse locations (hover), clicks, drags, etc.  This includes sendig end events for logical items, such as a "select".

So let's say you have two towers.  Clicking collider attached to tower A sends an OnSelect(true) to the gameobject that has that collider.  All scripts attached to that gameobject will try to fire a method of syntax "OnSelect(bool)".

Now you click the collider that is attached to tower B.  Among the burst of events that are dispatched there is an OnSelect(false) that is sent to tower A before an OnSelect(true) is sent to tower B.  (Again, I'm assuming this is the behavior.  I use OnPress events and manage the selection states myself.)

Depending upon where you are checking for the selection target, you could get different gameobjects.  I'm betting that OnSelect(false) still has the selection target as the "old" target, and won't be the new target until OnSelect(true) is called for the methods of the new target.

If you use a common script class to catch all interactions for your tower objects, you'll have to determine which object is being clicked.  For an onSelect(true) you should be able to check this.gameobject.name or this.transform.instanceID or something similar.  Probably InstanceID is a good unique key to use for tracking logical objects.

With NGUI 2.1.0 I'm going to have to dig into the new Generic Event stuff and see if I can completely abandon having to use Unity's native OnGUI for input handling.  Mastering the event system is something I'm just getting started on, so take my advice with a bit of skepticism... I might have something totally wrong.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Differentiating between Widget click, object click, and terrain click
« Reply #14 on: July 15, 2012, 09:28:43 AM »
2.1.0's generic event handler won't help you with input, unfortunately. It's simply a way to have one script receive all events, whether they should go to it or not -- basically making it possible to have a central event manager while still retaining the individual events sent to appropriate objects.