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 12 commits into
base: main
Choose a base branch
from
23 changes: 21 additions & 2 deletions packages/core/src/Script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,31 @@ 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
*/
onPointerBeginDrag(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 {}

/**
* When dragging ends, this function will be called(Dragged object).
* @param pointer - The pointer that triggered
* @remarks Dragged object: The object being dragged.
*/
onPointerEndDrag(pointer: Pointer): void {}

/**
* When dragging ends, this function will be called(Receiving object).
* @param pointer - The pointer that triggered
* @remarks Receiving object: The collider hit when ending the drag.
*/
onPointerDrop(pointer: Pointer): void {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure the comments is right and clearly


/**
* 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 { 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 @@
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 @@
/** @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 @@
}
);
}
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.onPointerBeginDrag(this);
},
(element: Script, index: number) => {
element._entityScriptsIndex = index;

Check warning on line 118 in packages/core/src/input/pointer/Pointer.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/input/pointer/Pointer.ts#L118

Added line #L118 was not covered by tests
}
);
}
}

/**
* @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 @@
/**
* @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
Loading
Loading