Unity的C#编程教程_55_Properties 详解及应用练习
目录:
1.Properties
2.Auto Properties
3.When to Use Properties
Challenge: Declaring Properties
C# Properties
1.Properties
- 什么是属性
- 其实就是一种变量
- 可以从中获取数据
- 可以运行函数
- 控制访问修饰符 control access modifiers
来看案例:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PropertyTest : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
transform.position = new Vector3(0, 0, 0);
// 为什么通过这个语句可以进行赋值操作呢?
// 鼠标移动到 position 上面可以看到 {get;set;}
// 说明我们可以从中获取数据,也可以设置数据
// 另外一个例子是:Time.deltaTime 增量时间(代表时间修正到真实时间)
// 如果鼠标移动到说明可以看到 {get;}
// 这表示只能获取,不能设置
// 所以我们不能赋值,即不能写成 Time.deltaTime = 5; 这样
}
// Update is called once per frame
void Update()
{
}
}
- 如何定义我们自己的 property
假设我们设置一个 bool 变量监控游戏是否结束:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PropertyTest : MonoBehaviour
{
public bool isGameOver; // 指示游戏是否结束
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void GameOver() // 当玩家死亡的时候触发调用该方法
{
isGameOver = true;
// 游戏结束变为 true 的时候,游戏 UI 显示 GAME OVER
}
}
其实我们可以直接在 property 的内部进行相应的操作:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PropertyTest : MonoBehaviour
{
private bool isGameOver; // 指示游戏是否结束
// 设置为 private 后仅有该脚本本身可以访问该变量
// 我们希望该变量变为只读
public bool IsGameOver // property 的声明,使用 pascal casing 命名方式
{
get
{
return isGameOver; // 读取对应数值
}
set
{
isGameOver = value; // 赋值操作,这里 value 的数据类型需要和目标一致,这里为 bool
}
}
// Start is called before the first frame update
void Start()
{
isGameOver = false; // 游戏开始,初始化为 false
}
// Update is called once per frame
void Update()
{
}
}
当我们设置了 property 的声明后,在下面的语句中输入 isGameOver 后,鼠标移动到上面就可以看到 {get; set; },如果仅仅设置了 get,那会显示 {get; }。
然后我们把修改 UI 的链接内置到 property 中:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PropertyTest : MonoBehaviour
{
private bool isGameOver; // 指示游戏是否结束
// 设置为 private 后仅有该脚本本身可以访问该变量
// 我们希望该变量变为只读
public bool IsGameOver // property 的修饰符,使用 pascal casing 命名方式
{
get
{
return isGameOver; // 读取对应数值
}
set
{
if (value == true)
{
Debug.Log("GAME OVER"); // 假设链接到 UI 上显示 GAME OVER
}
isGameOver = value; // 赋值操作,这里 value 的数据类型需要和目标一致,这里为 bool
}
}
// Start is called before the first frame update
void Start()
{
IsGameOver = false; // 游戏开始,初始化为 false
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space)) // 假设按下空格键表示玩家死亡
{
IsGameOver = true; // 触发游戏结束的指示
}
}
}
特别注意:这里在脚本里输入 isGameOver 后有两个选择,一个是 F (isGameOver),另外一个是 P (IsGameOver),我们想要调用 get 和 set,就必须使用 P 的这个 IsGameOver。
2.Auto Properties
- 什么是 auto property
- 控制谁可以访问里面的信息
- 设置只读,不能修改:删除 set 部分即可
- 原来我们设置的时候,首先需要一个 private 变量,然后通过 property 来访问,但是现在 auto property,只需要一个语句
举例:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PropertyTest : MonoBehaviour
{
public bool IsGameOver { get; set; } // 设为 auto property
// Start is called before the first frame update
void Start()
{
IsGameOver = false;
// 这里 auto property 可以当作普通的变量使用
// 但是和一般的 property 的区别在于,不能在里面添加额外的代码
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space)) // 假设按下空格键表示玩家死亡
{
IsGameOver = true; // 触发游戏结束的指示
}
}
}
如果我们想要 IsGameOver 仅限至于本脚本可以更改,外部脚本只能访问而不能修改其值,在 set 前面添加 private 即可:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PropertyTest : MonoBehaviour
{
public bool IsGameOver { get; private set; } // 设为 auto property
// Start is called before the first frame update
void Start()
{
IsGameOver = false;
// 这里 auto property 可以当作普通的变量使用
// 但是和一般的 property 的区别在于,不能在里面添加额外的代码
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space)) // 假设按下空格键表示玩家死亡
{
IsGameOver = true; // 触发游戏结束的指示
}
}
}
根据需要,如果需要子类可以访问修改,那设置成 protected set 即可。
总结一下,property 是让我们拥有了一个可变性更强的,更智能的“变量”。
但是 property 有个缺点,就是无法在 inspector 中查看(除非进入 debug 模式)。
3.When to Use Properties
- 什么时候使用 properties 呢?
- 通常用在 manager classes 中
- 比如整个游戏的控制脚本,监控游戏角色是否死亡,控制主体游戏进程,目前游戏关卡等
- 比如游戏角色死亡状态,只有主进程 game manager 可以设定,其他脚本只能进行只读访问,那就适合设置为 property
- 还比如说游戏得分,也是类似的
- 这样的设置方法是为了更为合理,减少游戏 bug,减少游戏漏洞,减少程序的安全问题。如果随便一个脚本就可以把角色改为死亡状态,或者直接改掉总得分,那绝对会出问题!
- 另外,在最佳的编程实践中,所有变量都应该默认是 private 的,除非该变量有不得不设置为 public 的理由
- unity 中 properties 不会在 inspector 中显示
- property 不能像普通的变量那样,在命名的时候进行初始化赋值,所以需要在 start 方法中进行初始化
- 另外一种情况使用 property,在 spawn manager 中,希望监控生成了多少 enemy 总数,因为这个数据不需要我在 inspector 中进行修改
Challenge: Declaring Properties
- 任务说明
- 设计 2 个 properties
- 一个是:速度,要求对于别的脚本只读
- 一个是:名字
- 尝试使用 auto properties 和 declaring properties
- 在 declaring properties 中使用 Debug.Log 显示该方法被调用
- 在 start 方法中测试
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PropertyTest : MonoBehaviour
{
public int Speed { get; private set; } // 速度,对于别的脚本只读
private string _playerName; // 名字
public string PlayerName
{
get
{
Debug.Log("get method called");
return _playerName;
}
set
{
Debug.Log("set method called");
_playerName = value;
}
}
// Start is called before the first frame update
void Start()
{
Speed = 16;
Speed += 10;
Debug.Log("Speed: " + Speed);
PlayerName = "Mike";
PlayerName += "s";
Debug.Log("Player name: " + PlayerName);
}
// Update is called once per frame
void Update()
{
}
}