Tile Engine: GameSprite

the moving bits

The GameSprite class here is not that different from the ones I used in other games. If you read the H.E.R.O tutorial you know exactly how this works.

This is the class which is extended by every moving sprite in the game, namely, the tanks and the bullets. And so it contains properties to store information regarding current position, next position and speeds. I've added a Render stage to the sprite, so that now the sprite is updated, moved, and rendered. And the values for X and Y are stored in the properties _nowX and _nowY. These properties will make sense in the next tutorial when I turn this game into an isometric game.

The different methods here are connected with the Maze collision logic and are:

protected function getPointLeft ():int {
	return _gameData.maze.getPointLeft(this, _speed + int(this.width/_gameData.maze.tileWidth));
	
}
protected function getPointRight ():int {
	return _gameData.maze.getPointRight(this, _speed +  int(this.width/_gameData.maze.tileWidth));
	
}
protected function getPointUp ():int {
	return _gameData.maze.getPointUp(this, _speed + int(this.height/_gameData.maze.tileHeight));
	
}
protected function getPointDown ():int {
	return _gameData.maze.getPointDown(this, _speed + int(this.height/_gameData.maze.tileHeight));
}

These methods call the Maze object and grabs the position of a probable collision point. If these methods return -1, it means there is no collision with the maze.

These methods are called from here:

protected function reachedTile ():Boolean {
	var result:Boolean = false;
	var limitX:int;
	var limitY:int;
	var reachedBase:Boolean = false;
	
	if (_nextY + this.height >= GameConstants.BASE_Y - GameConstants.BASE_HEIGHT) {
		reachedBase = true;
	}
	if (_vx > 0) {
		limitX = getPointRight();
		if (limitX > 0 && (_nextX >= limitX )) {
			
			if (reachedBase) checkBaseCollision(_nextX + this.width/2);
			_nextX = limitX;
			result = true;
		}
	} else if (_vx < 0) {
		limitX = getPointLeft();
		if (limitX > 0 && (_nextX <= limitX )) {
			
			if (reachedBase) checkBaseCollision(_nextX - this.width/2);
			_nextX = limitX;
			result = true;
		}
	}
	if (_vy > 0) {
		limitY = getPointDown();
		if (limitY > 0 && (_nextY >= limitY) ) {
			
			_nextY = limitY;
			if (reachedBase) checkBaseCollision(_nextX);
			result = true;
		}
	} else if (_vy < 0) {
		limitY = getPointUp();
		if (limitY > 0 && (_nextY <= limitY)) {
			
			_nextY = limitY;
			if (reachedBase) checkBaseCollision(_nextX );
			result = true;
		}
	}
	return result;
}

The method checks in which direction the sprite is moving, checks the collision point for that movement, and if there is one and the next position does overlap with it, then the next movement is adjusted so that the solid cell is touched but not crossed.

And finally the checkBaseCollision method which checks to see if the sprite has reached the HQ. It would make sense to have this logic as only part of the enemy tanks, but the enemy bullets need to check this too, so I've decided to have it in the super class of the sprites.

protected function checkBaseCollision (newX:int):void {
	if (newX >= GameConstants.BASE_X - GameConstants.BASE_WIDTH/2 - this.width/2
		&& newX <= GameConstants.BASE_X + GameConstants.BASE_WIDTH/2 + this.width/2) {
		if (this is AiBullet || this is AiTank) {
			//GAME OVER
			GameController.instance.gameOver();
		}
	}
	
}

The GameSprite class

