Unity RTS – Part 6 – GUI

Although promised many times, Unity still does not have a decent UI layout engine. The process can be simplified with NGUI from the asset store, but its still tedious. For UnityRTS I opted to use a system similar to the built in GUILayout, but tried to make it much simpler to use for common use cases. It works off of a loose hierarchy of elements, with three main functions.

Most of the time, child elements will borrow a slice from their parents; removing that space from the parent and giving it to the child. This is done through WidthSlice() and HeightSlice(). Another common function is tables and grids, created through the Grid() function. After specifying the number of rows and columns, calling Cell() on a grid returns the next cell (iterating horizontally then vertically). The final function is a flow layout, though not integrated as nicely as it could be. This scales down items uniformly to fit the specified number of items in the allocated space; much like grid, but the item dimensions and count are provided, and item aspect ratios are preserved.

The UI in UnityRTS is modelled off of Age of Mythology. Two height slices cut the top and bottom bars, and various other functions allocate required space. Information is pulled from the game scene as required. Also made some pretty box art for things 😀

unityrtsui
Webplayer | Download Project

Advertisements

Unity RTS – Part 5 – Pathfinding

The most common algorithm used in RTS pathfinding is A*. This algorithm traverses lists of nodes in a specific order to reduce the number of nodes processed while ensuring the fastest path is taken, and requires very little preprocessing of the map data for a trivial implementation. Unity RTS is designed to use this same concept, but combined with flow fields to allow more natural movements (removing the grid-aligned movements in unprocessed A*).

Firstly, a map is created with a value for each terrain cell, which represents if that cell is occupied or not. An int is used here, so that objects stacked on top of each other work correctly when added/removed. An A* pass then propagates the cost values throughout the terrain, but with a small change. When propagating a cost to each cell, the value used is the minimum of (from_cost + 1) or ((from_cost + neighbour_cost) / 2 + 1). This means costs can be propagated diagonally. The most extreme case, where neighbour is 0 and from is 1, this results in a value of 1.5 (quite close to the correct 1.41 when using cell distances).

Objects then move by calculating a movement direction from this persistent pathing query. First the interpolated value at the objects exact position is calculated (based on the 4 surrounding corners). Then for each corner, a desired direction is calculated and finally averaged to get the movement direction. A final piece of code tries to prevent directions from pointing into blocked areas, though is not necessary.

The complicated part here is calculating a desired direction from each corner. Each corner only knows about itself and the current object position, so this direction vector simply indicates how much closer or farther the object should be to the corner, based on the assumption that the cost increases by 1 for every unit distance. The ratio of delta cost and distance is first calculated, then the resulting vector is simply:

dir += deltaP * Mathf.Sign(ratio) / (1.0f – Mathf.Abs(ratio));

Where deltaP is the object position relative to the corner, ratio is the ratio of cost to distance. Of course, this can result in a division by zero; such a case indicates that the unit should move exactly toward the corner (ignoring cost calculation errors); cdir just needs to be very big in this case to overpower the directions from any other lesser corners.

A lot more goes into the pathfinding of a AAA RTS, you can read a little about how Starcraft handles their pathfinding here, or a little about Age of Empires pathfinding here.

pathingtest
When targeting the selected object, the blue lines display the direction each corner is suggesting that the object move in, the white is the resulting normalised sum of these directions. This project is the same as in Part 4 (I just took a long time to write it up), DownloadWebplayer.

Unity RTS – Part 4 – Vision blocking

Its been a while! This time I’ve made a lot of tweaks to the Line of Sight code so that items can correctly and efficiently occlude unit vision and implemented pathfinding using flowfields for smoother unit motion. Both items took quite a bit longer than I had anticipated and are probably still quite slow, but I’m happy with how they are looking. This post will talk about the vision blockers; I’ll leave pathfinding for the next.

Some code existed in the previous build for entities to write their blocking height to a grid covering the game world. This grid tracks the highest unit on each cell and blocks sight for all shorter units. The RevealLOS method has been rewritten to now more accurately read this data and project “shadows” outward to hide areas of terrain. The algorithm has two distinct phases.

Pass 1 – Occlusion angles

The first pass iterates through all tiles within the reveal radius, ordered by their occurrence when sweeping clockwise. Think of this as a radar, which pings for each cell as the detector passes them. This ordering is important to keep the input clean and reduce the amount of work needed later.

