Files
flame/lib/components/joystick/joystick_action.dart

236 lines
6.6 KiB
Dart

import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import '../../extensions/offset.dart';
import '../../extensions/vector2.dart';
import '../../gestures.dart';
import '../../sprite.dart';
import 'joystick_component.dart';
import 'joystick_events.dart';
import 'joystick_utils.dart';
enum JoystickActionAlign { TOP_LEFT, BOTTOM_LEFT, TOP_RIGHT, BOTTOM_RIGHT }
class JoystickAction {
final int actionId;
final Sprite sprite;
final Sprite spritePressed;
final Sprite spriteBackgroundDirection;
final double size;
final double sizeFactorBackgroundDirection;
final EdgeInsets margin;
final JoystickActionAlign align;
final bool enableDirection;
final Color color;
final double opacityBackground;
final double opacityKnob;
bool isPressed = false;
Rect _rectAction;
Rect _rectBackgroundDirection;
bool _dragging = false;
Sprite _spriteAction;
Offset _dragPosition;
final Paint _paintBackground;
final Paint _paintAction;
final Paint _paintActionPressed;
JoystickController _joystickController;
final double _sizeBackgroundDirection;
DragEvent _currentDragEvent;
double _tileSize;
JoystickAction({
@required this.actionId,
this.sprite,
this.spritePressed,
this.spriteBackgroundDirection,
this.enableDirection = false,
this.size = 50,
this.sizeFactorBackgroundDirection = 1.5,
this.margin = EdgeInsets.zero,
this.color = Colors.blueGrey,
this.align = JoystickActionAlign.BOTTOM_RIGHT,
this.opacityBackground = 0.5,
this.opacityKnob = 0.8,
}) : _spriteAction = sprite,
_sizeBackgroundDirection = sizeFactorBackgroundDirection * size,
_paintBackground = Paint()
..color = color.withOpacity(opacityBackground)
..style = PaintingStyle.fill,
_paintAction = Paint()
..color = color.withOpacity(opacityKnob)
..style = PaintingStyle.fill,
_paintActionPressed = Paint()
..color = color.withOpacity(opacityBackground)
..style = PaintingStyle.fill {
_tileSize = _sizeBackgroundDirection / 2;
}
void initialize(Vector2 _screenSize, JoystickController joystickController) {
_joystickController = joystickController;
final double radius = size / 2;
double dx = 0, dy = 0;
switch (align) {
case JoystickActionAlign.TOP_LEFT:
dx = margin.left + radius;
dy = margin.top + radius;
break;
case JoystickActionAlign.BOTTOM_LEFT:
dx = margin.left + radius;
dy = _screenSize.y - (margin.bottom + radius);
break;
case JoystickActionAlign.TOP_RIGHT:
dx = _screenSize.x - (margin.right + radius);
dy = margin.top + radius;
break;
case JoystickActionAlign.BOTTOM_RIGHT:
dx = _screenSize.x - (margin.right + radius);
dy = _screenSize.y - (margin.bottom + radius);
break;
}
_rectAction = Rect.fromCircle(
center: Offset(dx, dy),
radius: radius,
);
_rectBackgroundDirection = Rect.fromCircle(
center: Offset(dx, dy),
radius: _sizeBackgroundDirection / 2,
);
_dragPosition = _rectAction.center;
}
void render(Canvas c) {
if (_dragging && enableDirection) {
JoystickUtils.renderControl(
c,
spriteBackgroundDirection,
_rectBackgroundDirection,
_paintBackground,
);
}
final actionPaint = isPressed ? _paintActionPressed : _paintAction;
JoystickUtils.renderControl(c, _spriteAction, _rectAction, actionPaint);
}
void update(double dt) {
if (_rectBackgroundDirection != null && _dragging) {
final double _radAngle = atan2(
_dragPosition.dy - _rectBackgroundDirection.center.dy,
_dragPosition.dx - _rectBackgroundDirection.center.dx,
);
// Distance between the center of joystick background & drag position
final centerPosition = _rectBackgroundDirection.center.toVector2();
final dragPosition = _dragPosition.toVector2();
double dist = centerPosition.distanceTo(dragPosition);
// The maximum distance for the knob position to the edge of
// the background + half of its own size. The knob can wander in the
// background image, but not outside.
dist = min(dist, _tileSize);
// Calculate the knob position
final double nextX = dist * cos(_radAngle);
final double nextY = dist * sin(_radAngle);
final Offset nextPoint = Offset(nextX, nextY);
if (_rectAction != null) {
final Offset diff = Offset(
_rectBackgroundDirection.center.dx + nextPoint.dx,
_rectBackgroundDirection.center.dy + nextPoint.dy,
) -
_rectAction.center;
_rectAction = _rectAction.shift(diff);
}
final double _intensity = dist / _tileSize;
_joystickController.joystickAction(
JoystickActionEvent(
id: actionId,
event: ActionEvent.MOVE,
intensity: _intensity,
radAngle: _radAngle,
),
);
} else {
if (_rectAction != null) {
final Offset diff = _dragPosition - _rectAction.center;
_rectAction = _rectAction.shift(diff);
}
}
}
void onReceiveDrag(DragEvent event) {
if (_dragging || !(_rectAction?.contains(event.initialPosition) ?? false)) {
return;
}
if (enableDirection) {
_dragPosition = event.initialPosition;
_dragging = true;
}
_joystickController.joystickAction(
JoystickActionEvent(
id: actionId,
event: ActionEvent.DOWN,
),
);
tapDown();
_currentDragEvent = event;
_currentDragEvent
..onUpdate = onPanUpdate
..onEnd = onPanEnd
..onCancel = onPanCancel;
}
void tapDown() {
isPressed = true;
if (spritePressed != null) {
_spriteAction = spritePressed;
}
}
void tapUp() {
isPressed = false;
_spriteAction = sprite;
}
void onPanUpdate(DragUpdateDetails details) {
if (_dragging) {
_dragPosition = details.localPosition;
}
}
void onPanEnd(DragEndDetails p1) {
_currentDragEvent = null;
_dragging = false;
_dragPosition = _rectBackgroundDirection.center;
_joystickController.joystickAction(
JoystickActionEvent(
id: actionId,
event: ActionEvent.UP,
),
);
tapUp();
}
void onPanCancel() {
_currentDragEvent = null;
_dragging = false;
_dragPosition = _rectBackgroundDirection.center;
_joystickController.joystickAction(
JoystickActionEvent(
id: actionId,
event: ActionEvent.CANCEL,
),
);
tapUp();
}
}