Unity3D 实用技巧 - 基础知识大全集(二)

这篇博客介绍了Unity3D编程中的C#基础知识,包括C#与C++的区别、密封类和接口的使用、对象池的概念、反射的原理以及在Unity中的应用。此外,还探讨了面向对象的三大特性(继承、封装、多态),以及委托、事件和泛型在游戏开发中的重要性。文章通过实例展示了如何实现字符串拼接优化、对象创建和内存管理,以及如何在Unity中使用委托和事件。最后,提到了Unity中的脚本语言(JavaScript、C#、Boo)和堆栈内存管理,以及在游戏开发中如何利用这些知识来优化性能。

高阶内容

Part1:C#代码类
1、 C#和C++的区别?
简单的说:C# 与C++ 比较的话,最重要的特性就是C# 是一种完全面向对象的语言,而C++ 不是,另外C# 是基于IL 中间语言和.NET Framework CLR 的,在可移植性,可维护性和强壮性都比C++ 有很大的改进。C# 的设计目标是用来开发快速稳定可扩展的应用程序,当然也可以通过Interop和Pinvoke 完成一些底层操作。
2、 请简述sealed关键字用在类声明时与函数声明时的作用。
类声明时可防止其他类继承此类,在方法中声明则可防止派生类重写此方法。
3、 简述一下对象池,你觉得在FPS里哪些东西适合使用对象池?
对象池就存放需要被反复调用资源的一个空间,比如游戏中要常被大量复制的对象,子弹,敌人,以及任何重复出现的对象。
4、 C#中四种访问修饰符是哪些?各有什么区别?
四种访问修饰符是:属性修饰符、存取修饰符、类修饰符、成员修饰符
a. 属性修饰符:
Serializable:按值将对象封送到远程服务器。
STATread:是单线程套间的意思,是一种线程模型。
MATAThread:是多线程套间的意思,也是一种线程模型。
b. 存取修饰符:
public:存取不受限制。
private:只有包含该成员的类可以存取。
internal:只有当前命名空间可以存取。只能在包含该类的程序集中访问该类。
protected:只有包含该成员的类以及派生类可以存取。
protected internal:protected + internal
c. 类修饰符:
abstract:抽象类。指示一个类只能作为其它类的基类。
sealed:密封类。指示一个类不能被继承。理所当然,密封类不能同时又是抽象类,因为抽象总是希望被继承的。
d. 成员修饰符:
abstract:指示该方法或属性没有实现。
sealed:密封方法。可以防止在派生类中对该方法的override(重载)。不是类的每个成员方法都可以作为密封方法密封方法,必须对基类的虚方法进行重载,提供具体的实现方法。所以,在方法的声明中,sealed修饰符总是和override修饰符同时使用。
delegate:委托。用来定义一个函数指针。C#中的事件驱动是基于delegate + event的。
const:指定该成员的值只读不允许修改。
event:声明一个事件。
extern:指示方法在外部实现。
override:重写。对由基类继承成员的新实现。
readonly:指示一个域只能在声明时以及相同类的内部被赋值。
static:指示一个成员属于类型本身,而不是属于特定的对象。即在定义后可不经实例化,就可使用。
virtual:指示一个方法或存取器的实现可以在继承类中被覆盖。
new:在派生类中隐藏指定的基类成员,从而实现重写的功能。 若要隐藏继承类的成员,请使用相同名称在派生类中声明该成员,并用 new 修饰符修饰它。
5、 请简述ArrayList和List<Int>的主要区别?
a. ArrayList存在不安全类型,ArrayList会把所有插入其中的数据都当做Object来处理、装箱拆箱的操作;
b. List是接口,ArrayList是一个实现了该接口的类,可以被实例化。
6、 请简述GC(垃圾回收)产生的原因,并描述如何避免?
GC产生的原因:回收堆上的内存
避免:
a. 减少new产生对象的次数
b. 使用公用的对象(静态成员)
c. 将String换为StringBuilder
7、 反射的实现原理?
原理:审查元数据并收集关于它的类型信息的能力。
实现步骤:
a. 导入using System.Reflection;
b. Assembly.Load("程序集");//加载程序集,返回类型是一个Assembly;
c. 得到程序集中所有类的名称。
    