Each cells height is compared against the current units height, if the cell is too high, it is added to an ordered list of blocking cells (ordered the same as the iteration, clockwise around the unit). The distance and angles for where the blocked vision starts and stops are also cached. A small optimisation pass is done on this data to remove cells that are eclipsed by other cells nearer to the unit center; these are redundant, since any terrain they occlude will already be occluded by the nearer cells.

Pass 2 – Terrain reveal

The next pass iterates over all tiles within the reveal radius, determines their distance and start/end angles, and looks through the previous list of occlusion cells to determine how much of the tile is occluded. As the occlusion items are ordered, iterating this list forward and shrinking the visible arc is sufficient to correctly calculate occlusion for the left-most angle. This is then repeated in reverse for the right side.

The result of this calculation is four angles; the tiles initial start and end angles relative to the unit, and the unoccluded start and end angles. The visibility of each tile is simply the unoccluded arc size divided by the total tile arc size.

Optimisations

Trigonometry functions are expensive to execute, so “angles” here are approximations. A full circle in this system is 8 degrees, and calculating the angle of a vector is simply determining what quadrant it is in, and then dividing the coordinates.

It is impossible for the arc of a nearer cell to be smaller than a farther cell it is projecting onto. If this were not the case, it could be possible for small arcs to lie entirely within a tiles arc, and be ignored by the system.

losoccluded

View online or Download Project (note: has some unfinished UI code in there too)

Unity RTS – Part 3 – Resource Gathering

Most of the interactions between entities in an RTS revolve around trading resources between the player, environment, and enemy. If we consider unit hitpoints to be a resource, which causes death when depleted (much like trees when exhausting wood or mines when exhausting gold), implementing HP in the same system as other resources looks like a reasonable thing to do. However, in reality, this turns out to greatly complicate the content pipeline.

There are a number of subtle differences between the default configuration for health, and for other resources an RTS might have. By default:

  • When an entity damages another entity, it should not gain the damaged health for itself
  • Health should not be transferrable between entities
  • There is no global player health (ie. player stockpile for Food/Wood/Gold)
  • Armour types dont make sense for other resources

Many of these should be optionally possible, and there are many other behaviours that are shared between the two (animation timing, buffs, range, depletion events). I am not sure of the best approach when dealing with these two similar systems; for this project I am choosing to use polymorphism to expose similar capabilities with different defaults. A base class InteractionAction will provide logic for interacting with a target entity (offering range, action interval, and sub-action logic), with two specialisations for AttackAction and GatherAction, which provide inspector variables and the missing logic.

Eventually I would like to support something like an entity that generates 1 gold for every 2 hitpoints that it deals to an enemy. This will need to be implemented with a 3rd specialisation, CustomAction, which allows arbitrary resource exchanges on various events (perhaps also a boomerang that damages whatever it hits, and heals when it returns?)

Most of the logic for this system takes place in InteractionAction. This class handles moving toward a target, and then repeating an interaction at a set interval (ie. move toward a tree, then “chop” to gather some wood every second). The class internally holds a SubRequest, which is used for the current “stage” (this would first be targeting the tree, but once enough wood is collected, it would then target a resource drop site). It is up to the subclass to override BeginStage to determine the best target for the action (ie. if the unit is already holding its full capacity, look for a drop site, if not, try to mine resources) and call EndStage where appropriate (ie. after the resources were dropped, or after capacity was reached).

The component does not yet support projectile interactions, a nice way to implement them would be to create a new InteractionProjectile component and hold a reference to it in InteractionAction; creating new instances on each NotifyInterval() call, and having it call back to InteractionAction on specific events (hitting enemy, returning to source unit).

UnityRTSP3
As usual, heres a webbuild, and the project files.
The light-blue boxes are resources, dark-blue is a resource drop point, and the other sphere is an enemy that can be attacked.

Things are getting hard to debug without a UI, so I may skip pathfinding and do UI next instead.

Unity RTS – Part 2 – Line Of Sight

There are two common methods for displaying Line of Sight and and Fog of War in an RTS. The geometry hidden from the player can either be modified before being rendered to the screen, or a second pass can be used to paint over the top to cut out areas that should not be seen.

Painting over the top

Painting blackness over the top of the world was a very common approach for older 2D RTS’. It can be seen in Age of Empires 1 by the dithering pattern present in revealed foggy areas. This approach has the benefit of working very well with DirectDraw (and GDI) compatible hardware, but also nicely decouples the fog of war code from the rest of the game rendering. Civilization has another neat version of this for its cloudy fog of war.

