文章

Unity简单入门

Unity简单入门

C#基础

备忘

大多数略,以下为备忘。

枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
enum DayWeek {
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday
}
Console.WriteLine((int)DayWeek.Monday); // 0
Console.WriteLine((DayWeek)1); // Tuesday
Console.WriteLine(DayWeek.Monday.ToString()); // Monday
Console.WriteLine(DayWeek.Monday); // Monday

结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct Book
{
    public string title;
    public string author;
    public int year;

    // 结构体不能包含显式的无参构造函数
    // 写了有参构造函数 则字段必须要赋值
    public Book(string title, string author, int year)
    {
        this.title = title;
        this.author = author;
        this.year = year;
    }
}

Book book = new Book();
Book book2; // 可以不用new来构造

协程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
IEnumerator MyCoroutine()
{
    yield return new WaitForSeconds(1);
    Debug.Log("1秒后执行");
    yield return null;
    Debug.Log("下一帧执行");
    yield return new WaitForEndOfFrame();
    Debug.Log("本帧末执行");
}

void Start()
{
    StartCoroutine("MyCoroutine");
    StopCoroutine("MyCoroutine");

    var coroutine = MyCoroutine();
    StartCoroutine(coroutine);
    StopCoroutine(coroutine);

    StopAllCoroutines();
}

readonlyconst的区别: 常量必须在声明的时候初始化,而只读变量可以延迟到构造函数中初始化。常量只能修饰基本类型、枚举和字符串。常量对类的所有对象而言都是一样的,只读变量则可以不同。只读变量是要分配内存的。

refout的区别: refout都是引用传递,ref要求变量在传递前必须初始化,而out要求变量在函数内必须赋值。ref用于函数修改其值,out用于接值,以起到“返回”多个值的效果。

常用API

字符串常用API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
string str = "Hello World";
Debug.Log(str.Length); // 11
Debug.Log(str[0]); // H
Debug.Log(str.IndexOf("World")); // 6
Debug.Log(str.LastIndexOf("o")); // 7
Debug.Log(str.Substring(6)); // World
Debug.Log(str.Substring(6, 5)); // World
Debug.Log(str.ToUpper()); // HELLO WORLD
Debug.Log(str.ToLower()); // hello world
Debug.Log(str.Replace("World", "Unity")); // Hello Unity
Debug.Log(str.Contains("World")); // true
Debug.Log(str.StartsWith("Hello")); // true
Debug.Log(str.EndsWith("World")); // true
Debug.Log(str.Trim()); // Hello World
Debug.Log(str.TrimStart()); // Hello World
Debug.Log(str.TrimEnd()); // Hello World
Debug.Log(str.Insert(6, "Unity ")); // Hello Unity World
Debug.Log(str.Remove(6)); // Hello
Debug.Log(str.Remove(6, 5)); // Hello
Debug.Log(str.Split(' ')); // Hello,World
Debug.Log(str.Equals("Hello World")); // true
Debug.Log(str == "Hello World"); // true
Debug.Log(string.IsNullOrEmpty(str)); // false
Debug.Log(string.Compare(str, "Hello World")); // 0
Debug.Log(string.Format("Hello {0}", "Unity")); // Hello Unity
Debug.Log($"{str}!"); // Hello World!

数组

1
2
3
4
5
int[] arr = new int[5];
int[] arr2 = new int[] { 1, 2, 3, 4, 5 };
int[] arr3 = { 1, 2, 3, 4, 5 };
int[,] arr4 = new int[2, 3];
int[,] arr5 = { { 1, 2, 3 }, { 4, 5, 6 } };

列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
List<int> list = new List<int>();
list.Add(1);
list.Add(2);
list.Clear();
list.Add(3);
list.AddRange(new int[] { 3, 4, 5, 6, 7, 8, 9});
list.Insert(0, 0);
list.Remove(0);
list.RemoveAt(0);
list.Sort();
list.RemoveRange(3, 2);
list.Reverse();
Debug.Log(list.Contains(3));
Debug.Log(list.IndexOf(3));
Debug.Log(list.LastIndexOf(3));
Debug.Log(list.Count);
Debug.Log(list.Capacity);
Debug.Log(list.ToArray());
foreach (int i in list)
{
    Debug.Log(i);
}

字典

1
2
3
4
5
6
7
8
9
10
11
12
13
Dictionary<string, int> dict = new Dictionary<string, int>();
dict.Add("one", 1);
if dict.ContainsKey("one")
{
    dict.Remove("one");
}
dict["one"] = 1;
dict["two"] = 2;
dict["three"] = 3;
foreach (KeyValuePair<string, int> kvp in dict)
{
    Debug.Log(kvp.Key + " " + kvp.Value);
}

在Unity里编写代码

可以在检查器里修改的私有字段:

1
2
[SerializeField]
private int health = 100;

延迟调用:

1
Invoke("FuncName", delaySec);

VS快捷键

重命名标识符:ctrl+R+R 封装属性:ctrl+R+E

