Flutter [Gesto y dibujo] Análisis de movimiento del joystick del juego móvil

Estoy participando en el reclutamiento del programa de firma de creadores de la Comunidad Tecnológica de Nuggets, haga clic en el enlace para registrarse y enviar .

prefacio

El último artículo introdujo la aplicación de gestos en el lienzo, entonces, ¿qué tipo de chispas pueden producir los gestos y el dibujo del lienzo? Este artículo explicará el principio y el proceso de implementación del joystick para mover al personaje en el juego móvil.

la idea basica

Determine el área del joystick, determine el área de respuesta del gesto al hacer clic y, cuando el dedo deslice el joystick, calcule el desplazamiento en radianes entre la posición actual del dedo y el centro actual del joystick, para determinar la dirección de movimiento del personaje actual. Hagámoslo paso a paso.

dibujar

Dibuja los gráficos estáticos del joystick. Después de jugar el juego móvil, debes saber que la composición básica del joystick se compone de un círculo inferior y una bola que se mueve con los dedos. La bola que se mueve con los dedos gira 360° alrededor del círculo inferior para Controla al personaje para que se mueva en diferentes direcciones.

efecto estático

El núcleo del joystick se compone de dos círculos y el código es muy simple.
imagen.png

Codigo de dibujo:

// 底圆
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));

Agregar interacción de gestosGestureDetector

Idea general: cuando haga clic en el área táctil, mueva el joystick a la posición donde se presiona el dedo actual, mueva el dedo y calcule el ángulo de compensación de acuerdo con las coordenadas de posición del dedo y las coordenadas del centro del círculo cuando se presiona para obtener el punto de coordenadas del dedo en relación con el círculo inferior, suelte el dedo, el joystick se restablecerá a la posición original.

Componente de gesto

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

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

Observaciones: Como se introdujo en el artículo anterior, el punto de coordenadas de la pantalla táctil del dedo siempre toma la esquina superior izquierda como origen. Para facilitar la comprensión y el cálculo, también debemos desplazar el origen de la coordenada del dedo al centro de el lienzo y el lienzo para mantener Consistente, por lo que aquí debemos compensar los puntos de coordenadas obtenidos por gestos.

Independientemente de si se hace clic en el dedo, se mueve o se levanta, se debe notificar al lienzo para que se actualice. Aquí, el ValueNotifier<Offset>punto de coordenadas de notificación se usa para actualizar.

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

Haga clic en la interacción hacia abajo: cuando el usuario haga clic en el área táctil, mueva el círculo grande y el círculo pequeño a la posición donde hizo clic el dedo.

Debido a que el círculo inferior está en un estado estático hasta que se levanta después de hacer clic, solo el círculo pequeño se mueve cuando se mueve el dedo, por lo que aquí se usan dos coordenadas para guardar el centro del círculo inferior y el centro del círculo pequeño. Al hacer clic, el círculo inferior y el círculo pequeño se mueven. Los centros de los círculos son los mismos, por lo que aquí las posiciones de los dos centros de los círculos se actualizan al mismo tiempo cuando se hace clic.

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轴距离可触控区域边界距离大于等于底圆半径。
如果不控制边界点击时,操纵杆会偏离出触控区域, 10-jul-2022 10-12-52.gif
所以这里最好在点击时可以加一个边界处理,上下左右加一个边界控制。

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);
}

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

10-jul-2022 19-10-24.gif

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

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

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

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种情况,手指在底圆内部和手指在底圆外部。见下图:
imagen.png imagen.png

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

当手指移动到底圆外部,我们需要控制小圆的圆形坐标不能跑到底圆的外部,控制小圆 不能超过底圆的的范围,所以,这里需要进行计算当前手指的坐标距离底圆圆心的距离有没有超过底圆半径,如果超出,需要计算小圆的临界坐标值。
有了偏移弧度α,我们就可以通过三角函数计算出上面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);
}

Libere el restablecimiento de la interacción: cuando el usuario toque el área táctil, mueva el círculo grande y el círculo pequeño a la posición donde hizo clic con el dedo.
Regrese el centro de los dos círculos al origen del sistema de coordenadas.

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

Cabe señalar que al hacer clic y soltar, el personaje actual no se mueve, y solo cuando se mueve es el valor del ángulo asignado al personaje para el movimiento, por lo que aquí es necesario juzgar si el punto de contacto del dedo actual y el centro del superposición del círculo inferior, si coincide, significa que el personaje actual está en un estado estacionario. Debido a que no se procesa el valor predeterminado, se obtiene el radián pi, por lo que aquí se requiere un procesamiento especial. Aquí necesitamos pasar el valor obtenido en radianes, si actualmente está en un estado estacionario, establezca el radian en un número negativo, porque nuestro rango de radianes es 0-2pique es imposible ser negativo en un estado en movimiento.

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

Para facilitar la visualización del efecto, agregué la asistencia del eje de coordenadas, que parece más intuitivo.

Efecto final:
el valor en radianes obtenido actualmente se puede pasar al personaje para el movimiento.

10-jul-2022 11-22-32.gif

Resumir

Este artículo presenta principalmente cómo el joystick transmite información efectiva al personaje para controlar el movimiento del personaje. De hecho, la lógica de implementación del joystick no es complicada. La dificultad principal se centra en el cálculo del arco de compensación cuando se mueve el dedo. , y el procesamiento de límites de la bola pequeña. , dominando estos dos puntos, también dominando la lógica central. Ese artículo termina aquí, espero que te ayude~

Supongo que te gusta

Origin juejin.im/post/7118584436047740936
Recomendado
Clasificación