Investment vs. publishing

Ever since I started in this industry (about 10 years ago) there has been talks about both the demise and rise of the publishers. It seems that when a new platform comes around developers tend to see it as a solution to a problem of independence. Or even more specifically, a way to self publish. During my time as a game developer those revolutions have been the digital channels for consoles (Xbox Live Arcade, etc), Social networks (Facebook mainly), the smartphone/tablet Appstores and now the talk is all about VR/AR.

Whenever a new platform comes around there is generally a window of opportunity for smaller developers to reach an audience without the need for publishers. It works well for a while, but as the competition stiffens and the amount of content grows, the sales decline. And, yet again, the publishers tend to move in and establish themselves as sort of gatekeepers. With access to capital, marketing channels and good relations with the platform holders, they have the business tools that a developer often lacks. But, should a developer have proper funding and a long term business perspective, there is no reason a developer couldn't manage to gain all those tools themselves. Trying to get signed with a publisher isn't much more different from getting an investment from a venture capitalist. So, let's compare the two and consider the pros and cons.

Publishing

What do you give up?
More often than not, you have to give up your IP, and if not that, at least a rights of first refusal to publish any sequels or derivative works. You also have to consider working in a way that might not suit you, but rather the publisher, as most publishers pay on a milestone basis. Meaning that for a certain chunk of work done, they pay you a chunk of money. You will also share the revenue of the game with the publisher and those terms will vary greatly. Getting a 70/30 split (with 70% going to the developer) is quite good, but then again, it comes down to a lot more details such as if the publisher can deduct costs prior to sharing the revenue, etc.

What do you get?
You should be getting funds to develop your game. Feedback and support during the development process. Marketing budget and strategy executed by the publisher, often along with yourself and your team. Furthermore, the publisher should have access to gatekeepers at whatever platform you are targeting, to make sure you have potential for being featured.

you often end up in a weird “work for hire” kind of relationship

My experience
I have heard about a few developers who had great relationship with their publishers. I've heard way more often about developers who disliked their publisher. This is most likely not because the publishers always suck, it more likely has to do with different expectations. In my experience you often end up in a weird "work for hire" kind of relationship where the publishers only leverage towards the developer is the payments of milestones. As deadlines slip, the developers goal changes over time, from creating the best possible game to just staying in business. The publishers goal is to release a great game on time for a low budget. But as time progresses, if the development is on track, the power balance skews from the publisher over to the developer. The developer sits on the code, art and know how to finish the project. The Publisher might refuse to pay a milestone, but will then not get to release a finished game.

Developer vs. Publisher balance of power

Investment

What do you give up?
You give up equity. An investor will get some of your company shares. Many investors will want to have a seat on your board and the opportunity to influence your business decisions, or at least stay closely informed.

An investor often invests as much in you as your company

What do you get?
Cash in the bank. Freedom to make your own decisions (mostly). The ability to build something from the ground up. What it really gives you is time. Hopefully enough time to go to market, make some mistakes, adapt, improve and overcome. An investor often invests as much in you as your company

My experience
When I sold my game studio some years ago the buying company had several investors. I went from presenting monthly revenue and financial situation to my buddies over lunch, to travelling to London and reporting to a board of venture capitalists from various funds. A bit of a difference. While the access to the funding was great, it was really problematic that the investors didn't get the games industry. They had little understanding of development cycles, iterative/agile process and why the hell we weren't making millions of dollars yet.

As for Lavapotion, we have an amazing investor. The fine folk at Coffee Stain Studios understand exactly what we are working on. They attend our sprint reviews, give great feedback and advice, but never interfere with what we do. It is a relationship built on mutual respect. The funds give us the security to take long term decisions and always keep whats best for the game in mind. With that said, we are still open to work with a publisher, should we be able to find a great win/win-scenario.


To sum it up
In the end, it all comes down to how you want to work. Remember, many publishers started out as developers. What ever they learnt, you can learn too. But it takes time and effort. If you aren't interested in business development and marketing then a publisher is obviously a relevant option for you.

Whatever you do: Only sign contracts with people you could see yourself working with for a long period of time. Trust your gut feeling and consider how the other partner would react in stressful situation, because they will inevitably appear down the line.

And finally, here are some publishers that in my opinion really care about the process and developers:

Coffe Stain Publishing - Coffe Stain have created quite a few hit games themselves and are now taking on publishing with some very interesting titles signed. Lovely people to work with.

Raw Fury - Run by a savvy team with industry veterans. Offers very reaonable deal terms to the developers and really care about each project.

And, some investors who really gets game development:

London Venture partners - I've met these guys at lots of dev conferenses, they have been around for a long while and they get game development.

Indie fund - While I've only met a few of the people behind the fund, they have a transparent and very relevant business model, which I greatly appreciate.

