Torque 2D/Getting Started/ObjectSelection2Tutorial

From TDN

This page is a Work In Progress.

Torque 2D Tutorial – Object Selection 2: The Complex Method

by: Matthew "King BoB" Langley
edited by: Spider

This tutorial will cover
the following:

  • Mouse Tracking Functions
  • Selecting Objects
  • Moving Objects with the Mouse


Note: we're going to start with the same game.cs file and exec.cs files that we used in our last tutorial.

This tutorial is the second in a three part series on object selection and movement using the mouse. This second part shows a more complex but significantly more versatile method than the first part. This complex method will have an invisible object continually follow the mouse, and then attach selected objects to the invisible object using T2D's “mount” code. The advantages of this method are:

  1. You can apply mount offsets, mount forces, multiple mounts, etc...
  2. It is much more accurate in the aspect of collision and physics. We are not using the setPosition function, so everything will be processed in the game engine properly.
  3. We'll set up a few configurable parameters that allow much more control over how the object follows the mouse.

With these advantages come some small disadvantages. The obvious one is that it takes more coding and more complex coding to use this method, but don't worry... you can handle it. This more complex code does take a bit more processing time as well. In this case there's not a significance performance drop, but any time you increase the complexity of code you should be aware that you're slowing your game down.

Note: This tutorial was written using the early release version of T2D, which has a default white background. To make your screenshots match those shown, open the SDK\games\T2D\data\gui\mainScreenGui.gui file and find/replace the text “logoblack” with “logowhite”.

Note: Any time you do a tutorial or experiment with Torque 2D, it’s a good idea to copy your entire Torque2D directory and modify only the copy. That way your original files remain intact.

Our first steps will be similar to the simple method in part 1 of this tutorial. Start by creating a new script file named "complexMouse.cs" in the . Put in the mouse callbacks, like this:

function sceneWindow2D::onMouseMove( %this, %mod, %worldPos, %mouseClicks )
{
        if($debugMsg::mouse::callBacks)
                echo("Mouse Moving");

}

function sceneWindow2D::onMouseDragged( %this, %mod, %worldPos, %mouseClicks )
{
        if($debugMsg::mouse::callBacks)
                echo("Mouse Dragging");

}

function sceneWindow2D::onMouseDown( %this, %mod, %worldPos, %mouseClicks )
{
        if($debugMsg::mouse::callBacks)
                echo("Mouse Down");

        }

function sceneWindow2D::onMouseUp( %this, %mod, %worldPos, %mouseClicks )
{
        if($debugMsg::mouse::callBacks)
                echo("Mouse Up");
}

function sceneWindow2D::onMouseEnter( %this, %mod, %worldPos, %mouseClicks )
{
        if($debugMsg::mouse::callBacks)
                echo("Mouse Entered... Knew it would come crawling back");
}

function sceneWindow2D::onMouseLeave( %this, %mod, %worldPos, %mouseClicks )
{
        if($debugMsg::mouse::callBacks)
                echo("Mouse Left... Go Get It!");
}

We are going to store the mouse position a little differently this time... instead of storing it in the sceneWindow2D, like we did in this line of the simple method:
%this.mousePos = %worldPos;

we will store it in a different data structure. Functionally speaking it doesn't really matter where you store it, but it can make a huge difference in code readability and organization. This time we are going to be making extensive use of a new global object called mouseObj, so in onMouseMove and onMouseDragged enter the following code:
        //store the mouse position
        $mouseObj::pos = %worldPos;

like this:
function sceneWindow2D::onMouseMove( %this, %mod, %worldPos, %mouseClicks )
{
        if($debugMsg::mouse::callBacks)
                echo("Mouse Moving");

        //store the mouse position
        $mouseObj::pos = %worldPos;
}

function sceneWindow2D::onMouseDragged( %this, %mod, %worldPos, %mouseClicks )
{
        if($debugMsg::mouse::callBacks)
                echo("Mouse Dragging");

        //store the mouse position
        $mouseObj::pos = %worldPos;
}

