Testing out JmonkeyEngine to make a game in Scala with Akka Actors within a pure FP layer
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

589 lines
17 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. package wow.doge.mygame
  2. import com.jme3.app.SimpleApplication
  3. import com.jme3.app.state.AppStateManager
  4. import scala.reflect.ClassTag
  5. import com.jme3.app.state.AppState
  6. import com.jme3.scene.Node
  7. import com.jme3.scene.Spatial
  8. import com.simsilica.es.EntityData
  9. import com.simsilica.es.EntityComponent
  10. import com.simsilica.es.EntityId
  11. import akka.actor.typed.ActorRef
  12. import akka.util.Timeout
  13. import akka.actor.typed.Scheduler
  14. import monix.bio.Task
  15. import com.jme3.input.InputManager
  16. import com.jme3.input.controls.Trigger
  17. import com.jme3.input.controls.InputListener
  18. import com.jme3.math.Vector3f
  19. import wow.doge.mygame.math.ImVector3f
  20. import com.jme3.scene.Geometry
  21. import scala.jdk.CollectionConverters._
  22. import com.jme3.app.Application
  23. import com.jme3.scene.SceneGraphVisitor
  24. import monix.reactive.Observable
  25. import com.jme3.asset.AssetManager
  26. import com.jme3.asset.AssetLocator
  27. import com.jme3.input.controls.ActionListener
  28. import monix.reactive.OverflowStrategy
  29. import monix.execution.Ack
  30. import monix.execution.Cancelable
  31. import monix.execution.cancelables.SingleAssignCancelable
  32. import com.jme3.input.Action
  33. import com.jme3.bullet.PhysicsSpace
  34. import com.jme3.bullet.collision.PhysicsCollisionListener
  35. import com.jme3.bullet.collision.PhysicsCollisionEvent
  36. import com.jme3.bullet.PhysicsTickListener
  37. import monix.reactive.observers.Subscriber
  38. import monix.execution.Ack.Continue
  39. import monix.execution.Ack.Stop
  40. import com.jme3.bullet.BulletAppState
  41. import wow.doge.mygame.state.MyBaseState
  42. import monix.bio.UIO
  43. import com.jme3.bullet.control.BetterCharacterControl
  44. import com.jme3.scene.CameraNode
  45. import com.jme3.scene.control.CameraControl.ControlDirection
  46. import com.jme3.scene.control.Control
  47. import com.jme3.input.controls.AnalogListener
  48. import enumeratum._
  49. case class ActionEvent(binding: Action, value: Boolean, tpf: Float)
  50. case class EnumActionEvent[T <: EnumEntry](
  51. binding: T,
  52. value: Boolean,
  53. tpf: Float
  54. )
  55. case class AnalogEvent(binding: Action, value: Float, tpf: Float)
  56. case class EnumAnalogEvent[T <: EnumEntry](
  57. binding: T,
  58. value: Float,
  59. tpf: Float
  60. )
  61. case class PhysicsTickEvent(space: PhysicsSpace, tpf: Float)
  62. package object implicits {
  63. type PrePhysicsTickEvent = PhysicsTickEvent
  64. type PhysicsTickObservable =
  65. Observable[Either[PrePhysicsTickEvent, PhysicsTickEvent]]
  66. implicit class JMEAppExt(private val app: Application) extends AnyVal {
  67. def enqueueR(cb: () => Unit) =
  68. app.enqueue(new Runnable {
  69. override def run() = cb()
  70. })
  71. }
  72. implicit class StateManagerExt(private val sm: AppStateManager)
  73. extends AnyVal {
  74. def state[S <: AppState]()(implicit c: ClassTag[S]): S =
  75. sm.getState(c.runtimeClass.asInstanceOf[Class[S]])
  76. }
  77. implicit class SimpleApplicationExt[T <: SimpleApplication](private val sa: T)
  78. extends AnyVal {
  79. def stateManager: AppStateManager = sa.getStateManager()
  80. def inputManager: InputManager = sa.getInputManager()
  81. def assetManager: AssetManager = sa.getAssetManager()
  82. def guiNode = sa.getGuiNode()
  83. def flyCam = Option(sa.getFlyByCamera())
  84. def camera = sa.getCamera()
  85. def viewPort = sa.getViewPort()
  86. def rootNode = sa.getRootNode()
  87. def observableTick: Observable[Float] =
  88. Observable.create(OverflowStrategy.Unbounded) { sub =>
  89. val c = SingleAssignCancelable()
  90. val as = new MyBaseState {
  91. override def init(): Unit = {}
  92. override def update(tpf: Float) = {
  93. if (sub.onNext(tpf) == Ack.Stop)
  94. c.cancel()
  95. }
  96. override def stop(): Unit = {}
  97. override def onEnable() = {}
  98. override def onDisable() = {}
  99. }
  100. sa.stateManager.attach(as)
  101. c := Cancelable(() => sa.stateManager.detach(as))
  102. c
  103. }
  104. }
  105. implicit class NodeExt[T <: Node](private val n: T) extends AnyVal {
  106. /**
  107. * Attaches the given child
  108. *
  109. * @param s
  110. * @return
  111. */
  112. def withChild(s: Spatial): Node = {
  113. n.attachChild(s)
  114. n
  115. }
  116. /**
  117. * Gets the list of children as a monix observable
  118. *
  119. * @return
  120. */
  121. // def children = n.getChildren().asScala.toSeq
  122. def observableChildren =
  123. Observable.fromIterable(n.getChildren().asScala)
  124. def children = LazyList.from(n.getChildren().asScala)
  125. /**
  126. * Attach given children
  127. *
  128. * @param lst
  129. */
  130. def withChildren(lst: Spatial*): Node = {
  131. for (c <- lst) n.withChild(c)
  132. n
  133. }
  134. def +=(spatial: Spatial) = n.attachChild(spatial)
  135. def depthFirst(cb: Spatial => Unit) =
  136. n.depthFirstTraversal(new SceneGraphVisitor() {
  137. override def visit(s: Spatial) = cb(s)
  138. })
  139. def observableDepthFirst(): Observable[Spatial] = {
  140. def loop(
  141. subscriber: Subscriber[Spatial],
  142. spatials: LazyList[Spatial]
  143. ): Task[Unit] =
  144. spatials match {
  145. // spatial can be either a node or a geometry, but it's not a sealed trait
  146. case head #:: tail =>
  147. head match {
  148. case g: Geometry =>
  149. Task.deferFuture(subscriber.onNext(g)).flatMap {
  150. case Continue =>
  151. loop(subscriber, tail)
  152. case Stop =>
  153. Task(subscriber.onComplete())
  154. }
  155. case node: Node =>
  156. val children = node.children
  157. Task.deferFuture(subscriber.onNext(node)).flatMap {
  158. case Continue =>
  159. loop(subscriber, children #::: tail)
  160. case Stop =>
  161. Task(subscriber.onComplete())
  162. }
  163. // case _ => loop(subscriber, tail)
  164. }
  165. case LazyList() => Task.unit
  166. }
  167. Observable.create(OverflowStrategy.Unbounded) { sub =>
  168. implicit val sched = sub.scheduler
  169. loop(sub, LazyList(n)).runToFuture
  170. }
  171. }
  172. def breadthFirst(cb: Spatial => Unit) =
  173. n.breadthFirstTraversal(new SceneGraphVisitor() {
  174. override def visit(s: Spatial) = cb(s)
  175. })
  176. def observableBreadthFirst(): Observable[Spatial] = {
  177. def loop(
  178. subscriber: Subscriber[Spatial],
  179. spatials: LazyList[Spatial]
  180. ): Task[Unit] =
  181. spatials match {
  182. // spatial can be either a node or a geometry, but it's not a sealed trait
  183. case head #:: tail =>
  184. head match {
  185. case g: Geometry =>
  186. Task.deferFuture(subscriber.onNext(g)).flatMap {
  187. case Continue =>
  188. loop(subscriber, tail)
  189. case Stop =>
  190. Task(subscriber.onComplete())
  191. }
  192. case node: Node =>
  193. val children = node.children
  194. Task.deferFuture(subscriber.onNext(node)).flatMap {
  195. case Continue =>
  196. loop(subscriber, tail #::: children)
  197. case Stop =>
  198. Task(subscriber.onComplete())
  199. }
  200. case unknown =>
  201. Task.deferFuture(subscriber.onNext(unknown)).flatMap {
  202. case Continue =>
  203. loop(subscriber, tail)
  204. case Stop =>
  205. Task(subscriber.onComplete())
  206. }
  207. }
  208. case LazyList() => Task.unit
  209. }
  210. Observable.create(OverflowStrategy.Unbounded) { sub =>
  211. implicit val sched = sub.scheduler
  212. loop(sub, LazyList(n)).runToFuture
  213. }
  214. }
  215. def withControl[C <: Control](ctrl: C) = {
  216. n.addControl(ctrl)
  217. n
  218. }
  219. def withLocalTranslation(dir: ImVector3f) = {
  220. n.setLocalTranslation(dir.mutable)
  221. n
  222. }
  223. def withRotate(xAngle: Float, yAngle: Float, zAngle: Float) = {
  224. n.rotate(xAngle, yAngle, zAngle)
  225. n
  226. }
  227. def localRotation = n.getLocalRotation()
  228. def localTranslation = n.getLocalTranslation()
  229. }
  230. implicit class CameraNodeExt(private val cn: CameraNode) {
  231. def withControlDir(controlDir: ControlDirection) = {
  232. cn.setControlDir(controlDir)
  233. cn
  234. }
  235. def withLookAt(position: ImVector3f, upVector: ImVector3f) = {
  236. cn.lookAt(position.mutable, upVector.mutable)
  237. cn
  238. }
  239. }
  240. implicit class EntityDataExt(private val ed: EntityData) extends AnyVal {
  241. def query = new EntityQuery(ed)
  242. // def entities[T <: EntityComponent](entities: Seq[T])
  243. }
  244. implicit class EntityExt(private val e: EntityId) extends AnyVal {
  245. def withComponents(classes: EntityComponent*)(implicit
  246. ed: EntityData
  247. ): EntityId = {
  248. ed.setComponents(e, classes: _*)
  249. e
  250. }
  251. }
  252. implicit class ActorRefExt[Req](private val a: ActorRef[Req]) extends AnyVal {
  253. import akka.actor.typed.scaladsl.AskPattern._
  254. /**
  255. * @param replyTo
  256. * @param timeout
  257. * @param scheduler
  258. * @return
  259. */
  260. def askL[Res](
  261. replyTo: ActorRef[Res] => Req
  262. )(implicit timeout: Timeout, scheduler: Scheduler): Task[Res] = {
  263. Task.deferFuture(a.ask(replyTo)(timeout, scheduler))
  264. }
  265. def ??[Res](
  266. replyTo: ActorRef[Res] => Req
  267. )(implicit timeout: Timeout, scheduler: Scheduler): Task[Res] =
  268. askL(replyTo)
  269. /**
  270. * Same as [[tell]], but wrapped in a Task
  271. *
  272. * @param msg
  273. * @return
  274. */
  275. def tellL(msg: Req) = UIO(a.tell(msg))
  276. def !!(msg: Req) = tellL(msg)
  277. }
  278. // def ?[Res](replyTo: ActorRef[Res] => Req)(implicit timeout: Timeout, scheduler: Scheduler): Future[Res] = {
  279. // ask(replyTo)(timeout, scheduler)
  280. // }
  281. implicit class InputManagerExt(private val inputManager: InputManager)
  282. extends AnyVal {
  283. def withMapping(mapping: String, triggers: Trigger*): InputManager = {
  284. inputManager.addMapping(mapping, triggers: _*)
  285. inputManager
  286. }
  287. def withListener(listener: InputListener, mappings: String*) = {
  288. inputManager.addListener(listener, mappings: _*)
  289. inputManager
  290. }
  291. def observableAction(mappingNames: String*): Observable[ActionEvent] = {
  292. Observable.create(OverflowStrategy.DropOld(10)) { sub =>
  293. val c = SingleAssignCancelable()
  294. val al = new ActionListener {
  295. override def onAction(
  296. binding: String,
  297. value: Boolean,
  298. tpf: Float
  299. ): Unit = {
  300. if (
  301. sub.onNext(ActionEvent(Action(binding), value, tpf)) == Ack.Stop
  302. ) {
  303. sub.onComplete()
  304. c.cancel()
  305. }
  306. }
  307. }
  308. inputManager.addListener(al, mappingNames: _*)
  309. c := Cancelable(() => inputManager.removeListener(al))
  310. c
  311. }
  312. }
  313. def enumObservableAction[T <: EnumEntry](
  314. mappingEnum: Enum[T]
  315. ): Observable[EnumActionEvent[T]] = {
  316. Observable.create(OverflowStrategy.DropOld(10)) { sub =>
  317. val c = SingleAssignCancelable()
  318. val entryNames = mappingEnum.values.map(_.entryName)
  319. val al = new ActionListener {
  320. override def onAction(
  321. binding: String,
  322. value: Boolean,
  323. tpf: Float
  324. ): Unit = {
  325. mappingEnum.withNameOption(binding).foreach { b =>
  326. if (sub.onNext(EnumActionEvent(b, value, tpf)) == Ack.Stop) {
  327. sub.onComplete()
  328. c.cancel()
  329. }
  330. }
  331. }
  332. }
  333. inputManager.addListener(al, entryNames: _*)
  334. c := Cancelable(() => inputManager.removeListener(al))
  335. c
  336. }
  337. }
  338. // def enumObservableAction[T <: enumeratum.EnumEntry](
  339. // mappingNames: Enum[T]
  340. // ): Observable[ActionEvent] = {
  341. // observableAction2(mappingNames).doOnNext()
  342. // }
  343. def analogObservable(mappingNames: String*): Observable[AnalogEvent] = {
  344. Observable.create(OverflowStrategy.DropOld(50)) { sub =>
  345. val c = SingleAssignCancelable()
  346. val al = new AnalogListener {
  347. override def onAnalog(
  348. binding: String,
  349. value: Float,
  350. tpf: Float
  351. ): Unit = {
  352. if (
  353. sub.onNext(AnalogEvent(Action(binding), value, tpf)) == Ack.Stop
  354. ) {
  355. sub.onComplete()
  356. c.cancel()
  357. }
  358. }
  359. }
  360. inputManager.addListener(al, mappingNames: _*)
  361. c := Cancelable(() => inputManager.removeListener(al))
  362. c
  363. }
  364. }
  365. def enumAnalogObservable[T <: EnumEntry](
  366. mappingEnum: Enum[T]
  367. ): Observable[EnumAnalogEvent[T]] = {
  368. Observable.create(OverflowStrategy.DropOld(50)) { sub =>
  369. val c = SingleAssignCancelable()
  370. val entryNames = mappingEnum.values.map(_.entryName)
  371. val al = new AnalogListener {
  372. override def onAnalog(
  373. binding: String,
  374. value: Float,
  375. tpf: Float
  376. ): Unit = {
  377. mappingEnum.withNameOption(binding).foreach { b =>
  378. if (sub.onNext(EnumAnalogEvent(b, value, tpf)) == Ack.Stop) {
  379. sub.onComplete()
  380. c.cancel()
  381. }
  382. }
  383. }
  384. }
  385. inputManager.addListener(al, entryNames: _*)
  386. c := Cancelable(() => inputManager.removeListener(al))
  387. c
  388. }
  389. }
  390. }
  391. implicit class PhysicsSpaceExt(private val space: PhysicsSpace)
  392. extends AnyVal {
  393. def collisionObservable(): Observable[PhysicsCollisionEvent] = {
  394. Observable.create(OverflowStrategy.Unbounded) { sub =>
  395. val c = SingleAssignCancelable()
  396. val cl = new PhysicsCollisionListener {
  397. override def collision(event: PhysicsCollisionEvent): Unit = {
  398. if (sub.onNext(event) == Ack.Stop) {
  399. sub.onComplete()
  400. c.cancel()
  401. }
  402. }
  403. }
  404. space.addCollisionListener(cl)
  405. c := Cancelable(() => space.removeCollisionListener(cl))
  406. c
  407. }
  408. }
  409. def physicsTickObservable(): PhysicsTickObservable = {
  410. Observable.create(OverflowStrategy.Unbounded) { sub =>
  411. val c = SingleAssignCancelable()
  412. val cl = new PhysicsTickListener {
  413. override def prePhysicsTick(space: PhysicsSpace, tpf: Float): Unit = {
  414. val event = PhysicsTickEvent(space, tpf)
  415. if (sub.onNext(Left(event)) == Ack.Stop) {
  416. sub.onComplete()
  417. c.cancel()
  418. }
  419. }
  420. override def physicsTick(space: PhysicsSpace, tpf: Float): Unit = {
  421. val event = PhysicsTickEvent(space, tpf)
  422. if (sub.onNext(Right(event)) == Ack.Stop) {
  423. sub.onComplete()
  424. c.cancel()
  425. }
  426. }
  427. }
  428. space.addTickListener(cl)
  429. c := Cancelable(() => space.removeTickListener(cl))
  430. c
  431. }
  432. }
  433. //TODO Create a typeclass for this
  434. def +=(anyObject: Any) = space.add(anyObject)
  435. def +=(spatial: Spatial) = space.addAll(spatial)
  436. }
  437. implicit class AssetManagerExt(private val am: AssetManager) extends AnyVal {
  438. def registerLocator(
  439. assetPath: os.RelPath,
  440. locator: Class[_ <: AssetLocator]
  441. ): Unit = {
  442. am.registerLocator(assetPath.toString(), locator)
  443. }
  444. def loadModel(assetPath: os.RelPath): Spatial = {
  445. am.loadModel(assetPath.toString())
  446. }
  447. }
  448. implicit class BulletAppStateExt(private val bas: BulletAppState)
  449. extends AnyVal {
  450. def physicsSpace = bas.getPhysicsSpace()
  451. def speed = bas.getSpeed()
  452. }
  453. implicit class BetterCharacterControlExt(
  454. private val bcc: BetterCharacterControl
  455. ) {
  456. def withJumpForce(force: ImVector3f) = {
  457. bcc.setJumpForce(force.mutable)
  458. bcc
  459. }
  460. }
  461. implicit class Vector3fExt(private val v: Vector3f) extends AnyVal {
  462. //TODO add more operations
  463. def +=(that: Vector3f) = v.addLocal(that)
  464. def +=(that: ImVector3f) = v.addLocal(that.x, that.y, that.z)
  465. def +=:(that: ImVector3f) = v += that
  466. def *=(that: Vector3f) = v.multLocal(that)
  467. def -=(that: Vector3f) = v.subtractLocal(that)
  468. def /=(that: Vector3f) = v.divideLocal(that)
  469. def unary_- = v.negateLocal()
  470. def immutable = ImVector3f(v.x, v.y, v.z)
  471. }
  472. implicit class ImVector3fExt(private val v: ImVector3f) extends AnyVal {
  473. def +(that: ImVector3f) = v.copy(v.x + that.x, v.y + that.y, v.z + that.z)
  474. def *(that: ImVector3f) = v.copy(v.x * that.x, v.y * that.y, v.z * that.z)
  475. def *(f: Float): ImVector3f =
  476. v.copy(v.x * f, v.y * f, v.z * f)
  477. // v * ImVector3f(f, f, f)
  478. def -(that: ImVector3f) = v.copy(v.x - that.x, v.y - that.y, v.z - that.z)
  479. def /(that: ImVector3f) = v.copy(v.x / that.x, v.y / that.y, v.z / that.z)
  480. def unary_- = v.copy(-v.x, -v.y, -v.z)
  481. // def unary_-(that: ImVector3f) = this.copy(this.x, this.y, this.z)
  482. def mutable = new Vector3f(v.x, v.y, v.z)
  483. }
  484. // val TasktoUIO = new FunctionK[Task, UIO] {
  485. // def apply[T](f: Task[T]): UIO[T] =
  486. // f.hideErrors
  487. // }
  488. }
  489. // Observable.create(OverflowStrategy.Unbounded) { sub =>
  490. // // val c = SingleAssignCancelable()
  491. // val visitor = new SceneGraphVisitor {
  492. // override def visit(s: Spatial): Unit = {
  493. // sub.onNext(s)
  494. // // if (sub.onNext(s) == Ack.Stop)
  495. // // c.cancel()
  496. // }
  497. // }
  498. // n.depthFirstTraversal(visitor)
  499. // // c := Cancelable(() => ???)
  500. // // c
  501. // Cancelable.empty