Category Archives: Gamedev

Lessons and statistics after five years as an indie developer

My fifth year of running the indie game development studio is behind me. On January 26th 2021, Coldwild Games has turned five. Did I hope I’d get this far? Yes. Did I think we’d get this far? At times, if I am being realistic and honest, no. In this post, I am going to share this year’s personal discoveries and lessons learned.

If you want to compare it to previous year, here’s my fourth year summary

Finding Your Niche

Merchant of the Skies, art by Elena Nazarenko

Our biggest event of 2020 was out-of-early-access release of our game, Merchant of the Skies. Up until now, we’ve sold around 50000 copies on all platforms. I think 2020 encouraged me to keep working on peaceful games. I am not going to avoid games with combat entirely, but for now I really enjoy coming up with ways to make non-violent games fun. If anything, 2019 and 2020 has proven to me that there definitely are audiences who are OK with the lack of combat.

I think Merchant of the Skies has been a success mostly because of the amazing art by Elena Nazarenko ( https://twitter.com/ElenaNazaire ). That made me prioritize aesthetics-first approach for some of our next games.

Data

Here are some stats from previous years.

Expenses vs Income

The income in 2020 was greater than 4 previous years combined. The same goes for expenses.

Regarding income: Merchant of the Skies had a good early access graduation and console launch only strengthened it further.

Regarding expenses: first, the studio expanded a bit. But also the current company policy is to give creators / long-term contractors a profit share of the game they are working on. The usual % that someone gets now is at least 10% of the game profits, in addition to the salaries / contractor payment. Of course, as studio expands, this ratio is probably going to go lower, but I share around 30% of every project’s profits with people who helped to create it.

This approach does have drawbacks: since studio savings grow in a notably slower manner, each project is way riskier.

Income by source

2020 was the first year where we could stop freelancing entirely and just work on our own stuff. Trust me, it feels good and I am very lucky to be here. But it adds a new level of challenge: once you reach a status quo, you get extra stress from trying to maintain it.

The studio has grown in size. As of now, we are currently working with 4 contractors and three full-time employees (that number includes me). The plan right now is to not go crazy with expansion, but rather take it slow and ramp up the development progressively.

The GDC Talk

Despite GDC on-site event being cancelled, I managed to give a talk remotely. Not as cool as being in San Francisco, but still a personal milestone in my book 🙂

My talk, “Choosing Your Art Before Making a Game” is a summary of my aesthetic-driven development approach. It shows what I’ve learned and how we approached the development on Merchant of the Skies by putting the art first and marketing as early as possible.

We got lucky?

Merchant of the Skies leaving early access resulted in higher income than the early access launch itself (quite a rare occurrence, judging from what I’ve heard from other developments).

Nonetheless, each one of our games except for the first one essentially broke even at the very least. Make no mistake: this is mostly because of low expenses running a studio. My current net studio salary is under $1200/month (still a very decent amount for Latvia, but way less than usual sofware engineering jobs are paying way more). I am far from being a millionaire, even with a relatively successful game.

As it stands now, we have a chance to make two more games at the very least. Why am I not running around and pitching the studio everywhere? Because I want to make sure that we can consistently create good games that can comfortably sustain us.

Lazy Galaxy: Rebel Story, one of our previous games

What’s Next

I want to have a studio that works on 2 projects at the same time to reduce the risks. As I distance myself from programming and going more towards the management / planning role, I began to understand that I want to have a studio that is centered on the people, their personalities and individual growth. I can make games, but I also like seeing people grow creatively and take responsibility for their work. I want to have a crunch-free place (maybe except for one week of paid crunch before and during the _actual_ release because it’s impossible to work 8 hours only when you constantly need to reply to player feedback and adjust stuff). Work-life balance has been a priority of late 2020 and early 2021 and we plan to continue being that way.

Luna’s Fishing Garden, our next peaceful game

Ideally, I’d like Coldwild Games to be an alliance of small in-house studios and creators, each working on their own unique ideas, rather than the studio that works something big. I’d rather support talented people working on what they like rather than force my own views all the time. I can’t completely do this yet, but I might be able to later. If you are running an indie operation and like developing games, but want to meet someone who could potentially handle your production process / marketing and publishing help – drop me an email, I’d love to talk. I don’t just want to publish games and bail, I want to follow the people along their way, with both successes and failures.

These 5 years have been a real adventure. I still enjoy making games very much and I hope that I will get to continue doing it. Thanks to amazing people who worked on our games with us and our community of friends, who supported us along the way. I hope for many more years of working together.

Aesthetic-Driven Development: creating Merchant of the Skies from announcement to Early Access launch

Despite developing games full-time for three and a half years already, I am still discovering the best approach that I want to take while developing games. I run my studio together with my partner Helen, who makes the art for our games.

In October 2018, I’ve told her, Helen, to just draw whatever she wants and put it on twitter. Ten months later, we have published our game, Merchant of the Skies, into early access. It is a direct result of that decision. I’m going to tell you how this helped us make the most successful of our games so far.

How It Started

October, 2018. I’m finalizing our previous game, Rebel Story, port to consoles which mostly requires code changes and not much art assets. Helen does not want to idle so we decided why not, let her just draw things she likes and just put it to her twitter / portfolio in case it helps her to land some contract work later.

So she drew Octobit and Pixel Dailies images. The results have been varying.

Some pictures were well received. It made me think: what if we can use them as a style/concept for a new game?

Not all images are drawn equal though. If we try to put them in the game – we need to make sure that we can effectively create the assets of the same style. The temple on the left has distinguishable art style that is reproducible. The tweet with the ruins to the right got way more likes, but it is super-detailed, so it means that we’ll need way more time to draw similarly-styled sprites.

Side-note: for every game we decide to work on, there are 6 prototypes on average that did not work.

Market(ing) Research

As soon as we agreed that “we can do something with that temple”, the mockup /  concept / mini gif production process started. There was no game at that point.

Example of a gif that we made. Took roughly 4 hours for me to program in Unity.

via Gfycat

In the end, we made three tweets to see if the style resonates well.

All three tweets were well-received (based on our own standards). That made me think that “self-marketable” style could be a great asset. I suck at marketing so if the images make people want to reshare them, then we are on to something.

Gameplay

This is how Merchant of the Skies really started. I’ve had an idea of the sky-faring strategy game before, but I could never come up with a proper gameplay. This aesthetic style had its own constraints and actually enhanced my creative process. Essentially, this style gives following limitations:

  1. 2d-only space. Can only scroll left and right, not much options to put buildings above or below.
  2. If we want it to be strategic, how do we actually establish multiple buildings / production chains effectively? If we place too many, then scrolling and finding the right one becomes an exhausting task
  3. How do we enforce variety? That’s when the idea of “global” map with smaller islands comes in.

 

 
In the end, we decided to have a world map and different island hubs (each <6 different buildings/tiles) that player can visit. Each of those hubs either offers a base / company that player builds or some world event / quest.
Sounds easy in hindsight, but it took me quite some time to figure out how I want it to play. To me, gameplay should always win over style/aesthetics. If the game is not fun to me, then I scrap it.

Building the audience

So we’ve decided on the gameplay, but the game is not yet announced. We are in the process of making playable prototype with minimal asset amount to make a store page. Some people advise _not_ to post anything on twitter until you announce the game. We are a smaller team without large reach, so I’ve decided against this approach and essentially started putting most of the things that we were coming up with. Helen’s twitter audience grew from ~500 people in October to 1200+ people in February. Coldwild Games twitter account also started to see gradual organic increase in audience. It’s not a huge amount (we were under 2k) at that time, but some people who followed the progress closely started to appear.

Announcing the game

On Mar 7, we were ready. Steam page has been published, we’ve made two separate tweets at different times, sent press-releases (mostly ignored) and made reddit / imgur posts that got minor traction.

Helen’s tweet got more attention than mine so I’m still jealous about it, but it became clear that twitter should be our main source of announcements at that point. Everywhere else there was not much traction.

Gifs work much better than images.

Wishlists

Now, another curious thing is the Wishlist graph. We got a bit more wishlists that we wanted before the launch (slightly over 5k). It did not look like it would work out until our second trailer announcement and things also got better after summer sale and right before launch. Anyway, the chart and spikes:

To make it short:

  1. Announcement
  2. Second trailer (with relevant tweet), see this:
  3. Brief Rock Paper Shotgun mention
  4. Devlog / Imgur post about resource gathering / Reddit post with the same content
  5. Summer sale. Meh. Period of depression
  6. Carrot tweet
  7. Third trailer announcement. Yes I made three trailers.

Basically no secret formula here. Hard work at making content pays off. But it does take a lot of time. Roughly 30% of development time went into preparing social media posts / addressing the audience / spreading the news.

Mentorship

Extra shout-out goes to Weather Factory studio for taking me under their wing 🙂

To make it short: you already need to know what you are doing and have a vision of your own. I had the work of my studio planned beforehand, but I needed (and still need) sanity checks when it comes to the details. I.e. handling the marketing / press / pricing / working on game design. Lottie, Alexis and Claire has been able to selflessly help me out with that.

Apart from that, all of them provided informational support (such as retweets / mentions), even though they did not have to. It has been a substantial help over our own marketing efforts.

In any case, I can highly recommend finding yourself a mentor (after you are sure you want to go pro and actually finished a few smaller games to at least understand what you need from the mentor).

In case you want to learn more about Weather Factory mentorship, you can read about this on their website.

Release Prep

Two weeks before the release, things get hectic, so I always have a checkbox of things that I want to do. This release was no different. The timeline for me this time:

Two weeks before launch:

  • The game should be ready with no major changes planned. This is for your own sanity.
  • Start sending game to youtubers
  • Add game entry to Giantbomb website so that Twitch streamers / youtubers can select it if they want
  • Reddit / imgur posts / awareness post anywhere you can
  • Write a roadmap what to expect after development. It allows you to arrange things in your head, understand your priorities better and actually be a reference for your players.

One week before launch:

  • Start a countdown on twitter (post daily image)
  • Arrange front-page broadcast with a streamer (in our case the folks from Rocknight Studios were extremely kind to help)
  • Write articles explaining game mechanics, you can use  them for both promo and to quickly give answers to new players
  • Schedule reddit facebook ads
  • Schedule other game discounts on Steam
  • Arrange cross-promotion (David Stark from Airships: Conquer the Skies was very kind to reach out and offer to do a cross-promo campaign with referencing each other games).
  • Schedule twitter / facebook / mailchimp posts in advance
  • MAKE SURE TO KEEP PRESSKIT UP TO DATE – youtubers actually use those for thumbnails. If you are lucky to be noticed by press, you will 100% need it so don’t be late and just set it up in advance.
  • Buy a keymailer subscription and check key requests daily

Overall, I’ve sent >150 keys to youtubers and streamers and set an embargo on Release Date, 30th of July. 4 people broke it, but I reached out and asked to unlist the videos until the date and we actually resolved things peacefully.

Release Day

The stream started a bit earlier than we’ve pressed the release button. Helen was there to talk to players (and Jānis and Matējs from Rocknight studios). I’ve been keeping tracks in other social media and responding to them.

As soon as I pressed that green release button, I’ve made imgur / 9gag / reddit / facebook posts. Reddit post got a lot of traction but got removed due to self-promo rules (I comment a lot, just don’t post much apart from my own games). imgur post was pretty much unnoticed.

The ads went in for 1-2 days. I mostly posted them for my facebook followers and their friends to see and to a small related subreddit. Ads are not very effective by themselves for lower-priced games (or so I think), but they can help with the traffic burst at first.

All in all, HAVE THE CHECKLIST, you will be panicking / stressing out too much and the to-do list absolutely helps.

Also, ask your friends do the reddit posts.

Sales / Results

We did not get into trending. I don’t think the results / coverage was enough. Over the first week, we sold ~2.5k copies. Not great, but enough to keep us going to actually finish the game while paying ourselves minimal salaries and not worrying about taking up freelance tasks just to sustain ourselves while making our dream game.

In short (and to a surprise of noone), the youtubers / twitch streamers remain a driving force of sales. The bumps on the graph match the US workdays (most of our audience), but are also noticeable  we had our game covered by a larger youtubers (this could be an article by itself). I’ve used Keymailer to accept the request and also wrote personalized email to the ones I like (or just to a bigger ones).

Rock-Paper Shotgun mentioned our game twice (the review is actually well-written and mentions the games flaws without actually fixating on them too much). We had a small bump of sales because of it, but youtubers affect it much more. In any case, for us it was not about the sales but about the principle: it was an honor to be mentioned in a famous gaming magazine. Something that seemed unreachable for us before that.

Post-Release

The release has shown that players are expecting more content, but I’m happy with the level of polish that we had on launch. My own principle is to make the game feel as good as possible for Early Access and expand horizontally afterwards. Basically it’s better to have one polished level rather than two unpolished ones.

Right now I’m mostly working on content updates, but first games were different: catching and addressing bugs as soon as they pop up and answering community (>50% time).

Even after two weeks post-release, handling community discussions / answering the feedback still takes 25% of the work day. It is a hard work, but it’s very rewarding to get the bug reports and improvement suggestions. We are very grateful that people give us chance to make the game better.

What next?

I think I’ve already surpassed the proper word limit, so all I’m going to say is that we will continue working on Merchant of the Skies and make sure players are going to get the best possible experience on full launch. Thanks to all that made it possible and I hope you’ll enjoy the game even more when it’s done.

Stay tuned for a full-release post-mortem after the game is done and shipped fully 🙂

 

GDC Impressions, Eastern European style

Me, July 2018: I should apply for GDC scholarship so that I can complain to my friends because I never get chosen for things.
Game Industry Conference, September 2018: You are accepted for GDC Scholarship
Me: Oh. Cool.

So yes, I went to GDC this year. Thanks to the organisation by “Fundacja Indie Games Polska”, sponsorship from Google for Startups and co-organizing from Game Industry Conference, I was able to stay in a hostel in San-Francisco and get a GDC Conference + Summits pass, entirely for free. It would be unthinkable for me to go otherwise, but alleviating these costs really helped me to go.

In this post, I’ll try to go through the highlights of the trip and my first experience at the GDC and things that a lot of smaller developers can relate / look at the conferences.

The City of San Francisco

The first day in the city felt like paradise. I went to see the Fisherman’s wharf through Chinatown and then to the Golden Gate bridge. The sun was blazing hot (I left a feet of snow back home!), the city is located on a lot of hills and the architecture is rather unique. The parks are beautiful and the animals you meet is not something that I could encounter in the Northern/Eastern Europe. I saw the parrots on the tree! During one of the days I went to the Aquarium: without a doubt, it was a life-changing experience since I’ve never seen something like that. Schools of fish / crabs / medusas / sharks / rays swimming freely and you can observe how they behave.

Next days was more of a wake-up call because I ended up exploring a city center. There are a lot of homeless / mentally ill people. Sometimes it felt like a boring dystopia. Some citizens of San Francisco actually crowd-funded the campaign to avoid building the homeless shelter in their area. I have been warned about the homeless problem before going, but I’ve thought that I’ve seen it all, after travelling through Eastern Europe and living in a country that survived the collapse of Soviet Union and went through relatively noticeable economic decline at one point afterwards. Boy, was I wrong. There are homeless people at almost every corner of the business district; you can’t walk a block without encountering a homeless person. I’ve seen open drug use near the Moscone Center and a lot of syringe needles on some streets. The highlight was the poster of a robotics conference with a bunch of homeless people sleeping near it.

In the end, I think the city is full of contrasts: there are both really good and really bad places to be at. Despite the issues, I’d recommend visiting the city at least once. The variety of food is also not something to be missed due to the multiculturalism of San-Francisco.

The GDC itself

So the whole event is not as hectic as it might seem. The first two days, the expo hall is closed, so you mostly go to the talks or roam around and try to get to know people. The expo opens up on Wednesday and you get one more venue to explore, full of different publisher / engine / game booths. It’s an interesting experience by itself because you can meet like-minded people, see the games that you already know and say hi to the people you’ve seen before. On Friday, the expo is open only for half a day, but talks occur until the evening. Tuesday and Wednesday felt like the busiest days, while Friday felt like “we’re all tired day.” Still, every day was really cool.

The talks

I went to a few talks, but not many. Mostly to the ones whose authors I admire and want to know better (so I can catch them post-talk). Other than that, you can actually watch everything at GDC Vault afterwards so it’s better to spend time talking to people.

Nonetheless, some cool talks that I enjoyed in no particular order (look them up in the vault):

The parties

A lot of socializing actually happens after Moscone Center closes. Every evening, there are multiple parties going on. Without suggesting anything specific: don’t go to the big ones with open entry and no limit. Loud music is also a hard pass for me. I wanted to socialize with the peers and get to know people better. There are also daily events that are targeted at a specific gamedev professions. Without disclosing too much: I went to 5 private events and every one of them has been worth it. The public meeting (Marketer coffee) was also very good because these types of meetups define the roles and expectations of people you want to see there.

To find the relevant parties:

  1. Use the GDC party list.
  2. Write a letter to your partners / platforms / tools that you use and ask if they are planning something. You might not get invited, but you’ll establish a closer personal contact which seems like a win to me.
  3. Join GDC discord and watch what people are saying, there are some sudden meetups happening in Yerba Buena gardens all the time.

The socializing itself and is the event worth it?

First of all, I don’t think that you are going to die / be unsuccessful if you don’t go. There were discussions whether hosting the event in SF promotes accessibility (due to high prices / US entry visa), but if the organizers actually live there, it makes sense: you wouldn’t go to another city just to host something. Or you would, but it’s much more inconvenient.

Next, and I’m speaking for myself, in cases of events like these you never get a direct value back. Getting to know people is a long-time thing. Very few people in your life will e-mail you and ask: “I need this job done for me, I’m paying you X” or “you know what, I’ve randomly saw your game on twitter and I want to publish it” or “Can you suggest me someone who can do X?” – it takes time to know you and you can’t handle relationships as one-time business transactions.

So in a way, the event is a catalyst: if you are a successful developer, it’s easier for you to find like-minded people (or rather get meetings with them and get their replies) and meet with the platform holders. Your opportunities multiply. If you are not there yet – people will be polite but a lot of time you can see when they want to excuse themselves from the conversation, but the ones that stay can genuinely lead to meaningful connections. I felt like I’m on the line: from one side, I’m developing games for 3+ years already, on the other side I’ve never had a hit yet. Despite this, I could connect with people in equal situations and actually (hopefully) make this a beginning of future friendships.

Now, whether you need this particular catalyst is another question: in the end, I met some cool US developers and met with some platform holders which is a good time investment to me. I knew why I was going there and I tried to book the meetings I wanted 4 months in advance. But often you don’t necessarily need to afford to go to US for that: if you are starting out and are in Europe, Game Industry Conference in Poland is definitely a good start because it features both experienced and beginner developers while having a lower cost of entry (I might be biased here, but I was planning to go in any case before I was accepted for the scholarship; my other friends who went were really happy about it too). In a way it was easier for me to connect with people there because the pool of different people wasn’t as huge. Nordic in Sweden and GameOn in Lithuania could also be a good start. Basically, if you don’t have a hit or you don’t have a plan of what you are going to do there, look for the events closer to your home. Basically: KNOW WHY YOU ARE GOING THERE. Otherwise it will just be a cool vacation. YMMV.

Costs:
Plane Ticket to SF and back: ~$900, but I had to do a non-same-ticket transfer which was a huge stress
Expenses while staying (lunches, transport, a few small gifts for friends):  ~$500
Travel Insurance: ~$70

In the end it was roughly around $1500. A very significant sum of money for me, but not life-altering.

In short: I’m going to do it again, but only after I make a successful game and can afford the full trip with the ticket. But with GDC (and all conferences in particular), every event is a step in the ladder towards the next one, you get to know people and enjoy their company, then your social circles expand and you meet more people and find new friends / gain more experience / become more aware of what’s going on in the industry. To me, going to conferences because of that is absolutely worth it.

Lazy Galaxy: Rebel Story

Lessons and statistics after three years as an indie developer

A year ago I’ve published an article about surviving two years as indie developer. On 26th of January 2019, my company turned three years old. 2018 was a year of changes and new revelations, so I’ll try to outline them here. I hope these things will give you a perspective if you are only planning to go into indie / starting out.

My background and current situation: I am 29 years old and live in Riga, Latvia. After doing web development, I went to work as C++ dev for 4 years and then quit my job to start making games. The first year was all about libgdx, until eventually I moved to Unity. My studio has one other employee apart from me. In the end of 2017, we released a _relatively_ successful indie game Lazy Galaxy which combined incremental and RTS game mechanics.

I’m a bit conscious of writing these. If you are a seasoned indie developer – I think you might laugh at my advice/experiences. But if you are thinking about starting out professionally – I’m hoping this could save you some pain. Here goes.

Success of one game does not guarantee the success of the next one.
I guess this is obvious. Still, subpar launches will affect you in a way even if you rationally understand it. When I was launching Lazy Galaxy: Rebel Story, I was fairly confident that “same franchise, different genre, but I’m going to make it.” Yeah, it did not go great even though it’s my highest-quality game so far when it comes to production value.

Aim for niche. But not super-niche niche.
There are too many variables in why Lazy Galaxy release worked and Lazy Galaxy: Rebel Story did not.
I think the incremental game PC market is underpopulated. I also I added a good twist (RTS battles).

For Rebel Story, I went for a SHMUP genre (there weren’t many popular shmups released in 2018 at that point, so I felt I had a chance). The niche might have demand, but it’s way smaller than incrementals.

Do your market research! If your gifs are getting 5-10 likes on Twitter during #screenshotsaturdays, it’s probably not the project you should be focusing on. Same goes for finding a publisher. If you want a publisher and everyone keeps rejecting you – maybe (but not necessarily) you have a risky project on your hands. YMMV.

Lazy Galaxy

Project Scope

Rebel Story took 4 months to make and 2 months to port to Consoles. So far it’s a loss, but it’s (hopefully!) going to be OK in 6 months if I combine the sales on all three platforms. It would not have been OK if the project was longer and if it required more manpower. This is just how it is.

For now I’m still sticking to 6 month projects. Lazy Galaxy was 10 months. But it was that way because it had traction at the release, and I could see that I could keep up with the development without going bankrupt. As soon as sales stopped, I did one more month of updates (adding features on a game that left early access) and moved on. I’d love to heave a project of my life, but it was not it.

I think it’s sensible to not spend more than 6 months on a game if you don’t have enough sales or funding to warrant it. No, that does not mean that you need to abandon your games. That means that you need to have a feature-complete game that could be made in six months. In addition to that, you need to have a plan for how you would update and expand it if it goes well. Be ready to embrace success, but budgeting for failure is also important. You don’t want to go broke if one of your projects failed 🙂

Early access launch is not necessarily “the main and only launch”
At least for small and mid-size indies. Back in 2016-17 I remember people telling me that the early access launch is the main thing that matters and you won’t have a second chance on “normal launch”. I don’t find it to be true, at least not anymore. In my case, Lazy Galaxy got more sales during the launch month than it did when it got to early access.

Which brings me to the next point.

The game release is a sequence of decisions.

Don’t stress it. I can’t pinpoint that one decision that would make the game flop or bring it to the top. I see devs (myself included) worry about release dates, steam tag,  twitter presence, handling early access or “you name it” all the time.

Right now I think that if you mess up in one of these points, things can still work out. There is luck factor involved. You can also do some things right and still fail. Doing things properly just gives you a higher chance to succeed.

Lazy Galaxy was getting 15-20 likes on good #screenshotsaturdays. But it still did OK on launch. Lazy Galaxy had 14th of December release date (bear in mind that it was easier in 2017), but it still did OK. You might luck out, but why risk it if you know better?

Off to space!

Mentorship

In the second half of 2018, I managed to get a help of a mentor! Lottie Bevan from Weather Factory kindly agreed to have short monthly calls or email consultations whenever I needed help. I want to say that this really helps to add perspective to whatever you are doing, even if you feel like you “have it figured out. It really helps to discuss your issues with someone, and getting honest feedback. It has been a tremendous help and I really appreciate it.

Statistics? Data?

First, the (seemingly) depressing graph. Company revenue ( from both freelance and our own games ) vs expenses.

As you can see, the 2017 revenue mostly went to cover our expenses in 2018. 🙂 I am still paying myself close to minimal salary, but it felt more stable this year (I had to skip a few months in 2017 and almost every month in 2016). There is a nitpick though.

Here’s the revenue from my games compared to the income from freelance activities:

2016-2018, Income from freelance vs income from games

2016-2018, Revenue from freelance vs revenue from games

Our own games made the company more money than freelance in 2018. Isn’t it cool? Still not enough to sustain 2 people, but I’m pretty excited about what’s to come in 2019. Seriously.

Bear in mind that I was working on my own games every year at least 50% of the time. 2016 was a terrible year money-wise. My games were subpar to say the best. 2017 was OK, because I could find side-jobs and get some game-related freelance gigs, which I consider to be a step forward. The release of the small arcade game, Frequent Flyer, helped as well.

At the end of 2017, I launched the early access of my game Lazy Galaxy, and released it in March 2018. This proved to be my most-successful game so far. It is fair to say that it broke even and is even bringing a small-ish profit now (my sales dropped by ~70% after October changes though, but maybe it’s not directly related? the game was rather old at that point).

Consoles are pretty cool.

Lazy Galaxy: Rebel Story did not do well on Steam, but consoles fared much better. Both Nintendo Switch and Xbox outsold Rebel Story on Steam. Even though it’s not a huge success story like late indies of 2017, the game did considerably better on consoles.

I still have no idea how consoles pick which developers they want to approve or deny. Even if I did – it would probably be under NDA. I think credibility and released games matter: some people I know could not get in even with better-looking games (compared to Rebel Story) because this was their first game.

In any case: if you can – diversify. If you can’t get to consoles, try to design your games to support phones. Every bit helps. Maybe not so much with phones, but it gives you extra exposure and mobile games are much easier to show at social events.

Lazy Galaxy: Rebel Story

Lazy Galaxy: Rebel Story

GDC

I got a scholarship for GDC, covering the attendance ticket and accommodation in SF. Even though the plane tickets are not cheap, I’ll be able to go this year! I’m both excited and scared, but I am definitely looking forward to it.

If you are going too – drop me a message / email and I’ll be happy to meet up, talk about indie development and chill 🙂 I haven’t been to US before so I’m anticipating some great new experiences.

What I can conclude from 3 years:

  • Persistence pays off. If, in the midst of 2016, someone would have told me that my game will break even, I’d bitterly laugh. Even with “Indiepocalypse,” things are getting better.
  • Even if I am not exactly there yet, I believe that success takes patience. But you also have to look at growth. Dreaming about it is absolutely not enough. You have to be the biggest critic of your own games, even if you are super-proud of them. Lazy Galaxy: Rebel Story is my best game so far, but it’s not the most-successful one. Objectively speaking (and it pains me to say that), even though we wrote 40 full pages of text for character interactions, not many people pay attention to it and the game becomes really forgettable for most people. I am the one to blame for that.
  • I think the tools to make games improved a lot. If you are a hobbyist, you are going to have a time of your life. Just go for it! If you are planning to go full-time indie, remember to have a backup plan and (preferably) a potential side-income.
  • I absolutely don’t regret going indie, but there are some tough days. In essence, I changed security for excitement. My personal income has dropped 4x compared to before I went indie, but I am feeling as fulfilled as ever. I start most days with joy and pleasant anticipation. The financial instability is crazy, but it also motivates me to do as much as possible.


Goals for 2019

  • Double the revenue from my games. It isn’t huge to begin with, so it’s definitely doable if I make some cool stuff.
  • Related to previous one, release the biggest project I’ve ever done so far.
  • Drop my weight to 70kg. When I went indie, I was at a healthy 66kg. At the end of 2018, I hit 84kg (185lbs). My lifestyle choices were not exactly healthy, because I stayed at home so much.

I am fairly sure that not everyone who goes indie will make it. Maybe I’ll be one of those who won’t. Who knows. My days are often filled with worries about the future. But they are equally filled with exciting moments of anticipation and seeing the fruit of my labor. One thing I’m sure of is that things get better with time. If you are willing to face your mistakes and learn from them, you’ll eventually get better. It does not guarantee that you’ll make a hit game, but it gives stability over time. Our games suck less, even if they are not world-class just yet.  I’m hoping to devote my 2019 to making games and I’m looking forward to what we’ll have to show for it in 2020. Peace and may you have a good 2019!

Lessons learned after two years as an indie developer

As the release of out third game, Lazy Galaxy, is coming tomorrow, I thought it would be a good time to take a break and take a look on two years of my life as indie developer.

Why/who should care? I don’t think you’ll learn something here if you develop games for 2+ years. For those who plan to start or are in the gamedev process, I hope this helps.

Short info on my background:
28 years old. Live in Riga, Latvia. Started as a PHP developer -> Worked as C++ dev for about 4 years -> quit my job and started developing games on LibGDX,for about a year, and then moved to Unity, for another year. Right now I own a gamedev studio with 2 other employees.

Should I quit my job to become indie:

A lot of people say “don’t,” especially if you don’t have any successful/published titles. I was one of the people that did that anyway. For me, the logic is simple: as you age, time becomes more important than money. If I spend 2 hours after every work day, there’s no way I’m going to be good as the guy who spends 10 hours (in case he works as effectively as I do). I sucked at gamedev two years ago and I’m not great today, but I’m doing much better. You spend time, then you improve. I started late, so if I want to be as good as others – I need to practice much more and work much harder.

My mother said that if I quit my main job, then it will be much harder to find a new one if I take a break. The thing is, your own company is not the same as taking a break, as long as you have something to show for it. If you complete projects – you are essentially working for yourself. It’s as serious as you treat it. I still get job offers from other companies, but for now I turn them down.

Basically, if you feel like you need to quit your job to do indie – quit it after you have stable professional background, possible freelance contacts and some money stashed.

The money:

I think it’s reasonable to expect that you won’t be making any money for the first year. My second year was easier (people started to notice my work and I got some projects for my company out of it). Expect hard times and have a long-term plan, together with possible short-term freelance opportunities.

The opportunity costs over those two years for me were probably potential ~$40k lost. In Latvia, you can comfortably live 3-4 years with this amount of money. We’ll see if it pays off in the future.

At first, I had to take some freelances to sustain myself further. After a while, I was able to get game-related jobs occasionally.

Health:

If you stay indoors for a long time, you’
re going to feel weak and demotivated. Fish oil helps, but try to spend more time outside. You need it.

Project Scope:

If your first game takes more than 6 months to make – reconsider. Our current game, Lazy Galaxy, took 9 months for my team to make and I can see that it’s already starting to strain and exhaust us. Our next projects will be 3-6 months tops. As you are starting out, it’s crucial to constantly get feedback. Making a game 2 years and then publishing it = in general is a too big risk for a small possible reward. I know, I know, there are exceptions and you can name them. But in general, it’s better to release four small polished games in a year than one, because you’ll be learning about game releases and getting the feedback much faster. Development is not that much of an issue (with code snippets from current asset stores and online communities you can pretty much do anything an indie wants). The biggest problem will be moving your project forward, getting noticed, marketing and building a community.

Marketing The Game:

I think that’s the most important one.

First, gamedev forums and subreddits are not really your audience. It’s easy to share there, but this probably won’t be the thing that helps you promote your game.

A side tale: in school, I was asking lots of girls about how they want to be courted by a guy. Often I got answers like “you need to compliment themor “give them gifts.Obviously, when I tried to do this, it never worked. Were they lying? Not at all, it’s just I’ve been missing an important detail: when they talked about flowers, compliments or other gifts, they were imagining that they already _like_ the person. They don’t like you because you give gifts or flowers, they like you first for their own reasons and then you give the gifts.

It’s the same when you try to promote your game: I think I’ve posted about 50 entries in screenshotsaturday over 2 years and not one of them took off. Out every game release has been followed by my own written press-release, always ignored by big press outlets.

The thing that made me think were youtubers. Lazy Galaxy did not take off on twitter, but I saw something that I did not see with my first two releases. A few (real ones, not fake accounts) 5k+ sub youtubers contacted me and asked for a copy to play. Then made videos. All by themselves. Others were more responsive than with out previous game when I messaged them.

Imagine that you’ve read one of the good posts about marketing your game. You’ve learned a lot. So you start doing it, expecting feedback. But it does not come, people mostly ignore it. You are confused, because you’ve read and done everything correctly, posted on twitter, informed the press, sent the game to influencers. When it comes to marketing – even if you do everything “by the book,” it’s not a gurantee that the game gets noticed. Aesthetics are the key. Those make your game noticed it (and then allow people to actually play it). Your game has to have style. It won’t work on a game that does not feel great (judging by the screenshots, not the actual gameplay).

In truth, you are telling people about something that they already like (but don’t know it yet), so after that they can take the steps and cover your game. If you regularly do all the classic stuff like screenshotsaturday, influencers and press with a pretty, solid, game – you will get noticed. I am 99% sure. I thought that it was mostly a matter of luck, but right now I’m just confident that you need to make a good game with visual appeal.

That translates to the next point.

Artist:

You need a style for your game. You need an artist who can work with you. All elements must feel like they belong to one style. On my first game, Blades of the Righteous, I made a mistake of commissioning arts from different artists or just buying and assets and that affected the overall feel and quality of the game. Don’t make mistakes that I did. Style matters, if you get assets – don’t get absolutely different ones from different creators. Even if they look cool separately, there’s a high chance that they won’t fit together.

At the end of my first game I was joined by my girlfriend, Helen. She became an artist and we’ve been making the games together. It has been a great journey of learning and we often feel like we’re at the beginning of our way, but we’re sure that we are moving somewhere.

During the third game, my friend Michael has agreed to work together. He’s been a valuable help due to his attention to detail and ability to thoroughly connect artistic and code sides of the project, something that I often could not do as well as him. That taught me that diversifying talent is important. He’s great because he can do something that I can’t do as effectively, and that gives me time to talk about the game, promote, translate and do QA checks.

Mentoring:

One of the things that got suggested by some articles when I started out was “find a mentor.” I’d love something like that, but I’ve emailed about 20 different experienced people from the industry to see if they can have 15-20 minute calls monthly/biweekly to mutually discuss gamedev things, but I never got a reply. At the current stage, I still need a mentor for some questions, but I don’t think I can get one without personally meeting them somewhere first.

On networking and having a gamedev business

Chances are, there are gamedev enthusiasts no matter where you live. Go to your local meetups, even if you are introverted like I am. Don’t go with some “let me be an extroverted business” guy mindset, but go to meet people with similar interests. Chances are, some of them will become friends. The conversations won’t feel forced if you are talking about the subjects that interest you both. The people will probably be more open and honest about your games too. You could also meet people that need your help, so that’s also a plus.

Make an effort. Especially if you are living in US / Western Europe. I think you have a great opportunity to talk with the people who achieved something in the industry, an opportunity that lots of us from other countries don’t have.

On the other hand, I think that (relatively) low living costs in Latvia help. I genuinely think that if you are a developer from US that’s working on established indie project – it might be worth going here to reduce your living expenses to ~$1k/month.

In Summary

I think that’s all I had in mind. I’m probably going to write a separate postmortem on Lazy Galaxy, but I wanted to make this one more personal/general advice oriented.

Overall, I see gamedev as something that I want to do for my whole life, so we’ll see if I manage to build a sustainable company in the end. Those been two tough years, but I’m optimistic about the future and actually looking forward to what happens next.

If you want to follow me: consider adding me on twitter, https://twitter.com/ColdwildGames



Future Plans

Why My Tutorial Sucks – Lessons Learned after trying to make the best tutorial

If you showcase your game on conventions – you know how important it is to hold the attention of the user and make his experience playing the game trouble-free. The way people play your game during the event actually allows you to witness how people are going to play the game at home. This opens up a path to gather real-time feedback and, of course, improve the on-boarding process for the game. You can also miss some potential fans if your game does not look appealing or understandable in the first minute.

A bit of a backstory for context: The last 4 months of my life I’ve been spending all my spare time to develop the game with a working name “Lazy Galaxy.” It’s a game where you control an evil (albeit lazy) alien race and try to conquer the universe. It’s an experimental mix between RTS and clicker/incremental game: you build a base, then you send ships to space. The ships can be controlled as in RTS games, but this is not necessary. Since I’ve never seen something like that anywhere before (let me know if you have, I want to play this game!) – there’s a challenge of introducing players to this sort of game.

This might seem like a very specific case, but I’ve learned a lot of stuff that is going to be useful for a lot of games. Over the last two months, I’ve spent a considerable development effort to ease up the introduction into the game. Here’s how it went:

Experiment 1: Text Tutorials, Smaller Convention

I’ve actually found out that I’m going to an expo only two weeks before. The game seemed complex due to worse state of the ui in early stages of development. We knew that nobody reads texts – but how bad can it actually get? We’ve drawn cute mascot portraits, made the tutorials fit the narrative and made “bouncing buttons” that highlighted where the player should be clicking. We also made UI elements appear as the game progressed, instead of throwing everything at once.

Lazy Galaxy: Old Tutorial, Walls Of Text!

Needless to say: I think only three or four people read the texts. Most people left in the first minute of the game. That’s understandable. Next time you are at a convention, try looking for an unknown games with average amounts of text explanations and play them. Are you capable of going through all instructions? I think the whole event atmosphere creates a “rush” feeling, because there are plenty of things happening around you. The event escalates the dislike for text tutorials, because no one wants to sit in one place, hindered by walls of text, unbeknownst about what are they going to get in the end.

Players also often missed our bouncing button cues, even though I’ve made all parts of the UI appear after the relevant feature becomes necessary.

Five people left their emails at the end of the day: two of them were friendly developers. Only the remaining three of these were actually people that enjoyed the game and wanted to know more.

Experiment 2: Pictures instead of text. Propaganda posters. Arrows that show where to click.

For the next convention, GameOn, biggest game convention in the baltic states, we understood that we needed a much better preparation. I’ve spent two weeks trying to figure out the best way to present information.

The first one to get added was the floating cursor helper. It showed where player needed to click, so the game would be accessible even to the players who don’t know english.

The next one to go were the text: most of the texts were thrown away. Only a simple reminder of what needs to be done (i.e. gather 20 minerals). It blinked for the first two seconds to indicate that the task has changed.

Lazy Galaxy, new tutorial: propaganda posters!

Another idea was to replace the texts with iconic images, to show how the game operates and what needs to be done. To avoid making it look like a chore, we’ve added some sort of a “propaganda” posters to the right side of instruction images. Those were designed as a references to the real-world propaganda posters. The idea was to make a game-world feel a bit more alive and add some lore. Feeling that this should do the trick, we went to the convention with high hopes.

Success?

Actually, only partially. Twenty five interested people left their emails. Not that much, but the results are much more successful than the previous ones: we genuinely found people who were interested in the game, but I’ve noticed that I often had to step-in and explain the core principles that I want to do. In convention, it might be OK, because people actually want to communicate with the developers directly and essentially you are the one “selling” you game idea. But imagine if they were playing my game at home? Nobody would be there to say “Hey, you can actually upgrade your buildings to increase an income.”

Propaganda Posets

Here are the issues:

1) While the players stayed at the game much longer, the tutorial was unable to explain the principles of the game. The players heavily relied on the arrow pointer to show what to do. They progressed through the game, but only because the game explicitly told them what to do. They could not utilize the mechanics as those were introduced: a lot of players missed the concept of building upgrades, how those increased the flow of resources and made the gathering much easier. This is not their fault, but the fault of such design. From now on I understand that arrow/clicking tips have their place to be and are effective at directing players, but should be used with caution. The trade-off: players are able to progress, but unable to learn how to use the tools the game gives them to find solutions to their problems.
2) The images with propaganda posters / instructions got closed as annoying pop-ups. There was not much difference with text popups, although they did attract some initial attention. In the end, this did not prove to be a dependable solution. The small tasks were OK though, and people often look at them.
3) Another thing that affected the first one: the tasks were telling player what actions do they need to do instead of giving them the goals they need to achieve.

