创建脚本 脚本要创建在除Editor文件夹以外的任意目录或子目录下
脚本模板 脚本模板的位置:C:\Program Files\Unity\Hub\Editor\2020.3.6f1c1\Editor\Data\Resources\ScriptTemplates(视Unity安装位置而定),前面的数字代表菜单栏的顺序
拓展脚本模板 使用这种方法,在本地工程文件内添加自己的模板,而不是去修改Unity安装目录下的模板
在工程的Editor文件夹中,新建ScriptTemplates文件夹,放入C# Script-MyNewBehaviourScript.cs.txt文件,文件的内容如下
1 2 3 4 5 6 7 8 9 10 11 using System.Collections;using System.Collections.Generic;using UnityEngine;public class #NAME # : MonoBehaviour { void MyFunction () { } }
模板文件编辑完毕后,在Windows情况下,请确认行尾序列为CRLF
然后在Editor文件夹中新建ExtendingScriptTemplates
脚本
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 using UnityEngine;using UnityEditor;using UnityEditor.ProjectWindowCallback;using System;using System.IO;using System.Text;using System.Text.RegularExpressions;public class ExtendingScriptTemplates { private const string MY_SCRIPT_DEFAULT = "Assets/Editor/ScriptTemplates/C# Script-MyNewBehaviourScript.cs.txt" ; [MenuItem("Assets/Create/C# MyScript" ,false,80) ] public static void CreatMyScript () { string locationPath = GetSelectedPathOrFallback(); ProjectWindowUtil.StartNameEditingIfProjectWindowExists(0 , ScriptableObject.CreateInstance<MyDoCreateScriptAsset>(), locationPath + "/MyNewBehaviourScript.cs" , null , MY_SCRIPT_DEFAULT); } public static string GetSelectedPathOrFallback () { string path = "Assets" ; foreach (UnityEngine.Object obj in Selection.GetFiltered(typeof (UnityEngine.Object),SelectionMode.Assets)) { path = AssetDatabase.GetAssetPath(obj); if (!string .IsNullOrEmpty(path) && File.Exists(path)) { path = Path.GetDirectoryName(path); break ; } } return path; } } class MyDoCreateScriptAsset : EndNameEditAction { public override void Action (int instanceId, string pathName, string resourceFile ) { UnityEngine.Object o = CreateScriptAssetFromTemplate(pathName, resourceFile); ProjectWindowUtil.ShowCreatedAsset(o); } internal static UnityEngine.Object CreateScriptAssetFromTemplate (string pathName,string resourceFile ) { string fullPath = Path.GetFullPath(pathName); StreamReader streamReader = new StreamReader(resourceFile); string text = streamReader.ReadToEnd(); streamReader.Close(); string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(pathName); text = Regex.Replace(text, "#NAME#" , fileNameWithoutExtension); bool encoderShouldEmitUTF8Identifier = true ; bool throwOnInvalidBytes = false ; UTF8Encoding encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier, throwOnInvalidBytes); bool append = false ; StreamWriter streamWriter = new StreamWriter(fullPath,append,encoding); streamWriter.Write(text); streamWriter.Close(); AssetDatabase.ImportAsset(pathName); return AssetDatabase.LoadAssetAtPath(pathName, typeof (UnityEngine.Object)); } }
右键新建自己的脚本“C# MyScript”就会按照本地模板的内容生成脚本
脚本生命周期 Unity - Manual: Order of execution for event functions (unity3d.com)
脚本绑定事件 使用Reset
方法,挂载脚本时或者在脚本上面右键点击reset,就会执行此事件,reset方法仅仅在Editor模式中生效
在Scripts文件夹中新建TestReset
脚本
1 2 3 4 5 6 7 8 9 10 11 using UnityEngine;public class TestReset : MonoBehaviour { #if UNITY_EDITOR private void Reset () { Debug.LogFormat("GameObject:{0}绑定TestReset脚本" , gameObject.name); } #endif }
脚本初始化和销毁 脚本挂载在游戏对象上,运行时立即执行Awake
方法,它是一个同步方法,下一帧执行Start
方法。
当挂载脚本的对象被删除或者脚本被删除,都会执行OnDestroy
方法
初始化和销毁在整个脚本生命周期中只会执行一次
脚本挂载后,如果代码中有涉及运行时的代码,其左侧就会出现勾选框,可以多次设置脚本的激活和禁用,系统分别回调OnEnable
和OnDisable
方法
在Scripts文件夹中新建TestLifeCycle
脚本
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 using UnityEngine;public class TestLifeCycle : MonoBehaviour { private void Awake () { Debug.Log("Awake用于初始化并且永远只会执行一次" ); } private void OnEnable () { Debug.Log("OnEnable在脚本每激活执行一次" ); } private void Start () { Debug.Log("Start在初始化下一帧执行,并且永远只会执行一次" ); } private void OnDisable () { Debug.Log("OnDisable在脚本每次反激活后,执行一次" ); } private void OnDestroy () { Debug.Log("OnDestroy用于脚本反初始化并且永远只会执行一次" ); } private void OnApplicationQuit () { Debug.Log("应用程序退出时执行一次" ); } }
脚本更新与协程任务
Update
每帧更新
LateUpdate
在Update
之后更新
FixedUpdate
固定更新,在Editor——Project Settings——Time选项中设置时间间隔
在实际开发中,以秒为单位的倒计时,可以在FixedUpdate
中实现
在下面的代码中,使用协程来每秒生成一个游戏对象
在Scripts文件夹中新建CreateCubePerSecond
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 using System.Collections;using System.Collections.Generic;using UnityEngine;public class CreateCubePerSecond : MonoBehaviour { void Start () { StartCoroutine(CreateCube()); } IEnumerator CreateCube () { for (int i = 0 ; i < 5 ; i++) { GameObject.CreatePrimitive(PrimitiveType.Cube).transform.position = Vector3.one * i; yield return new WaitForSeconds (1f ) ; } } }
停止协程任务 如果一个协程没有结束,如果需要重启协程,必须先关闭此协程,否则会出现错误,使用StopCoroutine
或者StopAllCoroutine
来关闭一个或所有协程
StartCoroutine
也可以返回协程对象
在Scripts文件夹中新建StopCoroutineOnGUI
脚本
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 31 32 33 34 35 using System.Collections;using System.Collections.Generic;using UnityEngine;public class StopCoroutineOnGUI : MonoBehaviour { IEnumerator CreateCube () { for (int i = 0 ; i < 10 ; i++) { GameObject.CreatePrimitive(PrimitiveType.Cube).transform.position = Vector3.one * i; yield return new WaitForSeconds (1f ) ; } } private Coroutine m_Cotoutine = null ; private void OnGUI () { if (GUILayout.Button("开始协程" )) { if (m_Cotoutine != null ) { StopCoroutine(m_Cotoutine); } m_Cotoutine = StartCoroutine(CreateCube()); } if (GUILayout.Button("结束协程" )) { if (m_Cotoutine != null ) { StopCoroutine(m_Cotoutine); } } } }
使用OnGUI显示FPS 在Scripts文件夹中新建DisplayFPS
脚本
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 31 32 33 34 35 36 37 38 39 40 using UnityEngine;public class DisplayFPS : MonoBehaviour { public float updateInterval = 0.5f ; private float accum = 0 ; private int frames = 0 ; private float timeleft; private string stringFps; private void Start () { timeleft = updateInterval; } private void Update () { timeleft -= Time.deltaTime; accum += Time.timeScale / Time.deltaTime; ++frames; if (timeleft <= 0.0 ) { float fps = accum / frames; string format = System.String.Format("{0:F2} FPS" , fps); stringFps = format; timeleft = updateInterval; accum = 0.0f ; frames = 0 ; } } private void OnGUI () { GUIStyle gUIStyle = GUIStyle.none; gUIStyle.fontSize = 30 ; gUIStyle.normal.textColor = Color.red; gUIStyle.alignment = TextAnchor.UpperLeft; Rect rt = new Rect(40 , 0 , 100 , 100 ); GUI.Label(rt, stringFps, gUIStyle); } }
通过以下代码强制帧数来降低功耗
1 Application.targetFrameRate = 30f ;
多脚本管理 脚本的执行顺序 可以在Project Settings——Script Execution Order里面设置脚本的初始化顺序(Awake的顺序)
如果A脚本执行后B脚本执行,需要在A脚本的Start方法获取B脚本数据,如果使用Awake方法的话会出错,因为B脚本可能还没有初始化
多脚本优化 避免挂太多的脚本,避免在脚本中写入void Start
、void Update
等需要引擎接收但是没有执行内容的空方法