forked from nova/jmonkey-test
Rohan Sircar
4 years ago
36 changed files with 1722 additions and 770 deletions
-
4build.sbt
-
14src/main/resources/application.conf
-
4src/main/scala/com/jme3/animation/package.scala
-
2src/main/scala/com/jme3/app/package.scala
-
4src/main/scala/com/jme3/input/controls/package.scala
-
2src/main/scala/com/jme3/input/package.scala
-
4src/main/scala/com/jme3/scene/package.scala
-
57src/main/scala/org/slf4j/impl/StaticLoggerBuilder.scala
-
27src/main/scala/wow/doge/mygame/ActorSystemModule.scala
-
110src/main/scala/wow/doge/mygame/Main.scala
-
76src/main/scala/wow/doge/mygame/MainModule.scala
-
19src/main/scala/wow/doge/mygame/executors/Schedulers.scala
-
140src/main/scala/wow/doge/mygame/game/GameApp.scala
-
250src/main/scala/wow/doge/mygame/game/GameAppActor.scala
-
61src/main/scala/wow/doge/mygame/game/GameModule.scala
-
106src/main/scala/wow/doge/mygame/game/GameSystemsInitializer.scala
-
92src/main/scala/wow/doge/mygame/game/appstates/MovementActor.scala
-
47src/main/scala/wow/doge/mygame/game/appstates/PlayerMovementState.scala
-
58src/main/scala/wow/doge/mygame/game/nodes/PlayerController.scala
-
54src/main/scala/wow/doge/mygame/game/nodes/PlayerEventListeners.scala
-
237src/main/scala/wow/doge/mygame/game/subsystems/input/GameInputHandler.scala
-
9src/main/scala/wow/doge/mygame/game/subsystems/input/InputConstant.scala
-
63src/main/scala/wow/doge/mygame/game/subsystems/level/DefaultGameLevel.scala
-
184src/main/scala/wow/doge/mygame/game/subsystems/movement/MovementActor.scala
-
392src/main/scala/wow/doge/mygame/implicits/package.scala
-
104src/main/scala/wow/doge/mygame/subsystems/events/EventBus.scala
-
15src/main/scala/wow/doge/mygame/subsystems/events/Events.scala
-
81src/main/scala/wow/doge/mygame/subsystems/events/EventsModule.scala
-
59src/main/scala/wow/doge/mygame/subsystems/events/EventsModule2.scala
-
33src/main/scala/wow/doge/mygame/subsystems/events/MovementEvents.scala
-
14src/main/scala/wow/doge/mygame/subsystems/moddingsystem/ModdingSystem.scala
-
48src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptActor.scala
-
71src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptCachingActor.scala
-
25src/main/scala/wow/doge/mygame/utils/AkkaUtils.scala
-
12src/main/scala/wow/doge/mygame/utils/IOUtils.scala
-
14src/main/scala/wow/doge/mygame/utils/Settings.scala
@ -1,7 +1,7 @@ |
|||
jme-dispatcher { |
|||
type = "Dispatcher" |
|||
name = "JME-Thread" |
|||
executor = "wow.doge.mygame.executors.JMEThreadExecutorServiceConfigurator" |
|||
throughput = 1 |
|||
} |
|||
akka.jvm-exit-on-fatal-error = on |
|||
# jme-dispatcher { |
|||
# type = "Dispatcher" |
|||
# name = "JME-Thread" |
|||
# executor = "wow.doge.mygame.executors.JMEThreadExecutorServiceConfigurator" |
|||
# throughput = 1 |
|||
# } |
|||
# akka.jvm-exit-on-fatal-error = on |
@ -0,0 +1,27 @@ |
|||
package wow.doge.mygame |
|||
|
|||
import akka.actor.typed.ActorSystem |
|||
import cats.effect.Resource |
|||
import monix.bio.Task |
|||
import io.odin.Logger |
|||
import wow.doge.mygame.game.GameApp |
|||
import wow.doge.mygame.executors.Schedulers |
|||
|
|||
trait ActorSystemModule { |
|||
|
|||
def logger: Logger[Task] |
|||
def app: GameApp |
|||
def schedulers: Schedulers |
|||
|
|||
lazy val actorsResource = |
|||
Resource.make(logger.info("Creating Actor System") >> Task { |
|||
ActorSystem( |
|||
RootActor(app, schedulers, logger = logger), |
|||
name = "GameActorSystem" |
|||
) |
|||
})(sys => |
|||
logger.info("Shutting down actor system") >> Task( |
|||
sys.terminate() |
|||
) |
|||
) |
|||
} |
@ -1,10 +1,23 @@ |
|||
package wow.doge.mygame.executors |
|||
|
|||
import monix.execution.Scheduler |
|||
import monix.execution.UncaughtExceptionReporter |
|||
import com.typesafe.scalalogging.Logger |
|||
|
|||
final case class Schedulers( |
|||
blockingIO: Scheduler = Scheduler.io(), |
|||
async: Scheduler = Scheduler.global, |
|||
blockingIO: Scheduler = Scheduler |
|||
.io() |
|||
.withUncaughtExceptionReporter(Schedulers.reporter), |
|||
async: Scheduler = Scheduler.global |
|||
.withUncaughtExceptionReporter(Schedulers.reporter), |
|||
fx: Scheduler = JFXExecutionContexts.fxScheduler |
|||
// jme: SchedulerService |
|||
.withUncaughtExceptionReporter(Schedulers.reporter) |
|||
) |
|||
|
|||
object Schedulers { |
|||
val reporter = UncaughtExceptionReporter { ex => |
|||
val logger = Logger[Schedulers] |
|||
logger.error("Uncaught exception", ex) |
|||
} |
|||
|
|||
} |
@ -1,25 +1,70 @@ |
|||
package wow.doge.mygame.game |
|||
|
|||
import cats.effect.Resource |
|||
import com.jme3.app.state.AppState |
|||
import com.jme3.system.AppSettings |
|||
import monix.bio.Task |
|||
import io.odin.Logger |
|||
import akka.actor.typed.ActorRef |
|||
import akka.actor.typed.SpawnProtocol |
|||
import wow.doge.mygame.game.subsystems.input.GameInputHandler |
|||
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])] = |
|||
Resource.make( |
|||
for { |
|||
_ <- logger.info("Creating game app") |
|||
app <- Task(new GameApp(new StatsAppState())) |
|||
_ <- Task { |
|||
val settings = new AppSettings(true) |
|||
settings.setVSync(true) |
|||
settings.setUseInput(true) |
|||
// new FlyCamAppState |
|||
// settings.setFrameRate(250) |
|||
app.setSettings(settings) |
|||
// JMERunner.runner = app |
|||
app |
|||
} |
|||
fib <- Task(app.start()).executeOn(jmeScheduler).start |
|||
} yield (app -> fib) |
|||
)(logger.info("Closing game app") >> _._2.cancel) |
|||
} |
|||
|
|||
trait GameModule { |
|||
|
|||
def gameAppResource(appStates: AppState*): Resource[Task, GameApp] = |
|||
Resource.liftF { |
|||
for { |
|||
app <- Task(new GameApp(appStates: _*)) |
|||
def gameAppResource( |
|||
logger: Logger[Task], |
|||
jmeScheduler: Scheduler |
|||
): Resource[Task, (GameApp, Fiber[Throwable, Unit])] = |
|||
Resource.make( |
|||
(for { |
|||
_ <- logger.info("Creating game app") |
|||
app <- Task(new GameApp()) |
|||
_ <- Task { |
|||
val settings = new AppSettings(true) |
|||
// settings.setVSync(true) |
|||
settings.setFrameRate(144) |
|||
settings.setVSync(true) |
|||
// settings.setFrameRate(250) |
|||
app.setSettings(settings) |
|||
// JMERunner.runner = app |
|||
app |
|||
} |
|||
} yield (app) |
|||
fib <- Task(app.start()).executeOn(jmeScheduler).start |
|||
} yield (app -> fib)) |
|||
)(_._2.cancel) |
|||
|
|||
def inputHandlerSystemResource( |
|||
props: GameInputHandler.Props |
|||
): Resource[Task, Task[Unit]] = |
|||
Resource.liftF { |
|||
Task.evalAsync(props.begin) |
|||
} |
|||
def gameSystemsResource( |
|||
spawnProtocol: ActorRef[SpawnProtocol.Command], |
|||
gameSystems: Task[Unit]* |
|||
): Resource[Task, List[Unit]] = |
|||
Resource.liftF(IO.defer(Task.parSequence(gameSystems))) |
|||
} |
@ -1,13 +1,107 @@ |
|||
package wow.doge.mygame.game |
|||
|
|||
import wow.doge.mygame.state.MyBaseState |
|||
import scala.concurrent.duration._ |
|||
|
|||
class GameSystemsInitializer extends MyBaseState { |
|||
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.movement.ImMovementActor |
|||
import wow.doge.mygame.utils.IOUtils |
|||
|
|||
override protected def onEnable(): Unit = {} |
|||
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) |
|||
|
|||
override protected def onDisable(): Unit = {} |
|||
import GameSystemsInitializer._ |
|||
|
|||
override protected def init(): Unit = {} |
|||
override def stop(): Unit = {} |
|||
def init = |
|||
for { |
|||
playerMovementEventBus <- playerMovementEventBusTask |
|||
inputManager = app.inputManager |
|||
bulletAppState = new BulletAppState() |
|||
_ <- Task(app.stateManager.attach(bulletAppState)) |
|||
_ <- Task( |
|||
app.assetManager.registerLocator( |
|||
// "src/main/resources/assets/town.zip", |
|||
(os.rel / "src" / "main" / "resources" / "assets" / "town.zip"), |
|||
classOf[ZipLocator] |
|||
) |
|||
) |
|||
_ <- 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) |
|||
// ) |
|||
_ <- wire[GameInputHandler.Props].begin |
|||
|
|||
} yield () |
|||
} |
|||
|
|||
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)) |
|||
) |
|||
// .mapTask() |
|||
} |
@ -1,92 +0,0 @@ |
|||
package wow.doge.mygame.state |
|||
|
|||
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 |
|||
|
|||
trait CanMove[-A] { |
|||
def getDirection(cam: Camera, cardinalDir: CardinalDirection): ImVector3f |
|||
def move(inst: A, direction: ImVector3f): Unit |
|||
} |
|||
|
|||
object ImMovementActor { |
|||
sealed trait Command |
|||
// final case class Tick(tpf: Float) extends Command |
|||
final case class Tick(tpf: Float) extends Command |
|||
|
|||
sealed trait Movement extends Command |
|||
final case class MovedLeft(pressed: Boolean) extends Movement |
|||
final case class MovedUp(pressed: Boolean) extends Movement |
|||
final case class MovedRight(pressed: Boolean) extends Movement |
|||
final case class MovedDown(pressed: Boolean) extends Movement |
|||
|
|||
final case class Props[T: CanMove]( |
|||
app: com.jme3.app.Application, |
|||
movable: T |
|||
) |
|||
|
|||
/** |
|||
* Internal state of the actor |
|||
* |
|||
* @param cardinalDir Immutable, can be shared as is |
|||
* @param walkDirection Immutable |
|||
*/ |
|||
final case class State( |
|||
cardinalDir: CardinalDirection = CardinalDirection() |
|||
) |
|||
|
|||
def apply[T: CanMove](props: Props[T]): Behavior[Command] = |
|||
Behaviors.setup(ctx => { |
|||
ctx.log.info("Hello from MovementActor") |
|||
new ImMovementActor(ctx, props).receive(State()) |
|||
}) |
|||
|
|||
} |
|||
|
|||
class ImMovementActor[T]( |
|||
ctx: ActorContext[ImMovementActor.Command], |
|||
props: ImMovementActor.Props[T] |
|||
) { |
|||
import ImMovementActor._ |
|||
|
|||
def receive( |
|||
state: ImMovementActor.State |
|||
)(implicit cm: CanMove[T]): Behavior[Command] = |
|||
Behaviors.receiveMessage { msg => |
|||
msg match { |
|||
case m: Movement => |
|||
m match { |
|||
case MovedLeft(pressed) => |
|||
receive(state = state.modify(_.cardinalDir.left).setTo(pressed)) |
|||
case MovedUp(pressed) => |
|||
receive(state = state.modify(_.cardinalDir.up).setTo(pressed)) |
|||
case MovedRight(pressed) => |
|||
receive(state = state.modify(_.cardinalDir.right).setTo(pressed)) |
|||
case MovedDown(pressed) => |
|||
receive(state = state.modify(_.cardinalDir.down).setTo(pressed)) |
|||
} |
|||
|
|||
case Tick(tpf) => |
|||
val walkDir = |
|||
cm.getDirection(props.app.getCamera(), state.cardinalDir) |
|||
if (walkDir != ImVector3f.ZERO) { |
|||
val tmp = walkDir * 25f * tpf |
|||
// props.app.enqueue(new Runnable { |
|||
// override def run(): Unit = { |
|||
// cm.move(props.movable, tmp) |
|||
// } |
|||
// }) |
|||
props.app.enqueueF { |
|||
cm.move(props.movable, tmp) |
|||
} |
|||
} |
|||
Behaviors.same |
|||
// receive(state = state.modify(_.walkDirection).setTo(walkDir)) |
|||
|
|||
} |
|||
} |
|||
} |
@ -0,0 +1,54 @@ |
|||
package wow.doge.mygame.game.nodes |
|||
|
|||
import akka.actor.typed.ActorRef |
|||
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 com.typesafe.scalalogging.Logger |
|||
|
|||
object PlayerMovementEventHandler { |
|||
import PlayerMovementEvent._ |
|||
def apply(movementActor: ActorRef[ImMovementActor.Command]) = |
|||
Behaviors.logMessages( |
|||
LogOptions() |
|||
.withLevel(Level.TRACE) |
|||
.withLogger( |
|||
Logger[PlayerMovementEventHandler.type].underlying |
|||
), |
|||
Behaviors.setup[PlayerMovementEvent](ctx => |
|||
Behaviors.receiveMessage { |
|||
case PlayerMovedLeft(pressed) => |
|||
movementActor ! ImMovementActor.MovedLeft(pressed) |
|||
Behaviors.same |
|||
case PlayerMovedRight(pressed) => |
|||
movementActor ! ImMovementActor.MovedRight(pressed) |
|||
Behaviors.same |
|||
case PlayerMovedForward(pressed) => |
|||
movementActor ! ImMovementActor.MovedUp(pressed) |
|||
Behaviors.same |
|||
case PlayerMovedBackward(pressed) => |
|||
movementActor ! ImMovementActor.MovedDown(pressed) |
|||
Behaviors.same |
|||
case PlayerJumped => |
|||
movementActor ! ImMovementActor.Jump |
|||
Behaviors.same |
|||
case PlayerRotatedRight => |
|||
// ctx.log.warn("right rotate not implemented yet") |
|||
movementActor ! ImMovementActor.RotateRight |
|||
Behaviors.same |
|||
case PlayerRotatedLeft => |
|||
// ctx.log.warn("left rotate not implemented yet") |
|||
movementActor ! ImMovementActor.RotateLeft |
|||
Behaviors.same |
|||
case PlayerCameraUp => |
|||
ctx.log.warn("camera up not implemented yet") |
|||
Behaviors.same |
|||
case PlayerCameraDown => |
|||
ctx.log.warn("camera down not implemented yet") |
|||
Behaviors.same |
|||
} |
|||
) |
|||
) |
|||
} |
@ -0,0 +1,237 @@ |
|||
package wow.doge.mygame.game.subsystems.input |
|||
|
|||
import com.jme3.input.InputManager |
|||
import wow.doge.mygame.implicits._ |
|||
import akka.actor.typed.ActorRef |
|||
import wow.doge.mygame.events.EventBus |
|||
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 scala.concurrent.duration._ |
|||
import com.jme3.input.controls.MouseAxisTrigger |
|||
import com.jme3.input.MouseInput |
|||
|
|||
// class GameInputHandler( |
|||
// inputManager: InputManager |
|||
// // inputEventBus: InputEventBus |
|||
// ) {} |
|||
|
|||
object GameInputHandler { |
|||
|
|||
final case class Props( |
|||
inputManager: InputManager, |
|||
playerMovementEventBus: ActorRef[ |
|||
EventBus.Command[PlayerMovementEvent] |
|||
] |
|||
) { |
|||
def begin = |
|||
for { |
|||
_ <- UIO(setupKeys(inputManager)) |
|||
_ <- toIO( |
|||
generateMovementInputEvents( |
|||
inputManager, |
|||
playerMovementEventBus |
|||
).completedL.startAndForget |
|||
) |
|||
_ <- toIO( |
|||
generateRotateEvents( |
|||
inputManager, |
|||
playerMovementEventBus |
|||
).completedL.startAndForget |
|||
) |
|||
_ <- toIO( |
|||
generateCameraEvents( |
|||
inputManager, |
|||
playerMovementEventBus |
|||
).completedL.startAndForget |
|||
) |
|||
} yield () |
|||
} |
|||
|
|||
def setupKeys(inputManager: InputManager) = |
|||
inputManager |
|||
.withMapping( |
|||
"Left", |
|||
new KeyTrigger(KeyInput.KEY_A) |
|||
// new KeyTrigger(KeyInput.KEY_LEFT) |
|||
) |
|||
.withMapping( |
|||
"Right", |
|||
new KeyTrigger(KeyInput.KEY_D) |
|||
// new KeyTrigger(KeyInput.KEY_RIGHT) |
|||
) |
|||
.withMapping( |
|||
"Up", |
|||
new KeyTrigger(KeyInput.KEY_W) |
|||
// new KeyTrigger(KeyInput.KEY_UP) |
|||
) |
|||
.withMapping( |
|||
"Down", |
|||
new KeyTrigger(KeyInput.KEY_S) |
|||
// new KeyTrigger(KeyInput.KEY_DOWN) |
|||
) |
|||
.withMapping( |
|||
"Jump", |
|||
new KeyTrigger(KeyInput.KEY_SPACE) |
|||
) |
|||
.withMapping( |
|||
"ROTATE_RIGHT", |
|||
new KeyTrigger(KeyInput.KEY_RIGHT), |
|||
new MouseAxisTrigger(MouseInput.AXIS_X, true) |
|||
) |
|||
.withMapping( |
|||
"ROTATE_LEFT", |
|||
new KeyTrigger(KeyInput.KEY_LEFT), |
|||
new MouseAxisTrigger(MouseInput.AXIS_X, false) |
|||
) |
|||
.withMapping( |
|||
"CAMERA_UP", |
|||
// new KeyTrigger(KeyInput.KEY_LEFT), |
|||
new MouseAxisTrigger(MouseInput.AXIS_Y, false) |
|||
) |
|||
.withMapping( |
|||
"CAMERA_DOWN", |
|||
// new KeyTrigger(KeyInput.KEY_LEFT), |
|||
new MouseAxisTrigger(MouseInput.AXIS_Y, true) |
|||
) |
|||
.setCursorVisible(false) |
|||
|
|||
def generateMovementInputEvents( |
|||
inputManager: InputManager, |
|||
playerMovementEventBus: ActorRef[ |
|||
EventBus.Command[PlayerMovementEvent] |
|||
] |
|||
) = { |
|||
val name = "movementInputEventsGenerator" |
|||
inputManager |
|||
.observableAction( |
|||
"Left", |
|||
"Right", |
|||
"Up", |
|||
"Down", |
|||
"Jump", |
|||
"ROTATE_RIGHT", |
|||
"ROTATE_LEFT" |
|||
) |
|||
// .dump("O") |
|||
.doOnNext { action => |
|||
action.binding.name match { |
|||
case "Left" => |
|||
toTask( |
|||
playerMovementEventBus !! EventBus.Publish( |
|||
PlayerMovementEvent.PlayerMovedLeft(pressed = action.value), |
|||
name |
|||
) |
|||
) |
|||
case "Right" => |
|||
toTask( |
|||
playerMovementEventBus !! EventBus.Publish( |
|||
PlayerMovementEvent.PlayerMovedRight(pressed = action.value), |
|||
name |
|||
) |
|||
) |
|||
case "Up" => |
|||
toTask( |
|||
playerMovementEventBus !! EventBus.Publish( |
|||
PlayerMovementEvent.PlayerMovedForward(pressed = action.value), |
|||
name |
|||
) |
|||
) |
|||
case "Down" => |
|||
toTask( |
|||
playerMovementEventBus !! EventBus.Publish( |
|||
PlayerMovementEvent.PlayerMovedBackward(pressed = action.value), |
|||
name |
|||
) |
|||
) |
|||
|
|||
case "Jump" if action.value => |
|||
toTask( |
|||
playerMovementEventBus !! EventBus.Publish( |
|||
PlayerMovementEvent.PlayerJumped, |
|||
name |
|||
) |
|||
) |
|||
|
|||
case _ => monix.eval.Task.unit |
|||
} |
|||
} |
|||
} |
|||
|
|||
def generateRotateEvents( |
|||
inputManager: InputManager, |
|||
playerMovementEventBus: ActorRef[ |
|||
EventBus.Command[PlayerMovementEvent] |
|||
] |
|||
) = { |
|||
val name = "rotateMovementEventsGenerator" |
|||
inputManager |
|||
.analogObservable("ROTATE_RIGHT", "ROTATE_LEFT") |
|||
.sample(1.millis) |
|||
.mapEval(analogEvent => |
|||
analogEvent.binding.name match { |
|||
case "ROTATE_RIGHT" => |
|||
toTask( |
|||
playerMovementEventBus !! EventBus.Publish( |
|||
PlayerMovementEvent.PlayerRotatedRight, |
|||
name |
|||
) |
|||
) |
|||
case "ROTATE_LEFT" => |
|||
toTask( |
|||
playerMovementEventBus !! EventBus.Publish( |
|||
PlayerMovementEvent.PlayerRotatedLeft, |
|||
name |
|||
) |
|||
) |
|||
case _ => monix.eval.Task.unit |
|||
} |
|||
) |
|||
} |
|||
|
|||
def generateCameraEvents( |
|||
inputManager: InputManager, |
|||
playerMovementEventBus: ActorRef[ |
|||
EventBus.Command[PlayerMovementEvent] |
|||
] |
|||
) = { |
|||
val name = "cameraMovementEventsGenerator" |
|||
inputManager |
|||
.analogObservable("CAMERA_UP", "CAMERA_DOWN") |
|||
.sample(1.millis) |
|||
.mapEval(analogEvent => |
|||
analogEvent.binding.name match { |
|||
case "CAMERA_UP" => |
|||
toTask( |
|||
playerMovementEventBus !! EventBus.Publish( |
|||
PlayerMovementEvent.PlayerCameraUp, |
|||
name |
|||
) |
|||
) |
|||
case "CAMERA_DOWN" => |
|||
toTask( |
|||
playerMovementEventBus !! EventBus.Publish( |
|||
PlayerMovementEvent.PlayerCameraDown, |
|||
name |
|||
) |
|||
) |
|||
case _ => monix.eval.Task.unit |
|||
} |
|||
) |
|||
} |
|||
|
|||
// def bindMappings(inputManager: InputManager, mappings: ActionMapping*) = { |
|||
// inputManager |
|||
// .observableAction(mappings.map(_.name): _*) |
|||
// .doOnNext(action => |
|||
// mappings.map(m => |
|||
// if (action.binding.name == m.name) toTask(m.cb(action)) |
|||
// else monix.eval.Task.unit |
|||
// ) |
|||
// ) |
|||
// } |
|||
} |
|||
|
|||
// case class ActionMapping(name: String, cb: ActionEvent => Task[Unit]) |
@ -0,0 +1,9 @@ |
|||
package wow.doge.mygame.game.subsystems.input |
|||
|
|||
object InputConstants { |
|||
val PLAYER_MOVE_LEFT = "PLAYER_MOVE_LEFT" |
|||
val PLAYER_MOVE_RIGHT = "PLAYER_MOVE_RIGHT" |
|||
val PLAYER_MOVE_FORWARD = "PLAYER_MOVE_FORWARD" |
|||
val PLAYER_MOVE_BACKWARD = "PLAYER_MOVE_BACKWARD" |
|||
val PLAYER_JUMP = "PLAYER_JUMP " |
|||
} |
@ -0,0 +1,63 @@ |
|||
package wow.doge.mygame.game.subsystems.level |
|||
import com.jme3.bullet.BulletAppState |
|||
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape |
|||
import com.jme3.bullet.control.CharacterControl |
|||
import com.jme3.bullet.control.RigidBodyControl |
|||
import com.jme3.bullet.util.CollisionShapeFactory |
|||
import com.jme3.scene.Spatial |
|||
import wow.doge.mygame.implicits._ |
|||
import wow.doge.mygame.game.GameApp |
|||
import com.jme3.syntax._ |
|||
import com.jme3.math.ColorRGBA |
|||
import com.jme3.light.DirectionalLight |
|||
import com.jme3.math.Vector3f |
|||
import com.jme3.light.AmbientLight |
|||
object DefaultGameLevel { |
|||
|
|||
// lazy valbulletAppState: BulletAppState |
|||
// bulletAppState.setThreadingType(ThreadingType.SEQUENTIAL) |
|||
|
|||
// We set up collision detection for the scene by creating a |
|||
// compound collision shape and a static RigidBodyControl with mass zero. |
|||
|
|||
// We set up collision detection for the player by creating |
|||
// a capsule collision shape and a CharacterControl. |
|||
// The CharacterControl offers extra settings for |
|||
// size, stepheight, jumping, falling, and gravity. |
|||
// We also put the player in its starting position. |
|||
lazy val capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1) |
|||
|
|||
lazy val player: CharacterControl = |
|||
new CharacterControl(capsuleShape, 0.05f) |
|||
def apply(app: GameApp, bulletAppState: BulletAppState) = { |
|||
lazy val sceneModel: Spatial = app.assetManager.loadModel("main.scene") |
|||
lazy val sceneShape = CollisionShapeFactory.createMeshShape( |
|||
sceneModel.toNode match { |
|||
case util.Right(node) => node |
|||
case util.Left(ex) => |
|||
throw new NotImplementedError("No fallback sceneshape") |
|||
} |
|||
) |
|||
lazy val landscape: RigidBodyControl = |
|||
new RigidBodyControl(sceneShape, 0) |
|||
|
|||
// // discard { app.stateManager.attach(bulletAppState) } |
|||
|
|||
app.viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f)) |
|||
sceneModel.setLocalScale(2f) |
|||
sceneModel.addControl(landscape) |
|||
discard { app.rootNode.attachChild(sceneModel) } |
|||
bulletAppState.getPhysicsSpace.add(landscape) |
|||
bulletAppState.getPhysicsSpace.add(player) |
|||
|
|||
val al = new AmbientLight(); |
|||
al.setColor(ColorRGBA.White.mult(1.3f)); |
|||
app.rootNode.addLight(al); |
|||
|
|||
val dl = new DirectionalLight(); |
|||
dl.setColor(ColorRGBA.White); |
|||
dl.setDirection(new Vector3f(2.8f, -2.8f, -2.8f).normalizeLocal()); |
|||
app.rootNode.addLight(dl); |
|||
|
|||
} |
|||
} |
@ -0,0 +1,184 @@ |
|||
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 |
|||
|
|||
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 |
|||
|
|||
sealed trait RotateDir |
|||
object RotateDir { |
|||
case object Left extends 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 |
|||
|
|||
sealed trait Movement extends Command |
|||
final case class MovedLeft(pressed: Boolean) extends Movement |
|||
final case class MovedUp(pressed: Boolean) extends Movement |
|||
final case class MovedRight(pressed: Boolean) extends Movement |
|||
final case class MovedDown(pressed: Boolean) extends Movement |
|||
final case object Jump extends Movement |
|||
final case object RotateRight extends Movement |
|||
final case object RotateLeft extends Movement |
|||
|
|||
final case class Props[T: CanMove]( |
|||
app: GameApp, |
|||
movable: T, |
|||
playerMovementEventBus: ActorRef[ |
|||
EventBus.Command[PlayerMovementEvent] |
|||
] |
|||
) { |
|||
def create: Behavior[Command] = |
|||
Behaviors.setup(ctx => { |
|||
ctx.log.info("Hello from MovementActor") |
|||
// val playerMovementEventHandler = ctx.spawn( |
|||
// PlayerMovementEventHandler(ctx.self), |
|||
// "playerMovementEventHandler" |
|||
// ) |
|||
// playerMovementEventBus ! EventBus.Subscribe(playerMovementEventHandler) |
|||
new ImMovementActor(ctx, this).receive(State()) |
|||
}) |
|||
} |
|||
|
|||
/** |
|||
* Internal state of the actor |
|||
* |
|||
* @param cardinalDir The four directions the character can move |
|||
*/ |
|||
final case class State(cardinalDir: CardinalDirection = CardinalDirection()) |
|||
|
|||
// def apply[T: CanMove](props: Props[T]): Behavior[Command] = |
|||
// Behaviors.setup(ctx => { |
|||
// ctx.log.info("Hello from MovementActor") |
|||
// val playerMovementEventHandler = ctx.spawn( |
|||
// PlayerMovementEventHandler(ctx.self), |
|||
// "playerMovementEventHandler" |
|||
// ) |
|||
// props.playerMovementEventBus ! EventBus.Subscribe( |
|||
// playerMovementEventHandler |
|||
// ) |
|||
// new ImMovementActor(ctx, props).receive(State()) |
|||
// }) |
|||
|
|||
} |
|||
|
|||
class ImMovementActor[T]( |
|||
ctx: ActorContext[ImMovementActor.Command], |
|||
props: ImMovementActor.Props[T] |
|||
) { |
|||
import ImMovementActor._ |
|||
import Methods._ |
|||
|
|||
def receive( |
|||
state: ImMovementActor.State |
|||
)(implicit cm: CanMove[T]): Behavior[Command] = |
|||
Behaviors.receiveMessage { |
|||
case m: Movement => |
|||
m match { |
|||
case MovedLeft(pressed) => |
|||
props.app.enqueueF(stopIfNotPressed(pressed, props.movable)) |
|||
receive(state = state.modify(_.cardinalDir.left).setTo(pressed)) |
|||
case MovedUp(pressed) => |
|||
props.app.enqueueF(stopIfNotPressed(pressed, props.movable)) |
|||
receive(state = state.modify(_.cardinalDir.up).setTo(pressed)) |
|||
case MovedRight(pressed) => |
|||
props.app.enqueueF(stopIfNotPressed(pressed, props.movable)) |
|||
receive(state = state.modify(_.cardinalDir.right).setTo(pressed)) |
|||
case MovedDown(pressed) => |
|||
props.app.enqueueF(stopIfNotPressed(pressed, props.movable)) |
|||
receive(state = state.modify(_.cardinalDir.down).setTo(pressed)) |
|||
case Jump => |
|||
props.app.enqueueF(cm.jump(props.movable)) |
|||
Behaviors.same |
|||
case RotateLeft => |
|||
props.app.enqueueF(cm.rotate(props.movable, RotateDir.Left)) |
|||
Behaviors.same |
|||
case RotateRight => |
|||
props.app.enqueueF(cm.rotate(props.movable, RotateDir.Right)) |
|||
Behaviors.same |
|||
} |
|||
|
|||
case Tick(tpf) => |
|||
val walkDir = |
|||
getDirection(state.cardinalDir, ctx.log.trace) |
|||
if (walkDir != ImVector3f.ZERO) { |
|||
val tmp = walkDir * 25f * tpf |
|||
props.app.enqueueF { |
|||
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 |
|||
} |
|||
} |
|||
object Methods { |
|||
def getDirection(cardinalDir: CardinalDirection, trace: String => Unit) = { |
|||
val zero = ImVector3f.ZERO |
|||
val dir = cardinalDir |
|||
val walkDir = { |
|||
val mutWalkDir = new Vector3f() |
|||
if (dir.left) { |
|||
trace("left") |
|||
mutWalkDir += zero +=: new Vector3f(-1, 0, 0) |
|||
} |
|||
if (dir.right) { |
|||
trace("right") |
|||
mutWalkDir += zero +=: new Vector3f(1, 0, 0) |
|||
} |
|||
if (dir.up) { |
|||
trace("up") |
|||
mutWalkDir += zero +=: new Vector3f(0, 0, -1) |
|||
} |
|||
if (dir.down) { |
|||
trace("down") |
|||
mutWalkDir += zero +=: new Vector3f(0, 0, 1) |
|||
} |
|||
mutWalkDir.immutable |
|||
} |
|||
walkDir |
|||
} |
|||
|
|||
def stopIfNotPressed[T](pressed: Boolean, movable: T)(implicit |
|||
cm: CanMove[T] |
|||
) = |
|||
if (!pressed) cm.stop(movable) |
|||
} |
@ -1,17 +1,14 @@ |
|||
package wow.doge.mygame.events |
|||
|
|||
// object Test { |
|||
|
|||
// Events.BulletFired |
|||
// } |
|||
|
|||
object Events { |
|||
sealed trait Event |
|||
case object BulletFired extends Event |
|||
final case object BulletFired extends Event |
|||
// type BulletFired = BulletFired.type |
|||
case class EventWithData(data: Int) extends Event |
|||
final case class EventWithData(data: Int) extends Event |
|||
|
|||
sealed trait Tick extends Event |
|||
case object RenderTick extends Tick |
|||
case object PhysicsTick extends Tick |
|||
object Tick { |
|||
final case object RenderTick extends Tick |
|||
final case object PhysicsTick extends Tick |
|||
} |
|||
} |
@ -1,3 +1,82 @@ |
|||
package wow.doge.mygame.events |
|||
|
|||
trait EventsModule {} |
|||
import akka.actor.typed.ActorRef |
|||
import akka.actor.typed.SpawnProtocol |
|||
import wow.doge.mygame.implicits._ |
|||
import akka.actor.typed.scaladsl.AskPattern._ |
|||
import akka.actor.typed.Props |
|||
import akka.util.Timeout |
|||
import akka.actor.typed.Scheduler |
|||
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 akka.actor.typed.SupervisorStrategy |
|||
|
|||
trait EventsModule { |
|||
def spawnProtocol: ActorRef[SpawnProtocol.Command] |
|||
implicit def akkaScheduler: Scheduler |
|||
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]]( |
|||
Behaviors.logMessages( |
|||
logOptions = LogOptions().withLogger(eventBusLogger.underlying), |
|||
Behaviors |
|||
.supervise(EventBus[T]()) |
|||
.onFailure[Exception](SupervisorStrategy.restart) |
|||
), |
|||
busName, |
|||
Props.empty, |
|||
_ |
|||
) |
|||
) |
|||
} |
|||
object EventTypes { |
|||
type EventBus[T] = ActorRef[EventBus.Command[T]] |
|||
} |
@ -0,0 +1,59 @@ |
|||
// 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._ |
|||
|
|||
// 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, |
|||
// _ |
|||
// ) |
|||
|
|||
// )(1.second, spawnProtocol.scheduler) |
|||
|
|||
// Resource.liftF { |
|||
// { |
|||
// lazy val tickEventBusTask = createEventBus[Events.Tick]("tickEventBus") |
|||
|
|||
// lazy val playerMovementEventBusTask = |
|||
// createEventBus[Events.Movement.PlayerMovement]("movementEventBus") |
|||
|
|||
// // val r = (tickEventBusTask, playerMovementEventBusTask) |
|||
// // Task(r) |
|||
// for { |
|||
// tickEventBus <- tickEventBusTask |
|||
// playerMovementEventBus <- playerMovementEventBusTask |
|||
// } yield (tickEventBus, playerMovementEventBus) |
|||
// } |
|||
// } |
|||
// } |
|||
|
|||
// } |
@ -0,0 +1,33 @@ |
|||
package wow.doge.mygame.subsystems.events |
|||
|
|||
import wow.doge.mygame.subsystems.movement.CanMove |
|||
|
|||
sealed trait MovementEvent |
|||
|
|||
object MovementEvent { |
|||
final case class MovedLeft[T: CanMove](pressed: Boolean, movable: T) |
|||
extends MovementEvent |
|||
final case class MovedUp[T: CanMove](pressed: Boolean, movable: T) |
|||
extends MovementEvent |
|||
final case class MovedRight[T: CanMove](pressed: Boolean, movable: T) |
|||
extends MovementEvent |
|||
final case class MovedDown[T: CanMove](pressed: Boolean, movable: T) |
|||
extends MovementEvent |
|||
|
|||
sealed trait PlayerMovementEvent extends MovementEvent |
|||
object PlayerMovementEvent { |
|||
final case class PlayerMovedLeft(pressed: Boolean) |
|||
extends PlayerMovementEvent |
|||
final case class PlayerMovedRight(pressed: Boolean) |
|||
extends PlayerMovementEvent |
|||
final case class PlayerMovedForward(pressed: Boolean) |
|||
extends PlayerMovementEvent |
|||
final case class PlayerMovedBackward(pressed: Boolean) |
|||
extends PlayerMovementEvent |
|||
final case object PlayerJumped extends PlayerMovementEvent |
|||
final case object PlayerRotatedRight extends PlayerMovementEvent |
|||
final case object PlayerRotatedLeft extends PlayerMovementEvent |
|||
final case object PlayerCameraUp extends PlayerMovementEvent |
|||
final case object PlayerCameraDown extends PlayerMovementEvent |
|||
} |
|||
} |
@ -0,0 +1,25 @@ |
|||
package wow.doge.mygame.utils |
|||
|
|||
import akka.actor.typed.Props |
|||
import akka.util.Timeout |
|||
import akka.actor.typed.Scheduler |
|||
import akka.actor.typed.ActorRef |
|||
import akka.actor.typed.SpawnProtocol |
|||
import akka.actor.typed.Behavior |
|||
import wow.doge.mygame.implicits._ |
|||
|
|||
object AkkaUtils { |
|||
def spawnActorL[T]( |
|||
spawnProtocol: ActorRef[SpawnProtocol.Command], |
|||
actorName: String, |
|||
behavior: Behavior[T] |
|||
)(implicit timeout: Timeout, scheduler: Scheduler) = |
|||
spawnProtocol.askL[ActorRef[T]]( |
|||
SpawnProtocol.Spawn( |
|||
behavior, |
|||
actorName, |
|||
Props.empty, |
|||
_ |
|||
) |
|||
) |
|||
} |
@ -0,0 +1,12 @@ |
|||
package wow.doge.mygame.utils |
|||
|
|||
import monix.bio.IO |
|||
|
|||
object IOUtils { |
|||
def toTask[T](bio: monix.bio.IO[Throwable, T]) = |
|||
monix.eval.Task.deferAction(implicit s => bio.to[monix.eval.Task]) |
|||
|
|||
def toIO[T](task: monix.eval.Task[T]) = |
|||
IO.deferAction(implicit s => IO.from(task)) |
|||
|
|||
} |
@ -0,0 +1,14 @@ |
|||
package wow.doge.mygame.utils |
|||
|
|||
case class Display( |
|||
width: Int = 640, |
|||
height: Int = 480, |
|||
title: String = "JME-Game", |
|||
fullScren: Boolean = false, |
|||
vsync: Boolean = false, |
|||
frameRate: Int = -1 |
|||
) |
|||
object Display { |
|||
val default = Display() |
|||
} |
|||
case class GlobalSettings(display: Display = Display.default) |
Write
Preview
Loading…
Cancel
Save
Reference in new issue