Oct 302015
 

It’s been a slow year on the blog. This is mainly due to taking up a few new hobbies (learning Norwegian, and getting into astronomy and astrophotography) and preparing for a new child. I may write about some of these in future. But in the meantime I’ve done a little bit of work on my previously-untitled “spaceship game”, and figured I should put it up for download on the off-chance that anyone wants to play it.

I’ve mainly improved the frontend and UI, as it used to use the terrible looking built-in UI system in Unity pre-4.6. However, as my day job is making nice UIs for mobile apps, it’s not really the sort of thing I want to spend all my spare time on as well, so it’s pretty minimal (but not as embarrassing as before). The graphics are also terrible, because I’m not an artist. You may have heard the term “programmer art” before. If not, you have now.

At least the game now has a title. Using the time-honoured method of picking pairs of vaguely relevant words at random and putting them in Google until no existing game comes up, it is called Galaxy Arena. Download it here:

PC: GalaxyArena_PC.zip (13.1MB)

Mac: GalaxyArena_Mac.zip (15.1MB)

shipgame1

The Game

Galaxy Arena is a multiplayer-only game where you competitively build a spaceship out of square parts against the clock, and then fight everyone else with your creation. It’s pretty obviously heavily inspired by the board game Galaxy Trucker if you’ve played that (and if not, you should).

You can play free-for-all or with teams, and it supports 2-12 players. Team support is flexible but rudimentary – simply type a team number into the Team box by your name in the lobby, and the game will form teams accordingly.

I’ve only actually tested it on a LAN with up to eight players, but I presume it works over the internet. It uses the Unity development server for posting open games, and this is occasionally down for maintenance. If it is, you’ll just have to try again later (sorry), or use the Connect to IP option (port 33104).

Build Controls

The first phase of the game is where you build your ship. Your Core module in the middle of the screen. This has tiny engines and a puny laser built in, and your match is over when it’s destroyed. Around the edge of the screen are eight stacks of tiles, shared between all players. You have 60 seconds to grab tiles from the edge and attach them to your ship.

Left-click: grab a tile / place a tile if valid (when green)

Right-click: rotate the grabbed tile 90 degrees

Esc key: discard the current tile

Building Rules

Ships must be built following a few rules:

  • Tiles must connect to an existing part of the spaceship
  • Edges of the new tile must match all existing neighbouring tiles – either connector-to-connector, or blank-to-blank
  • No tile can be placed in the square directly in front of a weapon (shown with a red cross)
  • No tile can be placed in the square directly behind an engine (shown with a red cross)

After the time is up, you’ll be taken to the arena to fight.

Tiles

ga_coreCore. Lose this and you die.

 

ga_engine

Engine. Provides thrust and increases turning rate.

 

ga_laserLaser. Rapid firing, low damage weapon. Faster projectile than the missile.

 

ga_missileMissile. Slow firing, high damage weapon. Slightly higher DPS than the laser.

 

ga_gyroGyroscope. Increased turning rate.

 

ga_crewCrew. Increases rate of fire, and increases engine power.

 

ga_batteryBattery. Increases total energy reserves and recharge rate.

 

ga_armourArmour. More hit points than other modules.

 

 

Fight Controls

The arena stage is last man (or team) standing. There is no time limit and no walls around the arena, so just play nice and don’t run away forever… (this is remaining a prototype, not a polished full-featured game).

W/ S / Q / E : thrust forwards / backwards / right / left. Fires whichever engines you have pointed in that direction

A / D : steer left / right. Steering is faster the more engines and gyros you have, and slower the more mass of ship you have

Space : hold down to boost your engines, but this drains your energy bar quickly

Arrow keys : fire all of your weapons that face in that direction. Firing uses energy, and weapons won’t fire if there isn’t enough.

Tab : show the scores (just for fun, they don’t mean anything). Score is purely the amount of damage you’ve inflicted.

Your team mates have green names, and have green arrows pointing to them. Your enemies have red names and red arrows. You can damage your team mates, so be careful.

And that’s about it. Here’s a video of the previous version of the game. Enjoy!

Oct 012014
 

This is part 2 of how my spaceship building/fighting game is structured. Find part 1 here.

Space network synchronisation

Each player’s ship consists of a parent object with a PlayerShip script and a Rigidbody 2D, and a bunch of children (one for each attached ship module). I very much like the fact that you can just add a selection of children with box colliders (i.e. the modules) to an object with a Rigidbody and the physics interactions Just Work (certainly well enough for my purposes).

With that in mind, the only objects created with a Network.Instantiate() are the parent ship objects, one for each player. The server owns all network-instantiated objects, and nothing is network-instantiated on a client. The server keeps track of which object belongs to which player.

The clients have already been told which modules make up all the ships, so they create them all locally and attach them as children of the ships. The parent PlayerShips are the only things in the game that use the NetworkView state synchronisation (which automatically updates position and rotation 15 times/second). This is very efficient as there is only one synchronised object per player.

