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.

184 lines
5.8 KiB

  1. package wow.doge.mygame.subsystems.movement
  2. import akka.actor.typed.scaladsl.ActorContext
  3. import akka.actor.typed.Behavior
  4. import akka.actor.typed.scaladsl.Behaviors
  5. import com.softwaremill.quicklens._
  6. import wow.doge.mygame.implicits._
  7. import com.jme3.renderer.Camera
  8. import wow.doge.mygame.math.ImVector3f
  9. import wow.doge.mygame.game.GameApp
  10. import akka.actor.typed.ActorRef
  11. import wow.doge.mygame.events.EventBus
  12. import com.jme3.math.Vector3f
  13. import wow.doge.mygame.state.CardinalDirection
  14. import wow.doge.mygame.subsystems.events.MovementEvent.PlayerMovementEvent
  15. import akka.actor.typed.LogOptions
  16. import com.typesafe.scalalogging.Logger
  17. import org.slf4j.event.Level
  18. sealed trait RotateDir
  19. object RotateDir {
  20. case object Left extends RotateDir
  21. case object Right extends RotateDir
  22. }
  23. trait CanMove[-A] {
  24. // def getDirection(cam: Camera, cardinalDir: CardinalDirection): ImVector3f
  25. def move(inst: A, direction: ImVector3f): Unit
  26. def jump(inst: A): Unit
  27. def stop(inst: A): Unit
  28. def rotate(inst: A, rotateDir: RotateDir): Unit
  29. }
  30. object ImMovementActor {
  31. sealed trait Command
  32. // final case class Tick(tpf: Float) extends Command
  33. final case class Tick(tpf: Float) extends Command
  34. sealed trait Movement extends Command
  35. final case class MovedLeft(pressed: Boolean) extends Movement
  36. final case class MovedUp(pressed: Boolean) extends Movement
  37. final case class MovedRight(pressed: Boolean) extends Movement
  38. final case class MovedDown(pressed: Boolean) extends Movement
  39. final case object Jump extends Movement
  40. final case object RotateRight extends Movement
  41. final case object RotateLeft extends Movement
  42. final case class Props[T: CanMove](
  43. app: GameApp,
  44. movable: T,
  45. playerMovementEventBus: ActorRef[
  46. EventBus.Command[PlayerMovementEvent]
  47. ]
  48. ) {
  49. def create: Behavior[Command] =
  50. Behaviors.setup(ctx => {
  51. ctx.log.info("Hello from MovementActor")
  52. // val playerMovementEventHandler = ctx.spawn(
  53. // PlayerMovementEventHandler(ctx.self),
  54. // "playerMovementEventHandler"
  55. // )
  56. // playerMovementEventBus ! EventBus.Subscribe(playerMovementEventHandler)
  57. new ImMovementActor(ctx, this).receive(State())
  58. })
  59. }
  60. /**
  61. * Internal state of the actor
  62. *
  63. * @param cardinalDir The four directions the character can move
  64. */
  65. final case class State(cardinalDir: CardinalDirection = CardinalDirection())
  66. // def apply[T: CanMove](props: Props[T]): Behavior[Command] =
  67. // Behaviors.setup(ctx => {
  68. // ctx.log.info("Hello from MovementActor")
  69. // val playerMovementEventHandler = ctx.spawn(
  70. // PlayerMovementEventHandler(ctx.self),
  71. // "playerMovementEventHandler"
  72. // )
  73. // props.playerMovementEventBus ! EventBus.Subscribe(
  74. // playerMovementEventHandler
  75. // )
  76. // new ImMovementActor(ctx, props).receive(State())
  77. // })
  78. }
  79. class ImMovementActor[T](
  80. ctx: ActorContext[ImMovementActor.Command],
  81. props: ImMovementActor.Props[T]
  82. ) {
  83. import ImMovementActor._
  84. import Methods._
  85. def receive(
  86. state: ImMovementActor.State
  87. )(implicit cm: CanMove[T]): Behavior[Command] =
  88. Behaviors.receiveMessage {
  89. case m: Movement =>
  90. m match {
  91. case MovedLeft(pressed) =>
  92. props.app.enqueueF(stopIfNotPressed(pressed, props.movable))
  93. receive(state = state.modify(_.cardinalDir.left).setTo(pressed))
  94. case MovedUp(pressed) =>
  95. props.app.enqueueF(stopIfNotPressed(pressed, props.movable))
  96. receive(state = state.modify(_.cardinalDir.up).setTo(pressed))
  97. case MovedRight(pressed) =>
  98. props.app.enqueueF(stopIfNotPressed(pressed, props.movable))
  99. receive(state = state.modify(_.cardinalDir.right).setTo(pressed))
  100. case MovedDown(pressed) =>
  101. props.app.enqueueF(stopIfNotPressed(pressed, props.movable))
  102. receive(state = state.modify(_.cardinalDir.down).setTo(pressed))
  103. case Jump =>
  104. props.app.enqueueF(cm.jump(props.movable))
  105. Behaviors.same
  106. case RotateLeft =>
  107. props.app.enqueueF(cm.rotate(props.movable, RotateDir.Left))
  108. Behaviors.same
  109. case RotateRight =>
  110. props.app.enqueueF(cm.rotate(props.movable, RotateDir.Right))
  111. Behaviors.same
  112. }
  113. case Tick(tpf) =>
  114. val walkDir =
  115. getDirection(state.cardinalDir, ctx.log.trace)
  116. if (walkDir != ImVector3f.ZERO) {
  117. val tmp = walkDir * 25f * tpf
  118. props.app.enqueueF {
  119. cm.move(props.movable, tmp)
  120. }
  121. // props.app
  122. // .enqueueScala { () =>
  123. // 1
  124. // }
  125. // .map(println)(scala.concurrent.ExecutionContext.global)
  126. // monix.eval.Task
  127. // .fromFuture(
  128. // props.app
  129. // .enqueueScala { () =>
  130. // 1
  131. // }
  132. // )
  133. // .flatMap(i => monix.eval.Task(println(i)))
  134. // .runToFuture(monix.execution.Scheduler.Implicits.global)
  135. }
  136. Behaviors.same
  137. }
  138. }
  139. object Methods {
  140. def getDirection(cardinalDir: CardinalDirection, trace: String => Unit) = {
  141. val zero = ImVector3f.ZERO
  142. val dir = cardinalDir
  143. val walkDir = {
  144. val mutWalkDir = new Vector3f()
  145. if (dir.left) {
  146. trace("left")
  147. mutWalkDir += zero +=: new Vector3f(-1, 0, 0)
  148. }
  149. if (dir.right) {
  150. trace("right")
  151. mutWalkDir += zero +=: new Vector3f(1, 0, 0)
  152. }
  153. if (dir.up) {
  154. trace("up")
  155. mutWalkDir += zero +=: new Vector3f(0, 0, -1)
  156. }
  157. if (dir.down) {
  158. trace("down")
  159. mutWalkDir += zero +=: new Vector3f(0, 0, 1)
  160. }
  161. mutWalkDir.immutable
  162. }
  163. walkDir
  164. }
  165. def stopIfNotPressed[T](pressed: Boolean, movable: T)(implicit
  166. cm: CanMove[T]
  167. ) =
  168. if (!pressed) cm.stop(movable)
  169. }