diff --git a/lib/components/joystick/Joystick_action.dart b/lib/components/joystick/Joystick_action.dart new file mode 100644 index 000000000..985287b27 --- /dev/null +++ b/lib/components/joystick/Joystick_action.dart @@ -0,0 +1,222 @@ +import 'dart:math'; + +import 'package:flame/components/joystick/joystick_component.dart'; +import 'package:flame/sprite.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.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; + double sizeBackgroundDirection; + final EdgeInsets margin; + final JoystickActionAlign align; + final bool enableDirection; + final Color color; + + int _pointerDragging; + Rect _rect; + Rect _rectBackgroundDirection; + bool _dragging = false; + Sprite _sprite; + double _tileSize; + Offset _dragPosition; + Paint _paintBackground; + Paint _paintAction; + JoystickController _joystickController; + + JoystickAction({ + @required this.actionId, + this.sprite, + this.spritePressed, + this.spriteBackgroundDirection, + this.enableDirection = false, + this.size = 50, + this.sizeBackgroundDirection, + this.margin = EdgeInsets.zero, + this.color = Colors.blueGrey, + this.align = JoystickActionAlign.BOTTOM_RIGHT, + }) { + _sprite = sprite; + sizeBackgroundDirection = sizeBackgroundDirection ?? size * 1.5; + _tileSize = sizeBackgroundDirection; + } + + void initialize(Size _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.height - (margin.bottom + radius); + break; + case JoystickActionAlign.TOP_RIGHT: + dx = _screenSize.width - (margin.right + radius); + dy = margin.top + radius; + break; + case JoystickActionAlign.BOTTOM_RIGHT: + dx = _screenSize.width - (margin.right + radius); + dy = _screenSize.height - (margin.bottom + radius); + break; + } + _rect = Rect.fromCircle( + center: Offset(dx, dy), + radius: radius, + ); + _rectBackgroundDirection = Rect.fromCircle( + center: Offset(dx, dy), + radius: sizeBackgroundDirection / 2, + ); + + _paintBackground = Paint() + ..color = color.withOpacity(0.5) + ..style = PaintingStyle.fill; + + _paintAction = Paint() + ..color = color.withOpacity(0.8) + ..style = PaintingStyle.fill; + + _dragPosition = _rect.center; + } + + void render(Canvas c) { + if (_rectBackgroundDirection != null && _dragging && enableDirection) { + if (spriteBackgroundDirection == null) { + double radiusBackground = _rectBackgroundDirection.width / 2; + c.drawCircle( + Offset( + _rectBackgroundDirection.left + radiusBackground, + _rectBackgroundDirection.top + radiusBackground, + ), + radiusBackground, + _paintBackground, + ); + } else { + spriteBackgroundDirection.renderRect(c, _rectBackgroundDirection); + } + } + + if (_sprite != null) { + if (_rect != null) _sprite.renderRect(c, _rect); + } else { + double radiusAction = _rect.width / 2; + c.drawCircle( + Offset( + _rect.left + radiusAction, + _rect.top + radiusAction, + ), + radiusAction, + _paintAction, + ); + } + } + + void update(double dt) { + if (_dragging) { + double _radAngle = atan2( + _dragPosition.dy - _rectBackgroundDirection.center.dy, + _dragPosition.dx - _rectBackgroundDirection.center.dx, + ); + + // Distance between the center of joystick background & drag position + Point centerPoint = Point( + _rectBackgroundDirection.center.dx, + _rectBackgroundDirection.center.dy, + ); + double dist = + centerPoint.distanceTo(Point(_dragPosition.dx, _dragPosition.dy)); + + // The maximum distance for the knob position the edge of + // the background + half of its own size. The knob can wander in the + // background image, but not outside. + dist = dist < (_tileSize / 3) ? dist : (_tileSize / 3); + + // Calculation the knob position + final double nextX = dist * cos(_radAngle); + final double nextY = dist * sin(_radAngle); + final Offset nextPoint = Offset(nextX, nextY); + + final Offset diff = Offset( + _rectBackgroundDirection.center.dx + nextPoint.dx, + _rectBackgroundDirection.center.dy + nextPoint.dy, + ) - + _rect.center; + _rect = _rect.shift(diff); + + final double _intensity = dist / (_tileSize / 3); + + _joystickController.joystickAction( + JoystickActionEvent( + id: actionId, + event: ActionEvent.MOVE, + intensity: _intensity, + radAngle: _radAngle, + ), + ); + } else { + if (_rect != null) { + final Offset diff = _dragPosition - _rect.center; + _rect = _rect.shift(diff); + } + } + } + + void actionDown(int pointer, Offset localPosition) { + if (!_dragging && _rect != null && _rect.contains(localPosition)) { + _pointerDragging = pointer; + if (enableDirection) { + _dragPosition = localPosition; + _dragging = true; + } + _joystickController.joystickAction( + JoystickActionEvent( + id: actionId, + event: ActionEvent.DOWN, + ), + ); + pressed(); + } + } + + void actionMove(int pointer, Offset localPosition) { + if (pointer == _pointerDragging) { + if (_dragging) { + _dragPosition = localPosition; + } + } + } + + void actionUp(int pointer) { + if (pointer == _pointerDragging) { + _dragging = false; + _dragPosition = _rectBackgroundDirection.center; + _joystickController.joystickAction( + JoystickActionEvent( + id: actionId, + event: ActionEvent.UP, + ), + ); + unPressed(); + } + } + + void pressed() { + if (spritePressed != null) { + _sprite = spritePressed; + } + } + + void unPressed() { + _sprite = sprite; + } +} diff --git a/lib/components/joystick/Joystick_directional.dart b/lib/components/joystick/Joystick_directional.dart new file mode 100644 index 000000000..27908b4f7 --- /dev/null +++ b/lib/components/joystick/Joystick_directional.dart @@ -0,0 +1,267 @@ +import 'dart:math'; + +import 'package:flame/components/joystick/joystick_component.dart'; +import 'package:flame/sprite.dart'; +import 'package:flutter/material.dart'; + +class JoystickDirectional { + final double size; + final Sprite spriteBackgroundDirectional; + final Sprite spriteKnobDirectional; + final bool isFixed; + final EdgeInsets margin; + final Color color; + + Paint _paintBackground; + Paint _paintKnob; + + double _backgroundAspectRatio = 2.2; + Rect _backgroundRect; + Sprite _backgroundSprite; + + Rect _knobRect; + Sprite _knobSprite; + + bool _dragging = false; + + Offset _dragPosition; + + double _tileSize; + + int _pointerDragging = 0; + + JoystickController _joystickController; + + Size _screenSize; + + JoystickDirectional({ + this.spriteBackgroundDirectional, + this.spriteKnobDirectional, + this.isFixed = true, + this.margin = const EdgeInsets.only(left: 100, bottom: 100), + this.size = 80, + this.color = Colors.blueGrey, + }) { + if (spriteBackgroundDirectional != null) { + _backgroundSprite = spriteBackgroundDirectional; + } else { + _paintBackground = Paint() + ..color = color.withOpacity(0.5) + ..style = PaintingStyle.fill; + } + if (spriteKnobDirectional != null) { + _knobSprite = spriteKnobDirectional; + } else { + _paintKnob = Paint() + ..color = color.withOpacity(0.8) + ..style = PaintingStyle.fill; + } + + _tileSize = size / 2; + } + + void initialize(Size _screenSize, JoystickController joystickController) { + this._screenSize = _screenSize; + _joystickController = joystickController; + Offset osBackground = + Offset(margin.left, _screenSize.height - margin.bottom); + _backgroundRect = Rect.fromCircle(center: osBackground, radius: size / 2); + + Offset osKnob = + Offset(_backgroundRect.center.dx, _backgroundRect.center.dy); + _knobRect = Rect.fromCircle(center: osKnob, radius: size / 4); + + _dragPosition = _knobRect.center; + } + + void render(Canvas canvas) { + if (_backgroundRect != null) { + if (_backgroundSprite != null) { + _backgroundSprite.renderRect(canvas, _backgroundRect); + } else { + double radiusBackground = _backgroundRect.width / 2; + canvas.drawCircle( + Offset(_backgroundRect.left + radiusBackground, + _backgroundRect.top + radiusBackground), + radiusBackground, + _paintBackground, + ); + } + } + + if (_knobRect != null) { + if (_knobSprite != null) { + _knobSprite.renderRect(canvas, _knobRect); + } else { + double radiusKnob = _knobRect.width / 2; + canvas.drawCircle( + Offset(_knobRect.left + radiusKnob, _knobRect.top + radiusKnob), + radiusKnob, + _paintKnob, + ); + } + } + } + + void update(double t) { + if (_dragging) { + double _radAngle = atan2(_dragPosition.dy - _backgroundRect.center.dy, + _dragPosition.dx - _backgroundRect.center.dx); + + double degrees = _radAngle * 180 / pi; + + // Distance between the center of joystick background & drag position + Point p = Point(_backgroundRect.center.dx, _backgroundRect.center.dy); + double dist = p.distanceTo(Point(_dragPosition.dx, _dragPosition.dy)); + + // The maximum distance for the knob position the edge of + // the background + half of its own size. The knob can wander in the + // background image, but not outside. + dist = dist < (_tileSize * _backgroundAspectRatio / 3) + ? dist + : (_tileSize * _backgroundAspectRatio / 3); + + // Calculation the knob position + double nextX = dist * cos(_radAngle); + double nextY = dist * sin(_radAngle); + Offset nextPoint = Offset(nextX, nextY); + + Offset diff = Offset(_backgroundRect.center.dx + nextPoint.dx, + _backgroundRect.center.dy + nextPoint.dy) - + _knobRect.center; + _knobRect = _knobRect.shift(diff); + + double _intensity = dist / (_tileSize * _backgroundAspectRatio / 3); + + if (_intensity == 0) { + _joystickController.joystickChangeDirectional(JoystickDirectionalEvent( + directional: JoystickMoveDirectional.IDLE, + intensity: _intensity, + radAngle: _radAngle, + )); + return; + } + + if (degrees > -22.5 && degrees <= 22.5) { + _joystickController.joystickChangeDirectional(JoystickDirectionalEvent( + directional: JoystickMoveDirectional.MOVE_RIGHT, + intensity: _intensity, + radAngle: _radAngle, + )); + } + + if (degrees > 22.5 && degrees <= 67.5) { + _joystickController.joystickChangeDirectional(JoystickDirectionalEvent( + directional: JoystickMoveDirectional.MOVE_DOWN_RIGHT, + intensity: _intensity, + radAngle: _radAngle, + )); + } + + if (degrees > 67.5 && degrees <= 112.5) { + _joystickController.joystickChangeDirectional(JoystickDirectionalEvent( + directional: JoystickMoveDirectional.MOVE_DOWN, + intensity: _intensity, + radAngle: _radAngle, + )); + } + + if (degrees > 112.5 && degrees <= 157.5) { + _joystickController.joystickChangeDirectional(JoystickDirectionalEvent( + directional: JoystickMoveDirectional.MOVE_DOWN_LEFT, + intensity: _intensity, + radAngle: _radAngle, + )); + } + + if ((degrees > 157.5 && degrees <= 180) || + (degrees >= -180 && degrees <= -157.5)) { + _joystickController.joystickChangeDirectional(JoystickDirectionalEvent( + directional: JoystickMoveDirectional.MOVE_LEFT, + intensity: _intensity, + radAngle: _radAngle, + )); + } + + if (degrees > -157.5 && degrees <= -112.5) { + _joystickController.joystickChangeDirectional(JoystickDirectionalEvent( + directional: JoystickMoveDirectional.MOVE_UP_LEFT, + intensity: _intensity, + radAngle: _radAngle, + )); + } + + if (degrees > -112.5 && degrees <= -67.5) { + _joystickController.joystickChangeDirectional(JoystickDirectionalEvent( + directional: JoystickMoveDirectional.MOVE_UP, + intensity: _intensity, + radAngle: _radAngle, + )); + } + + if (degrees > -67.5 && degrees <= -22.5) { + _joystickController.joystickChangeDirectional(JoystickDirectionalEvent( + directional: JoystickMoveDirectional.MOVE_UP_RIGHT, + intensity: _intensity, + radAngle: _radAngle, + )); + } + } else { + if (_knobRect != null) { + Offset diff = _dragPosition - _knobRect.center; + _knobRect = _knobRect.shift(diff); + } + } + } + + void directionalDown(int pointer, Offset localPosition) { + if (_backgroundRect == null) return; + + _updateDirectionalRect(localPosition); + + Rect directional = Rect.fromLTWH( + _backgroundRect.left - 50, + _backgroundRect.top - 50, + _backgroundRect.width + 100, + _backgroundRect.height + 100, + ); + if (!_dragging && directional.contains(localPosition)) { + _dragging = true; + _dragPosition = localPosition; + _pointerDragging = pointer; + } + } + + void directionalMove(int pointer, Offset localPosition) { + if (pointer == _pointerDragging) { + if (_dragging) { + _dragPosition = localPosition; + } + } + } + + void directionalUp(int pointer) { + if (pointer == _pointerDragging) { + _dragging = false; + _dragPosition = _backgroundRect.center; + _joystickController.joystickChangeDirectional(JoystickDirectionalEvent( + directional: JoystickMoveDirectional.IDLE, + intensity: 0.0, + radAngle: 0.0, + )); + } + } + + void _updateDirectionalRect(Offset position) { + if (_screenSize != null && + (position.dx > _screenSize.width / 3 || + position.dy < _screenSize.height / 3 || + isFixed)) return; + + _backgroundRect = Rect.fromCircle(center: position, radius: size / 2); + + Offset osKnob = + Offset(_backgroundRect.center.dx, _backgroundRect.center.dy); + _knobRect = Rect.fromCircle(center: osKnob, radius: size / 4); + } +} diff --git a/lib/components/joystick/joystick_component.dart b/lib/components/joystick/joystick_component.dart new file mode 100644 index 000000000..601ddfb6c --- /dev/null +++ b/lib/components/joystick/joystick_component.dart @@ -0,0 +1,100 @@ +import 'dart:ui'; + +import 'package:flame/components/component.dart'; +import 'package:flame/components/joystick/Joystick_action.dart'; +import 'package:flame/components/joystick/Joystick_directional.dart'; +import 'package:flame/components/mixins/has_game_ref.dart'; +import 'package:flame/game/base_game.dart'; + +enum JoystickMoveDirectional { + MOVE_UP, + MOVE_UP_LEFT, + MOVE_UP_RIGHT, + MOVE_RIGHT, + MOVE_DOWN, + MOVE_DOWN_RIGHT, + MOVE_DOWN_LEFT, + MOVE_LEFT, + IDLE +} + +class JoystickDirectionalEvent { + final JoystickMoveDirectional directional; + final double intensity; + final double radAngle; + + JoystickDirectionalEvent({ + this.directional, + this.intensity = 0.0, + this.radAngle = 0.0, + }); +} + +enum ActionEvent { DOWN, UP, MOVE } + +class JoystickActionEvent { + final int id; + final double intensity; + final double radAngle; + final ActionEvent event; + + JoystickActionEvent( + {this.id, this.intensity = 0.0, this.radAngle = 0.0, this.event}); +} + +abstract class JoystickListener { + void joystickChangeDirectional(JoystickDirectionalEvent event); + void joystickAction(JoystickActionEvent event); +} + +abstract class JoystickController extends Component with HasGameRef { + final List _observers = []; + + void joystickChangeDirectional(JoystickDirectionalEvent event) { + _observers.forEach((o) => o.joystickChangeDirectional(event)); + } + + void joystickAction(JoystickActionEvent event) { + _observers.forEach((o) => o.joystickAction(event)); + } + + void addObserver(JoystickListener listener) { + _observers.add(listener); + } + + @override + bool isHud() => true; +} + +class JoystickComponent extends JoystickController { + final List actions; + final JoystickDirectional directional; + + JoystickComponent({this.actions, this.directional}); + + void addAction(JoystickAction action) { + if (actions != null && gameRef?.size != null) { + action.initialize(gameRef.size, this); + actions.add(action); + } + } + + @override + void render(Canvas canvas) { + directional?.render(canvas); + actions?.forEach((action) => action.render(canvas)); + } + + @override + void update(double t) { + directional?.update(t); + actions?.forEach((action) => action.update(t)); + } + + @override + void resize(Size size) { + directional?.initialize(size, this); + actions?.forEach((action) => action.initialize(size, this)); + super.resize(size); + } +}