From:Unity官网C#初级编程

Unity官网C#中级编程

脚本 Scripts

作为 behavior component (行为组件),用于 Object,不同对象的不同行为


Start & Awake

Awake Start
初始化之前,脚本不启用也会初始化 Awake 之后,首次 Updata 之前,脚本启用才会初始化

Public & Private & Protected

Public Private Protected
类外(其他脚本)可以访问 只允许类内访问,默认私有 允许子类访问

Updata & FixedUpdata

Updata FixedUpdata
使用的脚本每帧调用一次,调用间隔不同,非物理相关 固定时间,间隔相同调用,每秒调用50次,用于物理(Rigibody)相关函数,力相关
1
2
3
4
5
6
7
8
9
private void Update()
{
Debug.Log("Updata Time: " + Time.deltaTime); // Time.deltaTime 每帧增加的间隔时间
}

private void FixedUpdate()
{
Debug.Log("FixedUpdata Time: " + Time.deltaTime);
}
1
2
3
#结果
Updata Time: 0.0023467 0.0023239 ……
FixedUpdata Time: 0.02 0.02 0.02 ……

DeltaTime

Time 类的 DeltaTime 用于计算两次更新或固定函数调用的间隔时长,用于移动或其他增量变化变得平滑

因为完成一帧的时长是不一致的,按完成每帧做速率会不平滑

Speed * Time.deltaTime 用于速率恒定


Invoke

延迟执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public GameObject target;

void Start()
{
Invoke("SpawObject", 2); // 2s后实例化出target
InvokeRepeating("SpawObject", 2, 1); // 2s后实例化出target,生成后间隔为1秒

CancelInvoke("SpawObject"); // 取消循环
}

void SpawObject()
{
float x = Random.Range(-2f, 2f); // Random 随机n Range 范围n
Instantiate(target, new Vector(0, 2, 0), Quaternion.identity);
}

点积 & 叉积

Dot 点积 Cross 叉积
计算得到数值,==0为垂直,可用于飞机飞行方向判断 计算得到矩阵,可用于炮塔朝向
Vector3. Dot (VectorA, VectorB) Vector3. Cross (VectorA, VectorB)

1


启动组件

Inspector 窗口 ->组件前面的勾

1
Component.enabled = false or true;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class LightEnable : MonoBehaviour
{
private Light pointLight;

// Start is called before the first frame update
void Start()
{
pointLight = GetComponent<Light>();
}

// Update is called once per frame
void Update()
{
if (Input.GetKeyUp(KeyCode.Space)) // GetKeyUp 按下弹起算一次
pointLight.enabled = false;
}
}

启用对象

Hierarchy 窗口 -> GameObject 是否开启

1
>gameObject.SetActive(false);

Translate & Rotate

平移和旋转:针对局部坐标(Local axis)

Vector3. forward and Vector3. Up 都是局部坐标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class TransformObject : MonoBehaviour
{
public float moveSpeed = 10f;
public float turnSpeed = 50f;


// Update is called once per frame
void Update()
{
if (Input.GetKey(KeyCode.UpArrow)) // GetKey 长按
transform.Translate(Vector3.forward * Time.deltaTime * moveSpeed); // 作用于 Local axis 局部坐标
if (Input.GetKey(KeyCode.DownArrow))
transform.Translate(-Vector3.forward * Time.deltaTime * moveSpeed);
if (Input.GetKey(KeyCode.LeftArrow))
transform.Rotate(Vector3.up, -turnSpeed * Time.deltaTime); // up表示哪个轴
if (Input.GetKey(KeyCode.RightArrow))
transform.Rotate(Vector3.up, turnSpeed * Time.deltaTime);
}
}

LookAt

GameObject 的 forward 指向世界坐标(World axis)的另一个transform

1
2
3
4
5
6
7
# 摄像机看向目标
Transform target;

void Update()
{
transform.LookAt(target);
}

Destory

1
2
3
# 销毁对象or组件,第二个参数为延时(delay)
Destory(GameObject, 3f);
Destory(GetComponent<MeshRenderer>());

GetButton & GetKey

  • GetButton:输入管理器 Project Setting -> Input 设置按键,string 类型传入按键名

    1
    bool Up = Input.GetButtonUp("Jump");
  • GetKey:GetKey(KeyCode. X) 具体指定按键

    1
    2
    3
    bool crouch = Input.GetKey(KeyCode.S)			//长按
    Input.GetKeyUp(KeyCode.S) //按下弹起一次后触发
    Input.GetKeydown(KeyCode.S) //按下就触发

