unity的问题记录(信息管理)

闭包

  • 捕获引用:当你在委托或 lambda 表达式中使用外部变量时,它们捕获的是这个变量的引用,而不是当时的值。
  • 变量的生命周期:捕获的变量的生命周期不受限于它的作用域,委托可以在变量的作用域结束后继续访问它。

为了避免这种情况,可以在循环内引入一个新的局部变量,使每个委托捕获不同的变量引用。

List<Func<int>> funcs = new List<Func<int>>();

for (int i = 0; i < 5; i++)
{
    int captured = i; // 引入局部变量
    funcs.Add(() => captured);
}

foreach (var func in funcs)
{
    Console.WriteLine(func()); // 输出 0, 1, 2, 3, 4
}

委托delegate

// 定义一个委托类型
public delegate int MyDelegate();

// 用Lambda表达式创建委托实例
MyDelegate myDelegate = () => { return _value; };

 属性的表达式体定义

// 这是不合法的,不能在表达式体定义中使用代码块
public int MyProperty => { _value++; return _value * 10; };

为什么不能使用 { _value++; return _value * 10; }

{ _value++; return _value * 10; } 是一个包含多行语句的代码块,而表达式体定义的属性仅支持单行表达式

实现包含逻辑的属性

如果你想在属性中包含多行代码或者复杂逻辑(比如递增 _value,然后返回 _value * 10),你需要使用传统的属性语法,显式地使用 get 访问器:

private int _value;

public int MyProperty
{
    get
    {
        _value++;
        return _value * 10;
    }
}

 合法的属性只读定义

public int MyProperty => _value++;

public int MyProperty => _value+10;

public int MyProperty => { return _value };

只有3不合法。表达式体定义的属性只能包含一个简单的表达式,不能包含块级代码(即不能包含 {} 以及 return 语句)。

即:

private int _value = 10;

public int MyProperty
{
    get { return _value; }
}

lambda表达式

在 C# 中,=> 符号不仅仅用于 Lambda 表达式,还可以用于定义只读属性和表达式体成员(expression-bodied members)。虽然语法上看起来相似,但它们之间并没有直接的包含关系。

区分 => 在不同上下文中的用途

  1. Lambda 表达式中的 =>

    • 在 Lambda 表达式中,=> 用于分隔参数列表和方法体
    • 例如:(x, y) => x + y 是一个接收两个参数并返回它们之和的 Lambda 表达式。
    Func<int, int, int> add = (x, y) => x + y;
  2. 属性和方法的表达式体定义中的 =>

    • 在 C# 6.0 及以上版本中,=> 也用于定义只读属性或方法的表达式体。这个用法简化了只返回一个表达式的属性或方法的定义。
    • 例如:public int MyProperty => 42; 定义了一个只读属性,直接返回 42
    public class MyClass
    {
        // 只读属性,返回字段 _value
        private int _value = 42;
        public int MyProperty => _value;
    
        // 方法的表达式体定义,返回平方
        public int Square(int x) => x * x;
    }
    

关系与区别

  • Lambda 表达式和属性的表达式体定义 都使用了 => 符号,但它们不是同一个概念。
    • Lambda 表达式用于定义匿名方法。
    • 属性的表达式体定义用于简化属性和方法的实现。
  • => 符号 在这两种不同的上下文中只是语法上的相似,而不意味着属性与 Lambda 表达式之间有直接的关系。

Lambda 表达式可以有返回值

实际上,Lambda 表达式最常见的用途之一就是用于定义带有返回值的匿名函数。

1. 带有返回值的 Lambda 表达式

如果 Lambda 表达式的主体只包含一个表达式(即没有花括号 {}),这个表达式的结果会自动作为 Lambda 表达式的返回值。你不需要显式地使用 return 关键字。

// 一个带有返回值的Lambda表达式
Func<int, int, int> add = (x, y) => x + y;

// 调用Lambda表达式
int result = add(3, 4); // result = 7

2. 多行的 Lambda 表达式

如果 Lambda 表达式的主体包含多个语句,则需要使用花括号 {} 包裹整个主体,并且在需要返回值时,必须使用 return 关键字。

// 一个多行的Lambda表达式,带有返回值
Func<int, int, int> multiply = (x, y) =>
{
    int product = x * y;
    return product;
};

