一、场景
二、虚拟空间内移动传送
1、实现位置点选取
Tourism_LaserPointer
using UnityEngine;
using System.Collections;
public class Tourism_LaserPointer : MonoBehaviour
{
public bool active = true;
public Color color;
public float thickness = 0.002f;
public GameObject holder;
public GameObject pointer;
bool isActive = false;
public bool addRigidBody = false;
public Transform reference;
public event PointerEventHandler PointerIn;
public event PointerEventHandler PointerOut;
public Vector3 HitPoint;
Material currentCubeMaterial;
Transform previousContact = null;
Parabola parabola;
// Use this for initialization
void Start ()
{
holder = new GameObject();
holder.transform.parent = this.transform;
holder.transform.localPosition = Vector3.zero;
pointer = GameObject.CreatePrimitive(PrimitiveType.Cube);
pointer.transform.parent = holder.transform;
pointer.transform.localScale = new Vector3(thickness, thickness, 100f);
pointer.transform.localPosition = new Vector3(0f, 0f, 50f);
BoxCollider collider = pointer.GetComponent<BoxCollider>();
if (addRigidBody)
{
if (collider)
{
collider.isTrigger = true;
}
Rigidbody rigidBody = pointer.AddComponent<Rigidbody>();
rigidBody.isKinematic = true;
}
else
{
if(collider)
{
Object.Destroy(collider);
}
}
currentCubeMaterial = new Material(Shader.Find("Unlit/Color"));
currentCubeMaterial.SetColor("_Color", color);
pointer.GetComponent<MeshRenderer>().material = currentCubeMaterial;
parabola = GetComponent<Parabola>();
}
public virtual void OnPointerIn(PointerEventArgs e)
{
if (PointerIn != null)
PointerIn(this, e);
}
public virtual void OnPointerOut(PointerEventArgs e)
{
if (PointerOut != null)
PointerOut(this, e);
}
// Update is called once per frame
void Update ()
{
if (!isActive)
{
isActive = true;
this.transform.GetChild(0).gameObject.SetActive(true);
}
float dist = 100f;
SteamVR_TrackedController controller = GetComponent<SteamVR_TrackedController>();
Ray raycast = new Ray(transform.position, transform.forward);
RaycastHit hit;
bool bHit = Physics.Raycast(raycast, out hit);
if(previousContact && previousContact != hit.transform)
{
PointerEventArgs args = new PointerEventArgs();
if (controller != null)
{
args.controllerIndex = controller.controllerIndex;
}
args.distance = 0f;
args.flags = 0;
args.target = previousContact;
OnPointerOut(args);
previousContact = null;
}
if(bHit && previousContact != hit.transform)
{
PointerEventArgs argsIn = new PointerEventArgs();
if (controller != null)
{
argsIn.controllerIndex = controller.controllerIndex;
}
argsIn.distance = hit.distance;
argsIn.flags = 0;
argsIn.target = hit.transform;
OnPointerIn(argsIn);
previousContact = hit.transform;
}
if(!bHit)
{
previousContact = null;
currentCubeMaterial.SetColor("_Color", Color.black);
parabola.EndPosion = Vector3.zero;
}
else
{
currentCubeMaterial.SetColor("_Color", Color.red);
parabola.EndPosion = hit.point;
}
if (bHit && hit.distance < 100f)
{
dist = hit.distance;
}
if (bHit)
{
HitPoint = hit.point;
}
if (controller != null && controller.triggerPressed)
{
pointer.transform.localScale = new Vector3(thickness * 5f, thickness * 5f, dist);
}
else
{
pointer.transform.localScale = new Vector3(thickness, thickness, dist);
}
pointer.transform.localPosition = new Vector3(0f, 0f, dist/2f);
}
}
SteamVR_TrackedController
using UnityEngine;
using Valve.VR;
public struct ClickedEventArgs
{
public uint controllerIndex;
public uint flags;
public float padX, padY;
}
public delegate void ClickedEventHandler(object sender, ClickedEventArgs e);
public class SteamVR_TrackedController : MonoBehaviour
{
public uint controllerIndex;
public VRControllerState_t controllerState;
public bool triggerPressed = false;
public bool steamPressed = false;
public bool menuPressed = false;
public bool padPressed = false;
public bool padTouched = false;
public bool gripped = false;
public event ClickedEventHandler MenuButtonClicked;
public event ClickedEventHandler MenuButtonUnclicked;
public event ClickedEventHandler TriggerClicked;
public event ClickedEventHandler TriggerUnclicked;
public event ClickedEventHandler SteamClicked;
public event ClickedEventHandler PadClicked;
public event ClickedEventHandler PadUnclicked;
public event ClickedEventHandler PadTouched;
public event ClickedEventHandler PadUntouched;
public event ClickedEventHandler Gripped;
public event ClickedEventHandler Ungripped;
// Use this for initialization
void Start()
{
if (this.GetComponent<SteamVR_TrackedObject>() == null)
{
gameObject.AddComponent<SteamVR_TrackedObject>();
}
if (controllerIndex != 0)
{
this.GetComponent<SteamVR_TrackedObject>().index = (SteamVR_TrackedObject.EIndex)controllerIndex;
if (this.GetComponent<SteamVR_RenderModel>() != null)
{
this.GetComponent<SteamVR_RenderModel>().index = (SteamVR_TrackedObject.EIndex)controllerIndex;
}
}
else
{
controllerIndex = (uint) this.GetComponent<SteamVR_TrackedObject>().index;
}
}
public void SetDeviceIndex(int index)
{
this.controllerIndex = (uint) index;
}
public virtual void OnTriggerClicked(ClickedEventArgs e)
{
if (TriggerClicked != null)
TriggerClicked(this, e);
}
public virtual void OnTriggerUnclicked(ClickedEventArgs e)
{
if (TriggerUnclicked != null)
TriggerUnclicked(this, e);
}
public virtual void OnMenuClicked(ClickedEventArgs e)
{
if (MenuButtonClicked != null)
MenuButtonClicked(this, e);
}
public virtual void OnMenuUnclicked(ClickedEventArgs e)
{
if (MenuButtonUnclicked != null)
MenuButtonUnclicked(this, e);
}
public virtual void OnSteamClicked(ClickedEventArgs e)
{
if (SteamClicked != null)
SteamClicked(this, e);
}
public virtual void OnPadClicked(ClickedEventArgs e)
{
if (PadClicked != null)
PadClicked(this, e);
}
public virtual void OnPadUnclicked(ClickedEventArgs e)
{
if (PadUnclicked != null)
PadUnclicked(this, e);
}
public virtual void OnPadTouched(ClickedEventArgs e)
{
if (PadTouched != null)
PadTouched(this, e);
}
public virtual void OnPadUntouched(ClickedEventArgs e)
{
if (PadUntouched != null)
PadUntouched(this, e);
}
public virtual void OnGripped(ClickedEventArgs e)
{
if (Gripped != null)
Gripped(this, e);
}
public virtual void OnUngripped(ClickedEventArgs e)
{
if (Ungripped != null)
Ungripped(this, e);
}
// Update is called once per frame
void Update()
{
var system = OpenVR.System;
if (system != null && system.GetControllerState(controllerIndex, ref controllerState))
{
ulong trigger = controllerState.ulButtonPressed & (1UL << ((int)EVRButtonId.k_EButton_SteamVR_Trigger));
if (trigger > 0L && !triggerPressed)
{
triggerPressed = true;
ClickedEventArgs e;
e.controllerIndex = controllerIndex;
e.flags = (uint)controllerState.ulButtonPressed;
e.padX = controllerState.rAxis0.x;
e.padY = controllerState.rAxis0.y;
OnTriggerClicked(e);
}
else if (trigger == 0L && triggerPressed)
{
triggerPressed = false;
ClickedEventArgs e;
e.controllerIndex = controllerIndex;
e.flags = (uint)controllerState.ulButtonPressed;
e.padX = controllerState.rAxis0.x;
e.padY = controllerState.rAxis0.y;
OnTriggerUnclicked(e);
}
ulong grip = controllerState.ulButtonPressed & (1UL << ((int)EVRButtonId.k_EButton_Grip));
if (grip > 0L && !gripped)
{
gripped = true;
ClickedEventArgs e;
e.controllerIndex = controllerIndex;
e.flags = (uint)controllerState.ulButtonPressed;
e.padX = controllerState.rAxis0.x;
e.padY = controllerState.rAxis0.y;
OnGripped(e);
}
else if (grip == 0L && gripped)
{
gripped = false;
ClickedEventArgs e;
e.controllerIndex = controllerIndex;
e.flags = (uint)controllerState.ulButtonPressed;
e.padX = controllerState.rAxis0.x;
e.padY = controllerState.rAxis0.y;
OnUngripped(e);
}
ulong pad = controllerState.ulButtonPressed & (1UL << ((int)EVRButtonId.k_EButton_SteamVR_Touchpad));
if (pad > 0L && !padPressed)
{
padPressed = true;
ClickedEventArgs e;
e.controllerIndex = controllerIndex;
e.flags = (uint)controllerState.ulButtonPressed;
e.padX = controllerState.rAxis0.x;
e.padY = controllerState.rAxis0.y;
OnPadClicked(e);
}
else if (pad == 0L && padPressed)
{
padPressed = false;
ClickedEventArgs e;
e.controllerIndex = controllerIndex;
e.flags = (uint)controllerState.ulButtonPressed;
e.padX = controllerState.rAxis0.x;
e.padY = controllerState.rAxis0.y;
OnPadUnclicked(e);
}
ulong menu = controllerState.ulButtonPressed & (1UL << ((int)EVRButtonId.k_EButton_ApplicationMenu));
if (menu > 0L && !menuPressed)
{
menuPressed = true;
ClickedEventArgs e;
e.controllerIndex = controllerIndex;
e.flags = (uint)controllerState.ulButtonPressed;
e.padX = controllerState.rAxis0.x;
e.padY = controllerState.rAxis0.y;
OnMenuClicked(e);
}
else if (menu == 0L && menuPressed)
{
menuPressed = false;
ClickedEventArgs e;
e.controllerIndex = controllerIndex;
e.flags = (uint)controllerState.ulButtonPressed;
e.padX = controllerState.rAxis0.x;
e.padY = controllerState.rAxis0.y;
OnMenuUnclicked(e);
}
pad = controllerState.ulButtonTouched & (1UL << ((int)EVRButtonId.k_EButton_SteamVR_Touchpad));
if (pad > 0L && !padTouched)
{
padTouched = true;
ClickedEventArgs e;
e.controllerIndex = controllerIndex;
e.flags = (uint)controllerState.ulButtonPressed;
e.padX = controllerState.rAxis0.x;
e.padY = controllerState.rAxis0.y;
OnPadTouched(e);
}
else if (pad == 0L && padTouched)
{
padTouched = false;
ClickedEventArgs e;
e.controllerIndex = controllerIndex;
e.flags = (uint)controllerState.ulButtonPressed;
e.padX = controllerState.rAxis0.x;
e.padY = controllerState.rAxis0.y;
OnPadUntouched(e);
}
}
}
}
2、实现游客传送
using UnityEngine;
using System.Collections;
public class Teleport : MonoBehaviour
{
//手动引用左手柄物体
public GameObject GoLeft;
//设置全局变量存储数据
Tourism_LaserPointer slLeft;
SteamVR_TrackedController stLeft;
ClickedEventHandler ce;
Transform currentTransform;
PointerEventArgs tp;
void Start()
{
//在Start方法中初始化变量,以注册监听方法
slLeft = GoLeft.GetComponent<Tourism_LaserPointer>();
stLeft = GoLeft.GetComponent<SteamVR_TrackedController>();
//注册监听事件LeftPointIn(手柄有物体指向事件)LeftPointOut(取消指向事件)TriggerClicked(扳机扣下事件)
slLeft.PointerIn += LeftPointIn;
slLeft.PointerOut += LeftPointOut;
stLeft.TriggerClicked += TriggerClicked;
}
void LeftPointIn(object sender, PointerEventArgs e)
{
//当有物体指向时设置全局变量标识
currentTransform = e.target;
}
void LeftPointOut(object sender, PointerEventArgs e)
{
//取消指向事件时将标识置为空
currentTransform = null;
}
void TriggerClicked(object sender, ClickedEventArgs e)
{
//如果有指向物体则调用传送传送至目标位置
if (currentTransform != null)
{
TeleportByPosition(slLeft.HitPoint);
}
}
private void TeleportByPosition(Vector3 targetPosition)
{
Debug.Log("targetPosition:" + targetPosition.x + "_" + targetPosition.y + "_" + targetPosition.z);
//根据之前所得公式计算目标位置移动实际空间
this.gameObject.transform.position = new Vector3(targetPosition.x - GoLeft.transform.localPosition.x, targetPosition.y, targetPosition.z - GoLeft.transform.localPosition.z);
}
}
三、多场景编辑
using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;
public class TouristCheck : MonoBehaviour
{
//应用物体当检测条件达到时影藏该物体
public GameObject DoorObject;
//加载场景的名称
public string SceneName;
//场景异步加载操作控制器
AsyncOperation asyncOperation;
void OnTriggerEnter(Collider other)
{
//当检测到碰撞时查看碰撞物体是不是主相机,如果是进行场景加载并对asyncOperation赋值进行标记
if (other.tag == "MainCamera" && asyncOperation == null)
{
asyncOperation = SceneManager.LoadSceneAsync(SceneName, LoadSceneMode.Additive);
}
}
void FixedUpdate()
{
//通过asyncOperation.isDone来检测场景是否加载完成如果加载完成则将墙影藏并将该物体影藏来展现新场景,同时避免再度触发碰撞进行场景加载
if (asyncOperation != null && asyncOperation.isDone)
{
SceneManager.SetActiveScene(SceneManager.GetSceneByName(SceneName));
Manager.Instance.StartNewScene(this);
asyncOperation = null;
DoorObject.SetActive(false);
}
}
public void Reset()
{
//首先将隐藏的墙显示出来
DoorObject.SetActive(true);
//然后根据场景名称卸载场景
SceneManager.UnloadScene(SceneName);
}
}
四、场景管理类
单例模式
using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;
public class Manager : MonoBehaviour {
public static Manager Instance;
public Light CurrentLight;
TouristCheck currentCheck;
void Awake()
{
if (Instance == null)
{
Instance = this;
}
else
{
Debug.LogError("不能重复创建Manager类");
}
}
public void StartNewScene(TouristCheck touristCheck)
{
//检测当前是否已经有场景加载,如果没有将调用对象设置为currentCheck
if (currentCheck == null)
{
currentCheck = touristCheck;
}
//如果有则调用TouristCheck.Reset方法重置并更新currentCheck
else if (currentCheck != touristCheck)
{
currentCheck.Reset();
currentCheck = touristCheck;
}
CurrentLight = GameObject.Find("Directional Light").GetComponent<Light>();
}
}
五、实现不同时段效果
TimeController
using UnityEngine;
using System.Collections;
public class TimeController : MonoBehaviour {
SteamVR_TrackedController TrackedController;
float padX, padY;
bool isGripp;
// Use this for initialization
void Start () {
//获取控制器脚本
TrackedController = GetComponent<SteamVR_TrackedController>();
//添加握和松开的监听
TrackedController.Gripped += Gripped;
TrackedController.Ungripped += Ungripped;
}
//当握时修改全局变量开关
void Gripped(object sender, ClickedEventArgs e)
{
isGripp = true;
}
void Ungripped(object sender, ClickedEventArgs e)
{
isGripp = false;
}
// Update is called once per frame
void Update () {
if (isGripp)
{
//获取当前触摸位置,如果不为零则根据触摸的x轴来调整平行光朝向
if (TrackedController.controllerState.rAxis0.x != 0 && TrackedController.controllerState.rAxis0.y != 0)
{
float angle = 90 + TrackedController.controllerState.rAxis0.x * 120;
Manager.Instance.CurrentLight.transform.rotation = Quaternion.AngleAxis(angle, Vector3.right);
}
}
}
}
六、创建传送面和不可传送面
1、区分传送面
2、修改传送点选取方式
实现抛物线效果
using UnityEngine;
using System.Collections;
public class Parabola : MonoBehaviour {
//传入参数Speed方向shootTransfrom发射器位置StartPosition起始位置EndPosion结束位置GravitationalAcceleration重力加速度lineNodeNum绘制节点数量line绘制抛物线
public Vector3 Speed;
public Transform shootTransfrom;
public Vector3 StartPosition;
public Vector3 EndPosion;
public float GravitationalAcceleration = 10;
public int lineNodeNum = 10;
public LineRenderer line;
Vector3[] positions;
// Use this for initialization
void Start () {
//初始化线段绘制节点
positions = new Vector3[lineNodeNum];
line.SetVertexCount(lineNodeNum);
}
Vector3 GetPlaneVector(Vector3 v3)
{
return new Vector3(v3.x, 0, v3.z);
}
// Update is called once per frame
void FixedUpdate () {
//更新发射点位置
shootTransfrom.position = this.transform.position;
shootTransfrom.rotation = Quaternion.Euler(this.transform.rotation.eulerAngles.x - 30, this.transform.rotation.eulerAngles.y,0);
//当结束点为0及没有结束点时将线段回复为直线
if (EndPosion == Vector3.zero)
{
ResetLine();
return;
}
StartPosition = shootTransfrom.position;
//提前计算出在水平和竖直上的位移
float Sx = Vector3.Distance(GetPlaneVector(EndPosion),GetPlaneVector(StartPosition));
float Sy = StartPosition.y - EndPosion.y;
//计算出竖直方向和水平方向上的初速度比值
float tanA = -shootTransfrom.forward.y / Vector3.Distance(Vector3.zero, GetPlaneVector(shootTransfrom.forward));
//根据推导出来的结果计算出运动时间
float t = Mathf.Sqrt((2 * Sy - 2 * Sx * tanA) / GravitationalAcceleration);
//判断计算是否有异常
if (float.IsNaN(t))
{
ResetLine();
return;
}
//推导出水平和竖直初速度
float Vx = Sx / t;
float Vy = Vx * tanA;
//最后带出方程绘制出线段。
float firstLineNodeTime = t / lineNodeNum;
positions[0] = StartPosition;
for (int i = 1; i < lineNodeNum; i++)
{
float xz = GetX(Vx, firstLineNodeTime * (i + 1));
float y = GetY(firstLineNodeTime * (i + 1), Vy);
positions[i] = Vector3.Normalize(GetPlaneVector(shootTransfrom.forward)) * xz + Vector3.down * y + shootTransfrom.position;
}
line.SetPositions(positions);
}
/// <summary>
/// 计算水平方向位移
/// </summary>
/// <param name="speed">水平方向初速度</param>
/// <param name="time">时间</param>
/// <returns></returns>
private float GetX(float speed, float time)
{
float X = speed * time;
return X;
}
/// <summary>
/// 计算竖直方向位移
/// </summary>
/// <param name="time">时间</param>
/// <param name="speedDownFloat">竖直方向初速度</param>
/// <returns></returns>
private float GetY(float time, float speedDownFloat)
{
float Y = (float)(speedDownFloat * time + 0.5 * GravitationalAcceleration * time * time);
return Y;
}
/// <summary>
/// 将线段重置为一条直线
/// </summary>
void ResetLine()
{
for (int i = 0; i < lineNodeNum; i++)
{
positions[i] = transform.forward * i + transform.position;
}
line.SetPositions(positions);
}
}