Unity使用的C#和标准误差怎么算的C#有区别吗

9832人阅读
Unity3D(159)
C#中有一种还是挺实用的类型,就是委托类型,这也是初学者比较难理解的类型,我们常常将委托和事件合起来讲,其实他们两是有些区别的,委托(delegate)是一种类型,而事件(Event)是一种实例(委托中的一种)。下面我就从参考网上的一个例子,讲解一下unity中C#委托的应用。我们新建一个脚本,取名TestDelegate。这个主要的功能就是点击中文按钮输出中文名字,点击英文按钮输出英文名字。这里有两个函数分别实现了这两个功能。如果不用委托,可以用if else,switch来实现,相比新手用的是比较多的,但大牛们的代码一般比较少出现,所以这就告示我们要对我们的代码进行仔细的研究,下面就看一下代码:using UnityE
using System.C
public class TestDelegate : MonoBehaviour
//定义一个委托(格式是不是很类很像),用来指向我们某个函数。(c++里面的指针函数)
//param参数是名字
private delegate void DebugString(string param);
/// &summary&
/// 输出中文名字
/// &/summary&
public void DebugNameOfChina(string str)
Debug.Log(&中文名字:& + str);
/// &summary&
/// 输出英文名字
/// &/summary&
public void DebugNameOfEnglish(string str)
Debug.Log(&English Name:& + str);
//定义一个委托的变量事件
private DebugString handlerDebugS
void OnGUI()
if (GUILayout.Button(&输出中文名字&))
//我想输出中文名字的话就给handlerDebugString
赋值成输出中文名字的这个函数DebugNameOfChina
handlerDebugString = DebugNameOfC
handlerDebugString(&丁小未&);
else if (GUILayout.Button(&Debug English Name&))
//我想输出英文名字的话就给handlerDebugString
赋值成输出中文名字的这个函数DebugNameOfEnglish
handlerDebugString = DebugNameOfE
handlerDebugString(&DingXiaowei&);
}这里参考了一些网上的教程,@与现实冲突的教程写的比较不错,但我也加入一点自己的理解,我个人理解这个委托有点类似就是指针函数的意味,定义一个委托变量,也就类似C/C++中的函数指针,指向哪个函数就调用执行该函数的功能。然后我们运行一下,看一下效果:了解过C/C++的同学一类比函数指针也就比较容易理解了,其实也就那么回事,想要用哪个函数就指向这个函数,然后去调用这个函数的方法,其实委托还有+=和-=也就是多路广播机制,下面举例:using UnityE
using System.C
public class TestDelegate : MonoBehaviour
//定义一个委托(格式是不是很类很像),用来指向我们某个函数。(c++里面的指针函数)
//param参数是名字
private delegate void DebugString(string param);
/// &summary&
/// 输出中文名字
/// &/summary&
public void DebugNameOfChina(string str)
Debug.Log(&中文名字:& + str);
/// &summary&
/// 输出英文名字
/// &/summary&
public void DebugNameOfEnglish(string str)
Debug.Log(&English Name:& + str);
//定义一个委托的变量事件
private DebugString handlerDebugS
void Start()
//给委托对象赋值
handlerDebugString = DebugNameOfC
handlerDebugString(&丁小未&);
Debug.Log(&................................&);
//给委托对象再加一个事件,叫多路广播
handlerDebugString += DebugNameOfE
handlerDebugString(&DingXiaowei&);
Debug.Log(&................................&);
//输出中文试试
handlerDebugString(&丁小未&);
Debug.Log(&................................&);
//给委托对象减去一个事件
handlerDebugString -= DebugNameOfC
handlerDebugString(&DingXiaowei&);
}大家猜猜结果是啥,跟你预期的是不是一样的呢?可能觉得容易出错的就是第二条和第三条,也就是红色部分,他是分别执行了这两个事件,我们后来加上了一个事件嘛,所以就都执行一遍,然后参数就是委托方法带的参数,委托这是C#的基础知识,跟unity没关系,unity知识一个UI展示的界面而已,后带支持的还是C#语言,所以在学习unity开发的时候,C#/.NET基础学好还是很有必要的!==================== 迂者 丁小未 CSDN博客专栏=================MyBlog:& & & & & & &MyQQ:Unity QQ群:& & & & cocos2dx QQ群:======================&相互学习,共同进步&===================&转载请注明出处:欢迎关注我的微博:
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1728281次
积分:22111
积分:22111
排名:第375名
原创:416篇
转载:215篇
评论:426条
我叫小未,网名Aladdin,主做U3D游戏程序,不甘于平庸,喜欢尝试,喜欢研究计算机技术,13年9月份开始工作,曾两年就职于巨人网络做游戏客户端程序,闲暇时间做过麦子学院在线讲师,做过独立开发,做过C#游戏服务器和.NET Web服务器,做过IOS原生开发,爱游戏,爱coding!
学习交流群:
阅读:10560
文章:10篇
阅读:22615
文章:12篇
阅读:18316
文章:24篇
阅读:155959
文章:13篇
阅读:27673
文章:31篇
阅读:93666
文章:34篇
阅读:66374
文章:20篇
阅读:51388
(6)(1)(1)(2)(3)(1)(1)(2)(9)(2)(1)(3)(3)(1)(2)(1)(1)(3)(2)(2)(1)(1)(1)(3)(1)(1)(3)(1)(4)(2)(6)(17)(12)(13)(17)(12)(24)(32)(58)(42)(12)(48)(126)(33)(2)(2)(13)(9)(15)(5)(12)(14)(17)(5)(1)(1)(17)(2)(2)相关文章推荐
这个是我刚刚整理出的Unity面试题,为了帮助大家面试,同时帮助大家更好地复习Unity知识点,如果大家发现有什么错误,(包括错别字和知识点),或者发现哪里描述的不清晰,请在下面留言,我会重新更新,希...
coroutines在unity里用处多多,用的好可以大量简化代码。
让loading场景绘制进度条不阻塞,这里异步加载关卡的函数还是必须的。
简化编写不同阶段不同行为的GameObje...
unity常见面试题
1. 游戏对象
问题:游戏对象消失三种方法的区别?(enabled/Destroy/active)
gameObject.renderer.enabled=fasl...
1、添加、获取、删除组件的函数?
答:AddComponent、GetComponent、Desdroy。
2、unity有几种光,分别是哪些?
答:有4种,分别是平行光(Directional Li...
Unity3D常见面试题
原文链接:/p/39c383f45d4e
Unity技术面试题
一:什么是协同程序?
答:在主线程运行时同时开启另一段逻辑处理,来协助...
题目中有些答案有网上搜的,也有个人整理的,有错误或不足的地方,望指正,谢谢! 文章最后会有参考的一些网址或资料的链接,有想看原址的朋友可以直接跳到最后哦。 题目归类区(看题目字体颜色识第几部分)ps:...
本来想写一个系列的,一半是抨击现在面试之水,要人之奸,用大哥的话说,要走新手是做螺丝钉和抹布用的。另一半是对出出学校的或者是自废武功转3d的朋友们提供一个比较有价值的参考。不过我时间实在仓促。没有保证...
第一部分1.请简述值类型与引用类型的区别2.C# 中所有引用类型的基类是什么3.请简述 ArrayList 和 List 的主要区别4.请简述 GC (垃圾回收)产生的原因,并描述如何避免?5.请描述...
高频问答的问题:
一.什么是渲染管道?
是指在显示器上为了显示出图像而经过的一系列必要操作。
渲染管道中的很多步骤,都要将几何物体从一个坐标系中变换到另一个坐标系中去。
主要步骤有:
本地坐标->视图...
他的最新文章
他的热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)本文翻译自:
很多游戏时常崩溃,大多数情况下都是内存泄露导致的。这系列文章详细讲解了内存泄露的原因,如何找到泄露,又如何规避。
我要在开始这个帖子之前忏悔一下。虽然一直作为一个C&/&C++开发者,但是很长一段时间我都是微软的C#语言和.NET框架的秘密粉丝。大约三年前,当我决定离开狂野的基于C&/&C++的图形库,进入现代游戏引擎的文明世界,Unity&带着一个让我毫不犹豫选择它的特性脱颖而出。Unity&并不需要你用一种语言(如Lua或UnrealScript)&写脚本&却用另外一种语言'编程'。相反,它对Mono有深度的支持,这意味着所有的编程可以使用任何.NET语言。哦,真开心!我终于有一个正当的理由和C&++说再见,而且通过自动内存管理我所有的问题都得到了解决。此功能已经内置在C#语言,是其哲学的一个组成部分。没有更多的内存泄漏,没有更多的考虑内存管理!我的生活会变得容易得多。
如果你有哪怕是最基本的使用Unity或游戏编程的经验,你就知道我是多么的错误了。我费劲艰辛才了解到在游戏开发中,你不能依赖于自动内存管理。如果你的游戏或中间件足够复杂并且对资源要求很高,用C#做Unity开发就有点像往C&++方向倒退了。每一个新的Unity开发者很快学会了内存管理是很麻烦的,不能简单地托付给公共语言运行库(CLR)。Unity论坛和许多Unity相关的博客包含一些内存方面的技巧集合和最佳实不规范践。不幸的是,并非所有这些都是基于坚实的事实,尽我所知,没有一个是全面的。此外,在这样的网站上的C#专家似乎经常对Unity开发者面对的古怪的、非标准的问题没有一点耐心。由于这些原因,在这一篇和下面的两篇帖子,我试着给出关于Unity特有的C#的内存管理问题的概述,并希望能介绍一些深入的知识。
第一篇文章讨论了在.NET和Mono的垃圾收集世界中的内存管理基础知识。我也讨论了内存泄漏的一些常见的来源。第二篇着眼于发现内存泄漏的工具。Unity的Profiler是一个强大的工具,但它也是昂贵的(似乎在中国不是)。因此,我将讨论.NET反汇编和公共中间语言(CIL),以显示你如何只用免费的工具发现内存泄漏。第三篇讨论C#对象池。再次申明,重点只针对出现在Unity/&C#开发中的具体需要。
垃圾收集的限制大多数现代操作系统划分动态内存为栈和堆(,&),许多CPU架构(包括你的PC&/&Mac和智能手机/平板电脑)在他们的指令集支持这个区分。&C#通过区分值类型支持它(简单的内置类型以及被声明为枚举或结构的用户自定义类型)和引用类型(类,接口和委托)。值类型在堆中,引用类型分配在栈上。堆具有固定大小,在一个新的线程开始时被设定。它通常很小&-&例如,NET线程在Windows默认为一个1MB的堆栈大小。这段内存是用来加载线程的主函数和局部变量,并且随后加载和卸载被主函数调用的函数(与他们的本地变量)。一些内存可能会被映射到CPU的缓存,以加快速度。只要调用深度不过高或局部变量不过大,你不必担心堆栈溢出。这种栈的用法很好地契合结构化编程的概念()。
如果对象太大不适合放在栈上,或者如果他们要比创造了他们的函数活得长,堆这个时候就该出场了。堆是&其他的一切&-&是一段可以随着每个OS请求增长的内存,and&over&which&the&program&rules&as&it&wishes(这句不会&&)。不过,虽然栈几乎是不能管理(只使用一个指针记住free&section开始的地方),堆碎片很快会从分配对象的顺序到你释放的顺序打乱。把堆想成瑞士奶酪,你必须记住所有的孔!根本没有乐趣可言。进入自动内存管理。自动分配的任务&-&主要是为你跟踪奶酪上所有的孔&-&是容易的,而且几乎被所有的现代编程语言支持。更难的是自动释放,尤其是决定释放的时机,这样你就不必去管了。
后者任务被称为垃圾收集(GC)。不是你告诉你的运行时环境什么时候可以释放对象的内存,是运行时跟踪所有的对象引用,从而能够确定&&在特定的时间间隔里,一个对象不可能被你的代码引用到了。这样一个对象就可以被销毁,它的内存会被释放。GC仍被学者积极地研究着,这也解释了为什么GC的架构自.net框架1.0版以来改变如此之多。然而,Unity不使用.net而是其开源的表亲,Mono,而它一直落后于它的商业化对手(.net)。此外,Unity不默认使用Mono的最新版本(2.11/3.0),而是使用版本2.6(准确地说,2.6.5,在我的Windows4.2.2安装版上(编辑:这同样适用于Unity4.3])。如果你不确定如何自己验证这一点,我将在接下来的帖子里讨论。
在Mono2.6版本之后引入了有关GC的重大修改。新版本使用分代垃圾收集(),而2.6仍采用不太复杂的贝姆垃圾收集器()。现代分代GC执行得非常好,甚至可以在实时应用中使用(在一定限度内),如游戏。另一方面,勃姆式GC的工作原理是在堆上做穷举搜索垃圾。以一种相对&罕见&的时间间隔(即,通常的频率大大低于一次每帧)。因此,它极有可能以一定的时间间隔造成帧率下降,因而干扰玩家。Unity的文档建议您调用System.GC.Collect(),只要您的游戏进入帧率不那么重要的阶段(例如,加载一个新的场景,或显示菜单)。然而,对于许多类型的游戏,出现这样的机会也极少,这意味着,在GC可能会在你不想要它的时候闯进来。如果是这样的话,你唯一的选择是自己硬着头皮管理内存。而这正是在这个帖子的其余部分,也是以下两个帖子的内容!
自己做内存管理者
让我们申明在Unity/.NET的世界里&自己管理内存&意味着什么。你来影响内存是如何分配的的力量是(幸运的)非常有限的。你可以选择自定义的数据结构是类(总是在堆上分配的)或结构(在栈中分配,除非它们被包含在一个类中),并且仅此而已。如果你想要更多的神通,必须使用C#的不安全关键字。但是,不安全的代码只是无法验证的代码,这意味着它不会在Unity&Web&Player中运行,还可能包括一些其他平台。由于这个问题和其他原因,不要使用不安全的关键字。因为堆栈的上述限制,还因为C#数组是只是System.Array(这是一个类)的语法糖,你不能也不应该回避自动堆分配。你应该避免的是不必要的堆分配,我们会在这个帖子下一个(也是最后一个)部分讲到这个。
当谈到释放的时候你的力量是一样的有限。其实,可以释放堆对象的唯一过程是GC,而它的工作原理是不可见的。你可以影响的是对任何一个对象的最后一个引用在堆中超出范围的时机,因为在此之前,GC都不能碰他们。这种限制有巨大的实际意义,因为周期性的垃圾收集(你无法抑制)往往在没有什么释放的时候是非常快的。这一事实为构建对象池的各种方法提供了基础,我在第三篇帖子讨论。
不必要的堆分配的常见原因
你应该避免foreach循环吗?
在Unity&论坛和其他一些地方我经常碰到的常见建议是避免foreach循环,并用for或者while代替。乍一看理由似乎很充分。Foreach真的只是语法糖,因为编译器会这样把代码做预处理:
foreach (SomeType s in someList)
s.DoSomething();
...into something like the the following:
using (SomeType.Enumerator enumerator = this.someList.GetEnumerator()){
while (enumerator.MoveNext())
SomeType s = (SomeType)enumerator.C
s.DoSomething();
换句话说,每次使用foreach都会在后台创建一个enumerator对象-一个System.Collections.IEnumerator接口的实例。但是是创建在堆上的还是在堆栈上的?这是一个好问题,因为两种都有可能!最重要的是,在System.Collections.Generic&命名空间里几乎所有的集合类型(List&T&,&Dictionary&K,&V&,&LinkedList&T&,&等等)都会根据GetEnumerator()的实现聪明地返回一个struct。这包括伴随着Mono2.6.5的所有集合版本。(Unity所使用)
Matthew&Hanlon指出微软现在的C#编译器和Unity正在使用编译你的脚本的老的Mono/c#编译器之间一个不幸的差异。你也许知道你可以使用Microsoft&Visual&Studio来开发甚至编译&Unity/Mono&兼容的代码。你只需要将相应的程序集放到&Assets&目录下。所有代码就会在Unity/Mono运行时环境中执行。但是,执行结果还是会根据谁编译了代码不一样。Foreach循环就是这样一个例子,这是我才发现的。尽管两个编译器都会识别一个集合的GetEnumerator()返回struct还是class,但是Mono/C#有一个会把struct-enumerator装箱从而创建一个引用类型的BUG。
所以你觉得你该避免使用foreach循环吗?
不要在Unity替你编译的时候使用
在用最新的编译器的时候可以使用用来遍历standard&generic&collections&(List&T&&etc.)Visual&Studio或者免费的&.NET&Framework&SDK&都可以,而且我猜测最新版的Mono&和&也可以。
当你在用外部编译器的时候用foreach循环来遍历其他类型的集合会怎么样?很不幸,没有统一的答案。用在第二篇帖子里提到的技术自己去发现哪些集合是可以安全使用foreach的。
你应该避免闭包和LINQ吗?
你可能知道C#提供匿名函数和lambda表达式(这两个几乎差不多但是不太一样)。你能分别用delegate&关键字和=&操作符创建他们。他们通常都是很有用的工具,并且你在使用特定的库函数的时候很难避免(例如List&T&.Sort())&或者。
匿名方法和lambda会造成内存泄露吗?答案是:看情况。C#编译器实际上有两种完全不一样的方法来处理他们。来看下面小段代码来理解他们的差异:
1 int result = 0;
2 void Update(){
3 for (int i = 0; i & 100; i++)
System.Func&int, int& myFunc = (p) =& p *
result += myFunc(i);
正如你所看到的,这段代码似乎每帧创建了myFunc委托&100次,每次都会用它执行一个计算。但是Mono仅仅在Update()函数第一次调用的时候分配内存(我的系统上是52字节),并且在后续的帧里不会再做任何堆的分配。怎么回事?使用代码反射器(我会在下一篇帖子里解释)就会发现C#编译器只是简单的把myFunc替换为System.Func&int,&int&类的一个静态域。
我们来对这个委托的定义做一点点改变:
System.Func&int, int& myFunc = (p) =& p * i++;
通过把&p&替换成&i++&,我们把可以称为&本地定义的函数&变成了一个真正的闭包。闭包是函数式编程的核心。它们把函数和数据绑定在一起-更准确的说,是和在函数外定义的非本地变量绑定。在myFunc这个例子里,&p&是一个本地变量但是&i&不是,它属于Update()函数的作用域。C#编译器现在得把myFunc转换成可以访问甚至改变非本地变量的函数。它通过声明(后台)一个新类来代表myFunc创造时的引用环境来达到这个目的。这个类的对象会在我们每次经历for循环的时候创建,这样我们就突然有了一个巨大的内存泄露(在我的电脑上2.6kb每帧)。
当然,在C#3.0引入闭包和其他一些语言特性的主要原因是LINQ。如果闭包会导致内存泄露,那在游戏里使用LINQ是安全的吗?也许我不适合问这个问题,因为我总是像躲瘟疫一样避免使用LINQ。LINQ的一部分显然不会在不支持实时编译(jit)的系统上工作,比如IOS。但是从内存角度考虑,LINQ也不是好的选择。一个像这样基础到难以置信的表达式:
1 int[] array = { 1, 2, 3, 6, 7, 8 };
2 void Update(){
IEnumerable&int& elements = from element in array
4 orderby element descending
where element & 2
在我的系统上每帧需分配68字节(Enumerable.OrderByDescending()分配28,Enumerable.Where()40)!这里的元凶甚至不是闭包而是IEnumerable的扩展方法:LINQ必须得创建中间数组以得到最终结果,并且之后没有适当的系统来回收。虽然这么说,但我也不是LINQ方面的专家,我也不知道是否部分可以再实际中可以使用。
如果你通过StartCoroutine()来启动一个协程,你就隐式创建了一个UnityCoroutine类(21字节)和一个Enumerator&类(16字节)的实例。重要的是,当协程&yield和resume的时候不会再分配内存,所以你只需要在游戏运行的时候限制StartCoroutine()&的调用就能避免内存泄露。
对C#和Unity内存问题的概论不提及字符串是不完整的。从内存角度考虑,字符串是奇怪的,因为它们既是堆分配的又是不可变的。当你这样连接两个字符串的时候:
1 void Update(){
string string1 = "Two";
string string2 = "One" + string1 + "Three";
运行时必须至少分配一个新的string类型来装结果。在String.Concat()里这会通过一个叫FastAllocateString()的外部函数高效的执行,但是没有办法绕过堆分配(在我的系统里上述例子占用40字节)。如果你需要动态改变或者连接字符串,使用System.Text.StringBuilder。
有时候,数据必须在堆栈和堆之间移动。例如当你格式化这样的一个字符串:
string result = string.Format("{0} = {1}", 5, 5.0f);
你是在调用这样的函数:
1 public static string Format(
2 string format,
3 params Object[] args)
换句话说,当调用Format()的时候整数5和浮点数&5.0f&必须被转换成System.Object。但是Object是一个引用类型而另外两个是值类型。C#因此必须在堆上分配内存,将值拷贝到堆上去,然后处理Format()到新创建的int和float对象的引用。这个过程就叫装箱,和它的逆过程拆箱。
对&String.Format()来说这个行为也许不是一个问题,因为你怎样都希望它分配堆内存(为新的字符串)。但是装箱也会在意想不到的地方发生。最著名的一个例子是发生在当你想要为你自己的值类型实现等于操作符&==&的时候(例如,代表复数的结构)。阅读关于如果避免隐式装箱的例子点这里。
为了结束这篇帖子,我想说许多库函数也包含隐式内存分配。发现它们最好的方法就是通过分析。最近遇到的两个有趣的例子是:
之前我提到foreach循环通过大部分的标准泛集合类型并不会导致堆分配。这对Dictionary&K,&V&也成立。然而,神奇的是,Dictionary&K,&V&集合和Dictionary&K,&V&.Value集合是类类型,而不是结构。意味着&&(K&key&in&myDict.Keys)..."需要占用16字节。真恶心!
List&T&.Reverse()使用标准的原地数组翻转算法。如果你像我一样,你会认为这意味着不会分配堆内存。又错了,至少在Mono2.6里。有一个扩展方法你能使用,但是不像.NET/Mono版本那样优化过,但是避免了堆分配。和使用List&T&.Reverse()一样使用它:
public static class ListExtensions{
public static void Reverse_NoHeapAlloc&T&(this List&T& list)
int count = list.C
for (int i = 0; i & count / 2; i++)
T tmp = list[i];
list[i] = list[count - i - 1];
list[count - i - 1] =
还有其他的内存陷阱可以写的。但是,我不想给你更多的鱼了,而是教你自己捕鱼。这就是下篇帖子的内容!
阅读(...) 评论()【Unity开发】Unity3D中JavaScript与C#对比
第一个也是最容易区分的一点就是声明变量和方法。
JavaScript的脚本:
1.&private&var&cubeT
在C#中,同样的代码则会是:
1.&private&Transform&cubeT
这对方法同样适用,在C#中,一个方法什么值也没有返回,那么他的返回值为&void&类型,但是在JavaScript中则可以省略。
类的继承也是不同的。在JavaScript和C#中,方法是隐型并且不可重载,除非方法声明中添加虚拟关键字。不同的是C#只重载那些包含重载关键字的方法。而JavaScript不需要关键词,只要重载类方法就可继承他们。我们来看一个JavaScript类继承的例子:
1.&class&Weapon&extends&Item&&
3.&&&&&&//Class&members&and&declarations&&
在C#中,同样的代码则会是:
1.&public&class&Weapon&:&Item&&
3.&&&&&&//Class&members&and&declarations&&
这就是这两种代码的主要区别,实际上他需要定义全部的东西,像执行产出代码,访问GameObject和组件,激光投射等等。还有一些其他的不同点,比如导入函数库的关键字(在JavaScript中用“Import”,在C#中使用“using”),但是这些声明和关键字的不同就比较容易明白了。
1.&//do&this:&&
2.&private&var&score:int;&&
3.&//instead&of&this:&&
4.&private&var&
======================================================
使用JavaScript来获取GameObject很简单,你只需要调用Find()静态方法,并把想要的GameObject的名称作为参数:
1.&private&var&pawnGO:GameO&&
2.&function&Awake()&&
4.&&&&&pawnGO&=&GameObject.Find("Pawn");&&
用C#也很相似:
1.&using&UnityE&&
2.&using&System.C&&
4.&public&class&PawnGetter&:&MonoBehaviour&&
6.&&&&&private&GameObject&pawnGO;&&
8.&&&&&void&Awake&()&&
9.&&&&&{&&
10.&&&&&&&&&pawnGO&=&GameObject.Find("Pawn");&&
11.&&&&&}&&
在不考虑两种语言的关键字和格式不同的情况下,代码是完全一样的(第一个代码的第四行和第二个代码的第八行是相同的)。不管代码是强类型还是弱类型,GameObject.Find()方法总是会返回一个GameObject值。
现在,让我们看看如何获得一个GameObject上的组件。假设“PawnMover”组件赋给“Pawn”GameObject,让我们来看看如何使用JavaScript获得“PawnMover”组件:
1.&private&var&pawnGO:GameO&&
2.&private&var&pmSC:PawnM&&
3.&function&Awake()&&
5.&&&&&pawnGO&=&GameObject.Find("Pawn");&&
6.&&&&&pmSC&=&pawnGO.GetComponent("PawnMover");&&
基本上,要获得“PawnMover”组件,我们所需要做的就是从“Pawn”GameObject调用GetComponent()方法,并把所需组件的名称作为参数。除了名称,我们也可以通过组件类型作为参数,但是像上面的例子我们用名字就行了。因为JavaScript是弱类型,返回值为组件,我们不需要把组件给PawnMover&类作为结果。在C#中也是一样的:
1.&using&UnityE&&
2.&using&System.C&&
4.&public&class&PawnGetter&:&MonoBehaviour&&
6.&&&&&private&GameObject&pawnGO;&&
7.&&&&&private&PawnMover&pmSC;&&
9.&&&&&void&Awake()&&
10.&&&&&{&&
11.&&&&&&&&&pawnGO&=&GameObject.Find("Pawn");&&
12.&&&&&&&&&//returns&a&CS0266&error&&
13.&&&&&&&&&pmSC&=&pawnGO.GetComponent("PawnMover");//&=returns&a&CS0266&error&&
14.&&&&&&&&&//this&is&the&right&way&to&do&it&when&using&C#&&
15.&&&&&&&&&pmSC&=&pawnGO.GetComponent&&PawnMover&&();&&
16.&&&&&}&&
用C#就不可能只是调用GetComponent()方法并把该组件的名称作为参数了,这样他会导致错误CS0266,也就是说C#不能从一个类型隐型转换为另一个格式。因为C#属于强类型,我们不能把组件类型转换为PawnMover类型。我们需要调用一个方法传递这个类型,强制GetComponent()方法返回“PawnMover”对象而不是组件。
======================================================
这系列第三节,解释JavaScript和C#在Unity3d游戏引擎中编程时有什么不同。建议你最好先去阅读第一和第二节,方便理解这节内容。
在第三节中,主要讲解用JavaScript和C#让一个GameObject向前移动有什么不同。现在我们来看一段使用JavaScript让一个GameObject向前移动的代码:
public&var&goTransform:T&
private&var&vel:int&=&2;//how&fast&the&game&object&is&being&moved&&
function&Awake()
//get&this&GameObject's&Transform
&&goTransform&=&this.GetComponent(Transform);&
//&Update&is&called&once&per&frame&&
function&Update()
//moves&the&containing&GameObject&forward
goTransform.position.z&=&goTransform.position.z&+&
把这个脚本的目的是在每一个更新周期使goTransform的z轴坐标增加来控制让goTransform向前移动的,现在让我们看看用C#编写代码会是什么样的:
using&UnityE
using&System.C
public&class&PawnMover&:&MonoBehaviour
public&Transform&goT
private&int&vel&=&2;//how&fast&the&game&object&is&being&moved&&
void&Awake()
&&&&&&&&&//get&this&GameObject's&Transform&&
&&&&&&&&goTransform&=&this.GetComponent();
//&Update&is&called&once&per&frame&&
&&&&&void&Update()
&&&&&&&&&//returns&a&CS1612&error&&
&&&&&&&&goTransform.position.z=goTransform.position.z&+&//&=returns&a&CS1612&error&&
&&&&&&&&&//this&is&the&right&way&to&do&it&when&using&C#&&
goTransform.Translate(Vector3.forward&*&vel);//moves&the&containing&GameObject&forward&&
这里我们可以看到,在C#中不能像JavaScript脚本中那样直接改变goTransform的z轴坐标值来移动,这样会产生CS1612error,因为我们是要改变一个值而不是引用该值。为了避免这个错误,在用C#编写脚本时,我们用方法移动GameObejct,比如Translate()、&Rotate()、&RotateAround()等等。这些方法都是Transform类得公共成员变量。
=======================================================
&&&&&&&&&yielding
pauses代码对于游戏编程是相当有用的,你可以用它更好的控制你游戏中的事件。无论是用JavaScript或C#都不能简单的中断Update()方法。相信你已经猜到了,我们今天要议论的是这两种语言对此的解决方案有什么不同。我们这节中会给出这种解决方案的常用实例。现在我们来看看JavaScript是如何yield的:
private&var&textS:S
private&var&counter:float&=&0;
function&Update&()
&&&&//call&the&function&that&waits&three&seconds&to&execute&&
&&&&WaitThreeSeconds();&&
//This&function&waits&three&seconds&before&executing&&
function&WaitThreeSeconds()&&
&&&&//Waits&three&seconds&&
&&&&yield&WaitForSeconds(3);&&
&&&&//converts&the&counter&to&a&String&and&stores&its&value&in&textS&&
&&&&textS&=&"Three&seconds&has&passed,&now&I'm&counting..."+counter.ToString();&&
&&&&//add&one&to&the&counter&&
function&OnGUI()&&
&&&&//The&next&line&displays&the&first&line&of&text&&
&&&&GUI.Label(new&Rect(20,20,250,20),&"I&will&start&counting&in&3&seconds.");&&
&&&&//Displays&the&counter&&
&&&&GUI.Label(new&Rect(20,40,500,20),textS);&&
这个代码在屏幕上输出了两个文本,第一行会在开始不久就被渲染出来,第二行只会在暂停3秒后出现,yield代码只会执行一次,然后再正常执行Update()循环(也就是说不会再等三秒在去执行一次WaitThreeSeconds())。有一点要注意,我们知道能在函数内用yield,Update()方法是不能被暂停的。
现在来看看C#代码:
using&UnityE&&
using&System.C
public&class&Yield&:&MonoBehaviour&&
&&&&private&string&textS;&&
&&&&private&float&counter&=&0;
&&&&void&Update&()&&
&&&&&&&&//doesn't&generate&an&error&but&doesn't&work&either.&&
&&&&&&&&WaitThreeSeconds();//&=does&nothing&&
&&&&&&&&//start&the&coroutine&named&WaitThreeSeconds&&
&&&&&&&&StartCoroutine("WaitThreeSeconds");//&=right&&
&&&&//Waits&three&seconds&before&executing&&
&&&&IEnumerator&WaitThreeSeconds()&&
&&&&&&&&//Waits&three&seconds&&
&&&&&&&&yield&return&new&WaitForSeconds(3);&&
&&&&&&&&//converts&the&counter&to&a&String&and&stores&its&value&in&textS&&
&&&&&&&&textS&=&"Three&seconds&has&passed,&now&I'm&counting..."+counter.ToString();&&
&&&&&&&&//add&one&to&the&counter&&
&&&&&&&&++&&
&&&&void&OnGUI()&&
&&&&&&&&//The&next&line&displays&the&first&line&of&text&&
&&&&&&&&GUI.Label(new&Rect(20,20,250,20),&"I&will&start&counting&in&3&seconds.");&&
&&&&&&&&//Displays&the&counter&&
&&&&&&&&GUI.Label(new&Rect(20,40,500,20),textS);&&
主要区别就是我们在C#中不能把yield放在函数中使用,需要使用IEnumerator接口,之所以这样只是因为C#就是这么设计的(关于IEnumerator接口的更多信息可以在这里找到)我们不能像调用普通函数那样调用WaitThreeSeconds(),即便那么做也不会有任何反应的。所以我们的解决办法就是想调用协同那样调用WaitThreeSeconds()。(例如14行)
另外一个区别就是在21行,我们要用yield
return new WaitForSeconds(3)来替换&yield
WaitForSeconds(3)代码,因为我们用IEnumerator接口需要一个返回值。
====================================================
先来看点儿基础知识:何为射线投射?顾名思义,就是用程序模拟了一个现实生活中被打出的一道射线(“Ray”,译者:O(∩_∩)O哈哈~我的名字)。在游戏编程中相当有用,Raycast类可以返回源点到射线碰撞某物体的距离(有的时候被用作得到碰撞物体的名称)。Unity3D没有单独的Raycast类,它是被Physics,&RaycastHit&和&Ray&功能分散的类。
我来举一个用JavaScript投射射线的例子:
//Creates&a&ray&object&&
private&var&ray&:&R&&
//creates&a&RaycastHit,&to&query&informarion&about&the&objects&that&are&colliding&with&the&ray&&
private&var&hit&:&RaycastHit&&=
&new&RaycastHit();&&
//Get&this&GameObject's&transform&&
private&var&capsTrans
:&Transform;&&
function&Awake()&&
//get&this&Transform&&
capsTrans&=&this.GetComponent(Transform);&&
//&Update&is&called&once&per&frame&&
function&Update&()&&
//recreate&the&ray&every&frame&&
ray&=&new&Ray(capsTrans.position,&Vector3.left);&&
//Casts&a&ray&to&see&if&something&have&hit&it&&
if(Physics.Raycast(ray.origin,ray.direction,&hit,&10))//cast&the&ray&10&units&in&distance&&
//Collision&has&happened,&print&the&distance&and&name&of&the&object&into&the&console&&
Debug.Log(hit.collider.name);&&
Debug.Log(hit.distance);&&
//the&ray&isn't&colliding&with&anything&&
Debug.Log("none")&&
来看看它是如何工作的:
首先,我们需要创建一个射线对象(Ray
object),并且每帧都要重新创建一次(例如第2和20行),该射线类具备如光线投射的方向和来源等属性。
然后,我们需要一个RaycastHit类对象,获取射线与其他GameObject的碰撞发生时的距离和一些其他的详细信息。
接下来,我们需要从Physics类中调用Raycast()静态方法(例如23行),这个方法将实际的投射出一道射线,记录下射线源点、投射方向、RaycastHit&对象和距离这些参数,然后把距离和碰撞结果传递到RaycastHit对象。
乍一听起来可能有些乱,但是多尝试几回就熟练了(更多信息请查看),接下来看看C#代码:
using&UnityE&&
using&System.C&&
public&class&Raycast&:&MonoBehaviour&&
//Creates&a&ray&object&&
private&Ray&&&
//creates&a&RaycastHit,&to&query&informarion&about&the&objects&that&are&colliding&with&the&ray&&
private&RaycastHit&hit&=&new&RaycastHit();&&
//Get&this&GameObject's&transform&&
private&Transform&capsT&&
void&Awake()&&
//get&this&Transform&&
capsTrans&=&this.GetComponent();&&
//&Update&is&called&once&per&frame&&
void&Update&()&&
//recreate&the&ray&every&frame&&
ray&=&new&Ray(capsTrans.position,&Vector3.left);&&
//Casts&a&ray&to&see&if&something&have&hit&it&&
if(Physics.Raycast(ray.origin,&ray.direction,&out&hit,&10))//cast&the&ray&10&units&in&distance&&
//Collision&has&happened,&print&the&distance&and&name&of&the&object&into&the&console&&
Debug.Log(hit.collider.name);&&
Debug.Log(hit.distance);&&
//the&ray&isn't&colliding&with&anything&&
Debug.Log("none")&&
当投射射线时,这两种编程语言之间唯一的区别在第28行代码上。现在来看看我们怎么Out关键词来传递hit对象呢?因为在C#中Raycast()方法需要RaycastHit参数做参考,这恰巧也是out关键词做的事情。所以我们用传递一个Raycast()的参考来代替传递对象(就好像我们在JavaScript中做的那样)。(更多信息请参考)。
转自:.cn/s/blog_6b88dz.html
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

我要回帖

更多关于 标准误差怎么算 的文章

 

随机推荐