Next Steps, Solutions?

Think about the following objectives and which ones sound better:
3.1) “Build a mine” or “Establish Metal supply”
3.2) “Click on 20 asteroids to gain 20 metal” or “Gain 20 metal to start building a base”
3.3) “Research Space Travel” or “Go To Space”

I’m sure you can find out the similar action vs goal-oriented tasks in your game. After witnessing the results of the first option, the second option seems like the better choice. It needs more visual cues, but this gives players the goals they need to reach instead the actions they need to take. Instead of chewing your food in their place, you give them the opportunity to taste it. Give the opportunity to explore and experience your game, instead of letting them feel constrained.

Future Plans

Here’s what I’m trying to do now: the game is going to have a special “Codex,” a dictionary with lore and it’s going to have a special chapter. There is going to be an article for each one of those instructions (that essentially contains the scheme + propaganda poster). I’m not going to show the propaganda posters right away, the “?” button will appear next to a current objective and when player click it in case he needs help.

The leading mouse pointer goes away for most cases. It is going to be replaced by element outlines / slight shaking that will appear after some time if player struggles to complete his objective.

The small objective window is going to stay. The objectives are going to be task-focused instead of action-focused. Example: instead of “Upgrade Solar Panel,” there’s going to be “Get your total energy income to 2 per second.” Essentially, allow the player to find his own solutions and feel great about this. The “feel great” part will need more work: visual cues need to be subtle, instead of “In your face, doing everything instead of you.” Hopefully, this will have better results next time.

