赞
踩
不是很全,记录学习一下~希望对您有所帮助!
先解释一下静态构造方法
在类的构造函数前加上static会报错
.Net是一个语言平台,Mono为.Net提供了集成开发环境,集成实现了.Net的编译器、CLR和基础类库,使得.Net既可以运行在Windows,也可以运行在Linux、Unix、Mac OS等
引用类型基类是System.Object
值类型基类是System.ValueType(同时也隐式继承System.Object)
隐藏方法
通过在派生类中使用new关键字来显示隐藏基类中的方法,隐藏方法不会覆盖基类的方法,而是隐藏他们,如果通过基类引用调用该方法,仍然会调用基类的方法
using System;
public class BaseClass
{
public void Show()
{
Console.WriteLine("BaseClass Show method");
}
}
public class DerivedClass : BaseClass
{
public new void Show()
{
Console.WriteLine("DerivedClass Show method");
}
}
public class Program
{
public static void Main()
{
BaseClass baseClass = new BaseClass();
baseClass.Show(); // 调用 BaseClass 的 Show 方法
DerivedClass derivedClass = new DerivedClass();
derivedClass.Show(); // 调用 DerivedClass 的 Show 方法
BaseClass baseDerived = new DerivedClass();
baseDerived.Show(); // 调用 BaseClass 的 Show 方法
}
}
重写方法
通过在派生类中使用override关键字来覆盖基类中的虚方法或抽象方法,重写方法会替代基类的方法实现,如果通过基类引用来调用该方法,实际调用的是派生类的方法
using System;
public class BaseClass
{
public virtual void Show()
{
Console.WriteLine("BaseClass Show method");
}
}
public class DerivedClass : BaseClass
{
public override void Show()
{
Console.WriteLine("DerivedClass Show method");
}
}
public class Program
{
public static void Main()
{
BaseClass baseClass = new BaseClass();
baseClass.Show(); // 调用 BaseClass 的 Show 方法
DerivedClass derivedClass = new DerivedClass();
derivedClass.Show(); // 调用 DerivedClass 的 Show 方法
BaseClass baseDerived = new DerivedClass();
baseDerived.Show(); // 调用 DerivedClass 的 Show 方法
}
}
主要区别
每个虚函数都会有一个与之对应的虚函数表,
该虚函数表实质是一个指针数组,存放的是每一个对象虚函数入口地址
对于一个派生类来说,他会继承基类的虚函数表,同时增加自己的虚函数入口地址
如果派生类重写了基类的虚函数的话,那么继承过来的虚函数入口地址将被派生类的重写虚函数入口地址替代
那么在程序运行时会发生动态绑定,将父类指针绑定到实例化对象,从而实现多态
ref修饰的参数在使用前必须赋值,在方法内部可改可不改,ref又进又出
out修饰的参数在使用前不需要赋值,在方法内部必须赋值,out不进只出
引用参数和输出参数都不会创建新的存储位置,只是方法接受了 这个变量的地址
如果ref参数是值类型,原先的值类型数据,会随着方法里的数据改变而改变
如果ref参数是引用类型,方法里重新赋值后,原来对象堆中的数据会改变
如果引用类型再次创建新对象并赋值给ref参数,引用地址会重新指向新对象对数据
方法结束后形参和新对象都会消失,实参还是指向原始对象,只不过数据改变了
委托是一个类,该类内部维护者一个字段,指向一个方法。
public class BaseClass
{
public int publicField = 1;
private int privateField = 2;
protected int protectedField = 3;
internal int internalField = 4;
protected internal int protectedInternalField = 5;
private protected int privateProtectedField = 6;
}
public class DerivedClass : BaseClass
{
public void AccessFields()
{
Console.WriteLine(publicField); // 可访问
// Console.WriteLine(privateField); // 不可访问
Console.WriteLine(protectedField); // 可访问
Console.WriteLine(internalField); // 可访问
Console.WriteLine(protectedInternalField); // 可访问
Console.WriteLine(privateProtectedField); // 可访问
}
}
public class OtherClass
{
public void AccessFields()
{
BaseClass baseClass = new BaseClass();
Console.WriteLine(baseClass.publicField); // 可访问
// Console.WriteLine(baseClass.privateField); // 不可访问
// Console.WriteLine(baseClass.protectedField); // 不可访问
Console.WriteLine(baseClass.internalField); // 可访问
Console.WriteLine(baseClass.protectedInternalField); // 可访问
// Console.WriteLine(baseClass.privateProtectedField); // 不可访问
}
}
所有实现了IEnumerable
接口的集合都可以使用foreach进行遍历。
foreach使用了集合的迭代器,如果在迭代过程中修改原集合的结构(如添加或删除),会引发InvalidOperationException
,
这是因为当集合在迭代过程中被修改时,迭代器的状态会变得无效,从而引发异常
个人见解:
想使用foreach遍历某种类型,需要实现IEnumerable
接口中的方法GetEnumerator,
这个方法需要一个IEnumerator类型的返回值,可以自定义一个类继承IEnumerator,然后手动实现MoveNext、Current、Reset,详情见通过理解C# foreach原理看协程
这个迭代器的状态在使用foreach时就已经定了(里面的索引index,容器的大小),当通过foreach删除某个元素后,集合的内容变了,而迭代器的状态没变,前后不一致,继续运行肯定报错,有可能是索引越界,也有可能是空指针,咱也不清楚具体是啥错,所以报了一个异常InvalidOperationException
A. int add(int a, int b, int c)
B. int add(double a, double b)
C. double add(double a, double b)
D. int add(int a, int b)
decimal
类型具有更高的精度,适用于需要精确计算的场景(如财务和货币计算)float
和double
类型的精度较低,适用于科学和工程计算C#不支持直接的类多继承,但可以通过以下方法模拟多继承的行为
通过显示接口实现来区分
using System;
public interface IA
{
void Test();
}
public interface IB
{
void Test();
}
public class A : IA, IB
{
// 显式实现 IA 接口的 Test 方法
void IA.Test()
{
Console.WriteLine("IA Test method");
}
// 显式实现 IB 接口的 Test 方法
void IB.Test()
{
Console.WriteLine("IB Test method");
}
}
class Program
{
static void Main()
{
A a = new A();
// 通过 IA 接口调用 Test 方法
IA ia = a;
ia.Test(); // 输出 "IA Test method"
// 通过 IB 接口调用 Test 方法
IB ib = a;
ib.Test(); // 输出 "IB Test method"
}
}
可以在加载程序运行时,动态获取和加载程序集,并且可以获取到程序集的信息
反射即在运行期动态获取类、对象、方法、对象数据等的一种重要手段
主要使用的类库:System.Reflection
核心类:
for
循环
for
循环允许通过索引访问元素,因此可以方便地处理索引相关的逻辑,如访问特定范围内的元素或以特定步长跳跃for
循环可能比foreach
更高效,因为foreach
需要获取集合的枚举器,而for
直接使用索引访问for
循环可能比foreach
更难读懂for
循环容易出错,例如下标越界、索引变量初始化错误、忘记更新循环变量等等foreach
循环
foreach
循环语法简单,适合遍历集合中的所有元素,不需要关心索引和边界问题foreach
循环能防止越界和索引相关错误,因为它不适用显示索引foreach
循环不能修改集合的结构(如添加或删除元素),在循环内部也不能直接修改当前元素(除非元素是引用类型并且修改其内部状态)foreach
可能比for
循环效率低,因为它需要创建和管理枚举器对象foreach
不提供对元素的索引访问,不能轻易获取当前元素的索引在C#中,泛型参数类型在编译时不会被擦除,而是会被保留,并在运行时替换为具体的类型。
这与Java的泛型擦除机制不同(在编译时,Java的泛型机制使用类型擦除来转换泛型类型为原始类型(通常是Object
),并插入必要的类型转换。这样做的目的是为了保持与旧版本Java的兼容性)。
在C#中,泛型类型参数的行为可以通过以下几个方面来理解:
List<int>
,CLR会生成一个专门针对int
类型的List
实例int
、double
),CLR会为每个具体的值类型创建一个单独的泛型类型实例。这意味着List<int>
和List<double>
会生成不同的IL代码string
、object
等),CLR会创建一个共享的泛型类型实例,从而避免代码膨胀using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<int> intList = new List<int>();
intList.Add(1);
intList.Add(2);
List<string> stringList = new List<string>();
stringList.Add("Hello");
stringList.Add("World");
PrintList(intList);
PrintList(stringList);
}
static void PrintList<T>(List<T> list)
{
foreach (T item in list)
{
Console.WriteLine(item);
}
}
}
PrintList<T>
是一个泛型方法,它可以接受任何类型的List<T>
参数。T
。Count
List<T>
中当前实际包含的元素数量Capacity
List<T>
在不重新分配内存的情况下可以包含的元素数量Capacity
可以减少内存分配和数据复制的开销List<int> numbers = new List<int>(100); // 预先分配容量为 100
List<int> numbers = new List<int>();
numbers.Capacity = 100; // 预先设置容量
for (int i = 0; i < 100; i++)
{
numbers.Add(i);
}
public class MyScript : MonoBehaviour
{
public GameObject otherObject;
public MyScript()
{
otherObject.transform.position = Vector3.zero; // 可能会报空指针异常
}
}
在上述代码中,otherObject可能尚未被初始化,这会导致NullReferenceException
进程
线程
协程
Unity中,每次引擎准备数据并通知GPU的过程称为一次DrawCall
DrawCall越高对显卡的消耗就越大
降低DrawCall的方法
红点系统基于MVC思想,将分为三层:数据层、驱动层、显示层
更多详细内容可以看下面文章
Unity之红点树系统多层级高效能
Unity手游实战:从0开始SLG——独立功能扩展(三)用树实现客户端红点系统
Unity的协程是一种用于执行延迟任务或分布在多个帧上的长时间运行任务的机制。
协程允许在逻辑上连续的代码在不同的时间段执行,而不会阻塞主线程。
这对于处理动画、等待时间或异步任务非常有用
using System.Collections;
using UnityEngine;
public class CoroutineExample : MonoBehaviour
{
void Start()
{
StartCoroutine(MyCoroutine());
}
private IEnumerator MyCoroutine()
{
Debug.Log("Coroutine started");
yield return new WaitForSeconds(2f);
Debug.Log("Coroutine resumed after 2 seconds");
yield return null;
Debug.Log("Coroutine finished");
}
}
使用SynchronizationContext
using System.Threading;
using UnityEngine;
public class MainThreadContext : MonoBehaviour
{
private static SynchronizationContext _mainThreadContext;
void Awake()
{
_mainThreadContext = SynchronizationContext.Current;
}
public static void RunOnMainThread(Action action)
{
_mainThreadContext.Post(_ => action(), null);
}
}
using System.Threading;
using UnityEngine;
public class Example : MonoBehaviour
{
void Start()
{
Thread thread = new Thread(ThreadFunction);
thread.Start();
}
void ThreadFunction()
{
// Simulate some work on a background thread
Thread.Sleep(2000);
// Run code on the main thread
MainThreadContext.RunOnMainThread(() =>
{
Debug.Log("This is executed on the main thread");
});
}
}
A. 渲染效率更高
B. 渲染效果更平滑
C. 只能用来计算漫反射
D. 渲染次数更少
Ignore Layout
:如果勾选,该元素将被布局系统忽略,不会影响其父布局组的布局计算ignoreLayout
为true
ignoreLayout
控制哪些元素应该参与布局计算ignoreLayout
为true
Min Width
:用于设置UI元素在布局系统中占据的最小宽度。这意味着,当父布局组计算布局时,该元素的宽度不会小于设置的最小宽度值Min Height
与Min Width
大致同理Preferred Width
:用于设置UI元素在布局系统中占据的理想宽度。布局系统会尝试根据该值分配元素的宽度,但如果空间不足,它会根据其他布局规则(如Min Width
和Flexible Width
)进行调整Preferred Width
可以控制不同UI元素在布局中的相对比例Preferred Height
使用和Preferred Width
大致同理举例说明:
假如说现在有一个父节点Father,
其挂载了Horizontal Layout Group,勾选ControlChildSize和ChildForceExpand,
其下有三个Image都挂载了Layout Element,且设置了Preferred Width分别为100、200、300,
当Father宽度设置为600时,三个Image宽度分别是100、200、300
当Father设置宽度为300,三个Image将按比例平分,最后宽度分别为50、100、150
Flexible Width
:用于指定UI元素在布局系统中能够占据的额外宽度。当布局系统在分配空间时,会首先满足Min Width
和Preferred Width
,然后根据剩余的可用空间和个元素的Flexible Width
属性值,来分配额外的宽度Flexible Width
允许UI元素根据剩余空间动态调整大小,从而使布局更具弹性和响应性Flexible Width
值,可以控制各元素在布局中的相对比例。例如,如果一个元素的Flexible Width
设置为100,另一个设置为200,那么在剩余空间分配时,第二个元素将获得更多的空间Flexible Height
和Flexible Width
使用大致相同Layout Priority
:用于指定多个布局元素在同一个父布局容器中的优先级。它决定了在进行布局计算时,哪个元素应该优先考虑。Awake
– OnEnable
– Start
– FixedUpdate
– Update
– LateUpdate
– OnDisable
– OnDestroy
Awake
:用于在脚本实例被加载时初始化对象。通常在对象的生命周期内只调用一次。Start
:用于在脚本实例启用后的第一个帧更新前初始化对象,也是只调用一次Update
:用于处理每帧更新的逻辑LateUpdate
:用于在每帧的Update
方法之后进行处理FixedUpdate
:用于以固定时间间隔处理物理相关的逻辑FixedUpdate
通常先于Update
调用,但不一定总是如此,具体取决于物理步长(Time.fixedDeltaTime
)和帧率
在帧率较高或较低的情况下,FixedUpdate
和Update
的调用顺序可能会有所不同
Directional Light(方向光)
Point Light(点光源)
Spot Light(聚光灯)
Area Light(区域光)
Emissive Materials(自发光材质)
Layers
:用于管理物理和渲染相关的操作,可以设置碰撞矩阵和相机剔除掩码Tags
:用于识别和分类游戏对象,可以通过脚本查找和操作特定标签的对象在Unity中,MeshRenderer
组件有两个属性与材质相关:material
和sharedMaterial
,他们之间的区别在于如何使用和共享材质实例,影响到材质的独立性和性能。以下是它们的详细区别和使用方法:
material
material
属性返回的是一个材质的独立实例。这意味着对该材质属性的修改不会影响到其他使用相同材质的对象material
属性material
属性进行修改,Unity会在后台自动为该对象创建一个新的材质实例,即使多个对象原本共享同一个材质sharedMaterial
sharedMaterial
属性返回的是一个材质的共享实例。这意味着对该材质属性的修改会影响到所有使用相同材质的对象sharedMaterial
属性sharedMaterial
属性进行修改不会创建新的材质实例,因此多个对象可以真正共享同一个材质,节省内存和提高性能性能考虑
material
属性会创建新的材质实例,这会增加内存占用,特别是当有大量对象使用相同的材质时sharedMaterial
属性可以减少内存占用,因为多个对象共享同一个材质实例,有助于优化性能,特别是在需要频繁调整材质属性的情况下注意事项
material
sharedMaterial
进行批量修改Animator
是Unity的新动画系统的一部分,称为Mecanim
。它提供了更强大的功能和灵活性,适用于复杂的动画控制和状态管理主要特性和功能
Animator
,可以创建复杂的动画状态机(Animator Controller),在不同的动画状态之间进行平滑过渡using UnityEngine;
public class AnimatorExample : MonoBehaviour
{
private Animator animator;
void Start()
{
animator = GetComponent<Animator>();
}
void Update()
{
// 根据输入控制动画参数
if (Input.GetKeyDown(KeyCode.Space))
{
animator.SetTrigger("Jump");
}
}
}
Animation
是Unity的旧动画系统,通常用于简单的动画控制。它使用动画剪辑直接控制对象的变换、旋转、缩放等属性Animation
组件中管理和播放动画剪辑using UnityEngine;
public class AnimationExample : MonoBehaviour
{
private Animation animation;
void Start()
{
animation = GetComponent<Animation>();
}
void Update()
{
// 根据输入播放动画
if (Input.GetKeyDown(KeyCode.Space))
{
animation.Play("JumpAnimation");
}
}
}
总结
print(type(a))
a = 10
print(type(a))
a = print
print(type(a))
步骤一
File -> New -> New Project
,然后选择Android Library
Finish
src/main/java/your/package/name/
目录下MainActivity.java
(可以重命名为VibrationPlugin.java
,因为这是一个库项目,不需要MainActivity.java
)Build -> Build Bundle(s) / APK(s) -> Build APK(s)
,将生成的.aar
文件(在build/outputs/aar/
目录下)拷贝出来备用.aar
文件导入Unity项目.aar
文件拷贝到Unity项目的Android/Plugins/Android/
目录下mul(UNITY_MATRIX_MVP, v.vertex)
CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild
和CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild
来控制什么时候进行布局和绘制重建重载函数:返回值不同,参数类型不同,参数个数不同
A 参数个数不同,正确
B 返回值类型不同,正确
C 参数类型不同,正确
D 参数类型和个数相同,调用有歧义
A 逐像素渲染效率肯定不高,计算量大,由于需要在每个像素上进行光照计算,逐像素光照的计算量通常比逐顶点光照大。这对于性能要求较高的应用(如实时渲染)可能会有一定的挑战
B 逐像素光照可以在每个像素级别上进行插值和计算,使得光照效果在多边形表面上更加平滑和自然。这种平滑效果尤其在高光部分和阴影过渡处表现得更加自然
C 精确度高,每个像素都经过光照计算,因此可以获得更精确的光照效果。逐像素光照能够考虑到每个像素的法线和光照方向,产生更细致的阴影和反射效果
D 不解释了
Resources.Load<Texture2D>("Textures/MyTexture")
, Resources.UnloadAsset(texture);
StreamingAssets
文件夹中的文件不会被Unity压缩打包,可以直接读取文件数据(如图片、文本等等)。它适用于存储需要按原样访问的大文件
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;
public class TextureLoader : MonoBehaviour
{
private Texture2D texture;
void Start()
{
StartCoroutine(LoadTextureFromStreamingAssets("Textures/MyTexture.png"));
}
IEnumerator LoadTextureFromStreamingAssets(string relativePath)
{
string path = System.IO.Path.Combine(Application.streamingAssetsPath, relativePath);
using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(path))
{
yield return www.SendWebRequest();
if (www.result != UnityWebRequest.Result.Success)
{
Debug.LogError("Failed to load texture: " + www.error);
}
else
{
texture = DownloadHandlerTexture.GetContent(www);
GetComponent<Renderer>().material.mainTexture = texture;
}
}
}
}
void OnDestroy()
{
if (texture != null)
{
Destroy(texture);
texture = null;
}
}
Mask
组件Shader
Sprite Mask
RectMask2D
DrawCall是指在渲染过程中,由CPU向GPU发送的绘制指令。
每个DrawCall代表一次从CPU到GPU的绘制指令传递,它通常包括渲染一个对象的几何信息、材质、着色器参数等
当场景中的对象数量增多或材质、着色器变化频繁时,DrawCall的数量会急剧增加,导致性能下降
减少DrawCall
using UnityEngine;
using UnityEngine.UI;
public class ScalerMatchSetting : MonoBehaviour
{
public bool Debug = false; //开启会在Update调用,方便调试
CanvasScaler scaler;
private void Awake()
{
scaler = GetComponent<CanvasScaler>();
AutoSetMatch();
}
private void AutoSetMatch()
{
if (scaler)
{
// 16 / 9 = 1.777777..... 判断 宽高比大于 1.78则视为分辨率大于 16:9
float ratio = (float)Screen.width / (float)Screen.height;
scaler.matchWidthOrHeight = ratio > 1.78 ? 1 : 0;
}
}
#if UNITY_EDITOR_WIN
private void Update()
{
if (Debug)
{
AutoSetMatch();
}
}
#endif
}
RectMask2D
组件,可以显示部分Image的内容在 Unity 中,动态批处理和静态批处理是两种用于优化渲染性能的方法,它们通过减少需要发送到 GPU 的绘制调用数量来提高效率。
以下是它们的定义、区别和打断方式:
动态批处理 (Dynamic Batching):
定义:
动态批处理是指在运行时将多个相似的对象(例如使用相同材质的对象)合并成一个批次进行渲染。这种方法适用于小型对象(顶点数少于 300 个),可以在场景中节省大量的绘制调用。
特点:
打断方式:
静态批处理 (Static Batching)
定义:
静态批处理是在编辑器模式下,将不移动的静态对象合并成一个批次进行渲染。这种方法适用于大场景中的静态对象,例如地形、建筑物。
特点:
打断方式:
启用动态批处理(有些Unity版本可能找不到这个选项,原因是已经默认启用了)
在 Unity 项目设置中启用动态批处理:
启用静态批处理
在编辑器中为对象启用静态批处理:
RawImage
和Image
是Unity中两个用于在UI中显示图像的组件。
它们的主要区别在于它们支持的图像类型以及一些具体的用途和功能。
以下是它们的详细区别和各自的使用场景:
RawImage
Texture
),而不是精灵(Sprite
)Texture2D
、RenderTexture
等所有类型的纹理using UnityEngine;
using UnityEngine.UI;
public class Example : MonoBehaviour
{
public RawImage rawImage;
public Texture2D texture;
void Start()
{
// 将纹理分配给 RawImage
rawImage.texture = texture;
}
}
Image
Sprite
),主要用于UI元素(按钮、图标等)Sprite
类型using UnityEngine;
using UnityEngine.UI;
public class Example : MonoBehaviour
{
public Image image;
public Sprite sprite;
void Start()
{
// 将精灵分配给 Image
image.sprite = sprite;
}
}
使用区别
RawImage
用于显示Texture
Image
用于显示Sprite
Image
组件有一些优化,特别是与SpriteAtlas
配合使用时,可以减少draw callRawImage
通常用于特定用途的纹理显示,可能没有Image
那么高效Image
提供了更多与精灵相关的功能,比如九宫格切割、填充模式(如圆形进度条)、精灵的渲染模式等RawImage
更简单,适用于显示原始纹理数据更多见 Image与RawImage
虽然RawImage
可以用来显示Sprite2D
类型的纹理,但在大多数情况下,使用Image
组件会更适合显示UI元素
专为UI设计的功能
Image
支持九宫格切割,这对于制作可缩放的UI元素(如按钮、面板等)非常重要RawImage
不支持九宫格切割Image
支持各种填充模式(水平、垂直、径向等),可以方便地实现进度条、加载条等效果RawImage
不支持这些填充模式Image
组件有专门针对精灵的优化,特别是与SpriteAtlas
配合使用时,可以减少draw call,提高渲染性能RawImage
组件没有这些优化性能和兼容性
Image
可以与Sprite Atlas
配合使用,减少draw call,提高渲染性能RawImage
不能直接利用Sprite Atlas
的优化Image
组件在渲染多个UI元素时,有更好的批处理能力RawImage
可能会打破批处理,导致更多的draw call功能完整性
Image
组件与其他UI组件(如Button
、Toggle
、Slider
等)配合使用时,有更好的兼容性RawImage
组件在这些情况下可能需要更多的自定义处理尽管RawImage
可以显示Sprite2D
类型的纹理,但在显示UI元素时,Image
组件更为适合。
Image
组件提供了更多的功能和优化,专门针对UI的需求进行了设计和优化。
因此,建议在需要显示UI元素时,优先选择Image
组件。
Best Fit
:勾选Vertical Overflow
:设置为Overflow
Horizontal Overflow
:设置为Wrap
Font
、Font Size
、Color
等Content Size Fitter
组件
Horizontal Fit
和Vertical Fit
都设置为Preferred Size
Layout Element
组件
Preferred Width
和Preferred Height
Flexible Width
和Flexible Height
Text
的RectTransform
using UnityEngine;
using UnityEngine.UI;
public class AutoAdjustingText : MonoBehaviour
{
public Text uiText;
void Start()
{
// 示例文本
string text = "This is a sample text that will automatically wrap and adjust the height of the text component.";
UpdateText(text);
}
void UpdateText(string newText)
{
uiText.text = newText;
// 触发布局重新计算
LayoutRebuilder.ForceRebuildLayoutImmediate(uiText.rectTransform);
}
}
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
public class TypewriterEffect : MonoBehaviour
{
public Text uiText;
public string fullText;
public float delay = 0.1f;
void Start()
{
StartCoroutine(ShowText());
}
IEnumerator ShowText()
{
for (int i = 0; i <= fullText.Length; i++)
{
uiText.text = fullText.Substring(0, i);
yield return new WaitForSeconds(delay);
}
}
}
Update
方法和计时器using UnityEngine;
using UnityEngine.UI;
public class TypewriterEffectUpdate : MonoBehaviour
{
public Text uiText;
public string fullText;
public float delay = 0.1f;
private float timer;
private int currentIndex;
void Update()
{
if (currentIndex < fullText.Length)
{
timer += Time.deltaTime;
if (timer >= delay)
{
currentIndex++;
uiText.text = fullText.Substring(0, currentIndex);
timer = 0;
}
}
}
}
DoTween
DoTween
的 DOText
方法来逐字显示文本,并设置线性插值使其效果平滑using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;
public class TypewriterEffectDoTween : MonoBehaviour
{
public Text uiText;
public string fullText;
public float typingDuration = 3.0f;
void Start()
{
// 确保文本为空,然后开始打字机效果
uiText.text = "";
// 使用 DoTween 的 DOText 方法来实现打字机效果
uiText.DOText(fullText, typingDuration).SetEase(Ease.Linear);
}
}
可以通过链式调用添加更多效果,例如每个字母显示时的缩放效果或者颜色渐变
uiText.DOText(fullText, typingDuration).SetEase(Ease.Linear).OnStepComplete(() =>
{
uiText.transform.DOScale(1.2f, 0.1f).From();
});
StringBuilder
:StringBuilder
是处理字符串拼接的更高效的类,可以减少不必要的字符串创建和垃圾回收IEnumerator TypeText()
{
StringBuilder stringBuilder = new StringBuilder();
float timePerCharacter = time / content.Length;
for (int i = 0; i < content.Length; i++)
{
stringBuilder.Append(content[i]);
text.text = stringBuilder.ToString();
yield return new WaitForSeconds(timePerCharacter);
}
}
通常会按照层级关系依次执行,但具体的执行顺序可能会因为具体的布局和其他组件的依赖关系而有所不同。
一般来说,Unity的组件更新顺序是这样的:
Transform
层级结构,从根节点开始向下依次更新所有子节点的Transform在这种情况下,子节点的ContentSizeFitter通常会先于父节点执行。这是因为父节点的布局更新会影响子节点,而子节点的ContentSizeFitter需要在父节点的布局之前调整好自己的大小,以便父节点能够正确地进行布局计算。
所以,实际情况是,子节点Text会根据输入内容自动设置宽高,父节点Image的宽高为0,原因是父节点Image缺少一个水平或垂直布局组,尽管ContentSizeFitter按照层级结构从上到下依次更新,但在某些情况下,父节点可能无法在同一帧内捕捉到子节点尺寸的变化,导致父节点未能及时调整大小。
解决方法就是在父节点上添加一个布局组件,如VerticalLayoutGroup
或HorizontalLayoutGroup
ipairs
用于迭代数组部分的一个迭代器函数,它从索引1开始,按顺序遍历,直到遇到第一个nil
为止,因此ipairs
主要用于数组或列表
local t = {10, 20, nil, 30}
for i, v in ipairs(t) do
print(i, v)
end
-- 10
-- 20
pairs
是一个通用的迭代器函数,它用于遍历表的所有键值对,无论键是数字还是字符串,也无论键是否是连续的。它适用于散列表(哈希表)或具有非连续键的表。也可用于数组表,但是它不会按顺序遍历,输出顺序可能不固定
local t = {10, 20, 30}
for k, v in pairs(t) do
print(k, v)
end
由于ipairs
只遍历数组或列表,所以当表里有混合数据时,和pairs
相比,输出结果会有不同
local mixedTable = {10, 20, 30, a = 1, b = 2, c = 3}
for i, v in ipairs(mixedTable) do
print(i, v)
end
-- 1 10
-- 2 20
-- 3 30
for k, v in pairs(mixedTable) do
print(k, v)
end
-- 1 10
-- 2 20
-- 3 30
-- a 1
-- b 2
-- c 3
ipais
只遍历数组部分
pairs
遍历表中的所有元素,包括数组部分,也包括键值对部分,但是不保证输出结果顺序。
点乘的几何意义是一个向量在另一个向量方向上的投影乘以另一个向量的模
结果
叉乘的结果是一个向量,其方向与原来的两个向量所构成的平面垂直。这个方向遵循右手法则
方向
结果
叉乘结果的大小等于原来两个向量所构成的平行四边形的面积
应用
将顶点位置从对象空间变换到裁剪空间
对象空间:v.vertex
表示顶点在对象的局部坐标系中的位置
模型-视图-投影变换:通过乘以UNITY_MATRIX_MVP
矩阵,顶点被依次变换到世界空间、相机视角空间,最后变换到裁剪空间
持续更新中~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。