手势事件


物体编辑器的手势事件

只要 GameMaker Studio 2 检测到鼠标或触摸屏事件的 “手势”(这些手势事件专为移动设备使用而设计,它们也可用于其他目标平台以检测鼠标,虽然在这种情况下它们不会检测到多次触摸),将触发手势事件类别中的 手势事件(Gesture Event)。手势系统的目标是尝试识别比直接鼠标 \ 触摸读取功能更高级别的输入,并且旨在简化基于触摸的设备上的常用输入的实现。

注意: 这些事件 不会在 HTML5 平台上触发。如果你正在寻找该目标上的手势,那么你应该使用 设备函数


你可以选择检测 实例 手势或 全局 手势,其中实例手势事件仅在初始触摸 / 单击位于房间内的实例上时触发。请注意,实例 必须 具有有效的碰撞遮罩(有关详细信息,请参阅 精灵编辑器 - 碰撞遮罩物体编辑器 - 碰撞遮罩 部分)以触发此事件。但是,全局事件将通过触摸 / 点击游戏房间内的任何位置以及所有具有该事件的实例来触发。

当识别出手势事件时,它将触发一个或多个可用子事件,触发的子事件将取决于已检测到的手势的类型。但是,在每种情况下,都会为你生成 数据结构映射(DS Map) 并存储在内置变量 event_data 中。可用的密钥取决于它创建的事件类型,并显示在下面的每个子部分中。

注意: 变量 event_data 仅在这些事件中有效,因为它指向的 ds_map 在事件开始时自动创建,然后在结束时再次销毁,此变量在所有其他事件中重置为 -1 倍。


值得注意的是,如果你触摸的位置下有多个实例并且它们都有一个手势事件,那么所有这些实例都将触发,而不仅仅是 “最顶层” 实例。另请注意,在使用多个视图并拖动实例时,返回的值将基于你在接收到初始触摸 / 单击时所处的视图 - 对于该实例的该手势中的所有后续事件都是如此。因此,在一个视图中触摸并拖动实例然后在另一个视图中释放触摸,将返回相对于首次检测到手势的初始视图的值。


“点按(Tap)”、“拖动(Drag)” 和 “轻抚(Flick)” 事件都基于屏幕上的单触摸或鼠标单击,event_data 数据结构映射将包含以下键:

值描述
"gesture"
这是一个 ID 值,对于正在运行的手势是 唯一 的。这允许你将多个手势的不同部分(例如拖动开始,拖动和拖动结束)链接在一起。

"touch"
这是用于手势的触摸索引。一般来说,这将从 0 开始,并且每次手指按下时增加,然后在所有手指被移除时重置为 0,但如果用户在此事件触发时触摸屏幕其他地方,则值将大于 0。

"posX"
触摸相对于房间的 X 点。

"posY"
触摸相对于房间的 Y 点。

"rawposX"
触摸相对于窗口的 原始 X 点(相当于使用 device_mouse_raw_x() 获取鼠标位置)

"rawposY"
触摸相对于窗口的 原始 Y 点(相当于使用 device_mouse_raw_y() 获取鼠标位置)

"guiposX"
触摸相对于 GUI 层的 X 点(相当于使用 device_mouse_x_to_gui() 获取鼠标位置)

"guiposY"
触摸相对于 GUI 层的 Y 点(相当于使用 device_mouse_y_to_gui() 获取鼠标位置)

"diffX"
这是当前触摸的位置与该手势中的最后一次触摸的位置之间的以房间为基准的 X 距离。

"diffY"
这是当前触摸的位置与该手势中的最后一次触摸的位置之间的以房间为基准的 Y 距离。

"rawdiffX"
这是当前触摸的位置与该手势中的最后一次触摸的位置之间的以屏幕为基准的 X 距离。

"rawdiffY"
这是当前触摸的位置与该手势中的最后一次触摸的位置之间的以屏幕为基准的 Y 距离。

"guidiffX"
这是当前触摸的位置与该手势中的最后一次触摸的位置之间的以 GUI 层为基准的 X 距离。

"guidiffY"
这是当前触摸的位置与该手势中的最后一次触摸的位置之间的以 GUI 层为基准的 Y 距离。

"viewstartposX"
这是当前触摸的位置与房间开始时 X 坐标距离。

"viewstartposY"
这是当前触摸的位置与房间开始时 Y 坐标距离。

"rawstartposX"
这是当前触摸的位置与屏幕开始时 X 坐标距离。

