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

预览效果:

 

技术要点:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值