Painting on the surface

Warcraft 3

This seems to be the approach used in most modern RTS games. A trivial implementation would require a texture to be generated matching the terrain size, such that each pixel corresponds to one cell of the terrain. Each of these pixels contains visibility data for that cell, with a shader reading that data to darken or remove sections of the terrain. This method forces tight coupling between the geometry rendering and fog of war, but allows more advanced rendering; properly handling transparency, filters, and working well with 3D geometry.

LOS in Unity

Unity has sufficient support to easily handle the shaders necessary for painting line of sight darkening right onto the geometry. I like to implement this with a global shader texture and scale/translation to map from world XZ coordinates to the LOS texture. To work with the indie version of Unity, this texture needs to be generated on CPU and copied over each time it changes; this slower than doing the work directly on the GPU, but for smaller maps, it is sufficiently fast.

Each frame, the LOSManager clears the red channel of each pixel to 0. It then iterates through each entity in the world owned by the player, and paints a white circle onto the texture around the unit (importantly, it sets the red and green channels to 255). This means that every pixel that is currently visible will have a red and green value of 255. Every pixel that was previously visible and is now fogged will have a red value of 0 and green value of 255. Pixels that have not yet been revealed will be black. In the UnityRTS implementation, the blue channel is used to store the amount of time since the area was visible, and the alpha channel stores ambient occlusion, though these are not important features.

The shader needed to render line of sight on objects is quite simple. It can be implemented in a surface shader by adding the following lines:

half4 fow = tex2D(_FOWTex, TRANSFORM_TEX(IN.worldPos.xz, _FOWTex));
o.Albedo = t.rgb * (fow.r + fow.g) / 2;

Where t is the texel from _MainTex, and _FOWTex is the fog of war texture (along with _FOWTex_ST). This code will cause anything in full visibility to be draw as normal, anything revealed but not visible to be half-brightness, and anything hidden to be black.

UnityRTSP2

View online or Download Project (this may be removed)

Unity RTS – Part 1 – Framework

I’m going to start a series on building the major components of an RTS in Unity, starting with how to develop a typical framework. RTS games remain one of the most complex and difficult game types to create, dont expect to have anything fancy working for quite a long time.

Networking Considerations

Most developers looking to build an RTS will be looking to include a multiplayer component. I wont go into a lot of detail, but supporting multiplayer in a game with hundreds of active units constantly doing things in the world is quite difficult; 1500 Archers on a 28.8: Network Programming in Age of Empires and Beyond does a much better job of explaining the typical approach used (though some other approaches also exist). A well designed RTS should have a layer which all input “Commands” pass before interacting with the world; this layer synchronises actions through to the various clients to ensure the world simulates correctly for each client.

Components

An RTS framework will likely involve the following components (and more):

  • Entity: Represents the base data that each object within the scene contains (Name, Player, Health)
  • Action: Contains logic for an action that an entity can perform
    • MoveAction: Take a target location and walk the best path to reach that location
    • BuildAction: Invoke MoveAction; when in range, begin construction on a Buildable component
    • GatherAction: Invoke MoveAction; when in range, gather resources from a ResourceSite component
    • AttackAction: Invoke MoveAction; when in range, reduce the target Entitys Health
  • Components: Optional items that augment the way an entity acts
    • Sight: Cause this object to reveal the surrounding area
    • Buildable: Accept build actions from other entities; when complete, swap for another entity
    • ResourceSite: Hold an amount of resource that can be gathered
  • Pathfinder: Store terrain information required for pathfinding and provide pathfind query results
  • Player: Store information about a player (Name, Colour, Alliances)
  • Command: A target and action to be applied to a set of entities
  • SceneManager: Handle executing commands in the correct order and at the correct time, and querying for entities
  • LOSManager: Hide objects that are out of sight of the player
  • SelectionManager: Store and allow interactions of the currently selected entities
  • CameraManager: Handle camera movements
  • BuildManager: Allows the player to interactively place buildings when a building type is selected
  • InputManager: Receive mouse and keyboard inputs and send them to the appropriate manager

To ensure the game simulates correctly on all clients, no gameplay-altering information should ever leak into the entities or their actions/components except through generating Commands. Unfortunately, Unity makes this constraint very difficult to enforce; at any point a script can change the state of an entity or an action can query the Input class and cause a client desync. It is entirely up to the programmer to make sure that never happens.

