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.

278 lines
8.3 KiB

3 years ago
4 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
3 years ago
3 years ago
4 years ago
3 years ago
  1. package wow.doge.mygame.subsystems.movement
  2. import akka.actor.typed.Behavior
  3. import akka.actor.typed.PostStop
  4. import akka.actor.typed.scaladsl.ActorContext
  5. import akka.actor.typed.scaladsl.Behaviors
  6. import com.jme3.math.Vector3f
  7. import com.jme3.renderer.Camera
  8. import com.jme3.scene.Geometry
  9. import com.softwaremill.quicklens._
  10. import wow.doge.mygame.game.subsystems.movement.CanMove
  11. import wow.doge.mygame.implicits._
  12. import wow.doge.mygame.math.ImVector3f
  13. final case class CardinalDirection(
  14. left: Boolean = false,
  15. right: Boolean = false,
  16. up: Boolean = false,
  17. down: Boolean = false
  18. )
  19. sealed trait RotateDir
  20. object RotateDir {
  21. case object Left extends RotateDir
  22. case object Right extends RotateDir
  23. }
  24. object ImMovementActor {
  25. sealed trait Command
  26. case object Tick extends Command
  27. sealed trait Movement extends Command
  28. final case class MoveLeft(pressed: Boolean) extends Movement
  29. final case class MoveUp(pressed: Boolean) extends Movement
  30. final case class MoveRight(pressed: Boolean) extends Movement
  31. final case class MoveDown(pressed: Boolean) extends Movement
  32. case object Jump extends Movement
  33. class Props(
  34. val enqueueR: Function1[() => Unit, Unit],
  35. // playerMovementEventBus: ActorRef[
  36. // EventBus.Command[PlayerMovementEvent]
  37. // ]
  38. val camera: Camera
  39. ) {
  40. def behavior[T: CanMove](movable: T): Behavior[Command] =
  41. Behaviors.setup(ctx =>
  42. new ImMovementActor(ctx, this, movable).receive(State(), new Vector3f)
  43. )
  44. }
  45. /**
  46. * Internal state of the actor
  47. *
  48. * @param cardinalDir The four directions the character can move
  49. */
  50. final case class State(cardinalDir: CardinalDirection = CardinalDirection())
  51. }
  52. class ImMovementActor[T](
  53. ctx: ActorContext[ImMovementActor.Command],
  54. props: ImMovementActor.Props,
  55. val movable: T
  56. )(implicit cm: CanMove[T]) {
  57. import ImMovementActor._
  58. def receive(
  59. state: ImMovementActor.State,
  60. walkDirBuf: Vector3f
  61. ): Behavior[Command] =
  62. Behaviors
  63. .receiveMessage[Command] {
  64. case m: Movement =>
  65. m match {
  66. case MoveLeft(pressed) =>
  67. stopIfNotPressed(pressed)
  68. receive(
  69. state = state.modify(_.cardinalDir.left).setTo(pressed),
  70. walkDirBuf
  71. )
  72. case MoveUp(pressed) =>
  73. stopIfNotPressed(pressed)
  74. receive(
  75. state = state.modify(_.cardinalDir.up).setTo(pressed),
  76. walkDirBuf
  77. )
  78. case MoveRight(pressed) =>
  79. stopIfNotPressed(pressed)
  80. receive(
  81. state = state.modify(_.cardinalDir.right).setTo(pressed),
  82. walkDirBuf
  83. )
  84. case MoveDown(pressed) =>
  85. stopIfNotPressed(pressed)
  86. receive(
  87. state = state.modify(_.cardinalDir.down).setTo(pressed),
  88. walkDirBuf
  89. )
  90. case Jump =>
  91. cm.jump(movable)
  92. Behaviors.same
  93. }
  94. case Tick =>
  95. val camDir =
  96. props.camera.getDirection().clone().normalizeLocal.multLocal(0.6f)
  97. val camLeft =
  98. props.camera.getLeft().clone().normalizeLocal.multLocal(0.4f)
  99. val dir = state.cardinalDir
  100. walkDirBuf.set(0, 0, 0)
  101. if (dir.up) {
  102. ctx.log.traceP("up")
  103. walkDirBuf += camDir
  104. }
  105. if (dir.left) {
  106. ctx.log.traceP("left")
  107. walkDirBuf += camLeft
  108. }
  109. if (dir.right) {
  110. ctx.log.traceP("right")
  111. walkDirBuf += -camLeft
  112. }
  113. if (dir.down) {
  114. ctx.log.traceP("down")
  115. walkDirBuf += -camDir
  116. }
  117. walkDirBuf *= 25f *= (1f / 144)
  118. cm.move(movable, walkDirBuf.immutable, 20f)
  119. Behaviors.same
  120. }
  121. .receiveSignal {
  122. case (_, PostStop) =>
  123. ctx.log.debugP("Stopped")
  124. Behaviors.same
  125. }
  126. def stopIfNotPressed(pressed: Boolean) = if (!pressed) cm.stop(movable)
  127. def getDirection2(
  128. cardinalDir: CardinalDirection
  129. // debug: sourcecode.Text[String] => sourcecode.Text[String]
  130. ) = {
  131. val camDir =
  132. props.camera.getDirection().clone().normalizeLocal.multLocal(0.6f)
  133. val camLeft = props.camera.getLeft().clone().normalizeLocal.multLocal(0.4f)
  134. val dir = cardinalDir
  135. val walkDir = {
  136. val mutWalkDir = new Vector3f()
  137. if (dir.up) {
  138. ctx.log.traceP("up")
  139. mutWalkDir += camDir
  140. }
  141. if (dir.left) {
  142. ctx.log.traceP("left")
  143. mutWalkDir += camLeft
  144. }
  145. if (dir.right) {
  146. ctx.log.traceP("right")
  147. mutWalkDir += -camLeft
  148. }
  149. if (dir.down) {
  150. ctx.log.traceP("down")
  151. mutWalkDir += -camDir
  152. }
  153. mutWalkDir.immutable
  154. }
  155. walkDir
  156. }
  157. }
  158. // old/unused
  159. object MovementActor {
  160. sealed trait Command
  161. case object Tick extends Command
  162. sealed trait Movement extends Command
  163. final case class MovedLeft(pressed: Boolean) extends Movement
  164. final case class MovedUp(pressed: Boolean) extends Movement
  165. final case class MovedRight(pressed: Boolean) extends Movement
  166. final case class MovedDown(pressed: Boolean) extends Movement
  167. class Props(val app: com.jme3.app.Application, val geom: Geometry)
  168. /**
  169. * Internal state of the actor
  170. *
  171. * @param cardinalDir Immutable, can be shared as is
  172. * @param walkDirection scratch space to avoid allocations on every tick. Do not share outside the actor
  173. */
  174. final case class State(
  175. cardinalDir: CardinalDirection = CardinalDirection(),
  176. walkDirection: Vector3f = Vector3f.UNIT_X
  177. )
  178. def apply(props: Props): Behavior[Command] =
  179. Behaviors.setup(ctx => new MovementActor(ctx, props).receive(State()))
  180. }
  181. class MovementActor(
  182. ctx: ActorContext[MovementActor.Command],
  183. props: MovementActor.Props
  184. ) {
  185. import MovementActor._
  186. import com.softwaremill.quicklens._
  187. def receive(state: MovementActor.State): Behavior[Command] =
  188. Behaviors.receiveMessage { msg =>
  189. msg match {
  190. case m: Movement =>
  191. m match {
  192. case MovedLeft(pressed) =>
  193. receive(state = state.modify(_.cardinalDir.left).setTo(pressed))
  194. case MovedUp(pressed) =>
  195. receive(state = state.modify(_.cardinalDir.up).setTo(pressed))
  196. case MovedRight(pressed) =>
  197. receive(state = state.modify(_.cardinalDir.right).setTo(pressed))
  198. case MovedDown(pressed) =>
  199. receive(state = state.modify(_.cardinalDir.down).setTo(pressed))
  200. }
  201. case Tick =>
  202. val camDir =
  203. props.app.getCamera.getDirection().clone().multLocal(0.6f)
  204. val camLeft = props.app.getCamera.getLeft().clone().multLocal(0.4f)
  205. val walkDir = state.walkDirection.set(0, 0, 0)
  206. // val walkDir = new Vector3f
  207. val dir = state.cardinalDir
  208. if (dir.up) {
  209. ctx.log.debugP("up")
  210. // ctx.log.debugP(Thread.currentThread().getName())
  211. // walkDir.addLocal(0, 0, -1)
  212. walkDir += camDir
  213. }
  214. if (dir.left) {
  215. ctx.log.debugP("left")
  216. // walkDir.addLocal(-1, 0, 0)
  217. walkDir.addLocal(camLeft)
  218. }
  219. if (dir.right) {
  220. ctx.log.debugP("right")
  221. // walkDir.addLocal(1, 0, 0)
  222. walkDir.addLocal(camLeft.negateLocal())
  223. }
  224. if (dir.down) {
  225. ctx.log.debugP("down")
  226. walkDir.addLocal(camDir.negateLocal())
  227. // walkDir.addLocal(0, 0, 1)
  228. }
  229. // (dir.up, dir.down, dir.left, dir.right) match {
  230. // case (true, false, true, false) =>
  231. // case _ =>
  232. // }
  233. walkDir.multLocal(2f)
  234. // walkDir.multLocal(100f)
  235. // .multLocal(tpf)
  236. // val v = props.geom.getLocalTranslation()
  237. // props.geom.setLocalTranslation(
  238. // (v += walkDir)
  239. // )
  240. props.app.enqueue(new Runnable {
  241. override def run(): Unit = {
  242. // geom.setLocalTranslation(walkDir)
  243. val v = props.geom.getLocalTranslation()
  244. props.geom.setLocalTranslation(
  245. (v += walkDir)
  246. )
  247. }
  248. })
  249. Behaviors.same
  250. // receive(state = state.modify(_.walkDirection).setTo(walkDir))
  251. }
  252. }
  253. }