OnMouseDown

1
2
3
4
5
# 判断鼠标点击
void OnMouseDown()
{
rb.AddForce(-transform.forward * 500f); //鼠标点击推出物体
}

Class

面向对象编程:将脚本拆分成多个脚本,每一个脚本承担一个角色或指责

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
29
30
public Class School: MonoBehavior
{
public int studentCout;
public int teacherCout;
public int bookCout;
public float avgNum;
// 构造函数,与类同名,可以重载,不需要返回值,构造函数用于初始化
public School(int a, int b, int c)
{
studentCout = a;
teacherCout = b;
bookCout = c;
}
public School(int a, float avg)
{
student = a;
avgNum = avg;
}
public School()
{
studentCout = 1;
teacherCout = 1;
bookCout = 1;
}

// 实例化
public School dogSchool = new School(10, 100, 1000);
public School catSchool = new School(20, 50.5f);

}

Instantiate

Instantiate 用于克隆 gameObject,常用于克隆 Prefabs,即预配置对象

例如抛射出的物体

1
2
>Instantiate(object);	// 参数为想克隆的对象
>Instantiate(object, position, rotation); // 指定位置和角度

数组

1
2
3
4
5
6
int[] myIntArray = new int [3];
myIntArray[0] = 1;
myIntArray[1] = 2;
myIntArray[2] = 3;

int[] myInyArray = {1, 2, 3};

枚举

列出枚举值,定义变量,选择枚举值给变量赋值

1
2
3
4
5
6
7
enum Direction {North, East, South, West};	// 枚举 Direction 类型的4个值
Direction myDirection; // Direction 类型的变量 myDirection,值为4选1

void Start()
{
myDirection = Directio.North;
}

Switch

更简洁的选择语句,避免选择多时代码变得冗长

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
// NPC等级不同对白不同可用Switch
public class SwitchText : MonoBehaviour
{
// 设定最高智慧
public int intelligence = 5;

void Greet()
{
switch(intelligence)
{
case 5:
print("Nice to meet u!");
break; // break跳出循环
case 4:
print("Hello!");
break;
case 3:
print("ababababa");
break;
case 2:
print("0100010010");
break;
default: // 类似于else 未讨论到情况
print("Aroha!");
break;
}
}
}

属性

对私有变量建立属性,其他类可以通过属性访问

  1. 通过 get 和 set 的保留设置只读或只写
  2. 可以当做函数使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Player : MonoBehaviour
{
private int experience; // 私有变量经验

public int Experience
{
get // get必须有返回值,读
{
return experience;
}
set
{
// 前面可以加函数,写
experience = value;
}
}

// 简写
public int Health { get; set; }

}
  • 调用
1
2
3
4
5
6
7
8
void Start()
{
// 先生成实例再访问
Player myPlayer = new Player();

myPlayer.Experience = 5;
int x = myPlayer.Experience;
}

三目运算符

简答的 if-else 关系,执行内容短

1
2
3
4
int health = 10;
string messing;

messing = health > 0 ? "Player is alive" : "Player is dead";

Static 静态

跨类所有成员共享的成员,无需实例化,一个类中静态成员值一样,不可以包含非静态成员

静态方法属于类,非静态方法属于类的实例

1
2
3
4
5
6
7
# 计算生成实例 Enemy 数量
public class Enemy
{
pubic Static int enemyCout = 0;

public Enemy{ enemyCout++ }; // 调用一次数量+1
}
1
2
3
4
5
6
7
8
9
10
11
public class Game
{
void Start()
{
Enemy enemy1 = new Enemy();
Enemy enemy2 = new Enemy();
Enemy enemy3 = new Enemy();

int x = Enemy.enemyCout;
}
}

泛型

GetComponent<> 泛型方法,T 表示任何类型

所有 C# 类隐式从基类继承而来

1
2
3
4
5
6
7
8
9
public class SomeClass
{
// 对泛型参数添加限制 where T: class, struct, new(), MonoBehaviour, IEnumerable
public T GenericMethod<T>(T param) where T: class
// T 类型方法,返回T类型值,作为基类处理
{
return param;
}
}
1
2
3
4
5
6
7
8
9
# 调用
public class SomeOtherClass
{
void start()
{
SomeClass myclass = new SomeClass(); // 实例化
myclass.GenericMethod<int>(5);
}
}

继承

MonoBehaviour 类有 start、updata 等函数

eg:BoxCollider 是 Collider 的子类,BoxCollider 是 Collider

