cocos基础工程
This commit is contained in:
@@ -0,0 +1,385 @@
|
||||
/**
|
||||
* 输入管理器实现
|
||||
* 处理鼠标和触摸输入,转换为游戏事件
|
||||
*/
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "1a3cf9f0-ddcf-4175-9dc8-632e89c6941e",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* 输入相关类型定义
|
||||
*/
|
||||
|
||||
import { Vector2 } from '../Core/GameData';
|
||||
|
||||
/** 输入事件类型 */
|
||||
export enum InputEventType {
|
||||
MOUSE_DOWN = 'mouse_down',
|
||||
MOUSE_UP = 'mouse_up',
|
||||
MOUSE_MOVE = 'mouse_move',
|
||||
TOUCH_START = 'touch_start',
|
||||
TOUCH_END = 'touch_end',
|
||||
TOUCH_MOVE = 'touch_move'
|
||||
}
|
||||
|
||||
/** 输入按键枚举 */
|
||||
export enum InputButton {
|
||||
LEFT_MOUSE = 0,
|
||||
RIGHT_MOUSE = 1,
|
||||
MIDDLE_MOUSE = 2,
|
||||
TOUCH = 99
|
||||
}
|
||||
|
||||
/** 基础输入事件数据 */
|
||||
export interface BaseInputEvent {
|
||||
type: InputEventType;
|
||||
position: Vector2; // 世界坐标
|
||||
screenPosition: Vector2; // 屏幕坐标
|
||||
timestamp: number;
|
||||
button?: InputButton;
|
||||
}
|
||||
|
||||
/** 鼠标输入事件 */
|
||||
export interface MouseInputEvent extends BaseInputEvent {
|
||||
type: InputEventType.MOUSE_DOWN | InputEventType.MOUSE_UP | InputEventType.MOUSE_MOVE;
|
||||
button: InputButton;
|
||||
ctrlKey: boolean;
|
||||
shiftKey: boolean;
|
||||
altKey: boolean;
|
||||
}
|
||||
|
||||
/** 触摸输入事件 */
|
||||
export interface TouchInputEvent extends BaseInputEvent {
|
||||
type: InputEventType.TOUCH_START | InputEventType.TOUCH_END | InputEventType.TOUCH_MOVE;
|
||||
touchId: number;
|
||||
force?: number;
|
||||
}
|
||||
|
||||
/** 输入配置 */
|
||||
export interface InputConfig {
|
||||
enableMouse: boolean;
|
||||
enableTouch: boolean;
|
||||
doubleClickTime: number; // 双击时间间隔 (ms)
|
||||
longPressTime: number; // 长按时间 (ms)
|
||||
dragThreshold: number; // 拖拽阈值 (像素)
|
||||
}
|
||||
|
||||
/** 输入回调函数类型 */
|
||||
export type InputCallback<T extends BaseInputEvent = BaseInputEvent> = (event: T) => void;
|
||||
|
||||
/** 输入状态 */
|
||||
export interface InputState {
|
||||
isMouseDown: boolean;
|
||||
isTouchActive: boolean;
|
||||
lastClickTime: number;
|
||||
lastClickPosition: Vector2;
|
||||
isDragging: boolean;
|
||||
dragStartPosition: Vector2;
|
||||
activeTouches: Map<number, TouchInputEvent>;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "b07a5b72-454a-4fb1-8dd6-9b39141b85d8",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
Reference in New Issue
Block a user