From 0e53dde603ee3dd6f6fd9a70c260ced84128c909 Mon Sep 17 00:00:00 2001 From: PP Date: Wed, 13 Sep 2023 10:44:20 +0800 Subject: [PATCH] feat: support touch info query in input module (#16235) Co-authored-by: finscn --- cocos/input/input.ts | 131 +++++++++++++++++++++++------- cocos/input/types/touch.ts | 19 ++++- pal/input/minigame/touch-input.ts | 12 ++- pal/input/native/touch-input.ts | 12 ++- pal/input/touch-manager.ts | 52 ++++++------ pal/input/web/touch-input.ts | 13 ++- 6 files changed, 171 insertions(+), 68 deletions(-) diff --git a/cocos/input/input.ts b/cocos/input/input.ts index 4cf549caa09..cbbe0d923e8 100644 --- a/cocos/input/input.ts +++ b/cocos/input/input.ts @@ -25,9 +25,9 @@ */ import { EDITOR_NOT_IN_PREVIEW, NATIVE } from 'internal:constants'; -import { TouchInputSource, MouseInputSource, KeyboardInputSource, AccelerometerInputSource, GamepadInputDevice, HandleInputDevice, HMDInputDevice, HandheldInputDevice } from 'pal/input'; +import { AccelerometerInputSource, GamepadInputDevice, HMDInputDevice, HandheldInputDevice, HandleInputDevice, KeyboardInputSource, MouseInputSource, TouchInputSource } from 'pal/input'; import { touchManager } from '../../pal/input/touch-manager'; -import { sys, EventTarget } from '../core'; +import { EventTarget, error, sys } from '../core'; import { Event, EventAcceleration, EventGamepad, EventHandle, EventHandheld, EventHMD, EventKeyboard, EventMouse, EventTouch, Touch } from './types'; import { InputEventType } from './types/event-enum'; @@ -62,7 +62,7 @@ class InputEventDispatcher implements IEventDispatcher { } } -const pointerEventTypeMap: Record = { +const pointerEventTypeMap: Record = { [InputEventType.MOUSE_DOWN]: InputEventType.TOUCH_START, [InputEventType.MOUSE_MOVE]: InputEventType.TOUCH_MOVE, [InputEventType.MOUSE_UP]: InputEventType.TOUCH_END, @@ -160,28 +160,40 @@ export class Input { /** * This should be a private method, but it's exposed for Editor Only. */ - private _dispatchMouseDownEvent (nativeMouseEvent: any): void { this._mouseInput.dispatchMouseDownEvent?.(nativeMouseEvent); } + private _dispatchMouseDownEvent (nativeMouseEvent: any): void { + this._mouseInput.dispatchMouseDownEvent?.(nativeMouseEvent); + } /** * This should be a private method, but it's exposed for Editor Only. */ - private _dispatchMouseMoveEvent (nativeMouseEvent: any): void { this._mouseInput.dispatchMouseMoveEvent?.(nativeMouseEvent); } + private _dispatchMouseMoveEvent (nativeMouseEvent: any): void { + this._mouseInput.dispatchMouseMoveEvent?.(nativeMouseEvent); + } /** * This should be a private method, but it's exposed for Editor Only. */ - private _dispatchMouseUpEvent (nativeMouseEvent: any): void { this._mouseInput.dispatchMouseUpEvent?.(nativeMouseEvent); } + private _dispatchMouseUpEvent (nativeMouseEvent: any): void { + this._mouseInput.dispatchMouseUpEvent?.(nativeMouseEvent); + } /** * This should be a private method, but it's exposed for Editor Only. */ - private _dispatchMouseScrollEvent (nativeMouseEvent: any): void { this._mouseInput.dispatchScrollEvent?.(nativeMouseEvent); } + private _dispatchMouseScrollEvent (nativeMouseEvent: any): void { + this._mouseInput.dispatchScrollEvent?.(nativeMouseEvent); + } /** * This should be a private method, but it's exposed for Editor Only. */ - private _dispatchKeyboardDownEvent (nativeKeyboardEvent: any): void { this._keyboardInput.dispatchKeyboardDownEvent?.(nativeKeyboardEvent); } + private _dispatchKeyboardDownEvent (nativeKeyboardEvent: any): void { + this._keyboardInput.dispatchKeyboardDownEvent?.(nativeKeyboardEvent); + } /** * This should be a private method, but it's exposed for Editor Only. */ - private _dispatchKeyboardUpEvent (nativeKeyboardEvent: any): void { this._keyboardInput.dispatchKeyboardUpEvent?.(nativeKeyboardEvent); } + private _dispatchKeyboardUpEvent (nativeKeyboardEvent: any): void { + this._keyboardInput.dispatchKeyboardUpEvent?.(nativeKeyboardEvent); + } /** * @en @@ -229,6 +241,37 @@ export class Input { } this._eventTarget.off(eventType, callback, target); } + + /** + * @en + * Get touch object by touch ID. + * @zh + * 通过 touch ID 获取 touch对象。 + */ + public getTouch (touchID: number): Readonly | undefined { + return touchManager.getTouch(touchID); + } + + /** + * @en + * Get all the current touches objects as array. + * @zh + * 获取当前 所有touch对象 的数组。 + */ + public getAllTouches (): Touch[] { + return touchManager.getAllTouches(); + } + + /** + * @en + * Get the number of touches. + * @zh + * 获取当前 touch 对象的数量。 + */ + public getTouchCount (): number { + return touchManager.getTouchCount(); + } + /** * @en * Sets whether to enable the accelerometer event listener or not. @@ -264,7 +307,7 @@ export class Input { private _simulateEventTouch (eventMouse: EventMouse): void { const eventType = pointerEventTypeMap[eventMouse.type]; const touchID = 0; - const touch = touchManager.getTouch(touchID, eventMouse.getLocationX(), eventMouse.getLocationY()); + const touch = touchManager.getOrCreateTouch(touchID, eventMouse.getLocationX(), eventMouse.getLocationY()); if (!touch) { return; } @@ -295,8 +338,8 @@ export class Input { break; } } catch (e) { - console.error(`Error occurs in an event listener: ${event.type}`); - console.error(e); + error(`Error occurs in an event listener: ${event.type}`); + error(e); } } } @@ -304,10 +347,18 @@ export class Input { private _registerEvent (): void { if (sys.hasFeature(sys.Feature.INPUT_TOUCH)) { const eventTouchList = this._eventTouchList; - this._touchInput.on(InputEventType.TOUCH_START, (event): void => { this._dispatchOrPushEventTouch(event, eventTouchList); }); - this._touchInput.on(InputEventType.TOUCH_MOVE, (event): void => { this._dispatchOrPushEventTouch(event, eventTouchList); }); - this._touchInput.on(InputEventType.TOUCH_END, (event): void => { this._dispatchOrPushEventTouch(event, eventTouchList); }); - this._touchInput.on(InputEventType.TOUCH_CANCEL, (event): void => { this._dispatchOrPushEventTouch(event, eventTouchList); }); + this._touchInput.on(InputEventType.TOUCH_START, (event): void => { + this._dispatchOrPushEventTouch(event, eventTouchList); + }); + this._touchInput.on(InputEventType.TOUCH_MOVE, (event): void => { + this._dispatchOrPushEventTouch(event, eventTouchList); + }); + this._touchInput.on(InputEventType.TOUCH_END, (event): void => { + this._dispatchOrPushEventTouch(event, eventTouchList); + }); + this._touchInput.on(InputEventType.TOUCH_CANCEL, (event): void => { + this._dispatchOrPushEventTouch(event, eventTouchList); + }); } if (sys.hasFeature(sys.Feature.EVENT_MOUSE)) { @@ -328,42 +379,66 @@ export class Input { this._simulateEventTouch(event); this._dispatchOrPushEvent(event, eventMouseList); }); - this._mouseInput.on(InputEventType.MOUSE_WHEEL, (event): void => { this._dispatchOrPushEvent(event, eventMouseList); }); + this._mouseInput.on(InputEventType.MOUSE_WHEEL, (event): void => { + this._dispatchOrPushEvent(event, eventMouseList); + }); } if (sys.hasFeature(sys.Feature.EVENT_KEYBOARD)) { const eventKeyboardList = this._eventKeyboardList; - this._keyboardInput.on(InputEventType.KEY_DOWN, (event): void => { this._dispatchOrPushEvent(event, eventKeyboardList); }); - this._keyboardInput.on(InputEventType.KEY_PRESSING, (event): void => { this._dispatchOrPushEvent(event, eventKeyboardList); }); - this._keyboardInput.on(InputEventType.KEY_UP, (event): void => { this._dispatchOrPushEvent(event, eventKeyboardList); }); + this._keyboardInput.on(InputEventType.KEY_DOWN, (event): void => { + this._dispatchOrPushEvent(event, eventKeyboardList); + }); + this._keyboardInput.on(InputEventType.KEY_PRESSING, (event): void => { + this._dispatchOrPushEvent(event, eventKeyboardList); + }); + this._keyboardInput.on(InputEventType.KEY_UP, (event): void => { + this._dispatchOrPushEvent(event, eventKeyboardList); + }); } if (sys.hasFeature(sys.Feature.EVENT_ACCELEROMETER)) { const eventAccelerationList = this._eventAccelerationList; - this._accelerometerInput.on(InputEventType.DEVICEMOTION, (event): void => { this._dispatchOrPushEvent(event, eventAccelerationList); }); + this._accelerometerInput.on(InputEventType.DEVICEMOTION, (event): void => { + this._dispatchOrPushEvent(event, eventAccelerationList); + }); } if (sys.hasFeature(sys.Feature.EVENT_GAMEPAD)) { const eventGamepadList = this._eventGamepadList; - GamepadInputDevice._on(InputEventType.GAMEPAD_CHANGE, (event): void => { this._dispatchOrPushEvent(event, eventGamepadList); }); - GamepadInputDevice._on(InputEventType.GAMEPAD_INPUT, (event): void => { this._dispatchOrPushEvent(event, eventGamepadList); }); - GamepadInputDevice._on(InputEventType.HANDLE_POSE_INPUT, (event): void => { this._dispatchOrPushEvent(event, eventGamepadList); }); + GamepadInputDevice._on(InputEventType.GAMEPAD_CHANGE, (event): void => { + this._dispatchOrPushEvent(event, eventGamepadList); + }); + GamepadInputDevice._on(InputEventType.GAMEPAD_INPUT, (event): void => { + this._dispatchOrPushEvent(event, eventGamepadList); + }); + GamepadInputDevice._on(InputEventType.HANDLE_POSE_INPUT, (event): void => { + this._dispatchOrPushEvent(event, eventGamepadList); + }); } if (sys.hasFeature(sys.Feature.EVENT_HANDLE)) { const eventHandleList = this._eventHandleList; - this._handleInput._on(InputEventType.HANDLE_INPUT, (event): void => { this._dispatchOrPushEvent(event, eventHandleList); }); - this._handleInput._on(InputEventType.HANDLE_POSE_INPUT, (event): void => { this._dispatchOrPushEvent(event, eventHandleList); }); + this._handleInput._on(InputEventType.HANDLE_INPUT, (event): void => { + this._dispatchOrPushEvent(event, eventHandleList); + }); + this._handleInput._on(InputEventType.HANDLE_POSE_INPUT, (event): void => { + this._dispatchOrPushEvent(event, eventHandleList); + }); } if (sys.hasFeature(sys.Feature.EVENT_HMD)) { const eventHMDList = this._eventHMDList; - this._hmdInput._on(InputEventType.HMD_POSE_INPUT, (event): void => { this._dispatchOrPushEvent(event, eventHMDList); }); + this._hmdInput._on(InputEventType.HMD_POSE_INPUT, (event): void => { + this._dispatchOrPushEvent(event, eventHMDList); + }); } if (sys.hasFeature(sys.Feature.EVENT_HANDHELD)) { const eventHandheldList = this._eventHandheldList; - this._handheldInput._on(InputEventType.HANDHELD_POSE_INPUT, (event): void => { this._dispatchOrPushEvent(event, eventHandheldList); }); + this._handheldInput._on(InputEventType.HANDHELD_POSE_INPUT, (event): void => { + this._dispatchOrPushEvent(event, eventHandheldList); + }); } } diff --git a/cocos/input/types/touch.ts b/cocos/input/types/touch.ts index 1be58d480e2..2e0cc238702 100644 --- a/cocos/input/types/touch.ts +++ b/cocos/input/types/touch.ts @@ -199,7 +199,7 @@ export class Touch { _vec2.set(this._point); _vec2.subtract(this._prevPoint); - out.set(cclegacy.view.getScaleX(), cclegacy.view.getScaleY()); + out.set(cclegacy.view.getScaleX() as number, cclegacy.view.getScaleY() as number); Vec2.divide(out, _vec2, out); return out; } @@ -261,7 +261,7 @@ export class Touch { * @param x - x position of the touch point * @param y - y position of the touch point */ - public setTouchInfo (id = 0, x?: number, y?: number): void { + public setTouchInfo (id: number = 0, x: number = 0, y: number = 0): void { this._prevPoint = this._point; this._point = new Vec2(x || 0, y || 0); this._id = id; @@ -321,6 +321,21 @@ export class Touch { } this._lastModified = cclegacy.game.frameStartTime; } + + /** + * @zh Touch 对象的原始数据不应该被修改。如果你需要这么做,最好克隆一个新的对象。 + * @en The original Touch object shouldn't be modified. If you need to, it's better to clone a new one. + */ + public clone (): Touch { + const touchID = this.getID(); + this.getStartLocation(_vec2); + const clonedTouch = new Touch(_vec2.x, _vec2.y, touchID); + this.getLocation(_vec2); + clonedTouch.setPoint(_vec2.x, _vec2.y); + this.getPreviousLocation(_vec2); + clonedTouch.setPrevPoint(_vec2); + return clonedTouch; + } } cclegacy.Touch = Touch; diff --git a/pal/input/minigame/touch-input.ts b/pal/input/minigame/touch-input.ts index cce9b4ecd7b..5f6fc3aab6f 100644 --- a/pal/input/minigame/touch-input.ts +++ b/pal/input/minigame/touch-input.ts @@ -52,7 +52,7 @@ export class TouchInputSource { } private _createCallback (eventType: InputEventType) { - return (event: any): void => { + return (event: TouchEvent): void => { const handleTouches: Touch[] = []; const windowSize = screenAdapter.windowSize; const dpr = screenAdapter.devicePixelRatio; @@ -64,7 +64,7 @@ export class TouchInputSource { continue; } const location = this._getLocation(changedTouch, windowSize, dpr); - const touch = touchManager.getTouch(touchID, location.x, location.y); + const touch = touchManager.getOrCreateTouch(touchID, location.x, location.y); if (!touch) { continue; } @@ -74,8 +74,12 @@ export class TouchInputSource { handleTouches.push(touch); } if (handleTouches.length > 0) { - const eventTouch = new EventTouch(handleTouches, false, eventType, - macro.ENABLE_MULTI_TOUCH ? touchManager.getAllTouches() : handleTouches); + const eventTouch = new EventTouch( + handleTouches, + false, + eventType, + macro.ENABLE_MULTI_TOUCH ? touchManager.getAllTouches() : handleTouches, + ); this._eventTarget.emit(eventType, eventTouch); } }; diff --git a/pal/input/native/touch-input.ts b/pal/input/native/touch-input.ts index 08fce870209..72b3d5b2c83 100644 --- a/pal/input/native/touch-input.ts +++ b/pal/input/native/touch-input.ts @@ -54,7 +54,7 @@ export class TouchInputSource { return (changedTouches: TouchList, windowId: number): void => { const handleTouches: Touch[] = []; const length = changedTouches.length; - const windowSize = this._windowManager.getWindow(windowId).getViewSize(); + const windowSize = this._windowManager.getWindow(windowId).getViewSize() as Size; for (let i = 0; i < length; ++i) { const changedTouch = changedTouches[i]; const touchID = changedTouch.identifier; @@ -62,7 +62,7 @@ export class TouchInputSource { continue; } const location = this._getLocation(changedTouch, windowSize); - const touch = touchManager.getTouch(touchID, location.x, location.y); + const touch = touchManager.getOrCreateTouch(touchID, location.x, location.y); if (!touch) { continue; } @@ -72,8 +72,12 @@ export class TouchInputSource { handleTouches.push(touch); } if (handleTouches.length > 0) { - const eventTouch = new EventTouch(handleTouches, false, eventType, - macro.ENABLE_MULTI_TOUCH ? touchManager.getAllTouches() : handleTouches); + const eventTouch = new EventTouch( + handleTouches, + false, + eventType, + macro.ENABLE_MULTI_TOUCH ? touchManager.getAllTouches() : handleTouches, + ); eventTouch.windowId = windowId; this._eventTarget.emit(eventType, eventTouch); } diff --git a/pal/input/touch-manager.ts b/pal/input/touch-manager.ts index 6c4ec9a4bc2..7f64d6c138d 100644 --- a/pal/input/touch-manager.ts +++ b/pal/input/touch-manager.ts @@ -22,9 +22,10 @@ THE SOFTWARE. */ +import { Vec2 } from '../../cocos/core/math/vec2'; +import { log } from '../../cocos/core/platform/debug'; import { macro } from '../../cocos/core/platform/macro'; import { Touch } from '../../cocos/input/types'; -import { Vec2 } from '../../cocos/core/math/vec2'; const tempVec2 = new Vec2(); @@ -39,22 +40,6 @@ class TouchManager { this._touchMap = new Map(); } - /** - * The original touch object can't be modified, so we need to return the cloned touch object. - * @param touch - * @returns - */ - private _cloneTouch (touch: Touch): Touch { - const touchID = touch.getID(); - touch.getStartLocation(tempVec2); - const clonedTouch = new Touch(tempVec2.x, tempVec2.y, touchID); - touch.getLocation(tempVec2); - clonedTouch.setPoint(tempVec2.x, tempVec2.y); - touch.getPreviousLocation(tempVec2); - clonedTouch.setPrevPoint(tempVec2); - return clonedTouch; - } - /** * Create the touch object at the touch start event callback. * we have some policy to create the touch object: @@ -68,18 +53,18 @@ class TouchManager { */ private _createTouch (touchID: number, x: number, y: number): Touch | undefined { if (this._touchMap.has(touchID)) { - console.log('Cannot create the same touch object.'); + log('Cannot create the same touch object.'); return undefined; } const checkResult = this._checkTouchMapSizeMoreThanMax(touchID); if (checkResult) { - console.log('The touches is more than MAX_TOUCHES.'); // TODO: logID 2300 + log('The touches is more than MAX_TOUCHES.'); // TODO: logID 2300 return undefined; } const touch = new Touch(x, y, touchID); this._touchMap.set(touchID, touch); this._updateTouch(touch, x, y); - return this._cloneTouch(touch); + return touch; } /** @@ -99,14 +84,23 @@ class TouchManager { * @param touchID * @returns */ - public getTouch (touchID: number, x: number, y: number): Touch | undefined { - let touch = this._touchMap.get(touchID); + public getTouch (touchID: number): Touch | undefined { + return this._touchMap.get(touchID); + } + + /** + * Get or create touch object by touch ID. + * @param touchID + * @returns + */ + public getOrCreateTouch (touchID: number, x: number, y: number): Touch | undefined { + let touch = this.getTouch(touchID); if (!touch) { touch = this._createTouch(touchID, x, y); } else { this._updateTouch(touch, x, y); } - return touch ? this._cloneTouch(touch) : undefined; + return touch; } /** @@ -117,13 +111,19 @@ class TouchManager { const touches: Touch[] = []; this._touchMap.forEach((touch) => { if (touch) { - const clonedTouch = this._cloneTouch(touch); - touches.push(clonedTouch); + touches.push(touch); } }); return touches; } + /** + * Get the number of touches. + */ + public getTouchCount (): number { + return touchManager._touchMap.size; + } + /** * Update the location and previous location of current touch ID. * @param touchID @@ -148,7 +148,7 @@ class TouchManager { const now = performance.now(); this._touchMap.forEach((touch) => { if (now - touch.lastModified > macro.TOUCH_TIMEOUT) { - console.log(`The touches is more than MAX_TOUCHES, release touch id ${touch.getID()}.`); + log(`The touches is more than MAX_TOUCHES, release touch id ${touch.getID()}.`); // TODO: need to handle touch cancel event when exceed the max number of touches ? this.releaseTouch(touch.getID()); } diff --git a/pal/input/web/touch-input.ts b/pal/input/web/touch-input.ts index ae90109d852..fbc1a4da05f 100644 --- a/pal/input/web/touch-input.ts +++ b/pal/input/web/touch-input.ts @@ -33,6 +33,7 @@ import { touchManager } from '../touch-manager'; import { macro } from '../../../cocos/core/platform/macro'; import { InputEventType } from '../../../cocos/input/types/event-enum'; import { Feature } from '../../system-info/enum-type'; +import { warn } from '../../../cocos/core/platform/debug'; export class TouchInputSource { private _canvas?: HTMLCanvasElement; @@ -42,7 +43,7 @@ export class TouchInputSource { if (systemInfo.hasFeature(Feature.INPUT_TOUCH)) { this._canvas = document.getElementById('GameCanvas') as HTMLCanvasElement; if (!this._canvas && !TEST && !EDITOR) { - console.warn('failed to access canvas'); + warn('failed to access canvas'); } // In Editor, we don't receive touch event but maybe receive simulated touch event. if (!EDITOR) { @@ -71,7 +72,7 @@ export class TouchInputSource { continue; } const location = this._getLocation(changedTouch, canvasRect); - const touch = touchManager.getTouch(touchID, location.x, location.y); + const touch = touchManager.getOrCreateTouch(touchID, location.x, location.y); if (!touch) { continue; } @@ -88,8 +89,12 @@ export class TouchInputSource { this._canvas?.focus(); } if (handleTouches.length > 0) { - const eventTouch = new EventTouch(handleTouches, false, eventType, - macro.ENABLE_MULTI_TOUCH ? touchManager.getAllTouches() : handleTouches); + const eventTouch = new EventTouch( + handleTouches, + false, + eventType, + macro.ENABLE_MULTI_TOUCH ? touchManager.getAllTouches() : handleTouches, + ); this._eventTarget.emit(eventType, eventTouch); } };