子类调用的父类构造函数是固定的,base()

1
2
3
4
public class Scripts: MonoBehaviour	// Scripts 类继承自 MonoBehaviour类
{
}
public class Scripts // Scripts 类不继承自任何类

Overriding

更改子类中父类的方法

子类中创建函数,两个子类用的父类函数功能不一样

覆盖父类函数且保留方法的父版本

1
2
3
4
5
6
7
8
9
# 父类 virtual
public class Humanoid()
{
public virtual void Yell() // 父类用 virtual 定义,可以被子类覆盖
{
// Play "Yelling Sound"
}

}
1
2
3
4
5
6
7
8
9
# 派生类1
public class Enemy: Humanoid
{
public override void Yell()
{
base.Yell(); // 调用父方法 Play "Yelling Sound"
// Attract nearby enemies
}
}
1
2
3
4
5
6
7
8
9
# 派生类2
public class Orc: Enemy
{
public override void Yell() // 派生类的子类也可以覆盖
{
base.Yell(); // 调用父方法 Attract nearby enemies
// Power up nearby orcs
}
}

接口

  • 子类:子类 is a 父类,一个类只可以继承自一个类

  • 接口:类 实现 接口,一个类可以实现多个接口

实现接口的任何类,必须拥有其所有方法和属性

用于跨多个互不相关的类定义通用功能(继承同一父类没有意义,用接口销毁所有更有效)

类外声明,一般一个接口一个文件,直接调用实现,不能生成实例,类要声明接口中的函数

通常大写 I 开头作为接口名字

1
2
3
4
5
6
7
8
9
# Interface
public interface Ikillable // 名字I开头
{
void Kill(); // 类内声明此函数
}
public interface IDamagable<T>
{
void Damage(T damageTaken);
}

函数主体与接口互相独立

1
2
3
4
5
6
7
8
9
10
11
12
# Avatar
public class Avatar: MonoBehavior, Ikillable, IDamagable<float> // 调用接口传入类型
{
void Kill()
{
// Do kill
}
void Damage(float damageTaken)
{
// Do damage
}
}

Extension Methods

用于需要向类添加功能,但不能编辑类

eg:transform 类不能修改源代码

1
2
3
4
5
6
7
8
9
10
# Extension Methods 扩展transform的方法,初始化transform的要求值
public static class ExtensionMethod // 静态方法
{
public static void ResetTransformation(this Transform trans)
{
trans.position = Vector3.zero; //位置全部清 0
trans.localRotation = Quanternion.identity;
trans.localScale = new Vector3(1, 1, 1);
}
}
1
2
3
4
5
6
7
8
# SomeClass
public class SomeClass: Monobehaviour
{
void start()
{
transform.ResetTransformation();
}
}

命名空间

NameSpace 就像类的容器

1
2
3
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

列表和字典

list 是泛型类

列表对象. Add(添加) Remove(移除) Insert(插入) Sort(排序)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# BadGuy
public class BadGuy: IComparable<BadGuy>
{
public string name;
public int power;

// 构造函数
public BadGuy(string newName, int newPower)
{
name = newName;
power = newPower;
}

public int CompareTo(BadGuy other) // 比较接口
{
if (other == null)
return 1;
return power - other.power;
}
}
  • 列表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# SomeClass 创建列表
public class SomeClass : MonoBehaviour
{
void Start()
{
List<BadGuy> badguys = new List<BadGuy>(); // 新建存储badguy类型的列表badguys
badguys.Add(new BadGuy("Harry", 50));
badguys.Add(new BadGuy("Pip", 10));
badguys.Add(new BadGuy("Tom", 100));

// badguys.Remove Insert Sort
badguys.Sort();
foreach (BadGuy guy in badguys)
print(guy.name + " " + guy.power);
badguys.Clear();
}
}
  • 字典
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class SomeClass : MonoBehaviour
{
void Start()
{
// 键值对
Dictionary<string, BadGuy> badguys = new Dictionary<string, BadGuy>();

BadGuy bg1 = new BadGuy("Harry", 10);
BadGuy bg2 = new BadGuy("Tom", 20);

badguys.Add("Marry", bg1);
badguys.Add("Jerry", bg2);
}
}

协程

Coroutines,Yield 执行,IEnumerator 返回

