预览效果:
技术要点:
图片已进行各概念的说明
在“collisionNodeArray”属性下,放置需要点击的星球节点,系统会自己绑定碰撞器。
也可自己提前绑定。
注意相机的属性。可以根据自己的需要调整相机的z值。只要保证星球绕着相机布局则行。
可以通过设置
这两个参数进行调节,是个经验数值。
系统本身会进行基础的上下限制。这两个参数属于额外的限制。即顶部往下,底部往上。
控制器(planet_view_controller)代码:
直接拷贝到项目的一个空代码文件即可:
- import { _decorator, Component, Node, Camera, Input, input, EventTouch, Vec2, Quat, Vec3, screen, tween, Tween, Collider, SphereCollider, geometry, PhysicsSystem, EventHandler } from "cc";
- const { ccclass, property } = _decorator;
- @ccclass("PlanetViewController")
- export class PlanetViewController extends Component {
- start() {
- this.bindInputEvent();
- this.setLimitEuler();
- this.clickManagerStart();
- }
- //浏览功能区
- @property({
- displayName: "顶部额外限制角度",
- })
- private upLimitAngle = 0;
- @property({
- displayName: "底部额外限制角度",
- })
- private downLimitAngle = 0;
- @property(Camera)
- private centerCamera!: Camera;
- private bindInputEvent() {
- 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);
- }
- private limitEdgeEuler = 0;
- private setLimitEuler() {
- this.limitEdgeEuler = (180 * Math.atan(Math.tan((this.centerCamera.fov * Math.PI) / 360) / this.centerCamera.camera.aspect)) / Math.PI;
- }
- private startLocation = new Vec2();
- private onTouchStart(e: EventTouch) {
- e.getLocation(this.startLocation);
- Tween.stopAllByTarget(this.lastRotaionSpeed);
- }
- private lastRotaionSpeed = new Vec2();
- private onTouchMove(e: EventTouch) {
- e.getDelta(this.lastRotaionSpeed);
- this.rotateCenterCamera(this.lastRotaionSpeed);
- }
- private clickLocation = new Vec2();
- private onTouchEnd(e: EventTouch) {
- e.getLocation(this.clickLocation);
- const dis = Vec2.squaredDistance(this.startLocation, this.clickLocation);
- if (dis <= 0.1) {
- this.node.emit("click", this.clickLocation);
- console.log("click");
- return;
- }
- tween(this.lastRotaionSpeed)
- .to(
- 0.5,
- {
- x: 0,
- y: 0,
- },
- {
- easing: "sineOut",
- onUpdate: () => {
- this.rotateCenterCamera(this.lastRotaionSpeed);
- },
- }
- )
- .start();
- }
-
- private curRotateResultEuler = new Vec3();
- private rotateQuat = new Quat();
- private lastRotationQuat = new Quat();
- private rotateCenterCamera(volume: Vec2) {
- Quat.fromAxisAngle(this.rotateQuat, Vec3.UP, (volume.x * 0.785) / screen.windowSize.width);
- Quat.rotateX(this.rotateQuat, this.rotateQuat, (-volume.y * 0.785) / screen.windowSize.height);
- this.lastRotationQuat.set(this.centerCamera.node.rotation);
- Quat.multiply(this.lastRotationQuat, this.lastRotationQuat, this.rotateQuat);
- this.lastRotationQuat.getEulerAngles(this.curRotateResultEuler);
- this.centerCamera.node.rotate(this.rotateQuat);
- const isOverUp = this.curRotateResultEuler.x < -this.limitEdgeEuler + this.upLimitAngle;
- const isOverDown = this.curRotateResultEuler.x > this.limitEdgeEuler - this.downLimitAngle;
- if (isOverUp || isOverDown) {
- this.lastRotationQuat.set(this.centerCamera.node.rotation);
- this.lastRotationQuat.getEulerAngles(this.curRotateResultEuler);
- const { y, z } = this.curRotateResultEuler;
- const x = isOverUp ? -this.limitEdgeEuler + this.upLimitAngle : this.limitEdgeEuler - this.downLimitAngle;
- this.centerCamera.node.setRotationFromEuler(x, y, z);
- }
- const { x, y } = this.centerCamera.node.eulerAngles;
- this.centerCamera.node.setRotationFromEuler(x, y, 0);
- }
- //点击检测功能区
- @property([EventHandler])
- private collisionEventHandlerArray: EventHandler[] = [];
- @property([Node])
- private collisionNodeArray: Node[] = [];
- private clickManagerStart() {
- this.setCollisionNodeCollider();
- this.bindClickEvent();
- }
- private setCollisionNodeCollider() {
- this.collisionNodeArray.forEach((node) => {
- let collider = node.getComponent(Collider);
- if (!collider) {
- collider = node.addComponent(SphereCollider);
- }
- collider.isTrigger = true;
- });
- }
- private clickRay = new geometry.Ray();
- private bindClickEvent() {
- this.node.on("click", ({ x, y }: Vec2) => {
- if (this.collisionNodeArray.length === 0) return;
- this.centerCamera.screenPointToRay(x, y, this.clickRay);
- PhysicsSystem.instance.raycast(this.clickRay);
- if (PhysicsSystem.instance.raycastResults.length) {
- const firstColliderNode = PhysicsSystem.instance.raycastResults[0].collider.node;
- this.collisionEventHandlerArray.forEach((handler) => handler.emit([firstColliderNode]));
- }
- });
- }
- }
-