将文本放在游戏对象上,但就像它被绘制一样
Posted
技术标签:
【中文标题】将文本放在游戏对象上,但就像它被绘制一样【英文标题】:Put a text onto a game-object BUT as if it was painted 【发布时间】:2020-01-21 02:59:33 【问题描述】:我在网上寻找这个答案已经有一段时间了。 但显然我的搜索短语是错误的,或者真的那么难。但我对此表示怀疑。
想象一下,我有任何 3d 体。球体、圆柱体或立方体。
我想在那个对象上放一个文本。就好像它是一个贴纸或涂在那个物体上一样。 意思是,如果环绕它们,它将遵循对象的曲线或边缘。我设法创建了一些总是在我的对象前面或后面的文本。但这不是我想要的。
最接近我想要的看起来像这样:
我不敢相信,我需要为此编写代码。它必须可以通过子/父、画布和文本实现,只需使用检查器和组件即可。
是这样吗?
【问题讨论】:
我认为您确实需要一些将文本的每个顶点放置在正确位置的代码,但我不确定它是如何工作的。通常我所做的只是用文本创建一个纹理,但我确信这不是你想要的 嗯,Unity 可以为您将纹理应用到 UV 映射模型,但通常您将在 Unity 之外创建这些纹理。您可以假设自己将其实现到统一编辑器中,但对于一个问题来说,这个话题太宽泛了。可能已经存在一个统一资产,但要求一个库是堆栈溢出的主题。我建议寻找有关“紫外线映射”的教程。祝你好运! 我会研究贴花系统,然后通过制作自己的文本到纹理功能来扩展它们。 (因为那些贴花系统使用纹理)。 如果你想在不那么不规则的表面上使用它,并且有一些修复文本,那么你也可以使用 Unity 投影仪:docs.unity3d.com/Manual/class-Projector.html。 要求结束此问题的投票是否有具体原因?如果是这样,我很抱歉,如果这个缺少“要更正的代码”。 【参考方案1】:没那么简单,但您可以通过以下方式实现类似的目标。 这需要您的 3D 模型正确地进行 UV 映射,以便您可以简单地对其应用平面纹理。 设置可能听起来很复杂,但它真的很酷;)
-
创建一个
RenderTexture
:在Asset
中执行鼠标右键 → Create
→ RenderTexture
并以你喜欢的方式调用它,例如TextTexture
size
增加到例如2048*2048
当然取决于您的需求。
-
创建新材质
RenderTexture
作为Albedo
并将RenderingMode
设置为Fade
(以便以后使其背景透明)
-
创建一个新的Layer 并将其命名为例如
TEXT
-
在
Culling Mask
下的普通主Camera
排除刚刚创建的TEXT
层。所以它不会渲染我们的文本内容
-
在场景中添加一个新的
Camera
(它只会渲染文本)并调用它,例如TextCamera
并进行以下设置:
删除其AudioListener
组件
Clear Flags
→ Solid Color
Background
→ 颜色其实无关紧要,但请确保将 Alpha 级别设置为 0
!
Culling Mask
→ 除了创建的TEXT
层之外什么都没有
Target Texture
→ 创建的RenderTexture
现在您已经有了一个材质,它具有动态可变纹理、透明背景和您喜欢的任何内容。所以让我们来吧,例如一个 UI.Text
-
到您的场景中(我只是作为
TextCamera
的子对象完成的,因此我可以在处理其他内容时简单地将其移出SceneView 中)添加Text
(包括画布等 - Unity 通常会添加它自动)
TEXT
,这样它们就不会被普通的 Camera
渲染,而只能被 TextCamera
渲染。
确保
Canvas
使用RenderMode = WorldSpace
(它不适用于覆盖画布)!
将Canvas
放在例如3
TextCamera
前面的单位(或您喜欢的任何位置,以便稍后在纹理中看到文本)
为了获得更好的文本分辨率,我也会在Text
在RectTransform
上设置width = 1000
、height = 1000
、Scale = 0.001, 0.001, 0.001
在Text
组件集中Font Size = 300
只是为了确保禁用 Raycast Target
选项
现在您可以简单地将创建的材质应用到您的 3D 对象并点击播放,应该会看到它变得完全透明,除了上面有文字。
因此,为了将其用作 3D 对象的叠加层,您可以使用例如只需复制原始对象,调用一个例如Inner
另一个Outer
并使Inner
成为Outer
的孩子。现在在Outer
上设置我们的文本材料。这是有效的,因为使用Fade
作为渲染模式的材质在不同的渲染链上渲染,该渲染链呈现在默认链之上。
→ Tadaaa 3D 对象,其表面应用了文本,甚至可以动态更改文本及其属性,如颜色等
整个事情都是动态的(除了创建图层)
既然您问:是的,您可以在脚本中完成这一切...... 除了创建新层!这在运行时是不可能的!
所以你必须事先知道你将使用的所有图层,然后你才能做类似的事情
[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]
public class TextOnObjectManager : MonoBehaviour
// reference via Inspector if possible
[SerializeField] private Camera mainCamera;
[SerializeField] private string LayerToUse;
private void Awake()
// 0. make the clone of this and make it a child
var innerObject = new GameObject(name + "_original", typeof(MeshRenderer)).AddComponent<MeshFilter>();
innerObject.transform.SetParent(transform);
// copy over the mesh
innerObject.mesh = GetComponent<MeshFilter>().mesh;
name = name + "_textDecal";
// 1. Create and configure the RenderTexture
var renderTexture = new RenderTexture(2048, 2048, 24) name = name + "_RenderTexture" ;
// 2. Create material
var textMaterial = new Material(Shader.Find("Standard"));
// assign the new renderTexture as Albedo
textMaterial.SetTexture("_MainTex", renderTexture);
// set RenderMode to Fade
textMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
textMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
textMaterial.SetInt("_ZWrite", 0);
textMaterial.DisableKeyword("_ALPHATEST_ON");
textMaterial.EnableKeyword("_ALPHABLEND_ON");
textMaterial.DisableKeyword("_ALPHAPREMULTIPLY_ON");
textMaterial.renderQueue = 3000;
// 3. WE CAN'T CREATE A NEW LAYER AT RUNTIME SO CONFIGURE THEM BEFOREHAND AND USE LayerToUse
// 4. exclude the Layer in the normal camera
if (!mainCamera) mainCamera = Camera.main;
mainCamera.cullingMask &= ~(1 << LayerMask.NameToLayer(LayerToUse));
// 5. Add new Camera as child of this object
var camera = new GameObject("TextCamera").AddComponent<Camera>();
camera.transform.SetParent(transform, false);
camera.backgroundColor = new Color(0, 0, 0, 0);
camera.clearFlags = CameraClearFlags.Color;
camera.cullingMask = 1 << LayerMask.NameToLayer(LayerToUse);
// make it render to the renderTexture
camera.targetTexture = renderTexture;
camera.forceIntoRenderTexture = true;
// 6. add the UI to your scene as child of the camera
var Canvas = new GameObject("Canvas", typeof(RectTransform)).AddComponent<Canvas>();
Canvas.transform.SetParent(camera.transform, false);
Canvas.gameObject.AddComponent<CanvasScaler>();
Canvas.renderMode = RenderMode.WorldSpace;
var canvasRectTransform = Canvas.GetComponent<RectTransform>();
canvasRectTransform.anchoredPosition3D = new Vector3(0, 0, 3);
canvasRectTransform.sizeDelta = Vector2.one;
var text = new GameObject("Text", typeof(RectTransform)).AddComponent<Text>();
text.transform.SetParent(Canvas.transform, false);
var textRectTransform = text.GetComponent<RectTransform>();
textRectTransform.localScale = Vector3.one * 0.001f;
textRectTransform.sizeDelta = new Vector2(2000, 1000);
text.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
text.fontStyle = FontStyle.Bold;
text.alignment = TextAnchor.MiddleCenter;
text.color = Color.red;
text.fontSize = 300;
text.horizontalOverflow = HorizontalWrapMode.Wrap;
text.verticalOverflow = VerticalWrapMode.Overflow;
Canvas.gameObject.layer = LayerMask.NameToLayer(LayerToUse);
text.gameObject.layer = LayerMask.NameToLayer(LayerToUse);
text.text = "This is a dynamically generated example!";
// 7. finally assign the material to the child object and hope everything works ;)
innerObject.GetComponent<MeshRenderer>().material = textMaterial;
基本上重现了之前的所有步骤。由于我们无法在运行时创建或编辑图层,因此您必须事先了解它们并将其输入为LayerToUse
。
我将所有东西都创建为原始对象的子对象,因此稍后在运行时也可以轻松控制和更改它。
更新
在较新的 Unity 版本中,我建议通过 TextMeshPro 交换 Text
,它支持更多的文本格式和自动缩放等。
【讨论】:
非常感谢。这是相当复杂的,并且在“设计器 IDE”中需要做很多工作。代码方法会摆脱一些步骤吗? 嗯,你需要一个单独的层来处理新文本,所以在运行时添加它会变得非常棘手,因为你不能在运行时动态编辑/添加层。这在一定程度上取决于您的需求,但总的来说:是的,您基本上也可以在脚本中完成所有步骤......除了所说的创建新图层 我很失望,这个功能是如此“不方便”。我相信,我不是唯一一个有这种要求的人。这只是为了好玩,所以没有真正的生产力问题。 请查看我的更新 .. 我刚刚添加了一个脚本版本,可以在一个对象上为您完成整个设置。但是,您仍然需要事先设置图层 哦,使用渲染纹理的巧妙解决方案。非常好。【参考方案2】:我做了一些有帮助的更改。
-
您的代码:name = name + "_textDecal"; 可能意味着 innerObject.name = name + "_textDecal";
在同一区域,需要添加innerObject.transform.localPosition = Vector3.zero;和innerObject.transform.localRotaton = Quaternion.identity;否则文字如果移动或旋转对象,它似乎不会跟随对象
但我发现的真正问题是(在 Unity 2020.1.f1 上):原始网格的纹理只能在 Unity 编辑器中正确显示——独立构建显示黑色对象(或任何相机背景颜色) .
我看到的问题是否在 RenderTexture 文档(我无法立即再次找到)中解释得有些模糊,这暗示我需要在我的 Resources 文件夹中包含一个纹理(材质?),它具有与我在我的渲染纹理?
【讨论】:
【参考方案3】:您可以使用 UI 文本游戏对象和 3D 对象来做到这一点。
步骤如下:
在您的空场景中,创建一个 3D 立方体。创建一个 UI 文本对象。
拖动 Canvas 成为立方体的子对象。
将 Canvas 设置为 World Space 渲染模式,移除 Canvas Scaler 组件并设置 Width = Height = 1 且所有 Pos = 0。
设置文本 Width = Height = 100,所有 Scale = 0.01 和 Pos Z = -0.5。【讨论】:
以上是关于将文本放在游戏对象上,但就像它被绘制一样的主要内容,如果未能解决你的问题,请参考以下文章