// 调用Lambda表达式
int result = multiply(3, 4); // result = 12

3. 无返回值的 Lambda 表达式

如果 Lambda 表达式没有返回值(即它是 void 类型的),你可以使用 Action 委托类型,或者在 Lambda 表达式中不返回任何值。

// 一个无返回值的Lambda表达式
Action<string> printMessage = message => Console.WriteLine(message);

// 调用Lambda表达式
printMessage("Hello, World!");

4. 总结

  • Lambda 表达式可以有返回值,并且返回值的类型由使用的委托类型(如 Func<T>)决定。
  • 当 Lambda 表达式主体是单一表达式时,返回值会自动推导。
  • 当 Lambda 表达式包含多行代码时,使用 {} 包裹主体,并显式使用 return 关键字返回值。

谓词(Predicate)

1. 谓词(Predicate)是什么?

  • 谓词 是一个定义好的委托类型,通常用于表示一个返回 bool 类型结果的方法签名
  • Predicate<T> 是 C# 中一个内置的委托,它定义了一个接收类型 T 的参数返回 bool 类型结果的方法。
public delegate bool Predicate<T>(T obj);

谓词通常用于List<T>.FindList<T>.FindAll 这样的泛型方法中,作为条件来筛选、查找或删除元素。

2. => 一定要和谓词搭配使用吗?

  • 不一定=> 是 Lambda 表达式的标志,Lambda 表达式可以用于创建匿名方法,而不仅仅用于谓词。Lambda 表达式可以用于任何接受委托(如 FuncActionPredicate)的方法,而不仅仅是谓词。

3. => 是 Lambda 表达式的专有符号吗?

  • 是的=> 是 C# 中 Lambda 表达式的专有符号,用于定义匿名方法。Lambda 表达式允许你以简洁的方式定义内联的、无需命名的方法。

4. => 和 Lambda 表达式的关系?

  • => 是 Lambda 表达式的一部分。Lambda 表达式本质上是一个匿名函数,而 => 符号用于分隔参数和方法体。

5. => 和 Lambda 表达式是包含关系还是被包含关系?

  • 包含关系=> 是 Lambda 表达式的一部分,因此 => 是 Lambda 表达式所包含的符号。Lambda 表达式可以看作是一个完整的表达式,而 => 是其中用来分隔参数列表和表达式主体的符号。

6. 相关的概念有哪些?

  • 委托(Delegate):委托是 C# 中的类型,表示对具有特定参数和返回类型的方法的引用。Lambda 表达式通常用于创建符合委托签名的匿名方法
  • 匿名方法:没有名称的方法,可以用 Lambda 表达式或 delegate 关键字定义Lambda 表达式是一种更简洁的匿名方法定义方式。
  • FuncAction:这些是 C# 中内置的泛型委托类型,用于表示带有返回值和无返回值的方法。Func 通常用于带返回值的 Lambda 表达式,而 Action 用于无返回值的情况
  • 表达式树(Expression Trees):表达式树是 Lambda 表达式的另一种表示形式,通常用于 LINQ 查询中,它们允许你将 Lambda 表达式解析为数据结构,而不是编译为实际代码。

示例代码

// 使用Lambda表达式作为Predicate
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };

// 直接使用Lambda表达式
int result = numbers.Find(n => n > 2);

// 使用显式Predicate委托
Predicate<int> predicate = n => n > 2;
int result2 = numbers.Find(predicate);

重要总结:

  • 谓词一种委托类型,表示一个返回 bool 的方法
  • => 符号 是用于定义 Lambda 表达式的部分,它将参数列表和方法体分开。
  • Lambda 表达式 是匿名方法的一种形式,用于创建符合委托签名的内联方法。
  • 相关的概念还包括委托、匿名方法、FuncAction,以及表达式树。

Raycast和Collider

1. Collider 的存在与顺序

  • 尽管 UISpritedepth 高,但 Raycast 检测顺序不完全取决于 depth,而是取决于 Collider 在场景中的实际位置和存在。确保你的背景 sprite 和目标 sprite 只有一个 Collider,并且没有其他隐藏的 Collider 影响检测。

2. Depth 与 Raycast

  • depth 是 NGUI 中的一个属性,用于控制绘制顺序,但这并不直接影响物理 Raycast 的检测顺序。Raycast 的顺序由 Collider 在3D空间中的实际位置决定,而不是 depth

