UI Starter
My goal for today is to implement a basic UI for the FPS view. It needs to be sufficiently modular to support the likely cramming of a bunch of shit into it down the road.
HUD
I'll start with a HUD, here's the elements that we'll likely need:
- Health bar
- Toolbar
- Equipted item info
- XP Bar
- Character Level
- Available skill point notification
- Compass
Layout
The easiest place to start will be sectioning off portions of the screen that will contain overlays.
I start by adding a default Game Object to the scene to nest everything under, this will allow me to add controllers here later and easily access children from there. I'll call this object UI. Under here, I'll create another empty Game Object and call it HUD.
Next, I think it'll be easiest to define the panels that will be used to makeup the HUD. I'll add a canvas under the HUD object for each section, give it a guess to how large I want it to be, and position it on the screen around about where I think it'll end up.
Here's the resulting Hierarchy:
And how it looks on the screen:
Compass
I'll try to implement the compass first... I guess it would be easiest to define a text element that has a width matching 360 * a separation constant for each degree turn, then update the position of the text element on every frame depending on which angle around the Y axis our camera is facing? No, that would be too weird to handle wrapping with. Perhaps a what would work better is to use an array with 360 elements, where each element is a list of GameObjects that we can render onto the screen at the position at which each degree is at on the compass currently, depending on rotation of the camera. On every frame, we would move the rows depending on player rotation. For each GameObject in the rows, we could wrap them with a class which subscribes to compass events to help us translate or hide them as needed depending on player rotation. Seems legit, let's see how it goes! First I laid the ground work by adding a Compass GameObject to the 'Top Center' panel. Then I started a script which I attached to that. I declared some prefabs to pass in, which for now are static images, for the compass indicators:
/// <summary>
/// Indicator to place at 15 degree intervals along the compass.
/// </summary>
public GameObject MinorDegreeIndicator;
/// <summary>
/// Indicator to place at each 90 degree interval along the compass (N,S,E,W)
/// </summary>
public GameObject MajorDegreeIndicator;
Then, to position things onto here, we're going to need some information about the canvas and the camera...
/// <summary>
/// Gameobject containing the Canvas which the compass is rendered onto
/// </summary>
public GameObject canvas;
private int canvasWidth;
private int canvasHeight;
...
// Calculate canvas dimensions
var canvasRect = canvas.GetComponent<RectTransform>();
canvasHeight = (int)canvasRect.rect.height;
canvasWidth = (int)canvasRect.rect.width;
We're also going to want to keep updating the viewable compass range based on a configurable width...
/// <summary>
/// How many degrees are viewable in the compass range
/// </summary>
public int CompassWidth = 180;
private int[] range = new int[2] { 0, 180 };
...
void UpdateRange()
{
range[0] = center - CompassWidth/2;
if (range[0] < 0)
{
range[0] = 360 - range[0];
}
range[1] = center + CompassWidth/2;
if (range[1] > 359)
{
range[1] = range[1] - 360;
}
}
And a simple helper function to tell us if a given degree is in view...
bool InRange(int degree)
{
if (degree >= range[0] || degree <= range[1])
{
return true;
}
return false;
}
While we're working on this, I'll want to be able to easily see what our current camera angle is...
// Create game object to add to canvas
GameObject DegreeDisplayObject = new GameObject("Degree Display");
DegreeDisplayObject.transform.SetParent(this.transform);
// Create text component
DegreeDisplay = DegreeDisplayObject.AddComponent<Text>();
DegreeDisplay.text = center.ToString();
Font ArialFont = (Font)Resources.GetBuiltinResource(typeof(Font), "Arial.ttf");
DegreeDisplay.font = ArialFont;
DegreeDisplay.material = ArialFont.material;
// Add text component to our game object
DegreeDisplay.transform.SetParent(canvas.transform);
// Transform the game object into position
DegreeDisplayObject.GetComponent<RectTransform>().SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, 5, 30);
DegreeDisplayObject.GetComponent<RectTransform>().SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, canvasWidth/2 - 15, 30);
...
// Update is called once per frame
void Update()
{
// Update center from camera angle
center = (int)Camera.rotation.eulerAngles.y;
// Update angle display
DegreeDisplay.text = center.ToString();
}
That's all for tonight!