Sunday 21 February 2016

Run its course

Run Its Course

At this stage the game of Star Dancer has run its course.  

It has been available for download in its completed form since October 2015.  It has had almost 1,000 downloads for each version (free and full) in that time.  The majority of full version downloads took place in the month of January. 

As of February with the rankings reset for Google Play i've noticed that the game has ceased receiving attention.  

I know that were I to release an update regularly that would boost its position in the charts temporarily but the game's release period is effectively over.

I will continue playing it myself. During its time in the sun it had probably half a dozen people who really got into it. The majority discarded it almost immediately.  However I enjoy it.

As a permanently single bloke, never married, I once quipped to a close friend of mine that my efforts to market the game were about as successful as my efforts to find a female companion.  There's a lot of truth there it seems - at least going by the relative success in each of the two arenas.  

Anyway I enjoy the game, and I put a heap of work into it over the course of the year - it is valuable to me even if it doesn't receive any attention.  It would have been nice if more people had played and enjoyed it but there's always the next game that I build that may receive a bit more attention, though unlikely.

I won't advertise this post on twitter, facebook or email it to friends.  There's still a surprise in my game which will appear later in the year - just a short message to some people who play it - but I doubt it will be noticed as the game fades into complete obscurity.

It was quite disheartening back in December/January when every attempt I made to market the game fell flat. But that's how it goes. Kind of like life.

best regards,
Matt

Thursday 11 February 2016

Code Written for Star Dancer

Code Written for Star Dancer
(or how I made my fingers bleed)


When I put Star Dancer together there were a number of different disciplines and procedures that had to be completed, ranging from design to art to music and so on. The majority of the work that I produced involved coding.  I have already demonstrated the cost of development as being approximately $7000 for art assets and so on in another blog post.

However the actual amount of coding work that I did was substantial to say the least.

Every few months I would run a program that gave me some estimate of the amount of code written as part of the runtime but also as part of the development process. Since some of the game's assets were generated in external code files not included in the game itself I included much but not all of these.

The coding took place after business hours (I worked a day job) and usually averaged out to be about 20 hours per week for a 6 month period with an additional couple of months at a far reduced hourly amount per week (about 2 hrs) in the post release phase when the occasional enhancement and bug fix was released.

The total hours of coding equated to approximately 500-600 hours of code over the course of 2015 for Star Dancer.

Because I don't charge myself for coding this saved me a lot of money by doing it myself!  Had I hired myself out to a client for the same amount of time I would have expected no less than $100 per hour to work in my own time (ie in addition to my full time job for the inconvenience) and would have cost a client approximately $50k-$60k.

While lines of code is not necessarily a useful metric for measuring productivity it does tell you a little bit about just how much actual code was required to be built since the project was begun from scratch.

So here are some figures for those that enjoy numbers.

Java (Android app) - approximately 25,000 lines of code including approximately 600,000 characters typed.  (comments included)

XML (Android app) - approximately 4,000 lines of XML for approximately 100,000 characters typed.

GLSL (Android app - shader) - approximately 240 lines of GLSL with approximately 8000 characters.

PHP & SQL (Web Backend) - approximately 15,000 lines of code with approximately 350,000 characters typed.

BlitzPlus/Blitz3D (tools/image processing, prototyping ideas, tests) - approximately 25,000 lines of code written including approximately 500,000 characters typed.

The vast majority (over 95%) of this was written between early February and late June 2015.  It was a considerable amount of effort and had I been charging a client only $100 AUD per hour then I'd say they would have been getting an amazing deal in terms of output per hour.

So had I lacked programming skills my game would have been totally impossible for me to develop as a hobby as that would have been easily the greatest cost in development for a project like this.


Wednesday 10 February 2016

The human cost of space warfare

The human cost of space warfare
or put simply
"why I wouldn't want to be a star fighter pilot ever"


"The average life span of an EarthHope Fighter pilot is 1 minute 35 seconds" - as I have said elsewhere.

From the moment the two fleets drop out of hyperspace and engage in combat there is barely a couple of minutes survival time for one of the fleets' entire crew.

In space there is nowhere to hide, nowhere to run.  Once one of the fleets gets on top of the other  there is little hope of survival for the losing team.

The first few moments of battle are tense as the two fleets approach, at this stage there is no sign or indicator as to which side is going to come off second best. And second best means death in a space battle.

As the opposing line of fighters approach one another the EarthHope pilots expression changes to one of horror as they realise the AioSenti craft have been set to ram.  With no time to change their course one by one a series of bright flares illuminate the night sky as each pair collides.