反射

//这里的fieldValue实际上是一个字符串的数据,但用object装了

  object fieldValue = fieldInfo.GetValue(obj);  //返回的是object,但任可以和int相加

+操作符会处理隐式转换

as 关键字在C#中用于尝试将对象转换为指定的类型,并且如果转换失败,则返回null而不是抛出异常。这与+操作符在字符串和整型之间的行为没有直接关系。

当使用+操作符将字符串(string)和整型(int)相加时,隐式转换的过程大致可以描述为以下步骤(虽然实际的实现可能更复杂,涉及编译器和运行时环境):

  1. 识别操作数类型编译器首先识别出+操作符的两个操作数的类型,一个是string,另一个是int

  2. 查找合适的重载:由于string类为+操作符定义了重载,该重载接受一个string和一个object作为参数(因为所有引用类型都是object的子类,包括int的装箱形式),编译器会选择这个重载版本。但更具体地说,C#编译器会优化这个过程,直接识别到整型到字符串的隐式转换,而不是先装箱成object

  3. 隐式转换整型到字符串:在这一步,整型值被隐式地转换为字符串。这通常是通过调用整型类型的ToString方法来实现的,尽管这个过程对程序员来说是透明的。

  4. 字符串连接:一旦整型被转换为字符串,两个字符串就可以通过string类的+操作符重载进行连接。

Resources

Quaternion.identity:对于不需要修改的默认旋转(即没有旋转),你可以直接使用Quaternion.identity。这是一个静态的只读属性,表示没有旋转的四元数。

​

    void Start()  
    {  
        // 假设你的预设体名为"MyPrefab",并且直接位于Resources文件夹下  
        GameObject prefab = Resources.Load<GameObject>("MyPrefab");  

        // 实例化预设体  
        GameObject instance = Instantiate(prefab, transform.position, Quaternion.identity);  

        // 你还可以将实例化的预设体设置为某个特定对象的子对象  
        // instance.transform.SetParent(transform, false); // false表示不改变实例化的预设体的世界位置(或不写,默认保留世界坐标)  
    }  

​

如果你的预设体(Prefab)存放在Resources文件夹下的某个子文件夹中,你需要在Resources.Load方法中指定完整的路径来加载它。

假设你的预设体名为"MyPrefab",并且它位于Resources文件夹下的一个名为"SubFolder"的子文件夹中,你可以按照以下方式加载它:

GameObject prefab = Resources.Load<GameObject>("SubFolder/MyPrefab");

Particle System(粒子系统)

粒子系统的基本概念

  • 粒子(Particle):粒子是粒子系统的基本组成单位,代表系统中的每一个微小实体。每个粒子都拥有自身的属性,如位置、速度、加速度、颜色、生命值等。
  • 属性(Attributes):粒子的属性决定了其外观和行为。常见的属性包括坐标、速度、加速度、颜色、生命值等。通过对这些属性的控制,可以实现不同的视觉效果。
  • 规则(Rules):粒子系统的核心是规则提取,即确定粒子如何运动、变化以及相互作用。这些规则可以是简单的物理规律,如重力、阻力等,也可以是复杂的自定义规则。

粒子系统在Unity中的应用

Unity中的Particle System是一个专门用于制作特效的系统,它允许开发者以高效且易于控制的方式生成大量粒子,并通过各种参数来控制粒子的行为和外观。Unity粒子系统非常灵活,涵盖了多个模块,每个模块都可以独立执行不同的功能,如Emission(发射)、Shape(形状)、Color over Lifetime(生命周期颜色变化)、Size over Lifetime(生命周期大小变化)等。

在Unity中使用粒子系统时,可以通过以下步骤来创建和配置粒子效果:

  1. 创建粒子系统:在Hierarchy面板中右键点击空白处,选择Effects -> Particle System来创建一个新的粒子系统。
  2. 设置粒子系统的属性:在Inspector面板中,可以调整粒子系统的各种属性,包括粒子的形状、大小、颜色、速度等。Unity提供了丰富的选项和参数供开发者选择和调整。
  3. 添加粒子材质:点击粒子系统的Renderer组件,在Inspector面板中的Material属性中选择一个粒子材质。Unity自带了一些默认的粒子材质,也可以导入自定义的粒子材质。
  4. 设置粒子的发射位置和形式:可以通过设置粒子系统的Transform属性来控制粒子的发射位置和旋转角度。也可以通过编写脚本来动态控制粒子的发射位置和形式。
  5. 播放粒子效果:点击播放按钮即可看到粒子效果在场景中播放出来。可以通过调整粒子系统的播放速度、循环模式等属性来控制粒子效果的播放方式。

