diff --git a/src/client/haxe/ru/m/tankz/render/IRender.hx b/src/client/haxe/ru/m/tankz/render/IRender.hx deleted file mode 100755 index d8a3810..0000000 --- a/src/client/haxe/ru/m/tankz/render/IRender.hx +++ /dev/null @@ -1,10 +0,0 @@ -package ru.m.tankz.render; - -import ru.m.tankz.engine.Engine; -import haxework.gui.IView; - - -interface IRender extends IView { - public function draw(game:Engine):Void; - public function reset():Void; -} diff --git a/src/client/haxe/ru/m/tankz/render/Render.hx b/src/client/haxe/ru/m/tankz/render/Render.hx index 858a7f7..df8e95c 100755 --- a/src/client/haxe/ru/m/tankz/render/Render.hx +++ b/src/client/haxe/ru/m/tankz/render/Render.hx @@ -1,12 +1,9 @@ package ru.m.tankz.render; -import ru.m.tankz.core.MobileEntity; +import flash.display.DisplayObject; +import Type.ValueType; +import ru.m.tankz.render.RenderItem; import ru.m.tankz.engine.Engine; -import ru.m.tankz.core.Entity; -import ru.m.tankz.map.Brick; -import ru.m.geom.Direction; -import flash.geom.Matrix; -import openfl.Assets; import ru.m.tankz.core.Bullet; import ru.m.tankz.core.Tank; import flash.display.Sprite; @@ -14,64 +11,18 @@ import flash.display.Graphics; import haxework.gui.SpriteView; -interface IState { - public function update(object:T):Bool; -} - -class BrickState implements IState { - private var type:Int; - - public function new() {} - - public function update(object:Brick):Bool { - if (type != object.config.type) { - type = object.config.type; - return true; - } - return false; - } -} - -class EntityState implements IState { - private var x:Float; - private var y:Float; - private var d:Direction; - private var type:Int; - - public function new() {} - - public function update(object:Entity):Bool { - if (x != object.rect.x || y != object.rect.y || !d.equals(object.rect.direction)) { - x = object.rect.x; - y = object.rect.y; - d = object.rect.direction; - return true; - } - if (Std.is(object, Tank)) { - var tank:Tank = cast object; - if (tank.config.type != type) { - type = tank.config.type; - return true; - } - } - return false; - } -} - - -class Render extends SpriteView implements IRender { +class Render extends SpriteView { private var backgroundLayer:Sprite; private var groundLayer:Sprite; private var entryLayer:Sprite; private var upLayer:Sprite; - private var layersForUpdate:Map; - private var states:Map>; + private var background:Sprite; + private var items:Map>; public function new() { super(); - layersForUpdate = new Map(); backgroundLayer = new Sprite(); groundLayer = new Sprite(); entryLayer = new Sprite(); @@ -83,160 +34,61 @@ class Render extends SpriteView implements IRender { reset(); } - private function invalidateLayers(game:Engine):Void { - for (brick in game.map.bricks) { - if (!states.exists(brick.key)) { - states[brick.key] = new BrickState(); - } - if (states.get(brick.key).update(brick)) { - layersForUpdate[groundLayer] = true; - layersForUpdate[upLayer] = true; - } - } - layersForUpdate[groundLayer] = true; //ToDo: - for (entry in game.entities) { - if (!states.exists(entry.key)) { - states[entry.key] = new EntityState(); - } - if (states.get(entry.key).update(entry)) { - layersForUpdate[entryLayer] = true; - } - } - for (key in game.removedEntities) { - if (states.exists(key)) { - states.remove(key); - layersForUpdate[entryLayer] = true; - } - } - } - private function drawBackground(game:Engine):Void { var mapWidth = game.map.gridWidth * game.map.cellWidth; var mapHeight = game.map.gridHeight * game.map.cellHeight; - - if (layersForUpdate[backgroundLayer]) { - var g:Graphics = backgroundLayer.graphics; - g.clear(); - g.beginFill(0x000000); - g.drawRect(0, 0, mapWidth, mapHeight); - g.endFill(); - if (contentSize) { - width = mapWidth; - height = mapHeight; - } - layersForUpdate[backgroundLayer] = false; - } - } - - private function drawMap(game:Engine):Void { - if (layersForUpdate[groundLayer] || layersForUpdate[upLayer]) { - groundLayer.graphics.clear(); - upLayer.graphics.clear(); - for (brick in game.map.bricks) if (!brick.destroyed) { - var g:Graphics = null; - if (brick.config.layer < 3) { - g = groundLayer.graphics; - } else if (brick.config.layer >= 3) { - g = upLayer.graphics; - } - if (g != null) { - g.beginBitmapFill(Assets.getBitmapData('resources/images/map/map_${brick.config.type}.png')); - g.drawRect( - brick.rect.x, - brick.rect.y, - brick.rect.width, - brick.rect.height - ); - for (cell in brick.cells.iterator()) { - if (cell.destroyed) { - g.beginFill(0x000000); - g.drawRect( - cell.rect.x, - cell.rect.y, - cell.rect.width, - cell.rect.height - ); - } - } - g.endFill(); - } - } - - // Debug cells - /*var g = groundLayer.graphics; - for (c in game.map.grid.cells.iterator()) { - var color:Int = 0x000000; - if (c.armor == 1) { - color = 0xffcc00; - } else if (c.armor > 1) { - color = 0x606060; - } else if (c.layer == 0) { - color = 0xffff90; - } else if (c.layer == 1) { - color = 0x0000ff; - }else if (c.layer > 1) { - color = 0x00ff00; - } - g.beginFill(color); - g.drawRect(c.rect.x, c.rect.y, c.rect.width, c.rect.height); - g.endFill(); - } - layersForUpdate[groundLayer] = false; - layersForUpdate[upLayer] = false;*/ - } - } - - public function drawEntities(game:Engine):Void { - if (layersForUpdate[entryLayer]) { - var g:Graphics = entryLayer.graphics; - g.clear(); - for (ent in game.entities) { - var image:String = null; - if (Std.is(ent, Tank)) { - var tank:Tank = cast ent; - image = 'resources/images/tank/${tank.config.group}/tank_${tank.config.group.charAt(0)}${tank.config.type}_${tank.index}-0.png'; - } else if (Std.is(ent, Bullet)) { - var bullet:Bullet = cast ent; - image = 'resources/images/bullet/bullet_${bullet.config.piercing > 1 ? 1 : 0}.png'; - } else { - image = 'ERROR'; // ToDo: - } - var m = new Matrix(); - if (Std.is(ent, MobileEntity)) { - m.rotate(calcRotate(cast(ent, MobileEntity).direction)); - } - m.translate(ent.rect.x, ent.rect.y); - g.beginBitmapFill(Assets.getBitmapData(image), m, true, true); - g.drawRect(ent.rect.x, ent.rect.y, ent.rect.width, ent.rect.height); - g.endFill(); - } - layersForUpdate[entryLayer] = false; + var g:Graphics = backgroundLayer.graphics; + g.clear(); + g.beginFill(0x000000); + g.drawRect(0, 0, mapWidth, mapHeight); + g.endFill(); + if (contentSize) { + width = mapWidth; + height = mapHeight; } } public function draw(game:Engine):Void { - invalidateLayers(game); - drawBackground(game); - drawMap(game); - drawEntities(game); + for (brick in game.map.bricks) { + if (!items.exists(brick.key)) { + items[brick.key] = new BrickItem(brick); + if (brick.config.layer > 2) { + upLayer.addChild(items[brick.key].view); + } else { + groundLayer.addChild(items[brick.key].view); + } + } + } + for (entity in game.entities) { + if (!items.exists(entity.key)) { + items[entity.key] = switch (Type.typeof(entity)) { + case ValueType.TClass(Tank): cast new TankItem(cast entity); + case ValueType.TClass(Bullet): cast new BulletItem(cast entity); + case x: null; + } + entryLayer.addChild(items[entity.key].view); + } + } + for (key in game.removedEntities) { + if (items.exists(key)) { + var view:DisplayObject = items[key].view; + view.parent.removeChild(view); + items.remove(key); + } + } + for (item in items) { + item.update(); + } + if (background == null) { + drawBackground(game); + } } public function reset():Void { - states = new Map>(); - layersForUpdate[backgroundLayer] = true; - } - - private function calcRotate(direction:Direction):Float { - return (if (direction == Direction.RIGHT) { - 0; - } else if (direction == Direction.LEFT) { - 180; - } else if (direction == Direction.TOP) { - 270; - } else if (direction == Direction.BOTTOM) { - 90; - } else { - 0; - }) * (Math.PI / 180); + items = new Map>(); + if (background != null) { + backgroundLayer.removeChild(background); + background = null; + } } } diff --git a/src/client/haxe/ru/m/tankz/render/RenderItem.hx b/src/client/haxe/ru/m/tankz/render/RenderItem.hx new file mode 100644 index 0000000..290fbfc --- /dev/null +++ b/src/client/haxe/ru/m/tankz/render/RenderItem.hx @@ -0,0 +1,131 @@ +package ru.m.tankz.render; + +import flash.display.DisplayObject; +import flash.display.Shape; +import ru.m.geom.Direction; +import ru.m.geom.Rectangle; +import ru.m.tankz.core.Bullet; +import ru.m.tankz.map.Brick; +import openfl.Assets; +import ru.m.tankz.core.Tank; +import flash.display.Bitmap; + + +typedef TRectangle = { + var rect(default, null):Rectangle; +} + + +class RenderItem { + + public var value(default, null):T; + public var view(default, null):DisplayObject; + + private var bitmap:Bitmap; + + public function new(value:T) { + this.value = value; + this.bitmap = new Bitmap(); + this.view = bitmap; + redraw(); + } + + public function redraw():Void { + bitmap.bitmapData = Assets.getBitmapData(getImage()); + } + + public function update():Void { + var rect = value.rect; + view.rotation = calcRotate(rect.direction); + view.x = rect.x - rect.width * (rect.direction.x + 1) / 2 + rect.width * (rect.direction.y + 1) / 2 + 0.5 * rect.width; + view.y = rect.y - rect.height * (rect.direction.x + 1) / 2 - rect.height * (rect.direction.y + 1) / 2 + 1.5 * rect.height; + } + + private function getImage():String { + return 'ERROR'; + } + + private static function calcRotate(direction:Direction):Float { + return (if (direction == Direction.RIGHT) { + 0; + } else if (direction == Direction.LEFT) { + 180; + } else if (direction == Direction.TOP) { + 270; + } else if (direction == Direction.BOTTOM) { + 90; + } else { + 0; + }); + } +} + + +class BrickItem extends RenderItem { + + private var shape:Shape; + private var broken:Int; + + public function new(value:Brick) { + this.shape = new Shape(); + super(value); + this.view = shape; + } + + override public function redraw():Void { + var image = Assets.getBitmapData(getImage()); + var g = shape.graphics; + g.clear(); + if (value.destroyed) return; + g.beginBitmapFill(image); + g.drawRect(0, 0, value.rect.width, value.rect.height); + for (c in value.cells) { + if (c.destroyed) { + g.beginFill(0x000000); + g.drawRect(c.rect.x - value.rect.x, c.rect.y - value.rect.y, c.rect.width, c.rect.height); + } + } + g.endFill(); + } + + override public function update():Void { + super.update(); + var b = value.broken; + if (b != this.broken) { + this.broken = b; + redraw(); + } + } + + override private function getImage():String { + return 'resources/images/map/map_${value.config.type}.png'; + } +} + + + +class TankItem extends RenderItem { + + private var type:Int; + + override private function getImage():String { + return 'resources/images/tank/${value.config.group}/tank_${value.config.group.charAt(0)}${value.config.type}_${value.index}-0.png'; + } + + override public function update():Void { + super.update(); + var t = value.config.type; + if (t != this.type) { + this.type = t; + redraw(); + } + } +} + + +class BulletItem extends RenderItem { + + override private function getImage():String { + return 'resources/images/bullet/bullet_${value.config.piercing > 1 ? 1 : 0}.png'; + } +} diff --git a/src/client/haxe/ru/m/tankz/view/frames/GameFrame.hx b/src/client/haxe/ru/m/tankz/view/frames/GameFrame.hx index df0cb27..943483d 100755 --- a/src/client/haxe/ru/m/tankz/view/frames/GameFrame.hx +++ b/src/client/haxe/ru/m/tankz/view/frames/GameFrame.hx @@ -1,5 +1,6 @@ package ru.m.tankz.view.frames; +import haxe.Timer; import ru.m.tankz.config.ConfigBundle; import ru.m.tankz.proto.core.GameType; import ru.m.tankz.proto.core.Game; @@ -23,6 +24,7 @@ class GameFrame extends VGroupView implements ViewBuilder implements IPacketHand private var engine:Engine; private var controls:Map; + private var timer:Timer; public function init():Void { engine = new Engine(); @@ -33,24 +35,33 @@ class GameFrame extends VGroupView implements ViewBuilder implements IPacketHand var game:Game = Provider.get(Game); engine.init(ConfigBundle.get(GameType.CLASSIC, 0)); engine.initTanks(game.players); - content.addEventListener(Event.ENTER_FRAME, updateGame); + content.addEventListener(Event.ENTER_FRAME, redraw); for (index in 0...game.players.length) { var playerId:Int = game.players[index].id; controls.set(playerId, PlayerControl.forPlayer(index, playerId, engine)); } Provider.get(IConnection).packetHandler.addListener(this); render.draw(engine); + timer = new Timer(10); + timer.run = updateEngine; } public function onHide():Void { Provider.get(IConnection).packetHandler.removeListener(this); - content.removeEventListener(Event.ENTER_FRAME, updateGame); + if (timer != null) { + timer.stop(); + timer = null; + } + content.removeEventListener(Event.ENTER_FRAME, redraw); engine.clear(); render.reset(); } - private function updateGame(_):Void { + private function updateEngine():Void { engine.update(); + } + + private function redraw(_):Void { render.draw(engine); } diff --git a/src/common/haxe/ru/m/tankz/core/MobileEntity.hx b/src/common/haxe/ru/m/tankz/core/MobileEntity.hx index 0d7f963..db470a2 100755 --- a/src/common/haxe/ru/m/tankz/core/MobileEntity.hx +++ b/src/common/haxe/ru/m/tankz/core/MobileEntity.hx @@ -10,24 +10,21 @@ class MobileEntity extends Entity { public var layer(default, null):Int; public var speed(default, null):Float; - public var direction(default, default):Direction; public function new(rect:Rectangle, speed:Float, direction:Direction) { super(rect); this.speed = speed; - this.direction = direction; this.layer = 0; this.mx = 0; this.my = 0; } public function move(direction:Direction):Void { - if (this.direction != direction) { + if (this.rect.direction != direction) { this.rect.direction = direction; - this.direction = direction; } - mx = direction.x * speed; - my = direction.y * speed; + mx = rect.direction.x * speed; + my = rect.direction.y * speed; } public function stop():Void { diff --git a/src/common/haxe/ru/m/tankz/core/Tank.hx b/src/common/haxe/ru/m/tankz/core/Tank.hx index ebefd36..7dab410 100755 --- a/src/common/haxe/ru/m/tankz/core/Tank.hx +++ b/src/common/haxe/ru/m/tankz/core/Tank.hx @@ -28,8 +28,9 @@ class Tank extends MobileEntity { } private function set_config(value:TankConfig):TankConfig { + var d = rect.direction; rect = new Rectangle(rect.x, rect.y, value.width, value.height); - rect.direction = direction; + rect.direction = d; speed = value.speed; config = value; return value; @@ -38,8 +39,8 @@ class Tank extends MobileEntity { public function shot():Null { if (bulletsCounter >= config.bullets) return null; var bullet = new Bullet(id, config, config.bullet); - bullet.rect.center = rect.center.add(new Point(rect.width / 4 * direction.x, rect.height / 4 * direction.y)); - bullet.move(direction); + bullet.rect.center = rect.center.add(new Point(rect.width / 4 * rect.direction.x, rect.height / 4 * rect.direction.y)); + bullet.move(rect.direction); bulletsCounter++; return bullet; } diff --git a/src/common/haxe/ru/m/tankz/engine/Engine.hx b/src/common/haxe/ru/m/tankz/engine/Engine.hx index 43063d7..35471e5 100755 --- a/src/common/haxe/ru/m/tankz/engine/Engine.hx +++ b/src/common/haxe/ru/m/tankz/engine/Engine.hx @@ -39,7 +39,7 @@ class Engine { private function buildTank(index:Int, config:TankConfig, point:SpawnPoint):Tank { var tank = new Tank(index, config); tank.rect.center = new Point((point.x + 1) * map.cellWidth, (point.y + 1) * map.cellHeight); - tank.direction = Direction.fromString(point.direction); + tank.rect.direction = Direction.fromString(point.direction); return tank; } @@ -66,8 +66,8 @@ class Engine { .setObjectId(tank.id) .setX(tank.rect.x) .setY(tank.rect.y) - .setDirectionX(tank.direction.x) - .setDirectionY(tank.direction.y) + .setDirectionX(tank.rect.direction.x) + .setDirectionY(tank.rect.direction.y) ); } @@ -214,7 +214,7 @@ class Engine { } for (other in entities.iterator()) { - if (other != ent) { + if (other != ent && other != null) { if (other.rect.intersection2(side)) { //if (ent.rect.intersection(other.rect)) { var funName = 'collision${ent.type}${other.type}'; diff --git a/src/common/haxe/ru/m/tankz/map/Brick.hx b/src/common/haxe/ru/m/tankz/map/Brick.hx index 094e784..d94fcd5 100644 --- a/src/common/haxe/ru/m/tankz/map/Brick.hx +++ b/src/common/haxe/ru/m/tankz/map/Brick.hx @@ -24,6 +24,7 @@ class Brick implements IKey { public var rect(default, null):Rectangle; public var cells(default, null):HashMap; + public var broken(get, null):Int; public var destroyed(get, set):Bool; public function new(mapConfig:MapConfig, config:BrickConfig, cellX:Int, cellY:Int, cells:HashMap) { @@ -40,6 +41,16 @@ class Brick implements IKey { ); } + public function get_broken():Int { + var i:Int = 0; + for (c in cells.iterator()) { + if (c.destroyed) { + i++; + } + } + return i; + } + public function get_destroyed():Bool { var i = 0; var result:Bool = false;