I’ve been out of the country visiting my folks so progress since the last post has been slow. I’ve built a few more basic UI screens for the game, but I thought I would talk about something relatively done and dusted – portrait generation. Characters are the central focus of Star Dynasties, and bringing them to life visually is an important design goal. The game generates over a thousand characters for a game, so portraits also have to be generated dynamically.
Portraits are assembled from a library of sprites designed to stitch together seamlessly. Each component of the portrait is drawn on a seperate layer in a pre-determined order to create a human face. Some of these components are handled as multiple sprites. For example, the hair component is split into two seperate sprites; one is drawn beneath the face, the other is drawn on top of the face. Most components are also re-colored to further differentiate characters from each other; allowing for a dynamic range of eye colors, skin colors, dress colors, etc.
One complexity stems from the fact that through the game you will play through the lifetime of many characters. Thus characters age, and their facial features must age appropriately. Some features (such as eyes) change significantly as we age, so we need a version of each different type of eye sprite for each age group that we are representing in the game. Some other features age more slowly, so we can get away with less granular modifications. In addition, hair color changes as we age, so hair color is lightened for older characters.
There are other less immediately noticeable aspects of portrait generation that I hope players will appreciate as they become familiar with the game.
Facial feature sprites for male / female faces are drawn in matched pairs. This allows me to have children that inherit features from both mother and father, and leads to entire families that share facial similarities.
Dress color is picked so that it advertises the affiliation of the character to their house. Characters of a house whose flag is blue will be wearing blue and yellow, blue and green, different shades of blue, etc. Seeing all the characters of a house in one list will show a clear chromatic theme.
This is a good time to give a shout out to the awesome artist for Star Dynasties, Ven Locklear. Check out his portfolio.
I have been working on the information panels the player will use to explore the state of the world in Star Dynasties, so this feels like a good opportunity to provide a quick tour of some of the basic concepts of the game.
Justice / Morality
One of the core features of the character simulation is the concept of moral acts, i.e. actions such as declaring war, cheating on your wife, helping a family member in need, etc., that will trigger a universal condemnation or approval. An example of a non-moral act would be marrying off your son – the newly weds and new in-laws will be happy and pleased with you (presumably), but the rest of the world won’t really care. With moral acts, the world is watching and reviewing it’s opinion of you. On top of that, many moral acts will result in someone bearing a grievance (the victim of the action) or someone owing the doer a favour (the beneficiary of the action). The value of this “social memory” is that it modifies how subsequent moral acts are interpreted. For example, deposing one of your rulers from power is an immoral act, and will usually trigger a widespread opinion change against you for being tyrannical. However, imagine a narrative where the ruler had recently refused to muster their forces and come to your assistance during an attack by another faction. This was an immoral act that granted you a grievance. If you move against the ruler now and depose them, your action will be seen as justified… a punishment of the ruler’s misdeed.
Visibility and Secrets
Visibility is an important part of the game. Your visibility is limited to the visibility of your character, so at any one time you will only see a small portion of the galaxy and its inhabitants. This can lead to some interesting consequences. For example, if your heir is a widely travelled ambassador, you will have a much larger awareness of the world around you when they inherit. If instead you keep your heir locked away in some unimportant role on your home system, you may find that your view of the world shrinks when they take the reins.
As a general rule, if you know of a character then you have full visibility of what they get up to. There are some explicit exceptions to this rule, for e.g. committing murder, which are secret. Secrets stick around in the background and can be discovered by characters with a high Security skill (or by your house’s Security wing), and subsequently revealed to inflict political damage.
Apart from characters, houses are the most important unit in the game, and most of your political dealings will be with the heads of these houses, be they the ruling houses of colonies, the leading houses of factions, or lowly noble houses that vie for power within a colony.
Within a house, characters can be assigned to a role from Administration, Military, Diplomacy, Security, and Medical; with one character in each area appointed as the council member of that area. Right now, the members you have assigned in these areas largely contribute passively to your house’s attributes (e.g. Administration increases your income, and Military increases your combat bonuses), but the plan is to flesh out these systems so that each role has it’s own narrative building blocks. For example, characters assigned to Diplomacy can be sent as ambassadors to other houses. An ambassador could (based on their skills and relationships) create a rapproachment with a rival or commit a faux pas that triggers conflict.
When a dynasty has ruled for a long time, it’s head acquires a legitimate (and inheritable) right to rule or lead particular systems. Claims interact with the morality system in that it is generally considered immoral to replace the ruler or leader of a system with someone who has less of a right to rule that system. But if someone has no legitimate claim, then they are mostly considered fair game. Similarly, if you can find someone with a strong claim on a system, you can use them as a political excuse to declare war (although you’ll still suffer politically later if you annex other systems).
Factions are the highest level of social grouping of the game and relationships and conflicts between factions will generally be the most important strokes of the narrative you play through in the game.
This week I focused on the basic implementation of two UI panels; the History panel (which shows a list of historical events that have happened) and the Family panel, which shows a list of the characters that are related (by blood or marriage) to a particular character. I also spent some time getting the text to look sharper.
Smoothly Scrolling Lists
The main challenge was performance; I’m using ScrollRect objects in Unity and they are surprisingly slow. I’m not just talking about the need for object pooling (where you reduce the number of game objects used to render a list by re-using a small number of them to populate just the visible portion at any one time), but in the way ScrollRect inherently functions. To the best of my understanding a ScrollRect recalculates the layout of its contents and rebuilds each graphical element every time the scroll position is changed. The implication is that if you’re going to use a ScrollRect and want smooth scrolling, you need to make sure it’s contents are as lightweight as possible for Unity to rebuild. One of the components I am using liberally in the UI is the character card, a character portrait with some decoration, to give you at a glance; Faction affiliation, House affiliation, the character’s general importance (i.e. are they a leader, a ruler, etc.), the character’s happiness, your opinion of the character, and the character’s opinion of you.
In the History panel list, I can have up to four of these character cards per row. The control was using an “Auto Layout” based layout, and I got a significant speed up switching to a RectTransform based layout (i.e. where I calculate the position of each element manually once rather than let Unity dynamically recalculate it each rebuild).
The other tweak that seems to have helped considerably is an implementation detail of how I was doing object pooling on the list itself. It turns out that changing the parent of a UI element also triggers a significant amount of rebuilding, so it is cheaper to leave one or two masked out list elements past the edge of the visible area, rather than temporarily reparenting them to somewhere else until they are reused to show new items.
Sharper Text Rendering
I’m using TextMeshPro to render the text, primarily because I need the ability to embed links and sprites within the text (I’m under the impression that TextMeshPro is also generally the most robust text rendering solution available for Unity). A weakness of all the text renderers in Unity I’ve tried is that they struggle with drawing small text sharply. The text ends up looking quite fuzzy, rather than nicely anti-aliased but still sharp. The only solution that I can see (at least with TextMeshPro) is to find or roll your own bitmap fonts that are of the exact size you need and use them at the explicit size they were designed for (i.e. no scaling at all of either the canvas, game object, or text component font size). If you want text at different sizes you will need to use different bitmap fonts at each seperate size, but the results are then pixel perfect. And past a certain size, TextMeshPro’s ability to dynamically resize a font works just fine.