first commit
This commit is contained in:
commit
1f55e08fa5
@ -0,0 +1,39 @@
|
||||
|
||||
package ammonite
|
||||
package $file.src.main.resources
|
||||
import _root_.ammonite.interp.api.InterpBridge.{
|
||||
value => interp
|
||||
}
|
||||
import _root_.ammonite.interp.api.InterpBridge.value.{
|
||||
exit
|
||||
}
|
||||
import _root_.ammonite.interp.api.IvyConstructor.{
|
||||
ArtifactIdExt,
|
||||
GroupIdExt
|
||||
}
|
||||
import _root_.ammonite.runtime.tools.{
|
||||
browse,
|
||||
grep,
|
||||
time,
|
||||
tail
|
||||
}
|
||||
import _root_.ammonite.repl.tools.{
|
||||
desugar,
|
||||
source
|
||||
}
|
||||
import _root_.ammonite.main.Router.{
|
||||
doc,
|
||||
main
|
||||
}
|
||||
import _root_.ammonite.repl.tools.Util.{
|
||||
pathScoptRead
|
||||
}
|
||||
|
||||
|
||||
object dep{
|
||||
/*<script>*/class Test(x: Int)
|
||||
/*</script>*/ /*<generated>*/
|
||||
def $main() = { scala.Iterator[String]() }
|
||||
override def toString = "dep"
|
||||
/*</generated>*/
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
|
||||
package ammonite
|
||||
package $file.src.main.resources
|
||||
import _root_.ammonite.interp.api.InterpBridge.{
|
||||
value => interp
|
||||
}
|
||||
import _root_.ammonite.interp.api.InterpBridge.value.{
|
||||
exit
|
||||
}
|
||||
import _root_.ammonite.interp.api.IvyConstructor.{
|
||||
ArtifactIdExt,
|
||||
GroupIdExt
|
||||
}
|
||||
import _root_.ammonite.runtime.tools.{
|
||||
browse,
|
||||
grep,
|
||||
time,
|
||||
tail
|
||||
}
|
||||
import _root_.ammonite.repl.tools.{
|
||||
desugar,
|
||||
source
|
||||
}
|
||||
import _root_.ammonite.main.Router.{
|
||||
doc,
|
||||
main
|
||||
}
|
||||
import _root_.ammonite.repl.tools.Util.{
|
||||
pathScoptRead
|
||||
}
|
||||
import ammonite.$file.src.main.resources.{
|
||||
dep
|
||||
}
|
||||
|
||||
|
||||
object hello{
|
||||
/*<script>*/import $file.$
|
||||
import dep.Test
|
||||
|
||||
/*<amm>*/val res_2 = /*</amm>*/new Test(1)
|
||||
/*</script>*/ /*<generated>*/
|
||||
def $main() = { scala.Iterator[String]() }
|
||||
override def toString = "hello"
|
||||
/*</generated>*/
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
|
||||
package ammonite
|
||||
package $file.src.main.resources
|
||||
import _root_.ammonite.interp.api.InterpBridge.{
|
||||
value => interp
|
||||
}
|
||||
import _root_.ammonite.interp.api.InterpBridge.value.{
|
||||
exit
|
||||
}
|
||||
import _root_.ammonite.interp.api.IvyConstructor.{
|
||||
ArtifactIdExt,
|
||||
GroupIdExt
|
||||
}
|
||||
import _root_.ammonite.runtime.tools.{
|
||||
browse,
|
||||
grep,
|
||||
time,
|
||||
tail
|
||||
}
|
||||
import _root_.ammonite.repl.tools.{
|
||||
desugar,
|
||||
source
|
||||
}
|
||||
import _root_.ammonite.main.Router.{
|
||||
doc,
|
||||
main
|
||||
}
|
||||
import _root_.ammonite.repl.tools.Util.{
|
||||
pathScoptRead
|
||||
}
|
||||
|
||||
|
||||
object hello2{
|
||||
/*<script>*/import $repo.$
|
||||
// import $repo.`https://bintray.com/jmonkeyengine/com.jme3`
|
||||
// import $file.dep
|
||||
import $ivy.$
|
||||
// import $ivy.`wow.doge:game:1.0-SNAPSHOT`
|
||||
import $ivy.$
|
||||
// import wow.doge.game.types.GameScript
|
||||
import com.jme3.scene.control.Control
|
||||
|
||||
// println("hello from script")
|
||||
|
||||
// class Scr extends GameScript {
|
||||
// def start(): Unit = println("hello from start")
|
||||
|
||||
// def stop(): Unit = println("hello from stop")
|
||||
// }
|
||||
|
||||
// // class SomeClass extends Control {}
|
||||
|
||||
// @main
|
||||
// def main(): GameScript = new Scr()
|
||||
import com.simsilica.es.base.DefaultEntityData
|
||||
import com.simsilica.es.EntityData
|
||||
import com.jme3.app.Application
|
||||
import wow.doge.mygame.game.Implicits._
|
||||
import wow.doge.mygame.components.TestComponent
|
||||
import com.jme3.scene.shape.Box
|
||||
import com.jme3.scene.Geometry
|
||||
import com.jme3.material.Material
|
||||
import com.jme3.math.ColorRGBA
|
||||
import com.jme3.asset.AssetManager
|
||||
import com.jme3.math.Vector3f
|
||||
import wow.doge.mygame.state._
|
||||
class TestAppState(
|
||||
// private var _entity: Option[EntityData] = Some(new DefaultEntityData())
|
||||
) extends MyBaseState {
|
||||
protected lazy val b = new Box(1, 1, 1)
|
||||
protected lazy val geom = new Geometry("Box", b)
|
||||
protected lazy val mat = Material(
|
||||
assetManager = assetManager,
|
||||
path = "Common/MatDefs/Misc/Unshaded.j3md"
|
||||
)
|
||||
|
||||
// def entity = _entity
|
||||
// override def initialize(app: Application): Unit = {
|
||||
// super.initialize(app)
|
||||
|
||||
// }
|
||||
|
||||
override def init() = {
|
||||
entityData
|
||||
.createEntity()
|
||||
.withComponents(TestComponent())
|
||||
// entityData.setComponents(x, TestComponent())
|
||||
val es = entityData.getEntities(classOf[TestComponent])
|
||||
println(es)
|
||||
geom.setMaterial(mat)
|
||||
rootNode.attachChild(geom)
|
||||
|
||||
// geom.foreach(e => {
|
||||
|
||||
// })
|
||||
}
|
||||
|
||||
override def update(tpf: Float) = {
|
||||
geom.rotate(0, 0.5f * tpf, 0)
|
||||
geom.move(new Vector3f(0, 1 * tpf, 0))
|
||||
}
|
||||
|
||||
override def cleanup(app: Application): Unit = {
|
||||
// _entity.map(_.close())
|
||||
// _entity = None
|
||||
}
|
||||
|
||||
override def onEnable(): Unit = {}
|
||||
|
||||
override def onDisable(): Unit = {}
|
||||
|
||||
}
|
||||
|
||||
object Material {
|
||||
def apply(
|
||||
color: String = "Color",
|
||||
colorType: com.jme3.math.ColorRGBA = ColorRGBA.Blue,
|
||||
assetManager: AssetManager,
|
||||
path: String
|
||||
): Material = {
|
||||
val mat =
|
||||
new Material(assetManager, path)
|
||||
mat.setColor(color, colorType)
|
||||
mat
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
def main(): MyBaseState = new TestAppState()
|
||||
/*</script>*/ /*<generated>*/
|
||||
def $main() = { scala.Iterator[String]() }
|
||||
override def toString = "hello2"
|
||||
/*</generated>*/
|
||||
}
|
25
.gitignore
vendored
Normal file
25
.gitignore
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
*.class
|
||||
*.log
|
||||
|
||||
# sbt specific
|
||||
.cache/
|
||||
.history/
|
||||
.lib/
|
||||
dist/*
|
||||
target/
|
||||
lib_managed/
|
||||
src_managed/
|
||||
project/boot/
|
||||
project/plugins/project/
|
||||
metals.sbt
|
||||
.metals
|
||||
.bloop
|
||||
|
||||
# Scala-IDE specific
|
||||
.scala_dependencies
|
||||
.worksheet
|
||||
|
||||
.idea/
|
||||
.vscode
|
||||
assets/
|
||||
*.j3o
|
1
.scalafmt.conf
Normal file
1
.scalafmt.conf
Normal file
@ -0,0 +1 @@
|
||||
version = "2.6.4"
|
3
assets.jmp
Normal file
3
assets.jmp
Normal file
@ -0,0 +1,3 @@
|
||||
#assets properties
|
||||
#Fri Oct 23 22:34:43 IST 2020
|
||||
assets.folder.name=src/main/resources/assets
|
192
build.sbt
Normal file
192
build.sbt
Normal file
@ -0,0 +1,192 @@
|
||||
// The simplest possible sbt build file is just one line:
|
||||
|
||||
scalaVersion := "2.13.3"
|
||||
// That is, to create a valid sbt build, all you've got to do is define the
|
||||
// version of Scala you'd like your project to use.
|
||||
|
||||
// ============================================================================
|
||||
|
||||
// Lines like the above defining `scalaVersion` are called "settings". Settings
|
||||
// are key/value pairs. In the case of `scalaVersion`, the key is "scalaVersion"
|
||||
// and the value is "2.13.1"
|
||||
|
||||
// It's possible to define many kinds of settings, such as:
|
||||
|
||||
// Note, it's not required for you to define these three settings. These are
|
||||
// mostly only necessary if you intend to publish your library's binaries on a
|
||||
// place like Sonatype or Bintray.
|
||||
|
||||
// Want to use a published library in your project?
|
||||
// You can define other libraries as dependencies in your build like this:
|
||||
|
||||
resolvers += "Jcenter" at "https://jcenter.bintray.com/"
|
||||
resolvers += "JME Bintray" at "https://bintray.com/jmonkeyengine/com.jme3"
|
||||
|
||||
resolvers += Resolver.mavenLocal
|
||||
resolvers += Resolver.sonatypeRepo("snapshots")
|
||||
|
||||
lazy val jmeVersion = "3.3.2-stable"
|
||||
|
||||
lazy val osName = System.getProperty("os.name") match {
|
||||
case n if n.startsWith("Linux") => "linux"
|
||||
case n if n.startsWith("Mac") => "mac"
|
||||
case n if n.startsWith("Windows") => "win"
|
||||
case _ => throw new Exception("Unknown platform!")
|
||||
}
|
||||
lazy val javaFXModules =
|
||||
Seq("base", "controls", "fxml", "graphics", "media", "swing", "web")
|
||||
|
||||
lazy val root = (project in file(".")).settings(
|
||||
inThisBuild(
|
||||
List(
|
||||
scalaVersion := scalaVersion.value, // 2.11.12, or 2.13.3
|
||||
semanticdbEnabled := true, // enable SemanticDB
|
||||
semanticdbVersion := "4.3.24" // use Scalafix compatible version
|
||||
)
|
||||
),
|
||||
name := "mygame",
|
||||
organization := "wow.doge",
|
||||
version := "1.0-SNAPSHOT",
|
||||
libraryDependencies ++= Seq(
|
||||
"org.scala-lang.modules" %% "scala-parser-combinators" % "1.1.2",
|
||||
// https://mvnrepository.com/artifact/org.jmonkeyengine/jme3-core
|
||||
"org.jmonkeyengine" % "jme3-core" % jmeVersion,
|
||||
// https://mvnrepository.com/artifact/org.jmonkeyengine/jme3-desktop
|
||||
"org.jmonkeyengine" % "jme3-desktop" % jmeVersion,
|
||||
// https://mvnrepository.com/artifact/org.jmonkeyengine/jme3-lwjgl3
|
||||
"org.jmonkeyengine" % "jme3-lwjgl3" % jmeVersion,
|
||||
"org.jmonkeyengine" % "jme3-effects" % jmeVersion,
|
||||
"org.jmonkeyengine" % "jme3-plugins" % jmeVersion,
|
||||
"org.jmonkeyengine" % "jme3-blender" % jmeVersion,
|
||||
// https://mvnrepository.com/artifact/com.github.stephengold/Minie
|
||||
"com.github.stephengold" % "Minie" % "3.0.0",
|
||||
// https://mvnrepository.com/artifact/com.simsilica/zay-es
|
||||
"com.simsilica" % "zay-es" % "1.2.1",
|
||||
"org.typelevel" %% "cats-core" % "2.1.1",
|
||||
"com.lihaoyi" % "ammonite" % "2.2.0" cross CrossVersion.full,
|
||||
"org.jetbrains.kotlin" % "kotlin-main-kts" % "1.4.10",
|
||||
"org.jetbrains.kotlin" % "kotlin-scripting-jsr223" % "1.4.10",
|
||||
// "wow.doge" % "game" % "1.0-SNAPSHOT",
|
||||
"org.scalafx" %% "scalafx" % "14-R19",
|
||||
"com.typesafe.akka" %% "akka-actor-typed" % "2.6.10",
|
||||
"ch.qos.logback" % "logback-classic" % "1.2.3",
|
||||
"org.typelevel" %% "cats-core" % "2.1.1",
|
||||
"org.typelevel" %% "cats-effect" % "2.1.4",
|
||||
"io.monix" %% "monix" % "3.2.2",
|
||||
"io.monix" %% "monix-bio" % "1.0.0",
|
||||
"io.circe" %% "circe-core" % "0.13.0",
|
||||
"io.circe" %% "circe-generic" % "0.13.0",
|
||||
"com.softwaremill.sttp.client" %% "core" % "2.2.5",
|
||||
"com.softwaremill.sttp.client" %% "monix" % "2.2.5",
|
||||
"com.softwaremill.sttp.client" %% "circe" % "2.2.5",
|
||||
"com.softwaremill.sttp.client" %% "async-http-client-backend-monix" % "2.2.5",
|
||||
"com.github.valskalla" %% "odin-monix" % "0.8.1",
|
||||
"com.softwaremill.macwire" %% "util" % "2.3.7",
|
||||
"com.softwaremill.macwire" %% "macros" % "2.3.6" % "provided",
|
||||
"com.softwaremill.macwire" %% "macrosakka" % "2.3.6" % "provided",
|
||||
"com.github.valskalla" %% "odin-slf4j" % "0.8.1",
|
||||
"com.softwaremill.quicklens" %% "quicklens" % "1.6.1"
|
||||
),
|
||||
// Determine OS version of JavaFX binaries
|
||||
|
||||
// Add JavaFX dependencies
|
||||
libraryDependencies ++= javaFXModules.map(m =>
|
||||
"org.openjfx" % s"javafx-$m" % "14.0.1" classifier osName
|
||||
),
|
||||
scalacOptions ++= Seq(
|
||||
"-encoding",
|
||||
"UTF-8",
|
||||
"-deprecation",
|
||||
"-feature",
|
||||
"-unchecked",
|
||||
"-Xlint",
|
||||
"-Ywarn-numeric-widen",
|
||||
"-Ymacro-annotations",
|
||||
// "utf-8", // Specify character encoding used by source files.
|
||||
"-explaintypes" // Explain type errors in more detail.
|
||||
),
|
||||
javacOptions ++= Seq("-source", "11", "-target", "11"),
|
||||
javaOptions ++= Seq("-Xmx2G", "-Xms2G"),
|
||||
fork := true,
|
||||
assemblyMergeStrategy in assembly := {
|
||||
case PathList("javax", "servlet", xs @ _*) => MergeStrategy.first
|
||||
case PathList(ps @ _*) if ps.last endsWith ".html" => MergeStrategy.first
|
||||
case "application.conf" => MergeStrategy.concat
|
||||
case "unwanted.txt" => MergeStrategy.discard
|
||||
case x if Assembly.isConfigFile(x) =>
|
||||
MergeStrategy.concat
|
||||
case PathList("META-INF", xs @ _*) =>
|
||||
(xs map { _.toLowerCase }) match {
|
||||
case ("manifest.mf" :: Nil) | ("index.list" :: Nil) |
|
||||
("dependencies" :: Nil) =>
|
||||
MergeStrategy.discard
|
||||
case ps @ (x :: xs)
|
||||
if ps.last.endsWith(".sf") || ps.last.endsWith(".dsa") =>
|
||||
MergeStrategy.discard
|
||||
case "plexus" :: xs =>
|
||||
MergeStrategy.discard
|
||||
case "services" :: xs =>
|
||||
MergeStrategy.filterDistinctLines
|
||||
case ("spring.schemas" :: Nil) | ("spring.handlers" :: Nil) =>
|
||||
MergeStrategy.filterDistinctLines
|
||||
case _ => MergeStrategy.first // Changed deduplicate to first
|
||||
}
|
||||
case PathList(_*) => MergeStrategy.first
|
||||
// case x =>
|
||||
// val oldStrategy = (assemblyMergeStrategy in assembly).value
|
||||
// oldStrategy(x)
|
||||
}
|
||||
// scalaVersion := "2.13.2", // 2.11.12, or 2.13.3
|
||||
// semanticdbEnabled := true, // enable SemanticDB
|
||||
// semanticdbVersion := scalafixSemanticdb.revision // use Scalafix compatible version
|
||||
// semanticdbVersion := "4.3.24",
|
||||
)
|
||||
|
||||
// Here, `libraryDependencies` is a set of dependencies, and by using `+=`,
|
||||
// we're adding the scala-parser-combinators dependency to the set of dependencies
|
||||
// that sbt will go and fetch when it starts up.
|
||||
// Now, in any Scala file, you can import classes, objects, etc., from
|
||||
// scala-parser-combinators with a regular import.
|
||||
|
||||
// TIP: To find the "dependency" that you need to add to the
|
||||
// `libraryDependencies` set, which in the above example looks like this:
|
||||
|
||||
// "org.scala-lang.modules" %% "scala-parser-combinators" % "1.1.2"
|
||||
|
||||
// You can use Scaladex, an index of all known published Scala libraries. There,
|
||||
// after you find the library you want, you can just copy/paste the dependency
|
||||
// information that you need into your build file. For example, on the
|
||||
// scala/scala-parser-combinators Scaladex page,
|
||||
// https://index.scala-lang.org/scala/scala-parser-combinators, you can copy/paste
|
||||
// the sbt dependency from the sbt box on the right-hand side of the screen.
|
||||
|
||||
// IMPORTANT NOTE: while build files look _kind of_ like regular Scala, it's
|
||||
// important to note that syntax in *.sbt files doesn't always behave like
|
||||
// regular Scala. For example, notice in this build file that it's not required
|
||||
// to put our settings into an enclosing object or class. Always remember that
|
||||
// sbt is a bit different, semantically, than vanilla Scala.
|
||||
|
||||
// ============================================================================
|
||||
|
||||
// Most moderately interesting Scala projects don't make use of the very simple
|
||||
// build file style (called "bare style") used in this build.sbt file. Most
|
||||
// intermediate Scala projects make use of so-called "multi-project" builds. A
|
||||
// multi-project build makes it possible to have different folders which sbt can
|
||||
// be configured differently for. That is, you may wish to have different
|
||||
// dependencies or different testing frameworks defined for different parts of
|
||||
// your codebase. Multi-project builds make this possible.
|
||||
|
||||
// Here's a quick glimpse of what a multi-project build looks like for this
|
||||
// build, with only one "subproject" defined, called `root`:
|
||||
|
||||
// lazy val root = (project in file(".")).
|
||||
// settings(
|
||||
// inThisBuild(List(
|
||||
// organization := "ch.epfl.scala",
|
||||
// scalaVersion := "2.13.1"
|
||||
// )),
|
||||
// name := "hello-world"
|
||||
// )
|
||||
|
||||
// To learn more about multi-project builds, head over to the official sbt
|
||||
// documentation at http://www.scala-sbt.org/documentation.html
|
BIN
libbulletjme.so
Normal file
BIN
libbulletjme.so
Normal file
Binary file not shown.
1
project/build.properties
Normal file
1
project/build.properties
Normal file
@ -0,0 +1 @@
|
||||
sbt.version=1.3.13
|
2
project/plugins.sbt
Normal file
2
project/plugins.sbt
Normal file
@ -0,0 +1,2 @@
|
||||
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.15.0")
|
||||
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.23")
|
@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.mainKts.jsr223.KotlinJsr223MainKtsScriptEngineFactory
|
7
src/main/resources/application.conf
Normal file
7
src/main/resources/application.conf
Normal file
@ -0,0 +1,7 @@
|
||||
jme-dispatcher {
|
||||
type = "Dispatcher"
|
||||
name = "JME-Thread"
|
||||
executor = "wow.doge.mygame.executors.JMEThreadExecutorServiceConfigurator"
|
||||
throughput = 1
|
||||
}
|
||||
akka.jvm-exit-on-fatal-error = on
|
2
src/main/resources/dep.sc
Normal file
2
src/main/resources/dep.sc
Normal file
@ -0,0 +1,2 @@
|
||||
// println("hello from dep")
|
||||
class Test(x: Int)
|
36
src/main/resources/hello.main.kts
Normal file
36
src/main/resources/hello.main.kts
Normal file
@ -0,0 +1,36 @@
|
||||
// @file:Import("src/main/resources/hello2.main.kts")
|
||||
@file:DependsOn("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.0-M1")
|
||||
@file:DependsOn("/home/rohan/.m2/repository/wow/doge/game/1.0-SNAPSHOT/game-1.0-SNAPSHOT.jar")
|
||||
@file:DependsOn("/home/rohan/.ivy2/local/wow.doge/mygame_2.13/1.0-SNAPSHOT/jars/mygame_2.13.jar")
|
||||
|
||||
import wow.doge.game.types.GameScript
|
||||
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlin.random.Random
|
||||
|
||||
import wow.doge.mygame.components.Test
|
||||
|
||||
|
||||
//println(x * 2)
|
||||
|
||||
println("hello from main script")
|
||||
|
||||
class GameScriptImpl : GameScript {
|
||||
override fun start() = runBlocking {
|
||||
for(x in 0 until 5) {
|
||||
launch {
|
||||
val del = Random.nextLong(20, 100)
|
||||
|
||||
delay(del)
|
||||
println("hello from start $x, delay is $del")
|
||||
}
|
||||
}
|
||||
}
|
||||
override fun stop() {println("hello from stop")}
|
||||
}
|
||||
|
||||
GameScriptImpl()
|
||||
|
||||
class MyTest : Test()
|
35
src/main/resources/hello.sc
Normal file
35
src/main/resources/hello.sc
Normal file
@ -0,0 +1,35 @@
|
||||
// #!/usr/bin/env amm
|
||||
|
||||
// import coursierapi.MavenRepository
|
||||
|
||||
// interp.repositories.update(
|
||||
// interp.repositories() ::: List(
|
||||
// MavenRepository.of("file://home/rohan/.m2/repository")
|
||||
// )
|
||||
// )
|
||||
|
||||
// @
|
||||
// import $repo.`https://jcenter.bintray.com`
|
||||
// // import $repo.`https://bintray.com/jmonkeyengine/com.jme3`
|
||||
// import $file.dep
|
||||
// import $ivy.`org.jmonkeyengine:jme3-core:3.2.4-stable`
|
||||
// import $ivy.`wow.doge:game:1.0-SNAPSHOT`
|
||||
// import wow.doge.game.types.GameScript
|
||||
// import com.jme3.scene.control.Control
|
||||
|
||||
// println("hello from script")
|
||||
|
||||
// class Scr extends GameScript {
|
||||
// def start(): Unit = println("hello from start")
|
||||
|
||||
// def stop(): Unit = println("hello from stop")
|
||||
// }
|
||||
|
||||
// // class SomeClass extends Control {}
|
||||
|
||||
// @main
|
||||
// def main(): GameScript = new Scr()
|
||||
import $file.dep
|
||||
import dep.Test
|
||||
|
||||
new Test(1)
|
5
src/main/resources/hello2.main.kts
Normal file
5
src/main/resources/hello2.main.kts
Normal file
@ -0,0 +1,5 @@
|
||||
println("hello from dep")
|
||||
|
||||
class Test(val x: Int)
|
||||
|
||||
var x = 2
|
107
src/main/resources/hello2.sc
Normal file
107
src/main/resources/hello2.sc
Normal file
@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env amm
|
||||
|
||||
// import coursierapi.MavenRepository
|
||||
|
||||
// interp.repositories.update(
|
||||
// interp.repositories() ::: List(
|
||||
// MavenRepository.of("file://home/rohan/.m2/repository")
|
||||
// )
|
||||
// )
|
||||
|
||||
// @
|
||||
import $repo.`https://jcenter.bintray.com`
|
||||
// import $repo.`https://bintray.com/jmonkeyengine/com.jme3`
|
||||
// import $file.dep
|
||||
import $ivy.`org.jmonkeyengine:jme3-core:3.2.4-stable`
|
||||
// import $ivy.`wow.doge:game:1.0-SNAPSHOT`
|
||||
import $ivy.`wow.doge::mygame:1.0-SNAPSHOT`
|
||||
// import wow.doge.game.types.GameScript
|
||||
import com.jme3.scene.control.Control
|
||||
|
||||
// println("hello from script")
|
||||
|
||||
// class Scr extends GameScript {
|
||||
// def start(): Unit = println("hello from start")
|
||||
|
||||
// def stop(): Unit = println("hello from stop")
|
||||
// }
|
||||
|
||||
// // class SomeClass extends Control {}
|
||||
|
||||
// @main
|
||||
// def main(): GameScript = new Scr()
|
||||
import com.simsilica.es.base.DefaultEntityData
|
||||
import com.simsilica.es.EntityData
|
||||
import com.jme3.app.Application
|
||||
import wow.doge.mygame.game.Implicits._
|
||||
import wow.doge.mygame.components.TestComponent
|
||||
import com.jme3.scene.shape.Box
|
||||
import com.jme3.scene.Geometry
|
||||
import com.jme3.material.Material
|
||||
import com.jme3.math.ColorRGBA
|
||||
import com.jme3.asset.AssetManager
|
||||
import com.jme3.math.Vector3f
|
||||
import wow.doge.mygame.state._
|
||||
class TestAppState(
|
||||
// private var _entity: Option[EntityData] = Some(new DefaultEntityData())
|
||||
) extends MyBaseState {
|
||||
protected lazy val b = new Box(1, 1, 1)
|
||||
protected lazy val geom = new Geometry("Box", b)
|
||||
protected lazy val mat = Material(
|
||||
assetManager = assetManager,
|
||||
path = "Common/MatDefs/Misc/Unshaded.j3md"
|
||||
)
|
||||
|
||||
// def entity = _entity
|
||||
// override def initialize(app: Application): Unit = {
|
||||
// super.initialize(app)
|
||||
|
||||
// }
|
||||
|
||||
override def init() = {
|
||||
entityData
|
||||
.createEntity()
|
||||
.withComponents(TestComponent())
|
||||
// entityData.setComponents(x, TestComponent())
|
||||
val es = entityData.getEntities(classOf[TestComponent])
|
||||
println(es)
|
||||
geom.setMaterial(mat)
|
||||
rootNode.attachChild(geom)
|
||||
|
||||
// geom.foreach(e => {
|
||||
|
||||
// })
|
||||
}
|
||||
|
||||
override def update(tpf: Float) = {
|
||||
geom.rotate(0, 0.5f * tpf, 0)
|
||||
geom.move(new Vector3f(0, 1 * tpf, 0))
|
||||
}
|
||||
|
||||
override def cleanup(app: Application): Unit = {
|
||||
// _entity.map(_.close())
|
||||
// _entity = None
|
||||
}
|
||||
|
||||
override def onEnable(): Unit = {}
|
||||
|
||||
override def onDisable(): Unit = {}
|
||||
|
||||
}
|
||||
|
||||
object Material {
|
||||
def apply(
|
||||
color: String = "Color",
|
||||
colorType: com.jme3.math.ColorRGBA = ColorRGBA.Blue,
|
||||
assetManager: AssetManager,
|
||||
path: String
|
||||
): Material = {
|
||||
val mat =
|
||||
new Material(assetManager, path)
|
||||
mat.setColor(color, colorType)
|
||||
mat
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
def main(): MyBaseState = new TestAppState()
|
19
src/main/resources/weapon.json
Normal file
19
src/main/resources/weapon.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"record": "Weapon",
|
||||
"values": {
|
||||
"baseId": "4F00000062",
|
||||
"weaponType": "sword",
|
||||
"name": "Exquisite Steel Sword",
|
||||
"attack": "40",
|
||||
"weight": "20"
|
||||
}
|
||||
}
|
||||
|
||||
val entity = ed.createEntity()
|
||||
entity.setComponents(
|
||||
Record("Weapon"),
|
||||
WeaponType("Sword"),
|
||||
Name("Exquisite Steel Sword"),
|
||||
Attack(40),
|
||||
Weight(20)
|
||||
)
|
62
src/main/scala/com/jme3/animation/package.scala
Normal file
62
src/main/scala/com/jme3/animation/package.scala
Normal file
@ -0,0 +1,62 @@
|
||||
package com.jme3
|
||||
|
||||
import com.jme3.input.Action
|
||||
|
||||
package object animation {
|
||||
|
||||
implicit class AnimChannelWrap(val uval: AnimChannel) extends AnyVal {
|
||||
|
||||
/**
|
||||
* Set the current animation that is played by this AnimChannel.
|
||||
* <p>
|
||||
* See {@link #setAnim(java.lang.String, float)}.
|
||||
* The blendTime argument by default is 150 milliseconds.
|
||||
*
|
||||
* @param action The action (name) of the animation to play
|
||||
*/
|
||||
def setAnim(action: Action): Unit = uval.setAnim(action.name)
|
||||
|
||||
/**
|
||||
* Set the current animation that is played by this AnimChannel.
|
||||
* <p>
|
||||
* This resets the time to zero, and optionally blends the animation
|
||||
* over <code>blendTime</code> seconds with the currently playing animation.
|
||||
* Notice that this method will reset the control's speed to 1.0.
|
||||
*
|
||||
* @param action The action (name) of the animation to play
|
||||
* @param blendTime The blend time over which to blend the new animation
|
||||
* with the old one. If zero, then no blending will occur and the new
|
||||
* animation will be applied instantly.
|
||||
*/
|
||||
def setAnim(action: Action, blendTime: Float): Unit = uval.setAnim(action.name, blendTime)
|
||||
|
||||
|
||||
}
|
||||
|
||||
implicit class AnimEventListenerWrap(val uval: AnimEventListener) extends AnyVal {
|
||||
|
||||
/**
|
||||
* Invoked when an animation "cycle" is done. For non-looping animations,
|
||||
* this event is invoked when the animation is finished playing. For
|
||||
* looping animations, this even is invoked each time the animation is restarted.
|
||||
*
|
||||
* @param control The control to which the listener is assigned.
|
||||
* @param channel The channel being altered
|
||||
* @param action The new animation action that is done.
|
||||
*/
|
||||
def onAnimCycleDone(control: AnimControl, channel: AnimChannel, action: Action): Unit =
|
||||
uval.onAnimCycleDone(control, channel, action.name)
|
||||
|
||||
/**
|
||||
* Invoked when a animation is set to play by the user on the given channel.
|
||||
*
|
||||
* @param control The control to which the listener is assigned.
|
||||
* @param channel The channel being altered
|
||||
* @param action The new animation action set.
|
||||
*/
|
||||
def onAnimChange(control: AnimControl, channel: AnimChannel, action: Action): Unit =
|
||||
uval.onAnimChange(control, channel, action.name)
|
||||
}
|
||||
|
||||
|
||||
}
|
16
src/main/scala/com/jme3/app/package.scala
Normal file
16
src/main/scala/com/jme3/app/package.scala
Normal file
@ -0,0 +1,16 @@
|
||||
package com.jme3
|
||||
|
||||
/**
|
||||
* Created by Brandon Barker on 6/19/17.
|
||||
*/
|
||||
package object app {
|
||||
|
||||
implicit class SimpleApplicationWrap(val uval: SimpleApplication) extends AnyVal {
|
||||
|
||||
//FIXME: proof of concept, remove later
|
||||
def testWrap: String = uval.hashCode().toString
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
6
src/main/scala/com/jme3/input/Action.scala
Normal file
6
src/main/scala/com/jme3/input/Action.scala
Normal file
@ -0,0 +1,6 @@
|
||||
package com.jme3.input
|
||||
|
||||
/**
|
||||
* Created by Brandon Barker on 6/19/17.
|
||||
*/
|
||||
final case class Action(name: String) extends AnyVal
|
37
src/main/scala/com/jme3/input/controls/package.scala
Normal file
37
src/main/scala/com/jme3/input/controls/package.scala
Normal file
@ -0,0 +1,37 @@
|
||||
package com.jme3.input
|
||||
|
||||
/**
|
||||
* Created by Brandon Barker on 6/19/17.
|
||||
*/
|
||||
package object controls {
|
||||
|
||||
implicit class ActionListenerWrap(val uval: ActionListener) extends AnyVal {
|
||||
|
||||
/**
|
||||
* Called when an input to which this listener is registered to is invoked.
|
||||
*
|
||||
* @param action The action (name) of the mapping that was invoked
|
||||
* @param isPressed True if the action is "pressed", false otherwise
|
||||
* @param tpf The time per frame value.
|
||||
*/
|
||||
def onAction(action: Action, keyPressed: Boolean, tpf: Float): Unit =
|
||||
uval.onAction(action.name, keyPressed, tpf)
|
||||
}
|
||||
|
||||
implicit class AnalogListenerWrap(val uval: AnalogListener) extends AnyVal {
|
||||
|
||||
/**
|
||||
* Called to notify the implementation that an analog event has occurred.
|
||||
*
|
||||
* The results of KeyTrigger and MouseButtonTrigger events will have tpf
|
||||
* == value.
|
||||
*
|
||||
* @param action The action (name) of the mapping that was invoked
|
||||
* @param value Value of the axis, from 0 to 1.
|
||||
* @param tpf The time per frame value.
|
||||
*/
|
||||
def onAnalog(action: Action, value: Float, tpf: Float): Unit =
|
||||
uval.onAnalog(action.name, value, tpf)
|
||||
}
|
||||
|
||||
}
|
18
src/main/scala/com/jme3/input/package.scala
Normal file
18
src/main/scala/com/jme3/input/package.scala
Normal file
@ -0,0 +1,18 @@
|
||||
package com.jme3
|
||||
|
||||
import com.jme3.input.controls.{InputListener, Trigger}
|
||||
|
||||
/**
|
||||
* Created by Brandon Barker on 6/21/17.
|
||||
*/
|
||||
package object input {
|
||||
|
||||
implicit class InputManagerWrap(val uval: InputManager) extends AnyVal {
|
||||
def addMapping(action: Action, triggers: Trigger*): Unit =
|
||||
uval.addMapping(action.name, triggers: _*)
|
||||
def addListener(listener: InputListener, actions: Action*): Unit =
|
||||
uval.addListener(listener, actions.map(act => act.name): _*)
|
||||
|
||||
}
|
||||
|
||||
}
|
11
src/main/scala/com/jme3/material/package.scala
Normal file
11
src/main/scala/com/jme3/material/package.scala
Normal file
@ -0,0 +1,11 @@
|
||||
package com.jme3
|
||||
|
||||
import com.jme3.asset.AssetManager
|
||||
|
||||
package object material {
|
||||
|
||||
object Material {
|
||||
def apply(contentMan: AssetManager, defName: String): Material =
|
||||
new Material(contentMan, defName)
|
||||
}
|
||||
}
|
19
src/main/scala/com/jme3/scene/debug/package.scala
Normal file
19
src/main/scala/com/jme3/scene/debug/package.scala
Normal file
@ -0,0 +1,19 @@
|
||||
package com.jme3.scene
|
||||
|
||||
import com.jme3.animation.Skeleton
|
||||
|
||||
package object debug {
|
||||
|
||||
object SkeletonDebugger {
|
||||
|
||||
/**
|
||||
* Creates a debugger with no length data. The wires will be a connection between the bones' heads only.
|
||||
* The points will show the bones' heads only and no dotted line of inter bones connection will be visible.
|
||||
* @param name
|
||||
* the name of the debugger's node
|
||||
* @param skeleton
|
||||
* the skeleton that will be shown
|
||||
*/
|
||||
def apply(name: String, skeleton: Skeleton) = new SkeletonDebugger(name, skeleton)
|
||||
}
|
||||
}
|
56
src/main/scala/com/jme3/scene/package.scala
Normal file
56
src/main/scala/com/jme3/scene/package.scala
Normal file
@ -0,0 +1,56 @@
|
||||
package com.jme3
|
||||
|
||||
import com.jme3.scene.control.Control
|
||||
|
||||
/**
|
||||
* Created by Brandon Barker on 6/19/17.
|
||||
*/
|
||||
package object scene {
|
||||
|
||||
object Geometry {
|
||||
|
||||
/**
|
||||
* Create a geometry node with mesh data.
|
||||
* The material of the geometry is null, it cannot
|
||||
* be rendered until it is set.
|
||||
*
|
||||
* @param name The name of this geometry
|
||||
* @param mesh The mesh data for this geometry
|
||||
*/
|
||||
def apply(name: String, mesh: Mesh): Geometry = new Geometry(name, mesh)
|
||||
|
||||
}
|
||||
|
||||
object Node {
|
||||
|
||||
/**
|
||||
* Constructor instantiates a new <code>Node</code> with a default empty
|
||||
* list for containing children.
|
||||
*
|
||||
* @param name the name of the scene element. This is required for
|
||||
* identification and comparison purposes.
|
||||
*/
|
||||
def apply(name: String): Node = new Node(name)
|
||||
}
|
||||
|
||||
implicit class NodeWrap(val uval: Node) extends AnyVal {
|
||||
|
||||
def getControlMaybe[T <: Control](controlType: Class[T]): Option[T] =
|
||||
Option(uval.getControl(controlType))
|
||||
|
||||
}
|
||||
|
||||
implicit class SpatialWrap(val uval: Spatial) extends AnyVal {
|
||||
|
||||
def toNode: Either[ClassCastException, Node] =
|
||||
uval match {
|
||||
case ul: Node => Right(ul)
|
||||
case ul =>
|
||||
Left(
|
||||
new ClassCastException(s"Couldn't convert ${ul.getName} to Node")
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
20
src/main/scala/com/jme3/scene/shape/package.scala
Normal file
20
src/main/scala/com/jme3/scene/shape/package.scala
Normal file
@ -0,0 +1,20 @@
|
||||
package com.jme3.scene
|
||||
|
||||
package object shape {
|
||||
|
||||
object Box {
|
||||
|
||||
/**
|
||||
* Creates a new box.
|
||||
* <p>
|
||||
* The box has a center of 0,0,0 and extends in the out from the center by
|
||||
* the given amount in <em>each</em> direction. So, for example, a box
|
||||
* with extent of 0.5 would be the unit cube.
|
||||
*
|
||||
* @param xs the size of the box along the x axis, in both directions.
|
||||
* @param ys the size of the box along the y axis, in both directions.
|
||||
* @param zs the size of the box along the z axis, in both directions.
|
||||
*/
|
||||
def apply(xs: Float, ys: Float, zs: Float): Box = new Box(xs, ys, zs)
|
||||
}
|
||||
}
|
14
src/main/scala/com/jme3/syntax/package.scala
Normal file
14
src/main/scala/com/jme3/syntax/package.scala
Normal file
@ -0,0 +1,14 @@
|
||||
package com.jme3
|
||||
|
||||
|
||||
/**
|
||||
* Created by Brandon Barker on 6/21/17.
|
||||
*/
|
||||
package object syntax {
|
||||
|
||||
@specialized def discard[A](evaluateForSideEffectOnly: A): Unit = {
|
||||
val _: A = evaluateForSideEffectOnly
|
||||
() //Return unit to prevent warning due to discarding value
|
||||
}
|
||||
|
||||
}
|
5
src/main/scala/message.txt
Normal file
5
src/main/scala/message.txt
Normal file
@ -0,0 +1,5 @@
|
||||
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?
|
127
src/main/scala/wow/doge/mygame/Main.scala
Normal file
127
src/main/scala/wow/doge/mygame/Main.scala
Normal file
@ -0,0 +1,127 @@
|
||||
package wow.doge.mygame
|
||||
|
||||
import game.GameApp
|
||||
import com.jme3.app.StatsAppState
|
||||
|
||||
import akka.actor.typed.ActorSystem
|
||||
import akka.actor.typed.SpawnProtocol
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import akka.actor.typed.Behavior
|
||||
import akka.util.Timeout
|
||||
import com.jme3.system.AppSettings
|
||||
import wow.doge.mygame.game.GameAppActor
|
||||
import wow.doge.mygame.scriptsystem.ScriptCachingActor
|
||||
object Main extends App {
|
||||
import java.util.logging.{Logger, Level}
|
||||
Logger.getLogger("").setLevel(Level.SEVERE)
|
||||
|
||||
// runner.runCode("""|println("starting scala script engine")""".stripMargin)
|
||||
val gameApp = new GameApp(
|
||||
// new EntityDataState(),
|
||||
// new TestAppState(),
|
||||
// new PlayerMovementState(),
|
||||
// new FlyCamAppState(),
|
||||
new StatsAppState()
|
||||
)
|
||||
val settings = new AppSettings(true)
|
||||
// settings.setVSync(true)
|
||||
settings.setFrameRate(144)
|
||||
gameApp.setSettings(settings)
|
||||
val actorSystem = ActorSystem(RootActor(gameApp), "rootActor")
|
||||
// actorSystem.eventStream
|
||||
// gameApp.start()
|
||||
println("here 1")
|
||||
// actorSystem.terminate()
|
||||
// JMEExecutorService.shutdown()
|
||||
// println("here 2")
|
||||
//FIXME remove this
|
||||
// System.exit(0)
|
||||
}
|
||||
|
||||
object RootActor {
|
||||
def apply(app: GameApp): Behavior[SpawnProtocol.Command] =
|
||||
Behaviors.setup { ctx =>
|
||||
ctx.log.debug("Starting root actor")
|
||||
val testActor = ctx.spawn(TestActor(), "testActor")
|
||||
val _ = ctx.spawn(GameAppActor(app), "gameAppActor")
|
||||
|
||||
testActor ! TestActor.Test
|
||||
SpawnProtocol()
|
||||
}
|
||||
}
|
||||
|
||||
object TestActor {
|
||||
sealed trait Command
|
||||
case object Test extends Command
|
||||
private case object Done extends Command
|
||||
// sealed trait Result
|
||||
// case object Done extends Result
|
||||
|
||||
import scala.concurrent.duration._
|
||||
implicit val timeout = Timeout(15.seconds)
|
||||
// implicit val scheduler =
|
||||
|
||||
def apply(): Behavior[Command] =
|
||||
Behaviors.setup { ctx =>
|
||||
ctx.spawn(ScriptCachingActor(), "scriptCacher")
|
||||
Behaviors.receiveMessage { msg =>
|
||||
msg match {
|
||||
case Test =>
|
||||
// ctx.ask(
|
||||
// router,
|
||||
// ScriptActor.Compile(
|
||||
// // os.pwd / "some.sc",
|
||||
// os.pwd / "src" / "main" / "resources" / "hello2.main.kts",
|
||||
// _
|
||||
// )
|
||||
// ) {
|
||||
// case Success(value) =>
|
||||
// ctx.log.debug("Received Value")
|
||||
// ctx.log.debug(value.toString())
|
||||
// Done
|
||||
// case Failure(exception) =>
|
||||
// ctx.log.debug(s"Received Error ${exception.getMessage()}")
|
||||
// Done
|
||||
// }
|
||||
// val x = scriptStorer
|
||||
// .askT(
|
||||
// ScriptStoringActor
|
||||
// .Get(os.pwd / "src" / "main" / "resources" / "hello2.sc", _)
|
||||
// )(timeout, ctx.system.scheduler)
|
||||
|
||||
// ctx.ask(
|
||||
// scriptStorer,
|
||||
// ScriptStoringActor
|
||||
// .Get(os.pwd / "src" / "main" / "resources" / "hello2.sc", _)
|
||||
// ) {
|
||||
// case Success(value) => {
|
||||
// ctx.log.debug(value.toString())
|
||||
// ctx.ask(
|
||||
// scriptStorer,
|
||||
// ScriptStoringActor
|
||||
// .Get(os.pwd / "src" / "main" / "resources" / "hello2.sc", _)
|
||||
// ) {
|
||||
// case Success(value) => {
|
||||
// ctx.log.debug(value.toString())
|
||||
// Done
|
||||
// }
|
||||
// case Failure(exception) =>
|
||||
// ctx.log.debug(exception.getMessage())
|
||||
// Done
|
||||
// }
|
||||
// Done
|
||||
// }
|
||||
// case Failure(exception) =>
|
||||
// ctx.log.debug(exception.getMessage())
|
||||
// Done
|
||||
// }
|
||||
|
||||
Behaviors.same
|
||||
case Done => Behaviors.same
|
||||
}
|
||||
|
||||
// SpawnProtocol()
|
||||
// Behaviors.same
|
||||
}
|
||||
}
|
||||
}
|
6
src/main/scala/wow/doge/mygame/components/Position.scala
Normal file
6
src/main/scala/wow/doge/mygame/components/Position.scala
Normal file
@ -0,0 +1,6 @@
|
||||
package wow.doge.mygame.components
|
||||
|
||||
import com.simsilica.es.EntityComponent;
|
||||
import wow.doge.mygame.math.ImVector3f
|
||||
|
||||
final case class Position(location: ImVector3f) extends EntityComponent
|
28
src/main/scala/wow/doge/mygame/components/Tag.scala
Normal file
28
src/main/scala/wow/doge/mygame/components/Tag.scala
Normal file
@ -0,0 +1,28 @@
|
||||
package wow.doge.mygame.components
|
||||
|
||||
import com.simsilica.es.EntityComponent
|
||||
|
||||
final case class Tag(name: String) extends EntityComponent
|
||||
|
||||
object Tag {
|
||||
val SpaceShip = "SpaceShip"
|
||||
val BasicInvader = "BasicInvader"
|
||||
}
|
||||
// public class Model implements EntityComponent {
|
||||
// private final String name;
|
||||
// public final static String SpaceShip = "SpaceShip";
|
||||
// public final static String BasicInvader = "BasicInvader";
|
||||
|
||||
// public Model(String name) {
|
||||
// this.name = name;
|
||||
// }
|
||||
|
||||
// public String getName() {
|
||||
// return name;
|
||||
// }
|
||||
|
||||
// @Override
|
||||
// public String toString() {
|
||||
// return "Model[" + name + "]";
|
||||
// }
|
||||
// }
|
11
src/main/scala/wow/doge/mygame/components/Test.java
Normal file
11
src/main/scala/wow/doge/mygame/components/Test.java
Normal file
@ -0,0 +1,11 @@
|
||||
package wow.doge.mygame.components;
|
||||
|
||||
import com.jme3.math.Vector3f;
|
||||
import wow.doge.mygame.math.ImVector3f;
|
||||
|
||||
//test java final case class instantiation
|
||||
public class Test {
|
||||
public void test() {
|
||||
var pos = Position.apply(ImVector3f.apply(1, 1, 1));
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package wow.doge.mygame.components
|
||||
|
||||
import com.simsilica.es.EntityComponent
|
||||
|
||||
final case class TestComponent() extends EntityComponent
|
139
src/main/scala/wow/doge/mygame/events/EventBus.scala
Normal file
139
src/main/scala/wow/doge/mygame/events/EventBus.scala
Normal file
@ -0,0 +1,139 @@
|
||||
package wow.doge.mygame.events
|
||||
|
||||
// import akka.event.ActorEventBus
|
||||
// import akka.event.ManagedActorClassification
|
||||
// import akka.event.ActorClassifier
|
||||
import akka.actor.typed.ActorRef
|
||||
// import akka.actor.ActorSystem
|
||||
// import akka.event.EventBus
|
||||
// import akka.util.Subclassification
|
||||
// import java.util.concurrent.atomic.AtomicReference
|
||||
import akka.actor.typed.Behavior
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import scala.reflect.ClassTag
|
||||
import akka.event.EventStream
|
||||
|
||||
// private[events] final case class ClassificationMessage(ref: ActorRef, id: Int)
|
||||
|
||||
// class ActorBusImpl(val system: ActorSystem, val busSize: Int)
|
||||
// extends ActorEventBus
|
||||
// with ActorClassifier
|
||||
// with ManagedActorClassification {
|
||||
// type Event = ClassificationMessage
|
||||
|
||||
// // is used for extracting the classifier from the incoming events
|
||||
// override protected def classify(event: Event): ActorRef = event.ref
|
||||
|
||||
// // determines the initial size of the index data structure
|
||||
// // used internally (i.e. the expected number of different classifiers)
|
||||
// override protected def mapSize: Int = busSize
|
||||
// }
|
||||
|
||||
// class StartsWithSubclassification extends Subclassification[String] {
|
||||
// override def isEqual(x: String, y: String): Boolean =
|
||||
// x == y
|
||||
|
||||
// override def isSubclass(x: String, y: String): Boolean =
|
||||
// x.startsWith(y)
|
||||
// }
|
||||
|
||||
// import akka.event.SubchannelClassification
|
||||
|
||||
// final case class MsgEnvelope(topic: String, payload: Any)
|
||||
// import akka.actor.typed.scaladsl.adapter._
|
||||
|
||||
// /**
|
||||
// * Publishes the payload of the MsgEnvelope when the topic of the
|
||||
// * MsgEnvelope starts with the String specified when subscribing.
|
||||
// */
|
||||
// class SubchannelBusImpl extends EventBus with SubchannelClassification {
|
||||
// type Event = Any
|
||||
// type Classifier = Class[_]
|
||||
// type Subscriber = ActorRef
|
||||
|
||||
// // Subclassification is an object providing `isEqual` and `isSubclass`
|
||||
// // to be consumed by the other methods of this classifier
|
||||
// // override protected val subclassification: Subclassification[Classifier] =
|
||||
// // new StartsWithSubclassification
|
||||
|
||||
// private val initiallySubscribedOrUnsubscriber =
|
||||
// new AtomicReference[Either[Set[ActorRef], ActorRef]](Left(Set.empty))
|
||||
|
||||
// override protected implicit val subclassification
|
||||
// : Subclassification[Classifier] = new Subclassification[Class[_]] {
|
||||
// def isEqual(x: Class[_], y: Class[_]) = x == y
|
||||
// def isSubclass(x: Class[_], y: Class[_]) = y.isAssignableFrom(x)
|
||||
// }
|
||||
|
||||
// // is used for extracting the classifier from the incoming events
|
||||
// override protected def classify(event: Event): Classifier = event.getClass()
|
||||
|
||||
// // will be invoked for each event for all subscribers which registered
|
||||
// // themselves for the event’s classifier
|
||||
// override protected def publish(event: Event, subscriber: Subscriber): Unit = {
|
||||
// // subscriber ! event.payload
|
||||
// subscriber ! event
|
||||
// }
|
||||
// }
|
||||
|
||||
object EventBus {
|
||||
sealed trait Command[A]
|
||||
final case class Publish[A, E <: A](event: E, publisher: ActorRef[_])
|
||||
extends Command[A]
|
||||
|
||||
/**
|
||||
* Subscribe a typed actor to listen for types or subtypes of E
|
||||
* by sending this command to the [[akka.actor.typed.ActorSystem.eventStream]].
|
||||
*
|
||||
* ==Simple example==
|
||||
* {{{
|
||||
* sealed trait A
|
||||
* case object A1 extends A
|
||||
* //listen for all As
|
||||
* def subscribe(actorSystem: ActorSystem[_], actorRef: ActorRef[A]) =
|
||||
* actorSystem.eventStream ! EventStream.Subscribe(actorRef)
|
||||
* //listen for A1s only
|
||||
* def subscribe(actorSystem: ActorSystem[_], actorRef: ActorRef[A]) =
|
||||
* actorSystem.eventStream ! EventStream.Subscribe[A1](actorRef)
|
||||
* }}}
|
||||
*/
|
||||
final case class Subscribe[A, E <: A](subscriber: ActorRef[E])(implicit
|
||||
classTag: ClassTag[E]
|
||||
) extends Command[A] {
|
||||
|
||||
def topic: Class[_] = classTag.runtimeClass
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe an actor ref from the event stream
|
||||
* by sending this command to the [[akka.actor.typed.ActorSystem.eventStream]].
|
||||
*/
|
||||
final case class Unsubscribe[A, E <: A](subscriber: ActorRef[E])
|
||||
extends Command[A]
|
||||
|
||||
def apply[A](): Behavior[EventBus.Command[A]] =
|
||||
Behaviors.setup { ctx =>
|
||||
val eventStream = new EventStream(ctx.system.classicSystem)
|
||||
new EventBus().eventStreamBehavior(eventStream)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class EventBus[B] {
|
||||
import akka.actor.typed.scaladsl.adapter._
|
||||
|
||||
private def eventStreamBehavior(
|
||||
eventStream: akka.event.EventStream
|
||||
): Behavior[EventBus.Command[B]] =
|
||||
Behaviors.receiveMessage {
|
||||
case EventBus.Publish(event, publisher) =>
|
||||
eventStream.publish(event)
|
||||
Behaviors.same
|
||||
case s @ EventBus.Subscribe(subscriber) =>
|
||||
eventStream.subscribe(subscriber.toClassic, s.topic)
|
||||
Behaviors.same
|
||||
case EventBus.Unsubscribe(subscriber) =>
|
||||
eventStream.unsubscribe(subscriber.toClassic)
|
||||
Behaviors.same
|
||||
}
|
||||
}
|
17
src/main/scala/wow/doge/mygame/events/Events.scala
Normal file
17
src/main/scala/wow/doge/mygame/events/Events.scala
Normal file
@ -0,0 +1,17 @@
|
||||
package wow.doge.mygame.events
|
||||
|
||||
// object Test {
|
||||
|
||||
// Events.BulletFired
|
||||
// }
|
||||
|
||||
object Events {
|
||||
sealed trait Event
|
||||
case object BulletFired extends Event
|
||||
// type BulletFired = BulletFired.type
|
||||
case class EventWithData(data: Int) extends Event
|
||||
|
||||
sealed trait Tick extends Event
|
||||
case object RenderTick extends Tick
|
||||
case object PhysicsTick extends Tick
|
||||
}
|
3
src/main/scala/wow/doge/mygame/events/EventsModule.scala
Normal file
3
src/main/scala/wow/doge/mygame/events/EventsModule.scala
Normal file
@ -0,0 +1,3 @@
|
||||
package wow.doge.mygame.events
|
||||
|
||||
trait EventsModule {}
|
@ -0,0 +1,5 @@
|
||||
package wow.doge.mygame.executors
|
||||
|
||||
trait ExecutorsModule {
|
||||
lazy val schedulers = new Schedulers()
|
||||
}
|
103
src/main/scala/wow/doge/mygame/executors/GUIExecutor.scala
Normal file
103
src/main/scala/wow/doge/mygame/executors/GUIExecutor.scala
Normal file
@ -0,0 +1,103 @@
|
||||
package wow.doge.mygame.executors
|
||||
|
||||
import akka.dispatch.{
|
||||
DispatcherPrerequisites,
|
||||
ExecutorServiceFactory,
|
||||
ExecutorServiceConfigurator
|
||||
}
|
||||
import com.typesafe.config.Config
|
||||
import java.util.concurrent.{
|
||||
ExecutorService,
|
||||
AbstractExecutorService,
|
||||
ThreadFactory,
|
||||
TimeUnit
|
||||
}
|
||||
import java.util.Collections
|
||||
import javax.swing.SwingUtilities
|
||||
import javafx.application.Platform
|
||||
import monix.execution.Scheduler
|
||||
import scala.concurrent.ExecutionContext
|
||||
import java.util.concurrent.Executor
|
||||
import wow.doge.mygame.Main
|
||||
|
||||
// First we wrap invokeLater/runLater as an ExecutorService
|
||||
trait GUIExecutorService extends AbstractExecutorService {
|
||||
def execute(command: Runnable): Unit
|
||||
|
||||
def shutdown(): Unit = ()
|
||||
|
||||
def shutdownNow() = Collections.emptyList[Runnable]
|
||||
|
||||
def isShutdown = false
|
||||
|
||||
def isTerminated = false
|
||||
|
||||
def awaitTermination(l: Long, timeUnit: TimeUnit) = true
|
||||
}
|
||||
|
||||
object JavaFXExecutorService extends GUIExecutorService {
|
||||
override def execute(command: Runnable) = Platform.runLater(command)
|
||||
}
|
||||
|
||||
object SwingExecutorService extends GUIExecutorService {
|
||||
override def execute(command: Runnable) = SwingUtilities.invokeLater(command)
|
||||
}
|
||||
|
||||
object JMEExecutorService extends GUIExecutorService {
|
||||
override def execute(command: Runnable) = Main.gameApp.enqueue(command)
|
||||
}
|
||||
|
||||
class JavaFXEventThreadExecutorServiceConfigurator(
|
||||
config: Config,
|
||||
prerequisites: DispatcherPrerequisites
|
||||
) extends ExecutorServiceConfigurator(config, prerequisites) {
|
||||
private val f = new ExecutorServiceFactory {
|
||||
def createExecutorService: ExecutorService = JavaFXExecutorService
|
||||
}
|
||||
|
||||
def createExecutorServiceFactory(
|
||||
id: String,
|
||||
threadFactory: ThreadFactory
|
||||
): ExecutorServiceFactory = f
|
||||
}
|
||||
|
||||
class JMEThreadExecutorServiceConfigurator(
|
||||
config: Config,
|
||||
prerequisites: DispatcherPrerequisites
|
||||
) extends ExecutorServiceConfigurator(config, prerequisites) {
|
||||
private val f = new ExecutorServiceFactory {
|
||||
def createExecutorService: ExecutorService = JMEExecutorService
|
||||
}
|
||||
|
||||
def createExecutorServiceFactory(
|
||||
id: String,
|
||||
threadFactory: ThreadFactory
|
||||
): ExecutorServiceFactory = f
|
||||
}
|
||||
|
||||
// Then we create an ExecutorServiceConfigurator so that Akka can use our SwingExecutorService for the dispatchers
|
||||
class SwingEventThreadExecutorServiceConfigurator(
|
||||
config: Config,
|
||||
prerequisites: DispatcherPrerequisites
|
||||
) extends ExecutorServiceConfigurator(config, prerequisites) {
|
||||
private val f = new ExecutorServiceFactory {
|
||||
def createExecutorService: ExecutorService = SwingExecutorService
|
||||
}
|
||||
|
||||
def createExecutorServiceFactory(
|
||||
id: String,
|
||||
threadFactory: ThreadFactory
|
||||
): ExecutorServiceFactory = f
|
||||
}
|
||||
|
||||
object JFXExecutionContexts {
|
||||
val javaFxExecutionContext: ExecutionContext =
|
||||
ExecutionContext.fromExecutor(new Executor {
|
||||
def execute(command: Runnable): Unit = {
|
||||
Platform.runLater(command)
|
||||
}
|
||||
})
|
||||
val fxScheduler =
|
||||
Scheduler(javaFxExecutionContext)
|
||||
|
||||
}
|
10
src/main/scala/wow/doge/mygame/executors/Schedulers.scala
Normal file
10
src/main/scala/wow/doge/mygame/executors/Schedulers.scala
Normal file
@ -0,0 +1,10 @@
|
||||
package wow.doge.mygame.executors
|
||||
|
||||
import monix.execution.Scheduler
|
||||
|
||||
final case class Schedulers(
|
||||
blockingIO: Scheduler = Scheduler.io(),
|
||||
cpu: Scheduler = Scheduler.global,
|
||||
fx: Scheduler = JFXExecutionContexts.fxScheduler,
|
||||
jme: Option[Scheduler] = None
|
||||
)
|
109
src/main/scala/wow/doge/mygame/game/GameApp.scala
Normal file
109
src/main/scala/wow/doge/mygame/game/GameApp.scala
Normal file
@ -0,0 +1,109 @@
|
||||
package wow.doge.mygame.game
|
||||
|
||||
import com.jme3.app.SimpleApplication
|
||||
|
||||
import com.jme3.app.state.AppState
|
||||
import akka.actor.typed.ActorRef
|
||||
import akka.actor.typed.Behavior
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
|
||||
object Greeter {
|
||||
final case class Greet(whom: String, replyTo: ActorRef[Greeted])
|
||||
final case class Greeted(whom: String, from: ActorRef[Greet])
|
||||
|
||||
def apply(): Behavior[Greet] =
|
||||
Behaviors.receive { (context, message) =>
|
||||
// context.log.info("Hello {}!", message.whom)
|
||||
//#greeter-send-messages
|
||||
message.replyTo ! Greeted(message.whom, context.self)
|
||||
//#greeter-send-messages
|
||||
Behaviors.same
|
||||
}
|
||||
}
|
||||
|
||||
class GameApp(
|
||||
// actorSystem: ActorSystem[SpawnProtocol.Command],
|
||||
appStates: AppState*
|
||||
) extends SimpleApplication(appStates: _*) {
|
||||
// implicit val timeout = Timeout(10.seconds)
|
||||
// implicit val scheduler = actorSystem.scheduler
|
||||
|
||||
override def simpleInitApp(): Unit = {
|
||||
|
||||
// val ship = ed.createEntity()
|
||||
// val mbState = stateManager().state[EntityDataState]().map(_.getEntityData())
|
||||
// val mbState = Try(
|
||||
// stateManager()
|
||||
// .state[TestAppState]()
|
||||
// .entity
|
||||
// ).toOption.flatten.toRight(println("empty"))
|
||||
// // .flatMap(_.entity)
|
||||
// val x = mbState.flatMap(
|
||||
// _.query
|
||||
// .filter[TestComponent]("name", new Object())
|
||||
// // .filterOr[TestEntity](
|
||||
// // Filters
|
||||
// // .fieldEquals(classOf[TestEntity], "", null)
|
||||
// // )
|
||||
// .component[Tag]()
|
||||
// .component[TestComponent]()
|
||||
// .result
|
||||
// .toRight(println("failed"))
|
||||
// )
|
||||
|
||||
// rootNode
|
||||
// .child(geom)
|
||||
// .child(geom)
|
||||
// .child(geom)
|
||||
// .child(geom)
|
||||
// .child(geom)
|
||||
// .child(geom)
|
||||
// .child(geom)
|
||||
// .child(geom)
|
||||
// Future(println("hello"))(jmeEC(this))
|
||||
// val wbActor: Future[ActorRef[Greeter.Greet]] = actorSystem.ask(
|
||||
// SpawnProtocol.Spawn(
|
||||
// behavior = Greeter(),
|
||||
// name = "listener",
|
||||
// DispatcherSelector.fromConfig("jme-dispatcher"),
|
||||
// _
|
||||
// )
|
||||
// )
|
||||
|
||||
// wbActor.map(a => a.ask(Greeter.Greet("hello", _)).map(println))
|
||||
|
||||
}
|
||||
override def simpleUpdate(tpf: Float): Unit = {
|
||||
// val rot2 = rot.fromAngleAxis(FastMath.PI, new Vector3f(0, 0, 1))
|
||||
// val rotation = geom.getLocalRotation()
|
||||
// rotation.add(rot2)
|
||||
// geom.rotate(rot2)
|
||||
|
||||
// geom.updateModelBound()
|
||||
// geom.updateGeometricState()
|
||||
}
|
||||
|
||||
// override def stop(): Unit = {
|
||||
// actorSystem.terminate()
|
||||
// super.stop()
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
object GameApp {
|
||||
// def myExec(app: SimpleApplication, command: Runnable) = {
|
||||
// app.enqueue(command)
|
||||
// }
|
||||
// val javaFxExecutionContext: ExecutionContext =
|
||||
// ExecutionContext.fromExecutor(new Executor {
|
||||
// def execute(command: Runnable): Unit = {
|
||||
// Platform.runLater(command)
|
||||
// }
|
||||
// })
|
||||
// def jmeEC(app: SimpleApplication): ExecutionContext =
|
||||
// ExecutionContext.fromExecutor(new Executor {
|
||||
// override def execute(command: Runnable): Unit = app.enqueue(command)
|
||||
// })
|
||||
|
||||
// def jmeScheduler(app: SimpleApplication) = Sch
|
||||
}
|
67
src/main/scala/wow/doge/mygame/game/GameAppActor.scala
Normal file
67
src/main/scala/wow/doge/mygame/game/GameAppActor.scala
Normal file
@ -0,0 +1,67 @@
|
||||
package wow.doge.mygame.game
|
||||
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import wow.doge.mygame.state.MovementActor
|
||||
import wow.doge.mygame.state.PlayerMovementState2
|
||||
import wow.doge.mygame.state.MovementActorTimer
|
||||
import com.jme3.scene.shape.Box
|
||||
import com.jme3.scene.Geometry
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.events.EventBus
|
||||
import wow.doge.mygame.events.Events
|
||||
import wow.doge.mygame.state.ImMovementActor
|
||||
|
||||
object GameAppActor {
|
||||
|
||||
sealed trait Command
|
||||
def apply(app: GameApp) =
|
||||
Behaviors.setup[Command] { ctx =>
|
||||
lazy val b = new Box(1, 1, 1)
|
||||
lazy val geom = new Geometry("Box", b)
|
||||
val movementActor =
|
||||
ctx.spawn(
|
||||
MovementActor(MovementActor.Props(app, geom)),
|
||||
"movementActor"
|
||||
// DispatcherSelector.fromConfig("jme-dispatcher")
|
||||
)
|
||||
|
||||
val movementActorTimer = ctx.spawn(
|
||||
MovementActorTimer(movementActor),
|
||||
"movementActorTimer"
|
||||
)
|
||||
val imMovementActor = ctx.spawn(
|
||||
ImMovementActor(ImMovementActor.Props(app, geom)),
|
||||
"imMovementActor"
|
||||
)
|
||||
|
||||
val subscribingActor = ctx.spawn(SubscribingActor(), "subscriber-1")
|
||||
|
||||
val eventBus =
|
||||
ctx.spawn(Behaviors.logMessages(EventBus[Events.Tick]()), "eventBus1")
|
||||
|
||||
eventBus ! EventBus.Subscribe(subscribingActor)
|
||||
|
||||
eventBus ! EventBus.Publish(Events.PhysicsTick, ctx.self)
|
||||
|
||||
app
|
||||
.getStateManager()
|
||||
.attach(
|
||||
new PlayerMovementState2(
|
||||
movementActor,
|
||||
movementActorTimer,
|
||||
imMovementActor,
|
||||
geom
|
||||
)
|
||||
)
|
||||
app.start()
|
||||
Behaviors.stopped
|
||||
}
|
||||
}
|
||||
|
||||
object SubscribingActor {
|
||||
def apply() =
|
||||
Behaviors.receive[Events.PhysicsTick.type] { (ctx, msg) =>
|
||||
ctx.log.debug(s"received event $msg")
|
||||
Behaviors.same
|
||||
}
|
||||
}
|
55
src/main/scala/wow/doge/mygame/implicits/EntityQuery.scala
Normal file
55
src/main/scala/wow/doge/mygame/implicits/EntityQuery.scala
Normal file
@ -0,0 +1,55 @@
|
||||
package wow.doge.mygame.implicits
|
||||
|
||||
import com.simsilica.es.ComponentFilter
|
||||
import com.simsilica.es.EntityComponent
|
||||
import com.simsilica.es.EntityData
|
||||
import com.simsilica.es.EntitySet
|
||||
import com.simsilica.es.Filters
|
||||
import scala.reflect.ClassTag
|
||||
import scala.util.Try
|
||||
|
||||
class EntityQuery(ed: EntityData) {
|
||||
private var cfilter: Option[ComponentFilter[_ <: EntityComponent]] = None
|
||||
private val buf = collection.mutable.ListBuffer.empty[Class[_]]
|
||||
|
||||
def filter[T <: EntityComponent](field: String, value: Object)(implicit
|
||||
ev: ClassTag[T]
|
||||
): EntityQuery = {
|
||||
val c = ev.runtimeClass.asInstanceOf[Class[T]]
|
||||
cfilter = Try(Filters.fieldEquals(c, field, value)).toOption
|
||||
this
|
||||
}
|
||||
|
||||
def filterOr[T <: EntityComponent](operands: ComponentFilter[_ <: T]*)(
|
||||
implicit ev: ClassTag[T]
|
||||
): EntityQuery = {
|
||||
val c = ev.runtimeClass.asInstanceOf[Class[T]]
|
||||
cfilter = Try(Filters.or(c, operands: _*)).toOption
|
||||
this
|
||||
}
|
||||
|
||||
def filterAnd[T <: EntityComponent](operands: ComponentFilter[_ <: T]*)(
|
||||
implicit ev: ClassTag[T]
|
||||
): EntityQuery = {
|
||||
val c = ev.runtimeClass.asInstanceOf[Class[T]]
|
||||
cfilter = Try(Filters.and(c, operands: _*)).toOption
|
||||
this
|
||||
}
|
||||
|
||||
def component[T <: EntityComponent]()(implicit
|
||||
ev: ClassTag[T]
|
||||
): EntityQuery = {
|
||||
val c = ev.runtimeClass
|
||||
buf += c
|
||||
this
|
||||
}
|
||||
|
||||
def components[T <: EntityComponent](lst: Class[T]*): EntitySet = {
|
||||
ed.getEntities(lst: _*)
|
||||
}
|
||||
|
||||
def result: EntitySet =
|
||||
cfilter.fold(ed.getEntities(buf.toList: _*)) { filters =>
|
||||
ed.getEntities(filters, buf.toList: _*)
|
||||
}
|
||||
}
|
175
src/main/scala/wow/doge/mygame/implicits/package.scala
Normal file
175
src/main/scala/wow/doge/mygame/implicits/package.scala
Normal file
@ -0,0 +1,175 @@
|
||||
package wow.doge.mygame
|
||||
|
||||
import com.jme3.app.SimpleApplication
|
||||
import com.jme3.app.state.AppStateManager
|
||||
import scala.reflect.ClassTag
|
||||
import com.jme3.app.state.AppState
|
||||
import com.jme3.scene.Node
|
||||
import com.jme3.scene.Spatial
|
||||
import com.simsilica.es.EntityData
|
||||
import com.simsilica.es.EntityComponent
|
||||
import com.simsilica.es.EntityId
|
||||
import akka.actor.typed.ActorRef
|
||||
import akka.util.Timeout
|
||||
import akka.actor.typed.Scheduler
|
||||
import monix.eval.Task
|
||||
import com.jme3.input.InputManager
|
||||
import com.jme3.input.controls.Trigger
|
||||
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.state.CanMove
|
||||
import com.jme3.renderer.Camera
|
||||
|
||||
package object implicits {
|
||||
implicit class StateManagerExt(val sm: AppStateManager) extends AnyVal {
|
||||
def state[S <: AppState]()(implicit c: ClassTag[S]): S =
|
||||
sm.getState(c.runtimeClass.asInstanceOf[Class[S]])
|
||||
|
||||
}
|
||||
|
||||
implicit class SimpleApplicationExt(val sa: SimpleApplication)
|
||||
extends AnyVal {
|
||||
def stateManager() = sa.getStateManager()
|
||||
}
|
||||
|
||||
implicit class NodeExt(val n: Node) extends AnyVal {
|
||||
def child(s: Spatial): Node = {
|
||||
n.attachChild(s)
|
||||
n
|
||||
}
|
||||
}
|
||||
|
||||
implicit class EntityDataExt(val ed: EntityData) extends AnyVal {
|
||||
|
||||
def query = new EntityQuery(ed)
|
||||
|
||||
// def entities[T <: EntityComponent](entities: Seq[T])
|
||||
}
|
||||
|
||||
implicit class EntityExt(val e: EntityId) extends AnyVal {
|
||||
def withComponents(classes: EntityComponent*)(implicit
|
||||
ed: EntityData
|
||||
): EntityId = {
|
||||
ed.setComponents(e, classes: _*)
|
||||
e
|
||||
}
|
||||
}
|
||||
|
||||
implicit class ActorRefExt[Req](val a: ActorRef[Req]) extends AnyVal {
|
||||
import akka.actor.typed.scaladsl.AskPattern._
|
||||
def askT[Res](
|
||||
replyTo: ActorRef[Res] => Req
|
||||
)(implicit timeout: Timeout, scheduler: Scheduler): Task[Res] = {
|
||||
Task.deferFuture(a.ask(replyTo)(timeout, scheduler))
|
||||
}
|
||||
}
|
||||
// def ?[Res](replyTo: ActorRef[Res] => Req)(implicit timeout: Timeout, scheduler: Scheduler): Future[Res] = {
|
||||
// ask(replyTo)(timeout, scheduler)
|
||||
// }
|
||||
|
||||
implicit class InputManagerExt(val inputManager: InputManager)
|
||||
extends AnyVal {
|
||||
def withMapping(mapping: String, triggers: Trigger*): InputManager = {
|
||||
inputManager.addMapping(mapping, triggers: _*)
|
||||
inputManager
|
||||
}
|
||||
|
||||
def withListener(listener: InputListener, mappings: String*) = {
|
||||
inputManager.addListener(listener, mappings: _*)
|
||||
inputManager
|
||||
}
|
||||
}
|
||||
|
||||
implicit class Vector3fExt(val v: Vector3f) extends AnyVal {
|
||||
def +=(that: Vector3f) = v.addLocal(that)
|
||||
def *=(that: Vector3f) = v.multLocal(that)
|
||||
def -=(that: Vector3f) = v.subtractLocal(that)
|
||||
def /=(that: Vector3f) = v.divideLocal(that)
|
||||
def unary_- = v.negateLocal()
|
||||
def immutable = ImVector3f(v.x, v.y, v.z)
|
||||
}
|
||||
|
||||
implicit class ImVector3fExt(val v: ImVector3f) extends AnyVal {
|
||||
def +(that: ImVector3f) = v.copy(v.x + that.x, v.y + that.y, v.z + that.z)
|
||||
def *(that: ImVector3f) = v.copy(v.x * that.x, v.y * that.y, v.z * that.z)
|
||||
def *(f: Float): ImVector3f =
|
||||
// v.copy(v.x * f, v.y * f, v.x * f)
|
||||
v * ImVector3f(f, f, f)
|
||||
def -(that: ImVector3f) = v.copy(v.x - that.x, v.y - that.y, v.z - that.z)
|
||||
def /(that: ImVector3f) = v.copy(v.x / that.x, v.y / that.y, v.z / that.z)
|
||||
def unary_- = v.copy(-v.x, -v.y, -v.z)
|
||||
// def unary_-(that: ImVector3f) = this.copy(this.x, this.y, this.z)
|
||||
def mutable = new Vector3f(v.x, v.y, v.z)
|
||||
}
|
||||
|
||||
// implicit val implVector3fForVector3 = new Vector3[Vector3f] {
|
||||
// override def +(implicit v: Vector3f, that: com.jme3.math.Vector3f): Unit =
|
||||
// v += that
|
||||
|
||||
// override def *(implicit v: Vector3f, that: Vector3f): Unit = v *= that
|
||||
|
||||
// override def -(implicit v: Vector3f, that: Vector3f): Unit = v -= that
|
||||
|
||||
// override def /(implicit v: Vector3f, that: Vector3f): Unit = v /= that
|
||||
|
||||
// }
|
||||
|
||||
// implicit val implImVector3fForVector3 = new Vector3[ImVector3f] {
|
||||
// override def +(implicit v: ImVector3f, that: ImVector3f): Unit =
|
||||
// v + that
|
||||
|
||||
// override def *(implicit v: ImVector3f, that: ImVector3f): Unit = v * that
|
||||
|
||||
// override def -(implicit v: ImVector3f, that: ImVector3f): Unit = v - that
|
||||
|
||||
// override def /(implicit v: ImVector3f, that: ImVector3f): Unit = v / that
|
||||
|
||||
// }
|
||||
|
||||
// def test[T](v: T)(implicit ev: Vector3[T]) = {
|
||||
// import ev._
|
||||
// ev.+
|
||||
// }
|
||||
|
||||
implicit val implCanMoveForGeom = new CanMove[Geometry] {
|
||||
override def getDirection(
|
||||
cam: Camera,
|
||||
cardinalDir: CardinalDirection
|
||||
): ImVector3f = {
|
||||
val camDir =
|
||||
cam.getDirection().immutable * 0.6f
|
||||
val camLeft = cam.getLeft().immutable * 0.4f
|
||||
|
||||
val zero = ImVector3f.ZERO
|
||||
val dir = cardinalDir
|
||||
val walkDir = {
|
||||
val mutWalkDir = new Vector3f()
|
||||
if (dir.left) {
|
||||
// ctx.log.trace("left")
|
||||
mutWalkDir += (zero + camLeft).mutable
|
||||
}
|
||||
if (dir.right) {
|
||||
// ctx.log.trace("right")
|
||||
mutWalkDir += (zero + -camLeft).mutable
|
||||
}
|
||||
if (dir.up) {
|
||||
// ctx.log.trace("up")
|
||||
mutWalkDir += (zero + camDir).mutable
|
||||
}
|
||||
if (dir.down) {
|
||||
// ctx.log.trace("down")
|
||||
mutWalkDir += (zero + -camDir).mutable
|
||||
}
|
||||
mutWalkDir.immutable
|
||||
}
|
||||
walkDir
|
||||
}
|
||||
override def move(geom: Geometry, direction: ImVector3f): Unit = {
|
||||
val v = geom.getLocalTranslation()
|
||||
geom.setLocalTranslation(v += direction.mutable)
|
||||
}
|
||||
}
|
||||
}
|
11
src/main/scala/wow/doge/mygame/math/ImVector3f.scala
Normal file
11
src/main/scala/wow/doge/mygame/math/ImVector3f.scala
Normal file
@ -0,0 +1,11 @@
|
||||
package wow.doge.mygame.math
|
||||
|
||||
case class ImVector3f(x: Float = 0f, y: Float = 0f, z: Float = 0f)
|
||||
|
||||
object ImVector3f {
|
||||
val ZERO = ImVector3f(0, 0, 0)
|
||||
val UNIT_X = ImVector3f(1, 0, 0)
|
||||
val UNIT_Y = ImVector3f(0, 1, 0)
|
||||
val UNIT_Z = ImVector3f(0, 0, 1)
|
||||
|
||||
}
|
157
src/main/scala/wow/doge/mygame/scriptsystem/ScriptActor.scala
Normal file
157
src/main/scala/wow/doge/mygame/scriptsystem/ScriptActor.scala
Normal file
@ -0,0 +1,157 @@
|
||||
package wow.doge.mygame.state
|
||||
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import ammonite.Main
|
||||
import akka.actor.typed.ActorRef
|
||||
import com.jme3.app.state.AppState
|
||||
import ammonite.runtime.Storage.Folder
|
||||
import ammonite.main.Defaults
|
||||
import akka.actor.typed.Behavior
|
||||
import akka.actor.typed.scaladsl.ActorContext
|
||||
import ammonite.util.Res.Success
|
||||
import javax.script.ScriptEngine
|
||||
import javax.script.ScriptEngineManager
|
||||
import scala.util.Try
|
||||
import cats.implicits._
|
||||
|
||||
object ScriptActor {
|
||||
sealed trait Result
|
||||
final case class AppStateResult(state: AppState) extends Result
|
||||
final case class Error(reason: String) extends Result
|
||||
|
||||
sealed trait Command
|
||||
final case class Compile(path: os.Path, sender: ActorRef[Result])
|
||||
extends Command
|
||||
|
||||
final case class CompileAny(
|
||||
path: os.Path,
|
||||
result: ActorRef[Either[Error, Any]]
|
||||
) extends Command
|
||||
|
||||
def defaultScalaRunner() =
|
||||
ammonite
|
||||
.Main(
|
||||
storageBackend = new Folder(
|
||||
// os.pwd / "target"
|
||||
Defaults.ammoniteHome,
|
||||
isRepl = false
|
||||
)
|
||||
)
|
||||
|
||||
def defaultKotlinRunner(): ScriptEngine = {
|
||||
val manager = new ScriptEngineManager()
|
||||
val engine = manager.getEngineByExtension("main.kts")
|
||||
engine
|
||||
}
|
||||
|
||||
def apply(
|
||||
scalaRunner: Main = defaultScalaRunner(),
|
||||
kotlinRunner: ScriptEngine = defaultKotlinRunner()
|
||||
// parent: ActorRef[ScriptStoringActor.Command]
|
||||
): Behavior[ScriptActor.Command] =
|
||||
Behaviors.setup(ctx =>
|
||||
new ScriptActor(scalaRunner, kotlinRunner, ctx).receiveMessage
|
||||
)
|
||||
|
||||
sealed trait ScriptType
|
||||
case object ScalaType extends ScriptType
|
||||
case object KotlinType extends ScriptType
|
||||
case object GroovyType extends ScriptType
|
||||
|
||||
def determineScriptType(path: os.Path): Either[Error, ScriptType] =
|
||||
path.toString match {
|
||||
case s if s.endsWith(".sc") => Right(ScalaType)
|
||||
case s if s.endsWith(".main.kts") => Right(KotlinType)
|
||||
case s if s.endsWith(".groovy") => Right(GroovyType)
|
||||
case _ => Left(Error("Unknown script type"))
|
||||
}
|
||||
|
||||
def runScala(path: os.Path, scalaRunner: ammonite.Main): Either[Error, Any] =
|
||||
scalaRunner
|
||||
.runScript(
|
||||
path,
|
||||
Seq.empty
|
||||
)
|
||||
._1 match {
|
||||
case ammonite.util.Res.Exception(t, msg) => Left(Error(msg))
|
||||
|
||||
case Success(obj) => Right(obj)
|
||||
|
||||
case _ => Left(Error("Failed to run script"))
|
||||
}
|
||||
|
||||
def runKotlin(path: os.Path, kotlinRunner: ScriptEngine): Either[Error, Any] =
|
||||
Try(kotlinRunner.eval(os.read(path))).toEither.leftMap(t =>
|
||||
Error(t.getMessage())
|
||||
)
|
||||
|
||||
def runGroovy(path: os.Path): Either[Error, Any] =
|
||||
Left(Error("Not implemented yet"))
|
||||
|
||||
}
|
||||
|
||||
class ScriptActor(
|
||||
val scalaRunner: Main,
|
||||
val kotlinRunner: ScriptEngine,
|
||||
// parent: ActorRef[ScriptStoringActor.Command],
|
||||
context: ActorContext[ScriptActor.Command]
|
||||
) {
|
||||
import ScriptActor._
|
||||
|
||||
def receiveMessage: Behavior[Command] =
|
||||
Behaviors.receiveMessage { msg =>
|
||||
msg match {
|
||||
case Compile(path, sender) =>
|
||||
context.log.debug(s"Received $path")
|
||||
val res = getScript(path)
|
||||
sender ! res
|
||||
Behaviors.same
|
||||
// case CompileScripts(sender, paths) =>
|
||||
case CompileAny(path, requester) =>
|
||||
context.log.debug(s"Received $path")
|
||||
val res = getScript2(path)
|
||||
context.log.debug(s"result = $res")
|
||||
requester ! res
|
||||
// parent ! ScriptStoringActor.Put(path, res)
|
||||
Behaviors.same
|
||||
}
|
||||
}
|
||||
|
||||
def getScript(path: os.Path) = {
|
||||
val res = determineScriptType(path) match {
|
||||
case Right(ScalaType) => runScala(path, scalaRunner)
|
||||
case Right(KotlinType) => runKotlin(path, kotlinRunner)
|
||||
case Right(GroovyType) => runGroovy(path)
|
||||
case l @ Left(err) => l
|
||||
}
|
||||
|
||||
res match {
|
||||
case Left(err) => err
|
||||
case Right(obj) =>
|
||||
obj match {
|
||||
case s: MyBaseState => AppStateResult(s)
|
||||
case _ => Error("Class in script does not match known types")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def getScript2(path: os.Path): Either[Error, Any] = {
|
||||
val res = determineScriptType(path) match {
|
||||
case Right(ScalaType) => runScala(path, scalaRunner)
|
||||
case Right(KotlinType) => runKotlin(path, kotlinRunner)
|
||||
case Right(GroovyType) => runGroovy(path)
|
||||
case l @ Left(err) => l
|
||||
}
|
||||
|
||||
// res match {
|
||||
// case Left(err) => err
|
||||
// case Right(obj) =>
|
||||
// obj match {
|
||||
// case s: MyBaseState => AppStateResult(s)
|
||||
// case _ => Error("Class in script does not match known types")
|
||||
// }
|
||||
// }
|
||||
res
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,166 @@
|
||||
package wow.doge.mygame.scriptsystem
|
||||
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import akka.actor.typed.scaladsl.PoolRouter
|
||||
import akka.actor.typed.scaladsl.Routers
|
||||
import wow.doge.mygame.state.ScriptActor
|
||||
import akka.actor.typed.ActorRef
|
||||
import akka.actor.typed.scaladsl.ActorContext
|
||||
import akka.actor.typed.Behavior
|
||||
import akka.util.Timeout
|
||||
import scala.util.Success
|
||||
import scala.util.Failure
|
||||
import akka.actor.typed.SupervisorStrategy
|
||||
|
||||
object ScriptCachingActor {
|
||||
|
||||
/**
|
||||
* aka script representation
|
||||
*/
|
||||
type ScriptRepr = Any
|
||||
type ScriptsMap = Map[os.Path, ScriptRepr]
|
||||
type ScriptResult = Either[ScriptActor.Error, ScriptRepr]
|
||||
|
||||
sealed trait Command
|
||||
final case class Get(
|
||||
scriptPath: os.Path,
|
||||
requester: ActorRef[ScriptResult]
|
||||
) extends Command
|
||||
final case class GetMap(requester: ActorRef[ScriptsMap]) extends Command
|
||||
final case class Put(scriptPath: os.Path, script: ScriptRepr) extends Command
|
||||
private final case object NoOp extends Command
|
||||
|
||||
private final case class DelegateToChild(
|
||||
scriptActor: ActorRef[ScriptActor.Command],
|
||||
scriptPath: os.Path,
|
||||
requester: ActorRef[ScriptResult]
|
||||
) extends Command
|
||||
|
||||
final case class Props(
|
||||
ctx: ActorContext[Command],
|
||||
scriptActor: ActorRef[ScriptActor.Command]
|
||||
)
|
||||
final case class State(scriptsMap: ScriptsMap)
|
||||
def apply(state: State = State(Map.empty)): Behavior[Command] =
|
||||
Behaviors.logMessages {
|
||||
Behaviors.setup { ctx =>
|
||||
val pool = ScriptActorPool(4)
|
||||
val scriptsRouter = ctx.spawn(pool, "script-actors-pool")
|
||||
new ScriptCachingActor(Props(ctx, scriptsRouter)).receiveMessage(state)
|
||||
}
|
||||
}
|
||||
|
||||
private def getOrCompileScript(
|
||||
ctx: ActorContext[Command],
|
||||
scriptPath: os.Path,
|
||||
scriptsMap: ScriptsMap,
|
||||
scriptActor: ActorRef[ScriptActor.Command],
|
||||
requester: ActorRef[ScriptResult]
|
||||
) = {
|
||||
scriptsMap
|
||||
.get(scriptPath)
|
||||
.fold {
|
||||
ctx.log.debug("Delegating to child")
|
||||
ctx.self ! DelegateToChild(
|
||||
scriptActor,
|
||||
scriptPath,
|
||||
requester
|
||||
)
|
||||
} { s =>
|
||||
ctx.log.debug("Getting script from cache")
|
||||
requester ! Right(s)
|
||||
}
|
||||
}
|
||||
|
||||
private def askChildForScriptCompilation(
|
||||
ctx: ActorContext[Command],
|
||||
scriptActor: ActorRef[ScriptActor.Command],
|
||||
scriptPath: os.Path,
|
||||
requester: ActorRef[ScriptResult]
|
||||
)(implicit timeout: Timeout) = {
|
||||
ctx.ask(scriptActor, ScriptActor.CompileAny(scriptPath, _)) {
|
||||
case Success(value) =>
|
||||
requester ! value
|
||||
value.fold(
|
||||
err => {
|
||||
ctx.log.error(err.reason)
|
||||
NoOp
|
||||
},
|
||||
res => {
|
||||
Put(scriptPath, res)
|
||||
}
|
||||
)
|
||||
case Failure(exception) => {
|
||||
ctx.log.error(exception.getMessage())
|
||||
NoOp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ScriptCachingActor(props: ScriptCachingActor.Props) {
|
||||
import com.softwaremill.quicklens._
|
||||
import ScriptCachingActor._
|
||||
def receiveMessage(state: State): Behavior[Command] =
|
||||
Behaviors.receiveMessage { msg =>
|
||||
msg match {
|
||||
case Get(scriptPath, requester) =>
|
||||
getOrCompileScript(
|
||||
props.ctx,
|
||||
scriptPath,
|
||||
state.scriptsMap,
|
||||
props.scriptActor,
|
||||
requester
|
||||
)
|
||||
Behaviors.same
|
||||
|
||||
case DelegateToChild(scriptActor, scriptPath, requester) =>
|
||||
import scala.concurrent.duration._
|
||||
implicit val timeout = Timeout(15.seconds)
|
||||
// child ! ScriptActor.CompileAny(scriptPath, requester)
|
||||
askChildForScriptCompilation(
|
||||
props.ctx,
|
||||
scriptActor,
|
||||
scriptPath,
|
||||
requester
|
||||
)
|
||||
Behaviors.same
|
||||
|
||||
case GetMap(requester) =>
|
||||
requester ! state.scriptsMap
|
||||
Behaviors.same
|
||||
|
||||
case Put(scriptPath, script) =>
|
||||
props.ctx.log.debug(s"Putting $script at path $scriptPath")
|
||||
val newState =
|
||||
state.modify(_.scriptsMap).using(_ + (scriptPath -> script))
|
||||
props.ctx.log.debug(newState.toString())
|
||||
receiveMessage(state = newState)
|
||||
|
||||
case NoOp => Behaviors.same
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object ScriptActorPool {
|
||||
def apply(
|
||||
poolSize: Int
|
||||
): PoolRouter[ScriptActor.Command] =
|
||||
Routers.pool(poolSize = poolSize)(
|
||||
// make sure the workers are restarted if they fail
|
||||
Behaviors
|
||||
.supervise(ScriptActor())
|
||||
.onFailure[Exception](SupervisorStrategy.restart)
|
||||
)
|
||||
|
||||
// def apply(
|
||||
// poolSize: Int,
|
||||
// parent: ActorRef[ScriptStoringActor.Command]
|
||||
// ): PoolRouter[ScriptActor.Command] =
|
||||
// Routers.pool(poolSize = poolSize)(
|
||||
// // make sure the workers are restarted if they fail
|
||||
// Behaviors
|
||||
// .supervise(ScriptActor(parent = parent))
|
||||
// .onFailure[Exception](SupervisorStrategy.restart)
|
||||
// )
|
||||
}
|
38
src/main/scala/wow/doge/mygame/state/EntityDataState.java
Normal file
38
src/main/scala/wow/doge/mygame/state/EntityDataState.java
Normal file
@ -0,0 +1,38 @@
|
||||
package wow.doge.mygame.state;
|
||||
|
||||
import com.jme3.app.state.AbstractAppState;
|
||||
import com.simsilica.es.EntityData;
|
||||
import com.simsilica.es.base.DefaultEntityData;
|
||||
import com.jme3.app.state.BaseAppState;
|
||||
import com.jme3.app.Application;
|
||||
|
||||
public class EntityDataState extends BaseAppState {
|
||||
private EntityData entityData;
|
||||
|
||||
public EntityDataState() {
|
||||
this(new DefaultEntityData());
|
||||
}
|
||||
|
||||
public EntityDataState(EntityData ed) {
|
||||
this.entityData = ed;
|
||||
}
|
||||
|
||||
public EntityData getEntityData() {
|
||||
return entityData;
|
||||
}
|
||||
|
||||
public void onEnable() {
|
||||
}
|
||||
|
||||
public void onDisable() {
|
||||
}
|
||||
|
||||
// @Override
|
||||
public void cleanup(Application application) {
|
||||
entityData.close();
|
||||
entityData = null; // cannot be reused
|
||||
}
|
||||
|
||||
public void initialize(Application application) {
|
||||
}
|
||||
}
|
86
src/main/scala/wow/doge/mygame/state/MovementActor.scala
Normal file
86
src/main/scala/wow/doge/mygame/state/MovementActor.scala
Normal file
@ -0,0 +1,86 @@
|
||||
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[T] {
|
||||
def getDirection(cam: Camera, cardinalDir: CardinalDirection): ImVector3f
|
||||
def move(inst: T, 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 => 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 * 2f
|
||||
props.app.enqueue(new Runnable {
|
||||
override def run(): Unit = {
|
||||
cm.move(props.movable, tmp)
|
||||
}
|
||||
})
|
||||
}
|
||||
Behaviors.same
|
||||
// receive(state = state.modify(_.walkDirection).setTo(walkDir))
|
||||
|
||||
}
|
||||
}
|
||||
}
|
63
src/main/scala/wow/doge/mygame/state/MyBaseState.scala
Normal file
63
src/main/scala/wow/doge/mygame/state/MyBaseState.scala
Normal file
@ -0,0 +1,63 @@
|
||||
package wow.doge.mygame.state
|
||||
|
||||
import com.jme3.app.Application
|
||||
import com.jme3.app.SimpleApplication
|
||||
import com.jme3.app.state.AppState
|
||||
import com.jme3.scene.Node
|
||||
import com.jme3.app.state.BaseAppState
|
||||
import com.simsilica.es.EntityData
|
||||
import com.simsilica.es.base.DefaultEntityData
|
||||
|
||||
trait MyBaseState extends BaseAppState {
|
||||
|
||||
var simpleApp: SimpleApplication = null
|
||||
implicit val entityData: EntityData = new DefaultEntityData()
|
||||
def stateManager = simpleApp.getStateManager
|
||||
def guiNode = simpleApp.getGuiNode
|
||||
def rootNode = simpleApp.getRootNode
|
||||
def assetManager = simpleApp.getAssetManager
|
||||
def inputManager = simpleApp.getInputManager
|
||||
def cam = simpleApp.getCamera
|
||||
|
||||
override protected final def initialize(app: Application): Unit = {
|
||||
simpleApp = app.asInstanceOf[SimpleApplication]
|
||||
init()
|
||||
// stateManager.getState(classOf[FlyCamAppState]).getCamera().setMoveSpeed(100)
|
||||
}
|
||||
|
||||
protected def init(): Unit
|
||||
|
||||
override protected def cleanup(app: Application): Unit = {
|
||||
entityData.close()
|
||||
}
|
||||
|
||||
protected def getOrCreateNode(parent: Node, id: String) =
|
||||
Option(parent.getChild(id)).fold {
|
||||
val node = new Node(id)
|
||||
parent.attachChild(node)
|
||||
node
|
||||
}(node => node.asInstanceOf[Node])
|
||||
|
||||
protected def enableStates(classes: Class[_ <: AppState]*) =
|
||||
setEnabledToStates(true, classes: _*)
|
||||
protected def disableStates(classes: Class[_ <: AppState]*) =
|
||||
setEnabledToStates(false, classes: _*)
|
||||
|
||||
protected def setEnabledToStates(
|
||||
enabled: Boolean,
|
||||
classes: Class[_ <: AppState]*
|
||||
) = {
|
||||
for (clazz <- classes) {
|
||||
val st = stateManager.getState(clazz)
|
||||
if (st != null) st.setEnabled(enabled)
|
||||
}
|
||||
}
|
||||
|
||||
protected def removeStates(classes: Class[_ <: AppState]*) = {
|
||||
for (clazz <- classes) {
|
||||
val st = stateManager.getState(clazz)
|
||||
if (st != null) stateManager.detach(st)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
286
src/main/scala/wow/doge/mygame/state/PlayerMovementState2.scala
Normal file
286
src/main/scala/wow/doge/mygame/state/PlayerMovementState2.scala
Normal file
@ -0,0 +1,286 @@
|
||||
package wow.doge.mygame.state
|
||||
|
||||
|
||||
import scala.concurrent.duration.DurationInt
|
||||
|
||||
import com.jme3.input.InputManager
|
||||
import com.jme3.input.KeyInput
|
||||
import com.jme3.input.controls.ActionListener
|
||||
import com.jme3.input.controls.KeyTrigger
|
||||
import com.jme3.math.Vector3f
|
||||
|
||||
import akka.actor.typed.scaladsl.ActorContext
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import akka.actor.typed.Behavior
|
||||
import akka.actor.typed.ActorRef
|
||||
import com.jme3.scene.Geometry
|
||||
import akka.actor.typed.scaladsl.TimerScheduler
|
||||
|
||||
import wow.doge.mygame.implicits._
|
||||
|
||||
class PlayerMovementState2(
|
||||
movementActor: ActorRef[MovementActor.Command],
|
||||
movementActorTimer: ActorRef[MovementActorTimer.Command],
|
||||
imMovementActor: ActorRef[ImMovementActor.Command],
|
||||
geom: Geometry
|
||||
) extends MyBaseState
|
||||
with ActionListener {
|
||||
|
||||
protected lazy val mat = MyMaterial(
|
||||
assetManager = assetManager,
|
||||
path = "Common/MatDefs/Misc/Unshaded.j3md"
|
||||
)
|
||||
|
||||
override protected[state] def onEnable(): Unit = {}
|
||||
|
||||
override protected[state] def onDisable(): Unit = {}
|
||||
|
||||
override protected def init(): Unit = {
|
||||
|
||||
setupKeys(inputManager)
|
||||
geom.setMaterial(mat)
|
||||
rootNode.attachChild(geom)
|
||||
// movementActorTimer ! MovementActorTimer.Start(geom, cam)
|
||||
// movementActorTimer ! MovementActorTimer.Start
|
||||
}
|
||||
|
||||
// def system =
|
||||
// simpleApp.getStateManager.getState(classOf[ActorSystemState]).system
|
||||
// def player = system.actorSelection("/user/player") //.resolveOne(1.second)
|
||||
|
||||
var lastDir: Vector3f = Vector3f.UNIT_X
|
||||
|
||||
override def update(tpf: Float) = {
|
||||
// val direction = new Vector3f()
|
||||
// direction.multLocal(10 * tpf)
|
||||
// if (direction.length() > 0f) {
|
||||
// // player ! PlayerMove(direction)
|
||||
// lastDir = direction.normalize
|
||||
// }
|
||||
// if (shoot) {
|
||||
// shoot = false
|
||||
// // player ! Shoot(lastDir)
|
||||
// }
|
||||
// movementActor ! MovementActor.Tick(tpf, geom, cam)
|
||||
imMovementActor ! ImMovementActor.Tick(tpf)
|
||||
// movementActorTimer ! MovementActorTimer.Update(tpf)
|
||||
}
|
||||
|
||||
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)
|
||||
)
|
||||
inputManager.addMapping(
|
||||
"Up",
|
||||
// new KeyTrigger(KeyInput.KEY_W),
|
||||
new KeyTrigger(KeyInput.KEY_UP)
|
||||
)
|
||||
inputManager.addMapping(
|
||||
"Down",
|
||||
// new KeyTrigger(KeyInput.KEY_S),
|
||||
new KeyTrigger(KeyInput.KEY_DOWN)
|
||||
)
|
||||
inputManager.addMapping(
|
||||
"Space",
|
||||
new KeyTrigger(KeyInput.KEY_SPACE),
|
||||
new KeyTrigger(KeyInput.KEY_H)
|
||||
)
|
||||
inputManager.addMapping(
|
||||
"Reset",
|
||||
new KeyTrigger(KeyInput.KEY_R),
|
||||
new KeyTrigger(KeyInput.KEY_RETURN)
|
||||
)
|
||||
inputManager
|
||||
.withListener(this, "Left")
|
||||
.withListener(this, "Right")
|
||||
inputManager.addListener(this, "Up")
|
||||
inputManager.addListener(this, "Down")
|
||||
inputManager.addListener(this, "Space")
|
||||
inputManager.addListener(this, "Reset")
|
||||
}
|
||||
|
||||
def onAction(binding: String, value: Boolean, tpf: Float) =
|
||||
binding match {
|
||||
case "Left" => imMovementActor ! ImMovementActor.MovedLeft(value)
|
||||
case "Right" => imMovementActor ! ImMovementActor.MovedRight(value)
|
||||
case "Up" => imMovementActor ! ImMovementActor.MovedUp(value)
|
||||
case "Down" => imMovementActor ! ImMovementActor.MovedDown(value)
|
||||
case "Space" =>
|
||||
case _ =>
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final case class CardinalDirection(
|
||||
left: Boolean = false,
|
||||
right: Boolean = false,
|
||||
up: Boolean = false,
|
||||
down: Boolean = false
|
||||
)
|
||||
|
||||
object MovementActor {
|
||||
sealed trait Command
|
||||
// final case class Tick(tpf: Float, geom: Geometry, cam: Camera) 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
|
||||
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(app: com.jme3.app.Application, geom: Geometry)
|
||||
|
||||
/**
|
||||
* Internal state of the actor
|
||||
*
|
||||
* @param cardinalDir Immutable, can be shared as is
|
||||
* @param walkDirection scratch space to avoid allocations on every tick. Do not share outside the actor
|
||||
*/
|
||||
final case class State(
|
||||
cardinalDir: CardinalDirection = CardinalDirection(),
|
||||
walkDirection: Vector3f = Vector3f.UNIT_X
|
||||
)
|
||||
|
||||
def apply(props: Props): Behavior[Command] =
|
||||
Behaviors.setup(ctx => new MovementActor(ctx, props).receive(State()))
|
||||
|
||||
}
|
||||
class MovementActor(
|
||||
ctx: ActorContext[MovementActor.Command],
|
||||
props: MovementActor.Props
|
||||
) {
|
||||
import MovementActor._
|
||||
import com.softwaremill.quicklens._
|
||||
def receive(state: MovementActor.State): 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 =>
|
||||
val camDir =
|
||||
props.app.getCamera.getDirection().clone().multLocal(0.6f)
|
||||
val camLeft = props.app.getCamera.getLeft().clone().multLocal(0.4f)
|
||||
val walkDir = state.walkDirection.set(0, 0, 0)
|
||||
// val walkDir = new Vector3f
|
||||
val dir = state.cardinalDir
|
||||
if (dir.up) {
|
||||
ctx.log.debug("up")
|
||||
// ctx.log.debug(Thread.currentThread().getName())
|
||||
// walkDir.addLocal(0, 0, -1)
|
||||
walkDir += camDir
|
||||
}
|
||||
if (dir.left) {
|
||||
ctx.log.debug("left")
|
||||
// walkDir.addLocal(-1, 0, 0)
|
||||
walkDir.addLocal(camLeft)
|
||||
}
|
||||
if (dir.right) {
|
||||
ctx.log.debug("right")
|
||||
// walkDir.addLocal(1, 0, 0)
|
||||
walkDir.addLocal(camLeft.negateLocal())
|
||||
}
|
||||
if (dir.down) {
|
||||
ctx.log.debug("down")
|
||||
walkDir.addLocal(camDir.negateLocal())
|
||||
// walkDir.addLocal(0, 0, 1)
|
||||
}
|
||||
// (dir.up, dir.down, dir.left, dir.right) match {
|
||||
// case (true, false, true, false) =>
|
||||
// case _ =>
|
||||
// }
|
||||
|
||||
walkDir.multLocal(2f)
|
||||
|
||||
// walkDir.multLocal(100f)
|
||||
// .multLocal(tpf)
|
||||
|
||||
// val v = props.geom.getLocalTranslation()
|
||||
// props.geom.setLocalTranslation(
|
||||
// (v += walkDir)
|
||||
// )
|
||||
props.app.enqueue(new Runnable {
|
||||
override def run(): Unit = {
|
||||
// geom.setLocalTranslation(walkDir)
|
||||
|
||||
val v = props.geom.getLocalTranslation()
|
||||
props.geom.setLocalTranslation(
|
||||
(v += walkDir)
|
||||
)
|
||||
}
|
||||
})
|
||||
Behaviors.same
|
||||
// receive(state = state.modify(_.walkDirection).setTo(walkDir))
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object MovementActorTimer {
|
||||
sealed trait Command
|
||||
final case object Start extends Command
|
||||
final case object Update extends Command
|
||||
private case object Send extends Command
|
||||
case object TimerKey
|
||||
|
||||
final case class Props(
|
||||
timers: TimerScheduler[MovementActorTimer.Command],
|
||||
target: ActorRef[MovementActor.Command]
|
||||
)
|
||||
final case class State()
|
||||
def apply(target: ActorRef[MovementActor.Command]) =
|
||||
Behaviors.withTimers[Command] { timers =>
|
||||
new MovementActorTimer(Props(timers, target)).idle()
|
||||
}
|
||||
}
|
||||
class MovementActorTimer(
|
||||
props: MovementActorTimer.Props
|
||||
) {
|
||||
import MovementActorTimer._
|
||||
// import com.softwaremill.quicklens._
|
||||
|
||||
def idle(): Behavior[Command] =
|
||||
Behaviors.receiveMessage { msg =>
|
||||
msg match {
|
||||
case Start =>
|
||||
props.timers.startTimerWithFixedDelay(
|
||||
Send,
|
||||
10.millis
|
||||
)
|
||||
active()
|
||||
case _ => Behaviors.unhandled
|
||||
}
|
||||
}
|
||||
|
||||
def active(): Behavior[Command] =
|
||||
Behaviors.receiveMessage { msg =>
|
||||
msg match {
|
||||
case Update => active()
|
||||
case Send =>
|
||||
props.target ! MovementActor.Tick
|
||||
Behaviors.same
|
||||
case _ => Behaviors.unhandled
|
||||
|
||||
}
|
||||
}
|
||||
}
|
206
src/main/scala/wow/doge/mygame/state/ScriptingEngineState.scala
Normal file
206
src/main/scala/wow/doge/mygame/state/ScriptingEngineState.scala
Normal file
@ -0,0 +1,206 @@
|
||||
package wow.doge.mygame.state
|
||||
|
||||
import ammonite.runtime.Storage.Folder
|
||||
import ammonite.main.Defaults
|
||||
import ammonite.Main
|
||||
import javax.script.ScriptEngine
|
||||
import com.jme3.app.Application
|
||||
import com.jme3.app.state.AppState
|
||||
import akka.actor.typed.scaladsl.AbstractBehavior
|
||||
import akka.actor.typed.scaladsl.ActorContext
|
||||
import akka.actor.typed.Behavior
|
||||
import akka.actor.typed.ActorRef
|
||||
import ammonite.util.Res.Success
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import akka.actor.typed.SpawnProtocol
|
||||
|
||||
class ScriptingEngineState(
|
||||
sse: ScalaScriptingEngine,
|
||||
kse: KotlinScriptingEngine
|
||||
) extends MyBaseState {
|
||||
|
||||
// implicit val actorSystem =
|
||||
// ActorSystem.create(MyActorSystem(), "rootActor")
|
||||
// implicit val timeout: Timeout = Timeout(3.seconds)
|
||||
// val scalaScriptActor: Future[ActorRef[ScalaScriptBehavior.Command]] =
|
||||
// actorSystem.ask(
|
||||
// SpawnProtocol.Spawn(
|
||||
// ScalaScriptBehavior(sse.runner),
|
||||
// name = "ScalaScriptCompilerActor",
|
||||
// Props.empty,
|
||||
// _
|
||||
// )
|
||||
// )
|
||||
|
||||
override protected def cleanup(app: Application): Unit = {
|
||||
// actorSystem.terminate()
|
||||
}
|
||||
|
||||
// override protected def initialize(app: Application): Unit = {
|
||||
// super.initialize(app)
|
||||
|
||||
// }
|
||||
override def init() = {
|
||||
// Future {
|
||||
// while (true) {
|
||||
// // super.update(tpf)
|
||||
// val (res, k) = sse.runner.runScript(
|
||||
// // os.Path(getClass().getResource("/hello.sc").getPath),
|
||||
// os.pwd / "src" / "main" / "resources" / "hello.sc",
|
||||
// Seq.empty
|
||||
// // Seq(("start", None))
|
||||
// // Scripts.groupArgs(List(""))
|
||||
// )
|
||||
// val ms = res.map(_.asInstanceOf[GameScript])
|
||||
// ms.map(_.start())
|
||||
|
||||
// val res2 = kse.engine.eval(
|
||||
// os.read(os.pwd / "src" / "main" / "resources" / "hello.main.kts")
|
||||
// )
|
||||
// // val res2 = engine.eval(getClass().getResource("/hello.main.kts").getPath)
|
||||
// // val invoker = engine.asInstanceOf[Invocable]
|
||||
// // val scr = invoker.getInterface(res2, classOf[GameScript])
|
||||
// val scr = res2.asInstanceOf[GameScript]
|
||||
// scr.start()
|
||||
// Thread.sleep(2000)
|
||||
// }
|
||||
// }
|
||||
// Future {
|
||||
// sse.runner
|
||||
// .runScript(
|
||||
// os.pwd / "src" / "main" / "resources" / "hello2.sc",
|
||||
// Seq.empty
|
||||
// )
|
||||
// ._1
|
||||
// .map(_.asInstanceOf[MyBaseState])
|
||||
// .map(s => stateManager.attach(s))
|
||||
|
||||
// ()
|
||||
|
||||
// }
|
||||
|
||||
// val res = scalaScriptActor
|
||||
// .map(
|
||||
// _.ask(ref =>
|
||||
// ScalaScriptBehavior.Compile(
|
||||
// ref,
|
||||
// os.pwd / "src" / "main" / "resources" / "hello2.sc"
|
||||
// // os.Path(getClass().getResource("/hello2.sc").getPath)
|
||||
// )
|
||||
// )(Timeout(10.seconds), actorSystem.scheduler)
|
||||
// )
|
||||
// .flatten
|
||||
|
||||
// res.foreach(_ match {
|
||||
// case AppStateResult(state) => {
|
||||
// stateManager.attach(state)
|
||||
// }
|
||||
// case wow.doge.mygame.state.ScalaScriptBehavior.Error(reason) =>
|
||||
// println("error")
|
||||
// })
|
||||
|
||||
}
|
||||
|
||||
override def update(tpf: Float): Unit = {}
|
||||
|
||||
override protected def onEnable(): Unit = {}
|
||||
|
||||
override protected def onDisable(): Unit = {}
|
||||
|
||||
}
|
||||
|
||||
object MyActorSystem {
|
||||
def apply(): Behavior[SpawnProtocol.Command] =
|
||||
Behaviors.setup { context =>
|
||||
// Start initial tasks
|
||||
// context.spawn(...)
|
||||
|
||||
SpawnProtocol()
|
||||
}
|
||||
}
|
||||
class ScalaScriptingEngine(
|
||||
val runner: Main = ammonite
|
||||
.Main(
|
||||
// predefCode = """
|
||||
// import coursierapi.MavenRepository
|
||||
|
||||
// interp.repositories.update(
|
||||
// interp.repositories() ::: List(
|
||||
// MavenRepository.of("file://home/rohan/.m2/repository")
|
||||
// )
|
||||
// )
|
||||
|
||||
// @
|
||||
|
||||
// """,
|
||||
defaultPredef = false,
|
||||
storageBackend = new Folder(Defaults.ammoniteHome, isRepl = false)
|
||||
)
|
||||
) {}
|
||||
|
||||
class KotlinScriptingEngine(val engine: ScriptEngine) {
|
||||
// val manager = new ScriptEngineManager()
|
||||
// val engine = manager.getEngineByExtension("main.kts")
|
||||
}
|
||||
|
||||
object ScalaScriptBehavior {
|
||||
sealed trait Result
|
||||
final case class AppStateResult(state: AppState) extends Result
|
||||
final case class Error(reason: String) extends Result
|
||||
|
||||
sealed trait Command
|
||||
final case class Compile(sender: ActorRef[Result], path: os.Path)
|
||||
extends Command
|
||||
// final case class CompileScripts(sender: ActorRef[Result], paths: os.Path*)
|
||||
// extends Command
|
||||
def apply(
|
||||
runner: Main = ammonite
|
||||
.Main(
|
||||
storageBackend = new Folder(
|
||||
// os.pwd / "target"
|
||||
Defaults.ammoniteHome,
|
||||
isRepl = false
|
||||
)
|
||||
)
|
||||
) =
|
||||
Behaviors.setup(ctx => new ScalaScriptActor(runner, ctx))
|
||||
private class ScalaScriptActor(
|
||||
val runner: Main,
|
||||
context: ActorContext[Command]
|
||||
) extends AbstractBehavior[Command](context) {
|
||||
|
||||
override def onMessage(msg: Command): Behavior[Command] = {
|
||||
msg match {
|
||||
case Compile(sender, path) =>
|
||||
context.log.debug(s"Received $path")
|
||||
val res = getScript(path)
|
||||
println(res)
|
||||
sender ! res
|
||||
Behaviors.same
|
||||
// case CompileScripts(sender, paths) =>
|
||||
}
|
||||
}
|
||||
|
||||
def getScript(path: os.Path): Result = {
|
||||
runner
|
||||
.runScript(
|
||||
path,
|
||||
Seq.empty
|
||||
)
|
||||
._1 match {
|
||||
case ammonite.util.Res.Exception(t, msg) => Error(msg)
|
||||
|
||||
case Success(obj) =>
|
||||
obj match {
|
||||
case s: MyBaseState => AppStateResult(s)
|
||||
case _ => Error("Unknown script type")
|
||||
// AppStateResult(s.asInstanceOf[AppState])
|
||||
}
|
||||
|
||||
case _ => Error("Failed to run script")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
71
src/main/scala/wow/doge/mygame/state/TestAppState.scala
Normal file
71
src/main/scala/wow/doge/mygame/state/TestAppState.scala
Normal file
@ -0,0 +1,71 @@
|
||||
package wow.doge.mygame.state
|
||||
|
||||
import com.jme3.app.Application
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.components.TestComponent
|
||||
import com.jme3.scene.shape.Box
|
||||
import com.jme3.scene.Geometry
|
||||
import com.jme3.material.Material
|
||||
import com.jme3.math.ColorRGBA
|
||||
import com.jme3.asset.AssetManager
|
||||
import com.jme3.math.Vector3f
|
||||
class TestAppState(
|
||||
// private var _entity: Option[EntityData] = Some(new DefaultEntityData())
|
||||
) extends MyBaseState {
|
||||
var geom: Option[Geometry] = None
|
||||
|
||||
// def entity = _entity
|
||||
// override def initialize(app: Application): Unit = {
|
||||
// super.initialize(app)
|
||||
|
||||
// }
|
||||
|
||||
override def init() = {
|
||||
entityData
|
||||
.createEntity()
|
||||
.withComponents(TestComponent())
|
||||
// entityData.setComponents(x, TestComponent())
|
||||
val es = entityData.getEntities(classOf[TestComponent])
|
||||
println(es)
|
||||
val b = new Box(1, 1, 1)
|
||||
geom = Some(new Geometry("Box", b))
|
||||
|
||||
val mat = MyMaterial(
|
||||
assetManager = assetManager,
|
||||
path = "Common/MatDefs/Misc/Unshaded.j3md"
|
||||
)
|
||||
geom.foreach(e => {
|
||||
e.setMaterial(mat)
|
||||
rootNode.attachChild(e)
|
||||
})
|
||||
}
|
||||
|
||||
override def update(tpf: Float) = {
|
||||
geom.foreach(_.rotate(0, 0.5f * tpf, 0))
|
||||
geom.foreach(_.move(new Vector3f(0, 1 * tpf, 0)))
|
||||
}
|
||||
|
||||
override def cleanup(app: Application): Unit = {
|
||||
// _entity.map(_.close())
|
||||
// _entity = None
|
||||
}
|
||||
|
||||
override def onEnable(): Unit = {}
|
||||
|
||||
override def onDisable(): Unit = {}
|
||||
|
||||
}
|
||||
|
||||
object MyMaterial {
|
||||
def apply(
|
||||
color: String = "Color",
|
||||
colorType: com.jme3.math.ColorRGBA = ColorRGBA.Blue,
|
||||
assetManager: AssetManager,
|
||||
path: String
|
||||
): Material = {
|
||||
val mat =
|
||||
new Material(assetManager, path)
|
||||
mat.setColor(color, colorType)
|
||||
mat
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user