在游戏星际拓荒(outer wilds)中有一个很有趣的设定,当玩家不观察到石头时石头会在不同的平台上随机移动,而当玩家观察时石头会静止不动。
判断物体是否被观察到
做出类似的效果并不难,关键在于怎么判断一个物体是否被玩家所观测到,我们可以采用:
bool IsInView(Vector3 position)
{
Vector3 screenPoint = mainCamera.WorldToViewportPoint(position);
return screenPoint.z > 0 && screenPoint.x > 0 && screenPoint.x < 1 && screenPoint.y > 0 && screenPoint.y < 1;
}
WorldToViewportPoint是Unity 中Camera类的一种方法,用于将世界坐标转换为视口坐标。视口坐标是一种标准化的坐标系,范围从 (0,0) 到 (1,1),表示相机视图中的位置。
具体来说,这个方法的作用是将一个世界坐标系中的点(通常是 3D 坐标)转换为基于屏幕视口的 2D 坐标。视口坐标系的 (0,0) 位置对应屏幕的左下角,(1,1) 位置对应屏幕的右上角。
当物体处于 (0,0) 到 (1,1)的范围即表示我们观察到物体
让物体在不同平台间移动
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class test : MonoBehaviour
{
public Camera mainCamera;
public Transform[] positions; // 三个位置
public Transform objectToMove; // 要移动的物体
// Start is called before the first frame update
void Start()
{
}
bool IsInView(Vector3 position)
{
Vector3 screenPoint = mainCamera.WorldToViewportPoint(position);
return screenPoint.z > 0 && screenPoint.x > 0 && screenPoint.x < 1 && screenPoint.y > 0 && screenPoint.y < 1;
}
void MoveObjectToRandomPosition()
{
// 获取所有不在视线内的位置
var outOfViewPositions = new List<Transform>();
foreach (var pos in positions)
{
if (!IsInView(pos.position))
{
outOfViewPositions.Add(pos);
}
}
// 如果有不在视线内的位置,则随机选择一个进行移动
if (outOfViewPositions.Count > 0)
{
int randomIndex = Random.Range(0, outOfViewPositions.Count);
objectToMove.position = outOfViewPositions[randomIndex].position;
}
}
// Update is called once per frame
void Update()
{
if (!IsInView(objectToMove.position))
{
MoveObjectToRandomPosition();
}
}
}
添加三个位置到transform的列表,并生成一个随机数来实现物体的移动就行。