The experience of developing “Quak” during a hackathon.

In March 2019 we held a 2-day hackathon named “Ship IT!” in TouK. I was part of the team developing “Quak” – a 2D Liero/Soldat inspired game where players can immerse themselves into being a cute blob-like character while simultaneously laying waste to their opponent.

In this post I want to share our experience writing and designing the game, what kind of roles team members assumed and some of the technical choices we made.

Development

Working for a company that swims in JVM technologies – naturally we chose Kotlin as our development language. Our leader Rafał Golcz created a simple game engine in the ECS Model using the standard java game development library – libgdx and box2d for physics.

ECS stands for Entity Component System, and its main advantage over a standard object hierarchy (as games tend to be strongly object-oriented programs in principle) is its behavioural nature.

Instead of drowning in large inheritance trees and having potential “problems” with instantiation or making sure what kind of object goes where – we receive an elegant solution where the behaviour of every game object can be defined as a list of scripts that are attached to it. From design perspective it also allows for a more natural approach to creating specific objects (as in what properties an object has like dealing damage on impact, destruction on collision, bleeding).

Team Roles

Large gamedev companies (Blizzard, CD Project Red, Bethesda – to name a few) usually have strict roles for people working on their projects. Game directors, designers, writers, composers, testers, programmers, marketing teams, managers – all of which (except maybe for directors/managers) tend to be split between junior, senior and leads.

Developers are also often split according to their main responsibility: engine/tools development, gameplay programmers, special effect programmers. However, in smaller teams responsibilities tend to be way more relaxed. What I find really interesting is the natural emergence of similar structures during these 2 days of developing Quak.

We had a person responsible for art, another person for music and sound effects, 5 people who were actively developing various features for the game (controllers aka joystick and keyboard integration, weapons and missiles/bullets, collisions, character movement/controls, map loading, destructible terrain, blood splatter and camera shake effects).

At some point someone took the mantle of mapper and started creating the terrain you can see in the various screenshots in this article and a few others. What did wonders, in my opinion, was when one person became some sort of a Game Director and Designer’s hybrid. By making sure that everyone had a similar grasp of the direction the game was taking this person made sure that contradicting ideas and implementations didn’t emerge. I was this person.

Experience Itself

New features were “flowing in” as our leader noticed. Everything seemed to work seamlessly, every time someone wanted to push their changes to the repository they were met with the necessity of git pulling the changes which often introduced multiple new features. All of this added up made for a great passionate atmosphere, full of fast development and motivation. At some point we had outsourced testers from other projects to playtest early versions of the game as the sound of quacking echoed through the vast open-space of TouK.

Conclusions

During the project showcase Quak has been met with laughter and smirky remarks – great signs. We concluded that the game was a success – we delivered more than we thought we were capable of before the actual hackathon.

Making sure that the game is “juicy” (in gamedev slang – a game is juicy if love and care has been put into small details/finishing touches) at a relatively early stage with camera shake, visual effects like explosions or blood, sounds of duck quacking (as our bullets are all some kind of variation of TouK’s “duck” mascot) all made the game much more interesting to playtest and to develop – boosting the morale and work efficiency of the team.

Having little stand-ups every few hours to quickly discuss who is responsible for what and in which direction the game is going (what features to cut, which to implement, maybe some new ones?) as well as the aforementioned game director all made sure every member of the team knew what to do and in my opinion was the reason we managed to successfully finish the game. Quak has since then appeared at our stand during Scalar conference and was met with positive feedback.

It was an awesome experience but it wouldn’t be possible without a great team. Kudos to the entire Quak squad: Rafał Golcz, Robert Piwowarek, Agata Kłoss, Mateusz Mazur, Hubert Lipiński and Filip Majewski.

You May Also Like

Using WsLite in practice

TL;DR

There is a example working GitHub project which covers unit testing and request/response logging when using WsLite.

Why Groovy WsLite ?

I’m a huge fan of Groovy WsLite project for calling SOAP web services. Yes, in a real world you have to deal with those - big companies have huge amount of “legacy” code and are crazy about homogeneous architecture - only SOAP, Java, Oracle, AIX…

But I also never been comfortable with XFire/CXF approach of web service client code generation. I wrote a bit about other posibilites in this post. With JAXB you can also experience some freaky classloading errors - as Tomek described on his blog. In a large commercial project the “the less code the better” principle is significant. And the code generated from XSD could look kinda ugly - especially more complicated structures like sequences, choices, anys etc.

Using WsLite with native Groovy concepts like XmlSlurper could be a great choice. But since it’s a dynamic approach you have to be really careful - write good unit tests and log requests. Below are my few hints for using WsLite in practice.

Unit testing

Suppose you have some invocation of WsLite SOAPClient (original WsLite example):

