Java-Gaming.org Hi !
Featured games (90)
games approved by the League of Dukes
Games in Showcase (744)
Games in Android Showcase (225)
games submitted by our members
Games in WIP (825)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Game Design in Kotlin for Java Programmers  (Read 519 times)
0 Members and 1 Guest are viewing this topic.
Offline cygnus
« Posted 2017-11-15 22:39:43 »

Hi!

After about 2 years of working in Java programming my game, I switched everything to Kotlin. That was 7 months ago. I hope I can impart some of the lessons I learned through that here.

Please have IntelliJ and the Kotlin plugin for it installed. I'll cover how to set up building, VCS integration and other normal stuff in IDEs if anybody asks, but for now, I'm going to assume everything is working on your end.

I'm going to be making a Java2D game with simple graphics and input. However, I will go out of my way to utilize new Kotlin features that are not strictly speaking necessary but might be for more complicated projects. I'll tell you when something I've added in is there to teach you about it or to show you a what I think is a good design decision. This is not meant to be followed along, as code will be incomplete if it is not relevant to the tutorial - just read through it and hopefully get an understanding of the basics of game design in Kotlin. I'll make periodical comments in the code to explain syntax.

At first, Kotlin is anticlimactically easy to switch to with a basic game. Paste code from Java into Kotlin and it will be automatically converted - not perfectly, of course, but the basics will be there. I'll start out with a normal game loop using Canvas enclosed in a JFrame (you should know that this was Kotlin originally, but some elements have been changed to simulate copy-pasting from Java, therefore, these might be questionable design choices in Java)
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
// "..." in a comment means there would normally be something there but it was omitted
// I recommend you read through all the comments and look at the changes in syntax - first noticeable change, no semicolons if you don't want them.
// They only serve to let you write code on the same line
package main

// ...

// Equivalent to class Game extends Canvas implements Runnable, the parenthesis here will be covered later
class Game() : Canvas(), Runnable {
   
    @JvmStatic
    fun main(args: Array<String>) {
        // ...
    }

    // Variables (var) can change, values (val) can't
    const val WIDTH = 300
    const val HEIGHT = (WIDTH.toDouble() / 16 * 9).toInt()
    // Compile time constant versus normal val which is run time
    const val SCALE = 4

    // 'new' keyword not necessary - this forces you to use capitalization
    // to differentiate between methods and constructors
    val thread = Thread(this, "JGO Kotlin Tutorial Main Thread")
    // Some variables omitted

    // Same visibility modifiers as Java plus the 'internal' one, which I might cover later. No visibility modifier means public, not package level, however
    private var running = false

    // Equivalent to code in the constructor, except it runs for every constructor and not the one it was put in (multiple constructors covered later)
    init {
        // ...
        // In default Java, this is a method called setDefaultCloseOperation.
        // Kotlin automatically converts setters and getters into properties (more on this later)
        frame.defaultCloseOperation = JFrame.EXIT_ON_CLOSE
        // ...
    }

    // 'override' keyword is @Override, 'fun' means function
    override fun run() {
        // ...
    }
}

This ain't going to work. Notice the annotation @JVMStatic on the main function - this tells the compiler that on the Java side, this should be a static method. Static is not a keyword in Kotlin, because we have something that I like a bit better: functions that can be outside of classes, or package-level functions. If we move 'main' outside the Game class, (and remove the @JVMStatic annotation if you want because I'd recommend going full Kotlin), a nice little Kotlin logo on the left (next to the line numbers) of the function will appear. This means that IntelliJ recognizes this as a Kotlin application entry point and if you click on it, you can run the program from there.

More on packages in Kotlin: unlike Java, the package declaration at the top can be different from the folder the file is in. Handy.

Ok, now we have this code:
1  
2  
3  
4  
5  
6  
7  
// ...

fun main(args: Array<String>) {
    // Init stuff
}