1
public int CurrentHealth { get => currentHealth; set => currentHealth = value; }

格式化:ctrl+K+F 格式化文档:ctrl+K+D

事实上这里有个帮助界面

Unity基础

Unity生命周期

继承MonoBehaviour类,Unity自动调用生命周期函数。

  • Reset() 脚本第一次挂载到对象上,或使用Reset命令时调用。程序不运行时调用, 只调用一次,初始化脚本的属性。
  • Awake() 调用场景时/GameObject激活时/使用Instantiate创建GameObject时,仅调用一次,代替构造函数来初始化。
  • OnEnable() 依附的GameObject每次被激活时调用。每次激活时调用一次,初始化。
  • Start() 在第一帧更新之前执行。只有激活后才会被调用。
  • FixedUpdate() 每固定帧调用一次。
  • Update() 实时更新数据,每帧调用。
  • LateUpdate() Update()之后Unity会处理渲染,一般在那之后再处理摄像机位置等更新。
  • OnDisable() 每次被禁用时调用。销毁时调用。用于状态重置和清理。
  • OnApplicationQuit() 程序退出前调用/编辑器中用户终止播放模式时调用/网页视图关闭时调用。用于处理游戏退出后的一些逻辑。
  • OnDestroy() 销毁时调用。在最后一帧的所有Update()执行完后调用。

GameObject组件

GameObject是所有时Unity中所有实体的基类。

动态创建:

1
2
new GameObject("name"); // 创建一个空物体
GameObject.CreatePrimitive(PrimitiveType.Cube); // 创建一个Cube
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
gameObject.isStatic = true; // 设置为静态
gameObject.SetActive(true); // 设置为激活状态
gameObject.tag = "tag"; // 设置标签

GameObject.Destroy(gameObject); // 销毁物体

// 查找:
GameObject.Find("name"); // 查找名字为name的物体 名字可以写路径
GameObject.FindGameObjectsWithTag("tag"); // 查找标签为tag的所有物体
GameObject.FindWithTag("tag"); // 查找标签为tag的第一个物体
GameObject.FindObjectOfType<T>(); // 查找第一个T类型的物体

// 添加/获取组件:
gameObject.AddComponent<T>(); // 添加T类型的组件
gameObject.GetComponent<T>(); // 获取T类型的组件
gameObject.GetComponents<T>(); // 获取T类型的所有组件
gameObject.GetComponentInParent<T>(); // 获取父物体T类型的组件
gameObject.GetComponentInChildren<T>(); // 获取子物体T类型的组件

游戏对象的API

Transform组件

所有游戏对象都有Transform组件,有Position、Rotation、Scale三个属性。

属性:

1
2
3
4
5
6
7
Debug.Log(transform.localPosition);
Debug.Log(transform.position);
Debug.Log(transform.localScale);
Debug.Log(transform.localRotation); // 四元数
Debug.Log(transform.rotation);
Debug.Log(transform.eulerAngles);
Debug.Log(transform.localEulerAngles);

对rotate的操作应该使用外部的欧拉角,因为四元数不表示180度以上的旋转,而unity输出的欧拉角是从四元数计算得来的。

1
2
3
4
5
6
float x;
void Update ()
    {
        x += Time.deltaTime * 10;
        transform.rotation = Quaternion.Euler(x,0,0);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
if (GUILayout.Button("旋转"))
{
    transform.Rotate(new Vector3(0, 0, 10));
}
if (GUILayout.Button("绕轴旋转"))
{
    transform.RotateAround(transform.parent.position, new Vector3(0, 1, 0), 10);
}
if (GUILayout.Button("放大"))
{
    transform.localScale *= 1.1f;
}
if (GUILayout.Button("缩小"))
{
    transform.localScale *= 0.9f;
}
if (GUILayout.Button("向左移动"))
{
    transform.localPosition += new Vector3(-1, 0, 0);
}
if (GUILayout.Button("向右移动"))
{
    transform.localPosition += new Vector3(1, 0, 0);
}
if (GUILayout.Button("看向"))
{
    transform.LookAt(new Vector3(0, 0, 0)); // 前方指向目标 默认上方是世界上方
}

游戏对象的显示

MeshFilter:选择网格,包含顶点坐标、法线、纹理坐标、三角形绘制顺序等。 MeshRender:渲染网格 Material:定义材质

项目里 创建 > 材质

1
GetComponent<MeshRenderer>().material.color = Color.red;

Resources的方法: FindObjectsOfTypeAll:查找所有T类型的物体 Load:通过路径加载资源 LoadAll:加载所有资源 LoadAsync:异步加载资源 Unload:卸载资源 UnloadUnusedAssets:卸载未使用的资源

1
2
3
4
5
6
Material material = Resources.Load("新建材质1") as Material;
if (material != null)
{
    Renderer renderer = GetComponent<Renderer>();
    renderer.sharedMaterial = material;
}
本文由作者按照 CC BY 4.0 进行授权