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 use hitTestPoint(). If your DisplayObjects are Bitmaps with transparent areas, you can then draw the objects in a 1x1 pixel BitmapData 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 this BitmapData and match the pixel colour under the mouse with your object. The click BitmapData doesn't need to be the same fidelity as the final render, and you can implement an isSelectable 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 a BitmapData, 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.

An example of clicking and the camera

Colony mailing list

Plug for the Colony mailing list:

* indicates required

Share: