屏幕与世界坐标
相机定义 3D 世界与 2D 屏幕之间的映射,CameraComponent 提供双向转换。这是鼠标拾取、在光标下放置对象以及将 2D UI 锚定到 3D 对象的基础。同一段代码适用于所有工作流,因为这些都是在脚本中调用的运行时 API。
屏幕坐标转世界坐标
screenToWorld(x, y, z) 将 2D 屏幕位置转换为 3D 世界位置。由于单个屏幕点对应世界空间中的一整条射线,还需要传入 z,指定所求点与相机的距离。
常见做法是从鼠标光标向场景发射射线,例如与物理对象或几何体求交:
const from = camera.camera.screenToWorld(event.x, event.y, camera.camera.nearClip);
const to = camera.camera.screenToWorld(event.x, event.y, camera.camera.farClip);
// e.g. raycast against the physics world
const result = app.systems.rigidbody.raycastFirst(from, to);
if (result) {
console.log(`Hit: ${result.entity.name}`);
}
也可以在光标下方的固定距离放置对象:
const pos = camera.camera.screenToWorld(event.x, event.y, 10); // 10 units from the camera
entity.setPosition(pos);
世界坐标转屏幕坐标
worldToScreen(worldCoord) 执行相反操作,将 3D 世界位置投影为 2D 屏幕坐标。它适用于在场景对象上方放置 HTML 元素或 2D UI,例如名称标签、血条和路标:
const screenPos = camera.camera.worldToScreen(entity.getPosition());
// Position an absolutely-positioned HTML element over the entity, inside a
// container that covers the canvas
htmlElement.style.left = `${screenPos.x}px`;
htmlElement.style.top = `${screenPos.y}px`;
无论设备像素比是多少,x、y 分量都是相对于画布左上角的 CSS 像素,因此 HTML 可以直接使用。返回值的 z 分量保存该点的深度。对于透视相机,负 z 表示该点位于相机后方,显示元素前应先检查。用同样方式放置画布内 UI,参见角色上方的标签。
拾取对象
要精确选择光标下的网格,仅靠射线检测不一定足够:它需要物理碰撞体,而且忽略逐像素细节。引擎的 Picker 通过渲染场景,精确确定屏幕坐标对应的网格实例。参见场景拾取器。