From 2b3f368e0f3ec6de5986647e2858d16713f2420d Mon Sep 17 00:00:00 2001 From: shmyga Date: Mon, 17 Jun 2019 17:33:35 +0300 Subject: [PATCH] [client] add screen gamepad --- WORK.md | 19 +-- .../haxe/ru/m/tankz/control/HumanControl.hx | 58 +++++---- .../ru/m/tankz/storage/SettingsStorage.hx | 11 ++ src/client/haxe/ru/m/tankz/view/GameFrame.hx | 25 ++++ .../haxe/ru/m/tankz/view/GameFrame.yaml | 7 ++ .../haxe/ru/m/tankz/view/GamepadView.hx | 119 ++++++++++++++++++ .../haxe/ru/m/tankz/view/SettingsFrame.hx | 14 +++ .../haxe/ru/m/tankz/view/SettingsFrame.yaml | 6 + src/common/haxe/ru/m/tankz/control/Control.hx | 1 - src/common/haxe/ru/m/tankz/game/Game.hx | 3 +- src/common/haxe/ru/m/tankz/game/IGame.hx | 2 + 11 files changed, 228 insertions(+), 37 deletions(-) create mode 100644 src/client/haxe/ru/m/tankz/view/GamepadView.hx diff --git a/WORK.md b/WORK.md index 2d15ce1..9fe4339 100644 --- a/WORK.md +++ b/WORK.md @@ -1,8 +1,11 @@ -* [feature] **shovel** bonus with armor bricks -* [feature] bonuses in dota/death mod -* [feature] tanks and bullets speed balancing -* [feature] result frame update (next game select, only human player info) -* [feature] network game series -* [feature] map packs (create in editor, import in game, save imported in local storage) -* [feature] update bots -* [feature] improve bonuses system +* **shovel** bonus with armor bricks +* bonuses in dota/death mod +* tanks and bullets speed balancing +* result frame update (next game select, only human player info) +* network game series +* map packs (create in editor, import in game, save imported in local storage) +* update bots +* improve bonuses system +* gamepad support +* screen gamepad on mobiles +* resize render on mobiles diff --git a/src/client/haxe/ru/m/tankz/control/HumanControl.hx b/src/client/haxe/ru/m/tankz/control/HumanControl.hx index eb743c0..e5aaee0 100644 --- a/src/client/haxe/ru/m/tankz/control/HumanControl.hx +++ b/src/client/haxe/ru/m/tankz/control/HumanControl.hx @@ -4,6 +4,7 @@ import flash.events.FocusEvent; import flash.events.KeyboardEvent; import flash.Lib; import haxe.Timer; +import ru.m.geom.Direction; import ru.m.tankz.control.ActionConfig; import ru.m.tankz.control.Control; import ru.m.tankz.storage.SettingsStorage; @@ -14,7 +15,7 @@ class HumanControl extends Control { @:provide var storage:SettingsStorage; private var keyBinding:KeyBinding; - private var moveQueue:Array; + private var moveQueue:Array; private var shotTimer:Timer; public function new(playerId:PlayerId, controlIndex:Int) { @@ -40,34 +41,43 @@ class HumanControl extends Control { } } - private function onKeyDown(event:KeyboardEvent):Void { - if (keyBinding.exists(event.keyCode)) switch (keyBinding.get(event.keyCode)) { + public function toggleAction(action:TankAction, on:Bool):Void { + switch action { case TankAction.MOVE(direction): - if (moveQueue.indexOf(event.keyCode) == -1) { - moveQueue.unshift(event.keyCode); - updateMove(); + if (on) { + if (moveQueue.indexOf(direction) == -1) { + moveQueue.unshift(direction); + } + } else { + moveQueue.remove(direction); } + updateMove(); case TankAction.SHOT: - if (shotTimer == null) { - shotTimer = new Timer(300); - shotTimer.run = shot; - shot(); + if (on) { + if (shotTimer == null) { + shotTimer = new Timer(300); + shotTimer.run = shot; + shot(); + } + } else { + if (shotTimer != null) { + shotTimer.stop(); + shotTimer = null; + } } - case _: + case TankAction.STOP: + } + } + + private function onKeyDown(event:KeyboardEvent):Void { + if (keyBinding.exists(event.keyCode)) { + toggleAction(keyBinding.get(event.keyCode), true); } } private function onKeyUp(event:KeyboardEvent):Void { - if (keyBinding.exists(event.keyCode)) switch (keyBinding.get(event.keyCode)) { - case TankAction.MOVE(direction): - moveQueue.remove(event.keyCode); - updateMove(); - case TankAction.SHOT: - if (shotTimer != null) { - shotTimer.stop(); - shotTimer = null; - } - case _: + if (keyBinding.exists(event.keyCode)) { + toggleAction(keyBinding.get(event.keyCode), false); } } @@ -80,11 +90,7 @@ class HumanControl extends Control { if (moveQueue.length == 0) { action(TankAction.STOP); } else { - switch (keyBinding.get(moveQueue[0])) { - case TankAction.MOVE(direction): - action(TankAction.MOVE(direction)); - case _: - } + action(TankAction.MOVE(moveQueue[0])); } } diff --git a/src/client/haxe/ru/m/tankz/storage/SettingsStorage.hx b/src/client/haxe/ru/m/tankz/storage/SettingsStorage.hx index 29064b8..e571349 100644 --- a/src/client/haxe/ru/m/tankz/storage/SettingsStorage.hx +++ b/src/client/haxe/ru/m/tankz/storage/SettingsStorage.hx @@ -9,10 +9,21 @@ import ru.m.tankz.control.Control.TankAction; class SettingsStorage extends SharedObjectStorage { + public var screenGamepad(get, set):Bool; + public function new() { super("settings"); } + private function get_screenGamepad():Bool { + return read("screenGamepad"); + } + + private function set_screenGamepad(value:Bool):Bool { + write("screenGamepad", value); + return value; + } + public function getActionConfig(index:Int):ActionConfig { return exists('action:$index') ? read('action:$index') : getDefaultActionConfig(index); } diff --git a/src/client/haxe/ru/m/tankz/view/GameFrame.hx b/src/client/haxe/ru/m/tankz/view/GameFrame.hx index f4dca55..0811951 100644 --- a/src/client/haxe/ru/m/tankz/view/GameFrame.hx +++ b/src/client/haxe/ru/m/tankz/view/GameFrame.hx @@ -1,5 +1,9 @@ package ru.m.tankz.view; +import ru.m.tankz.storage.SettingsStorage; +import ru.m.tankz.control.Control.TankAction; +import ru.m.geom.Direction; +import ru.m.tankz.control.HumanControl; import haxework.view.frame.FrameSwitcher; import haxework.view.VGroupView; import ru.m.tankz.game.GameEvent; @@ -10,6 +14,7 @@ import ru.m.tankz.network.NetworkManager; import ru.m.tankz.sound.SoundManager; import ru.m.tankz.storage.GameStorage; import ru.m.tankz.view.game.GameView; +import ru.m.tankz.view.GamepadView; @:template class GameFrame extends VGroupView implements GameListener { public static inline var ID = "game"; @@ -17,6 +22,7 @@ import ru.m.tankz.view.game.GameView; private static inline var TAG = "GameFrame"; @:view("game") private var gameView(default, null):GameView; + @:view private var gamepad(default, null):GamepadView; @:provide var network:NetworkManager; @:provide var soundManager:SoundManager; @@ -24,10 +30,12 @@ import ru.m.tankz.view.game.GameView; @:provide var record:GameRecord; @:provide var switcher:FrameSwitcher; @:provide var gameStorage:GameStorage; + @:provide var settings:SettingsStorage; @:provide var game:IGame; public function onShow():Void { + gamepad.visible = settings.screenGamepad; gameView.type = game.type; soundManager.config = game.config; gameView.render.config = game.config; @@ -66,4 +74,21 @@ import ru.m.tankz.view.game.GameView; public function close():Void { switcher.change(LevelFrame.ID); } + + private function onGamepadAction(action:GamepadAction, on:Bool):Void { + // ToDo: + for (control in game.controls) { + if (Std.is(control, HumanControl)) { + var tankAction:TankAction = switch action { + case UP: MOVE(Direction.TOP); + case LEFT: MOVE(Direction.LEFT); + case DOWN: MOVE(Direction.BOTTOM); + case RIGHT: MOVE(Direction.RIGHT); + case BUTTON_1: SHOT; + } + cast(control, HumanControl).toggleAction(tankAction, on); + break; + } + } + } } diff --git a/src/client/haxe/ru/m/tankz/view/GameFrame.yaml b/src/client/haxe/ru/m/tankz/view/GameFrame.yaml index 0e8d03f..23b8fc5 100644 --- a/src/client/haxe/ru/m/tankz/view/GameFrame.yaml +++ b/src/client/haxe/ru/m/tankz/view/GameFrame.yaml @@ -2,6 +2,13 @@ views: - $type: haxework.view.VGroupView skinId: container + layout.overflow: true views: - id: game $type: ru.m.tankz.view.game.GameView + - id: gamepad + $type: ru.m.tankz.view.GamepadView + geometry.position: absolute + geometry.size.stretch: true + +actionSignal: $this:onGamepadAction + visible: false diff --git a/src/client/haxe/ru/m/tankz/view/GamepadView.hx b/src/client/haxe/ru/m/tankz/view/GamepadView.hx new file mode 100644 index 0000000..89bca81 --- /dev/null +++ b/src/client/haxe/ru/m/tankz/view/GamepadView.hx @@ -0,0 +1,119 @@ +package ru.m.tankz.view; + +import flash.display.Graphics; +import flash.events.MouseEvent; +import haxework.signal.Signal; +import haxework.view.skin.ISkin; +import haxework.view.SpriteView; +import ru.m.geom.Point; +import ru.m.geom.Rectangle; + +enum GamepadAction { + UP; + LEFT; + DOWN; + RIGHT; + BUTTON_1; +} + +typedef ActionArea = { + var action:GamepadAction; + var rect:Rectangle; +} + +class GamepadSkin implements ISkin { + + public var color(default, default):Int; + + public function new(color:Int = 0) { + 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 { + + public var actionSignal(default, null):Signal2; + + public var areas(default, null):Array; + public var currentArea(default, null):ActionArea; + + public function new() { + super(); + actionSignal = new Signal2(); + skin = [new GamepadSkin(0x00ff00)]; + content.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); + } + + private function onMouseDown(event:MouseEvent):Void { + onMouseMove(event); + content.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove); + content.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp); + } + + private function onMouseMove(event:MouseEvent):Void { + var point = new Point(event.localX, event.localY); + if (currentArea != null) { + if (!currentArea.rect.contain(point)) { + actionSignal.emit(currentArea.action, false); + currentArea = null; + } + } + for (area in areas) { + if (area.rect.contain(point)) { + currentArea = area; + actionSignal.emit(currentArea.action, true); + break; + } + } + } + + private function onMouseUp(event:MouseEvent):Void { + content.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove); + content.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp); + if (currentArea != null) { + actionSignal.emit(currentArea.action, false); + currentArea = null; + } + } + + override public function update():Void { + super.update(); + areas = []; + var size = Math.min(width, height) / 8; + var padding = size / 2; + areas.push({ + action: GamepadAction.UP, + rect: new Rectangle(padding + size, height - size * 3 - padding, size, size) + }); + areas.push({ + action: GamepadAction.LEFT, + rect: new Rectangle(padding, height - size * 2 - padding, size, size) + }); + areas.push({ + action: GamepadAction.DOWN, + rect: new Rectangle(padding + size, height - size - padding, size, size) + }); + areas.push({ + action: GamepadAction.RIGHT, + rect: new Rectangle(padding + size * 2, height - size * 2 - padding, size, size) + }); + areas.push({ + action: GamepadAction.BUTTON_1, + rect: new Rectangle(width - size * 1.5 - padding, height - size * 2 - padding, size, size) + }); + } +} diff --git a/src/client/haxe/ru/m/tankz/view/SettingsFrame.hx b/src/client/haxe/ru/m/tankz/view/SettingsFrame.hx index 8d143de..8e34627 100644 --- a/src/client/haxe/ru/m/tankz/view/SettingsFrame.hx +++ b/src/client/haxe/ru/m/tankz/view/SettingsFrame.hx @@ -1,7 +1,21 @@ package ru.m.tankz.view; +import haxework.view.ToggleButtonView; import haxework.view.VGroupView; +import ru.m.tankz.storage.SettingsStorage; @:template class SettingsFrame extends VGroupView { public static var ID(default, never):String = "settings"; + + @:view("gamepad") private var gamepadButton:ToggleButtonView; + + @:provide private static var settings:SettingsStorage; + + public function onShow():Void { + gamepadButton.on = settings.screenGamepad; + } + + private function toggleGamepad():Void { + gamepadButton.on = settings.screenGamepad = !settings.screenGamepad; + } } diff --git a/src/client/haxe/ru/m/tankz/view/SettingsFrame.yaml b/src/client/haxe/ru/m/tankz/view/SettingsFrame.yaml index 9f46d1b..d2a3e03 100644 --- a/src/client/haxe/ru/m/tankz/view/SettingsFrame.yaml +++ b/src/client/haxe/ru/m/tankz/view/SettingsFrame.yaml @@ -6,6 +6,12 @@ views: - $type: haxework.view.LabelView skinId: text.header text: Settings + - id: gamepad + $type: haxework.view.ToggleButtonView + skinId: button.simple + text: Screen Gamepad Disabled + onText: Screen Gamepad Enabled + +onPress: $code:toggleGamepad() - $type: haxework.view.HGroupView layout.margin: 20 views: diff --git a/src/common/haxe/ru/m/tankz/control/Control.hx b/src/common/haxe/ru/m/tankz/control/Control.hx index f98f5aa..78e0a17 100644 --- a/src/common/haxe/ru/m/tankz/control/Control.hx +++ b/src/common/haxe/ru/m/tankz/control/Control.hx @@ -14,7 +14,6 @@ enum TankAction { } class Control { - public var type:String; public var playerId(default, null):PlayerId; public var tankId(default, default):Int; private var handler:IGame; diff --git a/src/common/haxe/ru/m/tankz/game/Game.hx b/src/common/haxe/ru/m/tankz/game/Game.hx index 1cf0454..fd47d5b 100644 --- a/src/common/haxe/ru/m/tankz/game/Game.hx +++ b/src/common/haxe/ru/m/tankz/game/Game.hx @@ -29,8 +29,7 @@ import ru.m.tankz.Type; public var engine(default, null):IEngine; public var controlFactory(default, null):IControlFactory; public var pause(default, set):Bool; - - private var controls:Map; + public var controls(default, null):Map; @:provide var configBundle:IConfigBundle; diff --git a/src/common/haxe/ru/m/tankz/game/IGame.hx b/src/common/haxe/ru/m/tankz/game/IGame.hx index 0c1a772..f5ad075 100644 --- a/src/common/haxe/ru/m/tankz/game/IGame.hx +++ b/src/common/haxe/ru/m/tankz/game/IGame.hx @@ -2,6 +2,7 @@ package ru.m.tankz.game; import haxework.signal.Signal; import ru.m.tankz.config.Config; +import ru.m.tankz.control.Control; import ru.m.tankz.control.IControlFactory; import ru.m.tankz.Type; @@ -13,6 +14,7 @@ interface IGame extends GameListener { public var state(default, null):GameState; public var controlFactory(default, null):IControlFactory; public var pause(default, set):Bool; + public var controls(default, null):Map; public var gameEventSignal(default, null):Signal;