Colony tech update 1: Creating a game camera, part 2, the base CameraControls class

In the first part of this series on creating a game camera, we saw the Camera object itself, and an example of it in action; albeit moving randomly. If an unpredictable viewing experience is what you're after in your game, then happily you don't need to read any further, otherwise at some point you're going to want to control it.

There are a few ways to control a camera, depending on how you go about it. The ones that I'm going to show in this series are:

  • Using the keyboard
  • Using the screen edges (as you hit the edge of the screen, the camera moves)
  • Using the mouse to drag and fling the camera around (equivalent to dragging with your finger in a mobile game)
  • Following an object
  • Not really a controller per se, but screw it, I'm adding it anyway, clicking to select objects in the current camera view rect

A lot of these share common functionality, so we'll have them all extend a base CameraControls class.

What's in it?

It's mostly to hold common properties and to define the basics, like moveSpeed and zoomSpeed. We define them here rather than the Camera class, as it gives a lot more flexibility and stops unnecessary bloat (the Camera class is fat enough).

We also define whether we should move and zoom using velocity or not. If you're setting these to true, you may need to adjust your move and zoom speed as the effects of both are applied for a much longer time.

Also in there is whether or not we should compensate for the current zoom when moving the camera. Basically how it works is, imagine in a single frame, the camera was going to move 10 pixels. At camera zoom 1.0 (which equates to a scaleX/scaleY of 1.0), our layer would move 10 pixels. If our camera zoom is 0.5, then a move of 10 would actually result in a visual move of 20, as our layer is half the size is normally is. Setting shouldMoveCompenstateForZoom to true will mean that if our zoom is 0.5, the move is scaled to 5 pixels, so visually everything works out. If those last few sentences didn't make any sense, the playing with the code should make it pretty obvious ;) Normally, you should set this to true, as it provides a better user experience.

The code

Again, for the CameraControls to work properly, it needs to be added to an update loop, or at least have update() called every frame. In my Update class (which I'll share a basic version of with the full example code at the end of the series), update() isn't called if active is false.

You can also download it below.

package
{
	import flash.display.Stage;
	import flash.geom.Point;
	
	/**
	 * A generic camera controller
	 * @author Damian Connolly
	 */
	public class CameraControls implements IUpdateObj
	{
		
		/*******************************************************************************************/
		
		/**
		 * The distance between MOUSE_DOWN and MOUSE_UP, where we consider it as a click
		 */
		public static const CLICK_DIST:Number = 4.0;
		
		/*******************************************************************************************/
		
		/**
		 * The speed that we're moving the camera at
		 */
		public var moveSpeed:Number = 200.0;
		
		/**
		 * The speed that we're zooming the camera at
		 */
		public var zoomSpeed:Number = 5.0;
		
		/**
		 * Should we move the camera using velocity?
		 */
		public var shouldMoveWithVelocity:Boolean = false;
		
		/**
		 * Should we zoom the camera using velocity?
		 */
		public var shouldZoomWithVelocity:Boolean = false;
		
		/**
		 * Should we take zoom into account when moving? For the most part, you should leave this
		 * as true, as it gives a better user experience when zoomed in or out. Setting to false
		 * means that we move the same amount regardless of the camera zoom (and as zoom relates
		 * to scale, it means we'll effectively move at twice the speed when we're half the scale)
		 */
		public var shouldMoveCompenstateForZoom:Boolean = true;
		
		/*******************************************************************************************/
		
		/**
		 * The main stage - so we can add event listeners
		 */
		protected var m_stage:Stage = null;
		
		/**
		 * The camera that we're controlling
		 */
		protected var m_camera:Camera = null;
		
		/**
		 * The direction that we're going to move in
		 */
		protected var m_moveDir:Point = null;
		
		/*******************************************************************************************/
		
		private var m_active:Boolean = true; // are the controls active?
		
		/*******************************************************************************************/
		
		/**
		 * Returns true if the CameraControls are active
		 */
		[Inline] public final function get active():Boolean { return this.m_active; }
		[Inline] public final function set active( b:Boolean ):void
		{
			this.m_active = b;
		}
		
		/*******************************************************************************************/
		
		/**
		 * Creates a new camera controller
		 * @param camera The camera that we're controlling
		 * @param stage The main stage
		 */
		public function CameraControls( camera:Camera, stage:Stage ) 
		{
			this.m_camera 	= camera;
			this.m_stage	= stage;
			this.m_moveDir	= new Point;
		}
		
		/**
		 * Destroys the CameraControls and clears it for garbage collection
		 */
		public function destroy():void
		{
			// remove from the update
			// NOTE: it'll need to be removed from the Update
			this.m_active = false;
			
			// clear our properties
			this.m_camera	= null;
			this.m_stage	= null;
			this.m_moveDir	= null;
		}
		
		/**
		 * Updates the CameraControls every frame while they're active
		 * @param dt The delta time since the last update
		 */
		public function update( dt:Number ):void
		{
			// to be overridden
		}
		
	}

}

Colony mailing list

Again, this series is part of a tech update on my current project, Colony. If you want to get updated about it, you can sign up to the mailing list below.

* indicates required

Share: