cocos creator实现浏览星球的功能,附源码
创始人
2024-02-20 10:28:25
0

预览效果:

 

技术要点:

  1. 主摄像机的视场轴需要设置为水平。

  1. 在场景下创建一个空节点用于挂载控制器脚本

图片已进行各概念的说明

在“collisionNodeArray”属性下,放置需要点击的星球节点,系统会自己绑定碰撞器。

也可自己提前绑定。

  1. 布局场景,星球围绕相机。参考如下:

注意相机的属性。可以根据自己的需要调整相机的z值。只要保证星球绕着相机布局则行。

  1. 关于摄像机旋转的上下限制。

可以通过设置

这两个参数进行调节,是个经验数值。

系统本身会进行基础的上下限制。这两个参数属于额外的限制。即顶部往下,底部往上。

控制器(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]));}});}
}

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【Ctfer训练计划】——(三... 作者名:Demo不是emo  主页面链接:主页传送门 创作初心ÿ...