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.

248 lines
7.8 KiB

  1. package wow.doge.mygame.game.entities
  2. import akka.actor.typed.ActorRef
  3. import akka.actor.typed.Props
  4. import akka.actor.typed.Scheduler
  5. import akka.actor.typed.SpawnProtocol
  6. import akka.util.Timeout
  7. import cats.implicits._
  8. import com.jme3.asset.AssetManager
  9. import com.jme3.bullet.BulletAppState
  10. import com.jme3.bullet.PhysicsSpace
  11. import com.jme3.bullet.control.BetterCharacterControl
  12. import com.jme3.math.FastMath
  13. import com.jme3.renderer.Camera
  14. import com.jme3.scene.CameraNode
  15. import com.jme3.scene.Geometry
  16. import com.jme3.scene.Node
  17. import com.jme3.scene.control.CameraControl.ControlDirection
  18. import com.jme3.scene.shape.Box
  19. import com.softwaremill.macwire._
  20. import com.softwaremill.tagging._
  21. import io.odin.Logger
  22. import monix.bio.IO
  23. import monix.bio.Task
  24. import wow.doge.mygame.game.GameAppTags
  25. import wow.doge.mygame.game.SimpleAppExt
  26. import wow.doge.mygame.implicits._
  27. import wow.doge.mygame.math.ImVector3f
  28. import wow.doge.mygame.state.MyMaterial
  29. import wow.doge.mygame.subsystems.events.EventBus
  30. import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
  31. import wow.doge.mygame.subsystems.events.PlayerCameraEvent
  32. import wow.doge.mygame.subsystems.events.PlayerMovementEvent
  33. import wow.doge.mygame.subsystems.events.TickEvent
  34. import wow.doge.mygame.subsystems.movement.ImMovementActor
  35. import wow.doge.mygame.utils.AkkaUtils
  36. object PlayerControllerTags {
  37. sealed trait PlayerTag
  38. sealed trait PlayerCameraNode
  39. }
  40. object PlayerController {
  41. sealed trait Error
  42. case class CouldNotCreatePlayerNode(reason: String) extends Error
  43. case class GenericError(reason: String) extends Error
  44. class Props(
  45. enqueueR: Function1[() => Unit, Unit],
  46. rootNode: Node @@ GameAppTags.RootNode,
  47. loggerL: Logger[Task],
  48. physicsSpace: PhysicsSpace,
  49. initialPlayerPos: ImVector3f = ImVector3f.ZERO,
  50. spawnProtocol: ActorRef[SpawnProtocol.Command],
  51. playerMovementEventBus: ActorRef[
  52. EventBus.Command[PlayerMovementEvent]
  53. ],
  54. playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]],
  55. playerPhysicsControl: BetterCharacterControl,
  56. appScheduler: monix.execution.Scheduler,
  57. playerNode: Node @@ PlayerControllerTags.PlayerTag,
  58. cameraNode: CameraNode @@ PlayerControllerTags.PlayerCameraNode,
  59. tickEventBus: GameEventBus[TickEvent]
  60. )(implicit timeout: Timeout, scheduler: Scheduler) {
  61. val create: IO[Error, Unit] =
  62. (for {
  63. playerActor <- AkkaUtils.spawnActorL(
  64. spawnProtocol,
  65. "playerActorSupervisor",
  66. new PlayerActorSupervisor.Props(
  67. playerMovementEventBus,
  68. playerCameraEventBus,
  69. tickEventBus,
  70. ImMovementActor
  71. .Props(enqueueR, playerPhysicsControl)
  72. .create,
  73. wireWith(PlayerCameraEventListener.apply _)
  74. ).create(playerPhysicsControl)
  75. )
  76. _ <- IO {
  77. physicsSpace += playerNode
  78. physicsSpace += playerPhysicsControl
  79. }
  80. _ <- Task(rootNode += playerNode)
  81. } yield ())
  82. .onErrorHandleWith(e => IO.raiseError(GenericError(e.getMessage())))
  83. .executeOn(appScheduler)
  84. }
  85. def apply(
  86. app: SimpleAppExt,
  87. modelPath: os.RelPath,
  88. cam: Camera
  89. )(assetManager: AssetManager, bulletAppState: BulletAppState) = {
  90. lazy val playerPos = ImVector3f.ZERO
  91. lazy val playerPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f)
  92. .withJumpForce(ImVector3f(0, 5f, 0))
  93. lazy val playerNode = new Node("PlayerNode")
  94. .withChildren(
  95. assetManager
  96. .loadModel(modelPath)
  97. .asInstanceOf[Node]
  98. .withRotate(0, FastMath.PI, 0)
  99. )
  100. .withLocalTranslation(playerPos)
  101. .withControl(playerPhysicsControl)
  102. {
  103. bulletAppState.physicsSpace += playerNode
  104. bulletAppState.physicsSpace += playerPhysicsControl
  105. }
  106. {
  107. app.rootNode += playerNode
  108. }
  109. playerPhysicsControl
  110. }
  111. object Defaults {
  112. def defaultMesh = {
  113. val b = Box(1, 1, 1)
  114. val geom = Geometry("playerGeom", b)
  115. geom
  116. }
  117. def defaultTexture(assetManager: AssetManager) =
  118. MyMaterial(
  119. assetManager = assetManager,
  120. path = os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
  121. )
  122. def defaultCamerNode(cam: Camera, playerPos: ImVector3f) =
  123. new CameraNode("CameraNode", cam)
  124. .withControlDir(ControlDirection.SpatialToCamera)
  125. .withLocalTranslation(ImVector3f(0, 1.5f, 10))
  126. .withLookAt(playerPos, ImVector3f.UNIT_Y)
  127. def defaultPlayerNode(
  128. assetManager: AssetManager,
  129. modelPath: os.RelPath,
  130. playerPos: ImVector3f,
  131. camNode: CameraNode,
  132. playerPhysicsControl: BetterCharacterControl
  133. ) =
  134. Either
  135. .catchNonFatal(
  136. Node("PlayerNode")
  137. .withChildren(
  138. camNode,
  139. assetManager
  140. .loadModel(modelPath)
  141. .asInstanceOf[Node]
  142. .withRotate(0, FastMath.PI, 0)
  143. )
  144. .withLocalTranslation(playerPos)
  145. .withControl(playerPhysicsControl)
  146. )
  147. .map(_.taggedWith[PlayerControllerTags.PlayerTag])
  148. .leftMap(e => PlayerController.CouldNotCreatePlayerNode(e.getMessage()))
  149. def defaultNpcNode(
  150. assetManager: AssetManager,
  151. // modelPath: os.RelPath,
  152. initialPos: ImVector3f,
  153. npcPhysicsControl: BetterCharacterControl,
  154. npcName: String
  155. ) =
  156. Either
  157. .catchNonFatal(
  158. Node(npcName)
  159. .withChildren(
  160. // assetManager
  161. // .loadModel(modelPath)
  162. // .asInstanceOf[Node]
  163. // .withRotate(0, FastMath.PI, 0)
  164. defaultMesh.withMaterial(defaultTexture(assetManager))
  165. )
  166. .withLocalTranslation(initialPos)
  167. .withControl(npcPhysicsControl)
  168. )
  169. // .map(_.taggedWith[PlayerControllerTags.PlayerTag])
  170. // .leftMap(e => PlayerController.CouldNotCreatePlayerNode(e.getMessage()))
  171. def defaultPlayerPhysicsControl =
  172. new BetterCharacterControl(1.5f, 6f, 1f)
  173. .withJumpForce(ImVector3f(0, 5f, 0))
  174. }
  175. }
  176. object Methods {
  177. def spawnMovementActor(
  178. enqueueR: Function1[() => Unit, Unit],
  179. spawnProtocol: ActorRef[SpawnProtocol.Command],
  180. movable: BetterCharacterControl @@ PlayerControllerTags.PlayerTag,
  181. playerMovementEventBus: ActorRef[
  182. EventBus.Command[PlayerMovementEvent]
  183. ],
  184. loggerL: Logger[Task]
  185. )(implicit timeout: Timeout, scheduler: Scheduler) =
  186. spawnProtocol.askL[ActorRef[ImMovementActor.Command]](
  187. SpawnProtocol.Spawn(
  188. ImMovementActor.Props(enqueueR, movable).create,
  189. "imMovementActor",
  190. Props.empty,
  191. _
  192. )
  193. )
  194. // def spawnPlayerActor(
  195. // app: GameApp,
  196. // spawnProtocol: ActorRef[SpawnProtocol.Command],
  197. // movable: BetterCharacterControl @@ Player,
  198. // playerMovementEventBus: ActorRef[
  199. // EventBus.Command[PlayerMovementEvent]
  200. // ]
  201. // )(implicit timeout: Timeout, scheduler: Scheduler) =
  202. // spawnProtocol.askL[ActorRef[PlayerActorSupervisor.Command]](
  203. // SpawnProtocol.Spawn(
  204. // new PlayerActorSupervisor.Props(
  205. // app,
  206. // movable,
  207. // playerMovementEventBus
  208. // ).create,
  209. // "playerActor",
  210. // Props.empty,
  211. // _
  212. // )
  213. // )
  214. }
  215. // camNode <- IO(
  216. // _cameraNode.getOrElse(defaultCamerNode(camera, initialPlayerPos))
  217. // )
  218. // playerPhysicsControl <- IO(
  219. // _playerPhysicsControl
  220. // .getOrElse(defaultPlayerPhysicsControl)
  221. // .taggedWith[PlayerTag]
  222. // )
  223. // playerNode <- IO.fromEither(
  224. // _playerNode.fold(
  225. // defaultPlayerNode(
  226. // assetManager,
  227. // modelPath,
  228. // initialPlayerPos,
  229. // camNode,
  230. // playerPhysicsControl
  231. // )
  232. // )(_.asRight)
  233. // )