消消方块阵换皮表情
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

151 lines
5.2 KiB

1 week ago
import { _decorator, Component, EventTouch, find, Node, UITransform, v3, Vec3 } from 'cc';
import { MoveDirection, SuDacePlayer } from './SuDacePlayer';
import { SuDace } from './SuDace';
const { ccclass, property } = _decorator;
/**
* touchArea SuDacePlayer.setMoveInput
* stickRoot / knob
*/
@ccclass('SuDaceJoystick')
export class SuDaceJoystick extends Component {
@property(SuDacePlayer)
player: SuDacePlayer = null;
/** 固定可触摸区域(全屏透明层或左下角区域等) */
@property(Node)
touchArea: Node = null;
/** 按下时显示在触点处的摇杆底板节点(可选) */
@property(Node)
stickRoot: Node = null;
/** 相对 stickRoot 中心拖动的摇杆头(可选) */
@property(Node)
knob: Node = null;
@property({ tooltip: '摇杆头最大偏移(与 UI 像素一致,用于显示)' })
maxKnobOffset = 80;
@property({ tooltip: '死区半径(相对本次按下中心),低于此视为无方向输入' })
deadRadius = 24;
private readonly _centerUi = v3();
private readonly _tmpLocal = v3();
private _trackingId = -1;
protected onEnable(): void {
this.resolvePlayerRef();
if (this.stickRoot) {
this.stickRoot.active = false;
}
const surface = this.touchArea ?? this.node;
surface.on(Node.EventType.TOUCH_START, this.onTouchStart, this);
surface.on(Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
surface.on(Node.EventType.TOUCH_END, this.onTouchEnd, this);
surface.on(Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
}
protected onDisable(): void {
const surface = this.touchArea ?? this.node;
surface.off(Node.EventType.TOUCH_START, this.onTouchStart, this);
surface.off(Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
surface.off(Node.EventType.TOUCH_END, this.onTouchEnd, this);
surface.off(Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
this.endSession();
}
private onTouchStart(event: EventTouch): void {
if (this._trackingId !== -1) return;
if (!this.player?.isValid) return;
const tid = event.getID();
this._trackingId = tid !== null && tid !== undefined ? tid : 0;
const loc = event.getUILocation();
this._centerUi.set(loc.x, loc.y, 0);
if (this.stickRoot) {
this.stickRoot.active = true;
this.placeNodeAtUi(this.stickRoot, this._centerUi);
}
this.updateKnob(0, 0);
this.player.setMoveInput(MoveDirection.NONE);
}
private onTouchMove(event: EventTouch): void {
if (!this.isOurTouch(event) || !this.player?.isValid) return;
const loc = event.getUILocation();
const dx = loc.x - this._centerUi.x;
const dy = loc.y - this._centerUi.y;
this.updateKnob(dx, dy);
this.player.setMoveInput(this.quantizeFourWay(dx, dy));
}
private onTouchEnd(event: EventTouch): void {
if (!this.isOurTouch(event)) return;
this.endSession();
}
private isOurTouch(event: EventTouch): boolean {
if (this._trackingId === -1) return false;
const id = event.getID();
const eid = id !== null && id !== undefined ? id : 0;
return eid === this._trackingId;
}
private endSession(): void {
this._trackingId = -1;
if (this.stickRoot?.isValid) {
this.stickRoot.active = false;
}
if (this.player?.isValid) {
this.player.setMoveInput(MoveDirection.NONE);
}
}
/**
* player
*/
private resolvePlayerRef(): void {
if (this.player?.isValid) return;
this.player = SuDace.instance?.map?.player ?? find('map/player', this.node)?.getComponent(SuDacePlayer) ?? null;
}
/** 将 stickRoot 等节点放到 UI 坐标对应位置(相对其父节点 UITransform) */
private placeNodeAtUi(target: Node, uiWorld: Readonly<Vec3>): void {
const parent = target.parent;
const ui = parent?.getComponent(UITransform);
if (!ui) return;
ui.convertToNodeSpaceAR(uiWorld, this._tmpLocal);
target.setPosition(this._tmpLocal);
}
private updateKnob(dx: number, dy: number): void {
if (!this.knob?.isValid) return;
const max = this.maxKnobOffset;
let kx = dx;
let ky = dy;
const len = Math.sqrt(dx * dx + dy * dy);
if (len > max && len > 1e-6) {
const s = max / len;
kx *= s;
ky *= s;
}
this.knob.setPosition(kx, ky, 0);
}
/** 将相对中心的偏移量量化为四方向(主轴优先) */
private quantizeFourWay(ox: number, oy: number): MoveDirection {
const r = this.deadRadius;
if (ox * ox + oy * oy < r * r) {
return MoveDirection.NONE;
}
if (Math.abs(ox) >= Math.abs(oy)) {
return ox > 0 ? MoveDirection.RIGHT : MoveDirection.LEFT;
}
return oy > 0 ? MoveDirection.UP : MoveDirection.DOWN;
}
}