Assignment for RMIT Mixed Reality in 2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

281 lines
8.0 KiB

  1. /************************************************************************************
  2. See SampleFramework license.txt for license terms. Unless required by applicable law
  3. or agreed to in writing, the sample code is provided AS IS WITHOUT WARRANTIES OR
  4. CONDITIONS OF ANY KIND, either express or implied. See the license for specific
  5. language governing permissions and limitations under the license.
  6. ************************************************************************************/
  7. using System.Collections;
  8. using UnityEngine;
  9. using UnityEngine.Assertions;
  10. namespace OculusSampleFramework
  11. {
  12. public class TrainLocomotive : TrainCarBase
  13. {
  14. private const float MIN_SPEED = 0.2f;
  15. private const float MAX_SPEED = 2.7f;
  16. private const float SMOKE_SPEED_MULTIPLIER = 8f;
  17. private const int MAX_PARTICLES_MULTIPLIER = 3;
  18. private enum EngineSoundState
  19. {
  20. Start = 0,
  21. AccelerateOrSetProperSpeed,
  22. Stop
  23. };
  24. [SerializeField]
  25. [Range(MIN_SPEED, MAX_SPEED)]
  26. protected float _initialSpeed = 0f;
  27. [SerializeField] private GameObject _startStopButton = null;
  28. [SerializeField] private GameObject _decreaseSpeedButton = null;
  29. [SerializeField] private GameObject _increaseSpeedButton = null;
  30. [SerializeField] private GameObject _smokeButton = null;
  31. [SerializeField] private GameObject _whistleButton = null;
  32. [SerializeField] private GameObject _reverseButton = null;
  33. [SerializeField] private AudioSource _whistleAudioSource = null;
  34. [SerializeField] private AudioClip _whistleSound = null;
  35. [SerializeField] private AudioSource _engineAudioSource = null;
  36. [SerializeField] private AudioClip[] _accelerationSounds = null;
  37. [SerializeField] private AudioClip[] _decelerationSounds = null;
  38. [SerializeField] private AudioClip _startUpSound = null;
  39. [SerializeField] private AudioSource _smokeStackAudioSource = null;
  40. [SerializeField] private AudioClip _smokeSound = null;
  41. [SerializeField] private ParticleSystem _smoke1 = null;
  42. [SerializeField] private ParticleSystem _smoke2 = null;
  43. [SerializeField] private TrainCarBase[] _childCars = null;
  44. private bool _isMoving = true;
  45. private bool _reverse = false;
  46. private float _currentSpeed, _speedDiv;
  47. private float _standardRateOverTimeMultiplier;
  48. private int _standardMaxParticles;
  49. private Coroutine _startStopTrainCr;
  50. private void Start()
  51. {
  52. Assert.IsNotNull(_startStopButton);
  53. Assert.IsNotNull(_decreaseSpeedButton);
  54. Assert.IsNotNull(_increaseSpeedButton);
  55. Assert.IsNotNull(_smokeButton);
  56. Assert.IsNotNull(_whistleButton);
  57. Assert.IsNotNull(_reverseButton);
  58. Assert.IsNotNull(_whistleAudioSource);
  59. Assert.IsNotNull(_whistleSound);
  60. Assert.IsNotNull(_smoke1);
  61. Assert.IsNotNull(_engineAudioSource);
  62. Assert.IsNotNull(_accelerationSounds);
  63. Assert.IsNotNull(_decelerationSounds);
  64. Assert.IsNotNull(_startUpSound);
  65. Assert.IsNotNull(_smokeStackAudioSource);
  66. Assert.IsNotNull(_smokeSound);
  67. _standardRateOverTimeMultiplier = _smoke1.emission.rateOverTimeMultiplier;
  68. _standardMaxParticles = _smoke1.main.maxParticles;
  69. Distance = 0.0f;
  70. _speedDiv = (MAX_SPEED - MIN_SPEED) / _accelerationSounds.Length;
  71. _currentSpeed = _initialSpeed;
  72. UpdateCarPosition();
  73. _smoke1.Stop();
  74. _startStopTrainCr = StartCoroutine(StartStopTrain(true));
  75. }
  76. private void Update()
  77. {
  78. UpdatePosition();
  79. }
  80. public override void UpdatePosition()
  81. {
  82. if (!_isMoving)
  83. {
  84. return;
  85. }
  86. if (_trainTrack != null)
  87. {
  88. UpdateDistance();
  89. UpdateCarPosition();
  90. RotateCarWheels();
  91. }
  92. foreach (var trainCarBase in _childCars)
  93. {
  94. trainCarBase.UpdatePosition();
  95. }
  96. }
  97. public void StartStopStateChanged()
  98. {
  99. if (_startStopTrainCr == null)
  100. {
  101. _startStopTrainCr = StartCoroutine(StartStopTrain(!_isMoving));
  102. }
  103. }
  104. private IEnumerator StartStopTrain(bool startTrain)
  105. {
  106. float endSpeed = startTrain ? _initialSpeed : 0.0f;
  107. var timePeriodForSpeedChange = 3.0f;
  108. if (startTrain)
  109. {
  110. _smoke1.Play();
  111. _isMoving = true;
  112. var emissionModule1 = _smoke1.emission;
  113. var mainModule = _smoke1.main;
  114. emissionModule1.rateOverTimeMultiplier = _standardRateOverTimeMultiplier;
  115. mainModule.maxParticles = _standardMaxParticles;
  116. timePeriodForSpeedChange = PlayEngineSound(EngineSoundState.Start);
  117. }
  118. else
  119. {
  120. timePeriodForSpeedChange = PlayEngineSound(EngineSoundState.Stop);
  121. }
  122. // don't loop audio at first; only do when train continues movement below
  123. _engineAudioSource.loop = false;
  124. // make time period a tad shorter so that if it's not looping, we don't
  125. // catch the beginning of the sound
  126. timePeriodForSpeedChange = timePeriodForSpeedChange * 0.9f;
  127. float startTime = Time.time;
  128. float endTime = Time.time + timePeriodForSpeedChange;
  129. float startSpeed = _currentSpeed;
  130. while (Time.time < endTime)
  131. {
  132. float t = (Time.time - startTime) / timePeriodForSpeedChange;
  133. _currentSpeed = startSpeed * (1.0f - t) + endSpeed * t;
  134. UpdateSmokeEmissionBasedOnSpeed();
  135. yield return null;
  136. }
  137. _currentSpeed = endSpeed;
  138. _startStopTrainCr = null;
  139. _isMoving = startTrain;
  140. if (!_isMoving)
  141. {
  142. _smoke1.Stop();
  143. }
  144. else
  145. {
  146. _engineAudioSource.loop = true;
  147. PlayEngineSound(EngineSoundState.AccelerateOrSetProperSpeed);
  148. }
  149. }
  150. private float PlayEngineSound(EngineSoundState engineSoundState)
  151. {
  152. AudioClip audioClip = null;
  153. if (engineSoundState == EngineSoundState.Start)
  154. {
  155. audioClip = _startUpSound;
  156. }
  157. else
  158. {
  159. AudioClip[] audioClips = engineSoundState == EngineSoundState.AccelerateOrSetProperSpeed
  160. ? _accelerationSounds
  161. : _decelerationSounds;
  162. int numSounds = audioClips.Length;
  163. int speedIndex = (int)Mathf.Round((_currentSpeed - MIN_SPEED) / _speedDiv);
  164. audioClip = audioClips[Mathf.Clamp(speedIndex, 0, numSounds - 1)];
  165. }
  166. // if audio is already playing and we are playing the same track, don't interrupt it
  167. if (_engineAudioSource.clip == audioClip && _engineAudioSource.isPlaying &&
  168. engineSoundState == EngineSoundState.AccelerateOrSetProperSpeed)
  169. {
  170. return 0.0f;
  171. }
  172. _engineAudioSource.clip = audioClip;
  173. _engineAudioSource.timeSamples = 0;
  174. _engineAudioSource.Play();
  175. return audioClip.length;
  176. }
  177. private void UpdateDistance()
  178. {
  179. var signedSpeed = _reverse ? -_currentSpeed : _currentSpeed;
  180. Distance = (Distance + signedSpeed * Time.deltaTime) % _trainTrack.TrackLength;
  181. }
  182. public void DecreaseSpeedStateChanged()
  183. {
  184. if (_startStopTrainCr == null && _isMoving)
  185. {
  186. _currentSpeed = Mathf.Clamp(_currentSpeed - _speedDiv, MIN_SPEED, MAX_SPEED);
  187. UpdateSmokeEmissionBasedOnSpeed();
  188. PlayEngineSound(EngineSoundState.AccelerateOrSetProperSpeed);
  189. }
  190. }
  191. public void IncreaseSpeedStateChanged()
  192. {
  193. if (_startStopTrainCr == null && _isMoving)
  194. {
  195. _currentSpeed = Mathf.Clamp(_currentSpeed + _speedDiv, MIN_SPEED, MAX_SPEED);
  196. UpdateSmokeEmissionBasedOnSpeed();
  197. PlayEngineSound(EngineSoundState.AccelerateOrSetProperSpeed);
  198. }
  199. }
  200. private void UpdateSmokeEmissionBasedOnSpeed()
  201. {
  202. var emissionModule = _smoke1.emission;
  203. emissionModule.rateOverTimeMultiplier = GetCurrentSmokeEmission();
  204. var mainModule = _smoke1.main;
  205. mainModule.maxParticles = (int)Mathf.Lerp(_standardMaxParticles, _standardMaxParticles * MAX_PARTICLES_MULTIPLIER,
  206. _currentSpeed / (MAX_SPEED - MIN_SPEED));
  207. }
  208. private float GetCurrentSmokeEmission()
  209. {
  210. return Mathf.Lerp(_standardRateOverTimeMultiplier, _standardRateOverTimeMultiplier * SMOKE_SPEED_MULTIPLIER,
  211. _currentSpeed / (MAX_SPEED - MIN_SPEED));
  212. }
  213. public void SmokeButtonStateChanged()
  214. {
  215. if (_isMoving)
  216. {
  217. _smokeStackAudioSource.clip = _smokeSound;
  218. _smokeStackAudioSource.timeSamples = 0;
  219. _smokeStackAudioSource.Play();
  220. _smoke2.time = 0.0f;
  221. _smoke2.Play();
  222. }
  223. }
  224. public void WhistleButtonStateChanged()
  225. {
  226. if (_whistleSound != null)
  227. {
  228. _whistleAudioSource.clip = _whistleSound;
  229. _whistleAudioSource.timeSamples = 0;
  230. _whistleAudioSource.Play();
  231. }
  232. }
  233. public void ReverseButtonStateChanged()
  234. {
  235. _reverse = !_reverse;
  236. }
  237. }
  238. }