Unreal富文本RichText的逐字打印问题

23 篇文章 12 订阅

在做游戏的文字功能时,一个很通用的需求是,将文字逐字显示出来。

其原理是每次输出时,分别打印字符串的子串,对于“我早说过,这个古董是赝品啦!”这句话,输出时,像下面这样输出

我早

我早说

我早说过

我早说过,

我早说过,这

我早说过,这个

我早说过,这个古

我早说过,这个古董

我早说过,这个古董是

我早说过,这个古董是赝

我早说过,这个古董是赝品

我早说过,这个古董是赝品啦

我早说过,这个古董是赝品啦!

 

 普通文字,这样处理就够用了,但我们知道,游戏里面的文字是可以带有富文本信息的,如下面

它的源字符串,可能像这样: 我早说过,这个<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>赝品</>啦!

游戏里效果像这样,

可见这种逐字符的输出是不对的。

我们提取真正的有效字符,应该变成这样:我早说过,这个古董是赝品啦! 

所以逐字符的输出目标应该是“我早说过,这个古董是赝品啦! ”才对。然后在输出期间,针对当前的字符串进行富文本的信息插入处理,保证每次输出的字符串都是带有富文本信息的。

根据这个思路,我们把源字符串拆为两部分,一种是不带富文本信息的纯字符串,一种是富文本的格式信息。

源字符串中的每一条富文本都有重要的三条信息

  1. 富文本的StartIndex,这个index是纯字符串中的Index。
  2. 富文本的EndIndex,这个index是纯字符串中的Index
  3. 富文本的本体信息如<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;
}

本文采用的是倒序插入法,正序插入法也同样可以实现,有兴趣的同学可以改造一下。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值