Set the focus of the mouse on an object?
by amaranthia · in Torque Game Builder · 09/17/2006 (1:02 pm) · 20 replies
Here's my prob...
I've got two objects: Chicken and Background.
This is what I wanted:
When I drag the mouse over the Background object, I have the cursor turn into a shovel.
When I drag the mouse over the Chicken object, I have the cursor turn into a hand.
This is what is happening:
When the chicken is not over the Background object, the cursor turns into a hand like it should.
When I drag the cursor over the Background object, it turns into a shovel like it should.
However, when the chicken is over the Background object, the cursor does not turn into a hand. It turns into a shovel.
I have the Background object on layer 30.
I have the Chicken object on layer 20.
My question: Shouldn't the Chicken object have priority over the Background object since it is on a higher layer? Does anyone know how to set priority for mouse events and objects?
Thanks much!
I've got two objects: Chicken and Background.
This is what I wanted:
When I drag the mouse over the Background object, I have the cursor turn into a shovel.
When I drag the mouse over the Chicken object, I have the cursor turn into a hand.
This is what is happening:
When the chicken is not over the Background object, the cursor turns into a hand like it should.
When I drag the cursor over the Background object, it turns into a shovel like it should.
However, when the chicken is over the Background object, the cursor does not turn into a hand. It turns into a shovel.
I have the Background object on layer 30.
I have the Chicken object on layer 20.
My question: Shouldn't the Chicken object have priority over the Background object since it is on a higher layer? Does anyone know how to set priority for mouse events and objects?
Thanks much!
#2
yeah, i would do pickPoint as well, which gets all of the objects under a specific point. in this case, the mouse.
maybe something like this:
hope this helps,
joe
09/17/2006 (2:55 pm)
Hi Amanda...yeah, i would do pickPoint as well, which gets all of the objects under a specific point. in this case, the mouse.
maybe something like this:
function myScene::onMouseDown(%this, %mod, %worldPosition, %mouseClicks ){
// Pick some Objects from your scene.
%scenegraph = myScene.getSceneGraph();
%pickList = %scenegraph.pickPoint( %worldPosition );
// Get Object Count.
%objCount = getWordCount( %pickList );
// Ignore if no objects picked.
if ( %objCount == 0 ){
//here you could reset to your default cursor...
return;
}
// Handle Objects.
for ( %n = 0; %n < %objCount; %n++ ){
// Get Object.
%obj = getWord( %pickList, %n );
// Do something with object,
if(%obj.class $= "chickenClass"){
//change Cursor here...
return;
}
if(%obj.class $= "backgroundClass"){
//change Cursor here...
return;
}
}
}hope this helps,
joe
#3
Guys, your code also gave me a good idea for another part of the game!
Here's my fix (datablocks included):
09/17/2006 (3:43 pm)
Okay, I found a really simple fix. But first, the reason I had the problem: The lower an object's layer, the higher its mouse priority. So, whatever is in the background wins the mouse! Could this be a bug? Or maybe GG did this for a reason? It seems to me that objects in the foreground should take precedence...Guys, your code also gave me a good idea for another part of the game!
Here's my fix (datablocks included):
//---------------------------------------------------------------------------------------------
// DEFINE DATABLOCKS
//---------------------------------------------------------------------------------------------
datablock t2dSceneObjectDatablock(BackgroundDatablock)
{
Class = "Background";
Layer = "30";
};
datablock t2dSceneObjectDatablock(ForegroundDatablock)
{
Class = "Foreground";
Layer = "0";
BlendingEnabled = "1";
BlendColor = "255 255 255 0";
UseMouseEvents = "1";
};
datablock t2dSceneObjectDatablock(SpriteDatablock)
{
Class = "Sprite";
Layer = "20";
UseMouseEvents = "1";
};
//---------------------------------------------------------------------------------------------
// CHANGE MOUSE
//---------------------------------------------------------------------------------------------
function Foreground::onMouseMove(%this)
{
Canvas.setCursor(ShovelCursor);
}
function Sprite::onMouseMove(%this)
{
Canvas.setCursor(HandCursor);
}
#4
09/17/2006 (5:21 pm)
I also think that objects on top of other objects should be getting mouse focus... Glad you got it working Amanda!
#5
I submitted a bug to GG. Hopefully they'll take a look at it.
09/17/2006 (5:25 pm)
Good, so it isn't just me. I'm not crazy! :DI submitted a bug to GG. Hopefully they'll take a look at it.
#6
Ever since you posted your solution here I have been trying to learn from it. So first of all, thanks! The solution I posted earlier was sort of a brute force method and I use it in several places in my game. But what you have shown seems so simple and elegant that I felt pretty sure I needed to understand it better. First off I never thought to capture the onMouseMove() callback in any namespace but the scenegraph itself. It was an eye opener just to start thinking along those lines. I don't know why it was an eye opener since I use "onClicks" all over the place... but it was.
My first attempts to capture onMouseMove() events in alternate namespaces met without success so I've decided the key must be in the t2dSceneObjectDatablock which is a structure I haven't used before. I found the tutorial on these structures and I'm playing around with them now.
I don't have a question or anything... just thought I'd bump this thread to say thanks for steering me along these lines. I expect it will produce fruit very soon and eliminate a few brute force pickPoints() out of my code which is always a good thing!
-Robc
09/22/2006 (12:44 pm)
Amaranthia,Ever since you posted your solution here I have been trying to learn from it. So first of all, thanks! The solution I posted earlier was sort of a brute force method and I use it in several places in my game. But what you have shown seems so simple and elegant that I felt pretty sure I needed to understand it better. First off I never thought to capture the onMouseMove() callback in any namespace but the scenegraph itself. It was an eye opener just to start thinking along those lines. I don't know why it was an eye opener since I use "onClicks" all over the place... but it was.
My first attempts to capture onMouseMove() events in alternate namespaces met without success so I've decided the key must be in the t2dSceneObjectDatablock which is a structure I haven't used before. I found the tutorial on these structures and I'm playing around with them now.
I don't have a question or anything... just thought I'd bump this thread to say thanks for steering me along these lines. I expect it will produce fruit very soon and eliminate a few brute force pickPoints() out of my code which is always a good thing!
-Robc
#7
Before I go into this, I'll tell you what I've learned. It may help everyone understand why our mouse events aren't working the way we would expect them to. Torque 2D iterates by layers. It starts on layer 1 and goes to layer 30. This means that if your sprite is on layer 1, the game will check its state first, then go to the next layer, and so on. So, what happens is that your objects in the background overwrite the mouse actions you had for the objects in the foreground. I submitted a bug for this, but was told this was how the system works and it isn't going to change.
Here's the steps I took:
1. Define the following datablocks in Game.cs
2. Define the following mouse commands in Game.cs (be sure to define your cursors in the Cursor.cs file first):
3. Open the game editor and import your sprite image and background image, using the Project > Image Map Builder.
4. On the Create tab, set the datablock to BackgroundDatablock, and then drag your background image on the canvas.
5. On the Create tab, set the datablock to ForegroundDatablock, and then drag your background image on the canvas. (because the alpha for this datablock is 0, the image is invisible on the canvas)
6. On the Create tab, set the datablock to SpriteDatablock, and then drag your sprite image on the canvas.
7. Test the game! If you have problems, let me know!
09/22/2006 (6:43 pm)
For the fun of it, I thought I would post these steps for people who want to try the method I used. :)Before I go into this, I'll tell you what I've learned. It may help everyone understand why our mouse events aren't working the way we would expect them to. Torque 2D iterates by layers. It starts on layer 1 and goes to layer 30. This means that if your sprite is on layer 1, the game will check its state first, then go to the next layer, and so on. So, what happens is that your objects in the background overwrite the mouse actions you had for the objects in the foreground. I submitted a bug for this, but was told this was how the system works and it isn't going to change.
Here's the steps I took:
1. Define the following datablocks in Game.cs
//---------------------------------------------------------------------------------------------
// DEFINE DATABLOCKS
//---------------------------------------------------------------------------------------------
datablock t2dSceneObjectDatablock(BackgroundDatablock)
{
Class = "Background";
Layer = "30";
};
datablock t2dSceneObjectDatablock(ForegroundDatablock)
{
Class = "Foreground";
Layer = "0";
BlendingEnabled = "1";
BlendColor = "255 255 255 0";
UseMouseEvents = "1";
};
datablock t2dSceneObjectDatablock(SpriteDatablock)
{
Class = "Sprite";
Layer = "20";
UseMouseEvents = "1";
}; 2. Define the following mouse commands in Game.cs (be sure to define your cursors in the Cursor.cs file first):
//---------------------------------------------------------------------------------------------
// User drags mouse on sprite.
//---------------------------------------------------------------------------------------------
function Sprite::onMouseMove(%this)
{
Canvas.setCursor(HandCursor);
}
//---------------------------------------------------------------------------------------------
// User drags mouse off sprite.
//---------------------------------------------------------------------------------------------
function Foreground::onMouseMove(%this)
{
Canvas.setCursor(ShovelCursor);
}3. Open the game editor and import your sprite image and background image, using the Project > Image Map Builder.
4. On the Create tab, set the datablock to BackgroundDatablock, and then drag your background image on the canvas.
5. On the Create tab, set the datablock to ForegroundDatablock, and then drag your background image on the canvas. (because the alpha for this datablock is 0, the image is invisible on the canvas)
6. On the Create tab, set the datablock to SpriteDatablock, and then drag your sprite image on the canvas.
7. Test the game! If you have problems, let me know!
#8
Only problem is that I don't ever use the Game Builder itself, I prefer to script things. For some reason when I started building this game the game builder would distort my GUIs... so I lost interest in it. It was probably something I did or something that's long since fixed... but thats where I am. Anyway, since I needed to download the latest version of the engine anyway I went ahead and followed your instructions using the new game builder. I didn't exactly get it to work because I don't have any alternate cursor graphics handy but I did learn the crucial piece of information that I was missing and that I needed to script this method without using the Game Builder...
The association between the sprite/object and its objectDatablock is made by setting config=""; So for example if I want in script to associate a widget object with the "spriteDatablock" I would add a line to the widget definition:
Now when the mouse is over that sprite it will change the cursor or do whatever other action you want to do in the onMouseMove() callback.
I have by no means made everything work perfectly so there might still be issues but I do know that the onMouseMove() callback works and it just became a heck of a lot more flexible as a tool for me! One interesting thing I have noticed is that having a callback in the datablock namespace does not replace the onMouseMove() callback from the scenewindow namespace, both functions execute. If I remember correctly this is not the same as how the onAdd() callback works. I remember trying to have onAdd() execute different commands at the superClass, Class, and sprite level and only the highest level (or was it lowest?) namespace function would execute. Well... I'm rambling now. I hope I've added a little info anyway. I think this is useful way beyond just switching cursors! For one thing my code to highlight selectable objects under the cursor just got a heck of a lot better.
Thanks Amaranthia!!!
-Robc.
09/23/2006 (9:13 am)
Thanks! Only problem is that I don't ever use the Game Builder itself, I prefer to script things. For some reason when I started building this game the game builder would distort my GUIs... so I lost interest in it. It was probably something I did or something that's long since fixed... but thats where I am. Anyway, since I needed to download the latest version of the engine anyway I went ahead and followed your instructions using the new game builder. I didn't exactly get it to work because I don't have any alternate cursor graphics handy but I did learn the crucial piece of information that I was missing and that I needed to script this method without using the Game Builder...
The association between the sprite/object and its objectDatablock is made by setting config=""; So for example if I want in script to associate a widget object with the "spriteDatablock" I would add a line to the widget definition:
new t2DStaticSprite(widget)
{config = "spriteDatablock";}Now when the mouse is over that sprite it will change the cursor or do whatever other action you want to do in the onMouseMove() callback.
I have by no means made everything work perfectly so there might still be issues but I do know that the onMouseMove() callback works and it just became a heck of a lot more flexible as a tool for me! One interesting thing I have noticed is that having a callback in the datablock namespace does not replace the onMouseMove() callback from the scenewindow namespace, both functions execute. If I remember correctly this is not the same as how the onAdd() callback works. I remember trying to have onAdd() execute different commands at the superClass, Class, and sprite level and only the highest level (or was it lowest?) namespace function would execute. Well... I'm rambling now. I hope I've added a little info anyway. I think this is useful way beyond just switching cursors! For one thing my code to highlight selectable objects under the cursor just got a heck of a lot better.
Thanks Amaranthia!!!
-Robc.
#9
09/23/2006 (1:47 pm)
Coolness, I can see what you did! Yeah, I'm using the game editor for the moment, but I'm going to have everything generated by scripts too (much easier I'm finding out) :D
#10
09/23/2006 (2:29 pm)
Do remember that ultimately, it's the "Level Builder"--in that while it can be useful for prototyping/development, it's intended (although necessarily perfect for, yet!) production mode building of dozens/hundreds of levels.
#11
09/23/2006 (4:49 pm)
Thanks for the clarification Stephen, I meant no disrepect to the "Level Builder," I may indeed use it heavily on my next project, I just don't need it on this one. :D
#12
In some cases, it does make much more sense to do things in script--many times I'll create an object in the level builder as a "base", and the procedurally spawn and place them in script for example.
09/24/2006 (1:41 am)
I'm actually the same way--coming from a TorqueScript background (without having the Level Builder) it was (and still is) a bit difficult to get used to the cross-flow between what should be done in LB and what should be done in script.In some cases, it does make much more sense to do things in script--many times I'll create an object in the level builder as a "base", and the procedurally spawn and place them in script for example.
#13
Well, I've spent the better part of the weekend reworking my code to use this new method of capturing mouse events for highlighting objects under the mouse and I guess that I don't like the results. Although I thought my method of using pickpoints was brutish, I'm actually getting a slower framerate now though I'm executing fewer commands to highlight objects. Might be one of those cases of "if it aint broke, don't fix it" :p I also have other anomolies such as flickering in the highlights. I know the problem comes mostly from the steps I have to take to turn the highlight off when the mouse leaves the object, too bad onMouseLeave() or something like it isn't called when the mouse moves off an object. That would be the obvious solution then I wouldn't have to try and use an invisible foreground and/or scheduling methods for turning off highlights. Anyway, I've racked my brain and tried several approaches. Not sure now whether to keep trying or roll back to Friday's build, both choices seem painful... Another day in the life I guess ;)
I'm still grateful for the education I got playing around with these new toys though. Thanks for that! :)
09/24/2006 (1:21 pm)
Most of my objects and sprites are derivates of something I've created already, so copy and pasting script with a quick change of the bitmap and perhaps size and position is usually the fastest method for me.Well, I've spent the better part of the weekend reworking my code to use this new method of capturing mouse events for highlighting objects under the mouse and I guess that I don't like the results. Although I thought my method of using pickpoints was brutish, I'm actually getting a slower framerate now though I'm executing fewer commands to highlight objects. Might be one of those cases of "if it aint broke, don't fix it" :p I also have other anomolies such as flickering in the highlights. I know the problem comes mostly from the steps I have to take to turn the highlight off when the mouse leaves the object, too bad onMouseLeave() or something like it isn't called when the mouse moves off an object. That would be the obvious solution then I wouldn't have to try and use an invisible foreground and/or scheduling methods for turning off highlights. Anyway, I've racked my brain and tried several approaches. Not sure now whether to keep trying or roll back to Friday's build, both choices seem painful...
I'm still grateful for the education I got playing around with these new toys though. Thanks for that! :)
#14
I didn't want to intrude on the post so I didn't mention it (was asked not to do so by the OP in a different thread), so sorry you weren't made aware of the capability.
09/24/2006 (5:51 pm)
Actually, any object can receive ::onMouseEnter() and ::onMouseLeave() events. Amaranthia just chose not to use that method.I didn't want to intrude on the post so I didn't mention it (was asked not to do so by the OP in a different thread), so sorry you weren't made aware of the capability.
#15
09/24/2006 (8:42 pm)
Funny you should mention that, I was looking at a dump() of a staticSprite, something I've done a thousand jillion times... and only today did I notice the sneaky little useMouseEvents data member. I about fell back in my chair. Some days I progress at a phenomenal pace, others I spin my wheels trying to solve problems that don't really exist... guess the last couple days were of the latter variety. I'll chalk it up to the learning process. Thanks for mentioning it and no need to be sorry, I learn best when solutions clunk me over the head like that anyway! ;)
#16
09/24/2006 (10:06 pm)
I can't use them because they aren't available in 1.1.1 for the StaticSprite class. If I remember correctly, they have been created internally and are going to be available for the next release?
#17
09/24/2006 (10:11 pm)
@Amaranthia: you very well could be right--I checked the source code, but I am looking at current internal dev head, not CVS head, so I may be leaking bad information for the current release. I apologize in advance if that's true :(
#18
No worries! I can't wait for the new methods to come out for us. They are going to make things much easier for me. I've even got the methods set up so that I can do a quick swap when the time comes.
@Robert
I've only tried my method on a screen with five sprites, so I haven't seen a hit yet. I'm glad you've tested it. I'd wondered what sort of problems there would be when onMouseMove fired a gazillion times over the background.
09/24/2006 (10:21 pm)
@StephenNo worries! I can't wait for the new methods to come out for us. They are going to make things much easier for me. I've even got the methods set up so that I can do a quick swap when the time comes.
@Robert
I've only tried my method on a screen with five sprites, so I haven't seen a hit yet. I'm glad you've tested it. I'd wondered what sort of problems there would be when onMouseMove fired a gazillion times over the background.
#19
I think what happened to my frame rate is that I at least doubled the calls to onMouseMove routines. Since the sceneWindow onMouseMove still executes and I still need it to take care of camera activity, I am effectively using two callback routines where one did the work before. Even though I have fewer commands to execute, the overhead must be higher. I also say "at least" double because for awhile there I would swear that objects superimposed on each other would all have onMouseMove called, not just the lower layer object. I don't know if this jives with what you have reported because with cursor changes it could be changing several times on each tick and as long as the last change matched what you wanted then you might not be aware of the multiple calls. I suggest you throw some 'echos' in your callbacks and see if they are all firing or whether only the layer you want is firing. That might not be the case though because I confess that at this point I was on the verge of giving up the method so I might not have had my layers right or had everything working correctly. :p
I lost a bit of time on my schedule so I'm going to have to move on to other things. I am curious to learn if you finally decide it is a "go" for you though, so I'll keep in touch with this thread. :) I also see lots of uses for a mouseCallback on every object for me in the future. And something like an onMouseExitObject (if that's what you mean by any object being able to receive an onMouseLeave call) then that would suit me just right also if that is coming in a future release!! :D
09/25/2006 (7:46 am)
Thanks for the info on whats in and not in 1.1.1, I was debating whether to try again and now my way is clear... I wait and stick to the old method. I think what happened to my frame rate is that I at least doubled the calls to onMouseMove routines. Since the sceneWindow onMouseMove still executes and I still need it to take care of camera activity, I am effectively using two callback routines where one did the work before. Even though I have fewer commands to execute, the overhead must be higher. I also say "at least" double because for awhile there I would swear that objects superimposed on each other would all have onMouseMove called, not just the lower layer object. I don't know if this jives with what you have reported because with cursor changes it could be changing several times on each tick and as long as the last change matched what you wanted then you might not be aware of the multiple calls. I suggest you throw some 'echos' in your callbacks and see if they are all firing or whether only the layer you want is firing. That might not be the case though because I confess that at this point I was on the verge of giving up the method so I might not have had my layers right or had everything working correctly. :p
I lost a bit of time on my schedule so I'm going to have to move on to other things. I am curious to learn if you finally decide it is a "go" for you though, so I'll keep in touch with this thread. :) I also see lots of uses for a mouseCallback on every object for me in the future. And something like an onMouseExitObject (if that's what you mean by any object being able to receive an onMouseLeave call) then that would suit me just right also if that is coming in a future release!! :D
#20
Edit: And to clarify when I say "last in the function" - that doesn't mean the objects enter the function all at once for you to dispose of. Each object calls the onMouseOver function individually. I think that's what makes it tricky.
Double Edit: ...tired, but more problems I ran into regarding this have come to mind. When I was using the onMouseDown function objects were getting called twice... when the button was pressed and once more when released. Anyone else have similar experiences?
09/29/2006 (1:19 am)
Amaranthia, I ran into the same problem not long ago. The object on the lowest layer doesn't really have priority... it is in fact the other way around. The chicken gets run through the onMouseOver (or OnMouseDown in my case)... immediately followed by the next object under it. The local variables take new values and you're left seeing what gets run through last in the function Edit: And to clarify when I say "last in the function" - that doesn't mean the objects enter the function all at once for you to dispose of. Each object calls the onMouseOver function individually. I think that's what makes it tricky.
Double Edit: ...tired, but more problems I ran into regarding this have come to mind. When I was using the onMouseDown function objects were getting called twice... when the button was pressed and once more when released. Anyone else have similar experiences?
Torque Owner Robert "Robc" Charney
EDIT: I use this type of routine a lot, here is a code snippit of one routine
function aiControllerObject::deathCheck(%this,%tileID) { //fails (returns true) if martyrs in the area will kill the piece %objList = t2dScene.pickRadius(%tileID.tileCenter, 100, bits($MARTYRMASK)); %objCount = getWordCount(%objList); for(%i=0;%i<%objCount;%i++) { %obj = getWord(%objList, %i); if(%obj.objectType $= "martyr" && %obj.playerNumber == %this.enemy) { return(true); } } return(false); }Instead of returning true or false you could return the paticular cursor you want based on the objects found. Note that I am using a pickRadius here while you probably want only to pickPoint.