LaunchController.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. import {
  2. _decorator,
  3. BlockInputEvents,
  4. Button,
  5. Camera,
  6. Canvas,
  7. Component,
  8. director,
  9. Director,
  10. Label,
  11. Node,
  12. ProgressBar,
  13. } from 'cc';
  14. import { AppKernel } from '../../app/AppKernel';
  15. import { LaunchProgress, LaunchStateMachine } from './LaunchStateMachine';
  16. import { LaunchDialog } from './states/LaunchState';
  17. const { ccclass, property } = _decorator;
  18. @ccclass('LaunchController')
  19. export class LaunchController extends Component {
  20. @property(Canvas)
  21. canvas: Canvas | null = null;
  22. @property(Label)
  23. progressLabel: Label | null = null;
  24. @property(ProgressBar)
  25. progressBar: ProgressBar | null = null;
  26. @property(Node)
  27. dialogNode: Node | null = null;
  28. @property(Label)
  29. dialogLabel: Label | null = null;
  30. @property(Button)
  31. dialogConfirmButton: Button | null = null;
  32. @property(Button)
  33. dialogCancelButton: Button | null = null;
  34. private launchStateMachine: LaunchStateMachine | null = null;
  35. private dialogResolve: ((confirmed: boolean) => void) | null = null;
  36. protected onLoad(): void {
  37. director.addPersistRootNode(this.node);
  38. AppKernel.initialize();
  39. this.initEvents();
  40. this.launchStateMachine = new LaunchStateMachine({
  41. kernel: AppKernel.Instance,
  42. error: null,
  43. progress: (value) => this.updateProgress(value),
  44. dialog: (message) => this.showDialog(message),
  45. });
  46. }
  47. private initEvents(): void {
  48. director.on(Director.EVENT_AFTER_SCENE_LAUNCH, this.onChangeScene, this,);
  49. AppKernel.events.once('launch:completed', this.onLaunchCompleted, this);
  50. }
  51. private readonly onLaunchCompleted = (): void => {
  52. if (!this.node.isValid) return;
  53. this.node.active = false;
  54. if (director.isPersistRootNode(this.node)) {
  55. director.removePersistRootNode(this.node);
  56. }
  57. this.node.destroy();
  58. };
  59. protected async start(): Promise<void> {
  60. await this.launchStateMachine?.run();
  61. }
  62. protected onDestroy(): void {
  63. this.finishDialog(false);
  64. AppKernel.events.offTarget(this);
  65. director.off(Director.EVENT_AFTER_SCENE_LAUNCH, this.onChangeScene, this);
  66. }
  67. private readonly onChangeScene = (): void => {
  68. if (!this.node.isValid) return;
  69. const scene = director.getScene();
  70. const lobbyCanvas = scene?.getComponentsInChildren(Canvas)?.find(item => item != this.canvas);
  71. this.canvas.enabled = false;
  72. if (lobbyCanvas) {
  73. this.node.setParent(lobbyCanvas.node, true);
  74. this.node.setSiblingIndex(lobbyCanvas.node.children.length - 1);
  75. }
  76. };
  77. private updateProgress(value: LaunchProgress): void {
  78. if (value.title !== undefined && this.progressLabel) {
  79. this.progressLabel.string = value.title;
  80. }
  81. if (value.progress !== undefined && this.progressBar) {
  82. this.progressBar.progress = value.progress;
  83. }
  84. }
  85. private showDialog({ message }: LaunchDialog): Promise<boolean> {
  86. if (
  87. !this.dialogNode
  88. || !this.dialogLabel
  89. || !this.dialogConfirmButton
  90. || !this.dialogCancelButton
  91. ) {
  92. if (typeof globalThis.confirm === 'function') {
  93. return Promise.resolve(globalThis.confirm(message));
  94. }
  95. console.warn('[LaunchController] dialog nodes are not configured');
  96. return Promise.resolve(false);
  97. }
  98. this.finishDialog(false);
  99. this.dialogLabel.string = message;
  100. this.dialogNode.active = true;
  101. return new Promise<boolean>((resolve) => {
  102. this.dialogResolve = resolve;
  103. this.dialogConfirmButton?.node.once(
  104. Button.EventType.CLICK,
  105. this.onDialogConfirm,
  106. this,
  107. );
  108. this.dialogCancelButton?.node.once(
  109. Button.EventType.CLICK,
  110. this.onDialogCancel,
  111. this,
  112. );
  113. });
  114. }
  115. private readonly onDialogConfirm = (): void => {
  116. this.finishDialog(true);
  117. };
  118. private readonly onDialogCancel = (): void => {
  119. this.finishDialog(false);
  120. };
  121. private finishDialog(confirmed: boolean): void {
  122. this.dialogConfirmButton?.node.off(
  123. Button.EventType.CLICK,
  124. this.onDialogConfirm,
  125. this,
  126. );
  127. this.dialogCancelButton?.node.off(
  128. Button.EventType.CLICK,
  129. this.onDialogCancel,
  130. this,
  131. );
  132. if (this.dialogNode) this.dialogNode.active = false;
  133. const resolve = this.dialogResolve;
  134. this.dialogResolve = null;
  135. resolve?.(confirmed);
  136. }
  137. }