The Tile Engine: The Ins and Outs
squaring it all
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | 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.
1 2 3 4 5 6 7 8 9 10 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 | 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 {} } } |
This Post contains multiple pages:
Pages: 1 2 3 4 5 6 7 8 9 10 11

