[client] add screen gamepad
This commit is contained in:
19
WORK.md
19
WORK.md
@@ -1,8 +1,11 @@
|
|||||||
* [feature] **shovel** bonus with armor bricks
|
* **shovel** bonus with armor bricks
|
||||||
* [feature] bonuses in dota/death mod
|
* bonuses in dota/death mod
|
||||||
* [feature] tanks and bullets speed balancing
|
* tanks and bullets speed balancing
|
||||||
* [feature] result frame update (next game select, only human player info)
|
* result frame update (next game select, only human player info)
|
||||||
* [feature] network game series
|
* network game series
|
||||||
* [feature] map packs (create in editor, import in game, save imported in local storage)
|
* map packs (create in editor, import in game, save imported in local storage)
|
||||||
* [feature] update bots
|
* update bots
|
||||||
* [feature] improve bonuses system
|
* improve bonuses system
|
||||||
|
* gamepad support
|
||||||
|
* screen gamepad on mobiles
|
||||||
|
* resize render on mobiles
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import flash.events.FocusEvent;
|
|||||||
import flash.events.KeyboardEvent;
|
import flash.events.KeyboardEvent;
|
||||||
import flash.Lib;
|
import flash.Lib;
|
||||||
import haxe.Timer;
|
import haxe.Timer;
|
||||||
|
import ru.m.geom.Direction;
|
||||||
import ru.m.tankz.control.ActionConfig;
|
import ru.m.tankz.control.ActionConfig;
|
||||||
import ru.m.tankz.control.Control;
|
import ru.m.tankz.control.Control;
|
||||||
import ru.m.tankz.storage.SettingsStorage;
|
import ru.m.tankz.storage.SettingsStorage;
|
||||||
@@ -14,7 +15,7 @@ class HumanControl extends Control {
|
|||||||
@:provide var storage:SettingsStorage;
|
@:provide var storage:SettingsStorage;
|
||||||
|
|
||||||
private var keyBinding:KeyBinding;
|
private var keyBinding:KeyBinding;
|
||||||
private var moveQueue:Array<Int>;
|
private var moveQueue:Array<Direction>;
|
||||||
private var shotTimer:Timer;
|
private var shotTimer:Timer;
|
||||||
|
|
||||||
public function new(playerId:PlayerId, controlIndex:Int) {
|
public function new(playerId:PlayerId, controlIndex:Int) {
|
||||||
@@ -40,34 +41,43 @@ class HumanControl extends Control {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function onKeyDown(event:KeyboardEvent):Void {
|
public function toggleAction(action:TankAction, on:Bool):Void {
|
||||||
if (keyBinding.exists(event.keyCode)) switch (keyBinding.get(event.keyCode)) {
|
switch action {
|
||||||
case TankAction.MOVE(direction):
|
case TankAction.MOVE(direction):
|
||||||
if (moveQueue.indexOf(event.keyCode) == -1) {
|
if (on) {
|
||||||
moveQueue.unshift(event.keyCode);
|
if (moveQueue.indexOf(direction) == -1) {
|
||||||
updateMove();
|
moveQueue.unshift(direction);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
moveQueue.remove(direction);
|
||||||
|
}
|
||||||
|
updateMove();
|
||||||
case TankAction.SHOT:
|
case TankAction.SHOT:
|
||||||
|
if (on) {
|
||||||
if (shotTimer == null) {
|
if (shotTimer == null) {
|
||||||
shotTimer = new Timer(300);
|
shotTimer = new Timer(300);
|
||||||
shotTimer.run = shot;
|
shotTimer.run = shot;
|
||||||
shot();
|
shot();
|
||||||
}
|
}
|
||||||
case _:
|
} else {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
if (shotTimer != null) {
|
||||||
shotTimer.stop();
|
shotTimer.stop();
|
||||||
shotTimer = null;
|
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)) {
|
||||||
|
toggleAction(keyBinding.get(event.keyCode), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,11 +90,7 @@ class HumanControl extends Control {
|
|||||||
if (moveQueue.length == 0) {
|
if (moveQueue.length == 0) {
|
||||||
action(TankAction.STOP);
|
action(TankAction.STOP);
|
||||||
} else {
|
} else {
|
||||||
switch (keyBinding.get(moveQueue[0])) {
|
action(TankAction.MOVE(moveQueue[0]));
|
||||||
case TankAction.MOVE(direction):
|
|
||||||
action(TankAction.MOVE(direction));
|
|
||||||
case _:
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,21 @@ import ru.m.tankz.control.Control.TankAction;
|
|||||||
|
|
||||||
class SettingsStorage extends SharedObjectStorage {
|
class SettingsStorage extends SharedObjectStorage {
|
||||||
|
|
||||||
|
public var screenGamepad(get, set):Bool;
|
||||||
|
|
||||||
public function new() {
|
public function new() {
|
||||||
super("settings");
|
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 {
|
public function getActionConfig(index:Int):ActionConfig {
|
||||||
return exists('action:$index') ? read('action:$index') : getDefaultActionConfig(index);
|
return exists('action:$index') ? read('action:$index') : getDefaultActionConfig(index);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package ru.m.tankz.view;
|
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.frame.FrameSwitcher;
|
||||||
import haxework.view.VGroupView;
|
import haxework.view.VGroupView;
|
||||||
import ru.m.tankz.game.GameEvent;
|
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.sound.SoundManager;
|
||||||
import ru.m.tankz.storage.GameStorage;
|
import ru.m.tankz.storage.GameStorage;
|
||||||
import ru.m.tankz.view.game.GameView;
|
import ru.m.tankz.view.game.GameView;
|
||||||
|
import ru.m.tankz.view.GamepadView;
|
||||||
|
|
||||||
@:template class GameFrame extends VGroupView implements GameListener {
|
@:template class GameFrame extends VGroupView implements GameListener {
|
||||||
public static inline var ID = "game";
|
public static inline var ID = "game";
|
||||||
@@ -17,6 +22,7 @@ import ru.m.tankz.view.game.GameView;
|
|||||||
private static inline var TAG = "GameFrame";
|
private static inline var TAG = "GameFrame";
|
||||||
|
|
||||||
@:view("game") private var gameView(default, null):GameView;
|
@:view("game") private var gameView(default, null):GameView;
|
||||||
|
@:view private var gamepad(default, null):GamepadView;
|
||||||
|
|
||||||
@:provide var network:NetworkManager;
|
@:provide var network:NetworkManager;
|
||||||
@:provide var soundManager:SoundManager;
|
@:provide var soundManager:SoundManager;
|
||||||
@@ -24,10 +30,12 @@ import ru.m.tankz.view.game.GameView;
|
|||||||
@:provide var record:GameRecord;
|
@:provide var record:GameRecord;
|
||||||
@:provide var switcher:FrameSwitcher;
|
@:provide var switcher:FrameSwitcher;
|
||||||
@:provide var gameStorage:GameStorage;
|
@:provide var gameStorage:GameStorage;
|
||||||
|
@:provide var settings:SettingsStorage;
|
||||||
|
|
||||||
@:provide var game:IGame;
|
@:provide var game:IGame;
|
||||||
|
|
||||||
public function onShow():Void {
|
public function onShow():Void {
|
||||||
|
gamepad.visible = settings.screenGamepad;
|
||||||
gameView.type = game.type;
|
gameView.type = game.type;
|
||||||
soundManager.config = game.config;
|
soundManager.config = game.config;
|
||||||
gameView.render.config = game.config;
|
gameView.render.config = game.config;
|
||||||
@@ -66,4 +74,21 @@ import ru.m.tankz.view.game.GameView;
|
|||||||
public function close():Void {
|
public function close():Void {
|
||||||
switcher.change(LevelFrame.ID);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,13 @@
|
|||||||
views:
|
views:
|
||||||
- $type: haxework.view.VGroupView
|
- $type: haxework.view.VGroupView
|
||||||
skinId: container
|
skinId: container
|
||||||
|
layout.overflow: true
|
||||||
views:
|
views:
|
||||||
- id: game
|
- id: game
|
||||||
$type: ru.m.tankz.view.game.GameView
|
$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
|
||||||
|
|||||||
119
src/client/haxe/ru/m/tankz/view/GamepadView.hx
Normal file
119
src/client/haxe/ru/m/tankz/view/GamepadView.hx
Normal file
@@ -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<GamepadView> {
|
||||||
|
|
||||||
|
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<GamepadAction, Bool>;
|
||||||
|
|
||||||
|
public var areas(default, null):Array<ActionArea>;
|
||||||
|
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)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,21 @@
|
|||||||
package ru.m.tankz.view;
|
package ru.m.tankz.view;
|
||||||
|
|
||||||
|
import haxework.view.ToggleButtonView;
|
||||||
import haxework.view.VGroupView;
|
import haxework.view.VGroupView;
|
||||||
|
import ru.m.tankz.storage.SettingsStorage;
|
||||||
|
|
||||||
@:template class SettingsFrame extends VGroupView {
|
@:template class SettingsFrame extends VGroupView {
|
||||||
public static var ID(default, never):String = "settings";
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,12 @@ views:
|
|||||||
- $type: haxework.view.LabelView
|
- $type: haxework.view.LabelView
|
||||||
skinId: text.header
|
skinId: text.header
|
||||||
text: Settings
|
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
|
- $type: haxework.view.HGroupView
|
||||||
layout.margin: 20
|
layout.margin: 20
|
||||||
views:
|
views:
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ enum TankAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Control {
|
class Control {
|
||||||
public var type:String;
|
|
||||||
public var playerId(default, null):PlayerId;
|
public var playerId(default, null):PlayerId;
|
||||||
public var tankId(default, default):Int;
|
public var tankId(default, default):Int;
|
||||||
private var handler:IGame;
|
private var handler:IGame;
|
||||||
|
|||||||
@@ -29,8 +29,7 @@ import ru.m.tankz.Type;
|
|||||||
public var engine(default, null):IEngine;
|
public var engine(default, null):IEngine;
|
||||||
public var controlFactory(default, null):IControlFactory;
|
public var controlFactory(default, null):IControlFactory;
|
||||||
public var pause(default, set):Bool;
|
public var pause(default, set):Bool;
|
||||||
|
public var controls(default, null):Map<String, Control>;
|
||||||
private var controls:Map<String, Control>;
|
|
||||||
|
|
||||||
@:provide var configBundle:IConfigBundle;
|
@:provide var configBundle:IConfigBundle;
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package ru.m.tankz.game;
|
|||||||
|
|
||||||
import haxework.signal.Signal;
|
import haxework.signal.Signal;
|
||||||
import ru.m.tankz.config.Config;
|
import ru.m.tankz.config.Config;
|
||||||
|
import ru.m.tankz.control.Control;
|
||||||
import ru.m.tankz.control.IControlFactory;
|
import ru.m.tankz.control.IControlFactory;
|
||||||
import ru.m.tankz.Type;
|
import ru.m.tankz.Type;
|
||||||
|
|
||||||
@@ -13,6 +14,7 @@ interface IGame extends GameListener {
|
|||||||
public var state(default, null):GameState;
|
public var state(default, null):GameState;
|
||||||
public var controlFactory(default, null):IControlFactory;
|
public var controlFactory(default, null):IControlFactory;
|
||||||
public var pause(default, set):Bool;
|
public var pause(default, set):Bool;
|
||||||
|
public var controls(default, null):Map<String, Control>;
|
||||||
|
|
||||||
public var gameEventSignal(default, null):Signal<GameEvent>;
|
public var gameEventSignal(default, null):Signal<GameEvent>;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user