What would you do? What do you think could work in this case?

EDIT: In case anyone is wondering, here’s how the gameplay looks (explained by yours truly)

Developing Multiplatform Game with LibGDX, part 27: Android controls!

Implementing Android Controls

Technically, our game is ready, but in reality, we cannot launch it on Android. This is due to our controls read from Keyboard. Today, we are going to fix that by adding the arrow sprites for Android version.

Here’s 4 buttons that I’ve drawn:

We’re going to place 2 (up-down) buttons on the left side of the screen and remaining two on the right side. Now, let’s declare and load button images:

public TextureRegionDrawable leftArrowBtn;
public TextureRegionDrawable rightArrowBtn;
public TextureRegionDrawable upArrowBtn;
public TextureRegionDrawable downArrowBtn;

Then, at the end of Resources constructor, initialize them:

leftArrowBtn = new TextureRegionDrawable(gameSprites.findRegion("larrow"));
rightArrowBtn = new TextureRegionDrawable(gameSprites.findRegion("rarrow"));
upArrowBtn = new TextureRegionDrawable(gameSprites.findRegion("uarrow"));
downArrowBtn = new TextureRegionDrawable(gameSprites.findRegion("darrow"));

So far so good. Go to our GameScreen, declare new Group:

public Group controlGroup;

