[client] improve GamepadView

This commit is contained in:
2019-07-23 17:37:59 +03:00
parent e8e3a6f1b3
commit 42138cd4ad
14 changed files with 269 additions and 65 deletions

View File

@@ -110,6 +110,10 @@ class AppTheme extends Theme {
registerButton("start", "play-circle-solid.svg"); registerButton("start", "play-circle-solid.svg");
registerButton("login", "sign-in-solid.svg"); registerButton("login", "sign-in-solid.svg");
registerButton("logout", "sign-out-solid.svg"); registerButton("logout", "sign-out-solid.svg");
register(new Style("gamepad", [
"skin.color" => colors.active,
]));
} }
private function registerButton(name:String, resource:String):Void { private function registerButton(name:String, resource:String):Void {

View File

@@ -16,7 +16,7 @@ import ru.m.tankz.sound.SoundManager;
import ru.m.tankz.storage.GameStorage; import ru.m.tankz.storage.GameStorage;
import ru.m.tankz.storage.SettingsStorage; import ru.m.tankz.storage.SettingsStorage;
import ru.m.tankz.view.game.GameView; 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<GameInit> implements GameListener { @:template class GameFrame extends FrameView<GameInit> implements GameListener {
public static inline var ID = "game"; public static inline var ID = "game";

View File

@@ -6,7 +6,7 @@ views:
- id: game - id: game
$type: ru.m.tankz.view.game.GameView $type: ru.m.tankz.view.game.GameView
- id: gamepad - id: gamepad
$type: ru.m.tankz.view.GamepadView $type: ru.m.tankz.view.gamepad.GamepadView
geometry.position: absolute geometry.position: absolute
geometry.stretch: true geometry.stretch: true
visible: false visible: false

View File

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

View File

@@ -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<IActionArea> {
var areas:Array<IActionArea> = [];
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;
}
}

View File

@@ -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<GamepadView> {
@:style(0x00ff00) public var color(default, default):Null<Color>;
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();
}
}

View File

@@ -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.display.Stage;
import flash.events.MouseEvent; import flash.events.MouseEvent;
import flash.events.TouchEvent; import flash.events.TouchEvent;
import haxework.color.Color;
import haxework.signal.Signal; import haxework.signal.Signal;
import haxework.view.skin.ISkin;
import haxework.view.SpriteView; import haxework.view.SpriteView;
import ru.m.control.DeviceAction; import ru.m.control.DeviceAction;
import ru.m.control.DeviceType; import ru.m.control.DeviceType;
import ru.m.control.IControlDevice; import ru.m.control.IControlDevice;
import ru.m.geom.Direction;
import ru.m.geom.Point; import ru.m.geom.Point;
import ru.m.geom.Rectangle;
typedef ActionArea = {
var action:DeviceAction;
var rect:Rectangle;
}
@:style class GamepadSkin implements ISkin<GamepadView> {
@:style(0) public var color(default, default):Null<Color>;
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 { class GamepadView extends SpriteView implements IControlDevice {
public static var ID(default, never):Int = -128; 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 type(default, null):DeviceType;
public var signal(default, null):Signal2<DeviceAction, Bool>; public var signal(default, null):Signal2<DeviceAction, Bool>;
public var areas(default, null):Array<ActionArea>; public var areas(default, null):Array<IActionArea>;
public var currentAreas(default, null):Map<Int, ActionArea>; public var currentAreas(default, null):Map<Int, IActionArea>;
private var builder:IActionAreaBuilder;
private var stage:Stage; private var stage:Stage;
public function new() { public function new() {
super(); super();
style = "gamepad";
type = GAMEPAD(ID); type = GAMEPAD(ID);
builder = new SmartActionAreaBuilder();
signal = new Signal2(); signal = new Signal2();
areas = []; areas = [];
currentAreas = new Map(); currentAreas = new Map();
skin = new GamepadSkin(0x00ff00); skin = new GamepadSkin();
content.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); content.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
content.addEventListener(TouchEvent.TOUCH_BEGIN, onTouchBegin); content.addEventListener(TouchEvent.TOUCH_BEGIN, onTouchBegin);
} }
@@ -106,13 +76,13 @@ class GamepadView extends SpriteView implements IControlDevice {
private function updateTouch(pointID:Int, point:Point):Void { private function updateTouch(pointID:Int, point:Point):Void {
if (currentAreas.exists(pointID)) { if (currentAreas.exists(pointID)) {
var area = currentAreas[pointID]; var area = currentAreas[pointID];
if (!area.rect.contain(point)) { if (!area.contain(point)) {
currentAreas.remove(pointID); currentAreas.remove(pointID);
signal.emit(area.action, false); signal.emit(area.action, false);
} }
} }
for (area in areas) { for (area in areas) {
if (area.rect.contain(point)) { if (area.contain(point)) {
currentAreas[pointID] = area; currentAreas[pointID] = area;
signal.emit(area.action, true); signal.emit(area.action, true);
break; break;
@@ -129,31 +99,10 @@ class GamepadView extends SpriteView implements IControlDevice {
override public function update():Void { override public function update():Void {
super.update(); super.update();
areas = []; areas = builder.build(width, height);
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)
});
} }
public function dispose():Void { public function dispose():Void {
stage = null; stage = null;
} }

View File

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

View File

@@ -0,0 +1,5 @@
package ru.m.tankz.view.gamepad;
interface IActionAreaBuilder {
public function build(width:Float, height:Float):Array<IActionArea>;
}

View File

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

View File

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

View File

@@ -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<IActionArea> {
var areas:Array<IActionArea> = [];
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;
}
}

View File

@@ -1,8 +1,8 @@
package ru.m.tankz.view.settings; package ru.m.tankz.view.settings;
import haxework.view.data.DataView; import haxework.view.data.DataView;
import haxework.view.group.GroupView;
import haxework.view.form.LabelView; import haxework.view.form.LabelView;
import haxework.view.group.GroupView;
import haxework.view.group.VGroupView; import haxework.view.group.VGroupView;
import promhx.Deferred; import promhx.Deferred;
import promhx.Promise; import promhx.Promise;
@@ -11,6 +11,7 @@ import ru.m.control.DeviceType;
import ru.m.control.IControlBus; import ru.m.control.IControlBus;
import ru.m.tankz.control.Binding; import ru.m.tankz.control.Binding;
import ru.m.tankz.storage.SettingsStorage; import ru.m.tankz.storage.SettingsStorage;
import ru.m.tankz.view.gamepad.GamepadView;
import ru.m.tankz.view.settings.ActionView; import ru.m.tankz.view.settings.ActionView;
class BindEditor { class BindEditor {

View File

@@ -0,0 +1,41 @@
package ru.m.geom;
abstract Circle(Array<Float>) {
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;
}
}