LaunchController.ts 4.2 KB

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