按时间间隔执行的函数

  • IEnumerator 不是 IEnumerable
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// object 向另一个 position 移动的协程
public class SomeClass : MonoBehaviour
{
public float smoothing = 1f;
public Transform target;

void Start()
{
StartCoroutine(MyCoroutine(target));
}

IEnumerator MyCoroutine(Transform target)
{
// 每次更新执行按速率接近
while (Vector3.Distance(transform.position, target.position) > 0.05f)
{
transform.position = Vector3.Lerp(transform.position, target.position, smoothing * Time.deltaTime);
yield return null; // 一直执行while,执行一次返回一次null
}
print("Reach the Target!");
yield return new WaitForSeconds(3f); // 循环结束后更新执行
print("MyCoroutine is finished!"); // 3s后输出 协程结束
}
}

四元数

Quaternions:x,y,z,w

欧拉角:围绕 x,y,z 轴旋转

1
2
3
4
5
6
7
8
9
Vector3 relativePos = target.position - transform.position;
Quaternion rotation = Quaternion.LookRotation(relativePo); // 面向目标旋转

Quaternion current = transform.localRotation;

transform.localRotation = Quaternion.slerp(current, rotation, Time.deltaTime);
// 始终向前移动并面向目标,实现轨道效应

transform.rotation = Quaternion.identity; // 设置旋转为(0, 0, 0) 无旋转
  • lerp 线性插值:两个四元数均匀插值,始终均匀变化

  • slerp 球形插值:曲线上插值,变化中间加速度,后面越来越缓慢


委托

Delegates

看做存放函数的容器的变量,处理复杂行为,同时控制多个函数

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
public class DelegateTest : MonoBehaviour
{
// 建立委托
delegate void MultiDelegate();
MultiDelegate multiDelegate; // 实例化

void Start()
{
multiDelegate += PowerUp; // 加入函数
multiDelegate += TurnRed;

// 委托不为空就需要执行
if (multiDelegate != null)
multiDelegate();
}

// 委托包含下面两个函数
void PowerUp()
{
print("Orb is Up!");
}
void TurnRed()
{
GetComponent<Renderer>().material.color = Color.red;
}
}

事件

Events 特殊委托,方法(函数)定位到事件

通过 OnEnable += 将方法订阅到事件,在发生事件时可以调用方法

通过 OnDisable -= 将方法退订,在事件销毁时停用方法

  • 事件控制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# EventManager
public class EventManager : MonoBehaviour
{
public delegate void ClickAction(); // 委托 ClickAction(),订阅的事件无参数且返回为void
public static event ClickAction OnClicked; // 事件变量:在发生事件时调用 static 无需实例化

// 通过GUI创建按钮,按下触发事件 OnClicked(即其订阅的方法)
void OnGUI()
{
if (GUI.Button(new Rect(Screen.width / 2 - 50, 5, 100, 30), "Click"))
{
if (OnClicked != null)
OnClicked();
}
}
}
  • 订阅函数1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 变色效果
public class TurnColor : MonoBehaviour
{
void OnEnable()
{
EventManager.OnClicked += Turn;
}

void OnDisable()
{
EventManager.OnClicked -= Turn;
}

void Turn()
{
Color col = new Color(Random.value, Random.value, Random.value); //Color(R, B, G)
GetComponent<Renderer>().material.color = col;
}
}
  • 订阅函数2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 位移效果
public class TeleporScripts : MonoBehaviour
{
private void OnEnable()
{
EventManager.OnClicked += Teleport; // 注册方法,无需实例化
}

private void OnDisable()
{
EventManager.OnClicked -= Teleport;
}
void Teleport() // 跳动
{
Vector3 pos = transform.position;
pos.y = Random.Range(.3f, 1.0f);
transform.position = pos;
}
}

鼠标点击按钮,两个事件同时触发


特性

1
2
3
4
5
6
7
8
9
10
[ExecuteEditMode]		//脚本不执行也会实现类中效果
public class SpinScript: Monobehaviour
{
// 特性限制速度范围,Inspector 速度变成滑动条
[Range(-100, 100)]
public float speed = 0;
// 特性为变量分类,显示在变量前面
[Header("Player")]
GameObject player;
}

Print & Debug.log

  • 两者都输出到 Console 控制台,属于 UnityEngine 命名空间

  • Debug 是一个密闭的类, Print 是 MonoBehaviour 的一个成员,print 就是 Debug.Log 的一个简单封装

1
2
3
4
5
6
7
8
9
10
using UnityEngine;

public class OutPut: Monobehaviour
{
void start()
{
print("test Print");
Debug.log("test Debug");
}
}

From:https://blog.csdn.net/qq_42351033/article/details/83990499