[render] update

This commit is contained in:
2018-01-17 21:58:30 +03:00
parent 35404d5732
commit 507320414f
8 changed files with 219 additions and 226 deletions

View File

@@ -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;
}

View File

@@ -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<T> {
public function update(object:T):Bool;
}
class BrickState implements IState<Brick> {
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<Entity> {
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<Sprite, Bool>;
private var states:Map<String, IState<Dynamic>>;
private var background:Sprite;
private var items:Map<String, RenderItem<Dynamic>>;
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<String, IState<Dynamic>>();
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<String, RenderItem<Dynamic>>();
if (background != null) {
backgroundLayer.removeChild(background);
background = null;
}
}
}

View File

@@ -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<T:TRectangle> {
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<Brick> {
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<Tank> {
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<Bullet> {
override private function getImage():String {
return 'resources/images/bullet/bullet_${value.config.piercing > 1 ? 1 : 0}.png';
}
}

View File

@@ -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<Int, PlayerControl>;
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);
}