Cocos Creator 3.x 3D点击事件 - 射线检测

知识点

射线检测是什么?

射线检测是非常重要的功能,常常用来判断各种情况。其本质是对一条射线和另一个形状进行 相交性判断,如下图所示。
在这里插入图片描述

射线检测怎么使用?

构造射线

首先 引入 geometry 模块,

import { geometry } from 'cc';

然后 设置起点和方向,常见方法

一、通过 起点 + 方向,ray 的构造函数或静态接口 create
 import { geometry } from 'cc';
 const { ray } = geometry;
 // 构造一条从(0,-1,0)出发,指向 Y 轴的射线
 // 前三个参数是起点,后三个参数是方向
 const outRay = new ray(0, -1, 0, 0, 1, 0);

 // 或者通过静态方法 create
 const outRay2 = ray.create(0, -1, 0, 0, 1, 0);
二、通过 起点 + 射线上的另一点,ray 的静态接口 fromPoints:
 import { geometry, Vec3 } from 'cc';
 // 构造一条从原点出发,指向 Z 轴的射线
 const outRay = new geometry.ray();
 geometry.ray.fromPoints(outRay, Vec3.ZERO, Vec3.UNIT_Z);
三、用相机构造一条从相机原点到屏幕某点发射出的射线:
 import { geometry, Camera } from 'cc';
 const { ray } = geometry;
 // 此处假设已经有 cameraCom 的引用了
 const cameraCom: Camera;
 // 获得一条途径屏幕坐标(0,0)发射出的一条射线
 const outRay = new ray();
 cameraCom.screenPointToRay(0, 0, outRay);

注意:

  • 首先需要获取一个相机组件或者相机实例的引用。
  • 相机组件和相机实例两者暴露的接口参数顺序不一样。
接口介绍

Cocos Creator 提供了一套基于物理引擎的射线检测功能。但需要注意的是,检测的对象是物理碰撞器,在场景面板上与之对应的是碰撞器组件,例如 BoxCollider

目前接口由 PhysicsSystem 提供,有以下两类:

  • raycast : 检测所有的碰撞体,返回布尔值, 表示是否检测成功。
  • raycastClosest :检测所有的碰撞体,同样返回布尔值。

参数说明:

  • worldRay:世界空间下的射线
  • mask:用于过滤的掩码,可以传入需要检测的分组
  • maxDistance:最大检测距离,目前请勿传入 Infinity 或 Number.MAX_VALUE
  • queryTrigger:是否检测触发器
获取结果

获取以上接口的检测结果,需分别通过以下方式:

  • 获取 raycastAll 的检测结果:PhysicsSystem.instance.raycastResults
  • 获取 raycastClosest 的检测结果:PhysicsSystem.instance.raycastClosestResult

**注意:**返回对象是只读并且复用的,每次调用检测接口后会更新相应结果。

结果存储的信息

信息由 PhysicsRayResult 进行存储,主要有以下信息:

  • collider:击中的碰撞器
  • distance:击中点与射线起点的距离
  • hitPoint:击中点(世界坐标系中)
  • hitNormal:击中点所处面的法线(世界坐标系中)

以上摘抄于 Cocos Creator 3.x 官方文档。

正文

效果

在这里插入图片描述

Cocos Creator 3D 可以基于物理碰撞器,来判断3D点击事件。

首先肯定要给物体添加碰撞器。
在这里插入图片描述
检测代码

import { _decorator, Component, Node, PhysicsSystem, SystemEventType, systemEvent, geometry, Camera, Touch, EventTouch } from 'cc';
const { ccclass, property } = _decorator;

@ccclass('Typescript')
export class he extends Component {
	@property({ type: Camera, tooltip: '主相机' })
	public mainCamera: Camera | null = null;

	@property({ type: Node, tooltip: '待触摸物体' })
	public node_touch_1: Node | null = null;

	private _ray: geometry.Ray = new geometry.Ray();

	start() {
		systemEvent.on(SystemEventType.TOUCH_START, this.onTouchStart, this);
	}

	onTouchStart(touch: Touch, event: EventTouch) {
		// 基于摄像机 画射线
		this.mainCamera?.screenPointToRay(event.getLocation().x, event.getLocation().y, this._ray);
		// 基于物理碰撞器的射线检测
		// 当点击 node_touch_1 时,控制台打印 “node_touch_1”
		if (PhysicsSystem.instance.raycast(this._ray)) {
			const r = PhysicsSystem.instance.raycastResults;
			for (let index = 0; index < r.length; index++) {
				const element = r[index];
				console.log('当前点击: ' + element.collider.node.uuid);
				if (element.collider.node.uuid == this.node_touch_1?.uuid) {
					console.log('node_touch_1');
				}
			}
		}
	}
}

最后愉快的拉控件吧!!!
在这里插入图片描述
正文代码,基于Cocos Creator 3.1版本,其他版本自行修改部分接口!!!

可以使用以下的算法来计算射线和平面的交点: 1.将射线起点和相交面上的点表示成向量形式,记为 P 和 Q。 2.将相交面的法线表示成向量形式,记为 N。 3.计算射线方向向量的点积和法线向量的点积,如果它们的乘积为零,则说明射线和平面平行,没有交点;否则,继续执行下一步。 4.计算射线起点到相交面的距离,记为 t = dot(Q-P, N) / dot(dir, N),其中 dir 是射线方向向量。 5.如果 t < 0,则说明射线和平面不相交;否则,计算交点坐标,记为 P + t * dir。 在 Cocos Creator 坐标系下,可以按照以下步骤实现: 1.将 P、Q 和 dir 转换为世界坐标系下的向量,可以使用 cc.v3() 函数。 2.将 N 转换为世界坐标系下的向量,并进行归一化,可以使用 cc.v3() 和 normalize() 函数。 3.计算点积和距离,可以使用 cc.v3().dot() 函数和 cc.v3().sub() 函数。 4.根据 t 计算交点坐标,可以使用 cc.v3().add() 和 cc.v3().mul() 函数。 以下是示例代码: ``` // 射线起点 P,方向 dir,相交面的点 Q 和法线 N,都是在节点的本地坐标系下表示的 let P = cc.v3(0, 0, 0); let dir = cc.v3(1, 0, 0); let Q = cc.v3(1, 1, 0); let N = cc.v3(0, 0, 1); // 将 P、Q 和 dir 转换为世界坐标系下的向量 P = node.convertToWorldSpaceAR(P); Q = node.convertToWorldSpaceAR(Q); dir = node.convertToWorldSpaceAR(dir).sub(P).normalize(); // 将 N 转换为世界坐标系下的向量,并进行归一化 N = node.convertToWorldSpaceAR(N).normalize(); // 计算点积和距离 let dotDirN = dir.dot(N); if (dotDirN == 0) { // 射线和平面平行,没有交点 } else { let t = Q.sub(P).dot(N) / dotDirN; if (t < 0) { // 射线和平面不相交 } else { let intersection = P.add(dir.mul(t)); // intersection 就是交点坐标 } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值