WANTED: full-time game artist!

Who are we?

Sileni Studios is a young indie game studio from Antwerp, Belgium. We were founded in January 2012 by Karel Crombecq (programmer) and Hanne Maes (artist). We have previously released Castle Quest, a state-of-the-art HTML5 browser game, and Evo Dash, a mobile platforming game that evolves every 6 seconds to offer you unique new challenges.

We are currently working on Mayan Death Robots, a unique mix between a fighting game and Worms. You play an alien robot who lands in Mexico 1500 years ago during the height of the Maya civilization to blow the jungle to pieces and gather praise from the Mayans, who think their gods have descended from the heavens. Watch some recent gameplay footage from the game here:


Job offer

We are looking for a badass all-round artist to join our team and help us take Sileni Studios to the next level! We believe this is a unique opportunity for you for the following reasons:

  • Mayan Death Robots is quite far in development, which means you will be thrust into the fray right away. But as we are still only a 2-man team, you will be able to make a difference even this late in the project. And you’ll get to finish and release your first game with us pretty quickly!
  • When we start on our next project, you will be there from the start. Which game are we going to make? We’ll figure that out together. Join us and we’ll make the game of our dreams!
  • We have 100% creative freedom and financial stability, which is a very unique combination in the Belgian game industry indeed.

What are we looking for in a candidate?

  • You must be an awesome all-round artist with extensive experience in concept art, game graphics and animation. Since we’re a small company, we really need someone who can do everything.
  • You must have a fiery passion for video games. We breathe and live video games and we need someone who feels the same way!
  • You will not only be artist, but also art director. You must be able to maintain a vision for the art-style of the game and be able to execute it.
  • Experience with Flash and Unity is a big plus.
  • Experience with website design and interface design is a plus.

Interested?

Send your resume and portfolio to Karel@silenistudios.com and we’ll get in touch with you!

Until then enjoy these pictures, which give a 100% accurate impression of an average work day at Sileni Studios.

Call for Concept Artists

We are looking for a freelancer concept artist to work with us on improving the impact of the graphics of our game, Mayan Death Robots. Below you can find a video of how the game looks today:

We want to improve the in-game graphics, so that they “pop” more in screenshots and trailers. To do this, we are looking to contract a freelance concept artist for a few days to take a screenshot of the game, and enhance it to the point that it can be used in, for example, a poster.

We will then take this revamped/photoshopped/photobashed screenshot and use it as concept art to improve the game graphics ourselves. If your work is inspiring us, we might also commision you for additional work later.

This is an example of a screenshot we want you to manipulate into something Really Fucking Awesome:

It’s kind of a collage of different GUI elements, explosions, attacks and moments in the game. We want to pay you to turn this into something that makes you wanna buy the game right then and there.

If you are interested in this job, please contact me at Karel.Crombecq@gmail.com and send me your portfolio.

(Re-)announcing Mayan Death Robots

A lot has happened since our last post on this page! The game formerly known as Clockwork Rumble has been renamed to Mayan Death Robots, and has been given a major graphics and gameplay overhaul.

We have been steadily posting updates on Facebook and Twitter, but our website has been lacking in updates entirely. If you really want to be kept up to date, I suggest you check out our Facebook of Twitter, on which you can find a lot of content for Mayan Death Robots.
Either way, here’s a video of the game as it was submitted to Indiecade one month ago:

Announcing Clockwork Rumble!

Clockwork Rumble is officially the new Sileni Studios game! It’s been under development for about a month, and we are now finally ready to show it to the public! We are very excited to share the game with you.

Clockwork Rumble is a fast-paced build-and-destroy physics game, in which you play a small robot in a huge clockwork machine, and you try to protect your power source from other robots who try to destroy it.

To do this, you build a fort around your Heart using tetris-like blocks, and then shoot at the other player with rocket launchers and grenades. Between protecting your heart from enemy fire, collecting powerups, shooting the enemy and taking care of environmental hazards, you’ll be a busy little robot!

