From 42138cd4ad9c3095ad3898d5bdf22f8ced4ae41e Mon Sep 17 00:00:00 2001 From: shmyga Date: Tue, 23 Jul 2019 17:37:59 +0300 Subject: [PATCH] [client] improve GamepadView --- src/client/haxe/ru/m/tankz/AppTheme.hx | 4 + src/client/haxe/ru/m/tankz/view/GameFrame.hx | 2 +- .../haxe/ru/m/tankz/view/GameFrame.yaml | 2 +- .../m/tankz/view/gamepad/CircleActionArea.hx | 24 ++++++ .../view/gamepad/DefaultActionAreaBuilder.hx | 42 +++++++++++ .../ru/m/tankz/view/gamepad/GamepadSkin.hx | 29 ++++++++ .../m/tankz/view/{ => gamepad}/GamepadView.hx | 73 +++---------------- .../ru/m/tankz/view/gamepad/IActionArea.hx | 11 +++ .../tankz/view/gamepad/IActionAreaBuilder.hx | 5 ++ .../tankz/view/gamepad/RectangleActionArea.hx | 24 ++++++ .../m/tankz/view/gamepad/RhombusActionArea.hx | 32 ++++++++ .../view/gamepad/SmartActionAreaBuilder.hx | 42 +++++++++++ .../m/tankz/view/settings/SettingsEditor.hx | 3 +- src/common/haxe/ru/m/geom/Circle.hx | 41 +++++++++++ 14 files changed, 269 insertions(+), 65 deletions(-) create mode 100644 src/client/haxe/ru/m/tankz/view/gamepad/CircleActionArea.hx create mode 100644 src/client/haxe/ru/m/tankz/view/gamepad/DefaultActionAreaBuilder.hx create mode 100644 src/client/haxe/ru/m/tankz/view/gamepad/GamepadSkin.hx rename src/client/haxe/ru/m/tankz/view/{ => gamepad}/GamepadView.hx (58%) create mode 100644 src/client/haxe/ru/m/tankz/view/gamepad/IActionArea.hx create mode 100644 src/client/haxe/ru/m/tankz/view/gamepad/IActionAreaBuilder.hx create mode 100644 src/client/haxe/ru/m/tankz/view/gamepad/RectangleActionArea.hx create mode 100644 src/client/haxe/ru/m/tankz/view/gamepad/RhombusActionArea.hx create mode 100644 src/client/haxe/ru/m/tankz/view/gamepad/SmartActionAreaBuilder.hx create mode 100644 src/common/haxe/ru/m/geom/Circle.hx diff --git a/src/client/haxe/ru/m/tankz/AppTheme.hx b/src/client/haxe/ru/m/tankz/AppTheme.hx index 5c557fd..80f3f84 100644 --- a/src/client/haxe/ru/m/tankz/AppTheme.hx +++ b/src/client/haxe/ru/m/tankz/AppTheme.hx @@ -110,6 +110,10 @@ class AppTheme extends Theme { registerButton("start", "play-circle-solid.svg"); registerButton("login", "sign-in-solid.svg"); registerButton("logout", "sign-out-solid.svg"); + + register(new Style("gamepad", [ + "skin.color" => colors.active, + ])); } private function registerButton(name:String, resource:String):Void { diff --git a/src/client/haxe/ru/m/tankz/view/GameFrame.hx b/src/client/haxe/ru/m/tankz/view/GameFrame.hx index b701cc6..ebbe7b6 100644 --- a/src/client/haxe/ru/m/tankz/view/GameFrame.hx +++ b/src/client/haxe/ru/m/tankz/view/GameFrame.hx @@ -16,7 +16,7 @@ import ru.m.tankz.sound.SoundManager; import ru.m.tankz.storage.GameStorage; import ru.m.tankz.storage.SettingsStorage; import ru.m.tankz.view.game.GameView; -import ru.m.tankz.view.GamepadView; +import ru.m.tankz.view.gamepad.GamepadView; @:template class GameFrame extends FrameView implements GameListener { public static inline var ID = "game"; diff --git a/src/client/haxe/ru/m/tankz/view/GameFrame.yaml b/src/client/haxe/ru/m/tankz/view/GameFrame.yaml index 1cfbb00..c515784 100644 --- a/src/client/haxe/ru/m/tankz/view/GameFrame.yaml +++ b/src/client/haxe/ru/m/tankz/view/GameFrame.yaml @@ -6,7 +6,7 @@ views: - id: game $type: ru.m.tankz.view.game.GameView - id: gamepad - $type: ru.m.tankz.view.GamepadView + $type: ru.m.tankz.view.gamepad.GamepadView geometry.position: absolute geometry.stretch: true visible: false diff --git a/src/client/haxe/ru/m/tankz/view/gamepad/CircleActionArea.hx b/src/client/haxe/ru/m/tankz/view/gamepad/CircleActionArea.hx new file mode 100644 index 0000000..bb854c1 --- /dev/null +++ b/src/client/haxe/ru/m/tankz/view/gamepad/CircleActionArea.hx @@ -0,0 +1,24 @@ +package ru.m.tankz.view.gamepad; + +import flash.display.Graphics; +import ru.m.control.DeviceAction; +import ru.m.geom.Circle; +import ru.m.geom.Point; + +class CircleActionArea implements IActionArea { + public var action(default, null):DeviceAction; + public var circle(default, null):Circle; + + public function new(action:DeviceAction, circle:Circle) { + this.action = action; + this.circle = circle; + } + + public function contain(point:Point):Bool { + return circle.contain(point); + } + + public function draw(graphics:Graphics):Void { + graphics.drawCircle(circle.x, circle.y, circle.radius); + } +} diff --git a/src/client/haxe/ru/m/tankz/view/gamepad/DefaultActionAreaBuilder.hx b/src/client/haxe/ru/m/tankz/view/gamepad/DefaultActionAreaBuilder.hx new file mode 100644 index 0000000..cb2b5c1 --- /dev/null +++ b/src/client/haxe/ru/m/tankz/view/gamepad/DefaultActionAreaBuilder.hx @@ -0,0 +1,42 @@ +package ru.m.tankz.view.gamepad; + +import ru.m.control.DeviceAction; +import ru.m.geom.Direction; +import ru.m.geom.Rectangle; + +class DefaultActionAreaBuilder implements IActionAreaBuilder { + public function new() { + } + + public function build(width:Float, height:Float):Array { + var areas:Array = []; + var size = Math.min(width, height) / 7; + var padding = size / 2; + // top + areas.push(new RectangleActionArea( + DIRECTION(Direction.TOP), + new Rectangle(padding + size, height - size * 3 - padding, size, size) + )); + // left + areas.push(new RectangleActionArea( + DIRECTION(Direction.LEFT), + new Rectangle(padding, height - size * 2 - padding, size, size) + )); + // bottom + areas.push(new RectangleActionArea( + DIRECTION(Direction.BOTTOM), + new Rectangle(padding + size, height - size - padding, size, size) + )); + // right + areas.push(new RectangleActionArea( + DIRECTION(Direction.RIGHT), + new Rectangle(padding + size * 2, height - size * 2 - padding, size, size) + )); + // button + areas.push(new RectangleActionArea( + KEY(0), + new Rectangle(width - size * 1.5 - padding, height - size * 2 - padding, size, size) + )); + return areas; + } +} diff --git a/src/client/haxe/ru/m/tankz/view/gamepad/GamepadSkin.hx b/src/client/haxe/ru/m/tankz/view/gamepad/GamepadSkin.hx new file mode 100644 index 0000000..4b308d5 --- /dev/null +++ b/src/client/haxe/ru/m/tankz/view/gamepad/GamepadSkin.hx @@ -0,0 +1,29 @@ +package ru.m.tankz.view.gamepad; + +import flash.display.Graphics; +import haxework.color.Color; +import haxework.view.skin.ISkin; + +@:style class GamepadSkin implements ISkin { + + @:style(0x00ff00) public var color(default, default):Null; + + public function new(?color:Color) { + this.color = color; + } + + public function draw(view:GamepadView):Void { + var graphics:Graphics = view.content.graphics; + graphics.clear(); + graphics.beginFill(0, 0.0); + graphics.drawRect(0, 0, view.width, view.height); + graphics.endFill(); + graphics.lineStyle(2, color); + graphics.beginFill(color, 0.2); + for (area in view.areas) { + area.draw(graphics); + } + graphics.lineStyle(); + graphics.endFill(); + } +} diff --git a/src/client/haxe/ru/m/tankz/view/GamepadView.hx b/src/client/haxe/ru/m/tankz/view/gamepad/GamepadView.hx similarity index 58% rename from src/client/haxe/ru/m/tankz/view/GamepadView.hx rename to src/client/haxe/ru/m/tankz/view/gamepad/GamepadView.hx index b423226..6c32e4d 100644 --- a/src/client/haxe/ru/m/tankz/view/GamepadView.hx +++ b/src/client/haxe/ru/m/tankz/view/gamepad/GamepadView.hx @@ -1,47 +1,14 @@ -package ru.m.tankz.view; +package ru.m.tankz.view.gamepad; -import flash.display.Graphics; import flash.display.Stage; import flash.events.MouseEvent; import flash.events.TouchEvent; -import haxework.color.Color; import haxework.signal.Signal; -import haxework.view.skin.ISkin; import haxework.view.SpriteView; import ru.m.control.DeviceAction; import ru.m.control.DeviceType; import ru.m.control.IControlDevice; -import ru.m.geom.Direction; import ru.m.geom.Point; -import ru.m.geom.Rectangle; - -typedef ActionArea = { - var action:DeviceAction; - var rect:Rectangle; -} - -@:style class GamepadSkin implements ISkin { - - @:style(0) public var color(default, default):Null; - - public function new(?color:Color) { - this.color = color; - } - - public function draw(view:GamepadView):Void { - var graphics:Graphics = view.content.graphics; - graphics.clear(); - graphics.beginFill(0, 0.0); - graphics.drawRect(0, 0, view.width, view.height); - graphics.endFill(); - graphics.lineStyle(2, color); - graphics.beginFill(color, 0.2); - for (area in view.areas) { - graphics.drawRect(area.rect.x, area.rect.y, area.rect.width, area.rect.height); - } - graphics.lineStyle(); - } -} class GamepadView extends SpriteView implements IControlDevice { public static var ID(default, never):Int = -128; @@ -49,18 +16,21 @@ class GamepadView extends SpriteView implements IControlDevice { public var type(default, null):DeviceType; public var signal(default, null):Signal2; - public var areas(default, null):Array; - public var currentAreas(default, null):Map; + public var areas(default, null):Array; + public var currentAreas(default, null):Map; + private var builder:IActionAreaBuilder; private var stage:Stage; public function new() { super(); + style = "gamepad"; type = GAMEPAD(ID); + builder = new SmartActionAreaBuilder(); signal = new Signal2(); areas = []; currentAreas = new Map(); - skin = new GamepadSkin(0x00ff00); + skin = new GamepadSkin(); content.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); content.addEventListener(TouchEvent.TOUCH_BEGIN, onTouchBegin); } @@ -106,13 +76,13 @@ class GamepadView extends SpriteView implements IControlDevice { private function updateTouch(pointID:Int, point:Point):Void { if (currentAreas.exists(pointID)) { var area = currentAreas[pointID]; - if (!area.rect.contain(point)) { + if (!area.contain(point)) { currentAreas.remove(pointID); signal.emit(area.action, false); } } for (area in areas) { - if (area.rect.contain(point)) { + if (area.contain(point)) { currentAreas[pointID] = area; signal.emit(area.action, true); break; @@ -129,31 +99,10 @@ class GamepadView extends SpriteView implements IControlDevice { override public function update():Void { super.update(); - areas = []; - var size = Math.min(width, height) / 7; - var padding = size / 2; - areas.push({ - action: DIRECTION(Direction.TOP), - rect: new Rectangle(padding + size, height - size * 3 - padding, size, size) - }); - areas.push({ - action: DIRECTION(Direction.LEFT), - rect: new Rectangle(padding, height - size * 2 - padding, size, size) - }); - areas.push({ - action: DIRECTION(Direction.BOTTOM), - rect: new Rectangle(padding + size, height - size - padding, size, size) - }); - areas.push({ - action: DIRECTION(Direction.RIGHT), - rect: new Rectangle(padding + size * 2, height - size * 2 - padding, size, size) - }); - areas.push({ - action: KEY(0), - rect: new Rectangle(width - size * 1.5 - padding, height - size * 2 - padding, size, size) - }); + areas = builder.build(width, height); } + public function dispose():Void { stage = null; } diff --git a/src/client/haxe/ru/m/tankz/view/gamepad/IActionArea.hx b/src/client/haxe/ru/m/tankz/view/gamepad/IActionArea.hx new file mode 100644 index 0000000..a3172bb --- /dev/null +++ b/src/client/haxe/ru/m/tankz/view/gamepad/IActionArea.hx @@ -0,0 +1,11 @@ +package ru.m.tankz.view.gamepad; + +import flash.display.Graphics; +import ru.m.control.DeviceAction; +import ru.m.geom.Point; + +interface IActionArea { + public var action(default, null):DeviceAction; + public function contain(point:Point):Bool; + public function draw(graphics:Graphics):Void; +} diff --git a/src/client/haxe/ru/m/tankz/view/gamepad/IActionAreaBuilder.hx b/src/client/haxe/ru/m/tankz/view/gamepad/IActionAreaBuilder.hx new file mode 100644 index 0000000..4c491bb --- /dev/null +++ b/src/client/haxe/ru/m/tankz/view/gamepad/IActionAreaBuilder.hx @@ -0,0 +1,5 @@ +package ru.m.tankz.view.gamepad; + +interface IActionAreaBuilder { + public function build(width:Float, height:Float):Array; +} diff --git a/src/client/haxe/ru/m/tankz/view/gamepad/RectangleActionArea.hx b/src/client/haxe/ru/m/tankz/view/gamepad/RectangleActionArea.hx new file mode 100644 index 0000000..d3f0c12 --- /dev/null +++ b/src/client/haxe/ru/m/tankz/view/gamepad/RectangleActionArea.hx @@ -0,0 +1,24 @@ +package ru.m.tankz.view.gamepad; + +import flash.display.Graphics; +import ru.m.control.DeviceAction; +import ru.m.geom.Point; +import ru.m.geom.Rectangle; + +class RectangleActionArea implements IActionArea { + public var action(default, null):DeviceAction; + public var rectangle(default, null):Rectangle; + + public function new(action:DeviceAction, rectangle:Rectangle) { + this.action = action; + this.rectangle = rectangle; + } + + public function contain(point:Point):Bool { + return rectangle.contain(point); + } + + public function draw(graphics:Graphics):Void { + graphics.drawRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height); + } +} diff --git a/src/client/haxe/ru/m/tankz/view/gamepad/RhombusActionArea.hx b/src/client/haxe/ru/m/tankz/view/gamepad/RhombusActionArea.hx new file mode 100644 index 0000000..d327296 --- /dev/null +++ b/src/client/haxe/ru/m/tankz/view/gamepad/RhombusActionArea.hx @@ -0,0 +1,32 @@ +package ru.m.tankz.view.gamepad; + +import flash.display.Graphics; +import ru.m.control.DeviceAction; +import ru.m.geom.Point; +import ru.m.geom.Rectangle; + +class RhombusActionArea implements IActionArea { + public var action(default, null):DeviceAction; + public var rectangle(default, null):Rectangle; + + private var center:Point; + private var distance:Float; + + public function new(action:DeviceAction, rectangle:Rectangle) { + this.action = action; + this.rectangle = rectangle; + center = rectangle.center; + distance = Math.max(rectangle.width / 2, rectangle.height / 2); + } + + public function contain(point:Point):Bool { + return Math.abs(point.x - center.x) + Math.abs(point.y - center.y) < distance; + } + + public function draw(graphics:Graphics):Void { + graphics.moveTo(rectangle.x + rectangle.width / 2, rectangle.top); + graphics.lineTo(rectangle.right, rectangle.y + rectangle.height / 2); + graphics.lineTo(rectangle.x + rectangle.width / 2, rectangle.bottom); + graphics.lineTo(rectangle.left, rectangle.y + rectangle.height / 2); + } +} diff --git a/src/client/haxe/ru/m/tankz/view/gamepad/SmartActionAreaBuilder.hx b/src/client/haxe/ru/m/tankz/view/gamepad/SmartActionAreaBuilder.hx new file mode 100644 index 0000000..fa60a38 --- /dev/null +++ b/src/client/haxe/ru/m/tankz/view/gamepad/SmartActionAreaBuilder.hx @@ -0,0 +1,42 @@ +package ru.m.tankz.view.gamepad; + +import ru.m.control.DeviceAction; +import ru.m.geom.Circle; +import ru.m.geom.Direction; +import ru.m.geom.Rectangle; + +class SmartActionAreaBuilder implements IActionAreaBuilder { + public function new() { + } + + public function build(width:Float, height:Float):Array { + var areas:Array = []; + var size = Math.min(width, height) / 6; + var padding = size / 2; + // top + areas.push(new RhombusActionArea( + DIRECTION(Direction.TOP), + new Rectangle(padding + size * 0.5, height - size * 2 - padding, size, size ) + )); + // left + areas.push(new RhombusActionArea( + DIRECTION(Direction.LEFT), + new Rectangle(padding, height - size * 1.5 - padding, size, size) + )); + // bottom + areas.push(new RhombusActionArea( + DIRECTION(Direction.BOTTOM), + new Rectangle(padding + size * 0.5, height - size - padding, size, size) + )); + // right + areas.push(new RhombusActionArea( + DIRECTION(Direction.RIGHT), + new Rectangle(padding + size, height - size * 1.5 - padding, size, size) + )); + areas.push(new CircleActionArea( + KEY(0), + new Circle(width - size * 1 - padding, height - size * 1 - padding, size / 2) + )); + return areas; + } +} diff --git a/src/client/haxe/ru/m/tankz/view/settings/SettingsEditor.hx b/src/client/haxe/ru/m/tankz/view/settings/SettingsEditor.hx index 96a0142..8aa99bc 100644 --- a/src/client/haxe/ru/m/tankz/view/settings/SettingsEditor.hx +++ b/src/client/haxe/ru/m/tankz/view/settings/SettingsEditor.hx @@ -1,8 +1,8 @@ package ru.m.tankz.view.settings; import haxework.view.data.DataView; -import haxework.view.group.GroupView; import haxework.view.form.LabelView; +import haxework.view.group.GroupView; import haxework.view.group.VGroupView; import promhx.Deferred; import promhx.Promise; @@ -11,6 +11,7 @@ import ru.m.control.DeviceType; import ru.m.control.IControlBus; import ru.m.tankz.control.Binding; import ru.m.tankz.storage.SettingsStorage; +import ru.m.tankz.view.gamepad.GamepadView; import ru.m.tankz.view.settings.ActionView; class BindEditor { diff --git a/src/common/haxe/ru/m/geom/Circle.hx b/src/common/haxe/ru/m/geom/Circle.hx new file mode 100644 index 0000000..a1d640e --- /dev/null +++ b/src/common/haxe/ru/m/geom/Circle.hx @@ -0,0 +1,41 @@ +package ru.m.geom; + +abstract Circle(Array) { + public var x(get, set):Float; + public var y(get, set):Float; + public var radius(get, set):Float; + + public function new(x:Float = 0, y:Float = 0, radius:Float = 0) { + this = [ + x, y, radius, + ]; + } + + private inline function get_x():Float { + return this[0]; + } + + private inline function set_x(value:Float):Float { + return this[0] = value; + } + + private inline function get_y():Float { + return this[1]; + } + + private inline function set_y(value:Float):Float { + return this[1] = value; + } + + private inline function get_radius():Float { + return this[2]; + } + + private inline function set_radius(value:Float):Float { + return this[2] = value; + } + + public function contain(point:Point):Bool { + return Math.sqrt(Math.pow(point.x - x, 2) + Math.pow(point.y - y, 2)) < radius; + } +}