Files
shooter-demo/client-cocos/assets/scripts/Modules/Pinball/Input/InputManager.ts
2025-11-28 18:10:10 +08:00

385 lines
12 KiB
TypeScript

/**
* 输入管理器实现
* 处理鼠标和触摸输入,转换为游戏事件
*/
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<InputEventType, InputCallback[]> = 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<T extends BaseInputEvent>(eventType: InputEventType, callback: InputCallback<T>): void {
if (!this.callbacks.has(eventType)) {
this.callbacks.set(eventType, []);
}
this.callbacks.get(eventType)!.push(callback as InputCallback);
}
/**
* 移除事件回调
*/
off<T extends BaseInputEvent>(eventType: InputEventType, callback: InputCallback<T>): 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');
}
}