Hello Guest

Scala and Sbt getting started.

  • 8 Replies
  • 33551 Views
*

Offline Krux

  • *
  • 24
Scala and Sbt getting started.
« on: August 03, 2010, 15:45:32 »
You might know that Java is not the only programming language on the jvm. Scala is there too. The language is great, and performance is exactly as good as Java. But there is absolutely no documentation on how to create a game in Scala out there in the Internet, so I am going to change this.

We build our project with Simple Built Tool. It is a command line based tool, optimized to built Scala projects. But it can also build mixed Scala/Java projects and Java projects. you get it here. There you can also find how you have to install it depending on your operating system. I use SBT instead of an IDE. You can integrate SBT with e.g. IntelliJ, but I can't tell you how good this works.

As soon as you can launch sbt from the command line, create a new folder and name it something like myFirstSbtSclaLwjglProject, open a terminal and navigate into that location and run sbt here.

now you will be asked if you want to create a new project, type y and hit enter. now you have to enter some project specific details, but it doesn't really matter what you type here. just type something.

the lib folder is for all dependent libraries. Sbt searches in all sub folders, so we can simply copy our LWJGL installation here.
in src/main/scala/ we will have all our Scala source code. create a new file main.scala and write the following in it.

Code: [Select]
import org.lwjgl._
import opengl.{Display,GL11,DisplayMode}
import GL11._
import input._
import math._

object Main{
val GAME_TITLE = "My Game"
val FRAMERATE = 60
val width = 640
val height = 480

val player = new Player(0,0,0);

var finished = false
var angle = 0.0f
var rotation = 0.0f

def main(args:Array[String]){
var fullscreen = false
for(arg <- args){
arg match{
case "-fullscreen" =>
fullscreen = true
}
}

init(fullscreen)
run
}

def init(fullscreen:Boolean){

println("init Display")
Display.setTitle(GAME_TITLE)
Display.setFullscreen(fullscreen)
Display.setVSyncEnabled(true)
Display.setDisplayMode(new DisplayMode(width,height))
Display.create

println("init gl")
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0)
adjustcam
}

def adjustcam(){
val v = Display.getDisplayMode.getWidth.toFloat/Display.getDisplayMode.getHeight.toFloat
printf("v:%f",v)
glMatrixMode(GL_PROJECTION)
glLoadIdentity
glFrustum(-v,v,-1,1,1,100)
glMatrixMode(GL_MODELVIEW)
}

def cleanup(){
Display.destroy
}

def run(){
while(!finished){
Display.update

logic
render

Display.sync(FRAMERATE)
}
}

def logic(){
   // in scala we can locally import all methods from Keyboard.
   import Keyboard._
   
if(isKeyDown(KEY_ESCAPE))
finished = true
if(Display.isCloseRequested)
finished = true

// rx and rx store our keyboard input as direction  
var ry = 0
var rx = 0

// keys are IKJL for up down left right

if(isKeyDown(KEY_I))
ry += 1
if(isKeyDown(KEY_K))
ry -= 1
if(isKeyDown(KEY_J))
rx -= 1
if(isKeyDown(KEY_L))
rx += 1

// this makes the direction relative to the camera position
// it is a simple rotation matrix you may know from linear algebra
val ax = rx*cos(-rotation.toRadians)-ry*sin(-rotation.toRadians)
val ay = rx*sin(-rotation.toRadians)+ry*cos(-rotation.toRadians)

player.x += 0.1f*ax.toFloat
player.y += 0.1f*ay.toFloat

// this rotates our camera around the center
angle += 2.0f % 360
rotation += 0.2f
}

def renderGrid(size : Int){
   // this creates the nice looking background.
glDisable(GL_LIGHTING)
glBegin(GL_LINES)
for(i <- -size to size){
glVertex2i(i,-size)
glVertex2i(i, size)
glVertex2i(-size,i)
glVertex2i( size,i)
}
glEnd
glEnable(GL_LIGHTING)
}

def render(){
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity

glTranslatef(0,0,-20)
glRotatef(-70,1,0,0)
glRotatef(rotation,0,0,1)

glPushMatrix
player.applyPos
//rotate the player just for fun
glRotatef(angle, 0, 0, 1.0f)
player.draw
glPopMatrix

//without background, motion is not visible
// a green grid is nice and retro
glColor3f(0,1,0)
renderGrid(10000)
}
}