We will use it to place the button there (and also disable all of them when the game ends!). At the bottom of our GameScreen constructor, add:

controlGroup = new Group();
gameStage.addActor(controlGroup);
// comment out the next line to test buttons on desktop
if (Gdx.app.getType() == Application.ApplicationType.Android)
{
    prepareDirectionButtons();
}

We create our group as we would create any other actor. After that, we check which device launched our application. If it happened on Android – prepare button! (but if you are testing – comment out the “if” check, because you want to see the buttons on the screen during tests, but players will have no use for them).

Now, we need to define the prepareDirectionButtons function. In advance, I think all of the button functions are going to be somewhat equal (only movement direction, image and button coordinates differ). Therefore, we can make a separate function to create a direction button.

private void prepareDirectionButton(final int dx, final int dy, TextureRegionDrawable img, float x, float y)
{
    ImageButton btn = new ImageButton(img);
    btn.setPosition(x, y);
    btn.addListener(new ClickListener() {
        @Override
        public void touchUp (InputEvent event, float x, float y, int pointer, int button) {
            AttemptMove(dx, dy);
            super.touchUp(event, x, y, pointer, button);
        }
    });
    controlGroup.addActor(btn);
}

As simple as that: create a new button, create a click controller that is similar to our arrow keys (calls AttemtpMove) and then add it to our group. Now all that is left to do is to add 4 direction calls to our prepareDirectionButton:

