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.

237 lines
7.3 KiB

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