Friday, May 27, 2016

Using an ECS with Scala.JS

I've created an implementation of Entitas ECS in Scala. The source is over on github: https://github.com/darkoverlordofdata/entitas-scala.

I think the big drawback to using an entity component system is that it takes a lot of boilerplate to get a project up and running. So, I also have a command line tool to help me with that, entitas-cli. Entitas-cli requires nodejs, and uses Liquid templates to generate code from a json configuration file.

To install entitas-cli: 'npm install -g entitas-cli'

I will need an existing project, so using the tutorial at https://www.scala-js.org/tutorial/basic/, I've created a basic HelloWorld application.  I only followed the first three steps, and then I add my dependencies, so now my build.sbt looks like this:

scalaJSUseRhino in Global := false
enablePlugins(ScalaJSPlugin)
name := "Scala.js Tutorial"
scalaVersion := "2.11.7" // or any other Scala version >= 2.10.2
libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "0.9.0"

libraryDependencies += "com.darkoverlordofdata" %%% "entitas" % "0.0.0-SNAPSHOT"
resolvers += "Artifactory" at "https://oss.jfrog.org/artifactory/oss-snapshot-local/"
libraryDependencies += "co.technius.scalajs-pixi" %%% "core" % "0.0.1-SNAPSHOT"
jsDependencies += "org.webjars" % "pixi.js" % "3.0.7" / "pixi.js"
jsDependencies += RuntimeDOM
skip in packageJSDependencies := false
persistLauncher in Compile := true

I'll have to reload my sbt configuration. So while it's doing that, I open another terminal window in the project root, and type the following commands :

entitas init helloworld 
entitas create -c Destroy
entitas create -c Player 
entitas create -c Position x:Float y:Float
entitas create -c Velocity x:Float y:Float
entitas create -c View sprite:co.technius.scalajs.pixi.Sprite
entitas create -s Destroy IExecuteSystem
entitas create -s Physics IExecuteSystem
entitas create -s Render IInitializeSystem IExecuteSystem

Now I have an ./entitas.json file in my project root.


    {
      "namespace": "helloworld",
      "src": "lib/src",
      "output": {
        "javascript": "web/src/helloworld/generatedExtensions.js",
        "typescript": "lib/src/generatedComponents.ts",
        "declaration": "lib/ext/helloworld.d.ts"
      },
    ...}

These defaults are for a typescript project, so I make the following changes for scala:

      "namespace": "tutorial.webapp",
      "src": "src/main/scala",
      "output": {
        "generated": "components.scala"
      },

and generate the output:

entitas generate -p scala mutable=var

This creates all of my components and system classes. Components are regenerated every time I run the tool. Systems are not overwritten.

I also need some assets.  I've downloaded Kenney's SpaceShooterRedux package from http://opengameart.org/content/space-shooter-redux

The main TutorialApp class should load the assets and start the game loop:

package tutorial.webapp

import co.technius.scalajs.pixi.Container
import co.technius.scalajs.pixi.Pixi
import co.technius.scalajs.pixi.loaders.{ResourceDictionary, Loader}
import com.darkoverlordofdata.entitas.{Systems, Pool}
import tutorial.webapp.systems._
import org.scalajs.dom.document
import org.scalajs.dom.window
import scala.scalajs.js.JSApp

object TutorialApp extends JSApp {
  lazy val width = window.innerWidth
  lazy val height = window.innerHeight
  lazy val renderer = Pixi.autoDetectRenderer(width, height)
  lazy val stage = new Container

  lazy val pool: Pool = { new Pool(Component.TotalComponents.id) }
  lazy val systems: Systems = { new Systems() }

  def main(): Unit = {
    Pixi.loader
      .add("black",   "images/black.png")
      .add("ship",    "images/ship.png")
      .load(loaded)
    document.body.appendChild(renderer.view)
    window.requestAnimationFrame(render)
  }

