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

package wow.doge.mygame.subsystems.movement
import akka.actor.typed.Behavior
import akka.actor.typed.PostStop
import akka.actor.typed.scaladsl.ActorContext
import akka.actor.typed.scaladsl.Behaviors
import com.jme3.math.Vector3f
import com.jme3.renderer.Camera
import com.jme3.scene.Geometry
import com.softwaremill.quicklens._
import wow.doge.mygame.game.subsystems.movement.CanMove
import wow.doge.mygame.implicits._
import wow.doge.mygame.math.ImVector3f
final case class CardinalDirection(
left: Boolean = false,
right: Boolean = false,
up: Boolean = false,
down: Boolean = false
)
sealed trait RotateDir
object RotateDir {
case object Left extends RotateDir
case object Right extends RotateDir
}
object ImMovementActor {
sealed trait Command
case object Tick extends Command
sealed trait Movement extends Command
final case class MoveLeft(pressed: Boolean) extends Movement
final case class MoveUp(pressed: Boolean) extends Movement
final case class MoveRight(pressed: Boolean) extends Movement
final case class MoveDown(pressed: Boolean) extends Movement
case object Jump extends Movement
class Props(
val enqueueR: Function1[() => Unit, Unit],
// playerMovementEventBus: ActorRef[
// EventBus.Command[PlayerMovementEvent]
// ]
val camera: Camera
) {
def behavior[T: CanMove](movable: T): Behavior[Command] =
Behaviors.setup(ctx =>
new ImMovementActor(ctx, this, movable).receive(State(), new Vector3f)
)
}
/**
* Internal state of the actor
*
* @param cardinalDir The four directions the character can move
*/
final case class State(cardinalDir: CardinalDirection = CardinalDirection())
}
class ImMovementActor[T](
ctx: ActorContext[ImMovementActor.Command],
props: ImMovementActor.Props,
val movable: T
)(implicit cm: CanMove[T]) {
import ImMovementActor._
def receive(
state: ImMovementActor.State,
walkDirBuf: Vector3f
): Behavior[Command] =
Behaviors
.receiveMessage[Command] {
case m: Movement =>
m match {
case MoveLeft(pressed) =>
stopIfNotPressed(pressed)
receive(
state = state.modify(_.cardinalDir.left).setTo(pressed),
walkDirBuf
)
case MoveUp(pressed) =>
stopIfNotPressed(pressed)
receive(
state = state.modify(_.cardinalDir.up).setTo(pressed),
walkDirBuf
)
case MoveRight(pressed) =>
stopIfNotPressed(pressed)
receive(
state = state.modify(_.cardinalDir.right).setTo(pressed),
walkDirBuf
)
case MoveDown(pressed) =>
stopIfNotPressed(pressed)
receive(
state = state.modify(_.cardinalDir.down).setTo(pressed),
walkDirBuf
)
case Jump =>
cm.jump(movable)
Behaviors.same
}
case Tick =>
val camDir =
props.camera.getDirection().clone().normalizeLocal.multLocal(0.6f)
val camLeft =
props.camera.getLeft().clone().normalizeLocal.multLocal(0.4f)
val dir = state.cardinalDir
walkDirBuf.set(0, 0, 0)
if (dir.up) {
ctx.log.traceP("up")
walkDirBuf += camDir
}
if (dir.left) {
ctx.log.traceP("left")
walkDirBuf += camLeft
}
if (dir.right) {
ctx.log.traceP("right")
walkDirBuf += -camLeft
}
if (dir.down) {
ctx.log.traceP("down")
walkDirBuf += -camDir
}
walkDirBuf *= 25f *= (1f / 144)
cm.move(movable, walkDirBuf.immutable, 20f)
Behaviors.same
}
.receiveSignal {
case (_, PostStop) =>
ctx.log.debugP("Stopped")
Behaviors.same
}
def stopIfNotPressed(pressed: Boolean) = if (!pressed) cm.stop(movable)
def getDirection2(
cardinalDir: CardinalDirection
// debug: sourcecode.Text[String] => sourcecode.Text[String]
) = {
val camDir =
props.camera.getDirection().clone().normalizeLocal.multLocal(0.6f)
val camLeft = props.camera.getLeft().clone().normalizeLocal.multLocal(0.4f)
val dir = cardinalDir
val walkDir = {
val mutWalkDir = new Vector3f()
if (dir.up) {
ctx.log.traceP("up")
mutWalkDir += camDir
}
if (dir.left) {
ctx.log.traceP("left")
mutWalkDir += camLeft
}
if (dir.right) {
ctx.log.traceP("right")
mutWalkDir += -camLeft
}
if (dir.down) {
ctx.log.traceP("down")
mutWalkDir += -camDir
}
mutWalkDir.immutable
}
walkDir
}
}
// old/unused
object MovementActor {
sealed trait Command
case object Tick extends Command
sealed trait Movement extends Command
final case class MovedLeft(pressed: Boolean) extends Movement
final case class MovedUp(pressed: Boolean) extends Movement
final case class MovedRight(pressed: Boolean) extends Movement
final case class MovedDown(pressed: Boolean) extends Movement
class Props(val app: com.jme3.app.Application, val geom: Geometry)
/**
* Internal state of the actor
*
* @param cardinalDir Immutable, can be shared as is
* @param walkDirection scratch space to avoid allocations on every tick. Do not share outside the actor
*/
final case class State(
cardinalDir: CardinalDirection = CardinalDirection(),
walkDirection: Vector3f = Vector3f.UNIT_X
)
def apply(props: Props): Behavior[Command] =
Behaviors.setup(ctx => new MovementActor(ctx, props).receive(State()))
}
class MovementActor(
ctx: ActorContext[MovementActor.Command],
props: MovementActor.Props
) {
import MovementActor._
import com.softwaremill.quicklens._
def receive(state: MovementActor.State): Behavior[Command] =
Behaviors.receiveMessage { msg =>
msg match {
case m: Movement =>
m match {
case MovedLeft(pressed) =>
receive(state = state.modify(_.cardinalDir.left).setTo(pressed))
case MovedUp(pressed) =>
receive(state = state.modify(_.cardinalDir.up).setTo(pressed))
case MovedRight(pressed) =>
receive(state = state.modify(_.cardinalDir.right).setTo(pressed))
case MovedDown(pressed) =>
receive(state = state.modify(_.cardinalDir.down).setTo(pressed))
}
case Tick =>
val camDir =
props.app.getCamera.getDirection().clone().multLocal(0.6f)
val camLeft = props.app.getCamera.getLeft().clone().multLocal(0.4f)
val walkDir = state.walkDirection.set(0, 0, 0)
// val walkDir = new Vector3f
val dir = state.cardinalDir
if (dir.up) {
ctx.log.debugP("up")
// ctx.log.debugP(Thread.currentThread().getName())
// walkDir.addLocal(0, 0, -1)
walkDir += camDir
}
if (dir.left) {
ctx.log.debugP("left")
// walkDir.addLocal(-1, 0, 0)
walkDir.addLocal(camLeft)
}
if (dir.right) {
ctx.log.debugP("right")
// walkDir.addLocal(1, 0, 0)
walkDir.addLocal(camLeft.negateLocal())
}
if (dir.down) {
ctx.log.debugP("down")
walkDir.addLocal(camDir.negateLocal())
// walkDir.addLocal(0, 0, 1)
}
// (dir.up, dir.down, dir.left, dir.right) match {
// case (true, false, true, false) =>
// case _ =>
// }
walkDir.multLocal(2f)
// walkDir.multLocal(100f)
// .multLocal(tpf)
// val v = props.geom.getLocalTranslation()
// props.geom.setLocalTranslation(
// (v += walkDir)
// )
props.app.enqueue(new Runnable {
override def run(): Unit = {
// geom.setLocalTranslation(walkDir)
val v = props.geom.getLocalTranslation()
props.geom.setLocalTranslation(
(v += walkDir)
)
}
})
Behaviors.same
// receive(state = state.modify(_.walkDirection).setTo(walkDir))
}
}
}