Prediction and interpolation

The ships use some simple prediction to minimise the effects of lag. I’ve seen a few people asking about how this works, so here’s the serialisation code:

void OnSerializeNetworkView(BitStream stream, NetworkMessageInfo info)
{
  if (stream.isWriting)
  {
    Vector3 position = rigidbody2D.position;
    Vector3 velocity = rigidbody2D.velocity;
    float rotation = rigidbody2D.rotation;
    float rotSpeed = rigidbody2D.angularVelocity;

    stream.Serialize(ref position);
    stream.Serialize(ref velocity);
    stream.Serialize(ref rotation);
    stream.Serialize(ref rotSpeed);
  }
  else
  {
    stream.Serialize(ref syncPosition);
    stream.Serialize(ref syncVelocity);
    stream.Serialize(ref syncRotation);
    stream.Serialize(ref syncRotSpeed);

    syncPositionFrom = transform.position;
    syncRotationFrom = transform.rotation.eulerAngles.z;
    syncBlendTime = 0.0f;
  }
}

And here’s the update code to calculate the transform every frame:

void Update()
{
  if (!Network.isServer)
  {
    syncBlendTime += Time.deltaTime;
    float blend = Mathf.Min(1.0f, syncBlendTime / blendTimeMax);
    transform.position = Vector3.Lerp(syncPositionFrom, syncPosition, blend);

    float newRot = Mathf.LerpAngle(syncRotationFrom, syncRotation, blend);
    transform.rotation = Quaternion.Euler(0.0f, 0.0f, newRot);

    // Update the from and to values by velocity.
    syncPositionFrom += syncVelocity * Time.deltaTime;
    syncPosition += syncVelocity * Time.deltaTime;
    syncRotationFrom += syncRotSpeed * Time.deltaTime;
    syncRotation += syncRotSpeed * Time.deltaTime;
  }
}

This will predict the position/rotation in the frames following an update, and blend out previous prediction errors over blendTimeMax (set it the same as your time between updates). This will fix all positional discontinuities (nothing will pop to a new position) but there will still be first-order discontinuities (velocity will pop).

That’s not a problem at all for the other ships, as it’s not noticeable in a game like this with slow controls. The only issue is if the camera is fixed relative to your ship (which it currently the case), because a tiny change in the ship rotation leads to a large movement of the background at the edge of the screen. It’s still barely noticeable, but ideally the camera position/rotation needs to be slightly elastic.

Controlling the ship

The Space scene contains a PlayerControls script which takes input from the keyboard and sends it to the server. You have controls for applying thrust in four directions (forwards, backwards, left and right), firing in each of the for directions, and steering left and right. The PlayerControls sends an RPC to the server whenever any of the inputs change (e.g. started or stopped steering) to minimise server calls. On the server, the inputs are passed to the PlayerShip owned by that player.

Ships are controlled by applying physics forces to the Rigidbody. Every FixedUpdate(), the PlayerShip uses GetComponentsInChildren() to find all the Engine components (scripts attached to the engine module prefabs) and send them the net horizontal and vertical thrust. If the engine is facing the right way is applies a force to the parent Rigidbody with AddForceAtPosition().

Applying the force at the actual engine location results is wild spinning for even slightly unbalanced ships, so I blend the position nearly all the way back towards the centre of mass to make is more controllable (97% of the way in this case, and even then it’s hard to drive with off-centre engines).

Steering simply uses AddTorque() to rotate the ship.

shipgame1

A ship with unbalanced engines

Weapons

Weapons are fired in a slightly different way to engines. Because there are a variety of weapons systems, I use BroadcastMessage() to call a function on every child script that responds to it (scripts are added to each weapon module). Each weapon script keeps track of its own cooldown and fires if it can.

Firing weapons creates Projectile objects. Each weapon module prefab has a Projectile prefab referenced in it, and each Projectile can have different graphics, speed, lifetime, damage and particle effects. The projectile is created on all clients by doing a Network.Instantiate().

Because projectiles go in straight lines there is no need to synchronise the transforms over the network. The initial position and velocity are set precisely the same as on the server, and the parent ship velocity is added immediately after creation with an RPC (use the new object’s networkView.viewID to identify it on the client). The projectile can then move itself locally and be in exactly the right place.

Impacts and damage are all calculated on the server. OnTriggerEnter2D() is used to detect when a ShipModule has been hit. Network.Destroy() is called on the projectile, a particle effect is instantiated on all clients, and damage is applied to the ShipModule.

If the ShipModule has been destroyed it informs the parent PlayerShip which checks if any other components are no longer connect. RPCs are then used to tell clients to destroy the relevant modules. If the red central component is destroyed then you’re out of the game.