class Player(nx:Float,ny:Float,nz:Float){
var x:Float = nx
var y:Float = ny
var z:Float = nz

def applyPos {
   glTranslatef(x,y,z)
}

def draw = {
glColor3f(1, 0, 0)
glBegin(GL_TRIANGLE_FAN)
glNormal3d( 0, 0, 1); glVertex3d(0,0,0.5)
glNormal3d(-1,-1, 1); glVertex2d(-0.5, -0.5)
glNormal3d( 1,-1, 1); glVertex2d(0.5, -0.5)
glNormal3d( 1, 1, 1); glVertex2d(0.5, 0.5)
glNormal3d(-1, 1, 1); glVertex2d(-0.5, 0.5)
glNormal3d(-1,-1, 1); glVertex2d(-0.5, -0.5)
glEnd
}
}

now back to our sbt command line. write "compile". and you should see, that sbt compiles everything it it should do, but as soon as you type run, your program crashes with a class not found exception. This is because sbt did not find the LWJGL native libraries. To correct this problem you have to modify sbt settings. i did it with the following file in project/build. You can name it however you want to.

Code: [Select]
import sbt._
import java.io.File

// simple build tool does not find the so libraries automatically,
// so we need to import them manually

class LWJGLProject(info: ProjectInfo) extends DefaultProject(info) {
    // to specify new runJVMOptions we need to fork the execution    
    override def fork = Some(new ForkScalaRun {
        val (os, separator) = System.getProperty("os.name").split(" ")(0).toLowerCase match {
            case "linux" => "linux" -> ":"
            case "mac" => "macosx" -> ":"
            case "windows" => "windows" -> ";"
            case "sunos" => "solaris" -> ":"
            case x => x -> ":"
        }
       
        override def runJVMOptions = super.runJVMOptions ++ Seq("-Djava.library.path=" + System.getProperty("java.library.path") + separator + ("lib" / "native" / os))
       
        override def scalaJars = Seq(buildLibraryJar.asFile, buildCompilerJar.asFile)
    })
}

now run should work. I everything worked you will see something like this:



or you can just download my project file, but you still have to download and install sbt before you can start with it. My project file will be big, because everything LWJGL Scala-compiler and Scala-library is in there, too.
sbttest_tar.gz (29 MB)

PS:
Please tell me how this tutorial works.
if you like it, I might create more tutorials in the future. All Scala based, but maybe more specific in topics about game design

Edit:
thanks to darkfrog for improvements.
« Last Edit: September 23, 2010, 10:30:08 by Krux »

Re: Scala and Sbt getting started.
« Reply #1 on: August 16, 2010, 19:26:46 »
Really good tutorial but did you use some IDE ? I cant figure out the compile setup with intellij. Did you got the sbt and IDE working ?

*

Offline Krux

  • *
  • 24
Re: Scala and Sbt getting started.
« Reply #2 on: August 17, 2010, 20:23:39 »
I have not used any IDE, because I did not find any that worked well enough for me. IntelliJ didn't compile at all. Netbeans worked (after some configuration), but it does not support SBT at all, and compilation in Netbeans itself does take much longer than anywhere else. Eclipse i have not tried that much, but in the beginning it did not work. I ended in using JEdit. It is fast and it just works out of the box. It does not have autocomplete, but scaladoc and javadoc in a browser next to my Editor are good enough for me. Many IDE features like getter setter generation etc are simply not needed anymore in Scala and others only work on java, so I think it is still the best to use a good text editor for Scala development.

But i can say developing in scala is so much cleaner and nicer that Java or even C++, it just makes me happy to look it the sourcede. here is one example:
Vector class
Code: [Select]
class Vector2f(_x:Float,_y:Float){
  def x=_x
  def y=_y
  def +(that:Vector2f)=new Vector2f(x+that.x,y+that.y)
  def -(that:Vector2f)=new Vector2f(x-that.x,y-that.y)
}
it has constructor all getters +,-,+=,-= and all that in only 6 lines of code. (setters are not set, because Vector2f are immutable)
« Last Edit: August 17, 2010, 20:32:05 by Krux »

Re: Scala and Sbt getting started.
« Reply #3 on: September 19, 2010, 15:26:31 »
Here's my SBT project class:

Code: [Select]
class SgineProject(info: ProjectInfo) extends DefaultProject(info) {
    override def testSourceRoots = super.testSourceRoots +++ ("src" / "example")
    override def runClasspath = super.runClasspath +++ testClasspath +++ ("src" / "main" / "resources") +++ ("src" / "test" / "resources") +++ ("src" / "example" / "resources")
   