Nordisk Film Games - Last, but certainly not least, these guys know game development, they know how to spot good teams and has already made some interesting investments. As a disclaimer I should mention that I have worked for them, helping them with analyzing potential investments.

And finally - Feel free to reach out if you want some friendly advice :)

Connecting Unity Cloud Build and HockeyApp (the slightly hackish way)

When we started our project at Lavapotion, we wanted an automated way to build our game to any platform we wanted as well as a seamless and easy way to give these builds to ourselves, stakeholders and testers.

With the number of great services that is available today, we decided to use Unity Cloud Build to build and then use a script that uploads all the successful ones to HockeyApp with the commit messages as the changelog.

Why Unity Cloud Build?

Unity Cloud Build got options to unit test, build and export to platforms like iOS, Android, MacOS and Windows. Which is all we need at the moment. They also have the options of choosing exactly what Unity Version to build from if you want, and they always have the latest as an option to.

It's super easy to connect with slack (for build notifications) and the setup is pretty straightforward. Disregarding that sometimessome builds fail for no reason, it's really everything we need at the moment.

Why HockeyApp?

Sure, you can share all your builds in Unity Cloud Build by creating a share link and send it to the people that needs it, but there's a whole manual process involved there and no easy way to restrict downloads to certain groups.

HockeyApp got distribution groups that you can add/restrict to any builds pretty much whenever you want. You can also notify people with an email with a changelog when a new build is ready. It's neat.

Connecting the two platforms. Why do we need it?

The obvious way to connect the services is to do it manually everytime. To break it down into a step-by-step instruction - take a look at the list below.

  1. Log into Unity Cloud Build
  2. Download all the successful builds for the platforms you want to upload to HockeyApp to your computer
  3. Log into HockeyApp
  4. Choose the app that's on the platform you want to update.
  5. Upload a new version
  6. Write/Copy-Paste the changelog
  7. Choose the Distribution Groups that should have access to this specific build.
  8. Decide if they should get an email notification.
  9. Repeat steps 4-8 for every platform you would like to update.

A philosophy that we strongly hold at Lavapotion is to reduce all the "screw-up-able" steps that can occur in our process and automate as much as possible. Not only is the list above extremely time consuming, but almost every step is error prone to all kinds of mistakes because there are so many manual steps.

So as a first step, we decided to reduce the manual steps to the following when there's a successful build.

  1. Run a script.

That looks way better, doesn't it? ;)

Setting everything up

You will need to do the following before we start:

  • Your project in a Git repository (for the automatic changelog)
  • Install Node Package Manager on your computer
  • Setup your project in Unity Cloud Build
  • Setup your applications in HockeyApp

The script we're creating will be written in Javascript and using a gulp job to perform the actual tasks of downloading and uploading, so we will need to setup the node project first.

Create a file called package.json at the root of your project and fill it with the following: 

(Replace the placeholder with your own information.)

{
  "name": "Super secret project",
  "description": "The best super secret game in the world.",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "gulp": "node ./node_modules/gulp/bin/gulp.js"
  },
  "repository": {
    "type": "git",
    "url": "<YOUR GIT URL>"
  },
  "author": "<YOUR COMPANY NAME>",
  "license": "<YOUR LICENSE>",
  "private": true,
  "devDependencies": {
    "gulp": "^3.9.1",
    "gulp-download": "0.0.1",
    "gulp-hockeyapp": "^0.2.4",
    "gulp-rename": "^1.2.2",
    "request": "^2.80.0",
    "simple-git": "^1.67.0"
  }
}

This will setup a simple npm project. The devDependencies object explains what modules we will use in our scripts. The "gulp" part in scripts is optional - it's added so that we can easily use the locally installed version of gulp rather than a global one.

To install all of the devDependencies, open the command prompt (or terminal) and locate yourself at the root of the project and run the following command:

D:\YOUR_PROJECT_PATH>: npm install

After everything is installed, you can create a file called Gulpfile.js in your project root.

Gulp!

Given that your project is setup in Unity Cloud Build and HockeyApp, you can setup your Gulpfile.js like this and change the placeholders to your specific settings. This example will use an android build to upload.

'use strict'
var gulp = require('gulp');
var gulpDownload = require('gulp-download');
var rename = require('gulp-rename');
var request = require('request');
var git = require('simple-git');
var hockeyApp = require('gulp-hockeyapp');

var cloudBuildSettings = {
    apiKey: "<YOUR_CLOUDBUILD_API_KEY>",
    organizationID: "<YOUR_CLOUDBUILD_ORGANIZATION_NAME>",
    projectName: "<YOUR_CLOUDBUILD_PROJECT_NAME>"
};

