如何在ScalaJS中定义自定义JS对象

时间:2019-01-02 23:23:21

标签: phaser-framework scala.js

相位游戏库具有一个API,您可以在其中启动游戏场景(docs)时传递自定义对象。该数据对象完全可以是任何javascript对象,并且可以从scene's settings的场景中检索。我的问题是如何在相位器外观中以通用方式定义此对象,并在自己的代码中定义强类型版本?

到目前为止,我刚刚在相位器API中将对象引用为js.Object,并在创建场景时将其强制转换为我自己的类型:

@js.native
trait ScenePlugin extends js.Object {
  def start(key: SceneKey, data: js.UndefOr[js.Object] = js.undefined): ScenePlugin
}

@js.annotation.ScalaJSDefined
class LevelConfig(
  val key: LevelKey,
  val loadingImage: Option[AssetKey] = None) extends js.Object

@ScalaJSDefined
class LoadScene extends Scene {
  private val loader = new SceneLoader(scene = this)
  private var levelConfig: LevelConfig = _

  override def preload(): Unit = {
    levelConfig = sys.settings.data.asInstanceOf[LevelConfig]
  }

  ...
}

这可行,但是我不满意,因为我必须转换数据对象。传递给ScenePlugin.start()的实际对象的任何错误都将在运行时导致错误,我也可能刚刚使用了香草JS。另外,我的LevelConfig不能是case类,因为我得到了我不完全理解的编译错误Classes and objects extending js.Any may not have a case modifier

以前有人处理过这种情况吗?您是怎么做的?我猜问题出在正在使用的库中,所以也许我需要围绕Phaser的Scene类创建某种包装来处理此问题?我对ScalaJS还是陌生的,并且希望增进我的理解,因此,对解决方案的任何解释将不胜感激(并赞成)。非常感谢!

1 个答案:

答案 0 :(得分:1)

我遵循Justin du Coeur的评论建议,修改了Phaser门面。我为SceneData对象定义了一个非本地特征,并更新了原生Scene门面,使其具有两种必须替换的Scene子类类型。移相器场景是抽象的,打算被覆盖,所以我认为这很好用:

class Scene(config: SceneConfig) extends js.Object {
  type Key <: SceneKey
  type Data <: SceneData

  def scene: ScenePlugin = js.native
  def data: Data = js.native

  def preload(): Unit = js.native
  def create(): Unit = js.native
  def update(time: Double, delta: Double): Unit = js.native
}

object Scene {
  trait SceneKey { def value: String }
  implicit def keyAsString(id: SceneKey): String = id.value

  trait SceneData extends js.Object
}

@js.native
trait ScenePlugin extends js.Object {
  def start[S <: Scene](id: String, data: js.UndefOr[S#Data] = js.undefined): ScenePlugin = js.native
}

这是我的游戏场景的简化示例:

class LoadScene extends Scene(LoadScene.Config) {
  override type Key = LoadId.type
  override type Data = GameAssets

  override def preload(): Unit = {
    createLoadBar()
    loadAssets(data)
  }

  private def createLoadBar(): Unit = { ... }
  private def loadAssets(config: GameAssets): Unit = { ... }

  override def create(): Unit = {
    scene.start[GameScene](GameId)
  }
}

object LoadScene {
  case object LoadId extends SceneKey { val value = "loading" }
  val Config: SceneConfig = ...
}

我很喜欢这个,因为现在不可能用另一个场景的配置类型开始一个场景。