Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize drag and click of Pointer #2042

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
21 changes: 19 additions & 2 deletions packages/core/src/Script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,29 @@ export class Script extends Component {
onPointerExit(pointer: Pointer): void {}

/**
* Called when the pointer is down while over the ColliderShape and is still holding down.
* This function will be called when the pointer is pressed on the collider.
* @param pointer
*/
onPointerStartDrag(pointer: Pointer): void {}

/**
* When a drag collision occurs on the pointer, this function will be called every time it moves.
* @param pointer - The pointer that triggered
* @remarks onPointerDrag is called every frame while the pointer is down.
*/
onPointerDrag(pointer: Pointer): void {}

/**
* This function will be called when the pointer is released while dragging.
* @param pointer - The pointer that triggered
*/
onPointerEndDrag(pointer: Pointer): void {}

/**
* This function will be called when the pointer is lifted on the collider.
* @param pointer - The pointer that triggered
*/
onPointerDrop(pointer: Pointer): void {}

/**
* Called when be disabled.
*/
Expand Down
103 changes: 83 additions & 20 deletions packages/core/src/input/pointer/Pointer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Vector2 } from "@galacean/engine-math";
import { DisorderedArray } from "../../DisorderedArray";
import { Entity } from "../../Entity";
import { Script } from "../../Script";
import { HitResult } from "../../physics";
import { PointerButton } from "../enums/PointerButton";
import { PointerPhase } from "../enums/PointerPhase";

