使用场景

在游戏逻辑的开发中,经常需要用到某一个脚本的运行实例,典型的例如:在游戏中设置了一个分数属性,写在GamePlay脚本中,在其他脚本中需要用到这个分数属性,这时就要用到GamePlay的脚本运行实例。这里给出单例模式来获取。

使用方法

以cocos-creator为例,假设有A/B两个脚本,目标是在B中获取A的运行实例,代码如下所示:

  1. // A.ts
  2. export class A {
  3. static ins: A;
  4. score: number = 10
  5. onLoad() {
  6. A.ins = this
  7. }
  8. }
// B.ts
import { A } from "./A"
export class B {
    start() {
        cc.log(A.ins.score)
    }
}

原理浅析

在A脚本中,使用静态变量A.ins来存储脚本运行实例,在脚本onLoad()过程中将脚本运行实例this赋值给A.ins,完成了对脚本运行实例的存储。
在B脚本中,使用对应的import语句,调用A.ins即可获取A脚本的运行实例了。

最佳实践

(注意:游戏开发需要不断进步和调整,这里给出的只是我个人水平下认为的最佳实践,抛砖引玉)

  1. 单例单例,顾名思义,仅适用于场景中只有单个脚本激活,例如GamePlay,Player等等。如果有多个相同类型的脚本同时激活,激活时会覆盖掉前1次的赋值,虽然也可以使用,但是在逻辑上会给开发者造成误解,应该是需要尽量避免的。
  2. 常见天坑:未赋值即调用。这是我作为游戏开发新手经常遇到的坑。注意我在粒子中写的,B在调用时是在start()方法中调用,如果在onLoad()方法中调用并且B脚本在场景中挂载位置在A脚本之前的话,就会碰到这个坑。这个坑的本质上是由于脚本调用顺序导致的,虽然不是bug,但是在游戏开发新手阶段时,比较容易忽略。我会在一篇文章中单独介绍脚本的onLoad()/start()/constructor()的调用顺序,个人给出2种处理方案:
    1. 规范编程,在onLoad()中进行初始化,在start()中进行调用。
    2. 避免使用onLoad()/start()隐式初始化,改用init()/create()等自建函数进行显式初始化。
    3. 以上2种方案可以综合使用。
  3. 统一命名。依照个人习惯,可以命名为ins/instance/INS/Ins等等,尽量使其保持一致。