Add text to overlay unit sprite?

I’m working on a 2D strategy game, and I’d like to use generic square sprites and then overlay them with stats that change dynamically – e.g., attack strength, movement, health.

I tried adding a simple Text component, but I couldn’t get that to display at all. I also tried adding a Panel, then an Image, then a TMP or text object, but I had trouble getting it to display anything, and it seemed like overkill for what I want to do.

Then I tried a TextMeshPro component, using a sprite asset that I put in the Resources/Sprite Assets folder. (I found a helpful 45-minute video on this.) I successfully made a sprite asset, just using my one sprite for now. (I do have many variants I’d like to use, and I guess I’ll have to make a sprite sheet or such for that, but that’s a problem for another day.) I typed in the code <sprite=“Infantry” name=“Infantry”> and then, immediately aftewards or on the next line, “6-3,” (some sample stats). That does display the sprite and text both, but it displays the text next to the image, not overlapping it. In Scene mode, I can sorta get the text to overlap the sprite by setting the text’s sorting layer. But in game mode, it seems to revert: the sprite appears, then the text below it, with no overlap.

What’s more, if I change the text font, the sprite resizes too, which is not what I want. Note that the 6-3 is a lot larger than the space I want to put it in, at the bottom of the counter.

Maybe I should try a Button instead? I don’t really need Button functionality, but they look right – a background sprite with foreground text. Another option is just to create a sprite for every conceivable strength/movement combination, but that doesn’t really suit this project. (That might work in a game where every unit is unique, but that’s not the case with mine; plus I need dynamic stats.)

I apologize if this seems like an embarrassingly simple question, but I’ve spent a couple hours reading the documentation and Googling, and I’m still stumped. Thanks in advance for any suggestions.

It can be done, but perhaps you’re not finding the right combination of things.

There’s TMPro, TMPRoUGUI, and then the old Text, and finally the ancient TextMesh objects.

Three (3) primary ways that Unity draws / stacks / sorts / layers / overlays stuff:

In short,

  1. The default 3D Renderers draw stuff according to Z depth - distance from camera.

  2. SpriteRenderers draw according to their Sorting Layer and Sorting Depth properties

  3. UI Canvas Renderers draw in linear transform sequence, like a stack of papers

If you find that you need to mix and match items using these different ways of rendering, and have them appear in ways they are not initially designed for, you need to:

  • identify what you are using
  • search online for the combination of things you are doing and how to to achieve what you want.

There may be more than one solution to try.

Additional reading in the official docs:

And SortingGroups can also be extremely helpful in certain circumstances:

1 Like

Thanks for your reply. I’ll take a look at the resources you linked.

Those sources were helpful. I was able to make a Button that behaves the way I want, but it has to be a child of a canvas. That’s okay, but I don’t need the units to be buttons. I was also able to use a Text Mesh to display the stats right on top of the sprite, but that legacy tool produces fuzzy text. I see why Text Mesh Pro is the thing now. But I’m having trouble getting TMP to work the way I want with anything other than a Button.

I’ve had this same issue in other engines. It must not be an easy thing for engines to support, or maybe there’s just not much demand for it. In GameMaker Studio, I just ended up making different sprites with different stats. I guess I could do that here too. Do folks do that commonly in Unity as well?

There’s a trick to this… you set the font size quite large and then scale the object way down. You can get the text as sharp as you need.

EDIT: disregard the characterSize field… make sure you are changing the Font Size. Try object scale of 0.1, 0.1, 0.1 and font size of 64 (for example)

1 Like

Also, so you know, you can ask specific UI questions on their forums here.

1 Like

Thanks for your replies. Ooh, I’ll try that TextMesh trick! And thanks for the tip about the UI forums. I wasn’t quite sure where to post this thread, so that’s helpful. But before I post over there, I’m gonna play with the TextMesh more.

1 Like

TextMesh is super oldskool but I still use it sometimes just because it’s simpler than hauling in the massive TMPro setup if I’m just doing something ultra-simple.

1 Like

Yep, the old-skool approach seems easier for me here. It’s given me great results! Here’s the sprite I showed above, which was empty except for the pretty background, the NATO symbol for infantry, and the brigade “X” above that. Using your TextMesh trick, I’ve added 6-3 (combat-movement), its designation sideways on the left, and its “health” in red on the right. Your numbers (64 font) were perfect; I just had to tweak the item scale a bit to make text larger or smaller.

I did have to find a way to tell the text to display above the sprite. By default, the TextMesh apparently adopts the sorting layer of its parent, and the TextMesh’s sorting layer does not seem to be exposed in the Inspector. But in searching these forums, I found a very simple script that allows one to directly alter the sorting layer of the TextMesh itself. So I just created a new “UnitText” sorting layer, and presto, it worked. In case anyone stumbles on this thread in the future, I thought I’d repeat it here:

 public class SortText : MonoBehaviour
    {

        void Start()
        {
            this.gameObject.GetComponent<MeshRenderer>().sortingLayerName = "UnitText";
            //or whatever your layer name is

            this.gameObject.GetComponent<MeshRenderer>().sortingOrder = 50;
        }
    }

The only remaining issue is “stacks” of units. Right now the lowest unit on a stack will still display its text on the top UnitText sorting layer. I can solve that by suppressing text on all but the topmost unit. That means a tad more scripting, but it still might be easier (and less annoying and less bug-prone) than trying to swap in a new sprite every time a stat changes. That would mean a lot more sprites, and maybe a dictionary or something to link a sprite with a particular stat. Not impossible, but maybe more of a pain.

I hope that’s a reasonable plan. I’m still curious whether people just make custom sprites for each possible permutation of a military unit’s stats.

Beyond a small number that you might make by hand in advance, I doubt that anyone does that.

BUT… you can make “sprites” or textures at runtime, which can help you “snapshot” your stack of stuff on a given unit, and then be able to stack that one single sprite above or below other units that have also had that done.

The process involves primarily one of two different ways:

  • use a RenderTexture to snapshot what you have, make a sprite out of it and use that. This seems potentially the easiest for you.

  • use Graphics.Blit() to build up your sprite one layer at a time, probably less useful in your case.

There’s other ways too but the above are the two main ways. You also probably need some kind of sprite manager to keep track of it all or it will get slightly hairy.

1 Like

Oh, those are helpful ideas too! I had just been planning to use position offsets and sorting layers to stack units on top of one another – essentially to offset them enough so the user sees them as a stack of two, three or maybe four units. Alternatively, I had thought I might use Photoshop to make sprites of generic 2- and 3-unit stacks and then just swap them in and out at runtime, underneath the top unit, as needed. But your ideas sound more elegant. I will think on it.

Stacking is one of the most sought-after features by digital wargamers, but many wargames don’t implement it because it’s a pain, and because AI. (Even the two most recent entrants in Sid Meier’s Civilization series avoid stacking.) But I like stacking, so I’m doing it, because I like making games I like to play. I’ve done stacking before, in GameMaker, and it worked, albeit messily, and my AI did have fits with it. Unity’s lists will make the mechanics a lot easier. It’s up to me to try to make the AI work with it better. :slight_smile:

Success! I was easily able to display units with dynamic stats in stacks of three, using sorting layers for both the sprites and TextMesh children. @Kurt-Dekker 's idea of the TextMesh was a lifesaver. I like this solution because the player can at least get a sense of what’s beneath the top counter. To provide more info on each unit, and to provide an alternative to click-cycling through the stack, I’ll also provide a UI selection panel on the left:

[

And for this, I think I’ll need to borrow another idea from @Kurt-Dekker , a render texture. In theory, I will snapshot each unit in the stack and display them on the panel on the left. Conceptually, I know how to do this. Make a specialty camera; aim it (not quite sure how) at the unit I want to “photocopy”; then project it onto the UI element (the render target?) at left. I just haven’t tried yet. I got distracted playing with the UI Toolkit toys. :slight_smile: I’ll probably stick with UGUI, but the shiny Toolkit is tempting.](Imgur: The magic of the Internet)

1 Like

I’ve made progress with the render texture, and it does display stuff on the UI toggle after converting it to a Sprite – but I’m having trouble positioning the render cam, so I keep getting the wrong image. If I use renderCam.ScreenToWorldPoint, the x, y and z values returned increment by constant amounts every time I set the camera’s position. This mystifies me. I’m not adding the new coordinates to anything; I’m merely setting transform.position anew every time. To make matters worse, the coordinates values I see in the Inspector don’t match the values I see using Debug.Log when I try to investigate what’s happening to the coordinates. It’s all very perplexing.

After Googling, I tried using the main camera rather than the renderCam to convert to world points, but nothing changed. I also tried using ScreenToViewportPoint and then ViewportToWorldPoint, but that didn’t work correctly either. I tried giving the renderCam a parent rig and using its transform; that changed nothing. I tried giving the renderCam a child cam and having it perform ScreenToWorldPoint; that didn’t work either.

I understand that the camera needs world coordinates, not screen coordinates; I’m guessing the mixup results from using a camera set to capture a relatively tiny square piece of the screen. I’m at a loss on what to do.

Incidentally, if I do get this to work, can I photograph units in the middle and bottom of the stack by temporarily disabling the units on the top of the stack? If not, I may have to think about a different approach.

Maybe this requires a new thread in the UI forum? I’m not quite sure where to post questions about 2D cams used for render textures.

My first thought is that your post above this looks fine, so you might want to revert the ReenderTexture experiment for now, unless you find a new need that your solution above doesn’t address.

1 Like

Yeah, for now I’m going to declare victory and move on. I’ve marked the thread as resolved, since you’ve answered my original question (about displaying unit stats dynamically, using TextMesh).

I may post a separate question about Render Textures, possibly in the UI forum, because I’m having trouble positioning the camera, and I can think of a couple possible uses for a render texture. (E.g., a minimap! And I like the idea of taking a snapshot to put on a UI select-unit bar.) So I’d like to learn the technique. No matter what I try, the camera veers off in strange directions. I’m going to experiment more with camera.ScreenToWorldPoint to try to understand why I get unexpected output from that function.

I admire that. Whenever I use RTs in almost any capacity, I usually make a “far away stage” where all the photography happens, physically removed from the rest of the scene near (0,0,0)

See enclosed for fully operational example, intended to show how to alpha-down several sprites at once, sprites that might be overlaid and hence look funny if two 50% alpha sprites overlapped.

1 Like

I didn’t find your example, but I did enjoy watching some of your gameplay videos and poking around your itch.io page. Your Kurt flight sim looks like a blast! You should charge something for that. :slight_smile:

Oops, I failed. Now try, attached here.

Thanks! As with most of my games, I need to go back and make more interesting content to run through them!

7844700–994638–RenderTextureSetup.unitypackage (41.2 KB)

Ah great, thanks for that package! Very helpful. With your help, I’m on the right track now – I was just foolishly converting what I thought were screen coordinates when in fact they were already world coordinates. Now the only issue is that I seem to be photographing the wrong hexes, but that’s a logic issue that (theoretically) should be easy to solve!

Edit: Hmm, not so easy to solve. What I want is to click on a hex and see its top unit displayed as a rendertexture on a panel to the left. This works – except I get an image from the previously-clicked hex. It’s like a weird look into the past. I’ve tried calling my snapshot function twice in a row, to update the rendertexture to the object the camera is at right now, but that doesn’t change this behavior. Any suggestions on how to “update” the rendertexture.active method?

So you make games in your spare time, or is this a full-time gig? Either way, very impressive. :slight_smile: