cocos creator实现浏览星球的功能,附源码

news2025/1/23 7:03:56

预览效果:

 

技术要点:

  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]));
      }
    });
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/40412.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于SSM的学籍证明打印系统设计与实现。

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…

QT下TCP协议实现数据网络传输

QT开发框架以其跨平台的优势&#xff0c;在全世界IT界如雷贯耳。其封装了功能齐全的各种类&#xff0c;大大的提高了开发者的效率。本篇内容将介绍如何使用QT 6.4.1框架开发服务器和客户端程序&#xff0c;让两端能够首发消息&#xff0c;服务端往客户端发送文件&#xff08;客…

Spark在Yarn集群的两种提交模式

目录 一.Yarn Client(yarn的客户端模式) 二.Yarn Cluster(yarn的集群节点模式) 三.两者的差异 一.Yarn Client(yarn的客户端模式) 第一步&#xff1a;Driver端会在提交的本地机上运行 第二步&#xff1a;Driver端启动后会跟ResourceManager(RM)进行通信,申请启动一个Applic…

Linux安装Samba服务,基于Fedora

Linux安装Samba服务&#xff0c;基于Fedora1 安装samba服务2 启动samba服务3 更改配置信息4 使用windows系统进行连接5 其他说明1 安装samba服务 1 关闭防火墙及关闭防火墙开机自启 [whs02fedora ~]$ &#xff1a;sudo systemctl stop firewalld.service [whs02fedora ~]$ &a…

splay树:hdu4453 Looploop

题目链接如下&#xff1a; Problem - 4453 主要是要对区间操作和这种splay树的性质比较清楚。 关于区间我们设立两个额外节点&#xff0c;用来设立最开始的左右区间。 性质方面&#xff0c;其实就是二叉搜索树的性质&#xff0c;这里的体现就是中序遍历就是顺时针访问输入数…

《统计学习方法》 第十四章 聚类方法

聚类方法 1.聚类是针对给定的样本&#xff0c;依据它们属性的相似度或距离&#xff0c;将其归并到若干个“类”或“簇”的数据分析问题。一个类是样本的一个子集。直观上&#xff0c;相似的样本聚集在同类&#xff0c;不相似的样本分散在不同类。 2.距离或相似度度量在聚类中…

压力传感器

压力传感器 压力传感器是最常用的一种传感器&#xff0c;其应用范围有各种工业互通环境&#xff0c;涉及航空&#xff0c;航天&#xff0c;军工&#xff0c;石化&#xff0c;电力等。按照不同的测试&#xff0c;压力类型可分表压传感器&#xff0c;差压传感器&#xff0c;绝压…

现代密码学导论-19-基于伪随机函数的CPA安全

目录 3.5.2 基于伪随机函数的CPA安全 基于伪随机函数的加密示意图 CONSTRUCTION 3.28 构造基于伪随机函数的CPA安全的加密方案 THEOREM 3.29 方案3.28是CPA安全的 THEOREM 3.29 的证明 3.5.2 基于伪随机函数的CPA安全 基于伪随机函数的加密示意图 CONSTRUCTION 3.28 构造…

历史中的密码

角色 发送者、接收者和窃听者 当某个人向另一个人发送信息时&#xff0c;发出信息的人称为发送者&#xff0c;而收到信息的人称为接收者&#xff0c;被发送的信息有时也统称为消息&#xff08; message )。 窃听者 Eve 并不一定是人类&#xff0c;有可能是安装在通信设备上的某…

【JVM】jvm中的方法区简介

jvm中的方法区简介一、JVM体系结构二、方法区是什么&#xff1f;三、方法区能干什么&#xff1f;四、方法区总结一、JVM体系结构 二、方法区是什么&#xff1f; 本文所讲内容在上图中处于运行时数据区内的左侧部分&#xff0c;即 Method Area&#xff08;方法区&#xff09;&a…