Clockwork Rumble is a fast-paced, 1v1 local multiplayer game, featuring exciting 10-minute battles on a variety of maps. Build your defenses smart, take into account the environment and think on your feet, and you will be victorious!

The following infographic further illustrates the gameplay of Clockwork Rumble:

Castle Quest Release!

After 2 years of development, Castle Quest will finally be released 13/12/2013 at 20:00 CET (Central European Time)!

We will reset the game, so everyone will start from scratch again and everyone will be equal. We hope to see you in-game in great numbers, as there will be tournaments and great prizes for the first people to found their own kingdom and conquer the mainland.

Alpha starting again Saturday 2013-08-24 20:00 CET

Castle Quest is back, with a second alpha phase starting this Saturday 2013-08-24 at 20:00 CET! This time, the alpha will be completely open, so feel free to invite any of your friends to join. You will not need a registration key to play.

After the last alpha phase, we made several big adjustments to the game to respond to the feedback from players. One of the main problems turned out to be longevity: people rushed through the game too quickly, and settled with a particular army composition they were happy with, without experimenting with possibly better compositions. To remedy this, we made several adjustments to the core gameplay:

  • Auras were removed. They were fun the first few levels, but eventually became a brainless chore, as you just replaced your worst aura by the new one. Instead, first hit now depends on the age of the creature (see next point).
  • Creatures are now born as younglings, will age as they fight and will eventually die. An average creature has a lifespan of about 30 battles, and goes through 3 life stages: youngling, adult and veteran. Adults have a higher first hit chance than younglings, and veterans have a higher first hit chance than adults. Thus, the creature age replaces the aura as the mechanic that determines who has the highest chance to attack first in combat. The fact that creatures die also pushes you to experiment with different army compositions. Summoning a creature has also become immediate, and does not require a ritual anymore.
  • Each creature is born with unique DNA. Sometimes, creatures will evolve interesting traits that give them an advantage: creatures might live longer, become a veteran faster, get first hit chance bonuses and so on. If you have summoned a creature with interesting DNA, you can sacrifice it before it dies to preserve the DNA in an amulet, which can be traded with other players. But you can also combine two amulets to create children that inherit the properties of both parents! This way, you can create powerful bloodlines of creatures that can be sold or traded for, and that can really improve your power!
  • Rituals now take a certain time to complete, instead of taking a number of battles. For example, an improvement will take 6 hours. However, when you are online during the ritual, certain events can occur that will allow you to reduce the total ritual time. Also, as a kingdom you can start boosts that further decrease the time needed for a ritual. But whether you use the boosts or not, you will still have to wait a certain amount of time before the ritual finishes.

With these changes, we hope to give the game a much longer lifespan, without annoying players or making the game boring. On one side, rituals take time, so you will not be able to clear the game in a couple of days anymore. On the other side, people who want to play a lot still have plenty to do while waiting for their rituals to finish: they can work on breeding more powerful creatures and can experiment with their army composition.

We hope to see you back in great numbers tomorrow!

Castle Quest Beta Signup!

Castle Quest is nearing alpha/beta release, so we set up a registration form for anyone interested in participating in the alpha/beta of Castle Quest! If you subscribe, we will also keep you up to date on development progress as we near the alpha test. The alpha is currently scheduled for April 2013, and we will be inviting people from this list to participate in it. Be the first to playtest Castle Quest: subscribe now!

Subscribe here!

To whet your appetite, here’s a concept art sketch of the Dragon Nether Creature from the game:

Maximal compression for Websockets

While I was working on a proof-of-concept for a real-time websockets-based browser game, I came upon this curious issue that is packet compression in javascript. Say you have a real-time first person shooter, in which the server has to send a snapshot of the server state to each client 20 times per second. This snapshot will contain the location, direction, weapon etc of each player in the game at that point.

When you compress this using JSON, you get quite a big packet to send every 50ms. JSON is a very comfortable format for sending data, but it is not a very well compressed format. This blog post explains the problem very well in great detail. Eventually, the author proposes to avoid the object hierarchy altogether, instead flattening the data into one single array, which is processed on receival. This does reduce the packet size considerably, as no field names must be stored, but it also makes working with this array very unwieldy, and prone to mistakes.