Input.GetAxis

如果你想要检测鼠标滚轮的滚动事件,你应该使用 Input.GetAxis("Mouse ScrollWheel")。这个函数返回一个值,该值表示滚轮自上次调用以来滚动的量。如果滚轮向上滚动,这个值将大于0;如果滚轮向下滚动,这个值将小于0。请注意,这个值的大小(即滚动量)可能会因不同的硬件和操作系统而异,所以你可能需要根据自己的需求来对这个值进行缩放或解释。

Input.GetAxis 函数通常接受一个字符串参数,这个字符串是你在 Unity 的 Input Manager(输入管理器)中定义的轴(Axis)的名称。然后,它会根据当前绑定到这个轴上的输入设备(如游戏手柄的摇杆或键盘的某个键)的输入情况,返回一个介于 -1 到 1 之间的浮点数。

  • 当输入处于“中立”位置时(例如,摇杆居中),返回 0。
  • 当输入向一个方向完全倾斜时(例如,摇杆向左或向右推到极限),返回 -1 或 1,具体取决于方向。

示例

假设你有一个名为 "Horizontal" 的轴,它可能被绑定到了游戏手柄的左摇杆的水平移动或键盘的左右箭头键。你可以这样使用 Input.GetAxis 来获取这个轴的输入值:

float horizontalInput = Input.GetAxis("Horizontal");  
  
// 使用 horizontalInput 来控制游戏中的角色或相机等  
// 例如,将其乘以速度来改变角色的位置  
transform.position += new Vector3(horizontalInput * speed * Time.deltaTime, 0, 0);

注意事项

  • 在使用 Input.GetAxis 之前,确保在 Unity 的 Input Manager 中正确设置了轴的名称和绑定的输入设备。
  • Input.GetAxis 返回的是连续的值,而不仅仅是按下或释放的二进制状态。这使得它非常适合用于控制角色的移动速度或方向。
  • 如果你想要检测某个键是否被按下(而不是获取其连续的值),可以使用 Input.GetKeyDown 或 Input.GetKey
  • 对于游戏手柄,Unity 会自动处理大部分常见的输入映射,但你也可以自定义输入映射来满足特定需求。

为什么经常要乘缩放因子配上Time.deltaTime

保持帧率独立性
游戏在不同的设备和不同的性能设置下可能会有不同的帧率(FPS)。如果动画或物理模拟直接依赖于帧的数量而不是时间,那么在不同的帧率下,这些动画或模拟的速度可能会有所不同。通过使用 Time.deltaTime,你可以确保无论帧率如何变化,每帧的更新都是基于相同的时间间隔进行的。乘以缩放因子(如 150f)可以进一步调整这个速度,以适应游戏的需求。

* Time.deltaTime

Time.deltaTime 的作用就是提供一个表示自上一帧以来经过的时间(以秒为单位)的浮点数。通过乘以 Time.deltaTime,开发者可以将游戏逻辑中的时间间隔从“每秒执行多少次”转换为“每帧执行多少量的工作”,从而确保无论游戏的帧率如何变化,游戏逻辑的执行频率都是相对稳定的。

 Vector3.zero(表示(0, 0, 0)),Vector3.one(表示(1, 1, 1)),Vector3.forward(表示(0, 0, 1),即正Z轴方向),Vector3.back(表示(0, 0, -1),即负Z轴方向),Vector3.up(表示(0, 1, 0),即正Y轴方向),和Vector3.down(表示(0, -1, 0),即负Y轴方向)。

c#中的out和ref

想引用的变量必须要赋值ref是想引用之前就要赋值好,不然不准用,out是可以在引用之前不赋值,但在out引用后不赋值不让出去

ref 关键字

  • ref 关键字用于按引用传递参数。这意味着方法接收的是原始变量的引用,而不是它的一个副本。
  • 在使用 ref 参数之前,必须先将其初始化为一个具体的值。换句话说,你不能在方法内部首次为 ref 参数赋值,除非它作为 out 参数传递。
  • 使用 ref 可以让你在方法内部修改参数的值,并且这些修改会反映到原始变量上。