Now we're storing the mouse position... time to create our invisible object. Add this function:
function initMouse()
{
        $mouseObj::object = new t2dSceneObject() { scenegraph = t2dScene; };
        $mouseObj::object.setSize("1 1");
        $mouseObj::object.setLayer(31);
        $mouseObj::follow = true;
        $mouseObj::stopped = true;
}

As you can see here we simply create a new t2dSceneObject and store it in the global variable $mouseObj::object then we set its size and layer. Finally, we set $mouseObj::follow and $mouseObj::stopped to true. These are toggles that we'll set up and use later. The follow variable will tell T2D whether we want this object to follow the mouse. The stopped variable will be set to true when our object has reached its destination, the mouse pointer.

Now we'll write some code that will make our invisible object move to the mouse position then stop. It might seem like doing that code in onMouseMove and onMouseDragged would work, but not quite, because when the mouse isn't moving those functions don't get called. We want our object to move towards the mouse pointer whether the mouse is moving or not. What we need is a callback that gets called every frame. Of course, T2D has one. It's called onUpdateScene and while it's extremely useful, you want to be careful about not putting too much code in there since it does run every frame.

Add this to the end of your complexMouse.cs file:
function t2dScene::onUpdateScene(%this)
{
        if(($mouseObj::follow) && (!$mouseObj::stopped))
                mouseObjCheck();        
}

So we're checking to make sure that follow is true and stopped is false before we take the time to run through our actual following code in mouseObjCheck. Next, add this line to the end of onMouseMove and onMouseDragged:
        //reset stopped to false so it checks
        $mouseObj::stopped = false;

Like this:
function sceneWindow2D::onMouseMove( %this, %mod, %worldPos, %mouseClicks )
{
        if($debugMsg::mouse::callBacks)
                echo("Mouse Moving");

        //store the mouse position
        $mouseObj::pos = %worldPos;

        //reset stopped to false so it checks
        $mouseObj::stopped = false;
}

function sceneWindow2D::onMouseDragged( %this, %mod, %worldPos, %mouseClicks )
{
        if($debugMsg::mouse::callBacks)
                echo("Mouse Dragging");

        //store the mouse position
        $mouseObj::pos = %worldPos;

        //reset stopped to false so it checks
        $mouseObj::stopped = false;
}

This way, every time we move the mouse we set stopped to false and that will cause the object to start moving towards the mouse again. This type of toggling increases your code efficiency greatly.

The next step is to define what mouseObjCheck actually does. Add this to the end of your script file:

function mouseObjCheck()
{
}

In this function we need to check if the object is at the mouse position and if it is, set stopped to true. If not, we need to tell the object what direction to go. For the actual movement, we'll use the setPolarVelocity function. This means we'll have to get the angle between the object and the mouse position to set the proper direction. All of this is dependent on checking the positions of the mouse and the object, so inside mouseObjCheck add:
        // Get the position of the Mouse itself
        %x = getWord($mouseObj::pos, 0);
        %y = getWord($mouseObj::pos, 1);
                
        // Get the Position of the mounted scene object
        %objPos = $mouseObj::object.getPosition();
        %objX = getWord(%objPos, 0);
        %objY = getWord(%objPos, 1);

If you're not familiar with getWord, heres a quick rundown... The variable $mouseObj::pos will hold a string with a number, a space, then another number. That's how locations are stored in torque (and many other kinds of information composed of multiple values). So, for example, it will be something like "1 1". How do we separate the first and second numbers if it's all one string and not separate variables? We use getWord. Here's an example:

%val = "1 2";
%test1 = getWord(%val, 0);
%test2 = getWord(%val, 1);

In our example, %test1 will end up containing "1" and %test2 will end up containg "2". Pretty simple, right? Okay, back to the code...

At this point we have the x and y of both locations, and we just need to compare them and see if they are equal. You might be asking, "Why didn't you just check if $mouseObj::pos is equal to $mouseObj::object.getPosition()?" and that's a valid question. The answer is that we want a margin of error. We don't need the object to match up down to the decimal point (and T2D is accurate enough to try) so we add this to the mouseObjCheck function:
        %speed = 20;
        %space = 0.25;
        
        if((%objX > (%x - %space)) && (%objX < (%x + %space)) && 
            (%objY > (%y - %space)) && (%objY < (%y + %space)))
        {
                $mouseObj::object.setLinearVelocityPolar(0,0);
                $mouseObj::stopped = true;
        } else
        {
                //set the polar velocity towards the mouse
        }       

