GamingManager.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996
  1. using o0.Num;
  2. using o0.Wave.RealTime.Old;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Net.WebSockets;
  7. using TMPro;
  8. using UnityEngine;
  9. using UnityEngine.SceneManagement;
  10. using UnityEngine.UI;
  11. public enum EndGameReason
  12. {
  13. UserExit,
  14. GameOver,
  15. }
  16. public class GamingManager : MonoBehaviour
  17. {
  18. private SmartBowManager bowManager;
  19. private AudioManager audioManager;
  20. [SerializeField] private AudioSource BowFireAudio;
  21. [SerializeField] private AudioSource LooseLifeAudio;
  22. [SerializeField] private GameObject FruitPrefab;
  23. [SerializeField] private GameObject ArrowPrefab;
  24. [SerializeField] private GameObject BulletPrefab;
  25. [SerializeField] private GameObject ComboTextPrefab;
  26. [SerializeField] private GameObject CriticalTextPrefab;
  27. [SerializeField] private GameObject AddLifeTextPrefab;
  28. [SerializeField] private GameObject GamingUIContainer;
  29. [SerializeField] private Image AimingCross_img;
  30. [SerializeField] private GameObject Resume_btn;
  31. [SerializeField] private GameObject Pause_btn;
  32. [SerializeField] private GameObject Exit_btn;
  33. [SerializeField] private Image Life_0_img;
  34. [SerializeField] private Image Life_1_img;
  35. [SerializeField] private Image Life_2_img;
  36. [SerializeField] private TextMeshProUGUI ScoreText;
  37. [SerializeField] private CannonController Cannon;
  38. [SerializeField] private GameObject FullscreenClearBubble;
  39. [SerializeField] private int StartScores;
  40. private int uploadScore;//需要上传的分数
  41. [SerializeField] private float AimingCrossPosMultiplier = 1f;
  42. [SerializeField] private Camera Cam;
  43. //子弹部分
  44. [SerializeField] private BulletManager bulletManager;
  45. //是否是子弹初始的状态,
  46. public bool isBulletStatus { get; set; } = false;
  47. public const float ArrowSpeedMultiplier = 8.0f;
  48. public const float GravityMultiplier = 2.5f;
  49. private Quaternion CurrentRotation;
  50. private int Scores;
  51. private int Combos;
  52. private int Life;
  53. private bool bGameStarted;
  54. private bool bGamePaused;
  55. // FrozenBanana related variables
  56. private bool bFreeze;
  57. private bool bFreezing;
  58. private float FreezeTimeEclipsed;
  59. // ComboPomegarante related variables
  60. private bool bEnterComboTime;
  61. private bool bComboTime;
  62. private float ComboTimeEclipsed;
  63. FruitBehavior ActiveComboPomegarante;
  64. List<Vector3> FruitVelocityCache_Linear = new List<Vector3>();
  65. List<Vector3> FruitVelocityCache_Angular = new List<Vector3>();
  66. private float MakeNewWaveCountdown;
  67. private List<GameObject> FruitsInField = new List<GameObject>();
  68. private List<FruitType> FruitsPendingSpawn = new List<FruitType>();
  69. private float TimeSinceLastFruitSpawn = 0f;
  70. public delegate void GameEndDelegate(EndGameReason reason, int scores);
  71. public event GameEndDelegate OnGameEnd;
  72. private int FruitWaves = 0;
  73. private Vector2 ScreenMidPoint;
  74. private float FireCooldownTime_Current = 0f;
  75. private const float FireCooldownTime = 0.5f;
  76. UserGameAnalyse1 userGameAnalyse1;
  77. public static GamingManager gaming { get; set; }
  78. //射箭状态下
  79. private bool bAddCountScore = false;
  80. private void Awake()
  81. {
  82. audioManager = gameObject.GetComponent<AudioManager>();
  83. Debug.Assert(audioManager);
  84. bowManager = gameObject.GetComponent<SmartBowManager>();
  85. Debug.Assert(bowManager);
  86. Debug.Assert(Cannon);
  87. ScreenMidPoint = new Vector2(Screen.width / 2, Screen.height / 2);
  88. //枪模式连接的情况下需要判断子弹
  89. isBulletStatus = BluetoothAim.ins && BluetoothAim.ins.isMainConnectToGun();
  90. if (!isBulletStatus)
  91. {
  92. bulletManager.gameObject.SetActive(false);
  93. }
  94. }
  95. // Start is called before the first frame update
  96. void Start()
  97. {
  98. gaming = this;
  99. // byte[] dataToDecrypt = { 0x5A, 0xD4, 0x80, 0xA2, 0xB3, 0x03, 0x5D };
  100. // byte[] dataToDecrypt = { 0x5A, 0x76, 0xD1, 0x02, 0xEB, 0x8E, 0x5D };
  101. // Decryptor.GetResponseStr(dataToDecrypt);
  102. Resume_btn.GetComponentInChildren<Button>().onClick.AddListener(ResumeGame);
  103. Pause_btn.GetComponentInChildren<Button>().onClick.AddListener(PauseGame);
  104. Exit_btn.GetComponentInChildren<Button>().onClick.AddListener(ExitGame);
  105. if (ShootCheck.ins) {
  106. ShootCheck.ins.OnGameShoot += SmartBowFireArrow;
  107. ShootCheck.ins.OnGameUpdateTheMagazine += UpdateTheMagazine;
  108. }
  109. AimingCross_img.gameObject.SetActive(false);
  110. Resume_btn.gameObject.SetActive(false);
  111. ShowGamingUI(false);
  112. Physics.gravity = new Vector3(0.0f, -9.8f * GravityMultiplier, 0.0f);
  113. // Audio track start point correction only
  114. BowFireAudio.timeSamples = 3000;
  115. // Play background music
  116. AudioSource backgroundMusic = gameObject.GetComponent<AudioSource>();
  117. backgroundMusic.clip = audioManager.GetBackgroundMusic();
  118. if (UserSettings.ins.openBGM)
  119. backgroundMusic.Play();
  120. else
  121. backgroundMusic.Stop();
  122. userGameAnalyse1 = UserGameAnalyse1.CreateWhenGameStartAndReturn(15);
  123. }
  124. private void OnDestroy()
  125. {
  126. if (ShootCheck.ins)
  127. {
  128. ShootCheck.ins.OnGameShoot -= SmartBowFireArrow;
  129. ShootCheck.ins.OnGameUpdateTheMagazine -= UpdateTheMagazine;
  130. }
  131. if (AimHandler.ins)
  132. {
  133. InfraredDemo.infraredCameraHelper.OnPositionUpdate -= UpdateCrossHairPosition;
  134. }
  135. }
  136. // Update is called once per frame
  137. void Update()
  138. {
  139. if (FireCooldownTime_Current > 0f)
  140. {
  141. FireCooldownTime_Current = Mathf.Clamp(FireCooldownTime_Current - Time.deltaTime, 0f, FireCooldownTime);
  142. }
  143. if (bGameStarted && !bGamePaused)
  144. {
  145. // listen to mouse firing arrow
  146. if (Input.GetMouseButtonDown(0) && BowCamera.isTouchMode)
  147. {
  148. AimingCross_img.gameObject.GetComponent<RectTransform>().position = Input.mousePosition;
  149. MouseFireArrow(Input.mousePosition);
  150. }
  151. if (FruitsPendingSpawn.Count > 0)
  152. {
  153. if (TimeSinceLastFruitSpawn > GamingValues.SpawnFruitInterval)
  154. {
  155. if (!Cannon.IsFireAnimating())
  156. {
  157. Cannon.TriggerShot();
  158. // Skip reseting fire interval when this is the last pending fruit
  159. // since we need it to fire immediately when firing the first fruit of a new wave
  160. if (FruitsPendingSpawn.Count == 1)
  161. {
  162. TimeSinceLastFruitSpawn = 0f;
  163. }
  164. }
  165. else
  166. {
  167. // do nothing ,wait for cannon fire animation to finish
  168. }
  169. }
  170. else
  171. {
  172. TimeSinceLastFruitSpawn += Time.deltaTime;
  173. }
  174. }
  175. else // No pending spawning fruit
  176. {
  177. if (FruitsInField.Count == 0)
  178. {
  179. if (MakeNewWaveCountdown > 0)
  180. {
  181. MakeNewWaveCountdown -= Time.deltaTime;
  182. }
  183. else
  184. {
  185. MakeFruitsWave();
  186. MakeNewWaveCountdown = GamingValues.MakeNewWaveCountdown;
  187. }
  188. }
  189. }
  190. // update aiming cross position
  191. //if (BowCamera.isTouchMode == false && AimHandler.ins.bRuning9Axis())//!InfraredDemo.running
  192. //{
  193. // Vector3 raw_screen_pos = Cam.WorldToScreenPoint(Cam.transform.position + (CurrentRotation * Vector3.forward));
  194. // float dist_to_mid_x = (raw_screen_pos.x - ScreenMidPoint.x) * AimingCrossPosMultiplier;
  195. // float dist_to_mid_y = (raw_screen_pos.y - ScreenMidPoint.y) * AimingCrossPosMultiplier;
  196. // Vector3 scrren_pos = new Vector3(Mathf.Clamp((dist_to_mid_x + ScreenMidPoint.x), 0f, 2 * ScreenMidPoint.x), Mathf.Clamp((dist_to_mid_y + ScreenMidPoint.y), 0f, 2 * ScreenMidPoint.y), 1f);
  197. // AimingCross_img.gameObject.GetComponent<RectTransform>().position = scrren_pos;
  198. //}
  199. // Freeze functionality
  200. if (bFreeze)
  201. {
  202. Time.timeScale = 0.5f;
  203. bFreezing = true;
  204. bFreeze = false;
  205. foreach (var fruit in FruitsInField)
  206. {
  207. fruit.GetComponent<FruitBehavior>().Frost();
  208. }
  209. }
  210. if (bFreezing)
  211. {
  212. // Freeze visual effect
  213. float freeze_alpha;
  214. if (FreezeTimeEclipsed < 0.1f)
  215. {
  216. freeze_alpha = Mathf.Lerp(0f, 0.5f, FreezeTimeEclipsed / 0.1f);
  217. }
  218. else
  219. {
  220. freeze_alpha = Mathf.Lerp(0.5f, 0f, (FreezeTimeEclipsed - 0.1f) / (GamingValues.FreezeTime - 0.1f));
  221. }
  222. //MeshRenderer renderer = IceCube.GetComponent<MeshRenderer>();
  223. //renderer.material.SetFloat("_Cutoff", freeze_alpha);
  224. Cam.GetComponent<FrostEffect>().FrostAmount = freeze_alpha;
  225. FreezeTimeEclipsed += (Time.deltaTime / Time.timeScale); // deltaTime is scaled by timeScale, divide it by timeScale will fix it
  226. if (FreezeTimeEclipsed >= GamingValues.FreezeTime)
  227. {
  228. Time.timeScale = 1.0f;
  229. FreezeTimeEclipsed = 0f;
  230. bFreezing = false;
  231. foreach (var fruit in FruitsInField)
  232. {
  233. fruit.GetComponent<FruitBehavior>().UnFrost();
  234. }
  235. }
  236. }
  237. // ComboTime functionality
  238. if (bEnterComboTime)
  239. {
  240. bComboTime = true;
  241. bEnterComboTime = false;
  242. ComboTimeEclipsed = 0f;
  243. }
  244. if (bComboTime)
  245. {
  246. ComboTimeEclipsed += Time.deltaTime;
  247. if (ComboTimeEclipsed > GamingValues.ComboHitTime)
  248. {
  249. bComboTime = false;
  250. ComboTimeEclipsed = 0f;
  251. foreach (var f in FruitsInField)
  252. {
  253. FruitBehavior fr = f.GetComponent<FruitBehavior>();
  254. fr.bMoveAlongTrajectory = true;
  255. }
  256. FruitVelocityCache_Linear.Clear();
  257. FruitVelocityCache_Angular.Clear();
  258. DestroyFruit(ActiveComboPomegarante);
  259. ActiveComboPomegarante = null;
  260. }
  261. else
  262. {
  263. foreach (var f in FruitsInField)
  264. {
  265. FruitBehavior fr = f.GetComponent<FruitBehavior>();
  266. fr.bMoveAlongTrajectory = false;
  267. }
  268. }
  269. }
  270. }
  271. }
  272. public void StartGame()
  273. {
  274. Debug.Assert(FruitsInField.Count == 0);
  275. //InfraredDemo.running
  276. if (AimHandler.ins) {
  277. InfraredDemo.infraredCameraHelper.OnPositionUpdate += UpdateCrossHairPosition;
  278. }
  279. Scores = StartScores;
  280. uploadScore = Scores;
  281. ScoreText.text = "Scores : " + Scores;
  282. Combos = 0;
  283. MakeNewWaveCountdown = GamingValues.MakeNewWaveCountdown;
  284. Life = GamingValues.LifeCount;
  285. UpdateLife(false);
  286. bGameStarted = true;
  287. //if (true/*bowManager.IsSmartBowConnected()*/)
  288. //{
  289. // AimingCross_img.gameObject.SetActive(true);
  290. //}
  291. AimingCross_img.gameObject.SetActive(true);
  292. this.GetComponent<OverallLogics>().SetCrosshairOutLight();
  293. ShowGamingUI(true);
  294. GamingValues.ResetPomegranateSpawnedCount();
  295. Cannon.OnCannonFire += SpawnAFruit;
  296. FruitWaves = 0;
  297. //打水果的子弹,在新开一局游戏时,要更新为满弹的状态
  298. UpdateTheMagazine();
  299. }
  300. public void PauseGame()
  301. {
  302. bGamePaused = true;
  303. AimingCross_img.gameObject.SetActive(false);
  304. Pause_btn.gameObject.SetActive(false);
  305. Resume_btn.gameObject.SetActive(true);
  306. Time.timeScale = 0f;
  307. }
  308. public void ResumeGame()
  309. {
  310. bGamePaused = false;
  311. AimingCross_img.gameObject.SetActive(true);
  312. Pause_btn.gameObject.SetActive(true);
  313. Resume_btn.gameObject.SetActive(false);
  314. Time.timeScale = 1f;
  315. }
  316. public bool IsGameStarted()
  317. {
  318. return bGameStarted;
  319. }
  320. public bool IsGamePaused()
  321. {
  322. return bGamePaused;
  323. }
  324. private void MakeFruitsWave()
  325. {
  326. // Reset Normal fruit unique funcionality
  327. FruitTypeList fruit_list = GetComponent<FruitTypeList>();
  328. Debug.Assert(fruit_list != null);
  329. fruit_list.ResetUniqueNormalIndex();
  330. Debug.Assert(FruitsPendingSpawn.Count == 0);
  331. if (FruitWaves < 5)
  332. {
  333. switch (FruitWaves)
  334. {
  335. case 0:
  336. FruitsPendingSpawn.Add(FruitType.Normal);
  337. break;
  338. case 1:
  339. FruitsPendingSpawn.Add(FruitType.Normal);
  340. FruitsPendingSpawn.Add(FruitType.Bomb);
  341. break;
  342. case 2:
  343. FruitsPendingSpawn.Add(FruitType.Normal);
  344. FruitsPendingSpawn.Add(FruitType.Normal);
  345. break;
  346. case 3:
  347. FruitsPendingSpawn.Add(FruitType.Normal);
  348. FruitsPendingSpawn.Add(FruitType.Normal);
  349. FruitsPendingSpawn.Add(FruitType.Normal);
  350. FruitsPendingSpawn.Add(FruitType.FrozenBanana);
  351. break;
  352. case 4:
  353. FruitsPendingSpawn.Add(FruitType.Normal);
  354. FruitsPendingSpawn.Add(FruitType.Normal);
  355. FruitsPendingSpawn.Add(FruitType.Normal);
  356. FruitsPendingSpawn.Add(FruitType.Normal);
  357. FruitsPendingSpawn.Add(FruitType.RainbowApple);
  358. break;
  359. default:
  360. break;
  361. }
  362. }
  363. else
  364. {
  365. int normal_fruit_count = GamingValues.GetSpawnNum(Scores);
  366. bool bContain_bomb = GamingValues.DoesSpawnBomb(Scores);
  367. bool doesSpawnPome = GamingValues.DoesSpawnComboPomegranate(Scores);
  368. // if spawn FrozenBanana or RainbowApple, 0 - no spawn, 1 - frozen banana, 2 - rainbow apple
  369. int doesSpawnSpecial = 0;
  370. int specialAddition = 0;
  371. if (normal_fruit_count == 3)
  372. {
  373. specialAddition++;
  374. doesSpawnSpecial = 1;
  375. }
  376. else if (normal_fruit_count >= 4)
  377. {
  378. specialAddition++;
  379. doesSpawnSpecial = 2;
  380. }
  381. else
  382. {
  383. doesSpawnSpecial = 0;
  384. }
  385. if (doesSpawnPome)
  386. {
  387. specialAddition++;
  388. }
  389. for (int i = 0; i < normal_fruit_count + specialAddition; i++)
  390. {
  391. if (i == 0) // for the first fruit, spawn bomb if a bomb is included
  392. {
  393. FruitsPendingSpawn.Add(bContain_bomb ? FruitType.Bomb : FruitType.Normal);
  394. }
  395. else if (i == 1 && doesSpawnPome) // for the second, we see if a combo pomegranate is included, since if there is one, the minimum count of fruits is 2
  396. {
  397. FruitsPendingSpawn.Add(FruitType.ComboPomegranate);
  398. }
  399. else if (i == 2 && doesSpawnSpecial > 0) // the third one is last chance for special fruits, check for frozen banana and rainbow apple, they do not appear at the same time, and if there is one, the minimum count must be above 3
  400. {
  401. FruitsPendingSpawn.Add(doesSpawnSpecial == 1 ? FruitType.FrozenBanana : FruitType.RainbowApple);
  402. }
  403. else // all special fruits spawning posibility is checked when spawning the first 3 fruits, the rest must be normal
  404. {
  405. FruitsPendingSpawn.Add(FruitType.Normal);
  406. }
  407. }
  408. }
  409. FruitWaves++;
  410. }
  411. private void SpawnAFruit()
  412. {
  413. float speed_x = Random.Range(-100f, 100f);
  414. float speed_y = Random.Range(350f, 450f);
  415. Debug.Assert(FruitsPendingSpawn.Count > 0);
  416. GameObject newFruit = Instantiate(FruitPrefab, Cannon.GetFiringPoint(), Quaternion.identity);
  417. FruitBehavior fb = newFruit.GetComponent<FruitBehavior>();
  418. // Get a random target position with a small random range along x axis and lower half y axis
  419. Vector3 target = Cam.transform.position + new Vector3(Random.Range(-7f, 7f), Random.Range(-10f, -2f), 0f);
  420. float fruit_lifetime = GamingValues.GetFruitLifetime(FruitWaves, Scores);
  421. fb.Init(FruitsPendingSpawn[0], this, Cannon.GetFiringPoint(), target, fruit_lifetime);
  422. FruitsPendingSpawn.RemoveAt(0);
  423. fb.OnArrive += AFruitArrived;
  424. fb.OnHitEvent += OnFruitHit;
  425. FruitsInField.Add(newFruit);
  426. }
  427. private void AFruitArrived(FruitBehavior fb)
  428. {
  429. if (fb.GetFruitType() != FruitType.Bomb)
  430. {
  431. if (UpdateLife(true))
  432. {
  433. DelayEndGame(EndGameReason.GameOver, 2f);
  434. }
  435. FruitsInField.Remove(fb.gameObject);
  436. DestroyFruit(fb);
  437. SpawnStain(fb.JuiceStain, fb.gameObject.transform.position);
  438. }
  439. else
  440. {
  441. fb.gameObject.transform.position = new Vector3(0f, 0f, 10f);
  442. DestroyFruit(fb);
  443. DelayEndGame(EndGameReason.GameOver, 2f);
  444. }
  445. }
  446. private void EndGame(EndGameReason reason)
  447. {
  448. Cannon.ResetPos();
  449. Cannon.OnCannonFire -= SpawnAFruit;
  450. if (AimHandler.ins)
  451. {
  452. InfraredDemo.infraredCameraHelper.OnPositionUpdate -= UpdateCrossHairPosition;
  453. }
  454. // clear all fruits in the field
  455. foreach (var fruit in FruitsInField)
  456. {
  457. Destroy(fruit);
  458. }
  459. FruitsInField.Clear();
  460. if (IsGamePaused())
  461. {
  462. ResumeGame();
  463. }
  464. bGameStarted = false;
  465. ShowGamingUI(false);
  466. if (CommonConfig.StandaloneModeOrPlatformB)
  467. {
  468. //uploadScore = 455;
  469. //设置一下需要上传排行榜的分数
  470. LocalRank.RankManager.SetCurrentScore((int)uploadScore);
  471. //这里切水果可能会重复调用。加个数值判断,简单的防止重复调用
  472. if(uploadScore>0) LocalRank.RankManager.CreateRankView(GlobalData.singlePlayerGameType, null);
  473. }
  474. //水果上传分数
  475. onUploadScore();
  476. OnGameEnd.Invoke(reason, Scores);
  477. }
  478. //水果上传分数到服务器
  479. void onUploadScore() {
  480. if (uploadScore > 0)
  481. {
  482. Debug.Log("水果游戏上传的积分为:" + uploadScore);
  483. RankComp.Instance.uploadSinglePlayerGameRes(uploadScore);
  484. uploadScore = 0;
  485. }
  486. }
  487. /**
  488. * 点击结束按钮再触发
  489. */
  490. GameResultView gameResultView;
  491. int num = 0;
  492. public void onShowResultView() {
  493. if (num == 0)
  494. {
  495. onUploadScore();
  496. ////点击退出按钮时候。先滚动排行榜再退出
  497. //if (CommonConfig.StandaloneModeOrPlatformB)
  498. //{
  499. // LocalRank.RankManager.CreateRankView(GlobalData.singlePlayerGameType, ()=> {
  500. // //结束游戏页面
  501. // gameResultView = userGameAnalyse1.showResultView(() =>
  502. // {
  503. // UnityEngine.SceneManagement.SceneManager.LoadScene("Home", UnityEngine.SceneManagement.LoadSceneMode.Single);
  504. // });
  505. // });
  506. //}
  507. //else {
  508. // //结束游戏页面
  509. // gameResultView = userGameAnalyse1.showResultView(() =>
  510. // {
  511. // UnityEngine.SceneManagement.SceneManager.LoadScene("Home", UnityEngine.SceneManagement.LoadSceneMode.Single);
  512. // });
  513. //}
  514. //结束游戏页面
  515. gameResultView = userGameAnalyse1.showResultView(() =>
  516. {
  517. UnityEngine.SceneManagement.SceneManager.LoadScene("Home", UnityEngine.SceneManagement.LoadSceneMode.Single);
  518. });
  519. num += 1;
  520. return;
  521. }
  522. if (num == 1)
  523. {
  524. gameResultView.OnClick_Back();
  525. SceneManager.LoadScene("Home", LoadSceneMode.Single);
  526. Debug.Log("退出当前场景");
  527. }
  528. }
  529. private void DelayEndGame(EndGameReason reason, float wait_secs)
  530. {
  531. StartCoroutine(DelayEndGame_CR(reason, wait_secs));
  532. }
  533. private IEnumerator DelayEndGame_CR(EndGameReason reason, float wait_secs)
  534. {
  535. yield return new WaitForSeconds(wait_secs);
  536. EndGame(reason);
  537. }
  538. public static void DelayDestroy(MonoBehaviour excutor, GameObject to_destroy, float wait_secs)
  539. {
  540. excutor.StartCoroutine(DelayDestroy_CR(to_destroy, wait_secs));
  541. }
  542. private static IEnumerator DelayDestroy_CR(GameObject to_destroy, float wait_secs)
  543. {
  544. yield return new WaitForSeconds(wait_secs);
  545. if (to_destroy)
  546. Destroy(to_destroy);
  547. }
  548. public void ExitGame()
  549. {
  550. EndGame(EndGameReason.UserExit);
  551. }
  552. float maxDistance = 200f; // 最大检测距离
  553. [SerializeField] LayerMask hitLayers; // 碰撞检测的层
  554. /// <summary>
  555. /// Gun 模式下:射击的射线检测
  556. /// </summary>
  557. /// <param name="arrowBehavior"></param>
  558. /// <param name="spawnPos"></param>
  559. /// <param name="_direction"></param>
  560. /// <param name="_bulletSpeed"></param>
  561. void CheckCollision(ArrowBehavior arrowBehavior, Vector3 spawnPos, Vector3 _direction, int _bulletSpeed)
  562. {
  563. // 可视化射线
  564. Debug.DrawRay(spawnPos, _direction * maxDistance, Color.red, 2.0f);
  565. RaycastHit[] hits = Physics.RaycastAll(spawnPos, _direction, maxDistance, hitLayers);
  566. // 遍历所有击中的物体
  567. foreach (RaycastHit hit in hits)
  568. {
  569. //Debug.Log("Hit: " + hit.collider.gameObject.name);
  570. if (hit.collider.CompareTag("StaticCollisionScene"))
  571. {
  572. // 计算飞行时间
  573. float distance = hit.distance;
  574. float flightTime = distance / _bulletSpeed;
  575. // 生成弹坑
  576. // 处理碰撞
  577. // 延时生成弹坑图片
  578. StartCoroutine(CreateBulletHole(arrowBehavior, hit, flightTime));
  579. // Debug.Log("hit.transform.tag1 :" + hit.transform.tag);
  580. }
  581. //这里暂时忽略了水果运动和子弹位置,子弹位置速度快。
  582. if (hit.collider.CompareTag("Fruit"))
  583. {
  584. // Debug.Log("hit.transform.tag2 :" + hit.transform.tag);
  585. //Debug.Log("hit.transform.name :" + hit.transform.name);
  586. //处理水果分数
  587. FruitBehavior fruitBehavior = hit.transform.gameObject.GetComponent<FruitBehavior>();
  588. fruitBehavior.OnHitCollision(hit.collider, arrowBehavior);
  589. }
  590. }
  591. //if (Input.GetKeyDown(KeyCode.Alpha1))
  592. //{
  593. // UnityEditor.EditorApplication.isPaused = true;
  594. //}
  595. //if (Input.GetKeyDown(KeyCode.Alpha2))
  596. //{
  597. // UnityEditor.EditorApplication.isPaused = false;
  598. //}
  599. }
  600. IEnumerator CreateBulletHole(ArrowBehavior arrowBehavior,RaycastHit hit, float delay)
  601. {
  602. // 等待飞行时间
  603. yield return new WaitForSeconds(delay);
  604. // 在碰撞点生成弹坑预制体
  605. arrowBehavior.CreateBulletHole(hit.point, hit.normal, hit.transform);
  606. //Debug.Log("Hit object2: " + hit.collider.name);
  607. }
  608. private void FireArrow(float speed, Vector3 dir,bool bAddCount = false)
  609. {
  610. bAddCountScore = bAddCount;
  611. if (bGameStarted && !bGamePaused && FireCooldownTime_Current == 0f)
  612. {
  613. FireCooldownTime_Current = FireCooldownTime;
  614. //枪模式连接的情况下需要判断子弹
  615. if (isBulletStatus)
  616. {
  617. if (bulletManager.bulletZeroNotDelete()) return;
  618. //发射消耗子弹
  619. bulletManager.FireBullet();
  620. }
  621. Vector3 spawnPos = Cam.transform.position + dir;
  622. GameObject arrow = Instantiate(GlobalData.MyDeviceMode == DeviceMode.Archery? ArrowPrefab: BulletPrefab, spawnPos, Quaternion.FromToRotation(-Vector3.right, dir));
  623. if (GlobalData.MyDeviceMode == DeviceMode.Archery)
  624. {
  625. arrow.GetComponent<ArrowBehavior>().Init(speed, dir, true,true);
  626. BowFireAudio.clip = audioManager.GetRandomSound(SoundCategory.ArrowFire);
  627. if (UserSettings.ins.openEffect) BowFireAudio.Play();
  628. }
  629. else {
  630. //目前只有gun类型
  631. ArrowBehavior _arrowBehavior = arrow.GetComponent<ArrowBehavior>();
  632. int _bulletSpeed = 800;
  633. _arrowBehavior.Init(_bulletSpeed, dir.normalized, true, false);
  634. BowFireAudio.clip = audioManager.GetRandomSound(SoundCategory.GunFire);
  635. if (UserSettings.ins.openEffect) BowFireAudio.Play();
  636. CheckCollision(_arrowBehavior,spawnPos, dir.normalized , _bulletSpeed);
  637. //UnityEditor.EditorApplication.isPaused = true;
  638. }
  639. arrow.GetComponent<ArrowBehavior>().OnMissShoot += MissShoot;
  640. //统计射箭次数
  641. if(bAddCount)
  642. userGameAnalyse1.changeShootingCount(1);
  643. }
  644. }
  645. /// <summary>
  646. /// 手枪子弹刷新
  647. /// </summary>
  648. private void UpdateTheMagazine() {
  649. BulletManager.RemoveBulletExternally();
  650. bulletManager.ResetBullets();
  651. }
  652. private void SmartBowFireArrow(float speed)
  653. {
  654. if (Time.time == 0) return;
  655. if (IsGameStarted())
  656. {
  657. if (IsGamePaused()) // if game is paused, fire the smart bow will resume game
  658. {
  659. ResumeGame();
  660. }
  661. else // If game is started and not paused, shoot a arrow
  662. {
  663. Vector3 screenPoint = AimingCross_img.GetComponent<RectTransform>().position;
  664. screenPoint.z = 1;
  665. Vector3 direction = Cam.ScreenToWorldPoint(screenPoint);
  666. FireArrow(speed * ArrowSpeedMultiplier, direction , true);
  667. }
  668. }
  669. else
  670. {
  671. gameObject.GetComponent<OverallLogics>().StartGame();
  672. }
  673. }
  674. private void UpdateRotation(Quaternion q)
  675. {
  676. CurrentRotation = q;
  677. }
  678. private void UpdateCrossHairPosition(Vector2 position, Vector2 cameraLocation)
  679. {
  680. if (AimingCross_img) AimingCross_img.transform.position = position;
  681. }
  682. private void MouseFireArrow(Vector3 screenPos)
  683. {
  684. Vector3 worldPos = Cam.ScreenToWorldPoint(new Vector3(screenPos.x, screenPos.y, 10.0f));
  685. #if UNITY_EDITOR
  686. bool isAdd = true;
  687. #else
  688. bool isAdd = false;
  689. #endif
  690. FireArrow(20.0f, worldPos - Cam.transform.position, isAdd);
  691. }
  692. private void OnFruitHit(FruitBehavior fb, Vector3 hitPoint, bool bCritical)
  693. {
  694. Combos++;
  695. // if this is a ComboPomegranate, ignore critical and combo hit, only gain 1 score per hit
  696. if (fb.GetFruitType() == FruitType.ComboPomegranate)
  697. {
  698. if (GainScore(1))
  699. {
  700. UpdateLife(false);
  701. }
  702. }
  703. else if (bCritical)
  704. {
  705. if (GainScore(10))
  706. {
  707. UpdateLife(false);
  708. }
  709. // display a critical text
  710. GameObject critical = Instantiate(CriticalTextPrefab, GamingUIContainer.transform);
  711. critical.transform.position = Cam.WorldToScreenPoint(hitPoint);
  712. }
  713. else if (Combos > 1) // This combo section handles combo scores addition, text displaying is handled after
  714. {
  715. if (GainScore(5))
  716. {
  717. UpdateLife(false);
  718. }
  719. }
  720. else // A very bland hit, no critical, no combo
  721. {
  722. if (GainScore(5))
  723. {
  724. UpdateLife(false);
  725. }
  726. }
  727. if (Combos > 1) // This combo section handles combo text display
  728. {
  729. // display a combo text
  730. GameObject combo = Instantiate(ComboTextPrefab, GamingUIContainer.transform);
  731. combo.GetComponent<ComboTextBehavior>().SetComboNumber(Combos);
  732. }
  733. if (fb.GetFruitType() == FruitType.FrozenBanana)
  734. {
  735. // enter freeze state
  736. bFreeze = true;
  737. FreezeTimeEclipsed = 0.0f;
  738. DestroyFruit(fb);
  739. }
  740. else if (fb.GetFruitType() == FruitType.ComboPomegranate)
  741. {
  742. ComboPomegaranteBehavior cpb = fb.gameObject.GetComponent<ComboPomegaranteBehavior>();
  743. if (cpb.GetHitCount() == 0)
  744. {
  745. bEnterComboTime = true;
  746. ActiveComboPomegarante = fb;
  747. foreach (var f in FruitsInField)
  748. {
  749. FruitVelocityCache_Linear.Add(f.gameObject.GetComponent<Rigidbody>().velocity);
  750. FruitVelocityCache_Angular.Add(f.gameObject.GetComponent<Rigidbody>().angularVelocity);
  751. }
  752. }
  753. cpb.Hit();
  754. if (cpb.GetHitCount() >= GamingValues.ComboHitLimit)
  755. {
  756. bComboTime = false;
  757. DestroyFruit(fb);
  758. ActiveComboPomegarante = null;
  759. ComboTimeEclipsed = 0f;
  760. foreach (var f in FruitsInField)
  761. {
  762. FruitBehavior fr = f.GetComponent<FruitBehavior>();
  763. fr.bMoveAlongTrajectory = true;
  764. }
  765. }
  766. else
  767. {
  768. // do nothing
  769. }
  770. }
  771. else if (fb.GetFruitType() == FruitType.RainbowApple)
  772. {
  773. Instantiate(FullscreenClearBubble, fb.gameObject.transform.position, Quaternion.identity);
  774. DestroyFruit(fb);
  775. }
  776. else
  777. {
  778. DestroyFruit(fb);
  779. }
  780. }
  781. /// <summary>
  782. /// Gain some scores
  783. /// </summary>
  784. /// <returns> If exceeded another 100 scores, return true, for life recovering test </returns>
  785. private bool GainScore(int score)
  786. {
  787. int last_scores = Scores;
  788. //射箭状态下添加分数
  789. if (bAddCountScore) {
  790. Scores += score;
  791. uploadScore += score;
  792. }
  793. ScoreText.text = "Scores : " + Scores; // update socres display
  794. return ((Scores / 100) == (last_scores / 100) + 1);
  795. }
  796. /// <summary>
  797. /// Add or substract life point by 1
  798. /// </summary>
  799. /// <param name="bDecrease">true for decrease, false for add</param>
  800. /// <returns>return true if life reaches 0</returns>
  801. private bool UpdateLife(bool bDecrease)
  802. {
  803. if (bDecrease)
  804. {
  805. Life--;
  806. if (Life < 0)
  807. {
  808. Life = 0;
  809. }
  810. if (UserSettings.ins.openEffect) LooseLifeAudio.Play();
  811. }
  812. else
  813. {
  814. if (Life < 3)
  815. {
  816. Life++;
  817. GameObject addLife = Instantiate(AddLifeTextPrefab, GamingUIContainer.transform);
  818. }
  819. }
  820. switch (Life)
  821. {
  822. case 0:
  823. Life_0_img.gameObject.SetActive(true);
  824. Life_1_img.gameObject.SetActive(true);
  825. Life_2_img.gameObject.SetActive(true);
  826. return true;
  827. case 1:
  828. Life_0_img.gameObject.SetActive(true);
  829. Life_1_img.gameObject.SetActive(true);
  830. Life_2_img.gameObject.SetActive(false);
  831. return false;
  832. case 2:
  833. Life_0_img.gameObject.SetActive(true);
  834. Life_1_img.gameObject.SetActive(false);
  835. Life_2_img.gameObject.SetActive(false);
  836. return false;
  837. case 3:
  838. Life_0_img.gameObject.SetActive(false);
  839. Life_1_img.gameObject.SetActive(false);
  840. Life_2_img.gameObject.SetActive(false);
  841. return false;
  842. default:
  843. return false;
  844. }
  845. }
  846. private void MissShoot()
  847. {
  848. Combos = 0;
  849. }
  850. private void ShowGamingUI(bool b)
  851. {
  852. GamingUIContainer.SetActive(b);
  853. }
  854. public void DestroyFruit(FruitBehavior fb)
  855. {
  856. FruitsInField.Remove(fb.gameObject);
  857. fb.SmashEffect();
  858. }
  859. public AudioManager GetAudioManager()
  860. {
  861. return audioManager;
  862. }
  863. public void SpawnStain(Sprite stain, Vector3 position3d_ws)
  864. {
  865. GameObject stain_go = Instantiate(new GameObject(), GamingUIContainer.transform);
  866. stain_go.AddComponent<Image>().sprite = stain;
  867. stain_go.transform.position = Cam.WorldToScreenPoint(position3d_ws);
  868. stain_go.transform.localScale = Vector3.one * 8f;
  869. stain_go.AddComponent<StainBehaviour>();
  870. }
  871. }