out 关键字

  • out 关键字也用于按引用传递参数,但它主要用于从方法中输出多个值。
  • 与 ref 不同的是,out 参数在方法内部不需要事先初始化。实际上,在方法返回之前,你必须为 out 参数赋值。
  • 使用 out 参数时,调用方法时不需要提供具体的初始值,但必须指定一个变量来接收输出值。

Lerp函数(暂时用不上)

基本格式Lerp(A, B, t)

  • A 是起始值。
  • B 是目标值。
  • t 是插值系数,通常是一个介于0和1之间的值,表示当前值在A和B之间的比例。当t=0时,返回A;当t=1时,返回B;当t在0和1之间时,返回A和B之间的插值。

Lerp函数的应用场景

  1. 数值渐变:可以用于实现数值的平滑过渡,比如游戏中角色的生命值、能量值等的增减。
  2. 位置渐变:通过Vector3.Lerp函数,可以实现物体在两个位置之间的平滑移动。
  3. 颜色渐变:通过Color.Lerp函数,可以在两个颜色之间实现平滑的颜色渐变效果,常用于UI元素的视觉反馈。

限制Unity中变量的上下限

Mathf.Clamp是Unity中最常用的方法来限制一个值在指定的最小值和最大值之间。它接受三个参数:要限制的值、最小值、和最大值,并返回被限制在最小值和最大值之间的值。

float value = 5.0f;  
float min = 0.0f;  
float max = 10.0f;  
  
// 限制value的值在0到10之间  
value = Mathf.Clamp(value, min, max);

Mathf类进行圆形计算

  • Mathf.Sin 和 Mathf.Cos:这两个函数可以用来计算正弦和余弦值,这在计算圆上某点的位置时非常有用。
  • Mathf.Sqrt:计算平方根,这在计算圆的半径或距离时可能会用到。
  • Mathf.PI:表示π的值,是计算圆形相关角度和弧度的基础。

Mathf.Sin

Mathf.Sin 函数接受一个角度(以弧度为单位)作为参数,并返回该角度的正弦值。正弦函数在数学和物理学中有着广泛的应用,特别是在波形和振荡的描述中。在游戏开发中,它可以用于实现各种基于正弦波的效果,比如物体的周期性上下移动、光的闪烁效果等。

示例用法:
float angleInRadians = Mathf.PI / 4; // 45度转换为弧度
float sineValue = Mathf.Sin(angleInRadians);
Debug.Log("Sine Value: " + sineValue);

Mathf.Cos

Mathf.Sin类似,Mathf.Cos函数也接受一个角度(以弧度为单位)作为参数,但返回的是该角度的余弦值。余弦函数与正弦函数密切相关,它们共同描述了直角三角形中边与角的关系。在游戏开发中,余弦函数同样可以用于实现各种基于余弦波的效果,或者与正弦函数结合使用来创建更复杂的运动模式。

示例用法:
float angleInRadians = Mathf.PI / 2; // 90度转换为弧度
float cosineValue = Mathf.Cos(angleInRadians);
Debug.Log("Cosine Value: " + cosineValue);

值得注意的是

Mathf.Sin 和 Mathf.Cos 函数都期望接收的参数是以弧度为单位的。 

然而,在日常生活和许多其他领域,人们更习惯于使用角度来表示角度大小。Unity中的Mathf类提供了Mathf.Deg2RadMathf.Rad2Deg两个函数,用于在弧度和角度之间进行转换。

float angleInDegrees = 90;  
float angleInRadians = Mathf.Deg2Rad(angleInDegrees);
float angleInRadians = Mathf.PI / 2;  
float angleInDegrees = Mathf.Rad2Deg(angleInRadians);

在Unity中,如果你想要将sincos的值转换回对应的角度(通常是弧度转换为角度),你需要使用反三角函数,即Mathf.Asin(对于sin的逆运算)和Mathf.Acos(对于cos的逆运算)。但是,需要注意的是,这些函数返回的是弧度值,并且由于sincos函数的周期性,它们的逆运算可能返回的是在一个周期内的角度,而不是原始角度的精确值。 

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值