Add IO wrapper for InputManager

This commit is contained in:
Rohan Sircar 2021-03-10 19:25:12 +05:30
parent 4ff54c1373
commit dd01b070ff
6 changed files with 249 additions and 33 deletions

View File

@ -53,8 +53,10 @@ import wow.doge.mygame.game.entities.NpcActorSupervisor
import wow.doge.mygame.game.entities.NpcMovementActor
import wow.doge.mygame.game.entities.player.PlayerActor
import wow.doge.mygame.game.entities.player.PlayerController
import wow.doge.mygame.game.subsystems.input.GameInputHandler
import wow.doge.mygame.game.entities.player.PlayerMovementReducer
import wow.doge.mygame.game.subsystems.input.InputMappings
import wow.doge.mygame.game.subsystems.input.PlayerCameraInput
import wow.doge.mygame.game.subsystems.input.PlayerMovementInput
import wow.doge.mygame.game.subsystems.level.DefaultGameLevel
import wow.doge.mygame.implicits._
import wow.doge.mygame.launcher.Launcher
@ -80,6 +82,7 @@ import wow.doge.mygame.utils.IOUtils
import wow.doge.mygame.utils.MonixDirectoryWatcher
import wow.doge.mygame.utils.MonixDirectoryWatcher.ModifyEvent
import wow.doge.mygame.utils.controls.JFXProgressBar
import wow.doge.mygame.utils.wrappers.jme
import wow.doge.mygame.utils.wrappers.jme.AssetManager
import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace
@ -118,7 +121,7 @@ class MainApp(
.toIO(
obs
.doOnNextF(pme =>
Coeval(pprint.log(show"Received event $pme")).void
logger.trace(show"Received event $pme").toTask.void
)
.completedL
.startAndForget
@ -259,7 +262,8 @@ class MainAppDelegate(
_ <- level.addToGame(rootNode, physicsSpace)
playerActor <- createPlayerController()
// .onErrorRestart(3)
_ <- wire[GameInputHandler.Props].begin
// _ <- wire[GameInputHandler.Props].begin
_ <- new InputMappings(new jme.InputManager(inputManager)).setup
// .onErrorRestart(3)
johnActor <- createTestNpc("John")
// _ <- johnActor !! NpcActorSupervisor.Move(ImVector3f(0, 0, 20))
@ -394,7 +398,7 @@ class MainAppDelegate(
new Label("Health") { textFill = Color.White },
new Label("100") {
text <-- statsObs
.doOnNextF(i => loggerL.debug(show"Received stats: $i"))
.doOnNextF(i => loggerL.trace(show"Received stats: $i"))
.map(_.hp.toInt.toString)
textFill = Color.White
},
@ -407,11 +411,7 @@ class MainAppDelegate(
c += statsObs
.scanEval(me.Task.pure("green-bar")) {
case (a, b) =>
me.Task {
pprint.log(show"Received $a $b")
pprint.log(show"${styleClass.toString}")
styleClass.removeAll(a)
} >>
me.Task(styleClass.removeAll(a)) >>
me.Task.pure(
(b.hp.toInt: @switch) match {
case v if v > 80 => "green-bar"
@ -435,7 +435,7 @@ class MainAppDelegate(
new Label("100") {
textFill = Color.White
text <-- statsObs
.doOnNextF(i => loggerL.debug(show"Received stats: $i"))
.doOnNextF(i => loggerL.trace(show"Received stats: $i"))
.map(_.stamina.toInt.toString)
},
new JFXProgressBar {
@ -449,11 +449,7 @@ class MainAppDelegate(
c += statsObs
.scanEval(me.Task.pure("green-bar")) {
case (a, b) =>
me.Task {
pprint.log(show"Received $a $b")
pprint.log(show"${styleClass.toString}")
styleClass.removeAll(a)
} >>
me.Task(styleClass.removeAll(a)) >>
me.Task.pure(
(b.stamina.toInt: @switch) match {
case v if v > 80 => "green-bar"
@ -546,9 +542,22 @@ class MainAppDelegate(
.toIO
.hideErrors
.startAndForget
sched <- UIO.pure(schedulers.async)
fxSched <- UIO.pure(schedulers.fx)
playerActor <- wire[PlayerController.Props].create
playerMovementReducer = new PlayerMovementReducer(playerActor, loggerL)
_ <-
inputManager
.enumObservableAction(PlayerMovementInput)
.sample(1.millis)
.scanEval(me.Task.pure(PlayerMovementReducer.State.empty))(
playerMovementReducer.value
)
.completedL
.toIO
.hideErrors
.startAndForget
} yield playerActor
}

View File

@ -18,6 +18,7 @@ import wow.doge.mygame.subsystems.events.PlayerCameraEvent
import wow.doge.mygame.subsystems.events.PlayerEvent
import wow.doge.mygame.subsystems.events.PlayerMovementEvent
import wow.doge.mygame.utils.IOUtils._
import wow.doge.mygame.utils.methodName
object GameInputHandler {
@ -29,16 +30,16 @@ object GameInputHandler {
) {
def begin =
for {
_ <- UIO(setupMovementKeys(inputManager))
_ <- UIO(setupMovementKeys)
// _ <- UIO(setupAnalogMovementKeys)
_ <- UIO(setupCameraKeys())
_ <- toIO(
me.Task.parSequence(
Seq(
playerMovementInputEventsGenerator(
inputManager,
playerEventBus
).completedL,
// playerMovementInputEventsGenerator(
// inputManager,
// playerEventBus
// ).completedL,
// generateAnalogMovementEvents(
// inputManager,
// playerEventBus
@ -55,7 +56,7 @@ object GameInputHandler {
).startAndForget
} yield ()
def setupMovementKeys(inputManager: InputManager) =
def setupMovementKeys =
inputManager.withEnumMappings(PlayerMovementInput) {
case PlayerMovementInput.WalkRight =>
new KeyTrigger(KeyInput.KEY_D) :: Nil
@ -128,9 +129,6 @@ object GameInputHandler {
}
def methodName(implicit enclosing: sourcecode.Enclosing) =
enclosing.value.split(" ")(0).split("""\.""").last
def playerMovementInputEventsGenerator(
inputManager: InputManager,
playerEventBus: GameEventBus[PlayerEvent]

View File

@ -0,0 +1,87 @@
package wow.doge.mygame.game.subsystems.input
import com.jme3.input.KeyInput
import com.jme3.input.MouseInput
import com.jme3.input.controls.KeyTrigger
import com.jme3.input.controls.MouseAxisTrigger
import monix.{eval => me}
import wow.doge.mygame.implicits._
import wow.doge.mygame.utils.wrappers.jme.InputManager
class InputMappings(inputManager: InputManager) {
def setup =
for {
_ <- setupMovementKeys
_ <- setupAnalogMovementKeys
_ <- setupCameraKeys
_ <- cursorToggle
} yield ()
def setupMovementKeys =
inputManager.withEnumMappings(PlayerMovementInput) {
case PlayerMovementInput.WalkRight =>
new KeyTrigger(KeyInput.KEY_D) :: Nil
case PlayerMovementInput.WalkLeft =>
new KeyTrigger(KeyInput.KEY_A) :: Nil
case PlayerMovementInput.WalkForward =>
new KeyTrigger(KeyInput.KEY_W) :: Nil
case PlayerMovementInput.WalkBackward =>
new KeyTrigger(KeyInput.KEY_S) :: Nil
case PlayerMovementInput.Jump =>
new KeyTrigger(KeyInput.KEY_SPACE) :: Nil
}
def setupAnalogMovementKeys =
inputManager.withEnumMappings(PlayerAnalogMovementInput) {
case PlayerAnalogMovementInput.TurnRight =>
Seq(new KeyTrigger(KeyInput.KEY_D))
case PlayerAnalogMovementInput.TurnLeft =>
Seq(new KeyTrigger(KeyInput.KEY_A))
}
def setupCameraKeys =
inputManager.withEnumMappings(PlayerCameraInput) {
case PlayerCameraInput.CameraRotateLeft =>
Seq(
new KeyTrigger(KeyInput.KEY_LEFT),
new MouseAxisTrigger(MouseInput.AXIS_X, false)
)
case PlayerCameraInput.CameraRotateRight =>
Seq(
new KeyTrigger(KeyInput.KEY_RIGHT),
new MouseAxisTrigger(MouseInput.AXIS_X, true)
)
case PlayerCameraInput.CameraRotateUp =>
Seq(
new KeyTrigger(KeyInput.KEY_UP),
new MouseAxisTrigger(MouseInput.AXIS_Y, false)
)
case PlayerCameraInput.CameraRotateDown =>
Seq(
new KeyTrigger(KeyInput.KEY_DOWN),
new MouseAxisTrigger(MouseInput.AXIS_Y, true)
)
}
def cursorToggle =
inputManager.withMapping(
MiscInput.ToggleCursor,
new KeyTrigger(KeyInput.KEY_Z)
) >>
inputManager
.enumEntryObservableAction(MiscInput.ToggleCursor)
.doOnNext(action =>
action.binding match {
case MiscInput.ToggleCursor =>
if (action.value)(inputManager.cursorVisible =
!inputManager.cursorVisible).toTask
else me.Task.unit
}
)
.completedL
.toIO
.hideErrors
.startAndForget
}

View File

@ -40,19 +40,16 @@ object MonixDirectoryWatcher {
logger = logger.underlying
) {
override def onCreate(file: File, count: Int) =
// println(show"$file got created")
if (sub.onNext(CreateEvent(file, count)) == Ack.Stop)
c.cancel()
override def onModify(file: File, count: Int) = {
pprint.log(show"${file.toString} got modified $count times")
override def onModify(file: File, count: Int) =
if (sub.onNext(ModifyEvent(file, count)) == Ack.Stop)
c.cancel()
}
override def onDelete(file: File, count: Int) =
// println(show"$file got deleted")
if (sub.onNext(DeleteEvent(file, count)) == Ack.Stop)
c.cancel()
}
watcher.start()(scheduler)
c := Cancelable(() => watcher.stop())

View File

@ -1,7 +1,6 @@
package wow.doge.mygame
// import wow.doge.mygame.utils.wrappers.Node
package object utils {
// type AppNode = Node
def methodName(implicit enclosing: sourcecode.Enclosing) =
enclosing.value.split(" ")(0).split("""\.""").last
}

View File

@ -0,0 +1,126 @@
package wow.doge.mygame.utils.wrappers.jme
import com.jme3.input.controls.InputListener
import com.jme3.input.controls.Trigger
import com.jme3.{input => jmei}
import enumeratum._
import monix.bio.UIO
import monix.reactive.Observable
import wow.doge.mygame.ActionEvent
import wow.doge.mygame.AnalogEvent
import wow.doge.mygame.EnumActionEvent
import wow.doge.mygame.EnumAnalogEvent
import wow.doge.mygame.implicits._
final class InputManager(val delegate: jmei.InputManager) {
/**
* Create a new mapping to the given triggers.
*
* <p>
* The given mapping will be assigned to the given triggers, when
* any of the triggers given raise an event, the listeners
* registered to the mappings will receive appropriate events.
*
* @param mappingName The mapping name to assign.
* @param triggers The triggers to which the mapping is to be registered.
*/
def withMapping(mapping: String, triggers: Trigger*): UIO[Unit] =
UIO(delegate.withMapping(mapping, triggers: _*)).void
def withMapping[T <: EnumEntry](
mapping: T,
triggers: Trigger*
): UIO[Unit] = UIO(delegate.withMapping(mapping, triggers: _*)).void
def withListener(listener: InputListener, mappings: String*) =
UIO(delegate.withListener(listener, mappings: _*))
/**
* Creates new mappings from the values of the given Enum
*
* <p>
* The given mapping will be assigned to the given triggers, when
* any of the triggers given raise an event, the listeners
* registered to the mappings will receive appropriate events.
*
* @param mappingName The mapping name to assign.
* @param mappingFn Function from enum values to the sequence of trigers.
*
* @example
*
* {{{
*
* sealed trait PlayerAnalogMovementInput extends EnumEntry with UpperSnakecase
* object PlayerAnalogMovementInput extends Enum[PlayerAnalogMovementInput] {
* val values = findValues
* case object TurnRight extends PlayerAnalogMovementInput
* case object TurnLeft extends PlayerAnalogMovementInput
* }
*
* {
* inputManager.withEnumMappings(PlayerAnalogMovementInput) {
* case PlayerAnalogMovementInput.TurnRight =>
* Seq(new KeyTrigger(KeyInput.KEY_RIGHT))
* case PlayerAnalogMovementInput.TurnLeft =>
* Seq(new KeyTrigger(KeyInput.KEY_LEFT))
* }
* }
* }}}
*/
def withEnumMappings[T <: EnumEntry](
mappingEnum: Enum[T]
)(mappingFn: T => Seq[Trigger]) =
UIO(delegate.withEnumMappings(mappingEnum)(mappingFn))
/**
* Create an observable which emits the given mappings as elements of an observable
*
* @param mappingNames
* @return Observable of action events
*
* @see [[ActionEvent]]
* @see [[enumObservableAction]]
*/
def observableAction(mappingNames: String*): Observable[ActionEvent] =
delegate.observableAction(mappingNames: _*)
/**
* <p>
* Create an observable which emits the values of the given
* enum as elements of an observable
*
* @param mappingNames
* @return Observable of enum values
*
* @example {{{
* inputManager
* .enumObservableAction(PlayerMovementInput)
* .doOnNext { action =>
* action.binding match {
* case PlayerMovementInput.WalkLeft => Task {/* your actions */}
* }
* }
* }}}
*
* @see [[EnumActionEvent]]
* @see [[enumAnalogObservable]]
*/
def enumObservableAction[T <: EnumEntry](
mappingEnum: Enum[T]
): Observable[EnumActionEvent[T]] =
delegate.enumObservableAction(mappingEnum)
def enumEntryObservableAction[T <: EnumEntry](
mappingEnumEntry: T
) = delegate.enumEntryObservableAction(mappingEnumEntry)
def analogObservable(mappingNames: String*): Observable[AnalogEvent] =
delegate.analogObservable(mappingNames: _*)
def enumAnalogObservable[T <: EnumEntry](
mappingEnum: Enum[T]
): Observable[EnumAnalogEvent[T]] = delegate.enumAnalogObservable(mappingEnum)
def cursorVisible_=(value: Boolean) = UIO(delegate.setCursorVisible(value))
def cursorVisible = delegate.isCursorVisible
}