438 lines
15 KiB
C#
438 lines
15 KiB
C#
using System.Collections;
|
|
using UnityEngine;
|
|
using UnityEngine.Video;
|
|
using Newtonsoft.Json.Linq;
|
|
using UHFPS.Tools;
|
|
using TMPro;
|
|
|
|
namespace UHFPS.Runtime
|
|
{
|
|
public class VCRPlayer : MonoBehaviour, IInventorySelector, ISaveable
|
|
{
|
|
public enum DisplayText { Play, Pause, Stop, Eject, FastForwad, Rewind, None }
|
|
|
|
public ItemProperty VHSItem;
|
|
|
|
public Animator animator;
|
|
public AudioSource audioSource;
|
|
public InteractableItem VHSTape;
|
|
public Collider insertCollider;
|
|
public string tapeMaterialProperty = "_MainTex";
|
|
|
|
public TMP_Text timeText;
|
|
public TMP_Text stateText;
|
|
public GameObject displayParent;
|
|
public GameObject VHSIcon;
|
|
|
|
public CRTMonitor monitor;
|
|
public Vector2Int outputTextureSize = new Vector2Int(500, 350);
|
|
|
|
public float rewindSpeed;
|
|
public float fastForwardSpeed;
|
|
public float windingStartupSpeed;
|
|
public float timeBeforeWinding;
|
|
|
|
public string insertTrigger = "Insert";
|
|
public string ejectTrigger = "Eject";
|
|
public string closeCoverTrigger = "Close";
|
|
|
|
public string fastForwardSymbol = ">";
|
|
public string rewindSymbol = "<";
|
|
public string playSymbol = "€";
|
|
public string stopSymbol = "€";
|
|
public string pauseSymbol = "™";
|
|
public string ejectSymbol = "®";
|
|
|
|
public SoundClip tapeInsert;
|
|
public SoundClip tapeEject;
|
|
public SoundClip play;
|
|
public SoundClip stop;
|
|
public SoundClip rewind;
|
|
|
|
private RenderTexture outputTexture;
|
|
private Collider tapeCollider;
|
|
private string tapeCustomData;
|
|
|
|
private double tapeDuration;
|
|
private double currentTime;
|
|
private float windingMod;
|
|
|
|
public bool isStarted;
|
|
public bool isPoweredOn;
|
|
|
|
public bool IsPlaying => isStarted && !isPaused;
|
|
|
|
private bool canInsert;
|
|
private bool canEject;
|
|
private bool isPaused;
|
|
private bool isEnded;
|
|
private bool isWinding;
|
|
|
|
private void Awake()
|
|
{
|
|
tapeCollider = VHSTape.GetComponent<Collider>();
|
|
canInsert = true;
|
|
canEject = false;
|
|
|
|
outputTexture = new RenderTexture(outputTextureSize.x, outputTextureSize.y, 16);
|
|
outputTexture.Create();
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
SetPower(isPoweredOn);
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (isStarted && !isWinding)
|
|
{
|
|
UpdateClipTime(monitor.videoPlayer.time);
|
|
if ((tapeDuration - currentTime) <= 0.5f && !isEnded)
|
|
{
|
|
monitor.videoPlayer.Pause();
|
|
SetDisplayText(DisplayText.Stop);
|
|
monitor.SetDisplayTexture(DisplayTexture.Stop);
|
|
audioSource.SetSoundClip(stop, play: true);
|
|
isEnded = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UpdateClipTime(double time, bool setCurrent = true)
|
|
{
|
|
int seconds = ((int)time) % 60;
|
|
int minutes = Mathf.FloorToInt((int)time / 60f);
|
|
timeText.text = string.Format("SP {0:D2} : {1:D2}", minutes, seconds);
|
|
if(setCurrent) currentTime = time;
|
|
}
|
|
|
|
public void PowerOnOff()
|
|
{
|
|
if (isWinding) return;
|
|
SetPower(!isPoweredOn);
|
|
}
|
|
|
|
public void SetPower(bool power)
|
|
{
|
|
if (isWinding) return;
|
|
if (isPoweredOn = power)
|
|
{
|
|
if (canEject)
|
|
{
|
|
SetDisplayText(DisplayText.Stop, true);
|
|
monitor.SetDisplayTexture(DisplayTexture.Stop);
|
|
}
|
|
else
|
|
{
|
|
SetDisplayText(DisplayText.None, true);
|
|
monitor.SetDisplayTexture(DisplayTexture.NoTape);
|
|
}
|
|
|
|
insertCollider.gameObject.SetActive(canInsert);
|
|
}
|
|
else
|
|
{
|
|
monitor.SetDisplayTexture(DisplayTexture.NoSignal);
|
|
monitor.videoPlayer.Pause();
|
|
isPaused = isStarted;
|
|
}
|
|
|
|
displayParent.SetActive(isPoweredOn);
|
|
}
|
|
|
|
public void SetDisplayText(DisplayText? display, bool resetTimer = false)
|
|
{
|
|
stateText.gameObject.SetActive(display.HasValue);
|
|
stateText.text = display switch
|
|
{
|
|
DisplayText.Play => $"PLAY {playSymbol}",
|
|
DisplayText.Pause => $"PAUSE {pauseSymbol}",
|
|
DisplayText.Stop => $"STOP {stopSymbol}",
|
|
DisplayText.Eject => $"EJECT {ejectSymbol}",
|
|
DisplayText.Rewind => $"REW {rewindSymbol}",
|
|
DisplayText.FastForwad => $"FF {fastForwardSymbol}",
|
|
DisplayText.None => "",
|
|
_ => ""
|
|
};
|
|
|
|
if (resetTimer) timeText.text = "-- : --";
|
|
}
|
|
|
|
public void StartPausePlayback()
|
|
{
|
|
if ((tapeDuration - currentTime) <= 0.5f || !canEject || isEnded)
|
|
return;
|
|
|
|
if (isWinding)
|
|
{
|
|
//TODO: monitor.videoPlayer.time is not setting to currentTime
|
|
|
|
StopAllCoroutines();
|
|
SetDisplayText(DisplayText.Stop);
|
|
monitor.SetDisplayTexture(DisplayTexture.Stop);
|
|
audioSource.SetSoundClip(stop, play: true);
|
|
monitor.videoPlayer.time = currentTime;
|
|
monitor.videoPlayer.Prepare();
|
|
isWinding = false;
|
|
isPaused = true;
|
|
}
|
|
else if (!isStarted || isPaused)
|
|
{
|
|
SetDisplayText(DisplayText.Play);
|
|
monitor.SetVideoInput(outputTexture);
|
|
audioSource.SetSoundClip(play, play: true);
|
|
monitor.videoPlayer.Play();
|
|
isStarted = true;
|
|
isPaused = false;
|
|
}
|
|
else if(!isPaused)
|
|
{
|
|
SetDisplayText(DisplayText.Pause);
|
|
monitor.videoPlayer.Pause();
|
|
isPaused = true;
|
|
}
|
|
}
|
|
|
|
public void Rewind()
|
|
{
|
|
if(isStarted && !isWinding && (tapeDuration - currentTime) < tapeDuration - 0.5f)
|
|
{
|
|
monitor.videoPlayer.Pause();
|
|
StartCoroutine(OnRewind());
|
|
isWinding = true;
|
|
isEnded = false;
|
|
}
|
|
}
|
|
|
|
public void FastForward()
|
|
{
|
|
if (isStarted && !isWinding && (tapeDuration - currentTime) > 0.5f)
|
|
{
|
|
monitor.videoPlayer.Pause();
|
|
StartCoroutine(OnFastForward());
|
|
isWinding = true;
|
|
}
|
|
}
|
|
|
|
public void InsertVHSTape()
|
|
{
|
|
if (isPoweredOn && canInsert)
|
|
Inventory.Instance.OpenItemSelector(this);
|
|
}
|
|
|
|
public void EjectVHSTape()
|
|
{
|
|
if (isPoweredOn && canEject && !isWinding)
|
|
{
|
|
monitor.videoPlayer.Pause();
|
|
StartCoroutine(OnEject());
|
|
isWinding = true;
|
|
}
|
|
}
|
|
|
|
public void OnInventoryItemSelect(Inventory inventory, InventoryItem selectedItem)
|
|
{
|
|
if(selectedItem.ItemGuid == VHSItem)
|
|
{
|
|
var customData = selectedItem.CustomData.GetJson();
|
|
if(customData.TryGetValue("texture", out JToken texture))
|
|
{
|
|
string texturePath = texture.ToString();
|
|
Texture2D tapeTexture = Resources.Load<Texture2D>(texturePath);
|
|
MeshRenderer tapeRenderer = VHSTape.GetComponentInChildren<MeshRenderer>();
|
|
tapeRenderer.material.SetTexture(tapeMaterialProperty, tapeTexture);
|
|
}
|
|
|
|
if (customData.TryGetValue("video", out JToken video))
|
|
{
|
|
string videoPath = video.ToString();
|
|
VideoClip videoClip = Resources.Load<VideoClip>(videoPath);
|
|
monitor.PrepareVideo(videoClip, outputTexture);
|
|
tapeDuration = videoClip.length;
|
|
}
|
|
|
|
VHSTape.gameObject.SetActive(true);
|
|
insertCollider.gameObject.SetActive(false);
|
|
tapeCollider.enabled = false;
|
|
|
|
tapeCustomData = selectedItem.CustomData.JsonData;
|
|
inventory.RemoveItem(selectedItem);
|
|
|
|
canInsert = false;
|
|
canEject = false;
|
|
|
|
StartCoroutine(OnInsert());
|
|
}
|
|
else
|
|
{
|
|
Debug.Log("Selected item is not a VHS Tape!");
|
|
}
|
|
}
|
|
|
|
public void OnTakeVHSTape()
|
|
{
|
|
insertCollider.gameObject.SetActive(true);
|
|
VHSTape.gameObject.SetActive(false);
|
|
animator.SetTrigger(closeCoverTrigger);
|
|
canInsert = true;
|
|
}
|
|
|
|
#region Enumerators
|
|
IEnumerator OnInsert()
|
|
{
|
|
animator.SetTrigger(insertTrigger);
|
|
audioSource.SetSoundClip(tapeInsert, play: true);
|
|
|
|
yield return new WaitForAnimatorClip(animator, insertTrigger);
|
|
|
|
SetDisplayText(DisplayText.Stop);
|
|
monitor.SetDisplayTexture(DisplayTexture.Stop);
|
|
|
|
VHSIcon.SetActive(true);
|
|
VHSTape.gameObject.SetActive(false);
|
|
canEject = true;
|
|
}
|
|
|
|
IEnumerator OnEject()
|
|
{
|
|
yield return OnRewind();
|
|
|
|
SetDisplayText(DisplayText.Eject, true);
|
|
monitor.SetDisplayTexture(DisplayTexture.Stop);
|
|
yield return new WaitForSeconds(1f);
|
|
|
|
VHSTape.gameObject.SetActive(true);
|
|
tapeCollider.enabled = false;
|
|
animator.SetTrigger(ejectTrigger);
|
|
audioSource.SetSoundClip(tapeEject, play: true);
|
|
|
|
yield return new WaitForAnimatorClip(animator, ejectTrigger);
|
|
|
|
SetDisplayText(null);
|
|
monitor.SetDisplayTexture(null);
|
|
monitor.SetDisplayTexture(DisplayTexture.NoTape);
|
|
|
|
VHSIcon.SetActive(false);
|
|
VHSTape.ItemCustomData.JsonData = tapeCustomData;
|
|
tapeCustomData = null;
|
|
tapeCollider.enabled = true;
|
|
|
|
canInsert = true;
|
|
canEject = false;
|
|
isStarted = false;
|
|
isWinding = false;
|
|
isPaused = false;
|
|
isEnded = false;
|
|
}
|
|
|
|
IEnumerator OnRewind()
|
|
{
|
|
if (currentTime > 0)
|
|
{
|
|
windingMod = 0;
|
|
monitor.SetDisplayTexture(DisplayTexture.Rewind);
|
|
SetDisplayText(DisplayText.Rewind);
|
|
audioSource.SetSoundClip(rewind, play: true);
|
|
yield return new WaitForSeconds(timeBeforeWinding);
|
|
|
|
while (currentTime > 0)
|
|
{
|
|
UpdateClipTime(currentTime, false);
|
|
windingMod = Mathf.MoveTowards(windingMod, 1, Time.deltaTime * windingStartupSpeed);
|
|
currentTime -= Time.deltaTime * rewindSpeed * windingMod;
|
|
yield return null;
|
|
}
|
|
|
|
audioSource.SetSoundClip(stop, play: true);
|
|
}
|
|
|
|
monitor.SetDisplayTexture(DisplayTexture.Stop);
|
|
SetDisplayText(DisplayText.Stop);
|
|
|
|
currentTime = 0;
|
|
monitor.videoPlayer.time = currentTime;
|
|
isStarted = false;
|
|
isWinding = false;
|
|
isPaused = true;
|
|
}
|
|
|
|
IEnumerator OnFastForward()
|
|
{
|
|
if (currentTime < tapeDuration)
|
|
{
|
|
windingMod = 0;
|
|
monitor.SetDisplayTexture(DisplayTexture.FastForward);
|
|
SetDisplayText(DisplayText.FastForwad);
|
|
audioSource.SetSoundClip(rewind, play: true);
|
|
yield return new WaitForSeconds(timeBeforeWinding);
|
|
|
|
while (currentTime < tapeDuration)
|
|
{
|
|
UpdateClipTime(currentTime, false);
|
|
windingMod = Mathf.MoveTowards(windingMod, 1, Time.deltaTime * windingStartupSpeed);
|
|
currentTime += Time.deltaTime * fastForwardSpeed * windingMod;
|
|
yield return null;
|
|
}
|
|
}
|
|
|
|
monitor.SetDisplayTexture(DisplayTexture.Stop);
|
|
SetDisplayText(DisplayText.Stop);
|
|
audioSource.SetSoundClip(stop, play: true);
|
|
|
|
currentTime = tapeDuration;
|
|
monitor.videoPlayer.time = currentTime;
|
|
isWinding = false;
|
|
isPaused = true;
|
|
}
|
|
#endregion
|
|
|
|
public StorableCollection OnSave()
|
|
{
|
|
return new StorableCollection()
|
|
{
|
|
{ nameof(isPoweredOn), isPoweredOn },
|
|
{ nameof(canInsert), canInsert },
|
|
{ nameof(canEject), canEject },
|
|
{ nameof(isEnded), isEnded },
|
|
{ "playtime", currentTime },
|
|
{ "customData", !string.IsNullOrEmpty(tapeCustomData)
|
|
? JObject.Parse(tapeCustomData) : new JObject() }
|
|
};
|
|
}
|
|
|
|
public void OnLoad(JToken data)
|
|
{
|
|
bool isPowered = (bool)data[nameof(isPoweredOn)];
|
|
SetPower(isPowered);
|
|
isPaused = isPowered;
|
|
|
|
canInsert = (bool)data[nameof(canInsert)];
|
|
canEject = (bool)data[nameof(canEject)];
|
|
isEnded = (bool)data[nameof(isEnded)];
|
|
currentTime = (double)data["playtime"];
|
|
tapeCustomData = data["customData"].ToString();
|
|
|
|
if (!string.IsNullOrEmpty(tapeCustomData) && data["customData"]["texture"] != null && data["customData"]["video"] != null)
|
|
{
|
|
string texturePath = data["customData"]["texture"].ToString();
|
|
string videoPath = data["customData"]["video"].ToString();
|
|
|
|
Texture2D tapeTexture = Resources.Load<Texture2D>(texturePath);
|
|
VideoClip videoClip = Resources.Load<VideoClip>(videoPath);
|
|
MeshRenderer tapeRenderer = VHSTape.GetComponentInChildren<MeshRenderer>();
|
|
|
|
if (tapeTexture) tapeRenderer.material.SetTexture(tapeMaterialProperty, tapeTexture);
|
|
if (videoClip)
|
|
{
|
|
tapeDuration = videoClip.length;
|
|
monitor.PrepareVideo(videoClip, outputTexture);
|
|
}
|
|
}
|
|
|
|
UpdateClipTime(currentTime);
|
|
SetDisplayText(DisplayText.Stop);
|
|
monitor.SetDisplayTexture(DisplayTexture.Stop);
|
|
}
|
|
}
|
|
} |