StoryPannelCtr.ts 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import { _decorator, AudioClip, AudioSource, Component, Label, Node } from "cc"
  2. import { processCtr } from "./socket/processCtr"
  3. const { ccclass, property } = _decorator
  4. @ccclass("StoryPannelCtr")
  5. export class StoryPannelCtr extends Component {
  6. @property({ type: processCtr })
  7. public processCtr: processCtr | null = null
  8. @property
  9. typeSpeed: number = 0.2 // 每个字符出现的间隔时间
  10. @property
  11. maxLines: number = 18 // 最大行数
  12. @property(AudioClip)
  13. public typingClip: AudioClip = null!
  14. public audioSource: AudioSource = null!
  15. private fullText: string = ""
  16. private currentText: string = ""
  17. private allLines: string[] = []
  18. public label: Label | null = null
  19. private currentLine: string = ""
  20. private processedCharsCount: number = 0
  21. public isOnProcess: boolean = false
  22. public finishInit: boolean = false
  23. public storyContent: string =
  24. "《隐形守护者》巧妙的结合了影视剧专业的演技造诣和电子游戏独有的交互体验,用电子游戏独有的表现手法,让玩家对当时那段充满色彩的历史有了新的认识。剧本没有模仿传统谍战剧,整个故事设定都非常符合当时那个年代,游戏中的每个人物都信手拈来展现出难以想象的独创性。游戏提供了传统游戏所不具备的沉浸式的互动体验、真正意义上的多样选择所导致的不同结局。更强调简化的互动性和操作性,以完全影视剧的镜头和剧本来叙事。游戏采用高成本但效果好的真人实拍的方式。对剧本细节的打磨和对当时人物环境的考究。通过演员精湛的演技给玩家营造出真实的体验。这款游戏用一种特殊的方式,为玩家讲述了一个精彩的抗战背景下的谍战故事。《隐形守护者》是一部国内首款创新性互动影像作品。全程采用真人拍摄,以定格图像辅之以影视化剪辑的手法。游戏还利用真人互动影像的优势为用户进行了一场行之有效的爱国主义教育。人实景拍摄,所有场景均为实拍影像,包括为了增加历史真实感而添加的影像史料。加上精良的影音演绎的方式大大的增加了代入感,选角上,演员颜值、演技双在线,除了主角肖途展现出贯穿全剧的人性弧光外,女性角色都演绎得各有特点。而极为考究的场景、道具和服装,也都彰显出制作组的用心和细致。而全程语音加持,以及根据剧情推进或激燃或紧张的配乐,让人有亲临谍战大片的错觉。看似是一个拥有四大结局,多达12个小时的故事情节,但从整个故事来看,它的主旋律其实很简单。"
  25. initStory(str: string) {
  26. console.log("初始化故事!!!!!!!!!!!!!")
  27. this.label.string = str
  28. }
  29. updateStory(str: string) {
  30. console.log("开始更新故事!!!!!!!!!!!!!")
  31. //this.fullText = str.replace(/[\r\n]+/g, ' ').trim();
  32. this.fullText = str
  33. .replace(/[\r\n]+/g, " ")
  34. .replace(/\s+/g, " ")
  35. .trim()
  36. if (!this.node) {
  37. console.warn("RichText component is not assigned!")
  38. return
  39. }
  40. //初始化中间值
  41. this.processedCharsCount = 0
  42. this.currentLine = ""
  43. this.allLines = []
  44. this.audioSource.pause()
  45. this.currentText = ""
  46. this.label.string = this.currentText
  47. console.log(this.typeSpeed)
  48. this.typeWrite()
  49. this.audioSource.play()
  50. }
  51. typeWrite() {
  52. if (this.processedCharsCount < this.fullText.length) {
  53. const nextChar = this.fullText[this.processedCharsCount]
  54. this.currentLine += nextChar
  55. this.processedCharsCount++
  56. // 检查当前行的长度,决定是否开始新的一行
  57. if ((this.currentLine.length === 24 && nextChar !== " ") || this.currentLine.length === 25) {
  58. this.allLines.push(this.currentLine)
  59. this.currentLine = ""
  60. }
  61. // 检查所有行的总数,删除早期的行以维持 maxLines 的限制
  62. while (this.allLines.length > this.maxLines - 1) {
  63. // -1 是因为还有 currentLine
  64. this.allLines.shift()
  65. }
  66. if (this.label) {
  67. this.label.string = [...this.allLines, this.currentLine].join("\n")
  68. }
  69. this.scheduleOnce(() => {
  70. this.typeWrite()
  71. }, this.typeSpeed)
  72. } else {
  73. //初始化中间值
  74. this.processedCharsCount = 0
  75. this.currentLine = ""
  76. this.allLines = []
  77. this.audioSource.pause()
  78. if (this.isOnProcess) {
  79. console.log("展示剧情处理完成,处理下一个")
  80. this.processCtr.doneProcessing()
  81. }
  82. }
  83. }
  84. start() {
  85. this.label = this.getComponent(Label)
  86. this.audioSource = this.node.getComponent(AudioSource)
  87. let typeSpeed = new URLSearchParams(location.search).get("speed")
  88. if (typeSpeed !== null) {
  89. this.typeSpeed = Number(typeSpeed)
  90. console.log("get speed from URL:" + typeSpeed)
  91. } else {
  92. console.log("use defult roomId")
  93. }
  94. }
  95. update(deltaTime: number) {}
  96. }