REHL7.6静默安装Oracle19C

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&#x1f61…

【轨迹跟踪】基于matlab拓展卡尔曼滤波时序四旋翼无人机状态跟踪【含Matlab源码 2246期】

⛄一、拓展卡尔曼滤波时序四旋翼无人机状态跟踪 卡尔曼滤波算法为获得最优估计和最小误差方差&#xff0c;将从目标模型中得到的测量值一步步递推&#xff0c;实时获取新时刻的状态估计值。 假设目标状态方程和观测方程分别为&#xff1a; 其中&#xff0c;k为离散时间&…

投入产出公开数据集:世界投入产出表(1995-2014)、全国投入产出表(1990-2018)、分省市投入产出表(1997-2017)

一、数据介绍 数据名称&#xff1a;世界、全国、各省-投入产出表 数据年份&#xff1a;世界投入产出表(1995-2014)、全国投入产出表(1990-2018)、分省市投入产出表(1997-2017) 数据来源&#xff1a;WIOD、自计算 ① 世界投入产出表&#xff08;1995-2014&#xff09; downlo…

用JSX来写Vue3,瞬间找到React 的感觉

Ⅰ. vue3 的 JSX 写法 对于熟悉react 的小伙伴, 可以通过 jsx 来 做 vue3喜欢 jsx 写法做 vue&#xff0c;代码结构更加美观&#xff0c;让我们一起来踩坑 &#x1f447; 文章目录Ⅰ. vue3 的 JSX 写法Ⅱ. JSX 安装和配置1. 通过 webpack 构建的2. 通过 vite 构建的Ⅳ. JSX 的…

Yocto buildhistory介绍

Yocto buildhistory介绍 在yocto中会频繁的编译修改镜像&#xff0c;当多人多次修改镜像的时候会导致镜像难以维护&#xff0c;我们希望能有一个类似git一样的工具能够显示每次编译的差异性修改&#xff0c;这样当我想要回退到某个日期的某个镜像时能够清晰的知道镜像内部的具…

Dockerfile文件详解

组成部分 说明 基础镜像信息 使用 FROM 关键字指定基础镜像信息&#xff0c;必须是 Dockerfile 文件的第1条指令。 维护者信息 使用 MAINTAINER 关键字指定&#xff0c;可以使用 Dockerfile 文件创建者的姓名或者电子邮件作为维护者信息。 镜像操作指令 每执行一条镜像操…

vue3+Element-plus el-select 下拉选择 多选增加全选封装组件

一、效果图&#xff08;含适用于条件查询组件中使用&#xff09; 二、参数配置 1、代码示例&#xff1a; <t-selectplaceholder"请选择工序"v-model"selectVlaue":optionSource"state.stepList"valueKey"label"change"selec…

部署SpringBoot+Vue3 项目实战,打造企业级在线办公系统

文章目录一、安装docker二、安装2.1. 安装mysql2.2. 安装MongoDB2.3. 安装Redis程序2.4. 安装RabbitMQ2.5. 在云主机上面开放端口三、部署后端项目3.1. 下载JDK镜像3.2. 部署工作流项目3.3. 部署emos-api项目四、在Docker中部署前端项目4.1. 修改前端代码4.2. 打包VUE项目4.3. …

【Hack The Box】linux练习-- time

HTB 学习笔记 【Hack The Box】linux练习-- time &#x1f525;系列专栏&#xff1a;Hack The Box &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4c6;首发时间&#xff1a;&#x1f334;2022年11月24日&#x1f334; &#x1f36d…

Android~Compose脚手架和Toast

系列文章目录 Android~Compose初探Android~Compose之自定义ViewAndroid~Compose相关概念总结Android~Compose脚手架和ToastAndroid~Compose路由Navigation和传参 文章目录系列文章目录目标脚手架基于Snackbar自定义Toast实现效果目标 熟悉Compose中脚手架使用自定义Toast样式…