SteamVR2.0以后,玩家想要发生位移就要做几个设置:①Player预制体②Teleporting预制体③在地板上添加TeleportArea脚本,设置地板的layer,在Teleporting的预制体上把Teleport脚本的Trace Layer Mask设置为地板对应的layer。鉴于SteamVR的机制,在按下位移的Action后,脚本会自动给地板的MeshRender中的Mesh更换材质,并且显示出更换材质后的地板。这个机制在用拥有MeshRender的地板来说,没有什么问题,大不了将地板复制一份,然后设置复制后的地板作为Teleport Area 就是了。
今天在开发的时候就遇到了一个问题,地编给了个场景,场景是用Unity的地形系统搭建的。按照之前的做法,要想使用位移,就必须在地形下新建一个Plane,然后把Plane拉到和地形一样大,然后把TeleportArea附加到Plane上。但是这个操作会出现,当地形出现高低起伏的地方的时候,就会出现穿到地形以下的情况。(因为位移根据的碰撞体是我后面加的Plane,不是地形的Terrain Collider)如果给每个位移区域单独弄一块Plane做位移区域就太麻烦了。为了解决这个问题,我就稍微修改了SteamVR的TeleportArea脚本,让它像VRTK一样直接根据Trace Layer Mask对应的层上面的碰撞体来做位移。亲测能用。代码附上:
/****************************************************
文件:CustomTeleportArea.cs
作者:Paul 邮箱: 794451358@qq.com
日期:2020/11/19 11:41:29
功能:重写位移区域脚本,让其适应地形碰撞体
*****************************************************/
using UnityEngine;
using System.Collections.Generic;
using System.Collections;
using Valve.VR.InteractionSystem;
namespace DaschowStreet
{
public class CustomTeleportArea : TeleportMarkerBase
{
[Tooltip("如果勾选就是使用SteamVR本来的位移机制")]
public bool IsUseSteamVRTeleport = false;
//Public properties
public Bounds meshBounds { get; private set; }
//Private data
private MeshRenderer areaMesh;
private int tintColorId = 0;
private Color visibleTintColor = Color.clear;
private Color highlightedTintColor = Color.clear;
private Color lockedTintColor = Color.clear;
private bool highlighted = false;
private void Awake()
{
if (IsUseSteamVRTeleport)
{
areaMesh = GetComponent<MeshRenderer>();
tintColorId = Shader.PropertyToID("_TintColor");
CalculateBounds();
}
}
private void Start()
{
if (IsUseSteamVRTeleport)
{
visibleTintColor = Teleport.instance.areaVisibleMaterial.GetColor(tintColorId);
highlightedTintColor = Teleport.instance.areaHighlightedMaterial.GetColor(tintColorId);
lockedTintColor = Teleport.instance.areaLockedMaterial.GetColor(tintColorId);
}
}
public override void Highlight(bool highlight)
{
if (IsUseSteamVRTeleport)
{
if (!locked)
{
highlighted = highlight;
if (highlight)
{
areaMesh.material = Teleport.instance.areaHighlightedMaterial;
}
else
{
areaMesh.material = Teleport.instance.areaVisibleMaterial;
}
}
}
}
public override void SetAlpha(float tintAlpha, float alphaPercent)
{
if (IsUseSteamVRTeleport)
{
Color tintedColor = GetTintColor();
tintedColor.a *= alphaPercent;
areaMesh.material.SetColor(tintColorId, tintedColor);
}
}
public override bool ShouldActivate(Vector3 playerPosition)
{
return true;
}
public override bool ShouldMovePlayer()
{
return true;
}
public override void UpdateVisuals()
{
if (IsUseSteamVRTeleport)
{
if (locked)
{
areaMesh.material = Teleport.instance.areaLockedMaterial;
}
else
{
areaMesh.material = Teleport.instance.areaVisibleMaterial;
}
}
}
private bool CalculateBounds()
{
MeshFilter meshFilter = GetComponent<MeshFilter>();
if (meshFilter == null)
{
return false;
}
Mesh mesh = meshFilter.sharedMesh;
if (mesh == null)
{
return false;
}
meshBounds = mesh.bounds;
return true;
}
private Color GetTintColor()
{
if (locked)
{
return lockedTintColor;
}
else
{
if (highlighted)
{
return highlightedTintColor;
}
else
{
return visibleTintColor;
}
}
}
}
}