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.
 
 

201 lines
5.7 KiB

package wow.doge.mygame.game.nodes
import akka.actor.typed.scaladsl.Behaviors
import akka.actor.typed.scaladsl.ActorContext
import wow.doge.mygame.subsystems.movement.ImMovementActor
import wow.doge.mygame.game.GameApp
import akka.actor.typed.ActorRef
import wow.doge.mygame.events.EventBus
import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent
import wow.doge.mygame.subsystems.events.EntityMovementEvent
import akka.actor.typed.scaladsl.TimerScheduler
import akka.actor.typed.Behavior
import scala.concurrent.duration._
import akka.actor.typed.LogOptions
import org.slf4j.event.Level
import com.typesafe.scalalogging.Logger
import akka.actor.typed.SupervisorStrategy
import com.jme3.scene.CameraNode
import wow.doge.mygame.subsystems.events.PlayerCameraEvent
import wow.doge.mygame.game.subsystems.movement.CanMove
import wow.doge.mygame.implicits._
object PlayerActorSupervisor {
sealed trait Command
final case class Props(
app: GameApp,
camNode: CameraNode,
playerMovementEventBus: ActorRef[
EventBus.Command[PlayerMovementEvent]
],
playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]]
) {
def create[T: CanMove](movable: T) =
Behaviors.logMessages(
LogOptions()
.withLevel(Level.TRACE)
.withLogger(
Logger[PlayerActorSupervisor[T]].underlying
),
Behaviors.setup[Command] { ctx =>
ctx.log.info("Hello from PlayerActor")
// spawn children actors
lazy val movementActor =
ctx.spawn(
Behaviors
.supervise(
ImMovementActor
.Props(app, movable, playerMovementEventBus)
.create
)
.onFailure[Exception](SupervisorStrategy.restart),
"playerMovementActor"
)
lazy val playerMovementEventHandler = ctx.spawn(
Behaviors
.supervise(PlayerMovementEventListener(movementActor))
.onFailure[Exception](SupervisorStrategy.restart),
"playerMovementEventHandler"
)
lazy val movementActorTimer = ctx.spawn(
Behaviors
.supervise(MovementActorTimer(movementActor))
.onFailure[Exception](SupervisorStrategy.restart),
"playerMovementActorTimer"
)
lazy val playerCameraHandler = {
ctx.spawn(
Behaviors
.supervise(
PlayerCameraEventListener(camNode, app.enqueueR)
)
.onFailure[Exception](SupervisorStrategy.restart),
"playerCameraHandler"
)
}
//init actors
movementActorTimer ! MovementActorTimer.Start
playerMovementEventBus ! EventBus.Subscribe(
playerMovementEventHandler
)
playerCameraEventBus ! EventBus.Subscribe(playerCameraHandler)
new PlayerActorSupervisor(
ctx,
this,
Children(movementActor, playerMovementEventHandler)
).receive
}
)
}
case class Children(
movementActor: ActorRef[ImMovementActor.Command],
playerMovementEventHandler: ActorRef[
EntityMovementEvent.PlayerMovementEvent
]
)
}
class PlayerActorSupervisor[T: CanMove](
ctx: ActorContext[PlayerActorSupervisor.Command],
props: PlayerActorSupervisor.Props,
children: PlayerActorSupervisor.Children
) {
import PlayerActorSupervisor._
def receive =
Behaviors.receiveMessage[Command] {
case _ =>
// children.movementActor ! ImMovementActor.MovedDown(true)
Behaviors.same
}
}
object MovementActorTimer {
sealed trait Command
final case object Start extends Command
final case object Stop extends Command
private case object Send extends Command
case object TimerKey
def apply(target: ActorRef[ImMovementActor.Command]) =
Behaviors.withTimers[Command] { timers =>
new MovementActorTimer(timers, target).idle
}
}
class MovementActorTimer(
timers: TimerScheduler[MovementActorTimer.Command],
target: ActorRef[ImMovementActor.Command]
) {
import MovementActorTimer._
val idle: Behavior[Command] =
Behaviors.receiveMessage { msg =>
msg match {
case Start =>
timers.startTimerWithFixedDelay(TimerKey, Send, (60f / 144).millis)
active
case _ => Behaviors.unhandled
}
}
val active: Behavior[Command] =
Behaviors.receiveMessage { msg =>
msg match {
case Send =>
target ! ImMovementActor.Tick
Behaviors.same
case Stop =>
timers.cancel(TimerKey)
idle
case _ => Behaviors.unhandled
}
}
}
object GenericTimerActor {
sealed trait Command
final case object Start extends Command
final case object Stop extends Command
private case object Send extends Command
case object TimerKey
case class Props[T](
target: ActorRef[T],
messageToSend: T,
timeInterval: FiniteDuration
) {
val create = Behaviors.withTimers[Command] { timers =>
new GenericTimerActor(timers, this).idle
}
}
}
class GenericTimerActor[T](
timers: TimerScheduler[GenericTimerActor.Command],
props: GenericTimerActor.Props[T]
) {
import GenericTimerActor._
val idle: Behavior[Command] =
Behaviors.receiveMessage {
case Start =>
timers.startTimerWithFixedDelay(TimerKey, Send, props.timeInterval)
active
case _ => Behaviors.unhandled
}
val active: Behavior[Command] =
Behaviors.receiveMessagePartial {
case Send =>
props.target ! props.messageToSend
Behaviors.same
case Stop =>
timers.cancel(TimerKey)
idle
}
}