Expand All @@ -24,9 +25,13 @@ export class Pointer {
position: Vector2 = new Vector2();
/** The change of the pointer. */
deltaPosition: Vector2 = new Vector2();
/** The hit result of raycasting all scenes using pointer in this frame. */
hitResult: HitResult = new HitResult();
/** @internal */
_events: PointerEvent[] = [];
/** @internal */
_eventsMap: number = PointerEventType.None;
/** @internal */
_uniqueID: number;
/** @internal */
_upMap: number[] = [];
Expand All @@ -37,16 +42,45 @@ export class Pointer {
/** @internal */
_downList: DisorderedArray<PointerButton> = new DisorderedArray();

private _currentPressedEntity: Entity;
private _currentEnteredEntity: Entity;
private _pressedEntity: Entity;
private _enteredEntity: Entity;
private _draggedEntity: Entity;

/**
* If this pointer is hold down, return the entity hit when pointer down.
*/
get pressedEntity(): Entity | null {
return this._pressedEntity;
}

/**
* Returns the entity where the pointer is currently entered.
*/
get enteredEntity(): Entity | null {
return this._enteredEntity;
}

/**
* Returns the entity currently dragged by the pointer.
*/
get draggedEntity(): Entity | null {
return this._draggedEntity;
}

/**
* @internal
*/
constructor(id: number) {
this.id = id;
}

/**
* @internal
*/
_firePointerExitAndEnter(rayCastEntity: Entity): void {
if (this._currentEnteredEntity !== rayCastEntity) {
if (this._currentEnteredEntity) {
this._currentEnteredEntity._scripts.forEach(
if (this._enteredEntity !== rayCastEntity) {
if (this._enteredEntity) {
this._enteredEntity._scripts.forEach(
(element: Script) => {
element.onPointerExit(this);
},
Expand All @@ -65,33 +99,53 @@ export class Pointer {
}
);
}
this._currentEnteredEntity = rayCastEntity;
this._enteredEntity = rayCastEntity;
}
}

/**
* @internal
*/
_firePointerDown(rayCastEntity: Entity): void {
_firePointerDownAndStartDrag(rayCastEntity: Entity): void {
this._pressedEntity = this._draggedEntity = rayCastEntity;
if (rayCastEntity) {
rayCastEntity._scripts.forEach(
(element: Script) => {
element.onPointerDown(this);
element.onPointerStartDrag(this);
},
(element: Script, index: number) => {
element._entityScriptsIndex = index;
}
);
}
}

/**
* @internal
*/
_firePointerUpAndClick(rayCastEntity: Entity): void {
if (rayCastEntity) {
const sameTarget = this._pressedEntity === rayCastEntity;
rayCastEntity._scripts.forEach(
(element: Script) => {
element.onPointerUp(this);
sameTarget && element.onPointerClick(this);
},
(element: Script, index: number) => {
element._entityScriptsIndex = index;
}
);
}
this._currentPressedEntity = rayCastEntity;
this._pressedEntity = null;
}

/**
* @internal
*/
_firePointerDrag(): void {
if (this._currentPressedEntity) {
this._currentPressedEntity._scripts.forEach(
if (this._draggedEntity) {
this._draggedEntity._scripts.forEach(
(element: Script) => {
element.onPointerDrag(this);
},
Expand All @@ -105,27 +159,36 @@ export class Pointer {
/**
* @internal
*/
_firePointerUpAndClick(rayCastEntity: Entity): void {
const { _currentPressedEntity: pressedEntity } = this;
if (pressedEntity) {
const sameTarget = pressedEntity === rayCastEntity;
pressedEntity._scripts.forEach(
_firePointerEndDrag(receivingEntity: Entity): void {
const { _draggedEntity: draggedEntity } = this;
if (draggedEntity) {
draggedEntity._scripts.forEach(
(element: Script) => {
sameTarget && element.onPointerClick(this);
element.onPointerUp(this);
element.onPointerEndDrag(this);
!!receivingEntity && element.onPointerDrop(this);
},
(element: Script, index: number) => {
element._entityScriptsIndex = index;
}
);
this._currentPressedEntity = null;
this._draggedEntity = null;
}
}

/**
* @internal
*/
constructor(id: number) {
this.id = id;
_dispose(): void {
const { hitResult } = this;
this._enteredEntity = this._pressedEntity = this._draggedEntity = hitResult.entity = hitResult.shape = null;
}
}

export enum PointerEventType {
None = 0x0,
Down = 0x1,
Up = 0x2,
Leave = 0x4,
Move = 0x8,
Cancel = 0x10
}
38 changes: 31 additions & 7 deletions packages/core/src/input/pointer/PointerManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { HitResult } from "../../physics";
import { PointerButton, _pointerDec2BinMap } from "../enums/PointerButton";
import { PointerPhase } from "../enums/PointerPhase";
import { IInput } from "../interface/IInput";
import { Pointer } from "./Pointer";
import { Pointer, PointerEventType } from "./Pointer";

/**
* Pointer Manager.
Expand All @@ -18,7 +18,6 @@ import { Pointer } from "./Pointer";
export class PointerManager implements IInput {
private static _tempRay: Ray = new Ray();
private static _tempPoint: Vector2 = new Vector2();
private static _tempHitResult: HitResult = new HitResult();
/** @internal */
_pointers: Pointer[] = [];
/** @internal */
Expand Down Expand Up @@ -74,6 +73,7 @@ export class PointerManager implements IInput {
// Clean up the pointer released in the previous frame
for (let i = pointers.length - 1; i >= 0; i--) {
if (pointers[i].phase === PointerPhase.Leave) {
pointers[i]._dispose();
pointers.splice(i, 1);
}
}
Expand Down Expand Up @@ -113,6 +113,7 @@ export class PointerManager implements IInput {
for (let i = 0, n = pointers.length; i < n; i++) {
const pointer = pointers[i];
pointer._upList.length = pointer._downList.length = 0;
pointer._eventsMap = PointerEventType.None;
this._updatePointerInfo(frameCount, pointer, left, top, widthDPR, heightDPR);
this._buttons |= pointer.pressedButtons;
}
Expand All @@ -126,26 +127,36 @@ export class PointerManager implements IInput {
for (let i = 0, n = pointers.length; i < n; i++) {
const pointer = pointers[i];
const { _events: events, position } = pointer;
pointer._firePointerDrag();
const rayCastEntity = this._pointerRayCast(scenes, position.x / canvas.width, position.y / canvas.height);
const rayCastEntity = this._pointerRayCast(
scenes,
position.x / canvas.width,
position.y / canvas.height,
pointer.hitResult
);
pointer._firePointerExitAndEnter(rayCastEntity);
const length = events.length;
if (length > 0) {
if (pointer._eventsMap & PointerEventType.Move) {
pointer.phase = PointerPhase.Move;
pointer._firePointerDrag();
}
for (let i = 0; i < length; i++) {
const event = events[i];
switch (event.type) {
case "pointerdown":
pointer.phase = PointerPhase.Down;
pointer._firePointerDown(rayCastEntity);
pointer._firePointerDownAndStartDrag(rayCastEntity);
break;
case "pointerup":
pointer.phase = PointerPhase.Up;
pointer._firePointerUpAndClick(rayCastEntity);
pointer._firePointerEndDrag(rayCastEntity);
break;
case "pointerleave":
case "pointercancel":
pointer.phase = PointerPhase.Leave;
pointer._firePointerExitAndEnter(null);
pointer._firePointerEndDrag(null);
break;
}
}
Expand Down Expand Up @@ -208,20 +219,28 @@ export class PointerManager implements IInput {
pointer._downList.add(button);
pointer._downMap[button] = frameCount;
pointer.phase = PointerPhase.Down;
pointer._eventsMap |= PointerEventType.Down;
break;
case "pointerup":
_upList.add(button);
_upMap[button] = frameCount;
pointer._upList.add(button);
pointer._upMap[button] = frameCount;
pointer.phase = PointerPhase.Up;
pointer._eventsMap |= PointerEventType.Up;
break;
case "pointermove":
pointer.phase = PointerPhase.Move;
pointer._eventsMap |= PointerEventType.Move;
break;
case "pointerleave":
pointer.phase = PointerPhase.Leave;
pointer._eventsMap |= PointerEventType.Leave;
break;
case "pointercancel":
pointer.phase = PointerPhase.Leave;
pointer._eventsMap |= PointerEventType.Cancel;
break;
default:
break;
}
Expand All @@ -233,8 +252,13 @@ export class PointerManager implements IInput {
}
}

private _pointerRayCast(scenes: readonly Scene[], normalizedX: number, normalizedY: number): Entity {
const { _tempPoint: point, _tempRay: ray, _tempHitResult: hitResult } = PointerManager;
private _pointerRayCast(
scenes: readonly Scene[],
normalizedX: number,
normalizedY: number,
hitResult: HitResult
): Entity {
const { _tempPoint: point, _tempRay: ray } = PointerManager;
for (let i = scenes.length - 1; i >= 0; i--) {
const scene = scenes[i];
if (!scene.isActive || scene.destroyed) {
Expand Down