foreach ( Type type in assembly . GetTypes ( ) ) { string t = type . Name ; } Type type = assembly . GetType ( "程序集.类名" ) ; //获取当前类的类型 Activator . CreateInstance ( type ) ; //创建此类型实例 MethodInfo mInfo = type . GetMethod ( "方法名" ) ; //获取当前方法 mInfo . Invoke ( null , 方法参数 ) ;
8、 请描述Interface与抽象类之间的不同。
抽象类表示该类中可能已经有一些方法的具体定义,但接口就是公公只能定义各个方法的界面 ,不能具体的实现代码在成员方法中。类是子类用来继承的,当父类已经有实际功能的方法时该方法在子类中可以不必实现,直接引用父类的方法,子类也可以重写该父类的方法。实现接口的时候必须要实现接口中所有的方法,不能遗漏任何一个。
9、 .Net与Mono的关系?
Mono是.Net的一个开源跨平台工具,就类似Java虚拟机,Java本身不是跨平台语言,但运行在虚拟机上就能够实现了跨平台。.Net只能在Windows下运行,Mono可以实现跨平台编译运行,可以运行于Linux,Unix,Mac OS等。
10、 简述Unity3D支持的作为脚本的语言的名称。
Unity的脚本语言基于Mono的.Net平台上运行,可以使用.NET库,这也为XML、数据库、正则表达式等问题提供了很好的解决方案。Unity里的脚本都会经过编译,他们的运行速度也很快。这三种语言实际上的功能和运行速度是一样的,区别主要体现在语言特性上。主要使用语言:JavaScript、 C#、Boo。
11、 Heap与Stack有何区别?
a. Heap是堆,Stack是栈。
b.Stack的空间由操作系统自动分配和释放,Heap的空间是手动申请和释放的,Heap常用New关键字来分配。
c.Stack空间有限,Heap的空间是很大的自由区。
12、 值类型和引用类型有何区别?
a. 值类型的数据存储在内存的栈中;引用类型的数据存储在内存的堆中,而内存单元中只存放堆中对象的地址。
b. 值类型存取速度快,引用类型存取速度慢。
c. 值类型表示实际数据,引用类型表示指向存储在内存堆中的数据的指针或引用。
e. 值类型继承自System.ValueType,引用类型继承自System.Object。
f. 栈的内存分配是自动释放;而堆在.NET中会有GC来释放。
g. 值类型的变量直接存放实际的数据,而引用类型的变量存放的则是数据的地址,即对象的引用。
h. 值类型变量直接把变量的值保存在堆栈中,引用类型的变量把实际数据的地址保存在堆栈中。
13、 结构体和类有何区别?
结构体是一种值类型,而类是引用类型。(值类型、引用类型是根据数据存储的角度来分的)。就是值类型用于存储数据的值,引用类型用于存储对实际数据的引用。那么结构体就是当成值来使用的,类则通过引用来对实际数据操作。
14、 请写出求斐波那契数列任意一位的值得算法
    
