在游戏开发中,处理多语言输入是常见的需求。当使用Unity的TMP_InputField组件时,开发者经常会遇到一个棘手的问题:中英文字符在视觉上占据的宽度不同,但系统默认将它们视为等长的字符进行计数。这会导致界面显示不协调,比如12个英文字符"abcdefghijkl"和6个中文字符"你好世界"在输入框中占据的视觉空间差异很大,但系统却认为它们都是12个字符。
在大多数中文字体中,一个中文字符的宽度大约等于两个英文字符的宽度。这种视觉差异会导致:
要实现1个中文等于2个英文的计数方式,我们需要:
核心判断逻辑基于UTF-8编码特性:
csharp复制int leng = System.Text.Encoding.UTF8.GetBytes(singChar.ToString()).Length;
if (leng >= 2) {
leng = 2; // 中文等宽字符计为2
}
我们需要创建一个继承自MonoBehaviour的脚本,附加到TMP_InputField游戏对象上:
csharp复制using TMPro;
using UnityEngine;
[RequireComponent(typeof(TMP_InputField))]
public class BilingualInputLimiter : MonoBehaviour
{
private TMP_InputField inputField;
[Tooltip("最大允许的单位长度(英文=1,中文=2)")]
public int maxUnitLength = 16;
private void Awake()
{
inputField = GetComponent<TMP_InputField>();
inputField.onValidateInput += OnValidateInput;
}
private char OnValidateInput(string text, int charIndex, char addedChar)
{
int currentLength = CalculateVisualLength(text);
int addedLength = GetCharWeight(addedChar);
return (currentLength + addedLength <= maxUnitLength) ? addedChar : '\0';
}
private int CalculateVisualLength(string text)
{
int total = 0;
foreach (char c in text)
{
total += GetCharWeight(c);
}
return total;
}
private int GetCharWeight(char c)
{
// 使用UTF-8编码长度判断字符类型
int byteLength = System.Text.Encoding.UTF8.GetBytes(c.ToString()).Length;
return byteLength >= 2 ? 2 : 1;
}
}
提示:对于韩文、日文等其他宽字符语言,可以采用相同的处理逻辑,只需调整权重值即可。
为了更好的用户体验,可以添加实时显示当前字符数的功能:
csharp复制[Header("UI References")]
public TextMeshProUGUI counterText;
private void UpdateCounter()
{
int current = CalculateVisualLength(inputField.text);
counterText.text = $"{current}/{maxUnitLength}";
}
// 在Awake中添加事件监听
inputField.onValueChanged.AddListener(delegate { UpdateCounter(); });
默认实现无法限制粘贴内容,需要额外处理:
csharp复制private void Start()
{
inputField.onEndEdit.AddListener(ValidateFullText);
}
private void ValidateFullText(string text)
{
int totalLength = CalculateVisualLength(text);
if (totalLength > maxUnitLength)
{
// 截断超出部分
int allowedLength = 0;
StringBuilder sb = new StringBuilder();
foreach (char c in text)
{
int charWeight = GetCharWeight(c);
if (allowedLength + charWeight > maxUnitLength)
break;
sb.Append(c);
allowedLength += charWeight;
}
inputField.text = sb.ToString();
}
}
对于频繁调用的字符计算,可以考虑以下优化:
在角色创建界面,限制昵称为"16个英文字符或8个中文字符"的等效长度:
csharp复制public class PlayerNameInput : BilingualInputLimiter
{
protected override void Awake()
{
base.Awake();
maxUnitLength = 16; // 8个中文或16个英文
}
}
实现类似社交软件的字符计数方式:
| 输入类型 | 实际字符数 | 显示计数 |
|---|---|---|
| 纯英文 | 100 | 100/100 |
| 中英混合 | 50中文+25英文 | 125/100 (超出) |
| 纯中文 | 60 | 120/100 (超出) |
结合Unity的UI验证系统,创建自定义验证规则:
csharp复制public class BilingualLengthValidator : MonoBehaviour, IFieldValidator
{
public BilingualInputLimiter limiter;
public ValidationResult Validate()
{
int current = limiter.CalculateVisualLength(limiter.inputField.text);
bool isValid = current <= limiter.maxUnitLength;
return new ValidationResult {
IsValid = isValid,
Message = isValid ? "" : $"超出长度限制(最大{limiter.maxUnitLength}单位)"
};
}
}
在处理国际化游戏开发时,这种字符计数方式能够提供更符合用户预期的输入体验。实际项目中,我们还可以扩展支持更多语言的字符宽度计算,或者根据不同语言环境动态调整字符权重比例。