var hockeyAppSettings = {
    apiKey: "<YOUR_HOCKEYAPP_API_KEY>",
    androidDevKey: "<YOUR_HOCKEYAPP_APPLICATION_ID>",
    devTeamID: <YOUR_TEAM_ID>
};

var tasks = {
    hockeyApp: 'hockeyapp',
};

var paths = {
    dist: 'dist/'
}

Downloading information about the latest build

Before doing anything, we need to be able to download the build information of the last successful build on Unity Cloud Build so that we can download it to our computer.

Luckily, Unity got a REST API that we can use to do this that is well documented.

Using the "List all builds" method, we can fetch the information that we need. Add the following function to your Gulpfile.

function downloadBuildInformation(pBuildTargetID, pOnCompleteCallback) {
    var baseURL = 'https://build-api.cloud.unity3d.com/api/v1';
    var options = {
        url: baseURL + '/orgs/' + cloudBuildSettings.organizationID + '/projects/' + cloudBuildSettings.projectName + '/buildtargets/' + pBuildTargetID + '/builds?buildStatus=success',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Basic ' + cloudBuildSettings.apiKey
        }
    };
    request(options, function (error, response, body) {
        var latestBuild = null;
        if (!error) {
            var builds = JSON.parse(body);
            var latestDate = new Date(-8640000000000000);
            for (var i = 0; i < builds.length; ++i) {
                var currentBuild = builds[i];
                var finishedDate = new Date(currentBuild.finished);
                if (latestDate < finishedDate) {
                    latestBuild = currentBuild;
                    latestDate = finishedDate;
                }
            }
        }
        else {
            console.log('Failed to get build information! message: ' + error);
            pOnCompleteCallback();
        }
        if (typeof (pOnCompleteCallback) !== 'undefined' && pOnCompleteCallback != null) {
            pOnCompleteCallback(latestBuild);
        }
    })
}

This function will download the successful builds available and return the newest one in the complete handler once done.

Downloading the build

After we get the information, we need to download the build to our computer. The build will be downloaded with a npm module called gulp-module. When the file is downloaded, we will return the file name in the complete handler.

Add the following function to your Gulpfile:

function downloadLatestBuild(pBuildInfo, pOnCompleteCallback) {
    if (pBuildInfo === null) {
        throw 'Cannot download build! Information is null!'
    }
    var latestDownloadFileName = cloudBuildSettings.projectName + '_' + pBuildInfo.buildtargetid + '.' + pBuildInfo.links.download_primary.meta.type;
    gulpDownload(pBuildInfo.links.download_primary.href)
        .pipe(rename(latestDownloadFileName))
        .pipe(gulp.dest(paths.dist))
        .on('end', function () {
            console.log('\nFinished Downloading Build\n');
            pOnCompleteCallback(latestDownloadFileName);
        });
}

Creating the change log

Since HockeyApp supports uploading a change log in markdown, we wanted a quick look on what's been done since the last build. At this point we don't need any curated or filtered logs, so we can just use the git commit logs for now (given that our commit messages are properly written).

To do this, you can add the following three functions to your gulp file. One takes the information available from unity cloud build and the other two gets information from your git repository and retrieves the commit messages.

function createMarkdownReleaseNotes(pBuildInfo, pOnCompleteCallback) {
    if (pBuildInfo === null) {
        throw 'Cannot get changelog! Information is null!'
    }
    var markdownChangelog = '#' + cloudBuildSettings.projectName + ' ' + pBuildInfo.buildTargetName + '\n';
    markdownChangelog += '**Branch:** ' + pBuildInfo.scmBranch + '\n';
    markdownChangelog += '**SHA1:** ' + pBuildInfo.lastBuiltRevision + '\n\n';

    var ignoreCommitIds = [];
    if (pBuildInfo.changeset.length > 0) {
        markdownChangelog += '## Recent Changes \n\n';
        markdownChangelog += 'If installed, this build will: \n';
        for (var i = 0; i < pBuildInfo.changeset.length; ++i) {
            var message = pBuildInfo.changeset[i].message;
            markdownChangelog += '* ';
            markdownChangelog += message;
            markdownChangelog += '\n';
            ignoreCommitIds.push(pBuildInfo.changeset[i].commitId);
        }
    }

    var gitLogSettings = [];
    var maxHistoryLogs = 50;
    gitLogSettings.push(pBuildInfo.lastBuiltRevision);
    gitLogSettings.push(pBuildInfo.scmBranch);
    getGitLog(gitLogSettings, function (pLogArray) {
        markdownChangelog += buildChangelogHistoryFromGitArray(pLogArray, maxHistoryLogs, ignoreCommitIds);
        pOnCompleteCallback(markdownChangelog);
    });
}

