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.
 
 

240 lines
7.2 KiB

package wow.doge.mygame.game.entities
import akka.actor.typed.ActorRef
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.shape.Box
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.EventsModule.GameEventBus
import wow.doge.mygame.subsystems.events.PlayerEvent
import wow.doge.mygame.subsystems.events.TickEvent
import wow.doge.mygame.subsystems.movement.ImMovementActor
import wow.doge.mygame.utils.AkkaUtils
import com.softwaremill.macwire._
object PlayerControllerTags {
sealed trait PlayerTag
sealed trait PlayerCameraNode
sealed trait PlayerCameraPivotNode
}
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,
playerEventBus: GameEventBus[PlayerEvent],
playerPhysicsControl: BetterCharacterControl,
appScheduler: monix.execution.Scheduler,
playerNode: Node @@ PlayerControllerTags.PlayerTag,
cameraNode: CameraNode @@ PlayerControllerTags.PlayerCameraNode,
cameraPivotNode: Node @@ PlayerControllerTags.PlayerCameraPivotNode,
tickEventBus: GameEventBus[TickEvent],
camera: Camera
)(implicit
spawnProtocol: ActorRef[SpawnProtocol.Command],
timeout: Timeout,
scheduler: Scheduler
) {
val playerActorBehavior = {
val movementActorBeh = new ImMovementActor.Props(
enqueueR,
playerPhysicsControl,
camera
).behavior
val cameraActorBeh = new PlayerCameraActor.Props(
cameraPivotNode,
enqueueR,
playerNode.getWorldTranslation _
).behavior
new PlayerActorSupervisor.Props(
playerEventBus,
tickEventBus,
movementActorBeh,
cameraActorBeh
).behavior(playerPhysicsControl)
}
val create: IO[Error, Unit] =
(for {
playerActor <- AkkaUtils.spawnActorL(
playerActorBehavior,
"playerActorSupervisor"
)
_ <- Task(rootNode += playerNode)
_ <- IO {
physicsSpace += playerNode
physicsSpace += playerPhysicsControl
// rootNode += cameraNode
cameraPivotNode += cameraNode
// playerNode += cameraPivotNode
rootNode += cameraPivotNode
}
} yield ())
.onErrorHandleWith(e => IO.raiseError(GenericError(e.getMessage())))
// .executeOn(appScheduler)
}
def apply(
app: SimpleAppExt,
modelPath: os.RelPath,
cam: Camera
)(assetManager: AssetManager, bulletAppState: BulletAppState) = {
val playerPos = ImVector3f.ZERO
val playerPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f)
.withJumpForce(ImVector3f(0, 5f, 0))
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"
)
// new CameraControl(cam) {
// override def controlUpdate(tpf: Float) = {
// this.getCamera().setRotation(spatial.getWorldRotation())
// cameraPivotNode.setLocalTranslation(
// playerNode.getWorldTranslation()
// )
// this.getCamera().setLocation(spatial.getWorldTranslation())
// }
// }
def defaultCamerNode(
cam: Camera,
playerNode: Node,
cameraPivotNode: Node,
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 {}
// 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)
// )