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.

319 lines
10 KiB

4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
  1. package wow.doge.mygame
  2. import akka.actor.typed.ActorRef
  3. import akka.actor.typed.ActorSystem
  4. import akka.actor.typed.Scheduler
  5. import akka.actor.typed.SpawnProtocol
  6. import akka.util.Timeout
  7. import cats.effect.Resource
  8. import cats.effect.concurrent.Deferred
  9. import cats.syntax.eq._
  10. import com.jme3.app.state.AppStateManager
  11. import com.jme3.asset.plugins.ZipLocator
  12. import com.jme3.bullet.control.BetterCharacterControl
  13. import com.jme3.input.InputManager
  14. import com.jme3.material.Material
  15. import com.jme3.material.MaterialDef
  16. import com.jme3.math.FastMath
  17. import com.jme3.renderer.Camera
  18. import com.jme3.renderer.RenderManager
  19. import com.jme3.renderer.ViewPort
  20. import com.jme3.scene.Node
  21. import com.jme3.scene.control.AbstractControl
  22. import com.softwaremill.macwire._
  23. import com.softwaremill.tagging._
  24. import io.odin.Logger
  25. import monix.bio.Fiber
  26. import monix.bio.IO
  27. import monix.bio.Task
  28. import monix.bio.UIO
  29. import scalafx.scene.control.TextArea
  30. import wow.doge.mygame.executors.Schedulers
  31. import wow.doge.mygame.game.GameApp
  32. import wow.doge.mygame.game.GameAppActor
  33. import wow.doge.mygame.game.GameAppResource
  34. import wow.doge.mygame.game.GameAppTags
  35. import wow.doge.mygame.game.entities.EntityIds
  36. import wow.doge.mygame.game.entities.NpcActorSupervisor
  37. import wow.doge.mygame.game.entities.NpcMovementActor
  38. import wow.doge.mygame.game.entities.PlayerActorSupervisor
  39. import wow.doge.mygame.game.entities.PlayerController
  40. import wow.doge.mygame.game.entities.PlayerControllerTags
  41. import wow.doge.mygame.game.subsystems.input.GameInputHandler
  42. import wow.doge.mygame.game.subsystems.level.DefaultGameLevel
  43. import wow.doge.mygame.implicits._
  44. import wow.doge.mygame.launcher.Launcher
  45. import wow.doge.mygame.launcher.Launcher.LauncherResult
  46. import wow.doge.mygame.math.ImVector3f
  47. import wow.doge.mygame.subsystems.events.EventsModule
  48. import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
  49. import wow.doge.mygame.subsystems.events.PlayerEvent
  50. import wow.doge.mygame.subsystems.events.TickEvent
  51. import wow.doge.mygame.subsystems.scriptsystem.ScriptInitMode
  52. import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource
  53. import wow.doge.mygame.utils.AkkaUtils
  54. import wow.doge.mygame.utils.GenericConsoleStream
  55. import wow.doge.mygame.utils.wrappers.jme.AppNode
  56. import wow.doge.mygame.utils.wrappers.jme.AssetManager
  57. import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace
  58. import wow.doge.mygame.subsystems.events.EventBus.ObservableSubscription
  59. import wow.doge.mygame.subsystems.events.PlayerMovementEvent
  60. import monix.reactive.Observable
  61. import monix.eval.Coeval
  62. import wow.doge.mygame.utils.IOUtils
  63. import com.jme3.math.ColorRGBA
  64. class MainApp(
  65. logger: Logger[Task],
  66. jmeThread: monix.execution.Scheduler,
  67. schedulers: Schedulers,
  68. consoleStream: GenericConsoleStream[TextArea]
  69. )(implicit
  70. spawnProtocol: ActorRef[SpawnProtocol.Command],
  71. timeout: Timeout,
  72. scheduler: Scheduler
  73. ) {
  74. val scriptSystemInit =
  75. new ScriptSystemResource(os.pwd, ScriptInitMode.Eager).init
  76. val eventsModule = new EventsModule(scheduler, spawnProtocol)
  77. class TestClass(
  78. playerEventBus: GameEventBus[PlayerEvent],
  79. tickEventBus: GameEventBus[TickEvent]
  80. )
  81. def gameInit: Resource[Task, Fiber[Throwable, Unit]] =
  82. wire[GameAppResource].resource.evalMap {
  83. case gameApp -> gameAppFib =>
  84. for {
  85. playerEventBus <- eventsModule.playerEventBusTask
  86. mainEventBus <- eventsModule.mainEventBusTask
  87. tickEventBus <- eventsModule.tickEventBusTask
  88. obs <- playerEventBus.askL[Observable[PlayerMovementEvent]](
  89. ObservableSubscription(_)
  90. )
  91. _ <- IOUtils.toIO(
  92. obs
  93. .doOnNextF(pme => Coeval(pprint.log(s"Received event $pme")).void)
  94. .completedL
  95. .startAndForget
  96. )
  97. inputManager <- gameApp.inputManager
  98. assetManager <- UIO.pure(gameApp.assetManager)
  99. stateManager <- gameApp.stateManager
  100. camera <- gameApp.camera
  101. rootNode <- UIO.pure(gameApp.rootNode)
  102. enqueueR <- UIO(gameApp.enqueue _)
  103. viewPort <- gameApp.viewPort
  104. physicsSpace <- UIO.pure(gameApp.physicsSpace)
  105. _ <- logger.info("before")
  106. // jfxUI <- gameApp.jfxUI
  107. gameAppActor <- gameApp.spawnGameActor(
  108. GameAppActor.Props(tickEventBus).behavior,
  109. "gameAppActor"
  110. )
  111. _ <- gameAppActor !! GameAppActor.Start
  112. consoleTextArea <- Task(new TextArea {
  113. text = "hello \n"
  114. editable = false
  115. wrapText = true
  116. // maxHeight = 150
  117. // maxWidth = 300
  118. })
  119. // _ <- Task(consoleStream := consoleTextArea)
  120. // _ <- Task(jfxUI += consoleTextArea)
  121. _ <- logger.info("after")
  122. _ <- logger.info("Initializing console stream")
  123. _ <-
  124. wire[MainAppDelegate]
  125. .init()
  126. .executeOn(gameApp.scheduler)
  127. } yield gameAppFib
  128. }
  129. // val x: Task[Unit] = for {
  130. // tickEventBus <- eventsModule.tickEventBusTask
  131. // playerEventBus <- eventsModule.playerEventBusTask
  132. // _ <- UIO(wire[TestClass])
  133. // _ <- gameInit(tickEventBus).use(_.join)
  134. // } yield ()
  135. val program = for {
  136. scriptSystem <- scriptSystemInit
  137. launchSignal <- Deferred[Task, Launcher.LauncherResult]
  138. launcher <- new Launcher.Props(schedulers, launchSignal).create
  139. launchResult <- launcher.init.use(_ => launchSignal.get)
  140. _ <-
  141. /**
  142. * User chose to quit
  143. */
  144. if (launchResult === LauncherResult.Exit)
  145. logger.info("Exiting")
  146. /**
  147. * User chose launch. Wait for game window to close
  148. */
  149. else
  150. gameInit.use(_.join)
  151. } yield ()
  152. }
  153. /**
  154. * Class with all dependencies in one place for easy wiring
  155. */
  156. class MainAppDelegate(
  157. gameApp: GameApp,
  158. loggerL: Logger[Task],
  159. playerEventBus: GameEventBus[PlayerEvent],
  160. tickEventBus: GameEventBus[TickEvent],
  161. inputManager: InputManager,
  162. assetManager: AssetManager,
  163. stateManager: AppStateManager,
  164. physicsSpace: PhysicsSpace[Task],
  165. camera: Camera,
  166. viewPort: ViewPort,
  167. enqueueR: Function1[() => Unit, Unit],
  168. rootNode: AppNode[Task] @@ GameAppTags.RootNode
  169. )(implicit
  170. spawnProtocol: ActorRef[SpawnProtocol.Command],
  171. timeout: Timeout,
  172. scheduler: Scheduler
  173. ) {
  174. def init(
  175. // appScheduler: monix.execution.Scheduler
  176. // consoleStream: GenericConsoleStream[TextArea]
  177. ) =
  178. for {
  179. _ <- loggerL.info("Initializing Systems")
  180. _ <- assetManager.registerLocator(
  181. os.rel / "assets" / "town.zip",
  182. classOf[ZipLocator]
  183. )
  184. _ <- loggerL.info("test")
  185. // _ <- Task(consoleStream.println("text"))
  186. _ <- DefaultGameLevel(assetManager, viewPort)
  187. .flatMap(_.addToGame(rootNode, physicsSpace).hideErrors)
  188. .onErrorHandleWith(e => loggerL.error(e.toString))
  189. _ <- createPlayerController()
  190. .onErrorHandleWith(e => loggerL.error(e.toString))
  191. // .onErrorRestart(3)
  192. _ <- wire[GameInputHandler.Props].begin
  193. // .onErrorRestart(3)
  194. johnActor <- createTestNpc("John")
  195. .onErrorHandleWith(e => IO.raiseError(new Throwable(e.toString)))
  196. // _ <- johnActor !! NpcActorSupervisor.Move(ImVector3f(0, 0, 20))
  197. // _ <-
  198. // johnActor
  199. // .tellL(NpcActorSupervisor.Move(ImVector3f(-30, 0, 10)))
  200. // .delayExecution(2.seconds)
  201. // _ <-
  202. // IOUtils
  203. // .toIO(
  204. // rootNode
  205. // .observableBreadthFirst()
  206. // .doOnNext(spat => IOUtils.toTask(loggerL.debug(spat.getName())))
  207. // .completedL
  208. // )
  209. // .executeOn(appScheduler)
  210. // .startAndForget
  211. } yield ()
  212. def createPlayerController(
  213. // appScheduler: monix.execution.Scheduler
  214. ): IO[PlayerController.Error, ActorRef[PlayerActorSupervisor.Command]] = {
  215. val playerPos = ImVector3f.ZERO
  216. val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
  217. val playerPhysicsControl =
  218. PlayerController.Defaults.defaultPlayerPhysicsControl
  219. .taggedWith[PlayerControllerTags.PlayerTag]
  220. for {
  221. playerModel <-
  222. assetManager
  223. .loadModelAs[Node](modelPath)
  224. .map(_.withRotate(0, FastMath.PI, 0))
  225. .mapError(PlayerController.CouldNotCreatePlayerModel)
  226. playerNode <- UIO(
  227. PlayerController.Defaults
  228. .defaultPlayerNode(
  229. playerPos,
  230. playerModel,
  231. playerPhysicsControl
  232. )
  233. )
  234. cameraPivotNode <- UIO(
  235. new Node(EntityIds.CameraPivot.value)
  236. .withControl(new FollowControl(playerNode))
  237. .taggedWith[PlayerControllerTags.PlayerCameraPivotNode]
  238. )
  239. camNode <- UIO(
  240. PlayerController.Defaults
  241. .defaultCamerNode(camera, playerPos)
  242. .taggedWith[PlayerControllerTags.PlayerCameraNode]
  243. )
  244. playerActor <- wire[PlayerController.Props].create
  245. } yield playerActor
  246. }
  247. def createTestNpc(
  248. // appScheduler: monix.execution.Scheduler,
  249. npcName: String
  250. ) = {
  251. val initialPos = ImVector3f(50, 5, 0)
  252. val npcPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f)
  253. // (1f, 2.1f, 10f)
  254. .withJumpForce(ImVector3f(0, 5f, 0))
  255. val npcActorTask = AkkaUtils.spawnActorL(
  256. NpcActorSupervisor
  257. .Props(
  258. new NpcMovementActor.Props(
  259. enqueueR,
  260. initialPos,
  261. // tickEventBus,
  262. npcName,
  263. npcPhysicsControl
  264. ).behavior,
  265. npcName,
  266. initialPos
  267. )
  268. .behavior,
  269. s"${npcName}-npcActorSupervisor"
  270. )
  271. for {
  272. materialDef <- assetManager.loadAssetAs[MaterialDef](
  273. os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
  274. )
  275. material = new Material(materialDef)
  276. _ = material.setColor("Color", ColorRGBA.Blue)
  277. mesh = PlayerController.Defaults.defaultMesh.withMaterial(material)
  278. npcNode = PlayerController.Defaults.defaultNpcNode(
  279. mesh,
  280. initialPos,
  281. npcPhysicsControl,
  282. npcName
  283. )
  284. npcActor <- npcActorTask.hideErrors
  285. _ <- (for {
  286. _ <- physicsSpace += npcPhysicsControl
  287. _ <- physicsSpace += npcNode
  288. _ <- rootNode += npcNode
  289. } yield ()).hideErrors
  290. } yield npcActor
  291. }
  292. }
  293. class FollowControl(playerNode: Node) extends AbstractControl {
  294. override def controlUpdate(tpf: Float): Unit =
  295. this.spatial.setLocalTranslation(playerNode.getLocalTranslation())
  296. override def controlRender(
  297. rm: RenderManager,
  298. vp: ViewPort
  299. ): Unit = {}
  300. }