A handful of EarthHope pilots escape this fate only to find themselves all alone in a sea of combat. Their directives are to attack the larger capital and cruiser vessels.  Flying directly at the nearest cruiser one of the fighters is torn to shreds by a burst of rockets launched from the forward weapon mount on the enemy cruiser.

The remaining fighter pilot glances around the battle space - his capital ship is ablaze and taking heavy damage from AioSenti bombers, the cruisers can offer no support against the enemy light craft but they too are damaged heavily.  Outnumbered and outgunned by what remains of the enemy fleet the EarthHope fighter dodges and weaves knowing it is only a matter of time before he too will become one with the vacuum of space.

The radio chatter has gone silent, all signs of human life in this sector of space vanquished.  The vacuum reigns supreme over the battlefield.


Tuesday 9 February 2016

Coding Features - How the Lighting is Setup In Space

Coding Features - How the Lighting is Setup in Space

Star Dancer is set in Space. It uses a fake phong shader for the lighting with fake bump mapping.

The fake phong shader uses diffuse and specular components for lighting the space ship (per pixel) using the standard lighting equations for diffuse and specular components. However I ignore the ambient term.  Bump mapping is faked by taking the color map and extracting an average brightness value of the texel for two points - the current actual texel and a slightly offset texel and then using the difference in these to calculate a heightmap that alters the diffuse and specular components as a minor perturbation to the normal for the bumpmap..it is a crude but effective method when applied lightly (the bump mapping is very light - mostly not noticeable).

Given that this is a game not reality I had a bit of creative leeway with the positions of the lights.  In the game I use two light sources to represent a key and a fill light.  I would have liked to have had a back light as well as in a traditional 3 point lighting set up but getting the faint glow around the edges of the ships was going to prove a little difficult in a real time setup for me.  

The key and fill lights are oriented almost 180 degrees apart - slightly off from that, with the fill light being of lesser intensity. Both are white lights.  Because I have only two lights and because they are both static directional lights I include their position directly within the shader itself.  

Typically with game engines lights are expressed as parameters to a shader but because I am not making an engine but am making a game with a specific lighting setup that is unchanging I had no worries about simply expressing these lights as constant light source values in the game that could be built directly into the shader.  Not recommended for a more general purpose lighting solution but for a space environment it works.


Coding Features - How the AI Opponent Learns From You

Coding Features - How the AI Opponent Learns From You

In the game the AI opponent learns from the player in a variety of ways. The first is related to the online campaign, the second is related to the offline mode. I will detail both here.

Because the decisions taken by players in the game are all about setting the weightings for their ships' flight behaviours before a battle this makes it very easy for the AI to be able to make decisions.

The game is mostly about tweaking these settings to achieve victory and as such the method used for the AI player online is as follows.

Basic algorithm:

Every 10 minutes of  realtime the server goes through all the human players who have fought battles in the last month. 

Rank each player in terms of their win loss ratio but also weight more heavily in the favour of human players who have fought more battles than others (to a certain extent).

Take the top player, find their weighted flight settings and apply that to each of the AI players in the game.  Every few minutes make slight random adjustments to the AI players flight settings as a slight departure from the actual flight settings of the best human player.

This is the basis for the AI opponent online.

Offline:

Pick a random set of AI settings the first time through.  If the AI wins the battle change 10% of them randomly only slightly before the next battle.  If the AI loses the battle change 40% of them randomly more heavily before the next battle.

This is the basis of the AI opponent offline and it results in a computer opponent who is unpredictable but over time presents a balanced challenge to the player approximately at the level of the player.

The online AI is the tougher of the two and results in (at the moment) an AI with a win loss ratio of over 75% in most cases.  


Coding - Camera Positions and Motion in the Game

Coding - Camera Positions and Motion in the Game

In the preprocessing step of the battle the camera positions are chosen for the space battle to hopefully provide a more exciting and dynamic battle than if the user were to try and watch the action themselves by positioning the camera.

This is done as a post stage of the preprocessing step of the battle.

Once all frames of the battle (maximum is 8000) are completed the frames are then analysed by the game to generate a series of camera positions, motions and orientations that hopefully look good.

Originally the game used a static set of choices for the camera but as of an update back in October the camera is chosen from a random set of options that are generated - this means that the same battle may be shown from a variety of camera methods which means very few battles - even with the exact same parameters - will appear exactly the same.

However the method used to choose the camera angles is as follows.

Every 'n' frames of the battle the next 'm' frames of the battle (plus the preceding 'm') are analysed. A number of possible camera options are run through and then selected.  First the system decides whether it wishes to watch a small ship or a big ship.  It also examines whether any beams are firing (these are considered high priority for watching), whether any ships are taking lots of damage or are destroyed in the time period (these are given a high priority).

