在 Project-Assets 中,右擊-創建-腳本。或為物體添加一個自定義名稱的組件(就是腳本)。
Visual Studio
- 將VS的快捷鍵改成同VSCode:找到工具-->選項-->鍵盤-->映射方案 --> vscode
- 先在VS中點擊運行,再在Unity中點擊運行,即可進行斷點調試等。
生命周期
Awake
當前腳本實例創建時調用,類似構造函數
此時已經可以執行GameObject.Find
和GetComponent
等操作
Start
啟用腳本實例后,第一次幀更新之前,調用該方法
因此,在Update中通過腳本創建物體時,該物體的Start方法不會在當前幀調用
Update
每一幀都會調用。受Application.targetFrameRate
影響,不受Time.timeScale
影響。
LateUpdate
會在所有物體的Update
都完成后觸發。通常用于移動相機跟隨角色邏輯。
FixedUpdate
- Unity會嘗試以固定幀率(Edit->ProjectSetting->time)調用該函數。當某次FixedUpdate被阻塞而錯過調用時,Unity會在之后額外調用以彌補該次。因此在 FixedUpdate 內應用運動計算時,無需將值乘以
Time.deltaTime
(可乘以固定值Time.fixedDeltaTime
)。 - 兩次Update間可能會存在多次FixedUpdate,也可能一次都不存在
- 通常在該回調中進行物理模擬相關邏輯。
OnEnable
當前腳本所屬的GameObject每次被激活時調用(gameObject.SetActive(true)
)
OnDisable
當前腳本所屬的GameObject每次失活時調用(gameObject.SetActive(false)
)
OnDestroy
對象被銷毀時被調用(依附的GameObject對象被刪除時)
獲取內容
獲取GameObject
在腳本中聲明public
變量并在Script組件中手動對其賦值為目標GameObject,或通過方法查找對象:
public GameObject ball;//手動賦值
GameObject.Find("KimBall");
GameObject.FindGameObjectWithTag("KimBall");
注意,該方法無法查到被隱藏的對象(即在Hierarchy
中為灰色的,isActive=false的對象)。此時可通過其父元素的transform.Find進行查找:
ParentGameObject.transform.Find("Son Name").gameObject;
獲取組件
在腳本中聲明public
變量并在UnityEditor中對其賦值為目標組件,或通過方法/屬性獲取組件:
public Transform ballTransform;//手動賦值
ball.transform;
ball.GetComponent<Transform>();//應在Start而非Update中使用,防止性能浪費
ball.GetComponent("Transform");
//對于自身組件
transform;
GetComponent<Transform>();
GetComponent("Transform");
其中GetComponent
會返回第一個符合條件的組件,如果存在多個同類型組件,可以使用GetComponents
以返回組件列表。
引用其他腳本內容
- 通過
GetComponent
獲取對應腳本實例
ScoreController scoreController = GameObject.Find("Text").GetComponent<ScoreController>();
scoreController.DoSomething();
- 或使用單例模式
//在腳本UIHealthBarController中
public class UIHealthBarController: MonoBehaviour
{
public static UIHealthBarController instance { get; private set; }
private void Awake()
{
instance = this;
}
}
//在其他腳本中進行引用
UIHealthBar.instance
物體控制
控制物體顯隱
- 創建物體
通常不直接通過腳本創建物體,而是通過復制預制體的方式實現。
也可創建一個空物體,充當新物體的父級:
GameObject newParent = new GameObject("newParent");
GameObject newObj = Instantiate(bulletPrefab, newParent.transform);
- 復制物體
注意:預制體也屬于 GameObject。
Instantiate(Object original);
Instantiate(Object original, Transform parent);
Instantiate(Object original, Transform parent, bool instantiateInWorldSpace);
Instantiate(Object original, Vector3 position, Quaternion rotation);
Instantiate(Object original, Vector3 position, Quaternion rotation, Transform parent);
- 銷毀物體
Destroy(gameObject);
- 物體顯隱
gameObject.SetActive(true);
gameObject.SetActive(false);
控制物體移動
物體的坐標是一個三維向量
Vector3 position = new Vector3(0f, 0f, 10f);
- 直接控制物體的transform組件
- 缺點:剛體碰撞時,該位移和系統計算的剛體碰撞受力位移可能會沖突,造成物體碰撞時抖動。
transform.position = position;//絕對坐標
transform.localPosition = position;//相對于父物體的坐標
- 控制剛體組件移動(
MovePosition
)
如在其他地方調用,則會等到下一次FixedUpdate
才執行。且連續調用多次只有最后一個生效。因此建議在FixedUpdate
中調用。- 缺點:速度較快時,可能會直接越過剛體邊界,造成穿模。此時可修改主動運動方的剛體組件
Collision Detection
屬性為Continuous
解決,但會影響性能。
此外該方法必須同時設置x、y兩個值,容易和其他物理系統沖突。
- 缺點:速度較快時,可能會直接越過剛體邊界,造成穿模。此時可修改主動運動方的剛體組件
private Rigidbody2D rigidbody2D;
rigidbody2D = GetComponent<Rigidbody2D>();
rigidbody2D.MovePosition(position);
- 控制剛體組件受力
public Rigidbody rd;
Vector3 dir = new Vector3(h, j*10, v);
rd.AddForce(dir);
- 控制剛體組件速度
GetComponent<Rigidbody>().velocity = transform.forward * 100000f;
控制物體旋轉角度
腳本中,Transform 和 Rigidbody 的rotation
均不是Vector3(歐拉角),而是Quaternion(四元數),以避免萬向鎖問題。
以下以 Transform 的旋轉舉例,Rigidbody 的旋轉同理:
- 四元數轉歐拉角:
eulerAngles為Quaternion類的一個Get方法返回值
Vector3 r = transform.rotation.eulerAngles;
- 歐拉角轉四元數:
Quaternion rotation = Quaternion.Euler(new Vector3(0f, 30f, 0f));
-
transform.rotation
transform.localRotation
直接獲取或設置物體的四元數角
Vector3 r = transform.rotation.eulerAngles;
Quaternion rotation = Quaternion.Euler(r);
transform.rotation = rotation;
transform.eulerAngles
transform.localEulerAngles
直接獲取或設置物體的歐拉角transform.Rotate()
設置角度在當前基礎上的偏移值。
當xyz軸均有值時,按z、y、x順序依次發生旋轉。
默認圍繞自己坐標系旋轉,可改為Space.World
圍繞絕對坐標系旋轉。
public void Rotate(Vector3 eulerAngles, Space relativeTo = Space.Self);
public void Rotate(float xAngle, float yAngle, float zAngle, Space relativeTo = Space.Self);
-
Quaternion.identity
默認的無旋轉狀態四元數
計時器
通過時間自行實現
每隔一秒做某件事:
private float delayTime = 1;
private void Update()
{
if(Time.time >= delayTime)
{
//你要做的事......
delayTime += Time.time;
}
}
private float timer = 0;
private float delayTime = 1;
private void Update()
{
timer += Time.deltaTime;
if(timer >= delayTime)
{
//你要做的事......
timer = 0;
}
}
Invoke(string methodName,float time)
等待time秒后執行方法。其中函數名可以通過nameof(函數)
獲取。
InvokeRepeating (string methodName, float time, float repeatRate);
等待time秒后執行方法,之后每間隔 repeatRate 再次執行。
CancelInvoke(string methodName)
若傳入參數,則停止指定函數名在當前腳本中的所有Invoke和InvokeRepeating方法。否則停止當前腳本中所有Invoke和InvokeRepeating方法。
其他方法
- magnitude Vector3 向量的長度
transform.position.magnitude
射線檢測
創建一條射線,并判斷射線是否與指定layer有碰撞,并找到碰撞物
void Update()
{
// 參數依次為起點、方向、長度、layer
RaycastHit2D hit = Physics2D.Raycast(rigidbody2d.position + Vector2.up, lookDirection, 1.5f, LayerMask.GetMask("NPC"))
if (hit.collider != null){
hit.collider.gameObject
}
}