function buildChangelogHistoryFromGitArray(pLogArray, pMaxHistoryLogs, pExcludeShaArray) {
    var changeHistory = '';
    if (pLogArray.length > 0) {
        changeHistory += '\n## Change History \n\n';
        for (var i = 0; i < pLogArray.length; ++i) {
            var message = pLogArray[i].message;
            if (pExcludeShaArray.indexOf(pLogArray[i].hash) === -1) {
                changeHistory += '* ';
                changeHistory += message;
                changeHistory += '\n';
            }
            if (pMaxHistoryLogs !== -1 && i >= pMaxHistoryLogs) {
                break;
            }
        }
    }
    return changeHistory;
}

function getGitLog(pGitLogSettings, pOnCompleteCallback) {
    git().log(pGitLogSettings, function (err, log) {
        var logArray = null;
        if (!err) {
            logArray = log.all;
        }
        pOnCompleteCallback(logArray);
    });
}

Uploading the build to HockeyApp

To upload the build to HockeyApp, we are using a package called gulp-hockeyapp. It does exactly what we need and you can also decide whether or not you should send an email notification to your HockeyApp distribution groups.

Add this function to your Gulpfile.js that will use all of our previous functions and once everything is done, upload the build to hockeyapp.

function downloadAndUploadToHockeyApp(pBuildTargetId, pHockeyAppID, pOnCompleteCallback, pNotifyAll, pTeams) {
    downloadBuildInformation(pBuildTargetId, function (pBuildInfo) {
        downloadLatestBuild(pBuildInfo, function (pDownloadFilename) {
            createMarkdownReleaseNotes(pBuildInfo, function (pMarkdownChangelog) {
                var options = {
                    id: pHockeyAppID,
                    apiToken: hockeyAppSettings.apiKey,
                    inputFile: paths.dist + pDownloadFilename,
                    notify: pNotifyAll === true ? 2 : 0,
                    status: 2,
                    teamList: pTeams,
                    notes: pMarkdownChangelog,
                    notes_type: 2
                };
                console.log('\nUploading to hockeyapp...\n');
                hockeyApp.upload(options).then(
                    function (response) {
                        console.log("Successfully uploaded build to HockeyApp!");
                        pOnCompleteCallback();
                    },
                    function (err) {
                        throw err;
                    }
                );
            })
        });
    });
}

Creating the Gulp job

By now, we have all the functions that we need. The only thing left is to create a gulp job that uses the function that we just created.

Add the following gulp task to your Gulpfile:

gulp.task(tasks.hockeyApp, function (done) {
    var notifyByEmail = false;
    downloadAndUploadToHockeyApp('android-dev',
        hockeyAppSettings.androidDevKey,
        done,
        notifyByEmail,
        [hockeyAppSettings.devTeamID]);
});

That's it! To run this, just use the following command at the root of your project in the command prompt or terminal to set the job in motion:

D:\YOUR_PROJECT_PATH>: npm run gulp hockeyapp

Ending Notes

As the title suggests, there is much room for improvement with this script. You could split the methods into several files, create unit tests, add better error handling or even use a build computer with jenkins to automatically poll when a new build is successful and run everything automatically.

But at this moment, this is good enough for us. We removed some manual steps and saved everyone in the team some time. We hope that this can be of use to some of you as well!

If you need to have a look at the Gulpfile in it's entirety, you can find it here.

 

Starting over again, doing it the right way

It is a rare treat indeed to get to start on a new game. I started a game development company along with some buddies several years ago. We created advergames, facebook games and eventually mobile games. We produced over seventy games spanning various genres. Some of them did ok, a few did amazing but most of them never got much attention beyond a marketing campaign. Never really having funds to give every game the time and love it needed was frustrating. Eventually we sold the company and moved on with our lives.

And here we are again. Starting a new studio and working on something we passionately love - Strategy games! With funding from the amazing team at Coffee Stain Studios and a team of great developers. This time we are doing things the way we always wanted. Let me share some of our core beliefs.

We believe in SCRUM

It works well for us. SCRUM helps us with keeping our development in line with what our stakeholders expect and making sure that we do not loose sight of our target. It is also great to have a methodology to fall back onto when things are shaky. Being agile within constraints is a great thing.

We believe in work/life balance

This means that we do not work overtime. We take care to plan our days carefully and we make sure to allow both flexibility and structure, where needed. We work together, but we also play games together and enjoy our very Swedish fika every day. We encourage ourselves to work out, stay curious and having fun at work.

We believe in building something long term

As we are driven by passion, we want to maintain it and help it grow. We discuss very openly about personal goals as well as company goals. How can we align targets and how can we create a workplace where we enjoy our work each day? Building great games take time and if we enjoy that time, then better games we are going to create.

In the end, it all comes down to what we want to do with our lives. We choose to work with games because well, we love games. Creating games is a labour of love and for us, the process is equally as important as the result.