Once these calculations are performed the camera is placed at a number of different positions.  After watching many viewings of different movie space battles I worked out that there are range of common shots.  Some of these involve moving cameras, some of these involve static cameras, some of these involve wide shots (include as many ships as possible in the field of view), some of these involve close chase shots (include the 'target' ship and aim in a direction that includes the most amount of 'action' such as explosions and so on).

The general pseudo code for this process is as follows.

for frame = 1 to max frames step n (n = 300?)
{
   pick a new camera focus for this section from the following options
   {wide shot, chase fighter/bomber, chase capital/cruiser, and a few others}
   analyse the play field to see which direction viewed will show the most action
   decide on whether the camera is {static or dynamic}
   if {dynamic camera}
  {
      pick a start and end position for the camera, move the camera through these positions and aim in the direction that gives the most action shots related to the type chosen (fighters,wide, capital etc)

  }

}

Coding - How the Research Settings Work On the Server

Coding - How the Research Settings Work on the Server


Research in the game is performed only in campaign mode. Research is performed on the server.  The end user sees a system whereby the percentage in each focus area of the game (speed, economy, firepower, defense) is increased gradually over time from 0 to a maximum value the more time is spent researching a technology.

The algorithm used for calculating research is very, very simple.

When a research setting is changed we detect the time on the server that the setting was set.  At any point that the game requests information about the player's ships (such as before a battle, or when viewing ship details, or when viewing fleet information) the server reports an updated value of the appropriate value (hitpoints, firepower etc).

The mathematical technique used is to use an atan curve (inverse tangent).

The inverse tangent curve us a curve that goes from -Pi/2 to +Pi/2 for values from -infinity to +infinity.

For the purpose of the game we specify our research modifier (as an additional multiplier) to the base score as equivalent to this:

Modifier = Constant *  Atan(Time Researched * Fudge Factor) / (Pi/2 plus a little bit)

This means that as time goes to infinity the Modifier approaches its maximum value. At time is zero the Modifier begins at zero.  The curve is not linear and instead is similar to an 'S' curve with a low rate of increase at the beginning, a higher rate of increase in the mid section and a slow rate of increase asymptoting to the maximum value at higher values of time.

This is the basis for which research progress is calculated on the server.

Coding - How the space battle flight behaviours work

Coding Development - How the Game Works Part 1


I thought that given the game is complete now, and that given the game is available for free and all marketing has now been completed I would share some insights throughout the next set of blog posts about how the game hangs together - particularly the more unusual or innovative types of things the game does.

The first of these is the core gameplay feature of 'weighted behavioural AI' as I describe it for the flight behaviours of the ships in the game.

Space Battles in the game are precalculated, although the process I'm about to describe would work in both real time and preprocessing in any other game.

I'm not going to go into the basic mechanics of game programming - it will be expected that certain basic concepts such as a main game loop and the core principles of obtaining user/entity input, update world, draw world are understood generally speaking.

During the space battle each frame the various space ships have a counter decremented.  Once that counter reaches zero it is reset to a particular value and the count down is restarted again.  Should that space ship's currently targeted enemy spaceship be eliminated before this point the count down timer is automatically reset to zero.

Upon the countdown counter reaching zero a series of conditional statements are evaluated.

Within a short loop each enemy vessel is evaluated against a set of parameters such as their distance away, the vector position relative to the current vessel, their hitpoints, their damage, their speed and so on.  The AI flight settings set by the player beforehand results in a score being assigned to each of these parameters.  The individual scores based for each if statement (if true then add the value of the AI setting) are then totalled and compared with each other.  The enemy vessel with the greatest score is then set as the 'target enemy' for that ship.  

Once a target enemy for a ship is chosen the current ship will continually adjust its velocity to aim towards the enemy vessel.  Should it come too close then it will be given a nudge in a direction perpendicular to the direction to the enemy target vessel which will result in the current ship turning away from the target.  

That is the basis of how the ships acquire their targets throughout the space battle.


The pseudo code for the process would look something like this.

if 'aicounter' is zero then 
{
     reset ai counter to starting value (eg 200)
     set targetingscore to a highly negative value
     set targetedenemyvessel to -1
     loop through all enemy vessels
     {
         if 'some condition' is true then 
                add relevant ai weighting score to the currenttarget's score
         endif
         repeat if statement and add score for other conditions
         if current target's score is the highest then set the targetedenemyvessel to this one
     }
}
if 'currentship has a targetedenemyvessel' (which it always will unless the enemy fleet is destroyed)
{
     adjust velocity of ship to aim towards enemy vessel and fly towards it
}
loop through all enemy vessels
{
     if a collision is likely with the current vessel and an enemy vessel then 
     {
           change the trajectory of the current vessel to be away from the vessel it will collide with    
     }
}