[client] improve GamepadView
This commit is contained in:
@@ -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 {
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
24
src/client/haxe/ru/m/tankz/view/gamepad/CircleActionArea.hx
Normal file
24
src/client/haxe/ru/m/tankz/view/gamepad/CircleActionArea.hx
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/client/haxe/ru/m/tankz/view/gamepad/GamepadSkin.hx
Normal file
29
src/client/haxe/ru/m/tankz/view/gamepad/GamepadSkin.hx
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
11
src/client/haxe/ru/m/tankz/view/gamepad/IActionArea.hx
Normal file
11
src/client/haxe/ru/m/tankz/view/gamepad/IActionArea.hx
Normal 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package ru.m.tankz.view.gamepad;
|
||||||
|
|
||||||
|
interface IActionAreaBuilder {
|
||||||
|
public function build(width:Float, height:Float):Array<IActionArea>;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
32
src/client/haxe/ru/m/tankz/view/gamepad/RhombusActionArea.hx
Normal file
32
src/client/haxe/ru/m/tankz/view/gamepad/RhombusActionArea.hx
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
|
|||||||
41
src/common/haxe/ru/m/geom/Circle.hx
Normal file
41
src/common/haxe/ru/m/geom/Circle.hx
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user