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.
 
 

190 lines
5.8 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 com.jme3.bullet.BulletAppState
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.Spatial
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.AppError
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.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 wow.doge.mygame.utils.wrappers.jme._
object PlayerControllerTags {
sealed trait PlayerTag
sealed trait PlayerCameraNode
sealed trait PlayerCameraPivotNode
}
object PlayerController {
sealed trait Error
case class CouldNotCreatePlayerModel(reason: AssetManager.Error) extends Error
class Props(
enqueueR: Function1[() => Unit, Unit],
rootNode: AppNode2 @@ GameAppTags.RootNode,
loggerL: Logger[Task],
// physicsSpace: com.jme3.bullet.PhysicsSpace,
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,
camera
).behavior(playerPhysicsControl)
val cameraActorBeh = new PlayerCameraActor.Props(
cameraPivotNode,
enqueueR,
playerNode.getWorldTranslation _
).behavior
new PlayerActorSupervisor.Props(
playerEventBus,
tickEventBus,
movementActorBeh,
cameraActorBeh
).behavior
}
val create: IO[AppError, ActorRef[PlayerActorSupervisor.Command]] =
(for {
playerActor <-
AkkaUtils.spawnActorL(playerActorBehavior, "playerActorSupervisor")
_ <- (for {
_ <- rootNode += playerNode
_ <- physicsSpace += playerNode
_ <- physicsSpace += playerPhysicsControl
_ = cameraPivotNode += cameraNode
_ <- rootNode += cameraPivotNode
} yield ()).mapError(AppError.AppNodeError)
} yield playerActor)
}
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.UnitY)
def defaultPlayerNode(
playerPos: ImVector3f,
playerModel: Node,
// camNode: CameraNode,
playerPhysicsControl: BetterCharacterControl
) =
Node("PlayerNode")
.withChildren(playerModel)
.withLocalTranslation(playerPos)
.withControl(playerPhysicsControl)
.taggedWith[PlayerControllerTags.PlayerTag]
def defaultNpcNode(
// assetManager: AssetManager,
npcModel: Spatial,
initialPos: ImVector3f,
npcPhysicsControl: BetterCharacterControl,
npcName: String
) =
// Either
// .catchNonFatal(
Node(npcName)
.withChildren(
// defaultMesh.withMaterial(defaultTexture(assetManager))
npcModel
)
.withLocalTranslation(initialPos)
.withControl(npcPhysicsControl)
// )
def defaultPlayerPhysicsControl =
new BetterCharacterControl(1.5f, 6f, 1f)
.withJumpForce(ImVector3f(0, 5f, 0))
}
}