"rawstartposY"
这是当前触摸的位置与屏幕开始时 Y 坐标距离。

"guistartposX"
这是当前触摸的位置与 GUI 层开始时 X 坐标距离。

"guistartposY"
这是当前触摸的位置与 GUI 层开始时 Y 坐标距离。

"isflick"
仅在拖拽结束(Drag End)事件中可用。如果拖拽结束被检测为轻抚,则设置为 1,这意味着如果你正在处理拖动,则不需要单独的 轻抚事件

当触摸或点击实例时触发 点按 事件,或者 - 如果是全局事件 - 当游戏在房间的任何地方注册触摸或点击时触发。轻击被认为是快速触摸和释放,如果触摸持续时间过长,则将其视为拖动(并触发拖动手势事件而不是点按事件)。 此事件将生成 event_data 数据结构映射,你可以使用该地图获取有关该事件的信息。比如:比如:

创建事件

x_goto = x;
y_goto = y;

点按事件

x_goto = event_data[? "posX"];
y_goto = event_data[? "posY"];

步事件

var _pd = point_distance(x, y, x_goto, y_goto);
move_towards_point(x_goto, y_goto, clamp(_pd, 0, 5);


上面的代码将检测屏幕上的点击,然后获取点击的位置以将实例移动到该位置。请注意,如果你想要更长或更短的点击检测时间,则可以使用函数 gesture_drag_time 进行设置。这将初始检测和点击之间的时间设置为拖动,因此将其设置为较高的值以使点击检测更长或将更低的值设置为更短(值以秒为单位,默认为 0.16)。


当一个实例被快速连续触摸或点击两次时(如果它是全局事件,也可以是玩家在在游戏中房间的任何地方两次快速触摸或点击时)将触发 双击 事件。 双击被认为是两次快速触摸和释放,但如果任何触摸持续时间过长,则将其视为拖动(并触发拖动手势事件而不是双击事件)。此事件将生成 event_data 数据结构映射,你可以使用该地图获取有关该事件的信息。比如:比如:

创建事件

x_goto = x;
y_goto = y;

双击事件

instance_destroy();


上面的代码只是检测到双击,然后销毁实例。 请注意,你可以使用函数 gesture_double_tap_time(其默认值 - 以秒为单位 - 为 0.16)设置点击触发双击之间的时间,你还可以使用函数 gesture_double_tap_distance 设置点击之间的检测距离(如果第二次点按在此距离之外它将被视为常规点按事件)。


当用户保持触摸或单击而不释放它时,将触发 拖动开始(Drag Start) 事件。在初始触摸后经过设定时间后将触发一次,默认情况下为 0.16秒(尽管你可以使用函数 gesture_drag_time 将其设置为以秒为单位的任何其他值)。 触发此事件后,只要用户按住触摸 / 点击,就会在每步触发 拖动 事件,直到触摸 / 点击被释放。 此事件将生成 event_data 数据结构映射,你可以使用该映射获取有关该事件的信息。比如:

创建事件

drag_offsetX = 0;
drag_offsetY = 0;

拖动开始事件

drag_offsetX = x - event_data[?"posX"];
drag_offsetY = y - event_data[?"posY"];


上面的代码使用拖动开始事件来获取触摸 / 单击的位置,并使用它来设置 x 和 y 轴的偏移值。然后可以在拖动实例时使用它以确保它不会 “跳转” 到检测到触摸 / 单击的位置(请参阅下面的 拖动 事件以继续此示例)。


拖动(Dragging) 事件在 拖动开始 事件之后触发,并且将在用户维持触摸 / 单击实例(或屏幕,如果它是全局事件)并且移动超过定义的拖动阈值的每步触发。默认情况下,此距离为 0.1 英寸,但可以使用 gesture_drag_distance 函数进行设置。如果没有移动或移动低于定义的阈值,则不会触发事件。 此事件将生成 event_data 数据结构映射,你可以使用该映射获取有关该事件的信息。比如:

创建事件

drag_offsetX = 0;
drag_offsetY = 0;

拖动开始事件

drag_offsetX = x - event_data[?"posX"];
drag_offsetY = y - event_data[?"posY"];

拖动事件

x = event_data[?"posX"] + drag_offsetX;
y = event_data[?"posY"] + drag_offsetY;


上面的示例代码使用 拖动开始 事件中设置的偏移变量来在触发 拖动 事件时移动实例。


当用户在实例(如果事件是全局的,则触发屏幕)上释放触摸 / 单击时触发 拖动结束(Drag End) 事件。此事件将生成一个 event_data 数据结构映射,你可以使用该映射获取有关该事件的信息,但在这种情况下,映射将有一个额外的键:“isflick”。 轻抚计算为拖动发生的 每秒距离,如果 “轻抚” 键的值大于定义的每秒距离值,则该值将为 true,否则为 false。 请注意,默认值为每秒 2 英寸,但你可以使用 gesture_flick_speed 函数将其设置为其他值。 另请注意,如果 “isflick” 变量为 true,则会触发专用的 轻抚 事件。使用的一个例子是:

创建事件

flickVelX = 0.0;
flickVelY = 0.0;

拖动结束事件

isFlick = event_data[?"isflick"];
if (isFlick)
    {
    flickVelX = event_data[?"diffX"];
    flickVelY = event_data[?"diffY"];
    }
else
    {
    flickVelX = 0;
    flickVelY = 0;
    }

步事件

x += flickVelX;
y += flickVelY;
flickVelX *= 0.7;
flickVelY *= 0.7;


上面的代码简单地得到了最后一个 拖动 事件和当前 拖动结束 事件的 x 和 y 位置的差异,如果移动大于轻抚阈值,它将会设置一些用于在步事件中移动实例的变量。


轻抚(Flick) 事件仅在按住 / 拖动,拖动然后释放并且最后拖动位置和释放位置之间的距离大于每秒 2 英寸时触发(这是默认设置,尽管可以 使用 gesture_flick_speed 函数更改)。此事件将生成 event_data 数据结构映射,你可以使用该映射获取有关该事件的信息。比如:

创建事件

flickVelX = 0.0;
flickVelY = 0.0;

轻抚事件

flickVelX = event_data[?"diffX"];
flickVelY = event_data[?"diffY"];

步事件

x += flickVelX;
y += flickVelY;
flickVelX *= 0.7;
flickVelY *= 0.7;


上面的代码简单地得到了最后一个 拖动 事件和当前 轻抚 事件的 x 和 y 位置的差异,如果移动大于轻弹阈值,它会设置一些变量用于移动步骤中的实例事件。


“捏合” 事件基于对设备屏幕的一次识别的两次触摸,其中一个(或两个)移动超过一定距离。 触摸的移动角度以及每次触摸的移动将决定捏合或旋转事件的检测,其中(在 捏合事件(Pinch Event) 类型的情况下):

  • 如果其中一个触摸未移动,则另一个触摸必须在阈值角度内 靠近远离 它(可以使用函数 gesture_pinch_angle_towards()gesture_pinch_angle_away() 设置)。
  • 如果两个触摸都在移动,则它们的速度必须在大致相反的方向上,并且还进行相同的角度阈值检查以确保触摸以近似对准的方式移动。

当使用上述标准检测到两次触摸和移动时,将触发一个捏合事件,并且在每个事件中,将使用以下键填充 event_data 数据结构映射:

值描述
"gesture"
这是一个 ID 值,对于正在运行的手势是 唯一 的。这允许你将多个手势的不同部分(例如拖动开始,拖动和拖动结束)链接在一起。

"touch1"
这是第一次触摸的索引,用作捏合手势的一部分。一般情况下,这将是 0,但如果用户在其他任何地方触摸屏幕时此触发此事件,则该值将大于0。

"touch2"
这是第二次触摸的索引,用作捏合手势的一部分。通常,这将比 touch1 的值多 1,但可能是其他值,具体取决于在其他地方检测到的触摸次数。

"posX1"
第一次触摸相对于房间的 X 点。

"posY1"
第一次触摸相对于房间的 Y 点。

"rawposX1"
第一次触摸相对于窗口的 原始 X 点(相当于使用 device_mouse_raw_x() 获取鼠标位置)

"rawposY1"
第一次触摸相对于窗口的 原始 Y 点(相当于使用 device_mouse_raw_y() 获取鼠标位置)

"guiposX1"
第一次触摸相对于 GUI 层的 X 点(相当于使用 device_mouse_x_to_gui() 获取鼠标位置)

"guiposY1"
第一次触摸相对于 GUI 层的 Y 点(相当于使用 device_mouse_y_to_gui() 获取鼠标位置)

"posX2"
第二次触摸相对于房间的 X 点。

"posY2"
第二次触摸相对于房间的 Y 点。

"rawposX2"
第二次触摸相对于 原始 窗口的X 点

"rawposY2"
第二次触摸相对于 原始 窗口的 Y 点

"guiposX2"
第二次触摸相对于 GUI 层的 X 点

"guiposY2"
第二次触摸相对于 GUI 层的 Y 点

"midpointX"
房间空间中两个触摸之间的中点的 X 位置。

"midpointY"
房间空间中两个触摸之间的中点的 Y 位置。

"rawmidpointX"
这是原始窗口空间中点的 X 位置。

"rawmidpointY"
这是原始窗口空间中点的 Y 位置。

"guimidpointX"
这是 GUI 层中点的 X 位置。

"guimidpointY"
这是 GUI 层中点的 Y 位置。

"relativescale"
这是与该手势中最后一个事件相比的缩放差异(因此,对于 捏入(Pinch In) 事件中的缩放,它总是小于 1.0,而对于 捏出(Pinch Out)事件,它总是大于 1.0)

"absolutescale"
这是与手势开始时手指所处位置的比例(如果手指之间的距离减半,这将是 0.5,而如果距离增加一倍,这将是 2.0)。

当一个实例(或事件是全局的则为屏幕)被两个 “手指” 触摸(并且保持触摸)然后移动一个或两个 “手指” 时,将触发 捏合开始(Pinch Start)事件。如果触摸彼此远离或朝向彼此移动超过最小检查距离(默认为 0.1 英寸,但可以使用函数 gesture_pinch_distance 设置),并且它们之间的角度在定义的值内(默认值到 45° 但可以使用 gesture_pinch_angle_towards()gesture_pinch_angle_away()进行设置),就会触发 捏合开始 事件。 在这种情况下,你可以设置变量或存储位置数据以供将来使用。比如:

捏合开始事件

pinching = true;
pinch_x = event_data[? "midpointX"]; pinch_y = event_data[? "midpointY"];


上面的代码将检测一个捏合,并存储该啮合的中点位置。


每个步骤都会触发 捏入(Pinch In)捏出(Pinch Out) 事件,构成捏合的两个触摸之间的距离会在最小阈值上变化(默认设置为 +/- 0.1 英寸,但你可以使用函数 gesture_pinch_distance 更改它)。 如果捏合的两个触摸没有移动,那么这些事件将不会触发。这些事件将生成一个 event_data 数据结构映射,你可以使用该映射获取有关该事件的信息。比如:

全局捏入 / 捏出事件

var _scale = event_data[?"relativescale"];
var _w = camera_get_view_width(view_camera[0]);
var _h = camera_get_view_height(view_camera[0]);
var _x = camera_get_view_x(view_camera[0]) + (_w / 2);
var _y = camera_get_view_y(view_camera[0]) + (_h / 2);

_w *= _scale;
_h = _w * (room_height / room_width);
_x -= _w / 2;
_y -= _h / 2;

camera_set_view_pos(view_camera[0], _x, _y);
camera_set_view_size(view_camera[0], _w, _h);


上面的代码将根据捏合触摸的相对比例缩放视图。


当用户从设备释放一个(或两个)触摸时,将触发 捏合结束(Pinch End) 事件。 此事件将生成 event_data 数据结构映射,你可以使用该映射获取有关该事件的信息。比如:

捏合结束事件

var _pinchx = event_data[?"midpointX"]; var _pinchy = event_data[?"midpointY"]; var _w = camera_get_view_width(view_camera[0]);
var _h = camera_get_view_height(view_camera[0]);
var _x = _pinchx - (_w / 2);
var _y = _pinchy - (_h / 2);

camera_set_view_pos(view_camera[0], _x, _y);


上面的代码将视图位置设置为在释放触摸时构成捏合的两个触摸的中点。



“旋转” 事件基于对设备屏幕的两次触摸的一次识别,并且在特定时间内两者之间存在一致的角度旋转。触摸的移动角度以及每次触摸的移动将决定捏合或旋转事件的检测,其中(在 旋转事件(Rotate Event) 类型的情况下):

  • 必须按住指定的最短时间进行两次触摸(默认时间为 0.16 秒,但你可以使用函数 gesture_rotate_time() 更改它)。
  • 在这个最小时间段内,它们必须以一致的方向旋转(如果旋转方向在该时间内发生变化,则不会开始旋转)。
  • 旋转量必须超过最小阈值角度(默认设置为 5°,但可以使用函数 gesture_rotate_angle() 更改)。

当使用上述标准检测到两次触摸和移动时,将触发旋转事件,并且在每个事件中,将使用以下键填充 event_data 数据结构映射:

值描述
"gesture"
这是一个 ID 值,对于正在运行的手势是 唯一 的。这允许你将多个手势的不同部分(例如拖动开始,拖动和拖动结束)链接在一起。

"touch1"
这是第一次触摸的索引,用作捏合手势的一部分。一般情况下,这将是 0,但如果用户在其他任何地方触摸屏幕时此触发此事件,则该值将大于0。

"touch2"
这是第二次触摸的索引,用作捏合手势的一部分。通常,这将比 touch1 的值多 1,但可能是其他值,具体取决于在其他地方检测到的触摸次数。

"posX1"
第一次触摸相对于房间的 X 点。

"posY1"
第一次触摸相对于房间的 Y 点。

"rawposX1"
第一次触摸相对于窗口的 原始 X 点(相当于使用 device_mouse_raw_x() 获取鼠标位置)

"rawposY1"
第一次触摸相对于窗口的 原始 Y 点(相当于使用 device_mouse_raw_y() 获取鼠标位置)

"guiposX1"
第一次触摸相对于 GUI 层的 X 点(相当于使用 device_mouse_x_to_gui() 获取鼠标位置)

"guiposY1"
第一次触摸相对于 GUI 层的 Y 点(相当于使用 device_mouse_y_to_gui() 获取鼠标位置)

"posX2"
第二次触摸相对于房间的 X 点。

"posY2"
第二次触摸相对于房间的 Y 点。

"rawposX2"
第二次触摸相对于 原始 窗口的X 点

"rawposY2"
第二次触摸相对于 原始 窗口的 Y 点

"guiposX2"
第二次触摸相对于 GUI 层的 X 点

"guiposY2"
第二次触摸相对于 GUI 层的 Y 点

"pivotX"
在房间空间中旋转枢轴点的 X 位置。

"pivotY"
在房间空间中旋转枢轴点的 Y 位置。

"rawpivotX"
这是旋转枢轴点在原始窗口空间的 X 位置。

"rawpivotY"
这是旋转枢轴点在原始窗口空间的 Y 位置。

"guipivotX"
这是旋转枢轴点在 GUI 层的 X 位置。

"guipivotY"
这是旋转枢轴点在 GUI 层的 Y 位置。

"relativeangle"
与此手势中的最后一个事件相比,这是旋转的差异,以角度为单位

"absoluteangle"
这是与手指开始时手指的位置相比的角度差异,以角度为单位。 因此,例如,如果手指自手势开始以来已经旋转了四分之一圆,则该值将是 90° 或 -90°,这取决于旋转方向。

当一个实例(或事件是全局的则为屏幕)被两个 “手指” 触摸(并保持触摸)然后一个或两个 “手指” 从其开始旋转时,将在开始的位置触发 旋转开始(Rotate Start) 事件。 触摸的旋转需要在很短的时间内开始(默认为 0.16 秒,但可以使用函数 gesture_rotate_time() 设置)并且大于最小角度阈值(默认为 5°,但是可以使用函数 gesture_rotate_angle() 更改。 如果这些检查为真,则将触发 旋转启动 事件,你可以使用它来存储值或设置变量以与其余旋转事件一起使用。比如:

创建事件

rotating = false;
view_a = camera_get_view_angle(view_camera[0]);

旋转开始事件

rotating = true;


上面的代码只是为旋转视图相机设置了一些变量,然后在旋转开始事件中,它将其中一个设置为 true


只要移动大于最小角度阈值(默认为 5°,但是,可以使用函数 gesture_rotate_angle() 更改),旋转 事件将在屏幕上的触摸相互旋转的每一步触发。此事件可用于设置变量和操作实例,例如:

旋转事件

var _relangle = event_data[?"relativeangle"];
var _a = camera_get_view_angle(view_camera[0]);
_a += _relangle;
camera_set_view_angle(view_camera[0], _a);


上述代码根据事件中触摸的旋转运动来旋转摄像机视图。


当从设备屏幕释放构成手势的一个(或两个)触摸时,将触发 旋转结束(Rotate End) 事件。此事件可用于设置变量和操作实例,例如:

旋转结束事件

rotating = false;

步事件

if !rotating
    {
    var _a = camera_get_view_angle(view_camera[0]);
    var _adif = angle_difference(view_a, _a);
    _a += median(-5, _adif, 5);
    camera_set_view_angle(view_camera[0], _a);
    }


上面的代码使用旋转结束事件来检测用户何时停止手势然后设置变量。然后在步事件中使用此变量将视图相机旋转回其原始位置。