def getMothersDay(long _year) {
    def response = client.send(SOAPAction: action) {
       body {
           GetMothersDay('xmlns':'http://www.27seconds.com/Holidays/US/Dates/') {
              year(_year)
           }
       }
    }
    response.GetMothersDayResponse.GetMothersDayResult.text()
}

How can the unit test like? My suggestion is to mock SOAPClient and write a simple helper to test that builded XML is correct. Example using great SpockFramework:

void setup() {
   client = Mock(SOAPClient)
   service.client = client
}

def "should pass year to GetMothersDay and return date"() {
  given:
      def year = 2013
  when:
      def date = service.getMothersDay(year)
  then:
      1 * client.send(_, _) >> { Map params, Closure requestBuilder ->
            Document doc = buildAndParseXml(requestBuilder)
            assertXpathEvaluatesTo("$year", '//ns:GetMothersDay/ns:year', doc)
            return mockResponse(Responses.mothersDay)
      }
      date == "2013-05-12T00:00:00"
}

This uses a real cool feature of Spock - even when you mock the invocation with “any mark” (_), you are able to get actual arguments. So we can build XML that would be passed to SOAPClient's send method and check that specific XPaths are correct:

void setup() {
    engine = XMLUnit.newXpathEngine()
    engine.setNamespaceContext(new SimpleNamespaceContext(namespaces()))
}

protected Document buildAndParseXml(Closure xmlBuilder) {
    def writer = new StringWriter()
    def builder = new MarkupBuilder(writer)
    builder.xml(xmlBuilder)
    return XMLUnit.buildControlDocument(writer.toString())
}

protected void assertXpathEvaluatesTo(String expectedValue,
                                      String xpathExpression, Document doc) throws XpathException {
    Assert.assertEquals(expectedValue,
            engine.evaluate(xpathExpression, doc))
}

protected Map namespaces() {
    return [ns: 'http://www.27seconds.com/Holidays/US/Dates/']
}

The XMLUnit library is used just for XpathEngine, but it is much more powerful for comparing XML documents. The NamespaceContext is needed to use correct prefixes (e.g. ns:GetMothersDay) in your Xpath expressions.

Finally - the mock returns SOAPResponse instance filled with envelope parsed from some constant XML:

protected SOAPResponse mockResponse(String resp) {
    def envelope = new XmlSlurper().parseText(resp)
    new SOAPResponse(envelope: envelope)
}

Request and response logging

The WsLite itself doesn’t use any logging framework. We usually handle it by adding own sendWithLogging method:

private SOAPResponse sendWithLogging(String action, Closure cl) {
    SOAPResponse response = client.send(SOAPAction: action, cl)
    log(response?.httpRequest, response?.httpResponse)
    return response
}

private void log(HTTPRequest request, HTTPResponse response) {
    log.debug("HTTPRequest $request with content:\n${request?.contentAsString}")
    log.debug("HTTPResponse $response with content:\n${response?.contentAsString}")
}

This logs the actual request and response send through SOAPClient. But it logs only when invocation is successful and errors are much more interesting… So here goes withExceptionHandler method:

private SOAPResponse withExceptionHandler(Closure cl) {
    try {
        cl.call()
    } catch (SOAPFaultException soapEx) {
        log(soapEx.httpRequest, soapEx.httpResponse)
        def message = soapEx.hasFault() ? soapEx.fault.text() : soapEx.message
        throw new InfrastructureException(message)
    } catch (HTTPClientException httpEx) {
        log(httpEx.request, httpEx.response)
        throw new InfrastructureException(httpEx.message)
    }
}
def send(String action, Closure cl) {
    withExceptionHandler {
        sendWithLogging(action, cl)
    }
}

XmlSlurper gotchas

Working with XML document with XmlSlurper is generally great fun, but is some cases could introduce some problems. A trivial example is parsing an id with a number to Long value:

def id = Long.valueOf(edit.'@id' as String)

The Attribute class (which edit.'@id' evaluates to) can be converted to String using as operator, but converting to Long requires using valueOf.

The second example is a bit more complicated. Consider following XML fragment:

<edit id="3">
   <params>
      <param value="label1" name="label"/>
      <param value="2" name="param2"/>
   </params>
   <value>123</value>
</edit>
<edit id="6">
   <params>
      <param value="label2" name="label"/>
      <param value="2" name="param2"/>
   </params>
   <value>456</value>
</edit>

We want to find id of edit whose label is label1. The simplest solution seems to be:

def param = doc.edit.params.param.find { it['@value'] == 'label1' }
def edit = params.parent().parent()

But it doesn’t work! The parent method returns multiple edits, not only the one that is parent of given param

Here’s the correct solution:

doc.edit.find { edit ->
    edit.params.param.find { it['@value'] == 'label1' }
}

Example

The example working project covering those hints could be found on GitHub.