Files
cocos_zhuzhan/assets/scripts/controller/ViewController.ts

164 lines
5.8 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
_decorator, Camera, Component, EventMouse, EventTouch, Input, input, sys, Vec2, view,
} from 'cc';
import { CAMERA_ORTHO_HALF, DESIGN_WIDTH } from '../core/GridConstants';
import { getEmbeddedOrthoHalf } from '../core/EmbeddedView';
const { ccclass } = _decorator;
/** 与 UIMain 相同165×165 按钮 + 右侧 20px 边距,按 2560 设计宽缩到 960 */
const HUD_BTN = Math.round(165 * DESIGN_WIDTH / 2560);
const HUD_PAD_RIGHT = Math.round(20 * DESIGN_WIDTH / 2560);
const HUD_RIGHT_SLACK = 8;
function markMapViewDirty(): void {
(globalThis as { __tfrhPreserveMapView?: boolean }).__tfrhPreserveMapView = true;
}
function clearMapViewDirty(): void {
(globalThis as { __tfrhPreserveMapView?: boolean }).__tfrhPreserveMapView = false;
}
/** 对齐 Unity ViewControllerOrthographic 缩放与拖拽 */
@ccclass('ViewController')
export class ViewController extends Component {
static instance: ViewController | null = null;
/** Unity zoomSpeed=2 → 世界半高步进 200 */
zoomSpeed = 200;
/** Unity minZoom=3, maxZoom=10×100 世界单位) */
minOrtho = 300;
maxOrtho = 1000;
private camera: Camera | null = null;
private dragOrigin = new Vec2();
private readonly uiLoc = new Vec2();
private dragging = false;
onLoad() {
if (ViewController.instance && ViewController.instance !== this) {
this.destroy();
return;
}
ViewController.instance = this;
this.camera = this.getComponent(Camera);
if (this.camera && this.camera.orthoHeight <= 0) {
this.camera.orthoHeight = CAMERA_ORTHO_HALF;
}
input.on(Input.EventType.TOUCH_START, this.onTouchStart, this);
input.on(Input.EventType.TOUCH_MOVE, this.onTouchMove, this);
input.on(Input.EventType.TOUCH_END, this.onTouchEnd, this);
input.on(Input.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
input.on(Input.EventType.MOUSE_DOWN, this.onMouseDown, this);
input.on(Input.EventType.MOUSE_MOVE, this.onMouseMove, this);
input.on(Input.EventType.MOUSE_UP, this.onMouseUp, this);
}
onDestroy() {
if (ViewController.instance === this) ViewController.instance = null;
input.off(Input.EventType.TOUCH_START, this.onTouchStart, this);
input.off(Input.EventType.TOUCH_MOVE, this.onTouchMove, this);
input.off(Input.EventType.TOUCH_END, this.onTouchEnd, this);
input.off(Input.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
input.off(Input.EventType.MOUSE_DOWN, this.onMouseDown, this);
input.off(Input.EventType.MOUSE_MOVE, this.onMouseMove, this);
input.off(Input.EventType.MOUSE_UP, this.onMouseUp, this);
}
zoomIn() {
const cam = this.camera;
if (!cam || cam.orthoHeight <= this.minOrtho) return;
cam.orthoHeight = Math.max(this.minOrtho, cam.orthoHeight - this.zoomSpeed);
markMapViewDirty();
}
zoomOut() {
const cam = this.camera;
if (!cam || cam.orthoHeight >= this.maxOrtho) return;
cam.orthoHeight = Math.min(this.maxOrtho, cam.orthoHeight + this.zoomSpeed);
markMapViewDirty();
}
resetZoom() {
const cam = this.camera;
if (!cam) return;
cam.orthoHeight = getEmbeddedOrthoHalf();
cam.node.setPosition(0, 0, cam.node.position.z);
clearMapViewDirty();
}
private usingMouseInput(): boolean {
return !sys.isMobile && sys.hasFeature(sys.Feature.EVENT_MOUSE);
}
private onTouchStart(e: EventTouch) {
// 桌面端鼠标会同时模拟 touch只走 MOUSE_* 以免双倍位移、并过滤非左键
if (this.usingMouseInput()) return;
e.getUILocation(this.uiLoc);
if (this.isPointerOnUI(this.uiLoc)) return;
this.dragging = true;
this.dragOrigin.set(this.uiLoc);
}
private onTouchEnd() {
if (this.usingMouseInput()) return;
this.dragging = false;
}
private onTouchMove(e: EventTouch) {
if (this.usingMouseInput() || !this.dragging) return;
e.getUILocation(this.uiLoc);
this.applyDrag(this.uiLoc);
this.dragOrigin.set(this.uiLoc);
}
private onMouseDown(e: EventMouse) {
if (e.getButton() !== EventMouse.BUTTON_LEFT) return;
e.getUILocation(this.uiLoc);
if (this.isPointerOnUI(this.uiLoc)) return;
this.dragging = true;
this.dragOrigin.set(this.uiLoc);
}
private onMouseUp() {
this.dragging = false;
}
private onMouseMove(e: EventMouse) {
if (!this.dragging) return;
e.getUILocation(this.uiLoc);
this.applyDrag(this.uiLoc);
this.dragOrigin.set(this.uiLoc);
}
private applyDrag(cur: Vec2) {
if (!this.camera) return;
const dx = cur.x - this.dragOrigin.x;
const dy = cur.y - this.dragOrigin.y;
const ortho = this.camera.orthoHeight;
const { width, height } = view.getVisibleSize();
if (width <= 0 || height <= 0) return;
const worldPerPixelX = (2 * ortho * (width / height)) / width;
const worldPerPixelY = (2 * ortho) / height;
const pos = this.camera.node.position;
let nx = pos.x - dx * worldPerPixelX;
let ny = pos.y - dy * worldPerPixelY;
const limit = ortho - 20;
if (Math.abs(nx) > limit) nx = pos.x;
if (Math.abs(ny) > limit) ny = pos.y;
this.camera.node.setPosition(nx, ny, pos.z);
markMapViewDirty();
}
/** 仅拦截右侧 HUD 按钮列;坐标为 UI 空间(原点左下,与 visibleSize 一致) */
private isPointerOnUI(loc: Vec2): boolean {
const vis = view.getVisibleSize();
const right = vis.width > 0 ? vis.width : DESIGN_WIDTH;
return loc.x >= right - HUD_BTN - HUD_PAD_RIGHT - HUD_RIGHT_SLACK;
}
}