Tile Highlighting Based On Mouse Position
by Jesse Tucker · in Torque Game Builder · 09/08/2009 (9:25 pm) · 3 replies
Hi Everybody!
This is my first official question about TGB, and I'm hoping someone with a bit more experience can guide me in the right direction. I'm pretty new to this, and have a solution that may be difficult but will probably work. I'm just curious to see if someone else knows a faster/easier approach.
I'm trying to script up a system that will draw an alpha highlighter on a tile if the mouse pointer happens to be over it.
Here's my current idea for implementation:
1. Use onMouseMove and onMouseDragged Events and get the mouse worldLocation, then pass that worldLocation into an updateHighlighter() function.
2. In the updateHighlighter() function, I somehow use those world coordinates to determine if the mouse pointer is over a tile. (is there a quick way to do this?)
3. If the mouse is over a tile, draw a highlighter on that tile (won't conflict with the grid graphics, it's currently only being used for pathfinding.) If the mouse is over a new tile or no tile, then clear the highlighter on the previous tile.
Are there better ways to do this? I'm wondering if there's a way to use onMouseEnter/Leave or collision methods for individual tiles, or if I'd be better off dropping sprites at every tile piece and modifying the layer based on onMouseEnter/Leave.
Any help would be greatly appreciated!
-Jesse
This is my first official question about TGB, and I'm hoping someone with a bit more experience can guide me in the right direction. I'm pretty new to this, and have a solution that may be difficult but will probably work. I'm just curious to see if someone else knows a faster/easier approach.
I'm trying to script up a system that will draw an alpha highlighter on a tile if the mouse pointer happens to be over it.
Here's my current idea for implementation:
1. Use onMouseMove and onMouseDragged Events and get the mouse worldLocation, then pass that worldLocation into an updateHighlighter() function.
2. In the updateHighlighter() function, I somehow use those world coordinates to determine if the mouse pointer is over a tile. (is there a quick way to do this?)
3. If the mouse is over a tile, draw a highlighter on that tile (won't conflict with the grid graphics, it's currently only being used for pathfinding.) If the mouse is over a new tile or no tile, then clear the highlighter on the previous tile.
Are there better ways to do this? I'm wondering if there's a way to use onMouseEnter/Leave or collision methods for individual tiles, or if I'd be better off dropping sprites at every tile piece and modifying the layer based on onMouseEnter/Leave.
Any help would be greatly appreciated!
-Jesse
#2
09/11/2009 (2:53 pm)
Here's one of the files i had for highlighting tiles (using, as mentioned above, a non-visual variable to track data):function map::clear(%this)
{
echo("clearing map");
%mapWidth = %this.getTileCountX();
%mapHeight = %this.getTileCountY();
for (%x = 0; %x < %mapWidth; %x++)
{
for (%y = 0; %y < %mapHeight; %y++)
$map[%x, %y] = "";
}
}
function map::initializeMap(%this)
{
%this.clear();
}
function map::onLevelLoaded(%this, %scenegraph)
{
%this.initializeMap();
}
function map::print(%this)
{
%mapWidth = %this.getTileCountX();
%mapHeight = %this.getTileCountY();
for (%y = 0; %y < %mapHeight; %y++)
{
%line = "";
for (%x = 0; %x < %mapWidth; %x++)
if (isObject($map[%x, %y]))
{
%line = %line @ " Y ";
echo("found " @ $map[%x, %y].getName() @ " at " @ %x SPC %y);
}
else
%line = %line @ " N ";
echo(%line);
}
}
function map::setTile(%this, %object, %tile)
{
if (!("" $= %object.tileX))
{
$map[%object.tileX, %object.tileY] = "";
}
%object.tileX = getWord(%tile, 0);
%object.tileY = getWord(%tile, 1);
$map[%object.tileX, %object.tileY] = %object;
}
function map::hasValue(%this, %tile)
{
%x = getWord(%tile, 0);
%y = getWord(%tile, 1);
return isObject($map[%x, %y]);
}
function map::highlightTile(%this, %tile, %red, %green, %blue, %alpha)
{
//--- If they didn't pass in a color, use a default one
if (%red==0 && %green==0 && %blue==0)
{
%red = 0; %green = 1; %blue = 1; %alpha = 0.5;
}
%highlight = new t2dStaticSprite() {
Scenegraph = %this.getScenegraph();
BlendColor = %red SPC %green SPC %blue SPC %alpha;
ImageMap = "squareImageMap";
Layer = "2";
Position = %this.tilePosition(%tile);
Size = %this.getTileSizeX() SPC %this.getTileSizeY();
};
//--- Save this so we can turn it off later
$highlightMap[getWord(%tile, 0), getWord(%tile, 1)] = %highlight;
}
function map::clearHighlights(%this)
{
%mapWidth = map.getTileCountX();
%mapHeight = map.getTileCountY();
for (%x = 0; %x < %mapWidth; %x++)
{
for (%y = 0; %y < %mapHeight; %y++)
%this.dehighlightTile(%x SPC %y);
}
}
function map::dehighlightTile(%this, %tile)
{
%object = $highlightMap[getWord(%tile, 0), getWord(%tile, 1)];
if (isObject(%object))
{
%object.safeDelete();
$highlightMap[getWord(%tile, 0), getWord(%tile, 1)] = "";
}
}
function map::hasLineOfSight(%this, %start, %end)
{
%result = Bresenham(%start, %end);
%isBlocked = false;
//--- Should probably ignore the first and last points.
//--- If we're looking from us to someone else, both of those
//--- points will be occupied
//--- It's easier here to chop the points than at the caller's
//--- end since he won't know what the first point to use would be
%numPoints = %result.getCount() - 1;
for (%i=1; %i < %numPoints; %i++)
{
%object = %result.getObject(%i);
%point = %object.Point;
// echo("Line crosses tile (" @ %point @ ")");
if (%this.hasValue(%point))
{
%isBlocked = true;
map.highlightTile(%Point, 1, 0, 0, 0.5);
}
else
map.highlightTile(%Point, 0, 1, 0, 0.5);
}
echo("hasLineOfSight = " @ !%isBlocked);
return !%isBlocked;
}
function Bresenham(%start, %end)
{
%startX = getWord(%start, 0);
%startY = getWord(%start, 1);
%endX = getWord(%end, 0);
%endY = getWord(%end, 1);
//--- The algorithm only works in one direction
//--- so if that's not, change our problem to match the solution :(
//--- We're going to count along the long end so if the long end
//--- isn't X we're gonna swap X and Y
%isSteep = mAbs(%endY - %startY) > mAbs(%endX - %startX);
if (%isSteep)
{
%t = %startX;
%startX = %startY;
%startY = %t;
%t = %endX;
%endX = %endY;
%endY = %t;
}
//--- Force our line to go left to right
if (%startX > %endX)
{
%t = %startX;
%startX = %endX;
%endX = %t;
%t = %startY;
%startY = %endY;
%endY = %t;
}
%changeInX = %endX - %startX;
%changeInY = mAbs(%endY - %startY);
%error = 0;
if (%startY < %endY)
%yStep = 1;
else
%yStep = -1;
%result = new SimSet();
%y = %startY;
for (%x = %startX; %x <= %endX; %x++)
{
%point = %x SPC %y;
if (%isSteep)
{
%point = %y SPC %x;
}
//--- Using silly syntax because Codeweaver whines
//--- when you define datablocks inside function calls
%t = new SimObject() { Point = %point; };
%result.add(%t);
%error += %changeInY;
if ((2*%error) >= %changeInX)
{
%y += %yStep;
%error -= %changeinX;
}
}
return %result;
}
#3
09/11/2009 (8:52 pm)
Thanks a bunch for the reply! I'll digest your material and see if I can make some use out of it. :)
#4
09/18/2009 (8:21 pm)
Thanks for your help. I figured out a quick way to drop little highlighter sprites at the grid locations:function t2dTileMap::onTileScript (%this, %tilelayer, %tilepos, %tilescript)
{
if ( %tilescript $= "OpenSpace" )
{
%newHighlighterOpen = HighlighterOpenTemplate.cloneWithBehaviors();
%xTile = getWord( %tilepos, 0);
%yTile = getWord( %tilepos, 1);
%tileWorldPos = %tilelayer.getTileWorldPosition(%xTile, %yTile);
%newHighlighterOpen.setPosition(%tileWorldPos);
}
}
function HighlighterOpen::OnMouseEnter(%this)
{
%this.setLayer( 20 );
}
function HighlighterOpen::OnMouseLeave(%this)
{
%this.setLayer( 31 );
}
Torque 3D Owner baylor wetzel
i don't know if there's an onEnter/mouseOver event for individual tiles. What i do is basically what you're suggesting. i track the mouse position globally and then call a function i wrote that determines what tile i'm over. Then i create a sprite a little larger than the tile that is bright yellow and partially transparent
Here's the thing i think you most want - how do i get a tile from a world coordinate? Look in the help file for the method pickTilet2dTileLayer::pickTile(int tileX, int tileY)(int tileX, int tileY)
As for the rest, looking over some of my old code, it seems i took some ideas from http://tdn.garagegames.com/wiki/T2DScript/TileMovement
Here's one of the functions i wrote to get the world coordinates of a specific tile
function t2dTileLayer::tilePosition(%this, %tile) { %xOffset = %this.getPositionX() - (%this.getWidth() /2); %yOffset = %this.getPositionY() - (%this.getHeight()/2); %worldX = %xOffset + (%this.getTileSizeX() * getWord(%tile, 0)) + (%this.getTileSizeX() * 0.5); %worldY = %yOffset + (%this.getTileSizeY() * getWord(%tile, 1)) + (%this.getTileSizeY() * 0.5); return %worldX SPC %worldY; }In my games, i always had a non-visual map that i did all the work on and then just used the TGB map to render it (and to be honest, a lot of times i don't even do that, i just create at runtime arrays of sprites)