class Game : Canvas(), Runnable { // ...


How about we utilize an extremely handy thing Kotlin calls objects. Java's Object class and Kotlin's keyword object are not the same thing - in Kotlin, Object is replaced by Any (or Any?, but I'll get into that question mark later). The object keyword is essentially an easy way to make a singleton class, and we have the perfect place to use it: the Game class. It's not like we're going to be using more than 1 Game at a time, right? So all we do is replace
1  
class Game : Canvas(), Runnable

with
1  
object Game : Canvas(), Runnable

Now, Game's properties can be accessed without going through some static instance, instead, we can access all the variables and methods like
1  
2  
3  
Game.start()
Game.framesCount++
// etc.


Initialization for objects in Kotlin is lazy, so it is only run the first time it is accessed. All you have to do is access some property of Game or run some function in the main function to start the game, then.

In my own code, I use objects all the time (perhaps even too much), because they're great for things like an InputManager or LevelManager that should really only need to exist once. They are also used to create the equivalent of anonymous classes in Java (but not lambdas or the like, which I will get into later).

That will be all for now, I'll add more or make new posts later.

Please tell me what you think, if I made questionable choices anywhere or am just saying something wrong. This will hopefully be added to at least once every week, because I know this wasn't even this much and you could at this point go Google a Kotlin tutorial and apply it to gamedev faster. When there is more, it should become a more efficient way for game developers to switch, though.

Edit - made some updates for clarity, removed unnecessary code

Edit: Part 2
Next thing: operator overloading. It means exactly the same thing in Kotlin. The syntax is simple:
1  
2  
3  
operator fun plus(other: Position) {
   //...
}

The operators in Kotlin are the same as Java, with a few changes. There are no bitwise operators, but there are bitwise functions. See the 'infix' section below for details. Also, there is a new operator, invoke, which is called by adding parenthesis after whatever you're invoking with any number of arguments inside.
1  
2  
3  
4  
5  
6  
7  
8  
class ValidityChecker {
    operator fun invoke(arg: GameObject) {
        //...
    }
}
//...
validityCheck(obj) // calls the invoke method above with obj as the first argument
//...


I've already mentioned some syntactic changes in the code above. Let's do a few more advanced ones now: we'll start with lambdas. I'm going to assume you're familiar with Java 8 lambdas (those -> thingies) and method references (Class::method).

Here, I have some code for the render method.
1  
2  
3  
4  
5  
6  
fun render() {
    for(o : gameObjects) {
        if(o.valid)
            o.render()
    }
}

Already, from a Java point of view, we can change that to use streams. However, Kotlin collections don't need to use streams to use forEach, filter or any other stream methods. So now we can write this:
1  
2  
3  
fun render() {
    gameObjects.filter({o -> o.valid}).forEach({o -> o.render() })
}

There are a couple syntactic changes here that not only reduce code needed but make it more readable. The first, and most handy, is that ANY lambda with a single argument doesn't require that argument to be specified by default - it is automatically given the name 'it'. The next is that if a lambda is the last argument of a method, you can remove the parenthesis around the lambda (leaving just curly braces). So now, we have the following code:
1  
2  
3  
fun render() {
    gameObjects.filter { o.valid }.forEach { it.render() }
}


But wait, you say, aren't lambdas created using a rather unwieldy process of having an anonymous inner class with overridden methods and so are not so performance friendly? The Kotlin compiler has a keyword called 'inline'. If a function takes a lambda as a parameter and it has that keyword, instead of creating what I mentioned above, it simply copies the function's code to the call site, replacing the lambda with the code you inputted (all on the compiler side, of course - there is no difference in the end result for either). If you want to look further at things that Kotlin does (or does not do) towards performance, see this blog: https://medium.com/@BladeCoder/exploring-kotlins-hidden-costs-part-1-fbb9935d9b62

In my own code, I use lambdas for things like maintaining alignment on GUI elements or creating my own collections with their own forEach methods. Especially with inline notation, and some less noticeable changes Kotlin makes to Java lambdas (like being able to modify variables captured in closure), lambdas have become a much bigger part of my code after my switch.

A handy little thing: if you had multiple arguments in a lambda and you don't need one of them, you can just replace its name with an underscore.

Another nice syntactic addition is infix notation. This is most often seen in Kotlin in bitwise functions and ranges. Basically, adding the 'infix' keyword to a function means you can remove the period and the parenthesis around the argument (only allowed to be 1 argument) when calling it. For example, in the render code, we convert from the game's 16x16 tiles to pixels like so:
1  
2  
//...
val xPixel = tile.xTile shl 4

(shl is shift left, shr is shift right, ushr is unsigned shift right, etc.)
Yet again, this contributes to the more natural language feel of Kotlin.

That's all for now! I'll add more later Smiley
Offline Sickan
« Reply #1 - Posted 2017-11-16 10:54:55 »

Looking good! Nitpicks: @JVMStatic is @JvmStatic because JetBrains uses that kind of case convention. Also at THREAD it's all uppercase which is Java praxis for constants but isn't really used in Kotlin aside from const vals.

All in all, good job, looking forward to reading further parts
Offline cygnus
« Reply #2 - Posted 2017-11-16 19:15:09 »

Yep, you're right. I'll make the edits! I aim for the next part to be halfway finished by today.

EDIT - That was a generous estimate Cheesy. I'll probably have more time for this over the coming Thanksgiving break.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline cygnus
« Reply #3 - Posted 2017-11-25 07:03:09 »

Part 2 up! Hope my 2:02 AM work is comprehensible.

Edit 12/6/17 - I'm going to try and put up another part this weekend.
Offline fitz8

Innocent Bystander





« Reply #4 - Posted 2017-12-14 03:39:04 »

Good job. Really looking forward to further parts

JSON formatter & editor. Router 192.168.0.1
Pages: [1]
  ignore  |  Print  
 
 

 
Ecumene (149 views)
2017-09-30 02:57:34

theagentd (214 views)
2017-09-26 18:23:31

cybrmynd (301 views)
2017-08-02 12:28:51

cybrmynd (288 views)
2017-08-02 12:19:43

cybrmynd (297 views)
2017-08-02 12:18:09

Sralse (291 views)
2017-07-25 17:13:48

Archive (968 views)
2017-04-27 17:45:51

buddyBro (1094 views)
2017-04-05 03:38:00

CopyableCougar4 (1670 views)
2017-03-24 15:39:42

theagentd (1430 views)
2017-03-24 15:32:08
Java Gaming Resources
by philfrei
2017-12-05 19:38:37

Java Gaming Resources
by philfrei
2017-12-05 19:37:39

Java Gaming Resources
by philfrei
2017-12-05 19:36:10

Java Gaming Resources
by philfrei
2017-12-05 19:33:10

List of Learning Resources
by elect
2017-03-13 14:05:44

List of Learning Resources
by elect
2017-03-13 14:04:45

SF/X Libraries
by philfrei
2017-03-02 08:45:19

SF/X Libraries
by philfrei
2017-03-02 08:44:05
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!