Let's break this down... %space is the margin of error we're allowing (we use a variable so it's easy to change) and %speed is going to be used when we set the speed. The if statement is the somewhat complicated part. All we're doing is comparing each x and y position, but we subtract and add the %space to see if our object's position is within that extra margin of error. If our check returns true, then we're close enough to the mouse and we set linearVelocityPolar to velocity 0 and angle 0, then we set stopped to true. If our check returns false, then we need to figure out the direction to the mouse pointer and set a velocity towards it. Put this angleBetween function (for finding the angle to the pointer) at the end of the script file:
function angleBetween(%playerPos, %mousePos)
{
        // Separate Mouse Position
        %mxpos = getWord(%mousePos,0);
        %mypos = getWord(%mousePos,1);

        // Separate Player Position
        %px = getWord(%playerPos,0);
        %py = getWord(%playerPos,1);

        // Calculate Angle from player to mouse (convert to degrees).
        %angle = mRadToDeg( mAtan( %mxpos-%px, %py-%mypos ) );

        return %angle;
}

The function mRadToDeg converts radians to degrees and mAtan returns an arctangent. If you don't know your trigonometry, this might be a bit of a mystery to you. It's beyond the scope of this tutorial to explain this, but you can look this stuff up online or in a trig book... or you can just trust that it works.

Now let's go back to mouseObjCheck and add this bit of code after the “//set the polar velocity towards the mouse” line:
$mouseObj::object.setLinearVelocityPolar(angleBetween(%objPos, $mouseObj::pos),%speed);

The two arguments for setLinearVelocityPolar are angle and speed. For angle we're passing the angle that our angleBetween function calculated. For speed we're just using that %speed variable we set earlier. Your mouseObjCheck function should look like this now:
function mouseObjCheck()
{
        // Get the position of the Mouse itself
        %x = getWord($mouseObj::pos, 0);
        %y = getWord($mouseObj::pos, 1);
                
        // Get the Position of the mounted scene object
        %objPos = $mouseObj::object.getPosition();
        %objX = getWord(%objPos, 0);
        %objY = getWord(%objPos, 1);

        %speed = 20;
        %space = 0.25;
        
        if((%objX > (%x - %space)) && (%objX < (%x + %space)) &&
            (%objY > (%y - %space)) && (%objY < (%y + %space)))
        {
                $mouseObj::object.setLinearVelocityPolar(0,0);
                $mouseObj::stopped = true;
        } else
        {
                //set the polar velocity towards the mouse      
                $mouseObj::object.setLinearVelocityPolar(angleBetween(%objPos, $mouseObj::pos),%speed); 
        }        

}

Alright, this has all the basics we need, but there are a few things we'll need to tweak to make it work properly. One thing we'll need to change is the speed that we use. What we really want is something that will get the distance between the object and the mouse pointer and use that distance to calculate the speed. That way the object will move faster when it's further away from the mouse pointer. To do this, we're going to use another helper function to calculate the distance. Add this to the end of your script file:
function distBetween(%loc1, %loc2)
{       
        %x1 = getWord(%loc1, 0);
        %y1 = getWord(%loc1, 1);

        %x2 = getWord(%loc2, 0);
        %y2 = getWord(%loc2, 1);

        %xd = %x2 - %x1;
        %yd = %y2 - %y1;
        
        return mSqrt((mPow(%xd,2)) + (mPow(%yd,2)));
}

This is a little more simple geometry... mPow returns the first argument to the power of the second argument (in this case, squaring them). Then the function mSqrt takes the square root. That's right, y'all... it's the good 'ol Pythagorean theorem. To make use of this, paste the following code over the “%speed = 20;” line in mouseObjCheck:
                // calculate the distance between the two (for speed calculations)
                %dist = distBetween(%objPos, $mouseObj::pos);
        
                // Set values for easy configuration
                // set speed = distance * 20, that way it slows when approaching - no bobbing around
                %speed = %dist * 20;

In this bit of code, we get the distance between the object and the mouse pointer and then set %speed to that distance times 20. Now, as the object gets closer the velocity will decrease.

Now that our object's velocity is based on its distance from the mouse pointer, we'll have to take care of the possibility of it moving too fast. To avoid this, go back up to your initMouse function and add these values...
        $mouseObj::speed::max = 250;
        $mouseObj::speed::default = 250;

So that it looks like this:
function initMouse()
{
        $mouseObj::object = new t2dSceneObject() { scenegraph = t2dScene; };
        $mouseObj::object.setSize("1 1");
        $mouseObj::object.setLayer(31);
        $mouseObj::follow = true;
        $mouseObj::speed::max = 250;
        $mouseObj::speed::default = 250;
}

We're going to set things up so that if %speed exceeds $mouseObj::speed::max, it gets set back to $mouseObj::speed::default. Go back to your speed calculation code in mouseObjCheck and add this under “%speed = %dist * 20;”
        // limit speed if we want to
        if(%speed > $mouseObj::speed::max)
                %speed = $mouseObj::speed::default;

The last little change we're gonna make is to add a couple debug messages in the if statement of mouseObjCheck. Make it look like this:

function mouseObjCheck()
{

        // Get the position of the Mouse itself
        %x = getWord($mouseObj::pos, 0);
        %y = getWord($mouseObj::pos, 1);
                
        // Get the Position of the mounted scene object
        %objPos = $mouseObj::object.getPosition();
        %objX = getWord(%objPos, 0);
        %objY = getWord(%objPos, 1);

        // calculate the distance between the two (for speed calculations)
        %dist = distBetween(%objPos, $mouseObj::pos);
        
        // Set values for easy configuration
        // set speed = distance * 20, that way it slows when approach - no bobbing around
        %speed = %dist * 20;

        // limit speed if we want to
        if(%speed > $mouseObj::speed::max)
                %speed = $mouseObj::speed::default;

        %space = 0.25;
        
        if((%objX > (%x - %space)) && (%objX < (%x + %space)) && 
            (%objY > (%y - %space)) && (%objY < (%y + %space)))
        {
                if($debugMsg::mouse::object)
                        echo("object at mouse position, halting");

                $mouseObj::object.setLinearVelocityPolar(0,0);
                $mouseObj::stopped = true;
        } else
        {
                if($debugMsg::mouse::object)
                        echo("object seeking mouse at " @ angleBetween(%objPos, $mouseObj::pos));
        
                //set the polar velocity towards the mouse      
                $mouseObj::object.setLinearVelocityPolar(angleBetween(%objPos, $mouseObj::pos),%speed); 
        }        

}


Looks pretty good, now. Let's set up some exec statements and give this a whirl. Go to the exec.cs file that you created in the last tutorial and add a line for complexMouse.cs, and comment out simpleMouse.cs.
//exec("./simpleMouse.cs");
exec("./complexMouse.cs");

Now open game.cs, find your “simpleMouse();” call and replace it with “initMouse();” Save your files and fire up T2D. You should see this:

Image:T2D_tut_ObjSelection2_01.jpg

Ummm... wow... nothing. But if you think about it, that makes sense. If our code is working, we have an invisible object following our mouse. In order to see our object, we need to turn on one of T2D's debugging flags. Enter this into the console:

t2dScene.setDebugOn(BIT(1));

When you exit the console, you should see a little blue box hovering to follow your mouse like this:
Image:T2D_tut_ObjSelection2_02.jpg
Hey, that is pretty cool! Especially when you move the mouse fast. If you don't see the little box following the mouse, make sure that your exec.cs file is as shown above and that you have “initMouse();” in your game.cs file. If so, compare your complexMouse.cs code to this:

function sceneWindow2D::onMouseMove( %this, %mod, %worldPos, %mouseClicks )
{
        if($debugMsg::mouse::callBacks)
                echo("Mouse Moving");

        //store the mouse position
        $mouseObj::pos = %worldPos;

        //reset stopped to false so it checks
        $mouseObj::stopped = false;
}

function sceneWindow2D::onMouseDragged( %this, %mod, %worldPos, %mouseClicks )
{
        if($debugMsg::mouse::callBacks)
                echo("Mouse Dragging");

        //store the mouse position
        $mouseObj::pos = %worldPos;

        //reset stopped to false so it checks
        $mouseObj::stopped = false;
}

function sceneWindow2D::onMouseDown( %this, %mod, %worldPos, %mouseClicks )
{
        if($debugMsg::mouse::callBacks)
                echo("Mouse Down");


}

function sceneWindow2D::onMouseUp( %this, %mod, %worldPos, %mouseClicks )
{
        if($debugMsg::mouse::callBacks)
                echo("Mouse Up");
}

function sceneWindow2D::onMouseEnter( %this, %mod, %worldPos, %mouseClicks )
{
        if($debugMsg::mouse::callBacks)
                echo("Mouse Entered... Knew it would come crawling back");
}

function sceneWindow2D::onMouseLeave( %this, %mod, %worldPos, %mouseClicks )
{
        if($debugMsg::mouse::callBacks)
                echo("Mouse Left... Go Get It!");
}

function initMouse()
{
        $mouseObj::object = new t2dSceneObject() { scenegraph = t2dScene; };
        $mouseObj::object.setSize("1 1");
      $mouseObj::object.setLayer(31);
        $mouseObj::follow = true;
        $mouseObj::speed::max = 250;
        $mouseObj::speed::default = 250;
}

function t2dScene::onUpdateScene(%this)
{
        if(($mouseObj::follow) && (!$mouseObj::stopped))
                mouseObjCheck();        
}

function mouseObjCheck()
{

        // Get the position of the Mouse itself
        %x = getWord($mouseObj::pos, 0);
        %y = getWord($mouseObj::pos, 1);
                
        // Get the Position of the mounted scene object
        %objPos = $mouseObj::object.getPosition();
        %objX = getWord(%objPos, 0);
        %objY = getWord(%objPos, 1);

        // calculate the distance between the two (for speed calculations)
        %dist = distBetween(%objPos, $mouseObj::pos);
        
        // Set values for easy configuration
        // set speed = distance * 20, that way it slows when approach - no bobbing around
        %speed = %dist * 20;

        // limit speed if we want to
        if(%speed > $mouseObj::speed::max)
                %speed = $mouseObj::speed::default;

        %space = 0.25;
        
        if((%objX > (%x - %space)) && (%objX < (%x + %space)) && (%objY > (%y - %space)) && (%objY < (%y + %space)))
        {
                if($debugMsg::mouse::object)
                        echo("object at mouse position, halting");

                $mouseObj::object.setLinearVelocityPolar(0,0);
                $mouseObj::stopped = true;
        } else
        {
                if($debugMsg::mouse::object)
                        echo("object seeking mouse at " @ angleBetween(%objPos, $mouseObj::pos));
        
                //set the polar velocity towards the mouse      
                $mouseObj::object.setLinearVelocityPolar(angleBetween(%objPos, $mouseObj::pos),%speed); 
        }        

}

function angleBetween(%playerPos, %mousePos)
{
        // Separate Mouse Position
        %mxpos = getWord(%mousePos,0);
        %mypos = getWord(%mousePos,1);

        // Separate Player Position
        %px = getWord(%playerPos,0);
        %py = getWord(%playerPos,1);

        // Calculate Angle from player to mouse (convert to degrees).
        %angle = mRadToDeg( mAtan( %mxpos-%px, %py-%mypos ) );

        return %angle;
}

function distBetween(%loc1, %loc2)
{       
        %x1 = getWord(%loc1, 0);
        %y1 = getWord(%loc1, 1);

        %x2 = getWord(%loc2, 0);
        %y2 = getWord(%loc2, 1);

        %xd = %x2 - %x1;
        %yd = %y2 - %y1;
        
         return mSqrt((mPow(%xd,2)) + (mPow(%yd,2)));
}


Cool... our first task, making an invisible object that follows the mouse, is done. Now to move on to the rest of this... when we click on an object, we need to mount it to our invisible object. This is much simpler than what we just did. First, go back to initMouse and add these default parameters:
function initMouse()
{
        $mouseObj::object = new t2dSceneObject() { scenegraph = t2dScene; };
        $mouseObj::object.setSize("1 1");
        $mouseObj::object.setLayer(31);
        $mouseObj::follow = true;
        $mouseObj::speed::max = 250;
        $mouseObj::speed::default = 250;
        $mouseObj::mount::force = 0;
        $mouseObj::mount::offset = "0 0";
        $mouseObj::mount::trackRotation = true;
}

This will come in handy for the next step when we want to mount the object. Next we'll reuse the basic structure from the last tutorial and put it in our onMouseDown function like this:

function sceneWindow2D::onMouseDown( %this, %mod, %worldPos, %mouseClicks )
{
        if($debugMsg::mouse::callBacks)
                echo("Mouse Down");

        //check to see if we have an object already selected
        //if so we don't want to do anything expect set it to false so it doesn't follow anymore
        if(%this.objectSelected)
        {
                if($debugMsg::mouse::selection)
                        echo("unselecting");

                if($mouseObj::isObjMounted)
                {       
                        if(isObject($mouseObj::objMounted))
                        {
                                if($debugMsg::mouse::selection)
                                        echo("dismounting object");

                                $mouseObj::objMounted.dismount();
                                $mouseObj::isObjMounted = false;
                        }
                }

                %this.objectSelected = false;
        } else
        {
                //lets get a list of all the objects at the clicked point in the t2dScene
                %objList = t2dScene.pickPoint(%worldPos);
                //lets get a count of how many objects in the list
                %objCount = getWordCount(%objList);
        
                //we will start looping through the list
                for(%i=0;%i<%objCount;%i++)
                {
                        //grabing the entry corresponding to the loop
                        %obj = getWord(%objList, %i);
                
                        //if we find an object in the list that "isSelectable = true"
        
                        if(%obj.isSelectable)
                        {
                                //we toggle a value so we know we found an object that "isSelectable"
                                %selected = true;
                                //we kick out of the loop
                                %i = %objCount;
                        }       
                }
        
                //if we found an "isSelectable" object
                if(%selected)
                {
                        //we then store that object as the selectedObj
                        %this.selectedObj = %obj;
                
                        if($debugMsg::mouse::selection)
                                echo("You have selected:" SPC %obj SPC "with the name:" SPC %obj.objectName);

                        %this.objectSelected = true;

                        if($debugMsg::mouse::selection) 
                                echo("echo attempting to mount:" SPC %obj SPC " to mouse obj:" 
                                                SPC $mouseObj::object);
                                
                        %obj.mount($mouseObj::object, $mouseObj::mount::offset, 
                                             $mouseObj::mount::force, $mouseObj::mount::trackRotation);
                        $mouseObj::isObjMounted = true;
                        $mouseObj::objMounted = %obj;

                }
        }

}

Okay, this is a lot of code to add all at once, but we've seen almost all of it in the last tutorial. In fact, there are only two differences. The first is when the object is actually selected:
                        if($debugMsg::mouse::selection) 
                                echo("echo attempting to mount:" SPC %obj SPC " to mouse obj:" 
                                                SPC $mouseObj::object);
                                
                        %obj.mount($mouseObj::object, $mouseObj::mount::offset, 
                                             $mouseObj::mount::force, $mouseObj::mount::trackRotation);
                        $mouseObj::isObjMounted = true;
                        $mouseObj::objMounted = %obj;

The mount function we're using here is built into T2D objects. The values we are passing it are the defaults we set up earlier. The second differnece in this new version of onMouseDown is when we unselect our object:
                if($mouseObj::isObjMounted)
                {       
                        if(isObject($mouseObj::objMounted))
                        {
                                if($debugMsg::mouse::selection)
                                        echo("dismounting object");

                                $mouseObj::objMounted.dismount();
                                $mouseObj::isObjMounted = false;
                        }
                }

This just checks if an object is mounted, then makes sure it really is an object, if so it dismounts it and toggles our status variables.

To test our code, we need an object to select. Copy the whole simpleMouse function from simpleMouse.cs and paste it into the end of complexMouse.cs. Rename the function to “complexMouse” and add a call to it under the "initMouse();" line in your game.cs file. If you remember, the simpleMouse function (and now the complexMouse function) just creates a selectable box in the middle of the screen. Save your script files and fire up T2D. Click the box... it should mount and follow with a slight delay:
Image:T2D_tut_ObjSelection2_03.jpg

Cool, right!? If something doesn't work, compare your complexMouse.cs code to this:
function sceneWindow2D::onMouseMove( %this, %mod, %worldPos, %mouseClicks )
{
        if($debugMsg::mouse::callBacks)
                echo("Mouse Moving");

        //store the mouse position
        $mouseObj::pos = %worldPos;

        //reset stopped to false so it checks
        $mouseObj::stopped = false;
}

function sceneWindow2D::onMouseDragged( %this, %mod, %worldPos, %mouseClicks )
{
        if($debugMsg::mouse::callBacks)
                echo("Mouse Dragging");

        //store the mouse position
        $mouseObj::pos = %worldPos;

        //reset stopped to false so it checks
        $mouseObj::stopped = false;
}

function sceneWindow2D::onMouseDown( %this, %mod, %worldPos, %mouseClicks )
{
        if($debugMsg::mouse::callBacks)
                echo("Mouse Down");

        //check to see if we have an object already selected, 
        //if so we don't want to do anything expect set it to false so it doesn't follow anymore
        if(%this.objectSelected)
        {
                if($debugMsg::mouse::selection)
                        echo("unselecting");

                if($mouseObj::isObjMounted)
                {       
                        if(isObject($mouseObj::objMounted))
                        {
                                if($debugMsg::mouse::selection)
                                        echo("dismounting object");

                                $mouseObj::objMounted.dismount();
                                $mouseObj::isObjMounted = false;
                        }
                }

                %this.objectSelected = false;
        } else
        {
                //lets get a list of all the objects at the clicked point in the t2dScene
                %objList = t2dScene.pickPoint(%worldPos);
                //lets get a count of how many objects in the list
                %objCount = getWordCount(%objList);
        
                //we will start looping through the list
                for(%i=0;%i<%objCount;%i++)
                {
                        //grabing the entry corresponding to the loop
                        %obj = getWord(%objList, %i);
                
                        //if we find an object in the list that "isSelectable = true"
        
                        if(%obj.isSelectable)
                        {
                                //we toggle a value so we know we found an object that "isSelectable"
                                %selected = true;
                                //we kick out of the loop
                                %i = %objCount;
                        }       
                }
        
                //if we found an "isSelectable" object
                if(%selected)
                {
                        //we then store that object as the selectedObj
                        %this.selectedObj = %obj;
                
                        if($debugMsg::mouse::selection)
                                echo("You have selected:" SPC %obj SPC "with the name:" SPC %obj.objectName);

                        %this.objectSelected = true;

                        if($debugMsg::mouse::selection) 
                                echo("echo attempting to mount:" SPC %obj SPC " to mouse obj:" 
                                         SPC $mouseObj::object);
                                
                        %obj.mount($mouseObj::object, $mouseObj::mount::offset, 
                                             $mouseObj::mount::force, $mouseObj::mount::trackRotation);
                        $mouseObj::isObjMounted = true;
                        $mouseObj::objMounted = %obj;

                }
        }
}

function sceneWindow2D::onMouseUp( %this, %mod, %worldPos, %mouseClicks )
{
        if($debugMsg::mouse::callBacks)
                echo("Mouse Up");
}

function sceneWindow2D::onMouseEnter( %this, %mod, %worldPos, %mouseClicks )
{
        if($debugMsg::mouse::callBacks)
                echo("Mouse Entered... Knew it would come crawling back");
}

function sceneWindow2D::onMouseLeave( %this, %mod, %worldPos, %mouseClicks )
{
        if($debugMsg::mouse::callBacks)
                echo("Mouse Left... Go Get It!");
}

function initMouse()
{
        $mouseObj::object = new t2dSceneObject() { scenegraph = t2dScene; };
        $mouseObj::object.setSize("1 1");
      $mouseObj::object.setLayer(31);
        $mouseObj::follow = true;
        $mouseObj::speed::max = 250;
        $mouseObj::speed::default = 250;
        $mouseObj::mount::force = 0;
        $mouseObj::mount::offset = "0 0";
        $mouseObj::mount::trackRotation = true;
}

function t2dScene::onUpdateScene(%this)
{
        if(($mouseObj::follow) && (!$mouseObj::stopped))
                mouseObjCheck();        
}

function mouseObjCheck()
{

        // Get the position of the Mouse itself
        %x = getWord($mouseObj::pos, 0);
        %y = getWord($mouseObj::pos, 1);
                
        // Get the Position of the mounted scene object
        %objPos = $mouseObj::object.getPosition();
        %objX = getWord(%objPos, 0);
        %objY = getWord(%objPos, 1);

        // calculate the distance between the two (for speed calculations)
        %dist = distBetween(%objPos, $mouseObj::pos);
        
        // Set values for easy configuration
        // set speed = distance * 20, that way it slows when approach - no bobbing around
        %speed = %dist * 20;

        // limit speed if we want to
        if(%speed > $mouseObj::speed::max)
                %speed = $mouseObj::speed::default;

        %space = 0.25;
        
        if((%objX > (%x - %space)) && (%objX < (%x + %space)) && 
            (%objY > (%y - %space)) && (%objY < (%y + %space)))
        {
                if($debugMsg::mouse::object)
                        echo("object at mouse position, halting");

                $mouseObj::object.setLinearVelocityPolar(0,0);
                $mouseObj::stopped = true;
        } else
        {
                if($debugMsg::mouse::object)
                        echo("object seeking mouse at " @ angleBetween(%objPos, $mouseObj::pos));
        
                //set the polar velocity towards the mouse      
                $mouseObj::object.setLinearVelocityPolar(angleBetween(%objPos, $mouseObj::pos),%speed); 
        }        

}

function angleBetween(%playerPos, %mousePos)
{
        // Seperate Mouse Position
        %mxpos = getWord(%mousePos,0);
        %mypos = getWord(%mousePos,1);

        // Seperate Player Position
        %px = getWord(%playerPos,0);
        %py = getWord(%playerPos,1);

        // Calculate Angle from player to mouse (convert to degrees).
        %angle = mRadToDeg( mAtan( %mxpos-%px, %py-%mypos ) );

        return %angle;
}

function distBetween(%loc1, %loc2)
{       
        %x1 = getWord(%loc1, 0);
        %y1 = getWord(%loc1, 1);

        %x2 = getWord(%loc2, 0);
        %y2 = getWord(%loc2, 1);

        %xd = %x2 - %x1;
        %yd = %y2 - %y1;
        
        return mSqrt((mPow(%xd,2)) + (mPow(%yd,2)));
}

function complexMouse()
{
        $test = new t2dStaticSprite() { sceneGraph = t2dScene; };
        $test.setImageMap( tileMapImageMap );
        $test.setSize( "5 5" );
        $test.setPosition( "0 0" );
        $test.isSelectable = true;
        $test.objectName = "test box";
}

Now it's time to reap the benefits of the way we set up our configuration. Bring up the console and type:

$mouseObj::mount::force = 1;

Now remount the box. So COOL! That's practically a game right there! And if that's not exciting enough for you, you can now test the different configurable properties we stored in $mouseObj. This is one of the great advantages of working out a good data structure beforehand: its easy to go back and tweak the different values. It would also be very easy to make a GUI using this setup.

That's it for Object Selection 2. In the next and final section of this tutorial, we're going to do something with all those debug messages we've been putting into the code.