温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Flutter手游操纵杆移动的原理与实现方法

发布时间:2022-07-11 09:22:36 来源:亿速云 阅读:221 作者:iii 栏目:开发技术

这篇文章主要介绍“Flutter手游操纵杆移动的原理与实现方法”,在日常操作中,相信很多人在Flutter手游操纵杆移动的原理与实现方法问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Flutter手游操纵杆移动的原理与实现方法”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

绘制

绘制操纵杆的静态图形,玩过手游应该知道操纵杆基本构成由底部圆和手指移动圆球组成,手指移动的圆球围绕底圆进行360°旋转从而控制角色朝不同方向移动。

静态效果

操纵杆的核心是由两个圆形组成,代码也非常简单。

Flutter手游操纵杆移动的原理与实现方法

绘制代码:

// 底圆
canvas.drawCircle(
    Offset(0,0),
    bgR,
    _paint
      ..style = PaintingStyle.fill
      ..color = Colors.blue.withOpacity(0.2));
_paint.color = color;
_paint.style = PaintingStyle.stroke;


/// 手势小圆
canvas.drawCircle(
    Offset(0,0),
    bgR / 3,
    _paint
      ..style = PaintingStyle.fill
      ..color = Colors.blue.withOpacity(0.9));

添加手势交互 GestureDetector

大概思路:

当点击可触控区域,将操纵杆移动到当前手指按下的位置,移动手指,根据手指位置坐标和按下时圆心位置坐标计算偏移角度得出手指相对于底圆的坐标点,松开手指,操纵杆进行复位回到初始位置。

手势组件

return GestureDetector(
  child: CustomPaint(
    size: size,
    painter: JoyStickPainter(
        offset: _offset,
        offsetCenter: _offsetCenter,
        listenable: Listenable.merge([_offset, _offsetCenter])),
  ),

    // 按下
  onPanDown: down,
  // 移动
  onPanUpdate: update,
  // 抬起
  onPanEnd: reset,
);

备注:手指触控屏幕的坐标点永远都是以左上角为原点的,为了方便理解和计算,我们同样也需要将手指的坐标的原点进行偏移到画布中央和画布保持一致,所以这里我们通过手势获取的坐标点之后需要进行偏移。

不管手指点击、移动、还是抬起都要通知画布进行更新,这里使用ValueNotifier<Offset>通知坐标点更新。

ValueNotifier<Offset> _offset = ValueNotifier(Offset.zero);

点击交互 down: 当用户点击可触控区域,将大圆和小圆移动至手指点击的位置。

因为底圆在点击之后抬起之前都是处于静止状态,当移动手指只有小圆移动,所以这里用两个坐标来保存底圆的圆心,和小圆的圆心,当点击时,底圆和小圆的中心是一致的,所以这里当点击时同时更新两个圆心位置。

down(DragDownDetails details) {
  Offset offset = details.localPosition;
  _offsetCenter.value = offset.translate(-size.width / 2, -size.height / 2);
  _offset.value = offset.translate(-size.width / 2, -size.height / 2);
}

这里需要注意的是,当我们的手指点击在可触控区域边界距离小于底圆半径时,需要控制圆心位置的x轴和y轴距离可触控区域边界距离大于等于底圆半径。
如果不控制边界点击时,操纵杆会偏离出触控区域

Flutter手游操纵杆移动的原理与实现方法

所以这里最好在点击时可以加一个边界处理,上下左右加一个边界控制。

if (offset.dx > size.width - bgR) {
  offset = Offset(size.width - bgR, offset.dy);
}
if (offset.dx < bgR) {
  offset = Offset(bgR, offset.dy);
}
if (offset.dy > size.height - bgR) {
  offset = Offset(offset.dx, size.height - bgR);
}
if (offset.dy < bgR) {
  offset = Offset(offset.dx, bgR);
}

之后再点击边界时就不会出界了。

Flutter手游操纵杆移动的原理与实现方法

移动交互 update: 当用户移动手指时,小圆根据手指在底圆内部进行移动。

手指移动是操纵杆的核心交互逻辑。

