Как определить пользовательские объекты JS в ScalaJS - PullRequest
0 голосов
/ 03 января 2019

Библиотека игры phaser имеет API, в котором вы передаете пользовательский объект при запуске игровой сцены ( docs ). Этот объект данных может быть любым объектом javascript вообще и может быть извлечен изнутри сцены из настроек сцены . Мой вопрос: как мне определить этот объект на фасадах фазера в общем виде и определить строго типизированную версию в моем собственном коде?

До сих пор я только что ссылался на объект как js.Object в API фазера и приводил его к моему собственному типу при создании сцены:

@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(), вызовут ошибки во время выполнения, и я также мог просто использовать vanilla JS. Кроме того, мой LevelConfig не может быть классом case, поскольку я получаю ошибку компиляции Classes and objects extending js.Any may not have a case modifier, которую я не до конца понимаю.

Кто-нибудь имел дело с этой ситуацией раньше и что вы делали, чтобы обойти ее? Я предполагаю, что проблема связана с библиотекой, которая используется, поэтому, возможно, мне нужно создать какую-то оболочку для класса Phaser Scene, чтобы справиться с этим? Я довольно новичок в ScalaJS и надеюсь улучшить мое понимание, поэтому любые объяснения с решениями будут высоко оценены (и одобрены). Большое спасибо!

1 Ответ

0 голосов
/ 05 января 2019

Я последовал предложению Джастина дю Кера об изменении фасада Фазера.Я определил ненативную черту для объекта SceneData и обновил собственный фасад Scene, чтобы иметь два типа, которые подклассы Scene должны переопределять.Сцены Phaser абстрактны и предназначены для переопределения, поэтому я думаю, что это хорошо работает:

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 = ...
}

Мне очень нравится этопотому что теперь невозможно начать сцену с типом конфигурации другой сцены.

...