To solve this issue, I wrote a little library that converts an object hierarchy to a flat array and back again, based on a template. This will only work if your data is highly structured, but this is usually the case for packets that are sent repeatedly. This way, you only once need to define a template of the object hierarchy of your data (including arrays), and can work in your code with the original hierarchy directly, instead of the flat array.

Here is an example application:

    // template
    var template = {
        field1: 0,
        field2: [{
            field21: 0,
            field22: 0,
            field23: {
                field231: 0,
                field232: 0
            }
        }],
        field3: 0
    };

    // data
    var data = {
        field1: 1.0,
        field2: new Array(
            {
                field21: 2.1,
                field22: 2.2,
                field23: {
                    field231: 2.31,
                    field232: 2.32
                }
            },
            {
                field21: 2.1,
                field22: 2.2,
                field23: {
                    field231: 2.31,
                    field232: 2.32
                }
            }
        ),
        field3: 3.0
    };

    // flatten to single array
    console.log(PacketFlattener.flatten(data, template));

    // resulting array: [1, 2, 2.1, 2.2, 2.31, 2.32, 2.1, 2.2, 2.31, 2.32, 3]

So how does this work? PacketFlattener will traverse the entire hierarchy based on the template, and just store the data, not the field names or hierarchy. It will also store the length of the arrays it encounters, as it needs this data to restore the hierarchy later. Below you can find the code that does all this:

window.PacketFlattener = {

    flattenData: function(data, template, array) {
        if (typeof(data) != typeof(template)) {
            console.log("Found mismatch between template type " + typeof(template) + " and data type " + typeof(data) + " during flatten");
        }
        else if (typeof(template) == "number" || typeof(template) == "string" || typeof(template) == "boolean") {
            array.push(data);
        }
        else if (typeof(template) == "object") {
            if (template instanceof Array) {
                array.push(data.length);
                for (var i = 0; i < data.length; ++i) {
                    arguments.callee(data[i], template[0], array);
                }
            }
            else if (template.constructor && template.constructor === Object) {
                for (var key in template) {
                    if (typeof(data[key]) == "undefined") arguments.callee(template[key], template[key], array);
                    else arguments.callee(data[key], template[key], array);
                }
            }
        }
    },

    unflattenData: function(array, template, data, templateKey, dataKey, idx) {
        if (typeof(template[templateKey]) == "number" || typeof(template[templateKey]) == "string" || typeof(template[templateKey]) == "boolean") {
            data[dataKey] = array[idx++];
        }
        else if (typeof(template[templateKey]) == "object") {
            if (template[templateKey] instanceof Array) {
                var n = array[idx++];
                data[dataKey] = new Array();
                data[dataKey].length = n;
                for (var i = 0; i < n; ++i) {
                    idx = arguments.callee(array, template[templateKey], data[dataKey], 0, i, idx);
                }
            }
            else if (template[templateKey].constructor && template[templateKey].constructor === Object) {
                data[dataKey] = {};
                for (var key in template[templateKey]) {
                    idx = arguments.callee(array, template[templateKey], data[dataKey], key, key, idx);
                }
            }
        }
        return idx;
    },

    flatten: function(data, template) {
        var array = [];
        this.flattenData(data, template, array);
        return array;
    },

    unflatten: function(array, template) {
        var container = {};
        this.unflattenData(array, {data: template}, container, "data", "data", 0);
        return container.data;
    }
};

After performing this flattening step, you can still use any compression method to further increase the compression rate. This blog post suggests to use BISON, a compression algorithm that will work best with numbers, as it stores each number as a single-precision character. This makes it the perfect compression buddy for the PacketFlattener! Using BISON after PacketFlattener will result in very concise packets that can comfortably be sent 20 times every second.

Finally, remember that the easiest way to reduce bandwidth is to not have to send any data at all – there are many ways to reduce the amount of data that needs to be sent, such as delta compression. This blog post sums up some ways to further reduce the packet size. These tricks will work fine in conjunction with PacketFlattener.

PacketFlattener can also be found on Github.