forked from nova/jmonkey-test
dunno lol
This commit is contained in:
parent
85a28b3c39
commit
a2a328d078
@ -89,7 +89,9 @@ lazy val root = (project in file(".")).settings(
|
||||
"com.github.valskalla" %% "odin-slf4j" % "0.8.1",
|
||||
"com.softwaremill.quicklens" %% "quicklens" % "1.6.1",
|
||||
"org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.0-RC1",
|
||||
"com.typesafe.scala-logging" %% "scala-logging" % "3.9.2"
|
||||
"com.typesafe.scala-logging" %% "scala-logging" % "3.9.2",
|
||||
"io.circe" %% "circe-config" % "0.8.0",
|
||||
"com.beachape" %% "enumeratum-circe" % "1.6.1"
|
||||
),
|
||||
// Determine OS version of JavaFX binaries
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
package wow.doge.mygame
|
||||
|
||||
import monix.bio.Task
|
||||
import cats.effect.Resource
|
||||
import io.odin.syntax._
|
||||
|
||||
@ -10,15 +9,20 @@ import com.softwaremill.macwire._
|
||||
import scala.concurrent.duration._
|
||||
import monix.bio.BIOApp
|
||||
import monix.bio.UIO
|
||||
import monix.bio.IO
|
||||
import io.odin._
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.game.GameAppResource
|
||||
import io.odin.json.Formatter
|
||||
|
||||
import wow.doge.mygame.game.GameSystemsInitializer
|
||||
import wow.doge.mygame.subsystems.events.EventsModule2
|
||||
import wow.doge.mygame.implicits._
|
||||
import com.jme3.bullet.BulletAppState
|
||||
import akka.util.Timeout
|
||||
import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource
|
||||
import ammonite.runtime.tools.time
|
||||
object Main extends BIOApp with MainModule {
|
||||
import java.util.logging.{Logger => JLogger, Level}
|
||||
JLogger.getLogger("").setLevel(Level.SEVERE)
|
||||
implicit val timeout = Timeout(1.second)
|
||||
|
||||
def appResource =
|
||||
for {
|
||||
@ -29,23 +33,70 @@ object Main extends BIOApp with MainModule {
|
||||
Formatter.json
|
||||
).withAsync(timeWindow = 1.milliseconds)
|
||||
jmeScheduler <- jMESchedulerResource
|
||||
actorSystem <- actorSystemResource2(logger)
|
||||
scriptCacheActor <- new ScriptSystemResource(os.pwd, actorSystem)(
|
||||
timeout,
|
||||
actorSystem.scheduler
|
||||
).make
|
||||
// akkaScheduler = actorSystemResource2.scheduler
|
||||
// consoleTextArea <- Resource.liftF(Task(new TextArea()))
|
||||
// consoleStream <- wireWith(JFXConsoleStream.textAreaStream _)
|
||||
(gameApp, gameAppFib) <- {
|
||||
// new BulletAppState()
|
||||
// bas.setThreadingType(Thr)
|
||||
// gameAppResource(new StatsAppState())
|
||||
wire[GameAppResource].make
|
||||
wire[GameAppResource].get
|
||||
}
|
||||
app = gameApp
|
||||
inputManager = gameApp.inputManager
|
||||
assetManager = gameApp.assetManager
|
||||
bulletAppState = new BulletAppState()
|
||||
(playerMovementEventBus, playerCameraEventBus) <- new EventsModule2(
|
||||
actorSystem
|
||||
).resource
|
||||
// b1 = playerMovementEventBus
|
||||
// b2 = playerCameraEventBus
|
||||
|
||||
// playerPos = ImVector3f.ZERO
|
||||
// playerNode = None.taggedWith[Player]
|
||||
// modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o".taggedWith[Player]
|
||||
// playerController <- Resource.liftF {
|
||||
// implicit val s = actorSystem.scheduler
|
||||
// wire[PlayerController.Props].create.onErrorHandle(err =>
|
||||
// logger.error(err.toString())
|
||||
// )
|
||||
// }
|
||||
|
||||
// _ <- Resource.liftF(IO(JMERunner.runner = gameApp))
|
||||
// _ <- Resource.liftF(IO {
|
||||
// new ActorSystemModule {}
|
||||
// })
|
||||
actorSystem <- wireWith(actorSystemResource _)
|
||||
_ <- Resource.liftF(
|
||||
gameApp.enqueueT(actorSystem ! RootActor.Start(actorSystem.scheduler))
|
||||
)
|
||||
// actorSystem <- wireWith(actorSystemResource _)
|
||||
|
||||
// rootActor <- rootActorResource(logger, gameApp, schedulers, as2)
|
||||
// _ <- Resource.liftF(
|
||||
// gameApp.enqueueT(actorSystem ! RootActor.Start(actorSystem.scheduler))
|
||||
// )
|
||||
// gameSystemsInitializer = new GameSystemsInitializer(
|
||||
// actorSystem,
|
||||
// logger,
|
||||
// playerMovementEventBus,
|
||||
// playerCameraEventBus
|
||||
// )(gameApp)
|
||||
|
||||
gameSystemsInitializerFib <- Resource.make(
|
||||
logger.info("creating game systems initializer") >>
|
||||
gameApp
|
||||
.enqueueL(() => wire[GameSystemsInitializer])
|
||||
.start
|
||||
)(c => logger.info("destroying game systems initializer") >> c.cancel)
|
||||
_ <- Resource.liftF(gameSystemsInitializerFib.join.flatMap(_.init))
|
||||
|
||||
// .runAsync {
|
||||
// case Left(err) => println(err)
|
||||
// case _ =>
|
||||
// }(schedulers.async)
|
||||
|
||||
// _ <- Resource.liftF {
|
||||
// Task {
|
||||
// implicit val sched = actorSystem.scheduler
|
||||
@ -73,6 +124,18 @@ object Main extends BIOApp with MainModule {
|
||||
// (_ => IO(gameApp.stop(() => actorSystem ! RootActor.Stop)))
|
||||
} yield (gameAppFib)
|
||||
|
||||
// def createPlayerController(
|
||||
// playerMovementEventBus: ActorRef[
|
||||
// EventBus.Command[PlayerMovementEvent]
|
||||
// ],
|
||||
// playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]]
|
||||
// ): IO[PlayerController.Error, Unit] = {
|
||||
// val playerPos = ImVector3f.ZERO
|
||||
// val playerNode = None.taggedWith[Player]
|
||||
// val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
|
||||
// wire[PlayerController.Props].create
|
||||
// }
|
||||
|
||||
def run(args: List[String]): UIO[ExitCode] = {
|
||||
|
||||
// Console.withOut(
|
||||
|
@ -13,9 +13,11 @@ import monix.reactive.MulticastStrategy
|
||||
import monix.reactive.Observable
|
||||
import monix.execution.atomic.Atomic
|
||||
import scala.collection.immutable.Queue
|
||||
import wow.doge.mygame.executors.Schedulers
|
||||
|
||||
class GameApp(
|
||||
// actorSystem: ActorSystem[SpawnProtocol.Command],
|
||||
schedulers: Schedulers,
|
||||
appStates: AppState*
|
||||
) extends SimpleApplication(appStates: _*) {
|
||||
import GameApp._
|
||||
@ -27,7 +29,7 @@ class GameApp(
|
||||
|
||||
private val tickSubject =
|
||||
ConcurrentSubject[Float](multicast = MulticastStrategy.publish)(
|
||||
monix.execution.Scheduler.Implicits.global
|
||||
schedulers.async
|
||||
)
|
||||
// (scheduler)
|
||||
|
||||
|
@ -39,21 +39,21 @@ object GameAppActor {
|
||||
Behaviors.setup[Command] { ctx =>
|
||||
ctx.log.info("Hello from GameAppActor")
|
||||
|
||||
{
|
||||
implicit val s = schedulers.async
|
||||
// {
|
||||
// implicit val s = schedulers.async
|
||||
|
||||
val initializer: GameSystemsInitializer = wire[GameSystemsInitializer]
|
||||
// val initializer: GameSystemsInitializer = wire[GameSystemsInitializer]
|
||||
|
||||
schedulers.async.execute(() => initializer.init.runAsyncAndForget)
|
||||
// schedulers.async.execute(() => initializer.init.runAsyncAndForget)
|
||||
|
||||
// ctx.pipeToSelf(application.timed.runToFuture) {
|
||||
// case Failure(exception) =>
|
||||
// ApplicationStartFailed(exception.getMessage())
|
||||
// case Success(value) =>
|
||||
// println("here applications started")
|
||||
// ApplicationStarted
|
||||
// // ctx.pipeToSelf(application.timed.runToFuture) {
|
||||
// // case Failure(exception) =>
|
||||
// // ApplicationStartFailed(exception.getMessage())
|
||||
// // case Success(value) =>
|
||||
// // println("here applications started")
|
||||
// // ApplicationStarted
|
||||
// // }
|
||||
// }
|
||||
}
|
||||
|
||||
Behaviors.receiveMessage { msg =>
|
||||
msg match {
|
||||
|
@ -11,14 +11,17 @@ import monix.bio.IO
|
||||
import monix.bio.Fiber
|
||||
import monix.execution.Scheduler
|
||||
import com.jme3.app.StatsAppState
|
||||
import com.jme3.app.FlyCamAppState
|
||||
// import wow.doge.mygame.executors.JMERunner
|
||||
class GameAppResource(logger: Logger[Task], jmeScheduler: Scheduler) {
|
||||
def make: Resource[Task, (GameApp, Fiber[Throwable, Unit])] =
|
||||
import wow.doge.mygame.executors.Schedulers
|
||||
class GameAppResource(
|
||||
logger: Logger[Task],
|
||||
jmeScheduler: Scheduler,
|
||||
schedulers: Schedulers
|
||||
) {
|
||||
def get: Resource[Task, (GameApp, Fiber[Throwable, Unit])] =
|
||||
Resource.make(
|
||||
for {
|
||||
_ <- logger.info("Creating game app")
|
||||
app <- Task(new GameApp(new StatsAppState()))
|
||||
app <- Task(new GameApp(schedulers, new StatsAppState()))
|
||||
_ <- Task {
|
||||
val settings = new AppSettings(true)
|
||||
settings.setVSync(true)
|
||||
@ -38,12 +41,13 @@ trait GameModule {
|
||||
|
||||
def gameAppResource(
|
||||
logger: Logger[Task],
|
||||
jmeScheduler: Scheduler
|
||||
jmeScheduler: Scheduler,
|
||||
schedulers: Schedulers
|
||||
): Resource[Task, (GameApp, Fiber[Throwable, Unit])] =
|
||||
Resource.make(
|
||||
(for {
|
||||
_ <- logger.info("Creating game app")
|
||||
app <- Task(new GameApp())
|
||||
app <- Task(new GameApp(schedulers))
|
||||
_ <- Task {
|
||||
val settings = new AppSettings(true)
|
||||
settings.setVSync(true)
|
||||
|
@ -3,44 +3,54 @@ package wow.doge.mygame.game
|
||||
import scala.concurrent.duration._
|
||||
|
||||
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 com.jme3.asset.plugins.ZipLocator
|
||||
import com.jme3.bullet.BulletAppState
|
||||
import com.jme3.bullet.control.BetterCharacterControl
|
||||
import com.softwaremill.macwire._
|
||||
import com.softwaremill.tagging._
|
||||
import io.odin.Logger
|
||||
import monix.bio.Task
|
||||
import monix.reactive.Consumer
|
||||
import wow.doge.mygame.events.EventBus
|
||||
import wow.doge.mygame.events.EventsModule
|
||||
import wow.doge.mygame.game.nodes.Player
|
||||
import wow.doge.mygame.game.nodes.PlayerController
|
||||
import wow.doge.mygame.game.subsystems.input.GameInputHandler
|
||||
import wow.doge.mygame.game.subsystems.level.DefaultGameLevel
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.subsystems.events.MovementEvent.PlayerMovementEvent
|
||||
import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent
|
||||
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
||||
import wow.doge.mygame.utils.IOUtils
|
||||
import wow.doge.mygame.math.ImVector3f
|
||||
import monix.bio.IO
|
||||
import wow.doge.mygame.subsystems.events.EntityMovementEvent
|
||||
import akka.actor.typed.ActorSystem
|
||||
import wow.doge.mygame.subsystems.events.PlayerCameraEvent
|
||||
|
||||
class GameSystemsInitializer()(
|
||||
override val spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||
override implicit val akkaScheduler: Scheduler,
|
||||
app: GameApp,
|
||||
loggerL: Logger[Task]
|
||||
) extends EventsModule {
|
||||
override implicit val timeout: Timeout = Timeout(1.second)
|
||||
class GameSystemsInitializer(
|
||||
spawnProtocol: ActorSystem[SpawnProtocol.Command],
|
||||
loggerL: Logger[Task],
|
||||
// eventBuses: EventsModule2
|
||||
playerMovementEventBus: ActorRef[
|
||||
EventBus.Command[EntityMovementEvent.PlayerMovementEvent]
|
||||
],
|
||||
playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]]
|
||||
)(app: GameApp) {
|
||||
implicit val timeout: Timeout = Timeout(1.second)
|
||||
|
||||
import GameSystemsInitializer._
|
||||
|
||||
def init =
|
||||
implicit val akkaScheduler: Scheduler = spawnProtocol.scheduler
|
||||
lazy val inputManager = app.inputManager
|
||||
lazy val assetManager = app.assetManager
|
||||
lazy val bulletAppState = new BulletAppState()
|
||||
// lazy val playerMovementEventBus = eventBuses.playerMovementEventBusTask
|
||||
|
||||
val init =
|
||||
for {
|
||||
playerMovementEventBus <- playerMovementEventBusTask
|
||||
inputManager = app.inputManager
|
||||
bulletAppState = new BulletAppState()
|
||||
_ <- loggerL.info("Initializing Systems")
|
||||
// playerMovementEventBus <- playerMovementEventBusTask
|
||||
_ <- Task(app.stateManager.attach(bulletAppState))
|
||||
_ <- Task(
|
||||
app.assetManager.registerLocator(
|
||||
@ -50,58 +60,32 @@ class GameSystemsInitializer()(
|
||||
)
|
||||
)
|
||||
_ <- app.enqueueL(() => DefaultGameLevel(app, bulletAppState))
|
||||
playerController <- app.enqueueL(() =>
|
||||
PlayerController(
|
||||
app,
|
||||
modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o",
|
||||
cam = app.camera
|
||||
)(app.assetManager, bulletAppState)
|
||||
.taggedWith[Player]
|
||||
)
|
||||
// _ <- loggerL.debug(playerNode.getName())
|
||||
// _ <- Task(app.rootNode.attachChild(playerNode))
|
||||
// playerMovementActor <- wireWith(spawnMovementActor _)
|
||||
// _ <-
|
||||
// IOUtils
|
||||
// .toIO(
|
||||
// app.tickObservable
|
||||
// .doOnNext { tpf =>
|
||||
// IOUtils.toTask(playerMovementActor !! ImMovementActor.Tick(tpf))
|
||||
// }
|
||||
// .completedL
|
||||
// .startAndForget
|
||||
// .onErrorRestart(3)
|
||||
// )
|
||||
_ <- wireWith(createPlayerController _).startAndForget
|
||||
_ <- wire[GameInputHandler.Props].begin
|
||||
|
||||
} yield ()
|
||||
|
||||
def createPlayerController(
|
||||
// playerMovementEventBus: ActorRef[
|
||||
// EventBus.Command[PlayerMovementEvent]
|
||||
// ],
|
||||
// playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]]
|
||||
): IO[PlayerController.Error, Unit] = {
|
||||
val playerPos = ImVector3f.ZERO
|
||||
val playerNode = None.taggedWith[Player]
|
||||
val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
|
||||
wire[PlayerController.Props].create
|
||||
}
|
||||
}
|
||||
|
||||
object GameSystemsInitializer {
|
||||
def spawnMovementActor(
|
||||
app: GameApp,
|
||||
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||
playerNode: 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, playerNode, playerMovementEventBus).create,
|
||||
"imMovementActor",
|
||||
Props.empty,
|
||||
_
|
||||
)
|
||||
)
|
||||
|
||||
def playerMovementActorTickConsumer(
|
||||
playerMovementActor: ActorRef[ImMovementActor.Command]
|
||||
) =
|
||||
Consumer
|
||||
.foreachTask[Float](tpf =>
|
||||
IOUtils.toTask(playerMovementActor !! ImMovementActor.Tick(tpf))
|
||||
IOUtils.toTask(playerMovementActor !! ImMovementActor.Tick)
|
||||
)
|
||||
// .mapTask()
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ class PlayerMovementState(
|
||||
|
||||
override def update(tpf: Float) = {
|
||||
// movementActor ! MovementActor.Tick(tpf, geom, cam)
|
||||
imMovementActor ! ImMovementActor.Tick(tpf)
|
||||
// imMovementActor ! ImMovementActor.Tick(tpf)
|
||||
|
||||
// movementActorTimer ! MovementActorTimer.Update(tpf)
|
||||
}
|
||||
|
@ -0,0 +1,201 @@
|
||||
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
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package wow.doge.mygame.game.nodes
|
||||
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import akka.actor.typed.scaladsl.ActorContext
|
||||
|
||||
object PlayerCameraActor {
|
||||
sealed trait Command
|
||||
|
||||
class Props() {
|
||||
def create =
|
||||
Behaviors.setup[Command] { ctx =>
|
||||
new PlayerCameraActor(ctx, this).receive(State.empty)
|
||||
}
|
||||
}
|
||||
|
||||
case class State()
|
||||
object State {
|
||||
val empty = State()
|
||||
}
|
||||
}
|
||||
class PlayerCameraActor(
|
||||
ctx: ActorContext[PlayerCameraActor.Command],
|
||||
props: PlayerCameraActor.Props
|
||||
) {
|
||||
import PlayerCameraActor._
|
||||
def receive(state: State) =
|
||||
Behaviors.receiveMessage[Command] {
|
||||
case _ => Behaviors.same
|
||||
}
|
||||
}
|
@ -6,9 +6,7 @@ import com.jme3.scene.Geometry
|
||||
import com.jme3.renderer.Camera
|
||||
import com.jme3.asset.AssetManager
|
||||
import wow.doge.mygame.state.MyMaterial
|
||||
import com.jme3.math.Vector3f
|
||||
import com.jme3.scene.control.CameraControl.ControlDirection
|
||||
import com.jme3.syntax._
|
||||
import com.jme3.scene.shape.Box
|
||||
import com.jme3.bullet.control.BetterCharacterControl
|
||||
import com.jme3.bullet.BulletAppState
|
||||
@ -16,20 +14,90 @@ 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) {}
|
||||
trait Player
|
||||
sealed trait Player
|
||||
sealed trait PlayerCameraNode
|
||||
|
||||
object PlayerController {
|
||||
def defaultMesh = {
|
||||
lazy val b = new Box(1, 1, 1)
|
||||
lazy val geom = new Geometry("playerMesh", b)
|
||||
geom
|
||||
}
|
||||
def defaultTexture(assetManager: AssetManager) =
|
||||
MyMaterial(
|
||||
assetManager = assetManager,
|
||||
path = os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
|
||||
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,
|
||||
@ -41,10 +109,6 @@ object PlayerController {
|
||||
.withJumpForce(ImVector3f(0, 5f, 0))
|
||||
lazy val playerNode = new Node("PlayerNode")
|
||||
.withChildren(
|
||||
new CameraNode("CameraNode", cam)
|
||||
.withControlDir(ControlDirection.SpatialToCamera)
|
||||
.withLocalTranslation(ImVector3f(0, 1.5f, 10))
|
||||
.withLookAt(playerPos, ImVector3f.UNIT_Y),
|
||||
assetManager
|
||||
.loadModel(modelPath)
|
||||
.asInstanceOf[Node]
|
||||
@ -64,41 +128,93 @@ object PlayerController {
|
||||
|
||||
playerPhysicsControl
|
||||
}
|
||||
def apply(
|
||||
mesh: Geometry = defaultMesh,
|
||||
texturePath: os.RelPath =
|
||||
os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md",
|
||||
cam: Camera
|
||||
)(assetManager: AssetManager): Node = {
|
||||
}
|
||||
|
||||
lazy val playerNode = new Node("PlayerNode")
|
||||
lazy val camNode = new CameraNode("CameraNode", cam)
|
||||
|
||||
{
|
||||
val mat = MyMaterial(
|
||||
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 = texturePath
|
||||
path = os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
|
||||
)
|
||||
mesh.setMaterial(mat)
|
||||
}
|
||||
|
||||
// lazy val camNode = new CameraNode("CameraNode", simpleApp.getCamera())
|
||||
// camNode.setCamera(simpleApp.getCamera())
|
||||
discard {
|
||||
playerNode
|
||||
.withChild(camNode)
|
||||
.withChild(mesh)
|
||||
// playerNode.children(Seq(camNode, geom))
|
||||
}
|
||||
def defaultCamerNode(cam: Camera, playerPos: ImVector3f) =
|
||||
new CameraNode("CameraNode", cam)
|
||||
.withControlDir(ControlDirection.SpatialToCamera)
|
||||
.withLocalTranslation(ImVector3f(0, 1.5f, 10))
|
||||
.withLookAt(playerPos, ImVector3f.UNIT_Y)
|
||||
|
||||
{
|
||||
camNode.setControlDir(ControlDirection.SpatialToCamera)
|
||||
camNode.setLocalTranslation(
|
||||
new Vector3f(0, 1.5f, 10)
|
||||
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)
|
||||
)
|
||||
camNode.lookAt(playerNode.getLocalTranslation(), Vector3f.UNIT_Y)
|
||||
.withLocalTranslation(playerPos)
|
||||
.withControl(playerPhysicsControl)
|
||||
)
|
||||
|
||||
lazy val defaultPlayerPhysicsControl =
|
||||
new BetterCharacterControl(1.5f, 6f, 1f)
|
||||
.withJumpForce(ImVector3f(0, 5f, 0))
|
||||
}
|
||||
|
||||
playerNode
|
||||
}
|
||||
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
|
||||
// )
|
||||
|
@ -5,17 +5,23 @@ import akka.actor.typed.scaladsl.Behaviors
|
||||
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
||||
import org.slf4j.event.Level
|
||||
import akka.actor.typed.LogOptions
|
||||
import wow.doge.mygame.subsystems.events.MovementEvent.PlayerMovementEvent
|
||||
import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import wow.doge.mygame.subsystems.events.PlayerCameraEvent
|
||||
import wow.doge.mygame.subsystems.events.PlayerCameraEvent.CameraMovedUp
|
||||
import wow.doge.mygame.subsystems.events.PlayerCameraEvent.CameraMovedDown
|
||||
import com.jme3.scene.CameraNode
|
||||
import wow.doge.mygame.game.GameApp
|
||||
import wow.doge.mygame.implicits._
|
||||
|
||||
object PlayerMovementEventHandler {
|
||||
object PlayerMovementEventListener {
|
||||
import PlayerMovementEvent._
|
||||
def apply(movementActor: ActorRef[ImMovementActor.Command]) =
|
||||
Behaviors.logMessages(
|
||||
LogOptions()
|
||||
.withLevel(Level.TRACE)
|
||||
.withLogger(
|
||||
Logger[PlayerMovementEventHandler.type].underlying
|
||||
Logger[PlayerMovementEventListener.type].underlying
|
||||
),
|
||||
Behaviors.setup[PlayerMovementEvent](ctx =>
|
||||
Behaviors.receiveMessage {
|
||||
@ -52,3 +58,33 @@ object PlayerMovementEventHandler {
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
object PlayerCameraEventListener {
|
||||
def apply(
|
||||
camNode: CameraNode,
|
||||
enqueueR: Function1[() => Unit, Unit]
|
||||
) =
|
||||
Behaviors.logMessages(
|
||||
LogOptions()
|
||||
.withLevel(Level.TRACE)
|
||||
.withLogger(
|
||||
Logger[PlayerCameraEventListener.type].underlying
|
||||
),
|
||||
Behaviors.setup[PlayerCameraEvent](ctx =>
|
||||
Behaviors.receiveMessage {
|
||||
case CameraMovedUp =>
|
||||
enqueueR(() => {
|
||||
|
||||
camNode.move(0, 1, 0)
|
||||
})
|
||||
Behaviors.same
|
||||
case CameraMovedDown =>
|
||||
enqueueR(() => {
|
||||
|
||||
camNode.move(0, -1, 0)
|
||||
})
|
||||
Behaviors.same
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -8,10 +8,11 @@ import com.jme3.input.KeyInput
|
||||
import com.jme3.input.controls.KeyTrigger
|
||||
import monix.bio.UIO
|
||||
import wow.doge.mygame.utils.IOUtils._
|
||||
import wow.doge.mygame.subsystems.events.MovementEvent.PlayerMovementEvent
|
||||
import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent
|
||||
import scala.concurrent.duration._
|
||||
import com.jme3.input.controls.MouseAxisTrigger
|
||||
import com.jme3.input.MouseInput
|
||||
import wow.doge.mygame.subsystems.events.PlayerCameraEvent
|
||||
|
||||
// class GameInputHandler(
|
||||
// inputManager: InputManager
|
||||
@ -24,7 +25,8 @@ object GameInputHandler {
|
||||
inputManager: InputManager,
|
||||
playerMovementEventBus: ActorRef[
|
||||
EventBus.Command[PlayerMovementEvent]
|
||||
]
|
||||
],
|
||||
playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]]
|
||||
) {
|
||||
def begin =
|
||||
for {
|
||||
@ -44,7 +46,12 @@ object GameInputHandler {
|
||||
_ <- toIO(
|
||||
generateCameraEvents(
|
||||
inputManager,
|
||||
playerMovementEventBus
|
||||
playerCameraEventBus
|
||||
).completedL.startAndForget
|
||||
)
|
||||
_ <- toIO(
|
||||
myTest(
|
||||
inputManager
|
||||
).completedL.startAndForget
|
||||
)
|
||||
} yield ()
|
||||
@ -53,22 +60,22 @@ object GameInputHandler {
|
||||
def setupKeys(inputManager: InputManager) =
|
||||
inputManager
|
||||
.withMapping(
|
||||
"Left",
|
||||
PlayerMovementInput.WalkLeft.entryName,
|
||||
new KeyTrigger(KeyInput.KEY_A)
|
||||
// new KeyTrigger(KeyInput.KEY_LEFT)
|
||||
)
|
||||
.withMapping(
|
||||
"Right",
|
||||
PlayerMovementInput.WalkRight.entryName,
|
||||
new KeyTrigger(KeyInput.KEY_D)
|
||||
// new KeyTrigger(KeyInput.KEY_RIGHT)
|
||||
)
|
||||
.withMapping(
|
||||
"Up",
|
||||
PlayerMovementInput.WalkForward.entryName,
|
||||
new KeyTrigger(KeyInput.KEY_W)
|
||||
// new KeyTrigger(KeyInput.KEY_UP)
|
||||
)
|
||||
.withMapping(
|
||||
"Down",
|
||||
PlayerMovementInput.WalkBackward.entryName,
|
||||
new KeyTrigger(KeyInput.KEY_S)
|
||||
// new KeyTrigger(KeyInput.KEY_DOWN)
|
||||
)
|
||||
@ -77,12 +84,12 @@ object GameInputHandler {
|
||||
new KeyTrigger(KeyInput.KEY_SPACE)
|
||||
)
|
||||
.withMapping(
|
||||
"ROTATE_RIGHT",
|
||||
PlayerAnalogInput.TurnRight.entryName,
|
||||
new KeyTrigger(KeyInput.KEY_RIGHT),
|
||||
new MouseAxisTrigger(MouseInput.AXIS_X, true)
|
||||
)
|
||||
.withMapping(
|
||||
"ROTATE_LEFT",
|
||||
PlayerAnalogInput.TurnLeft.entryName,
|
||||
new KeyTrigger(KeyInput.KEY_LEFT),
|
||||
new MouseAxisTrigger(MouseInput.AXIS_X, false)
|
||||
)
|
||||
@ -104,62 +111,67 @@ object GameInputHandler {
|
||||
EventBus.Command[PlayerMovementEvent]
|
||||
]
|
||||
) = {
|
||||
val name = "movementInputEventsGenerator"
|
||||
val name = "playerMovementInputEventsGenerator"
|
||||
inputManager
|
||||
.observableAction(
|
||||
"Left",
|
||||
"Right",
|
||||
"Up",
|
||||
"Down",
|
||||
"Jump",
|
||||
"ROTATE_RIGHT",
|
||||
"ROTATE_LEFT"
|
||||
)
|
||||
.enumObservableAction(PlayerMovementInput)
|
||||
// .dump("O")
|
||||
.doOnNext { action =>
|
||||
action.binding.name match {
|
||||
case "Left" =>
|
||||
action.binding match {
|
||||
case PlayerMovementInput.WalkLeft =>
|
||||
toTask(
|
||||
playerMovementEventBus !! EventBus.Publish(
|
||||
PlayerMovementEvent.PlayerMovedLeft(pressed = action.value),
|
||||
name
|
||||
)
|
||||
)
|
||||
case "Right" =>
|
||||
case PlayerMovementInput.WalkRight =>
|
||||
toTask(
|
||||
playerMovementEventBus !! EventBus.Publish(
|
||||
PlayerMovementEvent.PlayerMovedRight(pressed = action.value),
|
||||
name
|
||||
)
|
||||
)
|
||||
case "Up" =>
|
||||
case PlayerMovementInput.WalkForward =>
|
||||
toTask(
|
||||
playerMovementEventBus !! EventBus.Publish(
|
||||
PlayerMovementEvent.PlayerMovedForward(pressed = action.value),
|
||||
name
|
||||
)
|
||||
)
|
||||
case "Down" =>
|
||||
case PlayerMovementInput.WalkBackward =>
|
||||
toTask(
|
||||
playerMovementEventBus !! EventBus.Publish(
|
||||
PlayerMovementEvent.PlayerMovedBackward(pressed = action.value),
|
||||
name
|
||||
)
|
||||
)
|
||||
// case "Jump" if action.value =>
|
||||
// toTask(
|
||||
// playerMovementEventBus !! EventBus.Publish(
|
||||
// PlayerMovementEvent.PlayerJumped,
|
||||
// name
|
||||
// )
|
||||
// )
|
||||
|
||||
case "Jump" if action.value =>
|
||||
toTask(
|
||||
playerMovementEventBus !! EventBus.Publish(
|
||||
PlayerMovementEvent.PlayerJumped,
|
||||
name
|
||||
)
|
||||
)
|
||||
|
||||
case _ => monix.eval.Task.unit
|
||||
// case _ => monix.eval.Task.unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def myTest(inputManager: InputManager) = {
|
||||
inputManager
|
||||
.enumObservableAction(PlayerMovementEnum)
|
||||
.sample(1.millis)
|
||||
.mapEval(action =>
|
||||
action.binding match {
|
||||
case PlayerMovementEnum.MOVE_RIGHT =>
|
||||
monix.eval.Task(println("move right")) >> monix.eval.Task.unit
|
||||
case PlayerMovementEnum.MOVE_LEFT =>
|
||||
monix.eval.Task(println("move left"))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
def generateRotateEvents(
|
||||
inputManager: InputManager,
|
||||
playerMovementEventBus: ActorRef[
|
||||
@ -168,34 +180,32 @@ object GameInputHandler {
|
||||
) = {
|
||||
val name = "rotateMovementEventsGenerator"
|
||||
inputManager
|
||||
.analogObservable("ROTATE_RIGHT", "ROTATE_LEFT")
|
||||
.enumAnalogObservable(PlayerAnalogInput)
|
||||
.sample(1.millis)
|
||||
.mapEval(analogEvent =>
|
||||
analogEvent.binding.name match {
|
||||
case "ROTATE_RIGHT" =>
|
||||
.map(e => e)
|
||||
.doOnNext(analogEvent =>
|
||||
analogEvent.binding match {
|
||||
case PlayerAnalogInput.TurnRight =>
|
||||
toTask(
|
||||
playerMovementEventBus !! EventBus.Publish(
|
||||
PlayerMovementEvent.PlayerRotatedRight,
|
||||
name
|
||||
)
|
||||
)
|
||||
case "ROTATE_LEFT" =>
|
||||
case PlayerAnalogInput.TurnLeft =>
|
||||
toTask(
|
||||
playerMovementEventBus !! EventBus.Publish(
|
||||
PlayerMovementEvent.PlayerRotatedLeft,
|
||||
name
|
||||
)
|
||||
)
|
||||
case _ => monix.eval.Task.unit
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
def generateCameraEvents(
|
||||
inputManager: InputManager,
|
||||
playerMovementEventBus: ActorRef[
|
||||
EventBus.Command[PlayerMovementEvent]
|
||||
]
|
||||
playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]]
|
||||
) = {
|
||||
val name = "cameraMovementEventsGenerator"
|
||||
inputManager
|
||||
@ -205,15 +215,15 @@ object GameInputHandler {
|
||||
analogEvent.binding.name match {
|
||||
case "CAMERA_UP" =>
|
||||
toTask(
|
||||
playerMovementEventBus !! EventBus.Publish(
|
||||
PlayerMovementEvent.PlayerCameraUp,
|
||||
playerCameraEventBus !! EventBus.Publish(
|
||||
PlayerCameraEvent.CameraMovedUp,
|
||||
name
|
||||
)
|
||||
)
|
||||
case "CAMERA_DOWN" =>
|
||||
toTask(
|
||||
playerMovementEventBus !! EventBus.Publish(
|
||||
PlayerMovementEvent.PlayerCameraDown,
|
||||
playerCameraEventBus !! EventBus.Publish(
|
||||
PlayerCameraEvent.CameraMovedDown,
|
||||
name
|
||||
)
|
||||
)
|
||||
|
@ -0,0 +1,19 @@
|
||||
package wow.doge.mygame.game.subsystems.input
|
||||
import enumeratum._
|
||||
import enumeratum.EnumEntry._
|
||||
|
||||
sealed trait PlayerMovementInput extends EnumEntry with UpperSnakecase
|
||||
object PlayerMovementInput extends Enum[PlayerMovementInput] {
|
||||
val values = findValues
|
||||
case object WalkForward extends PlayerMovementInput
|
||||
case object WalkRight extends PlayerMovementInput
|
||||
case object WalkLeft extends PlayerMovementInput
|
||||
case object WalkBackward extends PlayerMovementInput
|
||||
}
|
||||
|
||||
sealed trait PlayerAnalogInput extends EnumEntry with UpperSnakecase
|
||||
object PlayerAnalogInput extends Enum[PlayerAnalogInput] {
|
||||
val values = findValues
|
||||
case object TurnRight extends PlayerAnalogInput
|
||||
case object TurnLeft extends PlayerAnalogInput
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
package wow.doge.mygame.game.subsystems.movement
|
||||
|
||||
import com.jme3.bullet.control.BetterCharacterControl
|
||||
import com.jme3.math.FastMath
|
||||
import com.jme3.math.Quaternion
|
||||
import com.jme3.math.Vector3f
|
||||
import wow.doge.mygame.math.ImVector3f
|
||||
import wow.doge.mygame.subsystems.movement.RotateDir
|
||||
import wow.doge.mygame.implicits._
|
||||
import com.jme3.scene.Spatial
|
||||
import com.typesafe.scalalogging.LazyLogging
|
||||
|
||||
trait CanMove[-A] {
|
||||
// def getDirection(cam: Camera, cardinalDir: CardinalDirection): ImVector3f
|
||||
def move(inst: A, direction: ImVector3f): Unit
|
||||
def jump(inst: A): Unit
|
||||
def stop(inst: A): Unit
|
||||
def rotate(inst: A, rotateDir: RotateDir): Unit
|
||||
}
|
||||
|
||||
object CanMove {
|
||||
implicit val implCanMoveForBetterCharacterControl =
|
||||
new CanMove[BetterCharacterControl] {
|
||||
override def move(
|
||||
inst: BetterCharacterControl,
|
||||
direction: ImVector3f
|
||||
): Unit = {
|
||||
// val dir = direction.mutable
|
||||
// inst.setViewDirection(dir)
|
||||
// inst.setViewDirection(direction.mutable)
|
||||
inst.setWalkDirection(direction.mutable.multLocal(50f))
|
||||
}
|
||||
override def jump(inst: BetterCharacterControl): Unit = inst.jump()
|
||||
override def rotate(
|
||||
inst: BetterCharacterControl,
|
||||
rotateDir: RotateDir
|
||||
): Unit = {
|
||||
val q =
|
||||
rotateDir match {
|
||||
case RotateDir.Left =>
|
||||
new Quaternion()
|
||||
.fromAngleAxis(-10f * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y)
|
||||
case RotateDir.Right =>
|
||||
new Quaternion()
|
||||
.fromAngleAxis(10 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y)
|
||||
}
|
||||
|
||||
val tmp = new Vector3f()
|
||||
inst.getViewDirection(tmp)
|
||||
inst.setViewDirection(q.mult(tmp))
|
||||
}
|
||||
override def stop(inst: BetterCharacterControl) =
|
||||
inst.setWalkDirection(Vector3f.ZERO)
|
||||
}
|
||||
|
||||
implicit val implCanMoveForGeom = new CanMove[Spatial] with LazyLogging {
|
||||
override def move(inst: Spatial, direction: ImVector3f): Unit = {
|
||||
inst.move(direction.mutable)
|
||||
}
|
||||
override def jump(inst: Spatial): Unit =
|
||||
logger.warn("`Jump` is not implemented for type `Spatial`")
|
||||
override def rotate(inst: Spatial, rotateDir: RotateDir): Unit = {
|
||||
rotateDir match {
|
||||
case RotateDir.Left => inst.rotate(0, -0.01f, 0)
|
||||
case RotateDir.Right => inst.rotate(0, 0.01f, 0)
|
||||
}
|
||||
}
|
||||
override def stop(inst: Spatial) = { /*not required*/ }
|
||||
}
|
||||
}
|
@ -3,9 +3,7 @@ package wow.doge.mygame.subsystems.movement
|
||||
import akka.actor.typed.scaladsl.ActorContext
|
||||
import akka.actor.typed.Behavior
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import com.softwaremill.quicklens._
|
||||
import wow.doge.mygame.implicits._
|
||||
import com.jme3.renderer.Camera
|
||||
import wow.doge.mygame.math.ImVector3f
|
||||
import wow.doge.mygame.game.GameApp
|
||||
|
||||
@ -13,10 +11,9 @@ import akka.actor.typed.ActorRef
|
||||
import wow.doge.mygame.events.EventBus
|
||||
import com.jme3.math.Vector3f
|
||||
import wow.doge.mygame.state.CardinalDirection
|
||||
import wow.doge.mygame.subsystems.events.MovementEvent.PlayerMovementEvent
|
||||
import akka.actor.typed.LogOptions
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import org.slf4j.event.Level
|
||||
import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent
|
||||
import wow.doge.mygame.game.subsystems.movement.CanMove
|
||||
import com.softwaremill.quicklens._
|
||||
|
||||
sealed trait RotateDir
|
||||
object RotateDir {
|
||||
@ -24,18 +21,10 @@ object RotateDir {
|
||||
case object Right extends RotateDir
|
||||
}
|
||||
|
||||
trait CanMove[-A] {
|
||||
// def getDirection(cam: Camera, cardinalDir: CardinalDirection): ImVector3f
|
||||
def move(inst: A, direction: ImVector3f): Unit
|
||||
def jump(inst: A): Unit
|
||||
def stop(inst: A): Unit
|
||||
def rotate(inst: A, rotateDir: RotateDir): Unit
|
||||
}
|
||||
|
||||
object ImMovementActor {
|
||||
sealed trait Command
|
||||
// final case class Tick(tpf: Float) extends Command
|
||||
final case class Tick(tpf: Float) extends Command
|
||||
final case object Tick extends Command
|
||||
|
||||
sealed trait Movement extends Command
|
||||
final case class MovedLeft(pressed: Boolean) extends Movement
|
||||
@ -101,51 +90,36 @@ class ImMovementActor[T](
|
||||
case m: Movement =>
|
||||
m match {
|
||||
case MovedLeft(pressed) =>
|
||||
props.app.enqueueF(stopIfNotPressed(pressed, props.movable))
|
||||
props.app.enqueueR(() => stopIfNotPressed(pressed, props.movable))
|
||||
receive(state = state.modify(_.cardinalDir.left).setTo(pressed))
|
||||
case MovedUp(pressed) =>
|
||||
props.app.enqueueF(stopIfNotPressed(pressed, props.movable))
|
||||
props.app.enqueueR(() => stopIfNotPressed(pressed, props.movable))
|
||||
receive(state = state.modify(_.cardinalDir.up).setTo(pressed))
|
||||
case MovedRight(pressed) =>
|
||||
props.app.enqueueF(stopIfNotPressed(pressed, props.movable))
|
||||
props.app.enqueueR(() => stopIfNotPressed(pressed, props.movable))
|
||||
receive(state = state.modify(_.cardinalDir.right).setTo(pressed))
|
||||
case MovedDown(pressed) =>
|
||||
props.app.enqueueF(stopIfNotPressed(pressed, props.movable))
|
||||
props.app.enqueueR(() => stopIfNotPressed(pressed, props.movable))
|
||||
receive(state = state.modify(_.cardinalDir.down).setTo(pressed))
|
||||
case Jump =>
|
||||
props.app.enqueueF(cm.jump(props.movable))
|
||||
props.app.enqueueR(() => cm.jump(props.movable))
|
||||
Behaviors.same
|
||||
case RotateLeft =>
|
||||
props.app.enqueueF(cm.rotate(props.movable, RotateDir.Left))
|
||||
props.app.enqueueR(() => cm.rotate(props.movable, RotateDir.Left))
|
||||
Behaviors.same
|
||||
case RotateRight =>
|
||||
props.app.enqueueF(cm.rotate(props.movable, RotateDir.Right))
|
||||
props.app.enqueueR(() => cm.rotate(props.movable, RotateDir.Right))
|
||||
Behaviors.same
|
||||
}
|
||||
|
||||
case Tick(tpf) =>
|
||||
case Tick =>
|
||||
val walkDir =
|
||||
getDirection(state.cardinalDir, ctx.log.trace)
|
||||
if (walkDir != ImVector3f.ZERO) {
|
||||
val tmp = walkDir * 25f * tpf
|
||||
props.app.enqueueF {
|
||||
val tmp = walkDir * 25f * (1f / 144)
|
||||
props.app.enqueueR { () =>
|
||||
cm.move(props.movable, tmp)
|
||||
}
|
||||
|
||||
// props.app
|
||||
// .enqueueScala { () =>
|
||||
// 1
|
||||
// }
|
||||
// .map(println)(scala.concurrent.ExecutionContext.global)
|
||||
// monix.eval.Task
|
||||
// .fromFuture(
|
||||
// props.app
|
||||
// .enqueueScala { () =>
|
||||
// 1
|
||||
// }
|
||||
// )
|
||||
// .flatMap(i => monix.eval.Task(println(i)))
|
||||
// .runToFuture(monix.execution.Scheduler.Implicits.global)
|
||||
}
|
||||
Behaviors.same
|
||||
}
|
||||
@ -182,3 +156,18 @@ object Methods {
|
||||
) =
|
||||
if (!pressed) cm.stop(movable)
|
||||
}
|
||||
|
||||
// props.app
|
||||
// .enqueueScala { () =>
|
||||
// 1
|
||||
// }
|
||||
// .map(println)(scala.concurrent.ExecutionContext.global)
|
||||
// monix.eval.Task
|
||||
// .fromFuture(
|
||||
// props.app
|
||||
// .enqueueScala { () =>
|
||||
// 1
|
||||
// }
|
||||
// )
|
||||
// .flatMap(i => monix.eval.Task(println(i)))
|
||||
// .runToFuture(monix.execution.Scheduler.Implicits.global)
|
||||
|
43
src/main/scala/wow/doge/mygame/implicits/TestEnum.scala
Normal file
43
src/main/scala/wow/doge/mygame/implicits/TestEnum.scala
Normal file
@ -0,0 +1,43 @@
|
||||
package wow.doge.mygame.implicits
|
||||
|
||||
import enumeratum._
|
||||
|
||||
sealed trait TestEnum extends EnumEntry
|
||||
|
||||
object TestEnum extends Enum[TestEnum] {
|
||||
val values = findValues
|
||||
case object Test2 extends TestEnum
|
||||
}
|
||||
|
||||
sealed trait Greeting extends EnumEntry
|
||||
|
||||
object Greeting extends Enum[Greeting] {
|
||||
|
||||
/*
|
||||
`findValues` is a protected method that invokes a macro to find all `Greeting` object declarations inside an `Enum`
|
||||
|
||||
You use it to implement the `val values` member
|
||||
*/
|
||||
val values = findValues
|
||||
|
||||
case object Hello extends Greeting
|
||||
case object GoodBye extends Greeting
|
||||
case object Hi extends Greeting
|
||||
case object Bye extends Greeting
|
||||
|
||||
}
|
||||
object ObsTest {}
|
||||
|
||||
sealed trait PlayerMovementEnum extends EnumEntry {
|
||||
def test: String
|
||||
}
|
||||
|
||||
object PlayerMovementEnum extends Enum[PlayerMovementEnum] {
|
||||
val values = findValues
|
||||
case object MOVE_RIGHT extends PlayerMovementEnum {
|
||||
val test = "hmm"
|
||||
}
|
||||
case object MOVE_LEFT extends PlayerMovementEnum {
|
||||
val test = "mmh"
|
||||
}
|
||||
}
|
@ -19,11 +19,7 @@ import com.jme3.input.controls.InputListener
|
||||
import com.jme3.math.Vector3f
|
||||
import wow.doge.mygame.math.ImVector3f
|
||||
import com.jme3.scene.Geometry
|
||||
import wow.doge.mygame.state.CardinalDirection
|
||||
import wow.doge.mygame.subsystems.movement.CanMove
|
||||
import com.jme3.renderer.Camera
|
||||
import scala.jdk.CollectionConverters._
|
||||
import wow.doge.mygame.utils.JFXConsoleStreamable
|
||||
import com.jme3.app.Application
|
||||
import com.jme3.scene.SceneGraphVisitor
|
||||
import monix.reactive.Observable
|
||||
@ -46,20 +42,25 @@ import com.jme3.bullet.BulletAppState
|
||||
import wow.doge.mygame.state.MyBaseState
|
||||
import monix.bio.UIO
|
||||
import com.jme3.bullet.control.BetterCharacterControl
|
||||
import com.jme3.scene.control.AbstractControl
|
||||
import com.jme3.scene.CameraNode
|
||||
import com.jme3.scene.control.CameraControl.ControlDirection
|
||||
import com.jme3.bullet.control.AbstractPhysicsControl
|
||||
import com.jme3.scene.control.Control
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import com.typesafe.scalalogging.LazyLogging
|
||||
import com.jme3.input.controls.AnalogListener
|
||||
import com.jme3.math.Quaternion
|
||||
import com.jme3.math.FastMath
|
||||
import wow.doge.mygame.subsystems.movement.RotateDir
|
||||
import enumeratum._
|
||||
|
||||
case class ActionEvent(binding: Action, value: Boolean, tpf: Float)
|
||||
case class EnumActionEvent[T <: EnumEntry](
|
||||
binding: T,
|
||||
value: Boolean,
|
||||
tpf: Float
|
||||
)
|
||||
|
||||
case class AnalogEvent(binding: Action, value: Float, tpf: Float)
|
||||
case class EnumAnalogEvent[T <: EnumEntry](
|
||||
binding: T,
|
||||
value: Float,
|
||||
tpf: Float
|
||||
)
|
||||
case class PhysicsTickEvent(space: PhysicsSpace, tpf: Float)
|
||||
|
||||
package object implicits {
|
||||
@ -69,41 +70,11 @@ package object implicits {
|
||||
|
||||
implicit class JMEAppExt(private val app: Application) extends AnyVal {
|
||||
|
||||
// /**
|
||||
// * Blocking task. Execute on a thread pool meant for blocking operations.
|
||||
// * Prefer [[wow.doge.mygame.implicits.JMEAppExt#enqueueT]] instead.
|
||||
// *
|
||||
// * @param cb
|
||||
// * @param ec
|
||||
// * @return
|
||||
// */
|
||||
// def enqueueF[T](cb: () => T)(implicit ec: ExecutionContext): Future[T] =
|
||||
// Future {
|
||||
// app
|
||||
// .enqueue(new Callable[T]() {
|
||||
// override def call(): T = cb()
|
||||
// })
|
||||
// .get()
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Blocking task. Execute on a thread pool meant for blocking operations.
|
||||
// * Same as enqueue, but returns a Monix Task instead of Future
|
||||
// * @param cb
|
||||
// * @param ec
|
||||
// * @return
|
||||
// */
|
||||
// def enqueueL[T](cb: () => T): Task[T] =
|
||||
// Task
|
||||
// .deferFutureAction(implicit s => enqueueF(cb))
|
||||
|
||||
def enqueueF[T](cb: => T) =
|
||||
def enqueueR(cb: () => Unit) =
|
||||
app.enqueue(new Runnable {
|
||||
override def run() = cb
|
||||
override def run() = cb()
|
||||
})
|
||||
|
||||
def enqueueT(cb: => Unit) =
|
||||
Task(enqueueF(cb))
|
||||
}
|
||||
implicit class StateManagerExt(private val sm: AppStateManager)
|
||||
extends AnyVal {
|
||||
@ -190,38 +161,35 @@ package object implicits {
|
||||
def observableDepthFirst(): Observable[Spatial] = {
|
||||
def loop(
|
||||
subscriber: Subscriber[Spatial],
|
||||
spatial: Spatial
|
||||
): Task[Unit] = {
|
||||
spatials: LazyList[Spatial]
|
||||
): Task[Unit] =
|
||||
spatials match {
|
||||
// spatial can be either a node or a geometry, but it's not a sealed trait
|
||||
spatial match {
|
||||
|
||||
case node: Node =>
|
||||
Task.deferFuture(subscriber.onNext(node)).flatMap {
|
||||
case Ack.Continue => {
|
||||
//modifying a node's children list is forbidden
|
||||
val children = node.children
|
||||
if (!children.isEmpty) {
|
||||
Task.sequence(
|
||||
children.map(c => loop(subscriber, c))
|
||||
) >> Task.unit
|
||||
} else {
|
||||
Task.unit
|
||||
}
|
||||
|
||||
}
|
||||
case Ack.Stop => Task.unit
|
||||
|
||||
}
|
||||
//geomtries do not/cannot have children
|
||||
case head #:: tail =>
|
||||
head match {
|
||||
case g: Geometry =>
|
||||
Task.deferFuture(subscriber.onNext(g)) >> Task.unit
|
||||
case _ => Task.unit
|
||||
Task.deferFuture(subscriber.onNext(g)).flatMap {
|
||||
case Continue =>
|
||||
loop(subscriber, tail)
|
||||
case Stop =>
|
||||
Task(subscriber.onComplete())
|
||||
}
|
||||
case node: Node =>
|
||||
val children = node.children
|
||||
Task.deferFuture(subscriber.onNext(node)).flatMap {
|
||||
case Continue =>
|
||||
loop(subscriber, children #::: tail)
|
||||
case Stop =>
|
||||
Task(subscriber.onComplete())
|
||||
}
|
||||
// case _ => loop(subscriber, tail)
|
||||
}
|
||||
case LazyList() => Task.unit
|
||||
}
|
||||
|
||||
Observable.create(OverflowStrategy.Unbounded) { sub =>
|
||||
implicit val sched = sub.scheduler
|
||||
loop(sub, n).runToFuture
|
||||
loop(sub, LazyList(n)).runToFuture
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,16 +211,24 @@ package object implicits {
|
||||
Task.deferFuture(subscriber.onNext(g)).flatMap {
|
||||
case Continue =>
|
||||
loop(subscriber, tail)
|
||||
case Stop => Task.unit
|
||||
case Stop =>
|
||||
Task(subscriber.onComplete())
|
||||
}
|
||||
case node: Node =>
|
||||
val children = node.children
|
||||
Task.deferFuture(subscriber.onNext(node)).flatMap {
|
||||
case Continue =>
|
||||
loop(subscriber, tail #::: children)
|
||||
case Stop => Task.unit
|
||||
case Stop =>
|
||||
Task(subscriber.onComplete())
|
||||
}
|
||||
case unknown =>
|
||||
Task.deferFuture(subscriber.onNext(unknown)).flatMap {
|
||||
case Continue =>
|
||||
loop(subscriber, tail)
|
||||
case Stop =>
|
||||
Task(subscriber.onComplete())
|
||||
}
|
||||
// case _ => loop(subscriber, tail)
|
||||
}
|
||||
case LazyList() => Task.unit
|
||||
}
|
||||
@ -369,10 +345,12 @@ package object implicits {
|
||||
): Unit = {
|
||||
if (
|
||||
sub.onNext(ActionEvent(Action(binding), value, tpf)) == Ack.Stop
|
||||
)
|
||||
) {
|
||||
sub.onComplete()
|
||||
c.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inputManager.addListener(al, mappingNames: _*)
|
||||
|
||||
@ -380,9 +358,45 @@ package object implicits {
|
||||
c
|
||||
}
|
||||
}
|
||||
|
||||
def enumObservableAction[T <: EnumEntry](
|
||||
mappingEnum: Enum[T]
|
||||
): Observable[EnumActionEvent[T]] = {
|
||||
|
||||
Observable.create(OverflowStrategy.DropOld(10)) { sub =>
|
||||
val c = SingleAssignCancelable()
|
||||
val entryNames = mappingEnum.values.map(_.entryName)
|
||||
val al = new ActionListener {
|
||||
override def onAction(
|
||||
binding: String,
|
||||
value: Boolean,
|
||||
tpf: Float
|
||||
): Unit = {
|
||||
mappingEnum.withNameOption(binding).foreach { b =>
|
||||
if (sub.onNext(EnumActionEvent(b, value, tpf)) == Ack.Stop) {
|
||||
sub.onComplete()
|
||||
c.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inputManager.addListener(al, entryNames: _*)
|
||||
|
||||
c := Cancelable(() => inputManager.removeListener(al))
|
||||
c
|
||||
}
|
||||
}
|
||||
|
||||
// def enumObservableAction[T <: enumeratum.EnumEntry](
|
||||
// mappingNames: Enum[T]
|
||||
// ): Observable[ActionEvent] = {
|
||||
// observableAction2(mappingNames).doOnNext()
|
||||
// }
|
||||
|
||||
def analogObservable(mappingNames: String*): Observable[AnalogEvent] = {
|
||||
|
||||
Observable.create(OverflowStrategy.DropOld(100)) { sub =>
|
||||
Observable.create(OverflowStrategy.DropOld(50)) { sub =>
|
||||
val c = SingleAssignCancelable()
|
||||
val al = new AnalogListener {
|
||||
override def onAnalog(
|
||||
@ -392,10 +406,12 @@ package object implicits {
|
||||
): Unit = {
|
||||
if (
|
||||
sub.onNext(AnalogEvent(Action(binding), value, tpf)) == Ack.Stop
|
||||
)
|
||||
) {
|
||||
sub.onComplete()
|
||||
c.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inputManager.addListener(al, mappingNames: _*)
|
||||
|
||||
@ -403,6 +419,35 @@ package object implicits {
|
||||
c
|
||||
}
|
||||
}
|
||||
|
||||
def enumAnalogObservable[T <: EnumEntry](
|
||||
mappingEnum: Enum[T]
|
||||
): Observable[EnumAnalogEvent[T]] = {
|
||||
|
||||
Observable.create(OverflowStrategy.DropOld(50)) { sub =>
|
||||
val c = SingleAssignCancelable()
|
||||
val entryNames = mappingEnum.values.map(_.entryName)
|
||||
val al = new AnalogListener {
|
||||
override def onAnalog(
|
||||
binding: String,
|
||||
value: Float,
|
||||
tpf: Float
|
||||
): Unit = {
|
||||
mappingEnum.withNameOption(binding).foreach { b =>
|
||||
if (sub.onNext(EnumAnalogEvent(b, value, tpf)) == Ack.Stop) {
|
||||
sub.onComplete()
|
||||
c.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inputManager.addListener(al, entryNames: _*)
|
||||
|
||||
c := Cancelable(() => inputManager.removeListener(al))
|
||||
c
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
implicit class PhysicsSpaceExt(private val space: PhysicsSpace)
|
||||
@ -415,8 +460,10 @@ package object implicits {
|
||||
val cl = new PhysicsCollisionListener {
|
||||
override def collision(event: PhysicsCollisionEvent): Unit = {
|
||||
|
||||
if (sub.onNext(event) == Ack.Stop)
|
||||
if (sub.onNext(event) == Ack.Stop) {
|
||||
sub.onComplete()
|
||||
c.cancel()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -436,15 +483,19 @@ package object implicits {
|
||||
|
||||
override def prePhysicsTick(space: PhysicsSpace, tpf: Float): Unit = {
|
||||
val event = PhysicsTickEvent(space, tpf)
|
||||
if (sub.onNext(Left(event)) == Ack.Stop)
|
||||
if (sub.onNext(Left(event)) == Ack.Stop) {
|
||||
sub.onComplete()
|
||||
c.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
override def physicsTick(space: PhysicsSpace, tpf: Float): Unit = {
|
||||
val event = PhysicsTickEvent(space, tpf)
|
||||
if (sub.onNext(Right(event)) == Ack.Stop)
|
||||
if (sub.onNext(Right(event)) == Ack.Stop) {
|
||||
sub.onComplete()
|
||||
c.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -515,72 +566,6 @@ package object implicits {
|
||||
def mutable = new Vector3f(v.x, v.y, v.z)
|
||||
}
|
||||
|
||||
implicit val implCanMoveForBetterCharacterControl =
|
||||
new CanMove[BetterCharacterControl] {
|
||||
override def move(
|
||||
inst: BetterCharacterControl,
|
||||
direction: ImVector3f
|
||||
): Unit = {
|
||||
// val dir = direction.mutable
|
||||
// inst.setViewDirection(dir)
|
||||
// inst.setViewDirection(direction.mutable)
|
||||
inst.setWalkDirection(direction.mutable.multLocal(50f))
|
||||
}
|
||||
override def jump(inst: BetterCharacterControl): Unit = inst.jump()
|
||||
override def rotate(
|
||||
inst: BetterCharacterControl,
|
||||
rotateDir: RotateDir
|
||||
): Unit = {
|
||||
val q =
|
||||
rotateDir match {
|
||||
case RotateDir.Left =>
|
||||
new Quaternion()
|
||||
.fromAngleAxis(-10f * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y)
|
||||
case RotateDir.Right =>
|
||||
new Quaternion()
|
||||
.fromAngleAxis(10 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y)
|
||||
}
|
||||
|
||||
val tmp = new Vector3f()
|
||||
inst.getViewDirection(tmp)
|
||||
inst.setViewDirection(q.mult(tmp))
|
||||
}
|
||||
override def stop(inst: BetterCharacterControl) =
|
||||
inst.setWalkDirection(Vector3f.ZERO)
|
||||
}
|
||||
|
||||
implicit val implCanMoveForGeom = new CanMove[Spatial] with LazyLogging {
|
||||
override def move(inst: Spatial, direction: ImVector3f): Unit = {
|
||||
inst.move(direction.mutable)
|
||||
}
|
||||
override def jump(inst: Spatial): Unit =
|
||||
logger.warn("`Jump` is not implemented for type `Spatial`")
|
||||
override def rotate(inst: Spatial, rotateDir: RotateDir): Unit = {
|
||||
rotateDir match {
|
||||
case RotateDir.Left => inst.rotate(0, -0.01f, 0)
|
||||
case RotateDir.Right => inst.rotate(0, 0.01f, 0)
|
||||
}
|
||||
}
|
||||
override def stop(inst: Spatial) = {}
|
||||
}
|
||||
|
||||
implicit val implJFXConsoleStreamForTextArea =
|
||||
new JFXConsoleStreamable[scalafx.scene.control.TextArea] {
|
||||
|
||||
override def println(
|
||||
ta: scalafx.scene.control.TextArea,
|
||||
text: String
|
||||
): Unit =
|
||||
ta.appendText(text + "\n")
|
||||
|
||||
override def print(
|
||||
ta: scalafx.scene.control.TextArea,
|
||||
text: String
|
||||
): Unit =
|
||||
ta.appendText(text)
|
||||
|
||||
}
|
||||
|
||||
// val TasktoUIO = new FunctionK[Task, UIO] {
|
||||
// def apply[T](f: Task[T]): UIO[T] =
|
||||
// f.hideErrors
|
||||
|
@ -11,7 +11,7 @@ import akka.event.EventStream
|
||||
* Copied (and repurposed) from Akka's EventStream
|
||||
*/
|
||||
object EventBus {
|
||||
sealed trait Command[-A]
|
||||
sealed trait Command[A]
|
||||
final case class Publish[A, E <: A](
|
||||
event: E,
|
||||
publisherName: String
|
||||
|
@ -11,7 +11,7 @@ import akka.actor.typed.LogOptions
|
||||
import com.typesafe.scalalogging.{Logger => SLLogger}
|
||||
import wow.doge.mygame.events.EventBus
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import wow.doge.mygame.subsystems.events.MovementEvent.PlayerMovementEvent
|
||||
import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent
|
||||
import akka.actor.typed.SupervisorStrategy
|
||||
|
||||
trait EventsModule {
|
||||
@ -20,48 +20,11 @@ trait EventsModule {
|
||||
implicit def timeout: Timeout
|
||||
def eventBusLogger = SLLogger[EventBus[_]]
|
||||
|
||||
// val subscribingActor =
|
||||
// spawnProtocol.askT(
|
||||
// SpawnProtocol.Spawn[Events.PhysicsTick.type](
|
||||
// SubscribingActor(),
|
||||
// "subscriber-1",
|
||||
// Props.empty,
|
||||
// _
|
||||
// )
|
||||
// )
|
||||
|
||||
lazy val tickEventBusTask = createEventBus[Events.Tick]("tickEventBus")
|
||||
|
||||
// spawnProtocol.askL(
|
||||
// SpawnProtocol.Spawn[EventBus.Command[Events.Tick]](
|
||||
// Behaviors.logMessages(
|
||||
// logOptions = LogOptions().withLogger(eventBusLogger.underlying),
|
||||
// EventBus[Events.Tick]()
|
||||
// ),
|
||||
// "tickEventBus",
|
||||
// Props.empty,
|
||||
// _
|
||||
// )
|
||||
// )
|
||||
|
||||
lazy val playerMovementEventBusTask =
|
||||
createEventBus[PlayerMovementEvent]("movementEventBus")
|
||||
|
||||
// spawnProtocol.askL(
|
||||
// SpawnProtocol.Spawn[EventBus.Command[Events.Movement.PlayerMovement]](
|
||||
// Behaviors.logMessages(
|
||||
// logOptions = LogOptions().withLogger(eventBusLogger.underlying),
|
||||
// EventBus[Events.Movement.PlayerMovement]()
|
||||
// ),
|
||||
// "movementEventBus",
|
||||
// Props.empty,
|
||||
// _
|
||||
// )
|
||||
// )
|
||||
|
||||
// tickEventBus ! EventBus.Subscribe(subscribingActor)
|
||||
|
||||
// tickEventBus ! EventBus.Publish(Events.PhysicsTick, ctx.self)
|
||||
def createEventBus[T](busName: String) =
|
||||
spawnProtocol.askL(
|
||||
SpawnProtocol.Spawn[EventBus.Command[T]](
|
||||
@ -80,3 +43,13 @@ trait EventsModule {
|
||||
object EventTypes {
|
||||
type EventBus[T] = ActorRef[EventBus.Command[T]]
|
||||
}
|
||||
|
||||
// val subscribingActor =
|
||||
// spawnProtocol.askT(
|
||||
// SpawnProtocol.Spawn[Events.PhysicsTick.type](
|
||||
// SubscribingActor(),
|
||||
// "subscriber-1",
|
||||
// Props.empty,
|
||||
// _
|
||||
// )
|
||||
// )
|
||||
|
@ -1,59 +1,64 @@
|
||||
// package wow.doge.mygame.subsystems.events
|
||||
package wow.doge.mygame.subsystems.events
|
||||
|
||||
// import akka.actor.typed.ActorRef
|
||||
// import akka.actor.typed.SpawnProtocol
|
||||
// import wow.doge.mygame.implicits._
|
||||
// import akka.actor.typed.Props
|
||||
// import akka.actor.typed.LogOptions
|
||||
// import com.typesafe.scalalogging.{Logger => SLLogger}
|
||||
// import wow.doge.mygame.events.EventBus
|
||||
// import akka.actor.typed.scaladsl.Behaviors
|
||||
// import wow.doge.mygame.events.Events
|
||||
// import cats.effect.Resource
|
||||
// import monix.bio.Task
|
||||
// import akka.actor.typed.ActorSystem
|
||||
// import scala.concurrent.duration._
|
||||
import akka.actor.typed.ActorRef
|
||||
import akka.actor.typed.SpawnProtocol
|
||||
import wow.doge.mygame.implicits._
|
||||
import akka.actor.typed.Props
|
||||
import akka.actor.typed.LogOptions
|
||||
import com.typesafe.scalalogging.{Logger => SLLogger}
|
||||
import wow.doge.mygame.events.EventBus
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import scala.concurrent.duration._
|
||||
import akka.util.Timeout
|
||||
import akka.actor.typed.SupervisorStrategy
|
||||
import cats.effect.Resource
|
||||
import akka.actor.typed.ActorSystem
|
||||
import monix.bio.Task
|
||||
import org.slf4j.event.Level
|
||||
import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent
|
||||
|
||||
// trait EventsModule2 {
|
||||
// def eventBusesResource(
|
||||
// spawnProtocol: ActorSystem[SpawnProtocol.Command],
|
||||
// eventBusLogger: com.typesafe.scalalogging.Logger = SLLogger[EventBus[_]]
|
||||
// ): Resource[
|
||||
// Task,
|
||||
// (
|
||||
// ActorRef[EventBus.Command[Events.Tick]],
|
||||
// ActorRef[EventBus.Command[Events.Movement.PlayerMovement]]
|
||||
// )
|
||||
// ] = {
|
||||
// def createEventBus[T](busName: String) =
|
||||
// spawnProtocol.askL(
|
||||
// SpawnProtocol.Spawn[EventBus.Command[T]](
|
||||
// Behaviors.logMessages(
|
||||
// logOptions = LogOptions().withLogger(eventBusLogger.underlying),
|
||||
// EventBus[T]()
|
||||
// ),
|
||||
// busName,
|
||||
// Props.empty,
|
||||
// _
|
||||
// )
|
||||
class EventsModule2(
|
||||
spawnProtocol: ActorSystem[SpawnProtocol.Command]
|
||||
) {
|
||||
private implicit lazy val s = spawnProtocol.scheduler
|
||||
|
||||
// )(1.second, spawnProtocol.scheduler)
|
||||
private implicit lazy val timeout = Timeout(1.second)
|
||||
|
||||
// Resource.liftF {
|
||||
// {
|
||||
// lazy val tickEventBusTask = createEventBus[Events.Tick]("tickEventBus")
|
||||
private lazy val eventBusLogger = SLLogger[EventBus[_]]
|
||||
|
||||
// lazy val playerMovementEventBusTask =
|
||||
// createEventBus[Events.Movement.PlayerMovement]("movementEventBus")
|
||||
private lazy val playerMovementEventBusTask =
|
||||
createEventBus[PlayerMovementEvent]("movementEventBus")
|
||||
|
||||
// // val r = (tickEventBusTask, playerMovementEventBusTask)
|
||||
// // Task(r)
|
||||
// for {
|
||||
// tickEventBus <- tickEventBusTask
|
||||
// playerMovementEventBus <- playerMovementEventBusTask
|
||||
// } yield (tickEventBus, playerMovementEventBus)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
private lazy val playerCameraEventBusTask =
|
||||
createEventBus[PlayerCameraEvent]("playerCameraEventBus", Level.DEBUG)
|
||||
|
||||
// }
|
||||
def createEventBus[T](busName: String, logLevel: Level = Level.DEBUG) =
|
||||
spawnProtocol.askL(
|
||||
SpawnProtocol.Spawn[EventBus.Command[T]](
|
||||
Behaviors.logMessages(
|
||||
logOptions = LogOptions()
|
||||
.withLevel(logLevel)
|
||||
.withLogger(eventBusLogger.underlying),
|
||||
Behaviors
|
||||
.supervise(EventBus[T]())
|
||||
.onFailure[Exception](SupervisorStrategy.restart)
|
||||
),
|
||||
busName,
|
||||
Props.empty,
|
||||
_
|
||||
)
|
||||
)
|
||||
|
||||
type EventBuses = (
|
||||
ActorRef[
|
||||
EventBus.Command[EntityMovementEvent.PlayerMovementEvent],
|
||||
],
|
||||
ActorRef[EventBus.Command[PlayerCameraEvent]]
|
||||
)
|
||||
|
||||
val resource: Resource[Task, EventBuses] =
|
||||
Resource.liftF(for {
|
||||
playerMovementEventBus <- playerMovementEventBusTask
|
||||
playerCameraEventBus <- playerCameraEventBusTask
|
||||
} yield (playerMovementEventBus, playerCameraEventBus))
|
||||
}
|
||||
|
@ -1,20 +1,20 @@
|
||||
package wow.doge.mygame.subsystems.events
|
||||
|
||||
import wow.doge.mygame.subsystems.movement.CanMove
|
||||
import wow.doge.mygame.game.subsystems.movement.CanMove
|
||||
|
||||
sealed trait MovementEvent
|
||||
sealed trait EntityMovementEvent
|
||||
|
||||
object MovementEvent {
|
||||
object EntityMovementEvent {
|
||||
final case class MovedLeft[T: CanMove](pressed: Boolean, movable: T)
|
||||
extends MovementEvent
|
||||
extends EntityMovementEvent
|
||||
final case class MovedUp[T: CanMove](pressed: Boolean, movable: T)
|
||||
extends MovementEvent
|
||||
extends EntityMovementEvent
|
||||
final case class MovedRight[T: CanMove](pressed: Boolean, movable: T)
|
||||
extends MovementEvent
|
||||
extends EntityMovementEvent
|
||||
final case class MovedDown[T: CanMove](pressed: Boolean, movable: T)
|
||||
extends MovementEvent
|
||||
extends EntityMovementEvent
|
||||
|
||||
sealed trait PlayerMovementEvent extends MovementEvent
|
||||
sealed trait PlayerMovementEvent extends EntityMovementEvent
|
||||
object PlayerMovementEvent {
|
||||
final case class PlayerMovedLeft(pressed: Boolean)
|
||||
extends PlayerMovementEvent
|
||||
|
@ -0,0 +1,8 @@
|
||||
package wow.doge.mygame.subsystems.events
|
||||
|
||||
sealed trait PlayerCameraEvent
|
||||
|
||||
object PlayerCameraEvent {
|
||||
final case object CameraMovedUp extends PlayerCameraEvent
|
||||
final case object CameraMovedDown extends PlayerCameraEvent
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package wow.doge.mygame.subsystems.scriptsystem
|
||||
|
||||
import wow.doge.mygame.utils.AkkaUtils
|
||||
import cats.effect.Resource
|
||||
import wow.doge.mygame.scriptsystem.ScriptCachingActor
|
||||
import akka.actor.typed.ActorRef
|
||||
import akka.actor.typed.SpawnProtocol
|
||||
import akka.util.Timeout
|
||||
import akka.actor.typed.Scheduler
|
||||
|
||||
class ScriptSystemResource(
|
||||
path: os.Path,
|
||||
spawnProtocol: ActorRef[SpawnProtocol.Command]
|
||||
)(implicit timeout: Timeout, scheduler: Scheduler) {
|
||||
def make =
|
||||
Resource.liftF(
|
||||
AkkaUtils.spawnActorL(
|
||||
spawnProtocol,
|
||||
"scriptCachingActor",
|
||||
ScriptCachingActor()
|
||||
)
|
||||
)
|
||||
}
|
@ -4,7 +4,6 @@ import java.io.PrintStream
|
||||
import java.io.OutputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import scalafx.scene.control.TextArea
|
||||
import wow.doge.mygame.implicits._
|
||||
import cats.effect.Resource
|
||||
import monix.bio.Task
|
||||
|
||||
@ -41,6 +40,23 @@ object JFXConsoleStream {
|
||||
lazy val default = Config()
|
||||
}
|
||||
|
||||
implicit val implJFXConsoleStreamForTextArea =
|
||||
new JFXConsoleStreamable[scalafx.scene.control.TextArea] {
|
||||
|
||||
override def println(
|
||||
ta: scalafx.scene.control.TextArea,
|
||||
text: String
|
||||
): Unit =
|
||||
ta.appendText(text + "\n")
|
||||
|
||||
override def print(
|
||||
ta: scalafx.scene.control.TextArea,
|
||||
text: String
|
||||
): Unit =
|
||||
ta.appendText(text)
|
||||
|
||||
}
|
||||
|
||||
def textAreaStream(ta: TextArea) =
|
||||
Resource.make(
|
||||
Task(
|
||||
@ -50,4 +66,5 @@ object JFXConsoleStream {
|
||||
)
|
||||
)
|
||||
)(s => Task(s.close()))
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user