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.
 
 

220 lines
6.6 KiB

package wow.doge.mygame.game.nodes
import com.jme3.scene.Node
import com.jme3.scene.CameraNode
import com.jme3.scene.Geometry
import com.jme3.renderer.Camera
import com.jme3.asset.AssetManager
import wow.doge.mygame.state.MyMaterial
import com.jme3.scene.control.CameraControl.ControlDirection
import com.jme3.scene.shape.Box
import com.jme3.bullet.control.BetterCharacterControl
import com.jme3.bullet.BulletAppState
import wow.doge.mygame.game.GameApp
import wow.doge.mygame.implicits._
import wow.doge.mygame.math.ImVector3f
import com.jme3.math.FastMath
import monix.bio.IO
import cats.implicits._
import akka.actor.typed.ActorRef
import wow.doge.mygame.events.EventBus
import akka.actor.typed.SpawnProtocol
import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent
import wow.doge.mygame.subsystems.movement.ImMovementActor
import io.odin.Logger
import akka.util.Timeout
import monix.bio.Task
import akka.actor.typed.Scheduler
import akka.actor.typed.Props
import com.softwaremill.tagging._
import wow.doge.mygame.subsystems.events.PlayerCameraEvent
import wow.doge.mygame.utils.AkkaUtils
// class PlayerNode(val name: String) extends Node(name) {}
sealed trait Player
sealed trait PlayerCameraNode
object PlayerController {
sealed trait Error
case class GenericError(reason: String) extends Error
class Props(
app: GameApp,
loggerL: Logger[Task],
assetManager: AssetManager,
bulletAppState: BulletAppState,
initialPlayerPos: ImVector3f = ImVector3f.ZERO,
modelPath: os.RelPath,
spawnProtocol: ActorRef[SpawnProtocol.Command],
playerMovementEventBus: ActorRef[
EventBus.Command[PlayerMovementEvent]
],
playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]],
_playerPhysicsControl: Option[BetterCharacterControl],
_playerNode: Option[Node with Player] = None,
_cameraNode: Option[CameraNode with PlayerCameraNode] = None
)(implicit timeout: Timeout, scheduler: Scheduler) {
import Defaults._
import Methods._
val create: IO[Error, Unit] =
// IO.raiseError(GenericError("not implemented yet"))
(for {
camNode <- IO(
_cameraNode.getOrElse(defaultCamerNode(app.camera, initialPlayerPos))
)
playerPhysicsControl <- IO(
_playerPhysicsControl
.getOrElse(defaultPlayerPhysicsControl)
.taggedWith[Player]
)
playerNode <- IO.fromEither(
_playerNode.fold(
defaultPlayerNode(
assetManager,
modelPath,
initialPlayerPos,
camNode,
playerPhysicsControl
)
)(_.asRight)
)
playerActor <- AkkaUtils.spawnActorL(
spawnProtocol,
"playerActorSupervisor",
new PlayerActorSupervisor.Props(
app,
camNode,
playerMovementEventBus,
playerCameraEventBus
).create(playerPhysicsControl)
)
_ <- IO {
bulletAppState.physicsSpace += playerNode
bulletAppState.physicsSpace += playerPhysicsControl
}
_ <- IO(app.rootNode += playerNode)
} yield ())
.onErrorHandleWith(e => IO.raiseError(GenericError(e.getMessage())))
.executeOn(app.scheduler)
}
def apply(
app: GameApp,
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 {
lazy val defaultMesh = {
val b = Box(1, 1, 1)
val geom = Geometry("playerMesh", 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)
)
lazy val defaultPlayerPhysicsControl =
new BetterCharacterControl(1.5f, 6f, 1f)
.withJumpForce(ImVector3f(0, 5f, 0))
}
object Methods {
def spawnMovementActor(
app: GameApp,
spawnProtocol: ActorRef[SpawnProtocol.Command],
movable: BetterCharacterControl @@ Player,
playerMovementEventBus: ActorRef[
EventBus.Command[PlayerMovementEvent]
],
loggerL: Logger[Task]
)(implicit timeout: Timeout, scheduler: Scheduler) =
spawnProtocol.askL[ActorRef[ImMovementActor.Command]](
SpawnProtocol.Spawn(
ImMovementActor.Props(app, movable, playerMovementEventBus).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,
// _
// )
// )
}
// spawnPlayerActor(
// app,
// spawnProtocol,
// playerPhysicsControl,
// playerMovementEventBus
// )