一、框架视图
二、主要代码
VRTK_OutlineObjectCopyHighlighter
// Outline Object Copy|Highlighters|40030
namespace VRTK.Highlighters
{
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Linq;
/// <summary>
/// Creates a mesh copy and applies an outline shader which is toggled on and off when highlighting the object.
/// </summary>
/// <remarks>
/// > A valid mesh must be found or provided for the clone mesh to be created.
///
/// **Script Usage:**
/// * Place the `VRTK_OutlineObjectCopyHighlighter` script on either:
/// * The GameObject of the Interactable Object to highlight.
/// * Any other scene GameObject and then link that GameObject to the Interactable Objects `Object Highlighter` parameter to denote use of the highlighter.
/// * Ensure the `Active` parameter is checked.
/// </remarks>
/// <example>
/// `VRTK/Examples/005_Controller_BasicObjectGrabbing` demonstrates the outline highlighting on the green sphere when the controller touches it.
///
/// `VRTK/Examples/035_Controller_OpacityAndHighlighting` demonstrates the outline highlighting if the left controller collides with the green box.
/// </example>
[AddComponentMenu("VRTK/Scripts/Interactions/Highlighters/VRTK_OutlineObjectCopyHighlighter")]
public class VRTK_OutlineObjectCopyHighlighter : VRTK_BaseHighlighter
{
[Tooltip("The thickness of the outline effect")]
public float thickness = 1f;
[Tooltip("The GameObjects to use as the model to outline. If one isn't provided then the first GameObject with a valid Renderer in the current GameObject hierarchy will be used.")]
public GameObject[] customOutlineModels;
[Tooltip("A path to a GameObject to find at runtime, if the GameObject doesn't exist at edit time.")]
public string[] customOutlineModelPaths;
[Tooltip("If the mesh has multiple sub-meshes to highlight then this should be checked, otherwise only the first mesh will be highlighted.")]
public bool enableSubmeshHighlight = false;
protected Material stencilOutline;
protected Renderer[] highlightModels;
protected string[] copyComponents = new string[] { "UnityEngine.MeshFilter", "UnityEngine.MeshRenderer" };
/// <summary>
/// The Initialise method sets up the highlighter for use.
/// </summary>
/// <param name="color">Not used.</param>
/// <param name="affectObject">An optional GameObject to specify which object to apply the highlighting to.</param>
/// <param name="options">A dictionary array containing the highlighter options:\r * `<'thickness', float>` - Same as `thickness` inspector parameter.\r * `<'customOutlineModels', GameObject[]>` - Same as `customOutlineModels` inspector parameter.\r * `<'customOutlineModelPaths', string[]>` - Same as `customOutlineModelPaths` inspector parameter.</param>
public override void Initialise(Color? color = null, GameObject affectObject = null, Dictionary<string, object> options = null)
{
objectToAffect = (affectObject != null ? affectObject : gameObject);
usesClonedObject = true;
if (stencilOutline == null)
{
stencilOutline = Instantiate((Material)Resources.Load("OutlineBasic"));
}
SetOptions(options);
ResetHighlighter();
}
/// <summary>
/// The ResetHighlighter method creates the additional model to use as the outline highlighted object.
/// </summary>
public override void ResetHighlighter()
{
DeleteExistingHighlightModels();
//First try and use the paths if they have been set
ResetHighlighterWithCustomModelPaths();
//If the custom models have been set then use these to override any set paths.
ResetHighlighterWithCustomModels();
//if no highlights set then try falling back
ResetHighlightersWithCurrentGameObject();
}
/// <summary>
/// The Highlight method initiates the outline object to be enabled and display the outline colour.
/// </summary>
/// <param name="color">The colour to outline with.</param>
/// <param name="duration">Not used.</param>
public override void Highlight(Color? color, float duration = 0f)
{
if (highlightModels != null && highlightModels.Length > 0 && stencilOutline != null)
{
stencilOutline.SetFloat("_Thickness", thickness);
stencilOutline.SetColor("_OutlineColor", (Color)color);
for (int i = 0; i < highlightModels.Length; i++)
{
if (highlightModels[i] != null)
{
highlightModels[i].gameObject.SetActive(true);
highlightModels[i].material = stencilOutline;
}
}
}
}
/// <summary>
/// The Unhighlight method hides the outline object and removes the outline colour.
/// </summary>
/// <param name="color">Not used.</param>
/// <param name="duration">Not used.</param>
public override void Unhighlight(Color? color = null, float duration = 0f)
{
if (objectToAffect == null)
{
return;
}
if (highlightModels != null)
{
for (int i = 0; i < highlightModels.Length; i++)
{
if (highlightModels[i] != null)
{
highlightModels[i].gameObject.SetActive(false);
}
}
}
}
protected virtual void OnEnable()
{
if (customOutlineModels == null)
{
customOutlineModels = new GameObject[0];
}
if (customOutlineModelPaths == null)
{
customOutlineModelPaths = new string[0];
}
}
protected virtual void OnDestroy()
{
if (highlightModels != null)
{
for (int i = 0; i < highlightModels.Length; i++)
{
if (highlightModels[i] != null)
{
Destroy(highlightModels[i]);
}
}
}
Destroy(stencilOutline);
}
protected virtual void ResetHighlighterWithCustomModels()
{
if (customOutlineModels != null && customOutlineModels.Length > 0)
{
highlightModels = new Renderer[customOutlineModels.Length];
for (int i = 0; i < customOutlineModels.Length; i++)
{
highlightModels[i] = CreateHighlightModel(customOutlineModels[i], "");
}
}
}
protected virtual void ResetHighlighterWithCustomModelPaths()
{
if (customOutlineModelPaths != null && customOutlineModelPaths.Length > 0)
{
highlightModels = new Renderer[customOutlineModelPaths.Length];
for (int i = 0; i < customOutlineModelPaths.Length; i++)
{
highlightModels[i] = CreateHighlightModel(null, customOutlineModelPaths[i]);
}
}
}
protected virtual void ResetHighlightersWithCurrentGameObject()
{
if (highlightModels == null || highlightModels.Length == 0)
{
highlightModels = new Renderer[1];
highlightModels[0] = CreateHighlightModel(null, "");
}
}
protected virtual void SetOptions(Dictionary<string, object> options = null)
{
float tmpThickness = GetOption<float>(options, "thickness");
if (tmpThickness > 0f)
{
thickness = tmpThickness;
}
GameObject[] tmpCustomModels = GetOption<GameObject[]>(options, "customOutlineModels");
if (tmpCustomModels != null)
{
customOutlineModels = tmpCustomModels;
}
string[] tmpCustomModelPaths = GetOption<string[]>(options, "customOutlineModelPaths");
if (tmpCustomModelPaths != null)
{
customOutlineModelPaths = tmpCustomModelPaths;
}
}
protected virtual void DeleteExistingHighlightModels()
{
VRTK_PlayerObject[] existingHighlighterObjects = objectToAffect.GetComponentsInChildren<VRTK_PlayerObject>(true);
for (int i = 0; i < existingHighlighterObjects.Length; i++)
{
if (existingHighlighterObjects[i].objectType == VRTK_PlayerObject.ObjectTypes.Highlighter)
{
Destroy(existingHighlighterObjects[i].gameObject);
}
}
highlightModels = new Renderer[0];
}
protected virtual Renderer CreateHighlightModel(GameObject givenOutlineModel, string givenOutlineModelPath)
{
if (givenOutlineModel != null)
{
givenOutlineModel = (givenOutlineModel.GetComponent<Renderer>() ? givenOutlineModel : givenOutlineModel.GetComponentInChildren<Renderer>().gameObject);
}
else if (givenOutlineModelPath != "")
{
Transform getChildModel = objectToAffect.transform.Find(givenOutlineModelPath);
givenOutlineModel = (getChildModel ? getChildModel.gameObject : null);
}
GameObject copyModel = givenOutlineModel;
if (copyModel == null)
{
Renderer copyModelRenderer = objectToAffect.GetComponentInChildren<Renderer>();
copyModel = (copyModelRenderer != null ? copyModelRenderer.gameObject : null);
}
if (copyModel == null)
{
VRTK_Logger.Error(VRTK_Logger.GetCommonMessage(VRTK_Logger.CommonMessageKeys.REQUIRED_COMPONENT_MISSING_FROM_GAMEOBJECT, "VRTK_OutlineObjectCopyHighlighter", "Renderer", "the same or child", " to add the highlighter to"));
return null;
}
GameObject highlightModel = new GameObject(objectToAffect.name + "_HighlightModel");
highlightModel.transform.SetParent(copyModel.transform.parent, false);
highlightModel.transform.localPosition = copyModel.transform.localPosition;
highlightModel.transform.localRotation = copyModel.transform.localRotation;
highlightModel.transform.localScale = copyModel.transform.localScale;
highlightModel.transform.SetParent(objectToAffect.transform);
Component[] copyModelComponents = copyModel.GetComponents<Component>();
for (int i = 0; i < copyModelComponents.Length; i++)
{
Component copyModelComponent = copyModelComponents[i];
if (Array.IndexOf(copyComponents, copyModelComponent.GetType().ToString()) >= 0)
{
VRTK_SharedMethods.CloneComponent(copyModelComponent, highlightModel);
}
}
MeshFilter copyMesh = copyModel.GetComponent<MeshFilter>();
MeshFilter highlightMesh = highlightModel.GetComponent<MeshFilter>();
Renderer returnHighlightModel = highlightModel.GetComponent<Renderer>();
if (highlightMesh != null)
{
if (enableSubmeshHighlight)
{
HashSet<CombineInstance> combine = new HashSet<CombineInstance>();
for (int i = 0; i < copyMesh.mesh.subMeshCount; i++)
{
CombineInstance ci = new CombineInstance();
ci.mesh = copyMesh.mesh;
ci.subMeshIndex = i;
ci.transform = copyMesh.transform.localToWorldMatrix;
combine.Add(ci);
}
highlightMesh.mesh = new Mesh();
highlightMesh.mesh.CombineMeshes(combine.ToArray(), true, false);
}
else
{
highlightMesh.mesh = copyMesh.mesh;
}
returnHighlightModel.material = stencilOutline;
returnHighlightModel.shadowCastingMode = copyModel.transform.GetComponent<Renderer>().shadowCastingMode;
}
highlightModel.SetActive(false);
VRTK_PlayerObject.SetPlayerObject(highlightModel, VRTK_PlayerObject.ObjectTypes.Highlighter);
return returnHighlightModel;
}
}
}
UITipsCanvas
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 指向目标位置
/// </summary>
public class UITipsCanvas : MonoBehaviour {
public GameObject player;
private Vector3 targetPos;
void Start () {
// player = GameObject.FindGameObjectWithTag(Tags.Player);
targetPos = new Vector3(0,transform.position.y,0);
}
void Update () {
targetPos.x = player.transform.position.x;
targetPos.z = player.transform.position.z;
transform.LookAt(targetPos);
}
}
PointerManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using VRTK;
using VRTK.Highlighters;
/// <summary>
/// 指针管理
/// </summary>
public class PointerManager : MonoBehaviour {
public Color EnterColor, SetColor;
private VRTK_Pointer pointer;
private int musicID;
private GameObject go;
private void Awake()
{
pointer = GetComponent<VRTK_Pointer>();
pointer.DestinationMarkerEnter += Pointer_DestinationMarkerEnter;
pointer.DestinationMarkerExit += Pointer_DestinationMarkerExit;
pointer.DestinationMarkerSet += Pointer_DestinationMarkerSet;
}
private void OnDestroy()
{
pointer.DestinationMarkerEnter -= Pointer_DestinationMarkerEnter;
pointer.DestinationMarkerExit -= Pointer_DestinationMarkerExit;
pointer.DestinationMarkerSet -= Pointer_DestinationMarkerSet;
}
private void Pointer_DestinationMarkerSet(object sender, DestinationMarkerEventArgs e)
{
HighLight(e.target,SetColor);
switch (e.target.name)
{
case "Mercury":
musicID = 1;
break;
case "Venus":
musicID = 2;
break;
case "Earth":
musicID = 3;
break;
case "Mars":
musicID = 4;
break;
case "Jupiter":
musicID = 5;
break;
case "Saturn":
musicID = 6;
break;
case "Uranus":
musicID = 7;
break;
case "Neptune":
musicID = 8;
break;
case "Sun":
musicID = 9;
break;
default:
break;
}
AudioManager.Instance.PlaySound(musicID);
}
private void Pointer_DestinationMarkerExit(object sender, DestinationMarkerEventArgs e)
{
//GameObject go = e.target.transform.GetChild(0).gameObject;
try
{
go = e.target.transform.Find("UITipsCanvas").gameObject;
go.SetActive(false);
}
catch (System.Exception)
{
Debug.Log("不存在");
}
HighLight(e.target,Color.clear);
}
private void Pointer_DestinationMarkerEnter(object sender, DestinationMarkerEventArgs e)
{
// GameObject go = e.target.transform.GetChild(0).gameObject;
try
{
go = e.target.transform.Find("UITipsCanvas").gameObject;
go.SetActive(true);
}
catch (System.Exception)
{
Debug.Log("不存在");
}
HighLight(e.target,EnterColor);
}
/// <summary>
/// 高亮
/// </summary>
/// <param name="target"></param>
/// <param name="color"></param>
private void HighLight(Transform target,Color color) {
VRTK_BaseHighlighter highlighter = (target!=null?target.GetComponent<VRTK_BaseHighlighter>():null);
if (highlighter!=null)
{
highlighter.Initialise();
if (color!=Color.clear)
{
highlighter.Highlight(color);
}
else
{
highlighter.Unhighlight();
}
}
}
}
AudioManager
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using DG.Tweening;
using System;
public class AudioManager : MonoBehaviour
{
private static AudioManager instance;
public static AudioManager Instance
{
get
{
return instance;
}
}
private Dictionary<int, string> audioPathDict; // 存放音频文件路径
private AudioSource musicAudioSource;
private List<AudioSource> unusedSoundAudioSourceList; // 存放可以使用的音频组件
private List<AudioSource> usedSoundAudioSourceList; // 存放正在使用的音频组件
private Dictionary<int, AudioClip> audioClipDict; // 缓存音频文件
private float musicVolume = 1;
private float soundVolume = 1;
private string musicVolumePrefs = "MusicVolume";
private string soundVolumePrefs = "SoundVolume";
private int poolCount = 3; // 对象池数量
void Awake()
{
DontDestroyOnLoad(this.gameObject);
instance = this;
audioPathDict = new Dictionary<int, string>() // 这里设置音频文件路径。需要修改。 TODO
{
{ 1, "Audios/水星"},
{ 2, "Audios/金星"},
{ 3,"Audios/地球"},
{ 4,"Audios/火星"},
{ 5,"Audios/木星"},
{ 6,"Audios/土星"},
{ 7,"Audios/天王星"},
{ 8,"Audios/海王星"},
};
musicAudioSource = gameObject.AddComponent<AudioSource>();
musicAudioSource.spatialBlend = 1;//设置3D音效
musicAudioSource.maxDistance = 150; //设置3d距离
unusedSoundAudioSourceList = new List<AudioSource>();
usedSoundAudioSourceList = new List<AudioSource>();
audioClipDict = new Dictionary<int, AudioClip>();
}
void Start()
{
// 从本地缓存读取声音音量
if (PlayerPrefs.HasKey(musicVolumePrefs))
{
musicVolume = PlayerPrefs.GetFloat(musicVolumePrefs);
}
if (PlayerPrefs.HasKey(soundVolumePrefs))
{
musicVolume = PlayerPrefs.GetFloat(soundVolumePrefs);
}
}
/// <summary>
/// 播放背景音乐
/// </summary>
/// <param name="id"></param>
/// <param name="loop"></param>
public void PlayMusic(int id, bool loop = true)
{
// 通过Tween将声音淡入淡出
DOTween.To(() => musicAudioSource.volume, value => musicAudioSource.volume = value, 0, 0.5f).OnComplete(() =>
{
musicAudioSource.clip = GetAudioClip(id);
musicAudioSource.clip.LoadAudioData();
musicAudioSource.loop = loop;
musicAudioSource.volume = musicVolume;
musicAudioSource.Play();
DOTween.To(() => musicAudioSource.volume, value => musicAudioSource.volume = value, musicVolume, 0.5f);
});
}
/// <summary>
/// 播放音效
/// </summary>
/// <param name="id"></param>
public void PlaySound(int id, Action action = null)
{
if (unusedSoundAudioSourceList.Count != 0)
{
AudioSource audioSource = UnusedToUsed();
audioSource.clip = GetAudioClip(id);
audioSource.clip.LoadAudioData();
audioSource.Play();
StartCoroutine(WaitPlayEnd(audioSource, action));
}
else
{
AddAudioSource();
AudioSource audioSource = UnusedToUsed();
audioSource.clip = GetAudioClip(id);
audioSource.clip.LoadAudioData();
audioSource.volume = soundVolume;
audioSource.loop = false;
audioSource.Play();
StartCoroutine(WaitPlayEnd(audioSource, action));
}
}
/// <summary>
/// 播放3d音效
/// </summary>
/// <param name="id"></param>
/// <param name="position"></param>
public void Play3dSound(int id, Vector3 position)
{
AudioClip ac = GetAudioClip(id);
AudioSource.PlayClipAtPoint(ac, position);
}
/// <summary>
/// 当播放音效结束后,将其移至未使用集合
/// </summary>
/// <param name="audioSource"></param>
/// <returns></returns>
IEnumerator WaitPlayEnd(AudioSource audioSource, Action action)
{
yield return new WaitUntil(() => { return !audioSource.isPlaying; });
UsedToUnused(audioSource);
if (action != null)
{
action();
}
}
/// <summary>
/// 获取音频文件,获取后会缓存一份
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
private AudioClip GetAudioClip(int id)
{
if (!audioClipDict.ContainsKey(id))
{
if (!audioPathDict.ContainsKey(id))
return null;
AudioClip ac = Resources.Load(audioPathDict[id]) as AudioClip;
audioClipDict.Add(id, ac);
}
return audioClipDict[id];
}
/// <summary>
/// 添加音频组件
/// </summary>
/// <returns></returns>
private AudioSource AddAudioSource()
{
if (unusedSoundAudioSourceList.Count != 0)
{
return UnusedToUsed();
}
else
{
AudioSource audioSource ;
if (gameObject.AddComponent<AudioSource>()==null)
{
audioSource = gameObject.AddComponent<AudioSource>();
}
else
{
audioSource = gameObject.GetComponent<AudioSource>();
}
unusedSoundAudioSourceList.Add(audioSource);
return audioSource;
}
}
/// <summary>
/// 将未使用的音频组件移至已使用集合里
/// </summary>
/// <returns></returns>
private AudioSource UnusedToUsed()
{
AudioSource audioSource = unusedSoundAudioSourceList[0];
unusedSoundAudioSourceList.RemoveAt(0);
usedSoundAudioSourceList.Add(audioSource);
return audioSource;
}
/// <summary>
/// 将使用完的音频组件移至未使用集合里
/// </summary>
/// <param name="audioSource"></param>
private void UsedToUnused(AudioSource audioSource)
{
if (usedSoundAudioSourceList.Contains(audioSource))
{
usedSoundAudioSourceList.Remove(audioSource);
}
if (unusedSoundAudioSourceList.Count >= poolCount)
{
Destroy(audioSource);
}
else if (audioSource != null && !unusedSoundAudioSourceList.Contains(audioSource))
{
unusedSoundAudioSourceList.Add(audioSource);
}
}
/// <summary>
/// 修改背景音乐音量
/// </summary>
/// <param name="volume"></param>
public void ChangeMusicVolume(float volume)
{
musicVolume = volume;
musicAudioSource.volume = volume;
PlayerPrefs.SetFloat(musicVolumePrefs, volume);
}
/// <summary>
/// 修改音效音量
/// </summary>
/// <param name="volume"></param>
public void ChangeSoundVolume(float volume)
{
soundVolume = volume;
for (int i = 0; i < unusedSoundAudioSourceList.Count; i++)
{
unusedSoundAudioSourceList[i].volume = volume;
}
for (int i = 0; i < usedSoundAudioSourceList.Count; i++)
{
usedSoundAudioSourceList[i].volume = volume;
}
PlayerPrefs.SetFloat(soundVolumePrefs, volume);
}
/// <summary>
/// 是否3d音效
/// </summary>
/// <param name="is3d"></param>
public void Is3DSounds(bool is3d)
{
if (is3d)
musicAudioSource.spatialBlend = 1;//设置3D音效
else
musicAudioSource.spatialBlend = 0;//设置3D音效
}
/// <summary>
/// 设置3d音效距离
/// </summary>
/// <param name="distance"></param>
public void Set3DDistance(float distance)
{
musicAudioSource.maxDistance = distance; //设置3d音效距离
}
/// <summary>
/// 设置3D位置信息
/// </summary>
/// <param name="pos"></param>
public void SetAudioPos(Vector3 pos)
{
transform.position = pos;
}
/// <summary>
/// 播放速度
/// </summary>
/// <param name="isFast"></param>
/// <param name="speed"></param>
public void PlaySpeed(bool isFast, float speed)
{
if (isFast)
{
musicAudioSource.pitch = speed;
}
else
{
musicAudioSource.pitch = 1;
}
}
/// <summary>
/// 停止播放
/// </summary>
public void StopSound() {
musicAudioSource.Stop();
}
}
网友评论