概述
在做游戏的文字功能时,一个很通用的需求是,将文字逐字显示出来。
其原理是每次输出时,分别打印字符串的子串,对于“我早说过,这个古董是赝品啦!”这句话,输出时,像下面这样输出
我
我早
我早说
我早说过
我早说过,
我早说过,这
我早说过,这个
我早说过,这个古
我早说过,这个古董
我早说过,这个古董是
我早说过,这个古董是赝
我早说过,这个古董是赝品
我早说过,这个古董是赝品啦
我早说过,这个古董是赝品啦!
普通文字,这样处理就够用了,但我们知道,游戏里面的文字是可以带有富文本信息的,如下面
它的源字符串,可能像这样: 我早说过,这个<yellow>古董</>是<red>赝品</>啦!这个时候如果按照原来算法那样逐字打印,就会有问题
这
这个
这个<
这个<y
这个<ye
这个<yel
这个<yell
这个<yello
这个<yellow
这个<yellow>
这个<yellow>古
这个<yellow>古董
这个<yellow>古董<
这个<yellow>古董</
这个<yellow>古董</>
这个<yellow>古董</>是
这个<yellow>古董</>是<
这个<yellow>古董</>是<r
这个<yellow>古董</>是<re
这个<yellow>古董</>是<red
这个<yellow>古董</>是<red>
这个<yellow>古董</>是<red>赝
这个<yellow>古董</>是<red>赝品
这个<yellow>古董</>是<red>赝品<
这个<yellow>古董</>是<red>赝品</
这个<yellow>古董</>是<red>赝品</>
这个<yellow>古董</>是<red>赝品</>啦
这个<yellow>古董</>是<red>赝品</>啦!
游戏里效果像这样,
可见这种逐字符的输出是不对的。
我们提取真正的有效字符,应该变成这样:我早说过,这个古董是赝品啦!
所以逐字符的输出目标应该是“我早说过,这个古董是赝品啦! ”才对。然后在输出期间,针对当前的字符串进行富文本的信息插入处理,保证每次输出的字符串都是带有富文本信息的。
根据这个思路,我们把源字符串拆为两部分,一种是不带富文本信息的纯字符串,一种是富文本的格式信息。
源字符串中的每一条富文本都有重要的三条信息
- 富文本的StartIndex,这个index是纯字符串中的Index。
- 富文本的EndIndex,这个index是纯字符串中的Index
- 富文本的本体信息如<yellow>。
这几条信息有什么用呢?当我们对纯字符串进行逐字输出时,我们可以根据子串的长度来判断一个富文本信息是否应该被插入。如当输出的子串是“我早说过,这个古”的时候,我们发现第一个富文本的StartIndex在古的前面,也就是7,而这个子串的长度是8,所以我们判定,此时应该在7的位置插入富文本信息。
第二个问题是,我们知道该插入富文本信息,但插入的内容是什么呢?内容就是我们保存的本体信息如<yellow>,把这个插入。
第三个问题,富文本的结束符应该插入在什么位置?这个就是EndIndex要干的事情,我们判断当前的子串长度是否包含了EndIndex,如果包含,则说明本条富文本的信息应完全被输出,所以我们只需要在EndIndex处插入结束符</>即可,如果不包含,说明什么?说明本条富文本的信息还在输出过程当中,所以我们只需要在子串的末尾插入</>即可!
我们在插入的时候进行反向插入,为什么要反向插入呢?因为反向插入的时候,不会影响前面的字符Index,而正向插入会导致后面的Index失效。
比如"小<red>小明</>很<yellow>可爱</>!"
<red>的StartIndex =1 ,EndIndex=3,
<yellow>的StartIndex=3,EndIndex=6
如果我们进行正向插入,则当插入<red>之后,子串变为
小<red>小明</>很可爱!
此时如果要插入yellow,再用StartIndex=3 EndIndex=6去处理,会完全错误!因为原生的子串已经被破坏了!
而如果我们进行反向插入,则当插入yellow的时候,因为它是最后一个,所以子串完全是正确的,子串变为
小小明很<yellow>可爱</>!
此时再处理<red>,我们会发现red的StartInde=1,EndIndex=3还是有效的!因为从后往前处理不会破坏前面子串的Index!
以此类推,无论有多少个富文本信息,都可以正确处理,最终效果大致如下
talk is cheap, show me the code!
USTRUCT(BlueprintType)
struct FRichTextFlag
{
GENERATED_USTRUCT_BODY()
UPROPERTY(BlueprintReadOnly)
int StartIndex = -1;
UPROPERTY(BlueprintReadOnly)
int EndIndex = -1;
UPROPERTY(BlueprintReadOnly)
FString FlagString;
};
USTRUCT(BlueprintType)
struct FRichTextContext
{
GENERATED_USTRUCT_BODY()
UPROPERTY(BlueprintReadOnly)
FString SourceString;
UPROPERTY(BlueprintReadOnly)
FString PureString;
UPROPERTY(BlueprintReadOnly)
TArray<FRichTextFlag> FlagData;
};
void InitRichTextContext(const FString& RichText, FRichTextContext& RichTextContext)
{
// 我早说过,这个<yellow>古董</>是<red>赝品</>啦!
RichTextContext.SourceString = RichText;
RichTextContext.PureString.Empty();
RichTextContext.FlagData.Empty();
bool SeekFlagStart = false;
bool SeekFlagEnd = false;
FRichTextFlag TempFlag;
FString TempString;
for (int i = 0; i < RichText.Len();)
{
auto CurrentChar = RichText[i];
if (SeekFlagStart)
{
TempString.AppendChar(CurrentChar);
if (CurrentChar == '>')
{//seek start over
SeekFlagStart = false;
SeekFlagEnd = true;
TempFlag.FlagString = TempString;
TempFlag.StartIndex = RichTextContext.PureString.Len();
}
}
else if (SeekFlagEnd)
{
if (CurrentChar == '<')
{//找到尾部标识
SeekFlagEnd = false;
TempFlag.EndIndex = RichTextContext.PureString.Len();
RichTextContext.FlagData.Add(TempFlag);
i += 2;
}
else
{
RichTextContext.PureString.AppendChar(CurrentChar);
}
}
else
{
if (CurrentChar == '<')
{
SeekFlagStart = true;
TempString.Empty();
TempString.AppendChar(CurrentChar);
}
else
RichTextContext.PureString.AppendChar(CurrentChar);
}
++i;
}
}
FString GetRichTextSubString(const FRichTextContext& RichTextContext, int PureCharLength)
{
FString SubString = RichTextContext.PureString.Left(PureCharLength);
int InsertIndex = -1;
for (int i = 0; i < RichTextContext.FlagData.Num(); ++i)
{
if (RichTextContext.FlagData[i].StartIndex < PureCharLength)
InsertIndex++;
else
break;
}
for (int i = RichTextContext.FlagData.Num() - 1; i >= 0; --i)
{
const auto& Data = RichTextContext.FlagData[i];
if (i <= InsertIndex)
{
if (PureCharLength >= Data.EndIndex)
SubString.InsertAt(Data.EndIndex, TEXT("</>"));
else
SubString.Append(TEXT("</>"));
SubString.InsertAt(Data.StartIndex, Data.FlagString);
}
}
return SubString;
}
本文采用的是倒序插入法,正序插入法也同样可以实现,有兴趣的同学可以改造一下。
最后
以上就是眼睛大荔枝为你收集整理的Unreal富文本RichText的逐字打印问题的全部内容,希望文章能够帮你解决Unreal富文本RichText的逐字打印问题所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复