static int Fn ( int n ) { if ( n <= 0 ) { throw new ArgumentOutOfRangeException ( ) ; } if ( n == 1 || n == 2 ) { return 1 ; } return checked ( Fn ( n - 1 ) + Fn ( n - 2 ) ) ; // when n>46 memory will overflow }
15、 ref参数和out参数是什么?有什么区别?
ref和out参数的效果一样,都是通过关键字找到定义在主函数里面的变量的内存地址,并通过方法体内的语法改变它的大小。
区别:
a. 就是输出参数必须对参数进行初始化。
b. ref参数是引用,out参数为输出参数。
16、 C#的委托是什么?有何用处?
C#的委托类似于一种安全的指针引用,在使用它时是当做类来看待而不是一个方法,相当于对一组方法的列表的引用。
用处:使用委托使程序员可以将方法引用封装在委托对象内。然后可以将该委托对象传递给可调用所引用方法的代码,而不必在编译时知道将调用哪个方法。与C或C++中的函数指针不同,委托是面向对象,而且是类型安全的。
17、 什么是里氏代换元则?
里氏替换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏替换原则中说,任何基类可以出现的地方,子类一定可以出现,作用方便扩展功能。
18、 Mock和Stub有何区别?
Mock与Stub的区别:Mock:关注行为验证。细粒度的测试,即代码的逻辑,多数情况下用于单元测试。Stub:关注状态验证。粗粒度的测试,在某个依赖系统不存在或者还没实现或者难以测试的情况下使用,例如访问文件系统,数据库连接,远程协议等。
19、 概述序列化:
序列化简单理解成把对象转换为容易传输的格式的过程。比如,可以序列化一个对象,然后使用HTTP通过Internet在客户端和服务器端之间传输该对象。
20、 堆和栈的区别?
a. 栈由系统自动分配,而堆是人为申请开辟;
b. 栈获得的空间较小,而堆获得的空间较大;
c. 栈由系统自动分配,速度较快,而堆一般速度比较慢;
d. 栈是连续的空间,而堆是不连续的空间;
e. 堆像是一个仓库,储存着我们使用的各种对象等信息,跟栈不同的是他们被调用完毕不会立即被清理掉。
21、 概述c#中代理和事件?
代理就是用来定义指向方法的引用,C#事件本质就是对消息的封装,用作对象之间的通信;发送方叫事件发送器,接收方叫事件接收器。
22、 C#中的排序方式有哪些?
排序方式:选择排序,冒泡排序,快速排序,插入排序,希尔排序,归并排序
23、 客户端与服务器交互方式有几种?
Socket通常也称作"套接字",实现服务器和客户端之间的物理连接,并进行数据传输,主要有UDP和TCP两个协议。Socket处于网络协议的传输层。
Http协议传输的主要有Http协议 和基于Http协议的Soap协议(Web Service),常见的方式是 Http 的Post 和Get 请求,Web 服务。
24、 能用foreach遍历访问的对象需要实现 接口或声明 方法的类型?
IEnumerable接口、GetEnumerator方法
25、 重载和重写的区别?
a. 封装、继承、多态所处位置不同,重载在同类中,重写在父子类中。
b. 定义方式不同,重载方法名相同参数列表不同,重写方法名和参数列表都相同。
c. 调用方式不同,重载使用相同对象以不同参数调用,重写用不同对象以相同参数调用。
d. 多态时机不同,重载时编译时多态,重写是运行时多态。
26、 面向对象的三大特点
a. 继承: 提高代码重用度,增强软件可维护性的重要手段,符合开闭原则。继承最主要的作用就是把子类的公共属性集合起来,便与共同管理,使用起来也更加方便。你既然使用了继承,那代表着你认同子类都有一些共同的特性,所以你把这些共同的特性提取出来设置为父类。继承的传递性:传递机制 a->b; b->c; c具有a的特性 。继承的单根性:在C#中一个类只能继承一个类,不能有多个父类。
b.封装: 封装是将数据和行为相结合,通过行为约束代码修改数据的程度,增强数据的安全性,属性是C#封装实现的最好体现。就是将一些复杂的逻辑经过包装之后给别人使用就很方便,别人不需要了解里面是如何实现的,只要传入所需要的参数就可以得到想要的结果。封装的意义在于保护或者防止代码(数据)被我们无意中破坏。
c.多态性: 多态性是指同名的方法在不同环境下,自适应的反应出不同得表现,是方法动态展示的重要手段。多态就是一个对象多种状态,子类对象可以赋值给父类型的变量。
27、 反射的实现原理?
可以在加载程序运行时,动态获取和加载程序集,并且可以获取到程序集的信息反射即在运行期动态获取类、对象、方法、对象数据等的一种重要手段。
主要使用的类库:System.Reflection
核心类:
a. Assembly描述了程序集
b. Type描述了类这种类型
c. ConstructorInfo描述了构造函数
d. MethodInfo描述了所有的方法
e. FieldInfo描述了类的字段
f. PropertyInfo描述类的属性
通过以上核心类可在运行时动态获取程序集中的类,并执行类构造产生类对象,动态获取对象的字段或属性值,更可以动态执行类方法和实例方法等。
28、 在类的构造函数前加上Static会报什么错?为什么?
构造函数格式为Public+类名如果加上 Static 会报错(静态构造函数不能有访问、型的对象,静态构造函数只执行一次;运行库创建类实例或者首次访问静态成员之前,运行库调用静态构造函数;静态构造函数执行先于任何实例级别的构造函数;显然也就无法使用this和 base 来调用构造函数。
29、 C# String类型比 StringBuilder 类型的优势是什么?
a. 如果是处理字符串的话,用String中的方法每次都需要创建一个新的字符串对象并且分配新的内存地址,而 StringBuilder 是在原来的内存里对字符串进行修改,所以在字符串处理方面还是建议用stringBuilder这样比较节约内存。但是 string 类的方法和功能仍然还是比 stringBuilder 类要强。
b. String类由于具有不可变性(即对一个 string 对象进行任何更改时,其实都是创建另外一个 string 类的对象),所以当需要频繁的对一个 string 类对象进行更改的时候,建议使用StringBuilder 类,StringBuilder 类的原理是首先在内存中开辟一定大小的内存空间,当对此 StringBuilder 类对象进行更改时, 如果内存空间大小不够, 会对此内存空间进行扩充,而不是重新创建一个对象,这样如果对一个字符串对象进行频繁操作的时候,不会造成过多的内存浪费,其实本质上并没有很大区别,都是用来存储和操作字符串的,唯一的区别就在于性能上。
c. String主要用于公共 API,通用性好、用途广泛、读取性能高、占用内存小。
d. StringBuilder主要用于拼接 String,修改性能好。不过现在的编译器已经把String的 + 操作优化成 StringBuilder 了, 所以一般用String 就可以了。
e. String是不可变的,所以天然线程同步。
f. StringBuilder可变,非线程同步。
30、 C#函数 Func(string a, string b)用 Lambda 表达式怎么写?
(a,b) => {};
31、 数列1,1,2,3,5,8,13...第 n 位数是多少?用 C#递归算法实现
    
public int CountNumber ( int num ) { if ( num == 1 || num == 2 ) { return 1 ; } else { return CountNumber ( num - 1 ) + CountNumber ( num - 2 ) ; } }
32、 冒泡排序
    
public static void BubblingSort ( int [ ] array ) { for ( int i = 0 ; i < array . 长度 ; i ++ ) { for ( int j = array . 长度 - 1 ; j > 0 ; j -- ) { if ( array [ j ] < array [ i ] ) { int temp = array [ j ] ; array [ j ] = array [ j - 1 ] ; array [ j - 1 ] = temp ; } } } }
33、 C#中有哪些常用的容器类,各有什么特点?
容器类:List,HashTable,Dictionary,Stack,Queue
a. List:索引泛型容器 访问速度快 修改速度慢
b. HashTable/Dictionary:散列表格式 查询效率高 空间占用较大
c. Stack:后进先出
e. Queue:先进先出
34、 C#中常规容器和泛型容器有什么区别,哪种效率高?
不带泛型的容器需要装箱和拆箱操作速度慢,所以泛型容器效率更高数据类型更安全。
35、 有哪些常见的数值类?
简单值类型包括:整数类型、实数类型、字符类型、布尔类型
复合值类型包括:结构类型、枚举类型
36、 C#中委托和接口有什么区别?各自应用情况?
接口(interface)是约束类应该具备的功能集合,约束了类应该具备的功能,使类从千变万化的具体逻辑中解脱出来,便于类的管理和扩展,同时又合理解决了类的单继承问题。C#中的委托是约束方法集合的一个类,可以便捷的使用委托对这个方法集合进行操作。
使用接口应用情况:
a.无法使用继承的场合
b.完全抽象的场合
c.多人协作的场合
37、 C#中unsafe关键字是用来做什么的?什么场合下使用?
非托管代码才需要这个关键字一般用在带指针操作的场合
38、 C#中ref和out关键字有什么区别?
ref修饰参数,表示进行引用传递,out修饰参数也表示进行引用传递,但传递的引用只为带回返回值, ref又进又出,out不进只出。
39、 For,foreach,Enumerator.MoveNext的使用,与内存消耗情况?
for循环可以通过索引依次进行遍历,foreach和Enumerator.MoveNext通过迭代的方式进行遍历。内存消耗上本质上并没有太大的区别。但是在Unity中的Update中,一般不推荐使用foreach 因为会遗留内存垃圾。
40、 函数中多次使用string的+=处理,会产生大量内存垃圾(垃圾碎片),有什么好的方法可以解决?
通过StringBuilder那进行Append,这样可以减少内存垃圾
41、 当需要频繁创建使用某个对象时,有什么好的程序设计方案来节省内存?
设计单例模式进行创建对象或者使用对象池
42、 JIT和AOT区别?
JIT(Just-In-Time):实时编译,执行慢安装快占空间小一点。
AOT(Ahead-Of-Time ):预先编译,执行快安装慢占内存占外存大。
43、 给定一个存放参数的数组,重新排列数组。
void SortArray(Array arr){Array.Sort(arr);}
44、 Foreach循环迭代时,若把其中的某个元素删除,程序报错,怎么找到那个元素?以及具体怎么处理这种情况?(注:Try.....Catch捕捉异常,发送信息不可行)
Foreach不能进行元素的删除,因为迭代器会锁定迭代的集合,解决方法:记录找到索引或者Key值,迭代结束后再进行删除。
45、 GameObject a=new GameObject() GameObject b=a 实例化出来了A,将A赋给B,现在将B删除,问A还存在吗?
存在,b删除只是将它在栈中的内存删除,而A对象本身是在堆中,所以A还存在。
46、 C#中委托和事件的区别
委托是一个类,该类内部维护着一个字段,指向一个方法。事件可以被看作一个委托类型的变量,通过事件注册、取消多个委托或方法。以下分别通过委托和事件执行多个方法,从中体会两者的区别。
(1) 通过委托执行方法:在CLR运行时,委托DoSth实际上就一个类,该类有一个参数类型为方法的构造函数,并且提供了一个Invoke实例方法,用来触发委托的执行;委托DoSth定义了方法的参数和返回类型;通过委托DoSth的构造函数,可以把符合定义的方法赋值给委托;调用委托的实例方法Invoke执行了方法。
    
class Program { static void Main ( string [ ] args ) { Example example = new Example ( ) ; example . Go ( ) ; Console . ReadKey ( ) ; } } public class Example { public delegate void DoSth ( string str ) ; internal void Go ( ) { //声明一个委托变量,并把已知方法作为其构造函数的参数 DoSth d = new DoSth ( Print ) ; string str = "Hello,World" ; //通过委托的静态方法Invoke触发委托 d . Invoke ( str ) ; } void Print ( string str ) { Console . WriteLine ( str ) ; } }
(2) 委托变量(参数列表):委托DoSth的参数列表和方法Print的参数列表还是保持一致,委托DoSth中的参数object sender通常用来表示动作的发起者,EventArgs e用来表示动作所带的参数。
    
public class Example { public delegate void DoSth ( object sender , EventArgs e ) ; internal void Go ( ) { //声明一个委托变量,并把已知方法作为其构造函数的参数 DoSth d = new DoSth ( Print ) ; object sender = 10 ; EventArgs e = new EventArgs ( ) ; d ( sender , e ) ; } void Print ( object sender , EventArgs e ) { Console . WriteLine ( sender ) ; } }
(3) 通过事件执行方法:声明了事件myDoSth,事件的类型是DoSth这个委托;通过+=为事件注册委托;通过DoSth委托的构造函数为事件注册委托实例;采用委托变量(参数列表)这种形式,让事件执行方法。
    
public class Example { public delegate void DoSth ( object sender , EventArgs e ) ; public event DoSth myDoSth ; internal void Go ( ) { //声明一个委托变量,并把已知方法作为其构造函数的参数 DoSth d = new DoSth ( Print ) ; object sender = 10 ; EventArgs e = new EventArgs ( ) ; myDoSth += new DoSth ( d ) ; myDoSth ( sender , e ) ; } void Print ( object sender , EventArgs e ) { Console . WriteLine ( sender ) ; } }
(4) 通过+=还可以为事件注册多个委托。
    
public class Example { public delegate void DoSth ( object sender , EventArgs e ) ; public event DoSth myDoSth ; internal void Go ( ) { //声明一个委托变量,并把已知方法作为其构造函数的参数 DoSth d = new DoSth ( Print ) ; DoSth d1 = new DoSth ( Say ) ; object sender = 10 ; EventArgs e = new EventArgs ( ) ; //为事件注册多个委托 myDoSth += new DoSth ( d ) ; myDoSth += new DoSth ( d1 ) ; myDoSth ( sender , e ) ; } void Print ( object sender , EventArgs e ) { Console . WriteLine ( sender ) ; } void Say ( object sender , EventArgs e ) { Console . WriteLine ( sender ) ; } }
(5) 通过事件直接注册方法。
    
public class Example { public delegate void DoSth ( object sender , EventArgs e ) ; public event DoSth myDoSth ; internal void Go ( ) { object sender = 10 ; EventArgs e = new EventArgs ( ) ; //为事件注册多个委托 myDoSth += Print ; myDoSth += Say ; myDoSth ( sender , e ) ; } void Print ( object sender , EventArgs e ) { Console . WriteLine ( sender ) ; } void Say ( object sender , EventArgs e ) { Console . WriteLine ( sender ) ; } }
(6) 使用EventHandler来执行多个方法。
    
public class Example { public event EventHandler myEvent ; internal void Go ( ) { object sender = 10 ; EventArgs e = new EventArgs ( ) ; //为事件注册多个委托 myEvent += Print ; myEvent += Say ; myEvent ( sender , e ) ; } void Print ( object sender , EventArgs e ) { Console . WriteLine ( sender ) ; } void Say ( object sender , EventArgs e ) { Console . WriteLine ( sender ) ; } }
根据上述总结:
a. 委托就是一个类,也可以实例化,通过委托的构造函数来把方法赋值给委托实例
b. 触发委托有2种方式: 委托实例.Invoke(参数列表),委托实例(参数列表)
c. 事件可以看作是一个委托类型的变量
d. 通过+=为事件注册多个委托实例或多个方法
e. 通过-=为事件注销多个委托实例或多个方法
f. EventHandler就是一个委托
47、 空间内一物体绕球面固定点(0、1、2)按照固定速度speed环绕运动。
    
public float Speed = 1 ; void Update ( ) { transform . RotateAround ( new Vector3 ( 0 , 1 , 2 ) , Vector3 . up , Speed ) ; }
48、 用代码实现第三角色控制器
    
public class Player : MonoBehaviour { public Transform _cameraTrans ; private Vector3 _cameraOffset ; void Awake ( ) { _cameraOffset = transform . position - _cameraTrans . position ; } void Update ( ) { _cameraTrans . position = transform . position - _cameraOffset ; } }
49、 含义Mathf.Round, Mathf.Clamp, Mathf.Lerp?
Mathf.Round 四舍五入;Mathf.Clamp 限制;Mathf.Lerp 插值
50、 写一个计时器工具,从整点开始计时,格式为:00:00:00
    
private float timer = 0f ; private int h = 0 ; private int m = 0 ; private int s = 0 ; private string timeStr = string . Empty ; void Update ( ) { timer += Time . deltaTime ; if ( timer >= 1f ) { s ++ ; timer = 0 ; } if ( s >= 60 ) { m ++ ; s = 0 ; } if ( m >= 60 ) { h ++ ; m = 0 ; } if ( h >= 99 ) { h = 0 ; } } void OnGUI ( ) { timeStr = string . Format ( "{0:D2}:{1:D2}:{2:D2}" , h , m , s ) ; GUI . Label ( new Rect ( 10 , 10 , 100 , 200 ) , timeStr ) ; }
51、 用鼠标实现在场景中拖动物体,用鼠标滚轮实现缩放(用一个Cube 即可)。
在场景中添加一个Plan,Camera,Directional Light,Cube。添加两个脚本scrollerScirpt(挂在Camera),CubeDragScript(挂在Cube上)。
a.鼠标滚轮实现缩放:将摄像机的镜头拉近或者拉远,调整摄像机的视角就可以实现,主要实现代码如下:
    
void Update ( ) { //鼠标滚轮的效果 if ( Input . GetAxis ( "Mouse ScrollWheel" ) < 0 ) { if ( Camera . main . fieldOfView <= 100 ) Camera . main . fieldOfView += 2 ; if ( Camera . main . orthographicSize <= 20 ) Camera . main . orthographicSize += 0.5F ; } //Zoom in if ( Input . GetAxis ( "Mouse ScrollWheel" ) > 0 ) { if ( Camera . main . fieldOfView > 2 ) Camera . main . fieldOfView -= 2 ; if ( Camera . main . orthographicSize >= 1 ) Camera . main . orthographicSize -= 0.5F ; } }
b.鼠标实现在场景中拖动物体:
  解决思路就是将世界坐标转换成屏幕坐标,然后计算物体与鼠标之间移动量,循环鼠标被按下操作,得到鼠标的当前位置,加上计算好的移动量,将新的坐标赋值给物理就行了。主要是开启一个协同程序(Corountine)来处理。主要代码如下:
    
// Use this for initialization void Start ( ) { StartCoroutine ( OnMouseDown ( ) ) ; } IEnumerator OnMouseDown ( ) { //将物体由世界坐标系转换为屏幕坐标系 Vector3 screenSpace = Camera . main . WorldToScreenPoint ( transform . position ) ; //完成两个步骤 1.由于鼠标的坐标系是2维,需要转换成3维的世界坐标系 //2.只有3维坐标情况下才能来计算鼠标位置与物理的距离,offset即是距离 //将鼠标屏幕坐标转为三维坐标,再算出物体位置与鼠标之间的距离 Vector3 offset = transform . position - Camera . main . ScreenToWorldPoint ( new Vector3 ( Input . mousePosition . x , Input . mousePosition . y , screenSpace . z ) ) ; while ( Input . GetMouseButton ( 0 ) ) { //得到现在鼠标的2维坐标系位置 Vector3 curScreenSpace = new Vector3 ( Input . mousePosition . x , Input . mousePosition . y , screenSpace . z ) ; //将当前鼠标的2维位置转换成3维位置,再加上鼠标的移动量 Vector3 curPosition = Camera . main . ScreenToWorldPoint ( curScreenSpace ) + offset ; //curPosition就是物体应该的移动向量赋给transform的position属性 transform . position = curPosition ; yield return new WaitForFixedUpdate ( ) ; //这个很重要,循环执行 } }
52、 NGUI Button 怎样接受用户点击并调用函数,具体方法名称是什么?
a. 主要是在UICamera脚本中用射线判断点击的物体并通过SendMessage调用OnClick() OnPress()等函数,可以说NGUI的按钮是通过发消息这个方式调用的。具体方法名称是OnClick()。
b. 事例代码:
    
void Awake ( ) { //获取需要监听的按钮对象 GameObject button = GameObject . Find ( "UI Root/Button3" ) ; //设置这个按钮的监听,指向本类的ButtonClick方法中。 UIEventListener . Get ( button ) . onClick = OnButton3Click ; } private void OnButton3Click ( GameObject button ) { Debug . Log ( "我是按钮3被点击了" ) ; }
53、 itween 插件的作用是什么,itween 作用于世界坐标还是局部坐标,请列举出 3 个其常用方法?
iTween 是一个动画库,作者创建它的目的就是最小的投入实现最大的产出。让你做开发更轻松,用它可以轻松实现各种动画,晃动,旋转,移动,褪色,上色,控制音频等等方法:
a. MoveTo 物体移动;
b.ColorTo:随着时间改变对象的颜色组;
c. LookTo:随时间旋转物体让其脸部朝向所提供的 Vector3 或 Transform 位置;
54、 去掉敏感字的程序
    
String s = "你是坏蛋" ; s . Replace ( "坏蛋" , "**" ) ;
55、 写一个角色控制器,鼠标控制屏幕晃动,鼠标控制开枪。
    
public class Player : MonoBehaviour { public GameObject _prefabBullet ; private float _angleSpeed = 120f ; void Update ( ) { float eularY = Input . GetAxis ( "Mouse X" ) * _angleSpeed * Time . deltaTime ; transform . Rotate ( new Vector3 ( 0 , eularY , 0 ) ) ; if ( Input . GetMouseButtonDown ( 0 ) ) { Instantiate ( _prefabBullet , transform . position , transform . rotation ) ; } }
56、 需求:写一个角色控制器,鼠标控制屏幕晃动,鼠标控制开枪。敌人AI,有各种状态,实现各种状态之间的切换。敌人会和主角对抗,敌人被打到之后,会闪一次红色,然后红色比例提升10%,10次攻击之后,成红色。敌人会自动攻击主角,主角也会有颜色变化。敌人会在范围内巡逻。UI,左边显示8个AI的被攻击次数,右边显示AI的攻击次数排序。
    
public class Player : MonoBehaviour { public Camera _camera ; public GameObject _prefabBullet ; private float _angleSpeed = 120f ; //生命值 private int _life = 10 ; //玩家的状态 private bool _state = false ; //是否被打到 public void IsState ( ) { if ( _state && _life > 0 ) { _life -= 1 ; _state = false ; } else { _state = true ; } } public void RoleRotate ( ) { float eularY = Input . GetAxis ( "Mouse X" ) * _angleSpeed * Time . deltaTime ; } //风发射子弹 public void RoleShoot ( ) { if ( Input . GetMouseButtonDown ( 0 ) ) { Instantiate ( _prefabBullet , transform . position , transform . rotation ) ; } } } public class Enemy : MonoBehaviour { //敌人被打到状态 private bool _state = false ; //玩家与敌人距离 private float _distance ; //角色 public GameObject _role ; //生命值 private int _life = 10 ; //敌人被攻击次数 private int _timeAccack = 0 ; //敌人状态 public void EnemyState ( ) { if ( _distance >= 10f ) { if ( _life >= 1 && _state == true ) { _life -= 1 ; _timeAccack ++ ; } _state = false ; } else if ( _distance >= 0 && _state == false ) { _state = true ; } } //敌人与玩家距离 public void Distance ( ) { _distance = Vector3 . Distance ( transform . position , _role . transform . position ) ; } void OnGUI ( ) { GUI . TextArea ( new Rect ( 20 , 50 , 80 , 30 ) , _timeAccack . ToString ( ) ) ; GUI . TextArea ( new Rect ( 20 , 90 , 80 , 30 ) , _timeAccack . ToString ( ) ) ; GUI . TextArea ( new Rect ( 20 , 130 , 80 , 30 ) , _timeAccack . ToString ( ) ) ; } }
57、 3D空间有三个cube当做点,有一条鱼的模型,要求在三点之间游动,要求转向平滑一点,控制鱼的运动朝向(用四元数和欧拉角)
使用transform.localRotation = Quaternion.Slerp(Quaternion a,Quaternion b,float c)实现物体平滑转向。
基于径向基函数神经网络RBFNN的自适应滑模控制学习(Matlab代码实现)内容概要:本文介绍了基于径向基函数神经网络(RBFNN)的自适应滑模控制方法,并提供了相应的Matlab代码实现。该方法结合了RBF神经网络的非线性逼近能力和滑模控制的强鲁棒性,用于解决复杂系统的控制问题,尤其适用于存在不确定性和外部干扰的动态系统。文中详细阐述了控制算法的设计思路、RBFNN的结构与权重更新机制、滑模面的构建以及自适应律的推导过程,并通过Matlab仿真验证了所提方法的有效性和稳定性。此外,文档还列举了量相关的科研方向和技术应用,涵盖智能优化算法、机器学习、电力系统、路径规划等多个领域,展示了该技术的广泛应用前景。; 适合人群:具备一定自动控制理论基础和Matlab编程能力的研究生、科研人员及工程技术人员,特别是从事智能控制、非线性系统控制及相关领域的研究人员; 使用场景及目标:①学习和掌握RBF神经网络与滑模控制相结合的自适应控制策略设计方法;②应用于电机控制、机器人轨迹跟踪、电力电子系统等存在模型不确定性或外界扰动的实际控制系统中,提升控制精度与鲁棒性; 阅读建议:建议读者结合提供的Matlab代码进行仿真实践,深入理解算法实现细节,同时可参考文中提及的相关技术方向拓展研究思路,注重理论分析与仿真验证相结合。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值