Colony tech update 1: Creating a game camera, part 7, selecting objects
The penultimate entry in this series is a short one, dealing simply with selecting different objects. Most games use selecting in one form or another, so how does this tie into the camera that we've been making?
The basic idea is that our CameraClickControls
object registers some mouse listeners with the Stage
. When we get a click, we pass it to the camera, which notifies all of the layers. I chose the pass the local click coordinates rather than the global stage coordinates, as they're more useful, but you can modify it as you see fit.
Working with the CameraMouseControls
There is a quick note to keep in mind when using the CameraClickControls
with the CameraMouseControls class. As the latter controls the camera by dragging it around with the mouse, whenever you mouse up, it would trigger a CLICK
event. To stop this registering with the CameraClickControls
, we add an additional MOUSE_DOWN
listener, that keeps track of the original click position. If the MOUSE_UP
event is within X pixels of the MOUSE_DOWN
event, we count it as a click, otherwise we ignore it.
Working with the GUI
If you have a GUI in your game, you obviously won't want to trigger the CameraClickControls
when you click on a button. This is the same nature of problem as we saw with the CameraMouseEdgeControls class, and the solutions are roughly the same:
- Instead of adding your event listeners to the
Stage
, register them with a central class that only dispatches the event if the GUI wasn't hit - Do your own check in the
CLICK
event to see if you're over any GUI elements - Register "blocks" of the screen as GUI - e.g. the top 100px of the screen is considered GUI, so any clicks here should be ignored
Picking or selecting
The actual code behind picking, or selecting your objects isn't covered here - it's an in-depth subject that would need it's own update, especially if we enter the realm of pixel-perfect picking. For the example below, I used a simple hitTestPoint()
, but for a rough idea, depending on your rendering etc, you can:
- For
DisplayObject
based games, you can usehitTestPoint()
. If yourDisplayObjects
areBitmaps
with transparent areas, you can then draw the objects in a 1x1 pixelBitmapData
and test the alpha value to see if you're clicking on a transparent area or not. - For blitted games, where all graphics are rendered to a single
BitmapData
, you can keep another one in memory and draw each object again,threshold()
ing it to a unique colour. When you click, check thisBitmapData
and match the pixel colour under the mouse with your object. The clickBitmapData
doesn't need to be the same fidelity as the final render, and you can implement anisSelectable
property on your objects to speed the drawing process up - For physics style games (e.g. Box2D), and 3D games, you can use a ray cast to find the selected object
- For Starling games, you can again use the
hitTest()
function, and if you need to deal with transparent areas, do a 1x1 pixel orthographic projection render, convert to aBitmapData
, and check the pixel value. - For all types of games, you can work with math and bounding boxes. This could be a quick and viable solution depending on the result you're looking for. For example, if it's a mobile game, then precision isn't an issue, so a simple bounding box check would work quite well.
The code
As always, make sure and add it to your update loop. You can also download it below. Keep scrolling for an example
package
{
import flash.display.Stage;
import flash.events.MouseEvent;
import flash.geom.Point;
/**
* Lets us select objects through the camera by clicking on them
* @author Damian Connolly
*/
public class CameraClickControls extends CameraControls
{
/*******************************************************************************************/
private var m_mouseDownPos:Point = null; // the point where we've moused down (for clicking)
/*******************************************************************************************/
/**
* Creates a new click controller for the camera
* @param camera The camera that this is for
* @param stage The main stage
*/
public function CameraClickControls( camera:Camera, stage:Stage )
{
super( camera, stage );
this.m_mouseDownPos = new Point;
// add our listeners for our click
this.m_stage.addEventListener( MouseEvent.MOUSE_DOWN, this._onMouseDown );
this.m_stage.addEventListener( MouseEvent.CLICK, this._onClick );
}
/**
* Destroys the CameraClickControls and clears it for garbage collection
*/
override public function destroy():void
{
super.destroy();
// remove our listeners for our click
this.m_stage.removeEventListener( MouseEvent.MOUSE_DOWN, this._onMouseDown );
this.m_stage.removeEventListener( MouseEvent.CLICK, this._onClick );
// null our properties
this.m_mouseDownPos = null;
}
/*******************************************************************************************/
// called when we mouse down on the stage
protected function _onMouseDown( e:MouseEvent ):void
{
// we can only click if we're active
if ( this.active )
this.m_mouseDownPos.setTo( e.stageX, e.stageY );
}
// called when we click on the stage - if we haven't moved much, then it's a click
protected function _onClick( e:MouseEvent ):void
{
// if we're not active, then we can't click
if ( !this.active )
return;
// check our dist and tell our camera, if it's good
if ( MathHelper.dist( e.stageX, e.stageY, this.m_mouseDownPos.x, this.m_mouseDownPos.y ) < CameraControls.CLICK_DIST )
{
this.m_camera.onClick( e.stageX, e.stageY );
return;
}
}
}
}
Example
The click controller code in action; click on the stage to give it focus, then click to select/deselect the ships. Once a ship is selected, you can click again on the stage to move the ship there.
Colony mailing list
Plug for the Colony mailing list:
Comments
Submit a comment