Adds level prefabs, theme assets, audio, extensions, and deployment scripts for the Unity WebGL migration. Co-authored-by: Cursor <cursoragent@cursor.com>
137 lines
4.7 KiB
TypeScript
137 lines
4.7 KiB
TypeScript
import {
|
||
_decorator, Camera, Component, EventMouse, EventTouch, Input, input, Vec2, view,
|
||
} from 'cc';
|
||
import { CAMERA_ORTHO_HALF, DESIGN_WIDTH } from '../core/GridConstants';
|
||
import { getEmbeddedOrthoHalf } from '../core/EmbeddedView';
|
||
|
||
const { ccclass } = _decorator;
|
||
|
||
/** 对齐 Unity ViewController:Orthographic 缩放与拖拽 */
|
||
@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 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);
|
||
}
|
||
|
||
zoomOut() {
|
||
const cam = this.camera;
|
||
if (!cam || cam.orthoHeight >= this.maxOrtho) return;
|
||
cam.orthoHeight = Math.min(this.maxOrtho, cam.orthoHeight + this.zoomSpeed);
|
||
}
|
||
|
||
resetZoom() {
|
||
const cam = this.camera;
|
||
if (!cam) return;
|
||
cam.orthoHeight = getEmbeddedOrthoHalf();
|
||
cam.node.setPosition(0, 0, cam.node.position.z);
|
||
}
|
||
|
||
private onTouchStart(e: EventTouch) {
|
||
if (this.isPointerOnUI(e.getLocation())) return;
|
||
this.dragging = true;
|
||
e.getLocation(this.dragOrigin);
|
||
}
|
||
|
||
private onTouchEnd() {
|
||
this.dragging = false;
|
||
}
|
||
|
||
private onTouchMove(e: EventTouch) {
|
||
if (!this.dragging) return;
|
||
const cur = new Vec2();
|
||
e.getLocation(cur);
|
||
this.applyDrag(cur);
|
||
e.getLocation(this.dragOrigin);
|
||
}
|
||
|
||
private onMouseDown(e: EventMouse) {
|
||
if (this.isPointerOnUI(new Vec2(e.getLocationX(), e.getLocationY()))) return;
|
||
this.dragging = true;
|
||
this.dragOrigin.set(e.getLocationX(), e.getLocationY());
|
||
}
|
||
|
||
private onMouseUp() {
|
||
this.dragging = false;
|
||
}
|
||
|
||
private onMouseMove(e: EventMouse) {
|
||
if (!this.dragging) return;
|
||
this.applyDrag(new Vec2(e.getLocationX(), e.getLocationY()));
|
||
this.dragOrigin.set(e.getLocationX(), e.getLocationY());
|
||
}
|
||
|
||
private applyDrag(cur: Vec2) {
|
||
if (!this.camera) return;
|
||
const delta = cur.subtract(this.dragOrigin);
|
||
|
||
const ortho = this.camera.orthoHeight;
|
||
const { width, height } = view.getVisibleSize();
|
||
const worldPerPixelX = (2 * ortho * (width / height)) / width;
|
||
const worldPerPixelY = (2 * ortho) / height;
|
||
|
||
const pos = this.camera.node.position;
|
||
let nx = pos.x - delta.x * worldPerPixelX;
|
||
let ny = pos.y - delta.y * 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);
|
||
}
|
||
|
||
/** 右侧 UIMain 区域不拖拽镜头(与 UIMain 边距一致) */
|
||
private isPointerOnUI(loc: Vec2): boolean {
|
||
const vis = view.getVisibleSize();
|
||
const margin = Math.max(96, (DESIGN_WIDTH * 0.5) * 0.14);
|
||
const right = vis.width > DESIGN_WIDTH
|
||
? DESIGN_WIDTH * 0.5
|
||
: vis.width * 0.5;
|
||
return loc.x >= right - margin;
|
||
}
|
||
}
|