private void prepareDirectionButtons() {
    // Up-Down
    prepareDirectionButton(0, 1, game.res.upArrowBtn, 2, gameStage.getHeight() / 2 + 2);
    prepareDirectionButton(0, -1, game.res.downArrowBtn, 2, gameStage.getHeight() / 2 - 16);

    // Left-Right
    prepareDirectionButton(-1, 0, game.res.leftArrowBtn, gameStage.getWidth() - 36, gameStage.getHeight() / 2 - 9);
    prepareDirectionButton(1, 0, game.res.rightArrowBtn, gameStage.getWidth() - 18, gameStage.getHeight() / 2 - 9);
}

After that, let’s remove the buttons once the stage is complete. At the beginning of our OnGameEnd call in GameScreen, add the following line:

public void OnGameEnd(final boolean playerWon) {
    controlGroup.remove();

Good! Now we have to make sure that the game launches in landscape mode on Android. Go to our AndroidManifest.xml file and adjust it accordingly (if you have not already):

android:screenOrientation="landscape"

That’s it! The game is going to have the direction buttons now. Try running it on your phone to see if everything’s working as intended.

Multiplatform Gamedev Tutorial

Relevant git commit: https://github.com/vladimirslav/dodginghero/commit/183f5b02b8ddeff08ad71c7681e1ef766ecb50c1

Developing Multiplatform Game with LibGDX, part 26: Music!

Lesson 26 – Background Music

So in the previous lesson, we implemented the game sounds. But there’s no background music and no way to control it (turn it off / make it quieter).

Playing Music is actually pretty simple. Here’s what I added to SoundManager class:

public static Music bMusic = null;
public static void StopBattleMusic()
{
    if (bMusic != null)
    {
        bMusic.stop();
        bMusic = null;
    }
}

public static void PlayBattleMusic()
{
    bMusic = Gdx.audio.newMusic(Gdx.files.internal("music/music" + MathUtils.random(5) + ".mp3"));
    bMusic.setLooping(true);
    bMusic.play();
}

Music does not really work like sounds. We preload sounds, but music is different: since music files can be quite big, they are simply being read in realtime. We’ll simply pick the random tune from music0…music6, set it to looping and then play it. Now, just add a call to start music at the initialization of GameScreen and Stop the music at the disposal.

…
public GameScreen(DodgingHero _game) {
    super(_game);
    batch = new SpriteBatch();
    bg = new Background();
    SoundManager.PlayBattleMusic();
…
@Override
public void dispose()
{
    SoundManager.StopBattleMusic();
    super.dispose();
…

Alright. This part is done. Now let’s make sure we have a button to control the sound. I have four textures with sound button, to indicate different sound/music volume. 0%, 33%, 66%, 100%, called sound0, sound1, sound2 and sound3 respectively.

Adjust our Resources class by adding TextureRegionDrawable array called soundBtn;

public TextureRegionDrawable soundBtn[];

Then, load them:

soundBtn = new TextureRegionDrawable[4];
for (int i = 0; i < soundBtn.length; i++)
{
    soundBtn[i] = new TextureRegionDrawable(gameSprites.findRegion("sound" + i));
}

Now, let’s make a setting for the sound. We could make a Settings file similar to GameProgress file, but since we only have one setting (sound), let’s implement it in GameProgress. If you plan on adding more – I strongly suggest separating into your own settings file.

In our GameProgress file, add a constant to indicate MAX_SOUND_VALUE (that would be 3, 0..3), then add a new static variable called soundVolume; Also, add a save key for it.

public static final int MAX_SOUND_VOLUME = 3;
public static int soundVolume = MAX_SOUND_VOLUME;
private static final String SAVE_KEY_SOUND_VOLUME = "soundvolume";

Make sure to save/load them. In Load():

soundVolume = prefs.getInteger(SAVE_KEY_SOUND_VOLUME, MAX_SOUND_VOLUME);

In Save:

prefs.putInteger(SAVE_KEY_SOUND_VOLUME, soundVolume);

And add a new static function ToggleVolume();

public static void ToggleVolume() {
    soundVolume += 1;
    if (soundVolume > MAX_SOUND_VOLUME)
    {
        soundVolume = 0;
    }
}

Adjust the volume, if it goes over maximum value, just start anew (by disabling it, setting it to 0). Finally, let’s integrate the change to our SoundManager. When we play music/sound, we need to take the value of the soundVolume into account. First, the volume:

private static void playSoundRandomVolume(Sound sound, float min, float max)
{
    if (sound != null)
    {
        sound.play(MathUtils.random(min, max) * GameProgress.soundVolume / GameProgress.MAX_SOUND_VOLUME);
    }
}

Just multiply randomly generated value with soundVolume. Do something similar for music.

public static void PlayBattleMusic()
{
    bMusic = Gdx.audio.newMusic(Gdx.files.internal("music/music" + MathUtils.random(5) + ".mp3"));
    bMusic.setLooping(true);
    bMusic.setVolume((float)GameProgress.soundVolume / GameProgress.MAX_SOUND_VOLUME);
    bMusic.play();
}

Casting to float is important! Otherwise the division happens between two integer values and you end up dividing 1 with 3 and get zero as a result. So you have to be careful with things like these. Taking the current music volume is great. But what if we change the volume during the game? We’ll need to modify the current music volume. Create a new static function, called AdjustVolume. It will call GameProgress.ToggleVolume and then change the volume of the currently playing music to a new value.

public static void AdjustVolume()
{
    GameProgress.ToggleVolume();
    if (bMusic != null)
    {
        bMusic.setVolume((float)GameProgress.soundVolume / GameProgress.MAX_SOUND_VOLUME);
    }
}

That part is done! Now let’s make the sound button. In our GameScreen, add a new variable called

ImageButton sndBtn;

Then initialize it in our GameScreen constructor:

sndBtn = new ImageButton(game.res.soundBtn[GameProgress.soundVolume]);
sndBtn.setPosition(gameStage.getWidth() - sndBtn.getWidth() - 10, 10);
sndBtn.addListener(new ClickListener() {
    public void touchUp (InputEvent event, float x, float y, int pointer, int button) {
        SoundManager.AdjustVolume();
        sndBtn.getStyle().imageUp = game.res.soundBtn[GameProgress.soundVolume];
        super.touchUp(event, x, y, pointer, button);
    }
});

gameStage.addActor(sndBtn);

The idea is simple: change the image after the button has been pressed, indicating the current state of the volume.

Now, if you run the game, you’ll notice that the button is there but it is not clickable. The reason is that we set up our input to GameScreen and not gameStage. We catch events in our gamescreen, but we should be doing it in our stage. Thus, we have to move our keydown function into gamestage. First, remove the InputProcessor implementation from the GameScreen and move keyDown function to stage. Make sure to change the parameters from int keyCode to InputEvent event, int keyCode.

Gdx.input.setInputProcessor(gameStage);
gameStage.addListener(new InputListener(){
      @Override
      public boolean keyDown(InputEvent event, int keyCode) {
          switch (keyCode)
          {
              case Input.Keys.RIGHT:
                  AttemptMove(1, 0);
                  break;
              case Input.Keys.LEFT:
                  AttemptMove(-1, 0);
                  break;
              case Input.Keys.UP:
                  AttemptMove(0, 1);
                  break;
              case Input.Keys.DOWN:
                  AttemptMove(0, -1);
                  break;
              default:
                  break;
          }

          return false;
      }

});

Now, try running the game and pressing the button. You’ll see that both sound/music are adjusted! Awesome! We have a sound button and we can adjust the game volume now!

Disclaimer: I’ve also tampered with game ending functions (not describing them here) to change transitions in GameScreen for html version because it was bugged otherwise. I don’t think that’s relevant to the lesson, but you can check the git commit with full code here (look for endgame boolean):

https://github.com/vladimirslav/dodginghero/commit/223be4e0ecd6518608e0207196b28642b755f8e0

This concludes our second part of lessons, where we polished the game. Next, I’m going to focus on the Android-specific stuff implementation: ads, in-app purchases.

If you want to expand the game idea – think how you can make a special abilities for every character or add more bonuses or stats (gold per bonus picked?). The effects can also be adjusted, floating numbers on damage or when player gains gold. Never stop thinking about the things that can be improved!

Play the game online: http://coldwild.com/dodge2/

Developing Multiplatform Game with LibGDX, part 25: SOUND! And bug fixes.

Minor tweaks

The gameplay is mostly done. One major flaw that remains is the lack of „restart”/”character selection” buttons after the game is over.

Add

eventListener.OnGameEnd(false);

function call in our GameLogic class, exactly once player’s hp reaches zero, right before GameProgress.Reset(true); call. Then modify our GameEventListener and make playerWon Boolean final.


public interface GameEventListener
{
    void OnGameEnd(final boolean playerWon);
}

Modify GameScreen’s OnGameEnd listener function to actually make use of playerWon variable. On our last action (where we get rid of the current screen. Here’s how it looks now:

new Action() {
    @Override
    public boolean act(float delta) {
        dispose();
        if (playerWon)
        {
            game.setScreen(new GameScreen(game));
        }
        else
        {
            game.setScreen(new CharacterSelectionScreen(game));
        }
        return true;
    }
}

After player gets defeated, he gets brought to CharacterSelectionScreen. That’s it. We no longer need to relaunch the game file after loss.

Few bugs:

  • Let’s Tweak our “Start” button and call GameProgress.Reset(false) before the game starts: this will ensure that player gets his max hp (in case we made an upgrade and it raised the hp).
  • After the player wins the stage – make sure to record his hp to currentLives. In our Player.markVictorious function, save the current player’s hp to GameProgress.playerLives:
public void markVictorious()
{
    winTime = timeAlive;
    winning = true;
    GameProgress.playerLives = lives;
}

Sound and Music introduction

Our final (and very important!) part is going to be about music. I’ve got two music/sound packs from opengameart.com:

Converted them to .ogg (smaller size) with http://audio.online-convert.com/convert-to-ogg tool.

I’ve converted the following files from attack sounds:

  • swing.wav -> swing0.ogg
  • swing2.wav -> swing1.ogg
  • swing3.wav -> swing2.ogg
  • coin.wav -> coin.ogg
  • bite-small.wav -> heal.ogg
  • fantozzi-sandl1 -> walk0.ogg
  • fantozzi-sandl2 -> walk1.ogg
  • Fantozzi-SandR1 -> walk2.ogg

Music is left-as is, in mp3, but renamed to music0..music5.ogg. In the directory android/assets/, create music folder and put sounds/music there. Finally, let’s get to coding! In our com.coldwild.dodginghero package, create a new class and call it SoundManager. It’s going to have static functions that controls menu music. Let’s introduce functions that load and unload sounds.

public class SoundManager {

    public static AssetManager assets = new AssetManager();

    public static void LoadSounds() {
        for (int i = 0; i < 3; i++)
        {
            assets.load(Gdx.files.internal("music/swing" + i + ".ogg").path(), Sound.class);
            assets.load(Gdx.files.internal("music/walk" + i + ".ogg").path(), Sound.class);
        }


        assets.load("music/coin.ogg", Sound.class);
        assets.load("music/heal.ogg", Sound.class);
        assets.finishLoading();
    }

    public static void ReleaseSounds()
    {
        assets.dispose();
    }


}

In our DodgingHero class, in our create function, after GameProgress.Load() call, add the call to LoadSounds function:

SoundManager.LoadSounds();

And

SoundManager.ReleaseSounds();

At the end of dispose() function. This will ensure the sounds are loaded at the start of the game and disposed at the end. The easiest ones to implement would be walk / attack sounds. Add those things to our SoundManager:

private static void playSoundRandomVolume(Sound sound, float min, float max)
{
    if (sound != null)
    {
        sound.play(MathUtils.random(min, max));
    }
}

public static void PlaySwingSound()
{
    Sound s = assets.get("music/swing" + MathUtils.random(2) + ".ogg", Sound.class);
    playSoundRandomVolume(s, 0.90f, 1.0f);
}

public static void PlayWalkSound()
{
    Sound s = assets.get("music/walk" + MathUtils.random(2) + ".ogg", Sound.class);
    playSoundRandomVolume(s, 0.45f, 0.55f);
}

Pretty straightforward: pick the necessary (random!) sound, then play it at a reasonably random volume to make it even more randomized. Put the calls to the following functions:

Character.takeDamage()

public void takeDamage(int amnt)
{
    SoundManager.PlaySwingSound();
    timeOfDmgTaken = timeAlive;
    lives -= amnt;
    if (lives < 0)
    {
        lives = 0;
    }
}

And in our GameScreen.AttemptMove

public void AttemptMove(int dx, int dy)
{
    if (player.getLives() > 0 &&
        enemy.getLives() > 0 &&
            logic.CheckMove(player.getFieldX() + dx, player.getFieldY() + dy))
    {
        logic.AssignPlayerPosition(player.getFieldX() + dx, player.getFieldY() + dy);
        SoundManager.PlayWalkSound();
    }
}

Try running the game! Whenever you move or your player gets hit – you’ll hear it. As you remember, we try to split game logic from representation. We won’t be calling soundmanager from our gamelogic, we’ll pass the bonus pickup / attack even to our gamescreen and it will handle it properly. First, add the functions for coin / health pickup:

public static void PlayCoinSound()
{
    Sound coin = assets.get("music/coin.ogg", Sound.class);
    playSoundRandomVolume(coin, 0.9f, 1.0f);
}

public static void PlayHealSound()
{
    Sound heal = assets.get("music/heal.ogg", Sound.class);
    playSoundRandomVolume(heal, 0.9f, 1.0f);
}

In our GameEventListener interface, add a new function declaration:

void OnBonusPickup(byte bonusType);

We’ll call it when player picks a bonus, in our AssignPlayerPosition, right after the check:

if (currentBonus.getFieldX() == fx &&
        currentBonus.getFieldY() == fy)
{
    eventListener.OnBonusPickup(currentBonus.getBonusType());

The only thing left for GameScreen is to implement OnBonusPickup method.

@Override
public void OnBonusPickup(byte bonusType) {
    if (bonusType == Bonus.BONUS_TYPE_COIN)
    {
        SoundManager.PlayCoinSound();
    }
    else if (bonusType == Bonus.BONUS_TYPE_HEALTH)
    {
        SoundManager.PlayHealSound();
    }
}

Run the game, hear more sounds! Awesome. That concludes this lesson. In our next lesson, we’ll work on adding music.

Relevant git commit: https://github.com/vladimirslav/dodginghero/commit/e93739a981ad6da90c2fe69bfce876ae1be045c9

Developing Multiplatform Game with LibGDX, part 23: stat logic and display

Assigning meaning to stats

So far in our game, we can upgrade the character and unlock new ones. But the upgrades do nothing. Let’s actually make them affect the game.

Player’s hp, hp regen, damage from attack and bonus spawn time is going to depend on character level. Go to our CharacterRecord class. Introduce four functions to get each value. We are going to pass the level of the character, the function is going to return the value.

For HP, damage and regen – let’s make linear functions. First, get rid of maxPlayerLives and playerDamage in GameProgress. While you’re there, also get rid of playerLives = 3 in our „Reset” function. Then, go back to CharacterRecord and implement the three functions.

public int getMaxHp(int level)
{
    return 3 + level / levelsForHpUpgrade;
}

public int getDmg(int level)
{
    return 1 + level / levelsForAttackUpgrade;
}

public int getHpRestored(int level)
{
    return 1 + level / levelsForHpRegenUpgrade;
}

Pretty straightforward. Take the base value, add level divided by the levels necessary for upgrade, get result. Looks good. What about bonus spawning? We don’t want the bonus time to go down drastically, so it makes sense to introduce diminishing returns.

public float getBonusSpawnReduction(int level)
{
    int bonusSpawnLvl = level / levelsForBonusSpawnUpgrade;
    return bonusSpawnLvl / (30 + bonusSpawnLvl); // 30 enables diminishing returns, x / ( x + 30)
}

30 is just a magical number. You can make it any value you want. All we have to do is put it into necessary places now.

Go to our Player class. Change the lines

max_lives = GameProgress.maxPlayerLives;
set(res.playerSprites.get(CharacterRecord.CHARACTERS[GameProgress.currentCharacter].name));

To

max_lives = GameProgress.getPlayerMaxHp();
set(res.playerSprites.get(CharacterRecord.CHARACTERS[GameProgress.currentCharacter].name));

In our GameProgress class, define the following function:

public static int getPlayerMaxHp() {
    CharacterRecord currentChar = CharacterRecord.CHARACTERS[currentCharacter];
    return currentChar.getMaxHp(levels[currentCharacter]);
}

We just lookup the current character record, then try to get max hp depending on the level (we’ve also made the sprite setting line a bit more readable. Now, let’s modify GameLogic for our new character stats.

First, go to our AssignPlayerPosition function. There are two places that we should change. First,

else if (currentBonus.getBonusType() == Bonus.BONUS_TYPE_ATTACK)
{
    enemy.takeDamage(GameProgress.playerDamage);

Let’s replace GameProgress.playerDamage with static function of the similar name.

else if (currentBonus.getBonusType() == Bonus.BONUS_TYPE_ATTACK)
{
    enemy.takeDamage(GameProgress.getPlayerDamage());

In our GameProgress, create a function getPlayerDamage():

public static int getPlayerDamage() {
    CharacterRecord currentChar = CharacterRecord.CHARACTERS[currentCharacter];
    return currentChar.getDmg(levels[currentCharacter]);
}

While you’re still there, do the same for hp restored per bonus:

public static int getPlayerHealthRestored() {
    CharacterRecord currentChar = CharacterRecord.CHARACTERS[currentCharacter];
    return currentChar.getHpRestored(levels[currentCharacter]);
}

Go back to logic. In BONUS_TYPE_HEALTH check, replace player.addLives(1)  with

player.addLives(GameProgress.getPlayerHealthRestored());

Now, the only thing left to modify is our BONUS_SPAWN_INTERVAL. We’re going to set it’s value during GameLogic construction (since player cannot levelup during the battles).

Remove the default value of 2.0f from BONUS_SPAWN_INTERVAL and remove keyword static.

private final float BONUS_SPAWN_INTERVAL;

Instead, initialize BONUS_SPAWN_INTERVAL in GameLogic constructor.

BONUS_SPAWN_INTERVAL = 2.0f * (1 - GameProgress.getPlayerBonusReduction());

The function itself:

public static float getPlayerBonusReduction() {
    CharacterRecord currentChar = CharacterRecord.CHARACTERS[currentCharacter];
    return currentChar.getBonusSpawnReduction(levels[currentCharacter]);
}

 

Displaying Stat Information

 

That’s it. The changes should affect the game now. But we cannot really see them in character selection scree. Let’s fix that! First, move the hero sprite to the left by adjusting its X position:

heroSprite.setPosition((uiStage.getWidth() - heroSprite.getWidth()) / 4,
                       (uiStage.getHeight() - heroSprite.getHeight()) / 2);

Then, move the arrow button to the top of the screen (otherwise they just start getting in the way).

prevBtn.setPosition(uiStage.getWidth() / 6 - prevBtn.getWidth() / 2, uiStage.getHeight() * 5 / 6);
//...
nextBtn.setPosition(uiStage.getWidth() * 5 / 6 - nextBtn.getWidth() / 2,
        uiStage.getHeight() * 5 / 6);

Then, let’s actually work on adding the labels with stats. Since we have 4 stats which will involve repetitive label creation, make a function to do that:

private Label prepareStatLabel(String text, float x, float y, Label.LabelStyle textStyle)
{
    Label lbl = new Label(text, textStyle);
    lbl.setAlignment(Align.left);
    lbl.setPosition(x, y);
    uiStage.addActor(lbl);
    return lbl;
}

It will return the newly created label, so that it can be used to properly figure out the coordinates of the next one below it. I add the stats after I add heroSprite to uiStage:

uiStage.addActor(heroSprite);

Label stat = prepareStatLabel("DMG:" + GameProgress.getPlayerDamage(),
        uiStage.getWidth() / 2,
        heroSprite.getY() + heroSprite.getHeight(),
        textStyle);

stat = prepareStatLabel("HP:" + GameProgress.getPlayerMaxHp(),
        uiStage.getWidth() / 2,
        stat.getY() - 10,
        textStyle);

stat = prepareStatLabel("HEAL:" + GameProgress.getPlayerHealthRestored(),
        uiStage.getWidth() / 2,
        stat.getY() - 10,
        textStyle);

prepareStatLabel("BNS:" + GameProgress.getBonusReductionValue(),
        uiStage.getWidth() / 2,
        stat.getY() - 10,
        textStyle);
;

//Bonus function for reference in GameProgress:
public static int getBonusReductionValue() {
    CharacterRecord currentChar = CharacterRecord.CHARACTERS[currentCharacter];
    return levels[currentCharacter] / currentChar.levelsForBonusSpawnUpgrade;
}

This should do it! Not only stats are working, but they are also displayed properly. Here’s what I have now:

Relevant git commit: https://github.com/vladimirslav/dodginghero/commit/c867d7ca47e0ea721353d03107ee7f9e160d32aa