No physics calculations or collisions are processed on the clients at all. The game is entirely server-authoritative to minimise cheating possibilities – the clients simply sends off inputs and receive updates. Overall I’m pretty happy with how it all works, and very happy that the entire game comes in at under 3000 lines of code (total, including comments and blank space)!

Next it needs a load of polish – better graphics, add some sounds, a building tutorial etc, but it’s a decent start.

Sep 282014
 

A little while ago I posted about my spaceship building/fighting game, made in Unity. Because Unity is quite different to how I’m used to writing games (pure C++), it required a bit of getting used to how to structure things. This post will give a high level overview of how it works and all fits together. Again, as a Unity novice I’m not saying this is exactly right, but I’m happy with how easily everything came together so it can’t be too far off!

I plan to do a few more improvements and usability tweaks to the game and then I’ll probably put it up for download. No guarantees on when though.

Scenes

The game consists of just three scenes: Lobby, Garage and Space.

The Lobby contains the UI for creating, joining and leaving servers, a Start button and a chat panel. There is a singleton NetworkManager that stores state about which server you’ve created or joined, and the other players in your game. I talked about that here.

On starting the game, every player loads the Garage scene. This scene contains the base ship and the components available to build it. After 60 seconds of building, the server calls time and tells every client to load the Space scene.

The Space scene contains very little except for a textured quad for the Background, a camera and a couple of manager scripts. All players are added in code, and at the end of the game the Lobby is reloaded.

Quickly about the networking side of things. The server player also plays the game as a client, so as much as possible the code is split out into Server and Client parts. The client-running-on-the-server goes through all the same processes as any other player for simplicity (but the NetworkManager shortcuts the message passing, as I spoke about before).

Garage structure

The Garage scene contains a bunch of GarageSlots which are where the available components appear. There’s also a GarageManager object which has references to all the GarageSlots (set up in the inspector). Finally there’s a PlayerShip which is mainly a container for ShipModules (the square tiles you add to your ship).

Each individual ShipModule is defined as a prefab which contains a sprite, a box collider, a ShipModule script (with info about the connectors on each face, hit points etc), and any module-specific scripts. There are currently eight types of module and around 30 different module variants, so there are 30 different prefabs.

unitygarage

All very straightforward. One problem is then how to create a global accessor for these modules, so that both the Garage and Space scenes can get references to the prefabs. Looks like we need another singleton, which we’ll call the ModuleLibrary.

Singletons with configurable parameters

The ModuleLibrary script contains references to every module prefab, set up in the inspector. This is all fine if the script only exists in one scene because you can just drag one into the scene and set it up. However, singletons like the NetworkManager work by newing up a NetworkManager script and adding it to an object. Instead I want a singleton that I can configure in the editor.

To do this we can set up an object containing a ModuleLibrary script, configure it by adding all the Module prefabs to it, and save that as a prefab. Then you can use this singleton get() function to instantiate it from the prefab:

static ModuleLibrary m_instance;
public static ModuleLibrary Instance
{
  get
  {
    if (m_instance == null)
    {
      GameObject obj = Instantiate(Resources.Load("ModuleLibrary")) as GameObject;
      m_instance = obj.GetComponent();
      DontDestroyOnLoad(m_instance);
    }
    return m_instance;
  }
}

One thing to note is that Resources.Load() takes a path relative to the Resources folder in your Assets list. This folder doesn’t exist by default so you’ll have to create it.

unityResFolder

Now we are able to get a prefab for any tile from this singleton and a module ID number.

Garage security

For a small hobby game I’m not at all worried about cheating, but it’s good practice to design a robust hack-proof system as much as possible anyway. To that end, the server keeps track of and verifies all steps in the ship building process.

The server generates the IDs of the modules that will be available in the slots, and tells all clients. When a player clicks a module to pick it up, their client sends the chosen slot ID back to the server. The server stores which type of module that client has selected, and generates a new one to fill the gap.

When a player then clicks to attach a module to their ship, the client only sends the grid coordinates and the rotation (one of four). The server already knows which component is selected and verifies that it’s valid. Therefore it’s not possible to send new modules back, or create invalid ships, by sending fake packets to the server.

From the Garage to Space

The details of everyone’s ships are stored in a ShipStore, which is another configurable singleton. The ShipStore on the server is what keeps track of the ships that each player is building. When the Space scene has loaded, the server ShipStore uses RPCs to tell every other player the details of all the ships.

Unfortunately the built-in Unity networking doesn’t support sending arbitrary byte arrays, so the transmission is a bit cumbersome – an RPC call is made for every single component on every ship and contains the player, coordinates, module ID and rotation. It’s not ideal but it works, and there are at most a couple of hundred messages.

At this stage there is a little bit of message passing to ensure that every client has finished loading and receiving ship data. Everyone has now build a ship and made it into space so it’s time for some action, but that can wait until part 2.