Line data Source code
1 : import 'dart:math'; 2 : 3 : import 'package:flutter/rendering.dart' show EdgeInsets; 4 : 5 : import '../../../components.dart'; 6 : import '../../../extensions.dart'; 7 : import '../../gestures/events.dart'; 8 : import 'hud_margin_component.dart'; 9 : 10 41 : enum JoystickDirection { 11 : up, 12 : upLeft, 13 : upRight, 14 : right, 15 : down, 16 : downRight, 17 : downLeft, 18 : left, 19 : idle, 20 : } 21 : 22 : class JoystickComponent extends HudMarginComponent with Draggable { 23 : late final PositionComponent knob; 24 : late final PositionComponent? background; 25 : 26 : /// The percentage [0.0, 1.0] the knob is dragged from the center to the edge. 27 : double intensity = 0.0; 28 : 29 : /// The amount the knob is dragged from the center. 30 : Vector2 delta = Vector2.zero(); 31 : 32 : /// The percentage, presented as a [Vector2], and direction that the knob is 33 : /// currently pulled from its base position to a edge of the joystick. 34 0 : Vector2 get relativeDelta => delta / knobRadius; 35 : 36 : /// The radius from the center of the knob to the edge of as far as the knob 37 : /// can be dragged. 38 : late double knobRadius; 39 : 40 : /// The position where the knob rests. 41 : late Vector2 _baseKnobPosition; 42 : 43 1 : JoystickComponent({ 44 : required this.knob, 45 : this.background, 46 : EdgeInsets? margin, 47 : Vector2? position, 48 : double? size, 49 : double? knobRadius, 50 : Anchor anchor = Anchor.center, 51 : }) : assert( 52 0 : size != null || background != null, 53 : 'Either size or background must be defined', 54 : ), 55 : assert( 56 2 : knob.position.isZero() && (background?.position.isZero() ?? true), 57 : 'Positions should not be set for the knob or the background', 58 : ), 59 1 : super( 60 : margin: margin, 61 : position: position, 62 1 : size: background?.size ?? Vector2.all(size ?? 0), 63 : anchor: anchor, 64 : ) { 65 4 : this.knobRadius = knobRadius ?? this.size.x / 2; 66 : } 67 : 68 : @override 69 1 : Future<void> onLoad() async { 70 2 : await super.onLoad(); 71 2 : knob.anchor = Anchor.center; 72 5 : knob.position.add(size / 2); 73 4 : _baseKnobPosition = knob.position.clone(); 74 1 : if (background != null) { 75 0 : addChild(background!); 76 : } 77 2 : addChild(knob); 78 : } 79 : 80 0 : @override 81 : void update(double dt) { 82 0 : super.update(dt); 83 0 : final knobRadius2 = knobRadius * knobRadius; 84 0 : if (delta.isZero() && _baseKnobPosition != knob.position) { 85 0 : knob.position = _baseKnobPosition; 86 0 : } else if (delta.length2 > knobRadius2) { 87 0 : delta.scaleTo(knobRadius); 88 : } 89 0 : if (!delta.isZero()) { 90 0 : knob.position 91 0 : ..setFrom(_baseKnobPosition) 92 0 : ..add(delta); 93 : } 94 0 : intensity = delta.length2 / knobRadius2; 95 : } 96 : 97 0 : @override 98 : bool onDragStart(int pointerId, DragStartInfo info) { 99 : return false; 100 : } 101 : 102 0 : @override 103 : bool onDragUpdate(_, DragUpdateInfo info) { 104 0 : delta.add(info.delta.global); 105 : return false; 106 : } 107 : 108 0 : @override 109 : bool onDragEnd(int id, __) { 110 0 : onDragCancel(id); 111 : return false; 112 : } 113 : 114 0 : @override 115 : bool onDragCancel(_) { 116 0 : delta.setZero(); 117 : return false; 118 : } 119 : 120 : static const double _eighthOfPi = pi / 8; 121 : 122 1 : JoystickDirection get direction { 123 2 : if (delta.isZero()) { 124 : return JoystickDirection.idle; 125 : } 126 : 127 2 : var knobAngle = delta.screenAngle(); 128 : // Since screenAngle and angleTo doesn't care about "direction" of the angle 129 : // we have to use angleToSigned and create an only increasing angle by 130 : // removing negative angles from 2*pi. 131 3 : knobAngle = knobAngle < 0 ? 2 * pi + knobAngle : knobAngle; 132 2 : if (knobAngle >= 0 && knobAngle <= _eighthOfPi) { 133 : return JoystickDirection.up; 134 4 : } else if (knobAngle > 1 * _eighthOfPi && knobAngle <= 3 * _eighthOfPi) { 135 : return JoystickDirection.upRight; 136 4 : } else if (knobAngle > 3 * _eighthOfPi && knobAngle <= 5 * _eighthOfPi) { 137 : return JoystickDirection.right; 138 4 : } else if (knobAngle > 5 * _eighthOfPi && knobAngle <= 7 * _eighthOfPi) { 139 : return JoystickDirection.downRight; 140 4 : } else if (knobAngle > 7 * _eighthOfPi && knobAngle <= 9 * _eighthOfPi) { 141 : return JoystickDirection.down; 142 4 : } else if (knobAngle > 9 * _eighthOfPi && knobAngle <= 11 * _eighthOfPi) { 143 : return JoystickDirection.downLeft; 144 4 : } else if (knobAngle > 11 * _eighthOfPi && knobAngle <= 13 * _eighthOfPi) { 145 : return JoystickDirection.left; 146 4 : } else if (knobAngle > 13 * _eighthOfPi && knobAngle <= 15 * _eighthOfPi) { 147 : return JoystickDirection.upLeft; 148 0 : } else if (knobAngle > 15 * _eighthOfPi) { 149 : return JoystickDirection.up; 150 : } else { 151 : return JoystickDirection.idle; 152 : } 153 : } 154 : }