    lazy val runExample = task {
        args => runTask(Some(args(0)), runClasspath).dependsOn(testCompile)
    } completeWith(mainSources.getRelativePaths.toSeq.map(_.replace("/", ".").replace(".scala", "")))
   
    override def fork = Some(new ForkScalaRun {
        val (os, separator) = System.getProperty("os.name").split(" ")(0).toLowerCase match {
            case "linux" => "linux" -> ":"
            case "mac" => "macosx" -> ":"
            case "windows" => "windows" -> ";"
            case "sunos" => "solaris" -> ":"
            case x => x -> ":"
        }
       
        override def runJVMOptions = super.runJVMOptions ++ Seq("-Djava.library.path=" + System.getProperty("java.library.path") + separator + ("lib" / "native" / os))
       
        override def scalaJars = Seq(buildLibraryJar.asFile, buildCompilerJar.asFile)
    })
}

In my case I also have source code in src/example for non-unit tests but this will support the proper separators for the different operating systems and uses the proper references for the scala jars.  This is for my 3d engine Sgine (http://www.sgine.org).  We're in need of additional help if anyone is interested in contributing. :)

*

Offline Krux

  • *
  • 24
Re: Scala and Sbt getting started.
« Reply #4 on: September 23, 2010, 10:32:09 »
thanks, I updated my project file.

Re: Scala and Sbt getting started.
« Reply #5 on: October 04, 2010, 21:48:59 »
Thanks for the tuto, but what if you don't use sbt (I don't like it since it re-downloads every single jar for each project, thus making it weigh at least 15Mo)?

How would you run the lwjgl sample (org.lwjgl.test.WindowCreationTest) through scala for instance?
When I try: scala -cp .:/usr/share/java/lwjgl-2.5/jar/{every lwjgl jar}.jar: -Djava.library.path=/usr/share/java/lwjgl-2.5/native/linux org.lwjgl.test.WindowCreationTest

I got the error:
java.lang.UnsatisfiedLinkError: no lwjgl in java.library.path
   at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1681)
   at java.lang.Runtime.loadLibrary0(Runtime.java:840)
   at java.lang.System.loadLibrary(System.java:1047)
   at org.lwjgl.Sys$1.run(Sys.java:72)
   at java.security.AccessController.doPrivileged(Native Method)
   at org.lwjgl.Sys.doLoadLibrary(Sys.java:65)
   at org.lwjgl.Sys.loadLibrary(Sys.java:81)
   at org.lwjgl.Sys.<clinit>(Sys.java:98)
   at org.lwjgl.opengl.Display.<clinit>(Display.java:128)
   at org.lwjgl.test.WindowCreationTest.initialize(WindowCreationTest.java:80)
   at org.lwjgl.test.WindowCreationTest.main(WindowCreationTest.java:284)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
   at java.lang.reflect.Method.invoke(Method.java:616)
   at scala.tools.nsc.ObjectRunner$$anonfun$run$1.apply(ObjectRunner.scala:75)
   at scala.tools.nsc.ObjectRunner$.withContextClassLoader(ObjectRunner.scala:49)
   at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:74)
   at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:154)
   at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)

Moreover, scala can't import math._
Any special .jar to add to classpath?
« Last Edit: October 04, 2010, 21:54:48 by limestrael »

*

Offline Krux

  • *
  • 24
Re: Scala and Sbt getting started.
« Reply #6 on: October 16, 2010, 18:36:39 »
no i have not tried to use plain scalac.

But it is not true that sbt re-downloads for each project. You can make subprojects, then you should have shared resources. Might be my next tutorial. But who cares about 15mb project folders, video editors have project folders with more than 350Gb. So just ignore it, the final Product will be smaller again.

*

Offline Chuck

  • *
  • 42
  • aka sproingie on freenode
Re: Scala and Sbt getting started.
« Reply #7 on: September 21, 2011, 17:01:25 »
I came across this while digging up SBT tutorials, so I thought I'd engage in a little thread necromancy: do you have a port of your project/build handy as a build.sbt that works with sbt 0.10?

*

Offline Chuck

  • *
  • 42
  • aka sproingie on freenode
Re: Scala and Sbt getting started.
« Reply #8 on: September 29, 2011, 22:40:12 »
Answering my own question here for the sake of teh google in case other lost souls run across this...

There's an LWJGLProject type defined by the lwjgl plugin: https://github.com/philcali/sbt-lwjgl-plugin