/** * 输入管理器实现 * 处理鼠标和触摸输入,转换为游戏事件 */ import { Camera, Component, EventMouse, EventTouch, Vec2, Vec3, _decorator } from 'cc'; import { EventBus } from '../Core/EventBus'; import { Vector2 } from '../Core/GameData'; import { BaseInputEvent, InputButton, InputCallback, InputConfig, InputEventType, InputState, MouseInputEvent, TouchInputEvent } from './InputTypes'; const { ccclass, property } = _decorator; @ccclass export class InputManager extends Component { @property({ tooltip: '启用鼠标输入' }) enableMouse: boolean = true; @property({ tooltip: '启用触摸输入' }) enableTouch: boolean = true; @property({ tooltip: '双击时间间隔(ms)' }) doubleClickTime: number = 300; @property({ tooltip: '长按时间(ms)' }) longPressTime: number = 500; @property({ tooltip: '拖拽阈值(像素)' }) dragThreshold: number = 10; private eventBus: EventBus; private camera: Camera | null = null; private inputState: InputState; private config: InputConfig; private callbacks: Map = new Map(); /** * 组件初始化 */ onLoad(): void { this.eventBus = EventBus.getInstance(); this.initializeInputState(); this.initializeConfig(); this.setupInputEvents(); console.log('InputManager initialized'); } /** * 初始化输入状态 */ private initializeInputState(): void { this.inputState = { isMouseDown: false, isTouchActive: false, lastClickTime: 0, lastClickPosition: { x: 0, y: 0 }, isDragging: false, dragStartPosition: { x: 0, y: 0 }, activeTouches: new Map() }; } /** * 初始化配置 */ private initializeConfig(): void { this.config = { enableMouse: this.enableMouse, enableTouch: this.enableTouch, doubleClickTime: this.doubleClickTime, longPressTime: this.longPressTime, dragThreshold: this.dragThreshold }; } /** * 设置相机引用 */ setCamera(camera: Camera): void { this.camera = camera; } /** * 设置事件回调 */ on(eventType: InputEventType, callback: InputCallback): void { if (!this.callbacks.has(eventType)) { this.callbacks.set(eventType, []); } this.callbacks.get(eventType)!.push(callback as InputCallback); } /** * 移除事件回调 */ off(eventType: InputEventType, callback: InputCallback): void { const callbacks = this.callbacks.get(eventType); if (callbacks) { const index = callbacks.indexOf(callback as InputCallback); if (index > -1) { callbacks.splice(index, 1); } } } /** * 设置输入事件监听 */ private setupInputEvents(): void { if (this.config.enableMouse) { this.node.on('mousedown', this.onMouseDown, this); this.node.on('mouseup', this.onMouseUp, this); this.node.on('mousemove', this.onMouseMove, this); } if (this.config.enableTouch) { this.node.on('touchstart', this.onTouchStart, this); this.node.on('touchend', this.onTouchEnd, this); this.node.on('touchmove', this.onTouchMove, this); } } /** * 鼠标按下事件 */ private onMouseDown(event: EventMouse): void { const position = this.screenToWorldPosition(event.getUILocation()); const inputEvent: MouseInputEvent = { type: InputEventType.MOUSE_DOWN, position, screenPosition: { x: event.getUILocation().x, y: event.getUILocation().y }, timestamp: Date.now(), button: event.getButton() as InputButton, ctrlKey: false, // Cocos Creator 3.x 中需要通过其他方式获取 shiftKey: false, altKey: false }; this.inputState.isMouseDown = true; this.inputState.dragStartPosition = position; // 检测双击 const timeSinceLastClick = inputEvent.timestamp - this.inputState.lastClickTime; if (timeSinceLastClick < this.config.doubleClickTime) { const distance = this.calculateDistance(position, this.inputState.lastClickPosition); if (distance < this.config.dragThreshold) { this.emitDoubleClickEvent(inputEvent); } } this.inputState.lastClickTime = inputEvent.timestamp; this.inputState.lastClickPosition = position; this.emitEvent(inputEvent); } /** * 鼠标释放事件 */ private onMouseUp(event: EventMouse): void { const position = this.screenToWorldPosition(event.getUILocation()); const inputEvent: MouseInputEvent = { type: InputEventType.MOUSE_UP, position, screenPosition: { x: event.getUILocation().x, y: event.getUILocation().y }, timestamp: Date.now(), button: event.getButton() as InputButton, ctrlKey: false, shiftKey: false, altKey: false }; this.inputState.isMouseDown = false; this.inputState.isDragging = false; this.emitEvent(inputEvent); } /** * 鼠标移动事件 */ private onMouseMove(event: EventMouse): void { const position = this.screenToWorldPosition(event.getUILocation()); const inputEvent: MouseInputEvent = { type: InputEventType.MOUSE_MOVE, position, screenPosition: { x: event.getUILocation().x, y: event.getUILocation().y }, timestamp: Date.now(), button: event.getButton() as InputButton, ctrlKey: false, shiftKey: false, altKey: false }; // 检测拖拽开始 if (this.inputState.isMouseDown && !this.inputState.isDragging) { const distance = this.calculateDistance(position, this.inputState.dragStartPosition); if (distance > this.config.dragThreshold) { this.inputState.isDragging = true; this.emitDragStartEvent(inputEvent); } } this.emitEvent(inputEvent); } /** * 触摸开始事件 */ private onTouchStart(event: EventTouch): void { const touches = event.getAllTouches(); for (const touch of touches) { const position = this.screenToWorldPosition(touch.getUILocation()); const touchEvent: TouchInputEvent = { type: InputEventType.TOUCH_START, position, screenPosition: { x: touch.getUILocation().x, y: touch.getUILocation().y }, timestamp: Date.now(), touchId: touch.getID(), force: undefined // Cocos Creator 3.x 中 getForce 方法可能不可用 }; this.inputState.activeTouches.set(touch.getID(), touchEvent); this.inputState.isTouchActive = true; this.emitEvent(touchEvent); } } /** * 触摸结束事件 */ private onTouchEnd(event: EventTouch): void { const touches = event.getAllTouches(); for (const touch of touches) { const position = this.screenToWorldPosition(touch.getUILocation()); const touchEvent: TouchInputEvent = { type: InputEventType.TOUCH_END, position, screenPosition: { x: touch.getUILocation().x, y: touch.getUILocation().y }, timestamp: Date.now(), touchId: touch.getID(), force: undefined // Cocos Creator 3.x 中 getForce 方法可能不可用 }; this.inputState.activeTouches.delete(touch.getID()); if (this.inputState.activeTouches.size === 0) { this.inputState.isTouchActive = false; } this.emitEvent(touchEvent); } } /** * 触摸移动事件 */ private onTouchMove(event: EventTouch): void { const touches = event.getAllTouches(); for (const touch of touches) { const position = this.screenToWorldPosition(touch.getUILocation()); const touchEvent: TouchInputEvent = { type: InputEventType.TOUCH_MOVE, position, screenPosition: { x: touch.getUILocation().x, y: touch.getUILocation().y }, timestamp: Date.now(), touchId: touch.getID(), force: undefined // Cocos Creator 3.x 中 getForce 方法可能不可用 }; this.emitEvent(touchEvent); } } /** * 屏幕坐标转世界坐标 */ private screenToWorldPosition(screenPos: Vec2): Vector2 { if (!this.camera) { // 如果没有相机,使用简单的坐标转换 return { x: (screenPos.x - 400) / 100, // 假设屏幕中心为 (400, 300),缩放 100倍 y: (300 - screenPos.y) / 100 }; } // 使用相机进行坐标转换 - 转换为 Vec3 const screenPos3 = new Vec3(screenPos.x, screenPos.y, 0); const worldPos = this.camera.screenToWorld(screenPos3); return { x: worldPos.x / 100, // 转换为物理世界坐标 y: worldPos.y / 100 }; } /** * 计算两点距离 */ private calculateDistance(pos1: Vector2, pos2: Vector2): number { const dx = pos1.x - pos2.x; const dy = pos1.y - pos2.y; return Math.sqrt(dx * dx + dy * dy); } /** * 发射事件 */ private emitEvent(event: BaseInputEvent): void { const callbacks = this.callbacks.get(event.type); if (callbacks) { callbacks.forEach(callback => { try { callback(event); } catch (error) { console.error(`Error in input callback for ${event.type}:`, error); } }); } // 同时通过事件总线发射 this.eventBus.emit(event.type, event); } /** * 发射双击事件 */ private emitDoubleClickEvent(event: MouseInputEvent): void { this.eventBus.emit('double_click', event); } /** * 发射拖拽开始事件 */ private emitDragStartEvent(event: MouseInputEvent): void { this.eventBus.emit('drag_start', event); } /** * 获取当前输入状态 */ getInputState(): InputState { return { ...this.inputState }; } /** * 获取事件总线 */ getEventBus(): EventBus { return this.eventBus; } /** * 清理资源 */ onDestroy(): void { // 移除事件监听 this.node.off('mousedown', this.onMouseDown, this); this.node.off('mouseup', this.onMouseUp, this); this.node.off('mousemove', this.onMouseMove, this); this.node.off('touchstart', this.onTouchStart, this); this.node.off('touchend', this.onTouchEnd, this); this.node.off('touchmove', this.onTouchMove, this); // 清理回调 this.callbacks.clear(); // 清理事件总线 this.eventBus.clear(); console.log('InputManager destroyed'); } }