This commit is contained in:
Rohan Sircar 2021-01-10 21:27:47 +05:30
parent 978215b510
commit 64480e8e03
15 changed files with 266 additions and 123 deletions

View File

@ -1,5 +0,0 @@
Hey guys, few days ago I discovered I could load objects created in scripts at runtime, and cast them to an interface to call their methods provided
1. both host program and script have access to the same interface via a common library
2. script object implements that interface
I was thinking, maybe I could implement appstates in scripts to implement game mechanics and attach them to the state manager at runtime, and similarly for components of an ECS. What do you guys think?

View File

@ -6,10 +6,10 @@ import akka.actor.typed.SpawnProtocol
import akka.util.Timeout
import cats.effect.Resource
import cats.effect.concurrent.Deferred
import cats.syntax.eq._
import com.jme3.app.state.AppStateManager
import com.jme3.asset.AssetManager
import com.jme3.asset.plugins.ZipLocator
import com.jme3.bullet.BulletAppState
import com.jme3.bullet.control.BetterCharacterControl
import com.jme3.input.InputManager
import com.jme3.renderer.Camera
@ -35,21 +35,21 @@ import wow.doge.mygame.game.entities.NpcMovementActor
import wow.doge.mygame.game.entities.PlayerController
import wow.doge.mygame.game.entities.PlayerControllerTags
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.launcher.Launcher
import wow.doge.mygame.launcher.Launcher.LauncherResult
import wow.doge.mygame.math.ImVector3f
import wow.doge.mygame.subsystems.events.EventsModule
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
import wow.doge.mygame.subsystems.events.PlayerEvent
import wow.doge.mygame.subsystems.events.TickEvent
import wow.doge.mygame.subsystems.scriptsystem.ScriptInitMode
import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource
import wow.doge.mygame.utils.AkkaUtils
import wow.doge.mygame.utils.GenericConsoleStream
import cats.syntax.eq._
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
import wow.doge.mygame.utils.wrappers.jme.AppNode
import wow.doge.mygame.game.subsystems.level.DefaultGameLevel
import com.jme3.math.FastMath
class MainApp(
logger: Logger[Task],
@ -83,7 +83,7 @@ class MainApp(
assetManager <- gameApp.assetManager
stateManager <- gameApp.stateManager
camera <- gameApp.camera
rootNode <- gameApp.rootNode
rootNode <- Task.pure(gameApp.rootNode)
enqueueR <- Task(gameApp.enqueue _)
viewPort <- gameApp.viewPort
_ <- logger.info("before")
@ -98,12 +98,13 @@ class MainApp(
// _ <- Task(consoleStream := consoleTextArea)
// _ <- Task(jfxUI += consoleTextArea)
_ <- logger.info("after")
bulletAppState <- Task(new BulletAppState())
_ <- Task(stateManager.attach(bulletAppState))
// bulletAppState <- Task(new BulletAppState())
// _ <- Task(stateManager.attach(bulletAppState))
// bulletAppState <- Task.pure(gameApp.bulletAppstate)
_ <- logger.info("Initializing console stream")
_ <-
wire[MainAppDelegate]
.init(gameApp.scheduler)
.init()
.executeOn(gameApp.scheduler)
} yield gameAppFib
}
@ -141,20 +142,22 @@ class MainAppDelegate(
camera: Camera,
viewPort: ViewPort,
enqueueR: Function1[() => Unit, Unit],
rootNode: Node @@ GameAppTags.RootNode,
bulletAppState: BulletAppState
rootNode: AppNode[Task] @@ GameAppTags.RootNode
// bulletAppState: BulletAppState
)(implicit
spawnProtocol: ActorSystem[SpawnProtocol.Command],
@annotation.unused timeout: Timeout,
@annotation.unused scheduler: Scheduler
) {
val physicsSpace = bulletAppState.physicsSpace
// val physicsSpace = bulletAppState.physicsSpace
val physicsSpace = gameApp.physicsSpace
def init(
appScheduler: monix.execution.Scheduler
// appScheduler: monix.execution.Scheduler
// consoleStream: GenericConsoleStream[TextArea]
) =
for {
_ <- loggerL.info("Initializing Systems")
_ <- loggerL.debug(physicsSpace.toString())
_ <- Task(
assetManager.registerLocator(
os.rel / "assets" / "town.zip",
@ -164,14 +167,12 @@ class MainAppDelegate(
_ <- loggerL.info("test")
// _ <- Task(consoleStream.println("text"))
_ <- DefaultGameLevel(assetManager, viewPort)
.addToGame(
rootNode,
bulletAppState.physicsSpace
)
_ <- createPlayerController(appScheduler)
.absorbWith(e => DummyException("boom"))
.onErrorRestart(3)
_ <- wire[GameInputHandler.Props].begin.onErrorRestart(3)
.addToGame(rootNode, physicsSpace)
_ <- createPlayerController()
.absorbWith(e => DummyException(e.toString()))
// .onErrorRestart(3)
_ <- wire[GameInputHandler.Props].begin
// .onErrorRestart(3)
// johnActor <- createTestNpc(appScheduler, "John").executeOn(appScheduler)
// _ <- johnActor !! NpcActorSupervisor.Move(ImVector3f(0, 0, 20))
@ -193,7 +194,7 @@ class MainAppDelegate(
} yield ()
def createPlayerController(
appScheduler: monix.execution.Scheduler
// appScheduler: monix.execution.Scheduler
): IO[PlayerController.Error, Unit] = {
val playerPos = ImVector3f.ZERO
val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
@ -204,12 +205,14 @@ class MainAppDelegate(
// PlayerController.Defaults
// .defaultCamerNode(camera, playerPos)
// .taggedWith[PlayerControllerTags.PlayerCameraNode]
val playerModel = assetManager
.loadModel(modelPath)
.asInstanceOf[Node]
.withRotate(0, FastMath.PI, 0)
val mbPlayerNode = PlayerController.Defaults
.defaultPlayerNode(
assetManager,
modelPath,
playerPos,
// camNode
playerModel,
playerPhysicsControl
)
val cameraPivotNode = new Node(EntityIds.CameraPivot.value)
@ -240,7 +243,7 @@ class MainAppDelegate(
} yield ()
}
def createTestNpc(
appScheduler: monix.execution.Scheduler,
// appScheduler: monix.execution.Scheduler,
npcName: String
) = {
val initialPos = ImVector3f(50, 5, 0)
@ -273,11 +276,14 @@ class MainAppDelegate(
for {
npcNode <- IO.fromEither(mbNpcNode)
npcActor <- npcActorTask
_ <- IO {
physicsSpace += npcPhysicsControl
physicsSpace += npcNode
rootNode += npcNode
}
// _ <- IO {
// physicsSpace += npcPhysicsControl
// physicsSpace += npcNode
// // rootNode += npcNode
// }
_ <- physicsSpace += npcPhysicsControl
_ <- physicsSpace += npcNode
_ <- rootNode += npcNode
} yield npcActor
}

View File

@ -4,6 +4,7 @@ import cats.effect.Resource
import cats.effect.concurrent.Deferred
import com.jme3.app.state.AppStateManager
import com.jme3.asset.AssetManager
import com.jme3.bullet.BulletAppState
import com.jme3.input.InputManager
import com.jme3.scene.Node
import com.jme3.scene.Spatial
@ -17,78 +18,49 @@ import monix.catnap.ConcurrentChannel
import monix.catnap.ConsumerF
import monix.catnap.Semaphore
import monix.eval.Coeval
import monix.execution.CancelablePromise
import monix.execution.Scheduler
import wow.doge.mygame.executors.Schedulers
import wow.doge.mygame.game.subsystems.ui.JFxUI
import wow.doge.mygame.implicits._
import monix.execution.annotations.UnsafeBecauseImpure
import monix.reactive.Observable
import wow.doge.mygame.utils.wrappers.jme._
sealed trait Error
case object FlyCamNotExists extends Error
object GameAppTags {
sealed trait RootNode
sealed trait GuiNode
}
class GameApp(logger: Logger[Task], val app: SimpleAppExt) {
import Ops._
class GameApp private (logger: Logger[Task], app: SimpleAppExt) {
def stateManager: Task[AppStateManager] = Task(app.getStateManager())
def inputManager: Task[InputManager] = Task(app.getInputManager())
def assetManager: Task[AssetManager] = Task(app.getAssetManager())
def guiNode = Task(app.getGuiNode().taggedWith[GameAppTags.GuiNode])
def addToGuiNode = guiNode.flatMap(rn => Task(new AddToNode(rn)))
def guiNode2 = AppNode[Task](app.getGuiNode()).taggedWith[GameAppTags.GuiNode]
def flyCam =
IO(app.getFlyByCamera()).onErrorHandleWith(_ =>
IO.raiseError(FlyCamNotExists)
)
def camera = Task(app.getCamera())
def viewPort = Task(app.getViewPort())
def rootNode = Task(app.getRootNode().taggedWith[GameAppTags.RootNode])
def rootNode2 =
WrappedNode(app.getRootNode()).taggedWith[GameAppTags.RootNode]
// def rootNode2 = SynchedObject(app.getRootNode())
def addToRootNode = rootNode.flatMap(rn => Task(new AddToNode(rn)))
// def rootNode = Task(app.getRootNode().taggedWith[GameAppTags.RootNode])
val rootNode =
AppNode[Task](app.getRootNode()).taggedWith[GameAppTags.RootNode]
val physicsSpace = new PhysicsSpace[Task](app.bulletAppState.physicsSpace)
def enqueue(cb: () => Unit) =
app.enqueue(new Runnable {
override def run() = cb()
})
def enqueueL[T](cb: () => T): Task[T] = app.enqueueL(cb)
def start = Task(app.start())
def stop = Task(app.stop())
def scheduler = app.scheduler
def jfxUI = JFxUI(app)
}
class WrappedNode private (node: Node) {
// def +=(spat: Spatial) = lock.withPermit(Task(node.attachChild(spat)))
def children: Observable[Spatial] = node.observableChildren
def attachChild(n: Node): Task[Unit] = Task(node.attachChild(node))
def add(wn: WrappedNode): Task[Unit] =
Task(node.attachChild(wn.unsafeDelegate))
/**
* Get the underlying wrapped value
*/
@UnsafeBecauseImpure
def unsafeDelegate = node
}
object WrappedNode {
def apply(name: String) = new WrappedNode(new Node(name))
def apply(n: Node) = new WrappedNode(n)
implicit class WrappedNodeOps(private val wn: WrappedNode) extends AnyVal {
def +=(n: Node) = wn.attachChild(n)
def +=(wn: WrappedNode) = wn.add(wn)
}
}
object GameApp {
def resource(
@ -96,10 +68,12 @@ object GameApp {
jmeThread: Scheduler,
schedulers: Schedulers
) =
Resource.make(
Resource.make {
lazy val bullet = new BulletAppState
for {
startSignal <- Task(CancelablePromise[Unit]())
app <- Task(new SimpleAppExt(schedulers, startSignal))
// bullet <- Task(new BulletAppState)
// startSignal <- Task(CancelablePromise[Unit]())
app <- Task(new SimpleAppExt(schedulers, bullet))
_ <- Task {
val settings = new AppSettings(true)
settings.setVSync(true)
@ -111,11 +85,15 @@ object GameApp {
app.setShowSettings(false)
app.setSettings(settings)
}
fib <- Task(app.start).executeOn(jmeThread).start
_ <- Task.deferFuture(app.started)
// _ <- Task.fromCancelablePromise(startSignal)
_ <- Task(pprint.log(bullet.toString()))
_ <- Task(println(bullet.physicsSpace.toString()))
gameApp <- Task(new GameApp(logger, app))
fib <- gameApp.start.executeOn(jmeThread).start
_ <- Task.fromCancelablePromise(startSignal)
} yield gameApp -> fib
)(_._2.cancel)
}(_._2.cancel)
/**
* Synchronization wrapper for a mutable object

View File

@ -11,10 +11,11 @@ import monix.execution.Scheduler
import monix.execution.atomic.Atomic
import wow.doge.mygame.executors.GUIExecutorService
import wow.doge.mygame.executors.Schedulers
import com.jme3.bullet.BulletAppState
// import wow.doge.mygame.implicits._
class SimpleAppExt(
schedulers: Schedulers,
startSignal: CancelablePromise[Unit],
val bulletAppState: BulletAppState,
appStates: AppState*
) extends SimpleApplication(appStates: _*) {
import SimpleAppExt._
@ -22,11 +23,22 @@ class SimpleAppExt(
/**
* A non blocking synchronized queue using an immutable scala queue and monix's atomic class
*/
private lazy val taskQueue2 = Atomic(Queue.empty[MyTask[_]])
private val taskQueue2 = Atomic(Queue.empty[MyTask[_]])
// lazy val bulletAppState: BulletAppState = new BulletAppState
// def bulletAppState = synchronized(_bulletAppState)
// def tickObservable: Observable[Float] = tickSubject
// lazy val bulletAppState = stateManager.state[BulletAppState]()
private val startSignal: CancelablePromise[Unit] = CancelablePromise()
def started: CancelableFuture[Unit] = startSignal.future
override def simpleInitApp(): Unit = {
// _bulletAppState = new BulletAppState
stateManager.attach(bulletAppState)
startSignal.success(())
}

View File

@ -71,7 +71,7 @@ class NpcActorSupervisor(
import NpcActorSupervisor._
implicit val timeout = Timeout(1.second)
private val movementTimer = ctx.spawn(
val movementTimer = ctx.spawn(
GenericTimerActor
.Props(
children.npcMovementActor,

View File

@ -7,7 +7,6 @@ import akka.util.Timeout
import cats.implicits._
import com.jme3.asset.AssetManager
import com.jme3.bullet.BulletAppState
import com.jme3.bullet.PhysicsSpace
import com.jme3.bullet.control.BetterCharacterControl
import com.jme3.math.FastMath
import com.jme3.renderer.Camera
@ -29,7 +28,8 @@ import wow.doge.mygame.subsystems.events.PlayerEvent
import wow.doge.mygame.subsystems.events.TickEvent
import wow.doge.mygame.subsystems.movement.ImMovementActor
import wow.doge.mygame.utils.AkkaUtils
import com.softwaremill.macwire._
import wow.doge.mygame.utils.wrappers.jme._
import monix.bio.UIO
object PlayerControllerTags {
sealed trait PlayerTag
@ -44,13 +44,14 @@ object PlayerController {
class Props(
enqueueR: Function1[() => Unit, Unit],
rootNode: Node @@ GameAppTags.RootNode,
rootNode: AppNode[Task] @@ GameAppTags.RootNode,
loggerL: Logger[Task],
physicsSpace: PhysicsSpace,
// physicsSpace: com.jme3.bullet.PhysicsSpace,
physicsSpace: PhysicsSpace[Task],
initialPlayerPos: ImVector3f = ImVector3f.ZERO,
playerEventBus: GameEventBus[PlayerEvent],
playerPhysicsControl: BetterCharacterControl,
appScheduler: monix.execution.Scheduler,
// appScheduler: monix.execution.Scheduler,
playerNode: Node @@ PlayerControllerTags.PlayerTag,
cameraNode: CameraNode @@ PlayerControllerTags.PlayerCameraNode,
cameraPivotNode: Node @@ PlayerControllerTags.PlayerCameraPivotNode,
@ -85,17 +86,27 @@ object PlayerController {
playerActorBehavior,
"playerActorSupervisor"
)
_ <- Task(rootNode += playerNode)
// _ <- Task(rootNode += playerNode)
// _ <- Task(pprint.log("Physicsspace = " + physicsSpace.toString()))
// _ <- Task(pprint.log("playerNode = " + playerNode.toString()))
_ <- rootNode += playerNode
_ <- physicsSpace += playerNode
_ <- physicsSpace += playerPhysicsControl
_ <- IO {
physicsSpace += playerNode
physicsSpace += playerPhysicsControl
// physicsSpace += playerNode
// physicsSpace += playerPhysicsControl
// rootNode += cameraNode
cameraPivotNode += cameraNode
// playerNode += cameraPivotNode
rootNode += cameraPivotNode
// rootNode += cameraPivotNode
}
_ <- rootNode += cameraPivotNode
} yield ())
.onErrorHandleWith(e => IO.raiseError(GenericError(e.getMessage())))
.onErrorHandleWith(e =>
UIO(e.printStackTrace()) >> IO.raiseError(
GenericError(e.getMessage())
)
)
// .executeOn(appScheduler)
}
@ -166,9 +177,8 @@ object PlayerController {
.withLookAt(playerPos, ImVector3f.UNIT_Y)
def defaultPlayerNode(
assetManager: AssetManager,
modelPath: os.RelPath,
playerPos: ImVector3f,
playerModel: Node,
// camNode: CameraNode,
playerPhysicsControl: BetterCharacterControl
) =
@ -177,10 +187,7 @@ object PlayerController {
Node("PlayerNode")
.withChildren(
// camNode,
assetManager
.loadModel(modelPath)
.asInstanceOf[Node]
.withRotate(0, FastMath.PI, 0)
playerModel
)
.withLocalTranslation(playerPos)
.withControl(playerPhysicsControl)

View File

@ -9,7 +9,7 @@ import com.jme3.input.KeyInput
import com.jme3.input.MouseInput
import com.jme3.input.controls.KeyTrigger
import com.jme3.input.controls.MouseAxisTrigger
import monix.bio.UIO
import monix.bio.Task
import monix.{eval => me}
import wow.doge.mygame.implicits._
import wow.doge.mygame.subsystems.events.EventBus
@ -18,7 +18,6 @@ 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 monix.bio.Task
object GameInputHandler {

View File

@ -1,15 +1,15 @@
package wow.doge.mygame.game.subsystems.level
import com.jme3.bullet.PhysicsSpace
import com.jme3.bullet.control.RigidBodyControl
import com.jme3.light.AmbientLight
import com.jme3.light.DirectionalLight
import com.jme3.scene.Node
import com.jme3.scene.Spatial
import com.softwaremill.tagging._
import monix.bio.Task
import wow.doge.mygame.game.GameAppTags
import wow.doge.mygame.implicits._
// import wow.doge.mygame.implicits._
import wow.doge.mygame.utils.wrappers.jme.AppNode
import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace
class GameLevel(
model: Spatial,
@ -18,15 +18,15 @@ class GameLevel(
directionalLight: DirectionalLight
) {
def addToGame(
rootNode: Node @@ GameAppTags.RootNode,
physicsSpace: PhysicsSpace
rootNode: AppNode[Task] @@ GameAppTags.RootNode,
physicsSpace: PhysicsSpace[Task]
) = {
for {
_ <- Task(rootNode += model)
_ <- Task(rootNode :+ ambientLight)
_ <- Task(rootNode :+ directionalLight)
_ <- Task(physicsSpace += model)
_ <- Task(physicsSpace += physicsControl)
_ <- rootNode += model
_ <- rootNode += ambientLight
_ <- rootNode += directionalLight
_ <- physicsSpace += model
_ <- physicsSpace += physicsControl
} yield ()
}
}

View File

@ -726,7 +726,7 @@ package object implicits {
space.add(anyObject)
space
}
def :-(anyObject: Any) = {
def -(anyObject: Any) = {
space.remove(anyObject)
space
}
@ -738,7 +738,7 @@ package object implicits {
space
}
def :-(spatial: Spatial) = {
def -(spatial: Spatial) = {
space.removeAll(spatial)
space
}

View File

@ -1,6 +1,8 @@
package wow.doge.mygame.launcher
import cats.effect.Resource
import cats.effect.concurrent.Deferred
import cats.kernel.Eq
import javafx.application.Platform
import javafx.beans.value.ObservableValue
import monix.bio.Task
@ -17,8 +19,6 @@ import scalafx.stage.StageStyle
import wow.doge.mygame.executors.Schedulers
import wow.doge.mygame.implicits.JavaFXMonixObservables._
import wow.doge.mygame.utils.IOUtils._
import cats.effect.Resource
import cats.kernel.Eq
object Launcher {
sealed trait LauncherResult
object LauncherResult {

View File

@ -1,14 +1,21 @@
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
width: Int,
height: Int,
title: String,
fullScren: Boolean,
vsync: Boolean,
frameRate: Int
)
object Display {
val default = Display()
val default = Display(
width = 640,
height = 480,
title = "JME-Game",
fullScren = false,
vsync = false,
frameRate = -1
)
}
case class GlobalSettings(display: Display = Display.default)

View File

@ -0,0 +1,7 @@
package wow.doge.mygame
// import wow.doge.mygame.utils.wrappers.Node
package object utils {
// type AppNode = Node
}

View File

@ -0,0 +1,76 @@
package wow.doge.mygame.utils.wrappers.jme
import cats.effect.Sync
import com.jme3.{scene => jmes}
import monix.execution.annotations.UnsafeBecauseImpure
import monix.reactive.Observable
import wow.doge.mygame.implicits._
import com.jme3.light.Light
trait NodeDelegate {
/**
* Get the underlying wrapped value
*/
@UnsafeBecauseImpure
def unsafeDelegate: jmes.Node
}
abstract class NodeWrapper[F[_]: Sync] protected (node: jmes.Node) {
def children: Observable[jmes.Spatial] = node.observableChildren
def attachChild(n: jmes.Spatial): F[Unit] = Sync[F].delay(node.attachChild(n))
def add(wn: Node[F]): F[Unit] =
Sync[F].delay(node.attachChild(wn.unsafeDelegate))
def remove(n: jmes.Spatial): F[Unit] =
Sync[F].delay(node.detachChild(n))
def remove(wn: Node[F]): F[Unit] =
Sync[F].delay(node.detachChild(wn.unsafeDelegate))
def addLight(light: Light) =
Sync[F].delay {
node.addLight(light)
}
def removeLight(light: Light) =
Sync[F].delay {
node.removeLight(light)
}
def asSpatial: F[jmes.Spatial] = Sync[F].delay(node)
}
object NodeWrapper {
implicit class NodeOps[F[_]](private val nw: NodeWrapper[F]) extends AnyVal {
def +=(n: jmes.Spatial) = nw.attachChild(n)
def +=(n: Node[F]) = nw.add(n)
def -=(n: jmes.Spatial) = nw.remove(n)
def -=(wn: Node[F]) = nw.remove(wn)
def +=(light: Light) = {
nw.addLight(light)
}
def -=(light: Light) = {
nw.removeLight(light)
}
}
}
final class Node[F[_]: Sync] private (node: jmes.Node)
extends NodeWrapper[F](node)
with NodeDelegate {
/**
* Get the underlying wrapped value
*/
@UnsafeBecauseImpure
def unsafeDelegate = node
}
object Node {
def apply[F[_]: Sync](name: String) = new Node[F](new jmes.Node(name))
def apply[F[_]: Sync](n: jmes.Node) = new Node[F](n)
}
final class AppNode[F[_]: Sync] private (node: jmes.Node)
extends NodeWrapper[F](node)
object AppNode {
def apply[F[_]: Sync](name: String) = new AppNode[F](new jmes.Node(name))
def apply[F[_]: Sync](n: jmes.Node) = new AppNode[F](n)
}

View File

@ -0,0 +1,53 @@
package wow.doge.mygame.utils.wrappers.jme
import cats.effect.Sync
import com.jme3.{bullet => jmeb}
import com.jme3.{scene => jmes}
import wow.doge.mygame.implicits._
final class PhysicsSpace[F[_]: Sync](space: jmeb.PhysicsSpace) {
def add(anyObject: Any) = Sync[F].delay(space.add(anyObject))
def remove(anyObject: Any) =
Sync[F].delay {
space.remove(anyObject)
space
}
def addAll(spatial: jmes.Spatial) = Sync[F].delay(space.addAll(spatial))
def removeAll(spatial: jmes.Spatial) =
Sync[F].delay {
space.removeAll(spatial)
space
}
def collisionObservable = space.collisionObservable()
def physicsTickObservable = space.physicsTickObservable()
}
object PhysicsSpace {
implicit final class PhysicsSpaceOps[F[_]](private val space: PhysicsSpace[F])
extends AnyVal {
def +=(anyObject: Any) = space.add(anyObject)
def :+(anyObject: Any) = {
space.add(anyObject)
space
}
def -(anyObject: Any) = {
space.remove(anyObject)
space
}
def +=(spatial: jmes.Spatial) = space.addAll(spatial)
def :+(spatial: jmes.Spatial) = {
space.addAll(spatial)
space
}
def -(spatial: jmes.Spatial) = {
space.removeAll(spatial)
space
}
}
}

View File

@ -0,0 +1,3 @@
package wow.doge.mygame.utils.wrappers.jme
package object node {}