C# {get;set;}最大的好处是什么?这样会略微unity降低drawcall运行效率吧。(Unity中的C#)

Unity协程(Coroutine)原理深入剖析 - 推酷
Unity协程(Coroutine)原理深入剖析
Unity协程(Coroutine)原理深入剖析
By D.S.Qiu
尊重他人的劳动,支持原创,转载请注明出处:http.
& & & & 记得去年6月份刚开始实习的时候,当时要我写网络层的结构,用到了协程,当时有点懵,完全不知道Unity协程的执行机制是怎么样的,只是知道函数的返回值是IEnumerator类型,函数中使用yield return ,就可以通过StartCoroutine调用了。后来也是一直稀里糊涂地用,上网google些基本都是例子,很少能帮助深入理解Unity协程的原理的。
& & & & 本文只是从Unity的角度去分析理解协程的内部运行原理,而不是从C#底层的语法实现来介绍(后续有需要再进行介绍),一共分为三部分:
& & & & & & & & &&
线程(Thread)和协程(Coroutine)
& & & & & & & & & & & &&
Unity中协程的执行原理
& & & & & & & &
IEnumerator & Coroutine
& & & & 之前写过一篇《
》主要是介绍TaskManager实现对协程的状态控制,没有Unity后台实现的协程的原理进行深究。虽然之前自己对协程还算有点了解了,但是对Unity如何执行协程的还是一片空白,在上看到两篇讲解Coroutine,如数家珍,当我看到Advanced Coroutine后面的Hijack类时,顿时觉得十分精巧,眼前一亮,遂动了写文分享之。
线程(Thread)和协程(Coroutine)
& & & & D.S.Qiu觉得使用协程的作用一共有两点:1)延时(等待)一段时间执行代码;2)等某个操作完成之后再执行后面的代码。总结起来就是一句话:控制代码在特定的时机执行。
& & & & 很多初学者,都会下意识地觉得协程是异步执行的,都会觉得协程是C# 线程的替代品,是Unity不使用线程的解决方案。
& & & & 所以首先,请你牢记:协程不是线程,也不是异步执行的。协程和 MonoBehaviour 的 Update函数一样也是在MainThread中执行的。使用协程你不用考虑同步和锁的问题。
Unity中协程的执行原理
& & & & 给出了协程的定义:
& & & & & & & &A coroutine is a function that is executed partially and, presuming suitable conditions are met, will be resumed at some point in the future until its work is done.
& & & & 即协程是一个分部执行,遇到条件(yield return 语句)会挂起,直到条件满足才会被唤醒继续执行后面的代码。
& & & & Unity在每一帧(Frame)都会去处理对象上的协程。Unity主要是在Update后去处理协程(检查协程的条件是否满足),但也有写特例:
& & & & 从上图的剖析就明白,协程跟Update()其实一样的,都是Unity每帧对会去处理的函数(如果有的话)。如果MonoBehaviour 是处于激活(active)状态的而且yield的条件满足,就会协程方法的后面代码。还可以发现:如果在一个对象的前期调用协程,协程会立即运行到第一个 yield return 语句处,如果是 yield return null ,就会在同一帧再次被唤醒。如果没有考虑这个细节就会出现一些奇怪的问题『1』。
图和结论都是从 上得来的,经过下面的验证发现与实际不符,D.S.Qiu用的是Unity 4.3.4f1 进行测试的。
经过测试验证,协程至少是每帧的LateUpdate()后去运行。
& & & & 下面使用 yield return new WaitForSeconds(1f); 在Start,Update 和 LateUpdate 中分别进行测试:
using UnityE
using System.C
public class TestCoroutine : MonoBehaviour {
private bool isStartCall =
//Makesure Update() and LateUpdate() Log only once
private bool isUpdateCall =
private bool isLateUpdateCall =
// Use this for initialization
void Start () {
if (!isStartCall)
Debug.Log(&Start Call Begin&);
StartCoroutine(StartCoutine());
Debug.Log(&Start Call End&);
isStartCall =
IEnumerator StartCoutine()
Debug.Log(&This is Start Coroutine Call Before&);
yield return new WaitForSeconds(1f);
Debug.Log(&This is Start Coroutine Call After&);
// Update is called once per frame
void Update () {
if (!isUpdateCall)
Debug.Log(&Update Call Begin&);
StartCoroutine(UpdateCoutine());
Debug.Log(&Update Call End&);
isUpdateCall =
IEnumerator UpdateCoutine()
Debug.Log(&This is Update Coroutine Call Before&);
yield return new WaitForSeconds(1f);
Debug.Log(&This is Update Coroutine Call After&);
void LateUpdate()
if (!isLateUpdateCall)
Debug.Log(&LateUpdate Call Begin&);
StartCoroutine(LateCoutine());
Debug.Log(&LateUpdate Call End&);
isLateUpdateCall =
IEnumerator LateCoutine()
Debug.Log(&This is Late Coroutine Call Before&);
yield return new WaitForSeconds(1f);
Debug.Log(&This is Late Coroutine Call After&);
&得到日志输入结果如下:
& & & & 然后将yield return new WaitForSeconds(1f);改为 发现日志输入结果和上面是一样的,没有出现上面说的情况:
using UnityE
using System.C
public class TestCoroutine : MonoBehaviour {
private bool isStartCall =
//Makesure Update() and LateUpdate() Log only once
private bool isUpdateCall =
private bool isLateUpdateCall =
// Use this for initialization
void Start () {
if (!isStartCall)
Debug.Log(&Start Call Begin&);
StartCoroutine(StartCoutine());
Debug.Log(&Start Call End&);
isStartCall =
IEnumerator StartCoutine()
Debug.Log(&This is Start Coroutine Call Before&);
Debug.Log(&This is Start Coroutine Call After&);
// Update is called once per frame
void Update () {
if (!isUpdateCall)
Debug.Log(&Update Call Begin&);
StartCoroutine(UpdateCoutine());
Debug.Log(&Update Call End&);
isUpdateCall =
IEnumerator UpdateCoutine()
Debug.Log(&This is Update Coroutine Call Before&);
Debug.Log(&This is Update Coroutine Call After&);
void LateUpdate()
if (!isLateUpdateCall)
Debug.Log(&LateUpdate Call Begin&);
StartCoroutine(LateCoutine());
Debug.Log(&LateUpdate Call End&);
isLateUpdateCall =
IEnumerator LateCoutine()
Debug.Log(&This is Late Coroutine Call Before&);
Debug.Log(&This is Late Coroutine Call After&);
& & & & 前面在介绍TaskManager工具时,说到MonoBehaviour 没有针对特定的协程提供Stop方法,其实不然,可以通过MonoBehaviour enabled = false 或者 gameObject.active = false 就可以停止协程的执行『2』。
& & & & 经过验证,『2』的结论也是错误的,正确的结论是,MonoBehaviour.enabled = false 协程会照常运行,但 gameObject.SetActive(false) 后协程却全部停止,即使在Inspector把 &gameObject 激活还是没有继续执行:
using UnityE
using System.C
public class TestCoroutine : MonoBehaviour {
private bool isStartCall =
//Makesure Update() and LateUpdate() Log only once
private bool isUpdateCall =
private bool isLateUpdateCall =
// Use this for initialization
void Start () {
if (!isStartCall)
Debug.Log(&Start Call Begin&);
StartCoroutine(StartCoutine());
Debug.Log(&Start Call End&);
isStartCall =
IEnumerator StartCoutine()
Debug.Log(&This is Start Coroutine Call Before&);
yield return new WaitForSeconds(1f);
Debug.Log(&This is Start Coroutine Call After&);
// Update is called once per frame
void Update () {
if (!isUpdateCall)
Debug.Log(&Update Call Begin&);
StartCoroutine(UpdateCoutine());
Debug.Log(&Update Call End&);
isUpdateCall =
this.enabled =
//this.gameObject.SetActive(false);
IEnumerator UpdateCoutine()
Debug.Log(&This is Update Coroutine Call Before&);
yield return new WaitForSeconds(1f);
Debug.Log(&This is Update Coroutine Call After&);
yield return new WaitForSeconds(1f);
Debug.Log(&This is Update Coroutine Call Second&);
void LateUpdate()
if (!isLateUpdateCall)
Debug.Log(&LateUpdate Call Begin&);
StartCoroutine(LateCoutine());
Debug.Log(&LateUpdate Call End&);
isLateUpdateCall =
IEnumerator LateCoutine()
Debug.Log(&This is Late Coroutine Call Before&);
Debug.Log(&This is Late Coroutine Call After&);
&先在Update中调用 this.enabled = 得到的结果:
然后把 this.enabled = 注释掉,换成 this.gameObject.SetActive(false); 得到的结果如下:
:通过设置MonoBehaviour脚本的enabled对协程是没有影响的,但如果 gameObject.SetActive(false) 则已经启动的协程则完全停止了,即使在Inspector把gameObject 激活还是没有继续执行。也就说协程虽然是在MonoBehvaviour启动的(StartCoroutine)但是协程函数的地位完全是跟MonoBehaviour是一个层次的,不受MonoBehaviour的状态影响,但跟MonoBehaviour脚本一样受gameObject 控制,也应该是和MonoBehaviour脚本一样每帧“轮询” yield 的条件是否满足。
yield 后面可以有的表达式:
& & & &a) null - the coroutine executes the next time that it is eligible
& & & &b) WaitForEndOfFrame - the coroutine executes on the frame, after all of the rendering and GUI is complete
& & & &c) WaitForFixedUpdate - causes this coroutine to execute at the next physics step, after all physics is calculated
& & & &d) WaitForSeconds - causes the coroutine not to execute for a given game time period
& & & &e) WWW - waits for a web request to complete (resumes as if WaitForSeconds or null)
& & & &f) Another coroutine - in which case the new coroutine will run to completion before the yielder is resumed
值得注意的是 WaitForSeconds()受Time.timeScale影响,当Time.timeScale = 0f 时,yield return new WaitForSecond(x) 将不会满足。
IEnumerator & Coroutine
& & & & 协程其实就是一个IEnumerator(迭代器),IEnumerator 接口有两个方法 Current 和 MoveNext() ,前面介绍的TaskManager就是利用者两个方法对协程进行了管理,这里在介绍一个协程的交叉调用类 Hijack(参见附件):
using System.Collections.G
using System.L
using UnityE
using System.C
[RequireComponent(typeof(GUIText))]
public class Hijack : MonoBehaviour {
//This will hold the counting up coroutine
IEnumerator _countUp;
//This will hold the counting down coroutine
IEnumerator _countD
//This is the coroutine we are currently
//hijacking
IEnumerator _
//A value that will be updated by the coroutine
//that is currently running
int value = 0;
void Start()
//Create our count up coroutine
_countUp = CountUp();
//Create our count down coroutine
_countDown = CountDown();
//Start our own coroutine for the hijack
StartCoroutine(DoHijack());
void Update()
//Show the current value on the screen
guiText.text = value.ToString();
void OnGUI()
//Switch between the different functions
if(GUILayout.Button(&Switch functions&))
if(_current == _countUp)
_current = _countD
_current = _countUp;
IEnumerator DoHijack()
while(true)
//Check if we have a current coroutine and MoveNext on it if we do
if(_current != null && _current.MoveNext())
//Return whatever the coroutine yielded, so we will yield the
//same thing
yield return _current.C
//Otherwise wait for the next frame
IEnumerator CountUp()
//We have a local increment so the routines
//get independently faster depending on how
//long they have been active
float increment = 0;
while(true)
//Exit if the Q button is pressed
if(Input.GetKey(KeyCode.Q))
increment+=Time.deltaT
value += Mathf.RoundToInt(increment);
IEnumerator CountDown()
float increment = 0f;
while(true)
if(Input.GetKey(KeyCode.Q))
increment+=Time.deltaT
value -= Mathf.RoundToInt(increment);
//This coroutine returns a yield instruction
yield return new WaitForSeconds(0.1f);
&上面的代码实现是两个协程交替调用,对有这种需求来说实在太精妙了。
& & & & 今天仔细看了下 有关Coroutine的两篇文章,虽然第一篇(参考①)现在验证的结果有很多错误,但对于理解协程还是不错的,尤其是当我发现Hijack这个脚本时,就迫不及待分享给大家。
& & & & 本来没觉得会有上的文章会有错误的,无意测试了发现还是有很大的出入,当然这也不是说原来作者没有经过验证就妄加揣测,D.S.Qiu觉得很有可能是Unity内部的实现机制改变了,这种东西完全可以改动,Unity虽然开发了很多年了,但是其实在实际开发中还是有很多坑,越发觉得Unity的无力,虽说容易上手,但是填坑的功夫也是必不可少的。& & &&
& & & & 看来很多结论还是要通过自己的验证才行,贸然复制粘贴很难出真知,切记!
& & & & 如果您对D.S.Qiu有任何建议或意见可以在文章后面评论,或者发邮件(gd.s.)交流,您的鼓励和支持是我前进的动力,希望能有更多更好的分享。
& & & & 转载请在文首注明出处:
更多精彩请关注D.S.Qiu的博客和微博(ID:静水逐风) & &
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致c# get{}set{}_百度知道
c# get{}set{}
是怎么执行的;
}结果是aaa
120有没有人知道这个结果是怎么出来的; /dsf&quot.Book),还有用get{}set{}的好处;/
book = value,网上那些看的晕晕的;&#47?通俗点讲;}
public static void Main()
cls obj = new cls()public class cls
private int book.WriteLine(obj.WriteLine(&注意这一句
C).Book = 120;);
&#47,具体步骤?.WriteLine(&定义一个域
public int Book
提问者采纳
WriteLine(&quot.WriteLine(&quot.book = value:cls obj = new cls()。property的好处是在设计层面上的;Cdsf&quot,知道java bean里面property的设计是如何完成的:public void set_Book(int value){
C那么就是按照先后顺序输出Console.set_Book(120).book.WriteLine(obj,不过如果你学过aaa&}public int get_Book(){
Console,你定义的property 有get set两个块,虽然这种概念让人困惑;).set_Book(120);).Book = 120,没什么好稀奇的;这下你能理解了么.get_Book()).WriteLine的内容;在编译以后实际上是直接调用的方法如;obj,实际上这两个块会编译成两个方法?你执行的几句话顺序是,你可以认为编译以后有如下定义:}你所看到的
this给你讲讲底层吧,那么你就能感觉到property在C#里面实现的是多么优雅
private string unitcode = &&;/// &summary&
/// 单位编号
/// &/summary&
public string UnitCode
set { unitcode = }
}我为啥感觉它有点此一举啊?直接把数据读到需要使用的地方(例如一个sql语句里)多方便,干嘛非用set
get一次,在传到sql语句里??
你觉得多此一举是因为你还没有一定的应用程序设计经验,没设计过中大型的应用程序,不知道隔离状态和实现的重要性,这个你以后见过的程序多了,解决的问题多了,自然会慢慢知道为什么这样设计了。C#3.0加入了自动属性,你可以写成public string UnitCode {}来实现一个属性而不是字段,C#编译器会帮你实现一个属性需要的代码。
提问者评价
来自团队:
其他类似问题
为您推荐:
其他4条回答
输出了aaaConsoleobj.Book).Book = 120;这句话调用 了Get方法输出dsf.WriteLine( 这句话调用了Set方法
book 是cls类中的一个自定义属性,可以在实例化cls类之后 设置或获得book属性的值 cls obj = new cls();
obj.Book = 120; //注意这一句
Console.WriteLine(obj.Book);
//取值在上面这一过程中分别进行了对 book 属性的赋值和取值过程 get{} set{} 是属性中的关键字
属性一般与全局变量类似(关于相同和不同自己找参考资料)以楼主例子说明:在cls实例化后 obj的有效使用范围内可以设置或者获取其中book属性的值public int book{ get{}}
//只能取值--只读public int book{ set{}}
//只能赋值,无法获取设置的值public int book{get{}set{}}
//--可读可写
首先我想说你的代码是不是写的有问题?其次建议你去看看对象的概念,了解一个对象创建的步骤,这个在马士兵java讲解中有很好的说过,其实你在new一个cls对象的时候就是调用了它默认的一个构造方法,这个构造方法会先将这两个构造器先调用,当然先调用set再去调用get 因此输出了aaa,再输出dsf,后面你给这个对象的属性赋值了是120,再去打印这个属性自然是120
首先定义了一个cls类的对象,默认调用它的构造函数,set方法是赋值,控制台输出aaa,并将value赋值给book,因为定义时没有赋值,后面将120赋值给book,用get方法获取book的值,首先输出dsf,再将return的值输出。get和set就是封装字段。可以自动生成。
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁c#get和set用法详解_百度知道
c#get和set用法详解
int&nbsp,可以通过制定get和set方法来限定字段的访问权限;&&&&}&&Age{&&&&&return&nbsp。例如,就分别对应的是get和set方法了?&nbsp,字段有两种操作权限;&&&nbsp,get和set是属性特有的两个方法;{&_age&字段public&&&&&&&else&&&&0&&&&&&&&nbsp。总的来说;get&&&_age,是为了程序数据的安全性考虑的。属性是对字段的封装;_&150)&&||&&&&_age=&//年龄&&&nbsp,就是获取和修改;&}&_age=set&&nbsp:private&&&nbsp首先;if(_age&&{&&nbsp
其他类似问题
为您推荐:
其他3条回答
属性的访问器包含与获取(读取或计算)或设置(写)属性有关的可执行语句。访问器声明可以包含 get 访问器或 set 访问器,或者两者均包含。声明采用下列形式之一:
get 访问器
get 访问器体与方法体相似。它必须返回属性类型的值。执行 get 访问器相当于读取字段的值。以下是返回私有字段 name 的值的 get 访问器:
复制代码 代码如下:
// the name field
public string Name // the Name property
当引用属性时,除非该属性为赋值目标,否则将调用 get 访问器读取该属性的值。例如:
Employee e1 = new Employee();
Console.Write(e1.Name); // The get accessor is invoked here
get 访问器必须在 return 或...
get就是外部类可以访问此类里面的私有字段值set就是外部类可以修改此类里面的私有字段值他们都是一个中介这样理解就好了
set就是公共属性设置值,get就是获取公共属性的值
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁博客访问: 417290
博文数量: 335
注册时间:
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: 微软技术
C#开发Unity游戏教程之Unity中方法的参数
Unity的方法的参数
出现在脚本中的方法,无论是在定义的时候,还是使用的时候,后面都跟着一对括号“( )”,有意义吗?看起来最多也就是起个快速识别方法的作用吧。既然C#的语法规定方法就应该这么写,肯定是有一定道理的。如果是上升到战略意义的道理,连作者也不是很明白,但是作者知道这对括号里可以添加“参数”。
Unity中参数的作用
要说明参数的作用,就必须从方法说起。方法可以处理变量中的数据,进而影响游戏对象的行为逻辑,这是本章前面一直在强调的。但是就前面脚本中一直在使用的,方法的括号里没有参数的情况而言,方法总是在处理特定变量里的数据。例如,下面是前面一个示例中定义的方法:
int AddTwoNumber()& & & &&
& & & & &return number1 + number2;
这个方法的括号里没有参数,它处理的数据总是变量number1和number2里的数据。既然这只是一个用于求和的方法,没有必要指定它只计算变量number1和number2里的数据和。如果有其它变量的数据也要求和的话,就只能再定义一个方法了。为方法引入参数就可以很好的解决这类的问题。
Unity中的方法对于游戏的实际意义
开发者定义了一个方法,可以控制一个游戏对象的逻辑行为。如果其它对象也需要做出与之类似的行为,就没有必要再定义一个方法了。例如下面的游戏示例所做的那样。
(1)为游戏场景添加Cube、Cylinder和Directional light,这3个游戏对象。前两者将作为脚本方法控制行为逻辑的游戏对象,最后一个游戏对象负责游戏场景的照明。合理设置它们各自的位置,得到的Scene和Game视图,如图4-8所示。
图4-8&&在Scene和Game视图里,查看各对象的相对位置,以及游戏视图的效果
(2)在Project视图里,新建脚本文件,命名为MyScript,打开此脚本并添加下面的代码:
01&&&&&using UnityE
02&&&&&using System.C
04&&&&&public class MyScript : MonoBehaviour
06&&&&&&&&&&&&&&public GameObject myC
07&&&&&&&&&&&&&&public GameObject myC
08&&&&&&&&&&&&&&void OnGUI()
09&&&&&&&&&&&&&&{
10&&&& &&&&&&&&&&&&&&&&&& //当鼠标左键按下的时候
11&&&&&&&&&&&&&&&&&&&&&&&if(Input.GetMouseButton(0))
12&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&RotateObject (myCube);
13&&&& &&&&&&&&&&&&&&&&&& //当鼠标右键按下的时候
14&&&&&&&&&&&&&&&&&&&&&&&if(Input.GetMouseButton(1))
15&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&RotateObject (myCylinder);
16&&&&&&&&&&&&&&}
17&&&& &&&&&&&& //改变游戏对象朝向的方法
18&&&&&&&&&&&&&&void RotateObject(GameObject myObject)
19&&&&&&&&&&&&&&{
20&&&&&&&&&&&&&&&&&&&&&&&myObject.transform.Rotate(Vector3.right*100,Space.World);
21&&&&&&&&&&&&&&}
q脚本代码的18~21行,是有参数方法的定义部分。从代码来看,传入的参数是一个游戏对象,而方法中的语句,就是通过修改游戏对象Transform组件下的Rotation属性,实现改变游戏对象朝向的目地的;
q脚本代码的12、15行,调用了两次方法RotateObject(),先后传入的参数也是不一样的;
(3)将脚本MyScript赋予Main Camera对象,并在后者的Inspector视图里,设置My Script组件里的My Cube和My Cylinder属性为游戏场景中的Cube和Cylinder对象,属性的设置方法是,直接拖动Hierarchy视图里的Cube和Cylinder对象到对应的属性框中即可,如图4-9所示。
图4-9&&设置My Script组件下的属性
(4)运行游戏,在Game视图里,一直按下鼠标左键,读者会看到立方体对象在原地滚动。如果一直按下鼠标右键,那么原地滚动的就成了圆柱体。如果同时按住鼠标左键和右键,立方体和圆柱体就会同时滚动了,如图4-10所示。
图4-10&&游戏效果展示图
(5)游戏中,控制游戏对象滚动的方法是RotateObject(),它是一个需要传入参数的方法。正是因为需要传入参数,所以它才能被用于游戏场景中,所有游戏对象滚动的行为逻辑控制中。而不只是单独的控制一个对象的行为逻辑。
Unity中减少代码的重复书写
脚本中的方法,对于游戏对象而言,意义在于它可以控制游戏对象的行为逻辑,这是方法对于游戏效果的意义。方法对于脚本代码的书写,同样具有意义,那就是它可以帮助减少代码的重复书写。
本章在介绍方法时,曾将方法与变量做了简单比较,对比的过程中说过,方法也可以看做是一个存储单元,只不过里面存储的不是数据,而是一条或者多条语句。就这个意义而言,可以认为如果在方法中放入多条会在脚本中被重复使用的语句,并使用方法名作为这些语句的指代。那么,脚本中所有需要书写这多行代码的地方,都可以使用方法名(即方法的调用)来代替。例如,脚本中有下面的代码:
01&&&&&using UnityE
02&&&&&using System.C
04&&&&&public class MyScript : MonoBehaviour
06&&&&&&&&&&&&&&private int number1;
07&&&&&&&&&&&&&&private int number2;
08&&&&&&&&&&&&&&private int number3;
09&&&&&&&&&&&&&&
10&&&&&&&&&&&&&&void Start()
11&&&&&&&&&&&&&&{
12&&&&&&&&&&&&&&&&&&&&&&&number1 = 3;
13&&&&&&&&&&&&&&&&&&&&&&&number2 = 5;
14&&&&&&&&&&&&&&&&&&&&&&&number3 = 4;
15&&&&&&&&&&&&&&&&&&&&&&&number1 *= number1;
16&&&&&&&&&&&&&&&&&&&&&&&number2 *= number2;
17&&&&&&&&&&&&&&&&&&&&&&&number = number1 + number2;
18&&&&&&&&&&&&&&&&&&&&&&&Debug.Log("number1*number1 + number2*number2 = " + number);
20&&&&&&&&&&&&&&&&&&&&&&&number1 = 3;
21&&&&&&&&&&&&&&&&&&&&&&&number2 = 5;
22&&&&&&&&&&&&&&&&&&&&&&&number3 = 4;
23&&&&&&&&&&&&&&&&&&&&&&&number1 *= number1;
24&&&&&&&&&&&&&&&&&&&&&&&number3 *= number3;
25&&&&&&&&&&&&&&&&&&&&&&&number = number1 + number3;
26&&&&&&&&&&&&&&&&&&&&&&&Debug.Log("number1*number1 + number3*number3 = " + number);
27&&&&&&&&&&&&&&}
脚本中有3个变量:number1、number2和number3。为了计算number1与number2的平方和,代码中使用了12~18行,一共7行的代码。同样的,为了计算number1与number3的平方和,代码中使用了20~26行,一共7行的代码。如果在脚本中定义方法的话,就不用书写这么多类似的代码了,使用定义了方法的脚本以后,脚本中的代码如下:
01&&&&&using UnityE
02&&&&&using System.C
04&&&&&public class MyScript : MonoBehaviour
06&&&&&&&&&&&&&&private int number1;
07&&&&&&&&&&&&&&private int number2;
08&&&&&&&&&&&&&&private int number3;
09&&&&&&&&&&&&&&
10&&&&&&&&&&&&&&void Start()
11&&&&&&&&&&&&&&{
12&&&&&&&&&&&&&&&&&&&&&&&number1 = 3;
13&&&&&&&&&&&&&&&&&&&&&&&number2 = 5;
14&&&&&&&&&&&&&&&&&&&&&&&number3 = 4;
15&&&&&&&&&&&&&&&&&&&&&&&Debug.Log("number1*number1 + number2*number2 = " + AddTwoNumber(number1,number2));
16&&&&&&&&&&&&&&&&&&&&&&&Debug.Log("number1*number1 + number3*number3 = " + AddTwoNumber(number1,number3));
17&&&&&&&&&&&&&&}
18&&&&&&&&&&&&&&int AddTwoNumber(int i1,int i2)
19&&&&&&&&&&&&&&{
20&&&&&&&&&&&&&&&&&&&&&&&i1 *= i1;
21&&&&&&&&&&&&&&&&&&&&&&&i2 *= i2;
22&&&&&&&&&&&&&&&&&&&&&&&return i1 + i2;
23&&&&&&&&&&&&&&}
相比于之前使用的14行代码,这里只使用了5行代码!但是两个脚本实现的效果是一样的,如图4-11所示。
图4-11&&Console视图输出了两个数平方和的计算结果
Unity游戏示例
在Unity中编写脚本,读者可以自己编写方法,也可以使用Unity编写的方法。一般情况下,脚本代码中会用到很多Unity编写的方法。而自己编写方法,通常是为了减少代码的重复书写,或者将Unity的方法组织起来,实现新的功能。对于Unity编写的方法,不会使用的时候,读者一定要记得去帮助文档里查阅,查阅的步骤本章前面的部分已经说明了。
本节游戏示例编写的脚本中,完全使用Unity编写的方法,实现玩家通过键盘控制游戏对象上下左右移动的效果,具体的游戏制作过程如下:
(1)为游戏场景添加两个游戏对象:Sphere和Directional light。前者就是本游戏要控制的游戏对象,后者负责游戏视图的光照效果。调节游戏视图中各对象的位置,使得Sphere对象位于Game视图的中央,如图4-12所示。
图4-12&&在Scene和Game视图里,查看各游戏对象的相对位置,以及游戏视图的显示效果
(2)为游戏项目导入4个图片,它们将被用于在游戏视图里表示小球的运动方向,本游戏导入的4个图片如图4-13所示。
图4-13&&为游戏项目导入的4个表示方向的图片
提示:为游戏项目导入图片的方法是:直接拖动。即将图片直接拖动到Unity的Project视图里即可。
(3)在Project视图里,新建一个脚本,并命名为MyScript,打开此脚本,并添加下面的代码:
01&&&&&using UnityE
02&&&&&using System.C
04&&&&&public class MyScript : MonoBehaviour
06&&&&&&&&&&&&&&public Texture texUp,texDown,texLeft,texR
07&&&&&&&&&&&&&&public GameObject myS
08&&&&&&&&&&&&& public float sphereSpeed = 0.1f;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& //决定小球的移动速度
09&&&&&&&&&&&&&&private float horizontalNum,VerticalN
11&&&&&&&&&&&&&&void Update()
12&&&&&&&&&&&&&&{
13&&&& &&&&&&&&&&&&&&&&&& //检测是否按下了水平方向上的方向键
14&&&&&&&&&&&&&&&&&&&&&&&horizontalNum = Input.GetAxis ("Horizontal");
15&&&&&&&&&&&&&&&&&&&&&&&mySphere.transform.Translate (horizontalNum*Vector3.right*sphereSpeed);
16&&&& &&&&&&&&&&&&&&&&&& //检测是否按下了垂直方向上的方向键
17&&&&&&&&&&&&&&&&&&&&&&&VerticalNum = Input.GetAxis ("Vertical");
18&&&&&&&&&&&&&&&&&&&&&&&mySphere.transform.Translate (VerticalNum*Vector3.up*sphereSpeed);
19&&&&&&&&&&&&&&}
20&&&&&&&&&&&&&&void OnGUI()
21&&&&&&&&&&&&&&{
22&&&&&&&&&&&&&&&&&&&&&&&GUILayout.BeginHorizontal ();
23&&&& &&&&&&&&&&&&&&&&&& //绘制左箭头,或者右箭头
24&&&&&&&&&&&&&&&&&&&&&&&if(horizontalNum > 0)
25&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&GUILayout.Label (texRight,GUILayout.Width(100),GUILayout.Height (100));
26&&&&&&&&&&&&&&&&&&&&&&&else if(horizontalNum < 0)
27&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&GUILayout.Label (texLeft,GUILayout.Width(100),GUILayout.Height (100));
28&&&& &&&&&&&&&&&&&&&&&& //绘制上箭头,或者下箭头
29&&&&&&&&&&&&&&&&&&&&&&&if(VerticalNum > 0)
30&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&GUILayout.Label (texUp,GUILayout.Width(100),GUILayout.Height (100));
31&&&&&&&&&&&&&&&&&&&&&&&else if(VerticalNum < 0)
32&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&GUILayout.Label (texDown,GUILayout.Width(100),GUILayout.Height (100));
33&&&&&&&&&&&&&&&&&&&&&&&GUILayout.EndHorizontal ();
34&&&&&&&&&&&&&&}
将此脚本赋予Main Camera对象,并在后者的Inspector视图里设置My Script组件下的属性,如图4-14所示。
qTex Up:up;
qTex Down:down;
qTex Left:left;
qTex Right:right;
qMy Sphere:Sphere;
qSphere Speed:0.1;
图4-14&&设置组件下各属性
(4)脚本中使用的Unity方法,这里会做简要说明,如果读者对各方向的详细使用说明感兴趣,可以自己查阅帮助文档。
q14、17行使用了Input.GetAxis ()方法,用于检测玩家是否按下了键盘上的左,或者右方向键;为方法传入的参数是Horizontal和Vertical,它们是Unity默认定义的输入按键,读者可以在Unity里单击Edit|project Settings|Input命令,然后在弹出的InputManager对话框中看到它们,如图4-15所示。
图4-15&&InputManager对话框
q15、18行使用了游戏对象的transform.Translate ()方法,用于修改球体对象Transform组件下的Position属性,所以游戏场景中球体对象的位置就发生了改变;
q20行的OnGUI()方法,用于在游戏视图上绘制各种图形,或者控件,这些图形和控件并不存在于游戏场景中。如果非要说出位置的话,作者觉得说它们存在于屏幕上更合适些;
q22、33行的方法GUILayout.BeginHorizontal ()和GUILayout.EndHorizontal ()会迫使图形和控件,在水平方向上,一个挨着一个的绘制;
q25、27、30和32行使用了GUILayout.Label ()方法,作用是在屏幕上绘制标签(Label)控件;
(5)运行游戏,小球处于视图的中央,按下键盘上的方向键,小球就会朝着指定的方向移动,并且游戏视图的左上角还使用图片显示着小球的移动方向,并且图片的显示与否,与玩家是否按下了方向键有关。如果玩家都没有按下方向键,那么就不会有方向图片显示,游戏的运行效果如图4-16所示。
图4-16&&处于静止状态和运动状态的小球,以及显示在游戏视图左上角的方向图片
方法,从外在形式上来看,就是由多条语句组成的。考虑到读者对方法的理解,本章将它与变量做了对比,因为它们确实有不少相似的地方。方法对于游戏的真正意义在于,方法会将属性里的数据做特定的处理,并将处理的结果应用到游戏对象上。但是它也有其它用途,例如减少代码的重复书写。在学会了方法的编写以后,又带读者认识了Unity自定义的方法,以及需要传入参数的方法。最后,本章同样以一个游戏示例收尾。因为示例中使用了不少Unity自定义的方法,读者看来不得不去查阅下帮助文档才行,至于查阅的步骤,本章也介绍了。
本文选自:C#游戏开发快速入门大学霸内部资料,转载请注明出处,尊重技术尊重IT人!
阅读(415) | 评论(0) | 转发(0) |
相关热门文章
给主人留下些什么吧!~~
请登录后评论。

我要回帖

更多关于 unity shadowmap效率 的文章

 

随机推荐