  lazy val loaded = (loader:Loader, res:ResourceDictionary) => {
    systems
      .add(pool.createSystem(new RenderSystem(pool)))
      .add(pool.createSystem(new PhysicsSystem(pool)))
      .add(pool.createSystem(new DestroySystem(pool)))
    systems.initialize()
  }

  lazy val render: (Double) => Unit = { time =>
    systems.execute()
    window.requestAnimationFrame(render)
  }

}


Now I just need to fill in the systems and create entities. The full version is here
https://github.com/darkoverlordofdata/invaders-scala-js

Try it  live here, at https://darkoverlordofdata.com/invaders-scala-js. Be kind, it is just started.


Wednesday, May 4, 2016

You Will Test the Way We Want You to Test

That sounds like a line from Hogan's Heroes. But really it's JUinit. I can't believe the lack of alternatives in the java-sphere. Actually, this extends to Kotlin, too. I've implemented the Entitas api in Kotlin, and it needs to be tested. And all I get is JUint? Actually, there is Spek, but it requires JUnit...

I suppose that would be OK if I were only calling my Kotlin code from Java. But I'm not, I'm calling from Kotlin, and JUnit can't test idomatic usage. Plus, btw - have I mentioned lately that I hate java? That's why I'm using Kotlin.

So far, Kotlin's been doing a good job of insulating me from java, and I'd like to keep it that way. So I've written a unit test framework before - I can write one in Kotlin. In fact, I just did, it doesn't take long.
~ here is an example of usage:

fun main(args: Array<String>) {
    var k1 = 0
    var k2 = 0
    var k3 = 0

    TestIt("this is the test suite")
    .beforeEach {
        k1 += 1
    }.afterEach {
        k2 += 1
    }.onDone {
        k3 = k1 + k2
    }.run {

        it("this is the test") {
            it.equals(1, 1)
        }

        it("this is another test") {
            it.equals("fred", 1)
        }

    }

}

And, here is the TestIt code, the thing about kotlin is that it's so easy and succinct. The entire framework fits in on file, and is about 80 lines of code.


class TestIt(name:String) {

    private var name = name
    private var beforeProc = {}
    private var afterProc = {}
    private var doneProc = {}
    private val tests:MutableList = mutableListOf()

    data class Test(val name:String, val proc:(assert) -> Unit) {}

    object assert {
        var result = false
        var actual:Any? = null
        var expected:Any? = null
        fun equals(actual:Any, expected:Any):Boolean {
            if (actual.equals(expected)) {
                this.result = true
            } else {
                this.result = false
                this.actual = actual
                this.expected = expected
            }
            return this.result
        }
    }

    fun beforeEach(proc:() -> Unit):TestIt {
        beforeProc = proc
        return this
    }

    fun afterEach(proc:() -> Unit):TestIt {
        afterProc = proc
        return this
    }

    fun onDone(proc:() -> Unit):TestIt {
        doneProc = proc
        return this
    }

    fun run(main:(func:(name:String, proc:(assert) -> Unit) -> Unit) -> Unit) {

        val fail = "\u001b[31m" /* VT100 RED */
        val pass = "\u001b[32m" /* VT100 GREEN */
        val reset = "\u001b[0m" /* VT100 RESET */
        var passed = 0
        var failed = 0

        main {name:String, proc:(assert) -> Unit ->
            val ignore  = tests.add(Test(name, proc))
        }

        println("\t$name\n---------------------------------")
        for (test in tests) {
            assert.result = true
            beforeProc()
            test.proc(assert)
            afterProc()
            if (assert.result) {
                passed++
                println("${pass}PASS${reset} <=> ${test.name}")
            } else {
                failed++
                println("${fail}FAIL${reset} <=> ${test.name}")
                println("     expected ${fail}[${reset}${assert.expected}${fail}]${reset}")
                println("       actual ${fail}[${reset}${assert.actual}${fail}]${reset}")
            }
        }
        doneProc()
        println("---------------------------------")
        println("    <====> Pass: $passed")
        println("    <====> Fail: $failed\n\n")
    }
}