Raycasting (Cocos Creator)
最終更新: 2023年3月14日
概述
Raycasting是用于判定用户指定的Cubism网格是否与任意座标相交的功能。
可以获得单击/点击座标、场景中任意物体与指定网格之间的重叠检测。
参考此处了解如何使用Raycasting。
Cubism SDK for Cocos Creator中的Raycasting包括两个主要元素。
- 用于指定要判断的图形网格的组件
- 进行指定输入座标的图形网格重叠检测的组件。
1. 用于指定要判断的图形网格的组件
使用CubismRaycastable指定用于重叠检测的网格。
CubismRaycastable通过附加到置入[Prefab根]/Drawables/下的Node来使用。
与附加的Node具有相同ID的图形网格将用于重叠检测。
CubismRaycastable具有设置该网格的重叠检测精度的参数。
@ccclass('CubismRaycastable') export default class CubismRaycastable extends Component { /** The precision. */ @property({ type: Enum(CubismRaycastablePrecision), serializable: true, visible: true, readonly: false, }) public precision: CubismRaycastablePrecision = CubismRaycastablePrecision.boundingBox; }
CubismRaycastable.Precision有两个可设置的值,每个值如下。
- BoundingBox
包围网格并具有水平或垂直边的矩形用作重叠检测。
根据网格的形状,即使在网格外的座标处也会执行重叠检测,但它比Triangles具有更好的性能。 - Triangles
网格的形状被用作重叠检测的范围。
虽然性能不如BoundingBox,但可以在准确的范围内做出判断。
enum CubismRaycastablePrecision { /** Cast against bounding box. */ boundingBox, /** Cast against triangles. */ triangles, }
2. 进行指定输入座标的图形网格重叠检测的组件。
使用CubismRaycaster获得实际的重叠检测。
使用CubismRaycaster时,将其附加到Cubism的Prefab根部。
当CubismRaycaster原始化时,获取对附加到Prefab的所有CubismRaycastables的参考。
在执行过程中追加/删除重叠检测的网格时,会调用CubismRaycaster.refresh()再次获取参考。
/** Refreshes the controller. Call this method after adding and/or removing {@link CubismRaycastable}. */ private refresh(): void { const candidates = ComponentExtensionMethods.findCubismModel(this)?.drawables ?? null; if (candidates == null) { console.error('CubismRaycaster.refresh(): candidates is null.'); return; } // Find raycastable drawables. const raycastables = new Array<CubismRenderer>(); const raycastablePrecisions = new Array<CubismRaycastablePrecision>(); for (var i = 0; i < candidates.length; i++) { // Skip non-raycastables. if (candidates[i].getComponent(CubismRaycastable) == null) { continue; } const renderer = candidates[i].getComponent(CubismRenderer); console.assert(renderer); raycastables.push(renderer!); const raycastable = candidates[i].getComponent(CubismRaycastable); console.assert(raycastable); console.assert(raycastable!.precision); raycastablePrecisions.push(raycastable!.precision!); } // Cache raycastables. this.raycastables = raycastables; this.raycastablePrecisions = raycastablePrecisions; } /** Called by Cocos Creator. Makes sure cache is initialized. */ protected start(): void { // Initialize cache. this.refresh(); }
使用CubismRaycaster.raycast1()或CubismRaycaster.raycast2()从座标中获取重叠检测。
public raycast1( origin: Vector3, direction: Vector3, result: CubismRaycastHit[], maximumDistance: number = Number.POSITIVE_INFINITY ): number { return this.raycast2( geometry.Ray.create(origin.x, origin.y, origin.z, direction.x, direction.y, direction.z), result, maximumDistance ); } /** * Casts a ray. * @param ray * @param result The result of the cast. * @param maximumDistance [Optional] The maximum distance of the ray. * @returns * true in case of a hit; false otherwise. * * The numbers of drawables had hit */ public raycast2( ray: geometry.Ray, result: CubismRaycastHit[], maximumDistance: number = Number.POSITIVE_INFINITY ): number { // Cast ray against model plane. const origin = Vector3.from(ray.o); const direction = Vector3.from(ray.d); const intersectionInWorldSpace = origin.add( direction.multiplySingle(direction.z / origin.z) ); let intersectionInLocalSpace = Vector3.from( this.node.inverseTransformPoint(new math.Vec3(), intersectionInWorldSpace.toBuiltinType()) ); intersectionInLocalSpace = intersectionInLocalSpace.copyWith({ z: 0 }); const distance = intersectionInWorldSpace.magnitude(); // Return non-hits. if (distance > maximumDistance) { return 0; } // Cast against each raycastable. let hitCount = 0; console.assert(this.raycastables); const raycastables = this.raycastables!; console.assert(this.raycastablePrecisions); const raycastablePrecisions = this.raycastablePrecisions!; for (let i = 0; i < raycastables.length; i++) { const raycastable = raycastables[i]; const raycastablePrecision = raycastablePrecisions[i]; // Skip inactive raycastables. console.assert(raycastable.meshRenderer); if (!raycastable.meshRenderer!.enabled) { continue; } const bounds = raycastable.mesh.calculateBounds(); // Skip non hits (bounding box) if (!bounds.contains(intersectionInLocalSpace)) { continue; } // Do detailed hit-detection against mesh if requested. if (raycastablePrecision == CubismRaycastablePrecision.triangles) { if (!this.containsInTriangles(raycastable.mesh, intersectionInLocalSpace)) { continue; } } result[hitCount] = new CubismRaycastHit({ drawable: raycastable.getComponent(CubismDrawable), distance: distance, localPosition: intersectionInLocalSpace, worldPosition: intersectionInWorldSpace, }); ++hitCount; // Exit if result buffer is full. if (hitCount == result.length) { break; } } return hitCount; }
CubismRaycaster.raycast()在返回值中返回获得重叠检测的网格数。
此外,对于传递给参数的CubismRaycastHit[]类型的副本,设置获得重叠检测的网格信息。
result[hitCount] = new CubismRaycastHit({ drawable: raycastable.getComponent(CubismDrawable), distance: distance, localPosition: intersectionInLocalSpace, worldPosition: intersectionInWorldSpace, }); ++hitCount;
当多个网格在同一座标上重叠时,CubismRaycaster.raycast()会获得CubismRaycastHit[]类型副本的元素数量。
如果超过元素数量的网格重叠,将不会获得超出部分网格的结果。
CubismRaycastHit是包含已获得重叠检测的网格信息的类。
/** The hit {@link CubismDrawable} */ public readonly drawable: CubismDrawable | null; /** The distance the ray traveled until it hit the {@link CubismDrawable}. */ public readonly distance: number; /** The hit position local to the {@link CubismDrawable}. */ public readonly localPosition: Vector3; /** The hit position in world coordinates. */ public readonly worldPosition: Vector3;
- drawable
它是获得重叠检测的图形网格的参考。 - distance
与指定座标的距离。
传递给CubismRaycaster.Raycast()参数的origin或ray.origin与Drawable的Transform.position之间的线性距离。 - localPosition
获得重叠检测的图形网格的局部座标。 - worldPosition
获得重叠检测的图形网格的世界座标。