Implementation

UnityRTSP1
I’ve created a very basic framework, you can view it online here, or download the project file here:
http://weesals.com/Releases/Unity/UnityRTS/Part1/UnityRTS.zip
Its very simple, dont expect much.

This project contains a working Action system, though has only a partially implemented Move action. Entities can be selected by clicking or box-selecting them, they can then be ordered to move by right-clicking the terrain or another entity. Right-clicking another entity will cause the target position to continually update, and cause the entities to follow.

New actions can be implemented in a similar way, and then dragged onto the object via the inspector. An AttackAction may subclass the MoveAction and only run base.Step() when the object is too far away to attack. Once the objects are in range, it can then begin the attack logic. The action with the highest score for a given request is used, thus an attack action returning a score of 2 when targeted at an enemy entity would be used instead of the MoveAction returning a score of 1.

Flap The Fish

I’m not so much a fan of Flappy Bird, but theres no denying that its a very addictive game. It’s interesting how such a small change (increasing the vertical movement speed, decreasing horizontal) from Helicopter can have such a huge impact on how the game plays.

I decided to give it a go building a similar game, initially as a joke and eventually convincing some friends to help me improving it into something quite well polished and enjoyable. The game was built in under 2 weeks by 3 of us, here are some of the notable releases:

Initial release – play as a “blue bird”
Repackaged for portrait phones
Mutant Fish retheme
Improved model and shader experimentation
…lots of polish…
Current release

I’m really happy with how its turned out. Its released as a free Android game.

Unity 2014-02-23 18-47-05-03 - square
Download free from Google Play

Download free for Windows Phone

YouTube Trailer

RTS4 – Cliff models

Using a mesh for the cliff face turned out to be quite a bit more difficult than I originally thought. It’s finally working, though still incomplete. I’m not sure its worth the processing requirements and complexity it adds. I’ll probably disable it and decide later. Using a mesh means the cliff can have nice crevices that match the texture mapping, and have little ledges and rocks poking out. Otherwise without using a mesh, I’ll add some variation to make it look less rigid, but the texture wont match the mesh as nicely.

Other problems that this approach presents:

  • The cliff model would not be able to wrap around a raised plateau without a geometry seam (potentially showing through to the void below)
  • Matching the cliff model offset between terrain chunks is very complicated and unlikely to ever work completely (nasty geometry seams between each chunk)
  • Floating point errors cause holes in the geometry or can potentially crash the game
  • The cliffs are significantly higher poly
  • A mesh should be made for each cliff type, adding to the workload

This picture from Unity RTS shows a hand-modeled example of what I was going for.
RTSCliff2
Notice the darker sections of the cliff texture are inset.

RTS4 – Progress update

I’ve got back into the WebGL port of RTS4. I had previously done a lot of work for.. something.. I can’t quite remember, but I left it in a broken state. It’s now been fixed and had a little more progress made:

  • The UI is all data-bound through Knockout
  • Users can now box-select stuff
  • Units/buildings have a visual thing below them when selected
  • Constructing things now has a construction phase
  • The object bounds are used for building/attacking/gathering (instead of the unit walking to the object centre)
  • Scripts are combined into a single all.js file, reducing the load on my poor server
  • Models (including animations) are loaded in a much smaller binary format, meaning MUCH faster load times, and less content to download
  • Model transforming happens through an (as yet unseen) node.js server, which also opens a socket to the client; some basic multiplayer tests/features shouldnt be too far off.
  • Basics of an “EntityGroup” class, which manages a collection of entities that satisfy a list of requirements. (the “Villies” print out is using this system to count units)

I made a few models earlier, and since they look much better than the current build, ill post them here instead.

rts4models
“Play” in browser (IE11 or Chrome)

Next step will probably be terrain and other visual improvements. The action system also needs to be revamped; the over-engineered “resources” system needs to be integrated into the entities themselves, rather than a separate component (health and population are considered resources in this system). The resources overhead is insignificant, and almost every entity that needs behaviours, needs resources. This may make it easier to integrate more concepts (like line of sight) to be resources (probably to be renamed to “properties”), which will make buffs much easier.

Note to self: I’d also like to have a concept of “abilities”, such as “move” and “face direction”, so that the correct action can gain access to the abilities they need (otherwise a unit ordered to move left but attack right wont know which order should control movement)