package tiletank.dynamic {

	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Rectangle;
	import flash.geom.Point;
	
	
	import tiletank.*;
	import tiletank.map.*;
	import tiletank.tank.*;
	import tiletank.bullet.*;
	
	
	/**
	* The base class for every moving object in the game
	*/
	public class GameSprite extends Sprite {
		
		
		protected var _manager:ScreenManager;
		protected var _stageElements:StageElements;
		protected var _active:Boolean = true;
		protected var _trashme:Boolean;
		protected var _vx:int = 0;
		protected var _vy:int = 0;
		protected var _nextX:int;
		protected var _nextY:int;
		protected var _nowX:int;
		protected var _nowY:int;
		protected var _gameStage:GameStage;
		protected var _gameData:GameData;
		protected var _hit:Boolean;
		
		protected var _speed:int;
		
		
		function GameSprite () {
			cacheAsBitmap = true;
			_manager = ScreenManager.instance;
			_stageElements = StageElements.instance;
			_gameData = GameData.instance;
			_gameStage = _stageElements.getElementByClass(GameConstants.GAME_STAGE) as GameStage;
			
			addEventListener(Event.REMOVED_FROM_STAGE, removeMe, false, 0, true);
			addEventListener(Event.ADDED_TO_STAGE, initMe, false, 0, true);
			
			//the ScreenManager Events to Update next Poisition of element and then Render the element
			_manager.addEventListener(GameConstants.UPDATE_SCREEN, onUpdate, false, 0, true);
			_manager.addEventListener(GameConstants.SHOW_SCREEN, onShowScreen, false, 0, true);
			
			_gameStage.addEventListener(GameConstants.GAME_PAUSED, onGamePause);
		}

		public function set active (value:Boolean):void {
			_active = value;
		}
		public function get active ():Boolean {
			return _active;
		}
		public function set trashme (value:Boolean):void {
			_trashme  = value;
		}
		public function get trashme  ():Boolean {
			return _trashme;
		}
		
		public function get vy ():int {
			return _vy;
		}
		public function get vx ():int {
			return _vx;
		}

		public function set vy (value:int):void {
			_vy = value;
		}
		public function set vx (value:int):void {
			_vx = value;
		}

		public function get nextY ():int {
			return _nextY;
		}
		public function get nextX ():int {
			return _nextX;
		}

		public function set nextY (value:int):void {
			_nextY = value;
		}
		public function set nextX (value:int):void {
			_nextX = value;
		}
		
		
		public function get left ():int {
			return _nowX - width/2;
		}
		public function get right ():int {
			return _nowX + width/2;
		}
		
		public function get top ():int {
			return _nowY - height/2;
		}
		public function get bottom ():int {
			return _nowY + height/2;
		}
		
		public function get next_left ():int {
			return _nextX - width/2;
		}
		public function get next_right ():int {
			return _nextX + width/2;
		}
		
		public function get next_top ():int {
			return _nextY - height/2;
		}
		public function get next_bottom ():int {
			return _nextY + height/2;
		}
		
		public function get nowY ():int {
			return _nowY;
		}
		public function get nowX ():int {
			return _nowX;
		}

		public function set nowY (value:int):void {
			_nowY = value;
		}
		public function set nowX (value:int):void {
			_nowX = value;
		}
		
		override public function get width ():Number {
			return this.getBounds(stage).width;
			
		}
		
		override public function get height ():Number {
			return this.getBounds(stage).height;
			
		}
		
		public function get speed ():int {
			return _speed;
		}
	
		public function get box ():Rectangle {
			return this.getBounds(stage);
		}
		public function get next_box ():Rectangle {
			var nextOrigin:Point = localToGlobal(new Point(_nextX, _nextY));
			return new Rectangle(nextOrigin.x - width/2, nextOrigin.y - height/2, width, height);
			
		}
		
		public function move ():void {
			if (_gameData.gameMode == GameConstants.PAUSE) return;
			_nowX = _nextX;
			_nowY = _nextY;
			setState();
			render();
		}
		
		public function render ():void {
			x = _nowX;
			y = _nowY;
		}
		
		public function update ():void {
			_nextX = _nowX + _vx;
			_nextY = _nowY + _vy;
		}
		/**
		* Clear positioning data
		*
		*/
		public function reset ():void {
			_vx = _vy = 0;
			_nextX = _nowX = x;
			_nextY = _nowY = y;
		}
		public function setState ():void {
		}
		
		public function destroy ():void {
			_active = false;
		}
		/**
		* Collision Logic for current position
		* @param rect Rectangle of the object checked against collision
		* @returns Boolean, whether or not collision is occurring
		*/
		public function isIntersecting (rect:Rectangle):Boolean {
			return (this.box.intersection(rect).width > 0);
		}
		/**
		* Collision Logic for next iteration
		* @param rect Rectangle of the object checked against collision
		* @returns Boolean, whether or not collision is occurring
		*/
		public function willBeIntersecting (rect:Rectangle):Boolean {
			return (this.next_box.intersection(rect).width > 0);
		}
		
		/**
		* Between updates of the tank, the next position is checked against collision with the maze
		* if collision with the maze is detected, the method reachedTile returns true
		* reachedTile is declared in GameSprite
		*/
		public function checkMazeCollision ():void {
			reachedTile();
		}
		
		protected function getPointLeft ():int {
			return _gameData.maze.getPointLeft(this, _speed + int(this.width/_gameData.maze.tileWidth));
			
		}
		protected function getPointRight ():int {
			return _gameData.maze.getPointRight(this, _speed +  int(this.width/_gameData.maze.tileWidth));
			
		}
		protected function getPointUp ():int {
			return _gameData.maze.getPointUp(this, _speed + int(this.height/_gameData.maze.tileHeight));
			
		}
		protected function getPointDown ():int {
			return _gameData.maze.getPointDown(this, _speed + int(this.height/_gameData.maze.tileHeight));
		}
		
		protected function reachedTile ():Boolean {
			var result:Boolean = false;
			var limitX:int;
			var limitY:int;
			var reachedBase:Boolean = false;
			
			if (_nextY + this.height >= GameConstants.BASE_Y - GameConstants.BASE_HEIGHT) {
				reachedBase = true;
			}
			if (_vx > 0) {
				limitX = getPointRight();
				if (limitX > 0 && (_nextX >= limitX )) {
					
					if (reachedBase) checkBaseCollision(_nextX + this.width/2);
					_nextX = limitX;
					result = true;
				}
			} else if (_vx < 0) {
				limitX = getPointLeft();
				if (limitX > 0 && (_nextX <= limitX )) {
					
					if (reachedBase) checkBaseCollision(_nextX - this.width/2);
					_nextX = limitX;
					result = true;
				}
			}
			if (_vy > 0) {
				limitY = getPointDown();
				if (limitY > 0 && (_nextY >= limitY) ) {
					
					_nextY = limitY;
					if (reachedBase) checkBaseCollision(_nextX);
					result = true;
				}
			} else if (_vy < 0) {
				limitY = getPointUp();
				if (limitY > 0 && (_nextY <= limitY)) {
					
					_nextY = limitY;
					if (reachedBase) checkBaseCollision(_nextX );
					result = true;
				}
			}
			return result;
		}
		
		
		protected function checkBaseCollision (newX:int):void {
			if (newX >= GameConstants.BASE_X - GameConstants.BASE_WIDTH/2 - this.width/2
				&& newX <= GameConstants.BASE_X + GameConstants.BASE_WIDTH/2 + this.width/2) {
				if (this is AiBullet || this is AiTank) {
					//GAME OVER
					
					GameController.instance.gameOver();
				}
			}
			
		}
		/**
		* The UPDATE SCREEN event dispatched by ScreenManager
		*
		*/
		protected function onUpdate (event:Event):void {
			if (!_trashme) update();
		}
		
		/**
		* The SHOW SCREEN event dispatched by ScreenManager
		*
		*/
		protected function onShowScreen (event:Event):void {
			if (_active) move();
		}
		
		
		/**
		* REMOVED_FROM_STAGE Event
		*
		*/
		protected function removeMe (event:Event):void {
			_manager.removeEventListener(GameConstants.UPDATE_SCREEN, onUpdate);
			_manager.removeEventListener(GameConstants.SHOW_SCREEN, onShowScreen);
			removeEventListener(Event.ADDED_TO_STAGE, initMe);
			removeEventListener(Event.REMOVED_FROM_STAGE, removeMe);
			
		}
		
		/**
		* ADDED_TO_STAGE Event
		*
		*/
		protected function initMe (event:Event):void {
			reset();
			
		}
		
		protected function onGamePause (event:Event):void {}
		
	}
}