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.
 
 

248 lines
7.8 KiB

package wow.doge.mygame.game.entities
import akka.actor.typed.ActorRef
import akka.actor.typed.Props
import akka.actor.typed.Scheduler
import akka.actor.typed.SpawnProtocol
import akka.util.Timeout
import cats.implicits._
import com.jme3.asset.AssetManager
import com.jme3.bullet.BulletAppState
import com.jme3.bullet.PhysicsSpace
import com.jme3.bullet.control.BetterCharacterControl
import com.jme3.math.FastMath
import com.jme3.renderer.Camera
import com.jme3.scene.CameraNode
import com.jme3.scene.Geometry
import com.jme3.scene.Node
import com.jme3.scene.control.CameraControl.ControlDirection
import com.jme3.scene.shape.Box
import com.softwaremill.macwire._
import com.softwaremill.tagging._
import io.odin.Logger
import monix.bio.IO
import monix.bio.Task
import wow.doge.mygame.game.GameAppTags
import wow.doge.mygame.game.SimpleAppExt
import wow.doge.mygame.implicits._
import wow.doge.mygame.math.ImVector3f
import wow.doge.mygame.state.MyMaterial
import wow.doge.mygame.subsystems.events.EventBus
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
import wow.doge.mygame.subsystems.events.PlayerCameraEvent
import wow.doge.mygame.subsystems.events.PlayerMovementEvent
import wow.doge.mygame.subsystems.events.TickEvent
import wow.doge.mygame.subsystems.movement.ImMovementActor
import wow.doge.mygame.utils.AkkaUtils
object PlayerControllerTags {
sealed trait PlayerTag
sealed trait PlayerCameraNode
}
object PlayerController {
sealed trait Error
case class CouldNotCreatePlayerNode(reason: String) extends Error
case class GenericError(reason: String) extends Error
class Props(
enqueueR: Function1[() => Unit, Unit],
rootNode: Node @@ GameAppTags.RootNode,
loggerL: Logger[Task],
physicsSpace: PhysicsSpace,
initialPlayerPos: ImVector3f = ImVector3f.ZERO,
spawnProtocol: ActorRef[SpawnProtocol.Command],
playerMovementEventBus: ActorRef[
EventBus.Command[PlayerMovementEvent]
],
playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]],
playerPhysicsControl: BetterCharacterControl,
appScheduler: monix.execution.Scheduler,
playerNode: Node @@ PlayerControllerTags.PlayerTag,
cameraNode: CameraNode @@ PlayerControllerTags.PlayerCameraNode,
tickEventBus: GameEventBus[TickEvent]
)(implicit timeout: Timeout, scheduler: Scheduler) {
val create: IO[Error, Unit] =
(for {
playerActor <- AkkaUtils.spawnActorL(
spawnProtocol,
"playerActorSupervisor",
new PlayerActorSupervisor.Props(
playerMovementEventBus,
playerCameraEventBus,
tickEventBus,
ImMovementActor
.Props(enqueueR, playerPhysicsControl)
.create,
wireWith(PlayerCameraEventListener.apply _)
).create(playerPhysicsControl)
)
_ <- IO {
physicsSpace += playerNode
physicsSpace += playerPhysicsControl
}
_ <- Task(rootNode += playerNode)
} yield ())
.onErrorHandleWith(e => IO.raiseError(GenericError(e.getMessage())))
.executeOn(appScheduler)
}
def apply(
app: SimpleAppExt,
modelPath: os.RelPath,
cam: Camera
)(assetManager: AssetManager, bulletAppState: BulletAppState) = {
lazy val playerPos = ImVector3f.ZERO
lazy val playerPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f)
.withJumpForce(ImVector3f(0, 5f, 0))
lazy val playerNode = new Node("PlayerNode")
.withChildren(
assetManager
.loadModel(modelPath)
.asInstanceOf[Node]
.withRotate(0, FastMath.PI, 0)
)
.withLocalTranslation(playerPos)
.withControl(playerPhysicsControl)
{
bulletAppState.physicsSpace += playerNode
bulletAppState.physicsSpace += playerPhysicsControl
}
{
app.rootNode += playerNode
}
playerPhysicsControl
}
object Defaults {
def defaultMesh = {
val b = Box(1, 1, 1)
val geom = Geometry("playerGeom", b)
geom
}
def defaultTexture(assetManager: AssetManager) =
MyMaterial(
assetManager = assetManager,
path = os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
)
def defaultCamerNode(cam: Camera, playerPos: ImVector3f) =
new CameraNode("CameraNode", cam)
.withControlDir(ControlDirection.SpatialToCamera)
.withLocalTranslation(ImVector3f(0, 1.5f, 10))
.withLookAt(playerPos, ImVector3f.UNIT_Y)
def defaultPlayerNode(
assetManager: AssetManager,
modelPath: os.RelPath,
playerPos: ImVector3f,
camNode: CameraNode,
playerPhysicsControl: BetterCharacterControl
) =
Either
.catchNonFatal(
Node("PlayerNode")
.withChildren(
camNode,
assetManager
.loadModel(modelPath)
.asInstanceOf[Node]
.withRotate(0, FastMath.PI, 0)
)
.withLocalTranslation(playerPos)
.withControl(playerPhysicsControl)
)
.map(_.taggedWith[PlayerControllerTags.PlayerTag])
.leftMap(e => PlayerController.CouldNotCreatePlayerNode(e.getMessage()))
def defaultNpcNode(
assetManager: AssetManager,
// modelPath: os.RelPath,
initialPos: ImVector3f,
npcPhysicsControl: BetterCharacterControl,
npcName: String
) =
Either
.catchNonFatal(
Node(npcName)
.withChildren(
// assetManager
// .loadModel(modelPath)
// .asInstanceOf[Node]
// .withRotate(0, FastMath.PI, 0)
defaultMesh.withMaterial(defaultTexture(assetManager))
)
.withLocalTranslation(initialPos)
.withControl(npcPhysicsControl)
)
// .map(_.taggedWith[PlayerControllerTags.PlayerTag])
// .leftMap(e => PlayerController.CouldNotCreatePlayerNode(e.getMessage()))
def defaultPlayerPhysicsControl =
new BetterCharacterControl(1.5f, 6f, 1f)
.withJumpForce(ImVector3f(0, 5f, 0))
}
}
object Methods {
def spawnMovementActor(
enqueueR: Function1[() => Unit, Unit],
spawnProtocol: ActorRef[SpawnProtocol.Command],
movable: BetterCharacterControl @@ PlayerControllerTags.PlayerTag,
playerMovementEventBus: ActorRef[
EventBus.Command[PlayerMovementEvent]
],
loggerL: Logger[Task]
)(implicit timeout: Timeout, scheduler: Scheduler) =
spawnProtocol.askL[ActorRef[ImMovementActor.Command]](
SpawnProtocol.Spawn(
ImMovementActor.Props(enqueueR, movable).create,
"imMovementActor",
Props.empty,
_
)
)
// def spawnPlayerActor(
// app: GameApp,
// spawnProtocol: ActorRef[SpawnProtocol.Command],
// movable: BetterCharacterControl @@ Player,
// playerMovementEventBus: ActorRef[
// EventBus.Command[PlayerMovementEvent]
// ]
// )(implicit timeout: Timeout, scheduler: Scheduler) =
// spawnProtocol.askL[ActorRef[PlayerActorSupervisor.Command]](
// SpawnProtocol.Spawn(
// new PlayerActorSupervisor.Props(
// app,
// movable,
// playerMovementEventBus
// ).create,
// "playerActor",
// Props.empty,
// _
// )
// )
}
// camNode <- IO(
// _cameraNode.getOrElse(defaultCamerNode(camera, initialPlayerPos))
// )
// playerPhysicsControl <- IO(
// _playerPhysicsControl
// .getOrElse(defaultPlayerPhysicsControl)
// .taggedWith[PlayerTag]
// )
// playerNode <- IO.fromEither(
// _playerNode.fold(
// defaultPlayerNode(
// assetManager,
// modelPath,
// initialPlayerPos,
// camNode,
// playerPhysicsControl
// )
// )(_.asRight)
// )