思路: 当手指点击之后移动离开圆心,计算当前坐标点以当前底圆圆心为原点的偏移弧度,通过反正切函数atan2(y,x)可以得出当前坐标针对x轴向右为正,y轴向下为正的偏移弧度&alpha;,默认范围 [-pi]-[pi], 为了方便理解计算,这里我们将得到的角度+pi转换为 0-2pi,角度范围:0-360&deg;。见下图:

Flutter手游操纵杆移动的原理与实现方法

Offset类里的direction(y,x)方法就是通过atan2方法计算当前坐标的偏移弧度。

/// The angle of this offset as radians clockwise from the positive x-axis, in
/// the range -[pi] to [pi], assuming positive values of the x-axis go to the
/// right and positive values of the y-axis go down.

double get direction => math.atan2(dy, dx);

角色移动的关键就是通过得出的偏移弧度来进行不同方向的移动。

核心代码:

/// 手指移动坐标
var offsetTranslate = offset.value;
/// 操纵杆圆心坐标
var offsetTranslateCenter = offsetCenter.value;
/// 计算当前位置坐标点 左半区域 X为负数
double x = offsetTranslateCenter.dx - offsetTranslate.dx;
/// y轴 下半区域 Y为负数
double y = offsetTranslateCenter.dy - offsetTranslate.dy;
/// 反正切函数 通过此函数可以计算出此坐标旋转的弧度 为正 代表X轴逆时针旋转的角度 为负 顺时针旋转角度
/// 范围 [-pi] - [pi]
double ata = atan2(y, x);
/// 默认坐标系范围为-pi - pi  顺时针旋转坐标系180度 变为 0 - 2*pi;
var thta = ata + pi;
print("angle ${(180 / pi * thta).toInt()}");

这里手指移动分为2种情况,手指在底圆内部和手指在底圆外部。见下图:

Flutter手游操纵杆移动的原理与实现方法

Flutter手游操纵杆移动的原理与实现方法

当手指在底圆内部,我们可以直接使用当前手指传递的坐标计算。

当手指移动到底圆外部,我们需要控制小圆的圆形坐标不能跑到底圆的外部,控制小圆 不能超过底圆的的范围,所以,这里需要进行计算当前手指的坐标距离底圆圆心的距离有没有超过底圆半径,如果超出,需要计算小圆的临界坐标值。

有了偏移弧度&alpha;,我们就可以通过三角函数计算出上面x1,y1的坐标点,也就是当前手指控制小圆圆心的临界坐标。

核心代码:

/// 当前手指坐标距离底圆圆心长度
var r = sqrt(pow(x, 2) + pow(y, 2));
if (r > bgR) {
  var dx = bgR * cos(thta) + offsetTranslateCenter.dx; // x轴坐标点
  var dy = bgR * sin(thta) + offsetTranslateCenter.dy; // y轴坐标点
  offsetTranslate = Offset(dx, dy);
}

松开交互 reset: 当用户点击可触控区域,将大圆和小圆移动至手指点击的位置。

将两个圆的圆心回归坐标系原点。

reset(DragEndDetails details) {
  _offset.value = Offset.zero;
  _offsetCenter.value = Offset.zero;
}

注意的是,当点击和松开时,当前角色都是不动的,只有当移动时才传递角度值赋给角色进行移动,所以当这里需要判断当前手指触控点和底圆圆心是否重合,如果重合表示当前角色处于静止状态。因为默认不作处理,弧度获取的是pi,所以这里需要特殊处理一下。 这里我们需要将获取的弧度值传递出去,如果当前处于静止状态,将弧度设为负数,因为我们的弧度范围是0-2pi,移动状态中不可能为负。

if (x == 0 && y == 0) {
  onAngle?.call(-1);
} else {
  onAngle?.call(thta);
}

为了方便展示效果,我加了坐标轴辅助,这样看起来更直观一些。

最终效果:

通过当前获取的弧度值即可传递给角色进行移动。

Flutter手游操纵杆移动的原理与实现方法

到此,关于“Flutter手游操纵杆移动的原理与实现方法”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI