Multiple colors in a line of text to highlight keywords

Started by rongel, Wed 10/04/2024 09:38:22

Previous topic - Next topic

rongel

Hello!

I looking into using several colors in a line of text, highlighting key words and effects. Is this possible at all?
I'm experimenting with text labels and the dialog system. The point is to do something a bit similar than in Slay the Spire and many other RPG's:



I'm using abstauber's CustomDialogGui 1.9, which is great, but it doesn't offer that kind of functionality with colors afaik.

When searching the topic, I found SSH's Hypertext module, which is supposed to do this, but the module is very outdated, and doesn't seem to work that well anymore.

Thanks for any help! 
Dreams in the Witch House on Steam & GOG

Matti

I was just wondering the same thing. I also experimented with text labels and wasn't able to have different colors within a single label/line. Would be a great functionality.

Snarky

To do this you will need to draw the string yourself in multiple steps, each with a different color, using DrawingSurface.DrawString().

That isn't particularly difficult, but the question is how you control which colors to use for the different parts of the text. SSH's module allows you to use markup to modify the appearance, but parsing that takes a bit of work. You could of course hard-code it, but at that point you might as well just use separate labels.

rongel

Quote from: Snarky on Wed 10/04/2024 11:50:39To do this you will need to draw the string yourself in multiple steps, each with a different color, using DrawingSurface.DrawString().

Thanks Snarky for the advice! I'll look into DrawingSurface, but I have a feeling that path will complicate my current setup too much. Currently I have a custom dialog Gui and a text label for the descriptions. They work together nicely, and it's easy to create and edit similar scenarios as in the screenshot. So I might just try to use separate labels and see if something similar is possible.

Quote from: Matti on Wed 10/04/2024 09:52:01I was just wondering the same thing. I also experimented with text labels and wasn't able to have different colors within a single label/line. Would be a great functionality.

I agree 100%!
Dreams in the Witch House on Steam & GOG

Snarky

Quote from: rongel on Wed 10/04/2024 14:00:35
Quote from: Matti on Wed 10/04/2024 09:52:01I was just wondering the same thing. I also experimented with text labels and wasn't able to have different colors within a single label/line. Would be a great functionality.

I agree 100%!

Again it comes back to: how would you configure it?

Matti

Quote from: Snarky on Wed 10/04/2024 14:02:54Again it comes back to: how would you configure it?

Wouldn't it be (theoretically) possible to have a textcolor for a specific String instead of a whole label? That way one could add several strings in a row with different colors each.

Crimson Wizard

#6
Quote from: Matti on Wed 10/04/2024 15:00:53Wouldn't it be (theoretically) possible to have a textcolor for a specific String instead of a whole label? That way one could add several strings in a row with different colors each.

Label has a single String Text property and it must be able to return everything that was set. If it has to support multiple strings each with its own color, then Label would need to be redone into having an array of Texts, paired with color values, at least.


A more universal way to do this would be to implement an extended type of Label that supports some kind of a display formatting in a string, using tags.

eri0o

I think tags are a nice way, it could be like there is a DrawStringWrapped it could have a DrawFancyStringWrapped or whatever the name for it that adds such feature - in addition to LabelFancy or whatever, like Winforms has TextBox and RichTextBox.

There's a particular Game Maker plugin/module called Scribble (made by Juju Adams) that has a ton of interesting features for writing text and one of them is this addition of having text using multiple colors. It uses a tag system somewhat similar to the one we have here on the forums: https://www.jujuadams.com/Scribble/#/latest/text-formatting

rongel

Quote from: Crimson Wizard on Wed 10/04/2024 15:33:51A more universal way to do this would be to implement an extended type of Label that supports some kind of a display formatting in a string, using tags.

Quote from: eri0o on Wed 10/04/2024 23:15:02I think tags are a nice way, it could be like there is a DrawStringWrapped it could have a DrawFancyStringWrapped or whatever the name for it that adds such feature - in addition to LabelFancy or whatever, like Winforms has TextBox and RichTextBox.

It would be great to have that kind of functionality added to labels. It could be used to emphasize a certain word in a dialog (by changing the font / color), or by making fancy RPG Guis for example.

If that functionality could be added to dialog options as well, everything would be perfect  :=
Dreams in the Witch House on Steam & GOG

Crimson Wizard

#9
This may be done with a script module, if it's only colors then this should not be overly complicated imo.

General idea may be following:
1. Parse the input string, write down an array of positions where color changes, and get a pure string without tags.
2. Calculate line splitting for a pure string, as you would with a normal text.
3. Draw parts of lines line by line and colored section by section.

Snarky

#10
Yeah, that's part of what SSH's module does, but it also has things like hyperlink support that isn't really needed.

What would we want to be able to support? Of things AGS lets you control, I can think of:
  • Font
  • Color
  • Outline (width, color)
  • Opacity

(Also, if you're mixing different fonts, you probably need some way to control the baseline alignment, though that might be more a module configuration thing than something in the markup.)

Some further options would need to be "faked" (using different fonts or drawing tricks), but it might be convenient to make them accessible via a simple markup:
  • Bold
  • Italic/slant (pseudo-italic)
  • Underline
  • Strikethrough
  • Size/scale (horizontal and vertical separately?)

Other possible formatting/markup features:
  • Advanced outline options (blur, rounded/sharp, shadow...)
  • Background color (highlight)
  • Embed images (e.g. emojis)
  • Superscripts, subscripts
  • Character spacing
  • Layout (center/right-align/justified, indentation, etc.)
  • Numbered/bulleted lists (multi-level/nested?)
  • Styles (user-defined, for convenience)

And something I think will be a very useful general feature:
  • Identify which part of the text a certain coordinate belongs to

Things I don't think should be supported by such a module:
  • Animation/effects (including typewriter text)
  • Hyperlinks
  • Other types of content aside from linear text (tables, footnotes, formulas)

I think the biggest decision to make is how to encode the formatting. XML/HTML (+CSS?) or LATEX seem like overkill. Markdown would be a nice choice in a lot of ways, but it doesn't support setting color. BBCode (like on this forum) runs into the problem of AGS interpreting [ as a linebreak.

Quote from: eri0o on Wed 10/04/2024 23:15:02There's a particular Game Maker plugin/module called Scribble (made by Juju Adams) that has a ton of interesting features for writing text and one of them is this addition of having text using multiple colors. It uses a tag system somewhat similar to the one we have here on the forums: https://www.jujuadams.com/Scribble/#/latest/text-formatting

It's an interesting point of comparison, and I like the approach of not having to close tags. The only tags I see as immediately relevant are color and possibly alpha and scale (though with AGS's text rendering I don't think scale will give very good results).

eri0o

My feature list in mind is very different, lol, as for me typewriter and animation are much more important than bold and italics, which I wouldn't support (they don't work in game UI in my opinion), I also wouldn't support bullet list (again, think if you need you are already with some button system, which doesn't make it useful)... I think if you can have enough to do a system like the one in Zelda Ocarina of Time and Undertale, then it should work.

I thought about making such text module in the past but imagining it's output is a sprite, there's not something that could receive it to implement the rest of the dialog system, so you endup to have to go beyond and also do all the dialog boxes in the same module.

Here is a module for love2d called Moan :  https://github.com/elennick/Moan.lua



Note it also includes delays when showing typewritten text, I remember Rpg maker 2000 supported that too using a tag system.

Here is SYSL-Text : https://github.com/sysl-dev/SYSL-Text



I think I probably would do this with one Overlay per letter to be able to be more flexible but the issue is you lose the easyness to share it with some other module, this is why in my head it has to become a big swiss army knife of talking module.

But remember... text rendering hates you.

Snarky

#12
The main reason I wouldn't include typewriter text is because there already is a typewriter module, and because then you need some way to update it, keep track of state, etc., which adds a whole other level of complexity. (Though I agree that it should be compatible, so that you could combine formatted text with a typewriter effect or other kinetic typography.)

If we're going to have text formatting, I think things like bold and italics are pretty essential. Don't think just of the UIs, but use-cases like dialog (e.g. to use italics for thoughts, or italics or bold for emphasis or sound effects in conversations), games where you can read books or look at web-pages, or in-game documentation, etc. The use-case I had in mind for lists was for if we could parse markdown documents, so that you could read e.g. a change log from file and display it in-game.

Crimson Wizard

#13
I would also separate typewriting from text formatting, as typewriting is more or less step-by-step alteration of "current text" (or "current position"), and this may be done by a higher-level "class", similar to how existing typewriter modules use labels or textual overlays.

In other words,

  Typewriter
            ->  Fancy text drawer
                        -> Drawing surface

Snarky

Quote from: eri0o on Thu 11/04/2024 11:19:42I think I probably would do this with one Overlay per letter to be able to be more flexible but the issue is you lose the easyness to share it with some other module, this is why in my head it has to become a big swiss army knife of talking module.

This is something I would try hard to avoid. When I've been working on the SpeechBubble and Lipsync modules, the architecture and implementation have often pushed in this direction, but the scope just gets so gigantic and unwieldy that it's almost impossible to complete.

What I do agree with is that it would make sense to also control typewriter dynamic text formatting (e.g. speed) with tags in the same way as rich text.

Rather than try to do everything in one big module, would it be possible to have a parser that could be generic enough that it could simply report the tags/commands detected one by one, and then different modules could handle the ones they know about?

Crimson Wizard

#15
By the way, in regards to module communication; it's a fact that AGS script currently does not have a easy method of making "interfaces", as in, abstract classes that declare methods which other classes implement.

It's possible to extend a base class, and upcast a managed pointer to base class. This allows to make a parent class with the most basic commands and properties, which may be then extended by an actual implementor. For example:
Code: ags
managed struct TextDrawer
{
    protected String fullText;
    protected int drawFromChar;
    protected int drawToChar;

    import void SetText(String text);
    import void SetDrawRange(int from, int to);
};

// then elsewhere

managed struct FancyTextDrawer extends TextDrawer
{
    ... do actual stuff
};

/*managed?*/ struct Typewriter
{
     import void SetTextDrawer(TextDrawer *text_drawer);
};

In the above example, a Typewriter class could have a pointer to TextDrawer and assign from/to properties, telling actual FancyTextDrawer which part to draw.

The problem with above is that this imposes a base class restriction on implementing classes. There may be only 1 parent class, and if there's a case where extending something is not convenient, or we need more than 1 interface type implemented by the same class for some reason, then this approach no longer works.


So I've been thinking if there's a workaround in AGS script, and I think that an alternative is a helper managed struct serving a "messenger" between 2 classes.

Consider following example:
Code: ags
managed struct TextDrawerInstruction
{
    int drawFromChar;
    int drawToChar;
};

/*managed?*/ struct FancyTextDrawer
{
    import void SetDrawerInstruction(TextDrawerInstruction *inst);

    protected TextDrawerInstruction *myInst;
};

/*managed?*/ struct Typewriter
{
    import TextDrawerInstruction *GetDrawerInstruction();

    protected TextDrawerInstruction *myInst;
};

Here we do not require the class to extend any parent, but require that it could accept a certain object by pointer, and read that object when deciding what to do. Typewriter in this example has to give this "message" object out and change its parameters over time, thus saying that things should be drawn differently.

Cassiebsg

Just meddling a bit.  :-D
I like the tags idea, and while we can't use [ maybe we could use some other character(s) that would be unlikely to show up in text, like ##C123456#B#U## could maybe work (doesn't need to be #, just something unlikely to show up in text labels?

player.Say("This is a ##C123456#B#U## test.");
There are those who believe that life here began out there...

Scavenger

I've done something similar, though it only works in 8-bit mode.



With the markup being:
Code: ags
//That's @j1BILL@n. @c009Bill Beltbusta!@c015 Don't you forget it!
It's pretty easy to parse something like this if you have an escape character. For it I don't use labels, just dynamic sprites and buttons. Though it's far from usable outside of the contexts I'm using it for.

eri0o

The reason it would be good to get it in-engine is I believe I can hack down a module that does whatever in a low resolution game. But I don't think I can do the same efficiently for a high resolution game, it's just that raw sprite drawing is slow through the script API right now to do this in there.

But to me anything like markdown feels completely useless in a game context and animation is much more relevant. In a high resolution game if a textbox takes a quarter of the screen, that will be the amount of pixels per frame I will be pushing to texture convertion per frame.

Anyway, I think it's doable for a script context for low resolution... But maybe a plugin would be easier to hack up to demonstrate it going since the plugin API for text already exists.

Crimson Wizard

#19
I think this conversation goes somewhat sideways. It started with a very specific user request, but now there's a discussion about a hypothetical functionality which contains a list of features, and everyone has got their own list.

The starting request has 2 conditions, at least from my understanding:
1. Being able to color particular words or sequence of words.
2. The text is static, so "raw" drawn once and then kept displayed without changes.

Is this correct, or was there anything else to it?

I think that above may be easily done by a script module today, and should not cause much trouble in high resolution (latter may be tested for performance to see if I'm right or wrong).

Perhaps it would be proper to focus on this problem in this thread, and find out how to solve this within the existing engine version?



For a more complicated functionality, I believe, it's a matter of deciding the purpose and use cases, then writing specification of a desired behavior. Personally, I can easily see a use case for both an animated text, and a emulation of a markdown or HTML page in game. These may require separate approaches, while having some "base functionality" beyond (there's always ways to pick out common code).

Implementation is always a separate question. Once there's a requirement for behavior, we can think about how to implement this, having performance in mind.

It may turn out that it is not possible to run "something" fast in the current engine, because engine does not support modern fast ways of doing things. Like rendering text glyphs as textures, for example, or similar. That's fine, we may leave that "something" for the better times, or keep it potentially slow, available for users who may live with that, and writing down list of things we'd need in the engine to make it faster.

But please, don't plan on pushing the giant formatting feature into the engine only because it's slow to make in script at the moment. I do not think that this whole text formatting with colors and animations should be inside engine, because engine is meant for "basic logical bricks". Everyone will have their own ideas about the formatting behavior, and should have a chance to implement them. IMO it's better to look at this in opposite way, and figure out what the engine could provide to make things like this formatting easier to do in script. Like, if you remember, there was a suggestion of hardware accelerated drawing: https://github.com/adventuregamestudio/ags/issues/2059



On another hand, there may also be various tricks already available to speed things up, if updating a text made with raw drawing appears slow. I don't know about having each letter as an overlay, but it could be separate overlays for animated parts, for example.
Another thing, for a typewriting, instead of redrawing whole thing after every letter is added, one could draw the full text at once, and then hide the part that should not be visible yet with an overlaying object.
Well, just saying.

SMF spam blocked by CleanTalk