Unity的学习笔记

本文介绍了Unity开发中的一些核心概念和技术,包括自定义EditorWindow、Selection类的使用、打包APK的步骤、处理随机冲突、相机应用、光照、UI交互判断、协程的运用、委托与事件、并行与线程、继承封装与多态、骨骼动画、资源打包、XLua热更新、lambda表达式、字典与哈希表以及如何提高代码的健壮性和树的结构。详细讲解了Unity在各个层面的实现与最佳实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.定义一个自己的窗口:EditorWindow.GetWindow(窗口类型,窗口是否浮动,窗口的标题)

要引用using UnityEditor;

例如:using UnityEngine;

      using System.Collections;

      using UnityEditor;//注意要引用

      public class MyWindow: EditorWindow

      {

                  [MenuItem("Window/MyWindow")]//在unity菜单Window下有MyWindow选项

                  static void Init()

                  {

                MyWindow myWindow = (MyWindow)EditorWindow.GetWindow(typeof(MyWindow), false, "MyWindow", true);//创建窗口

                     myWindow.Show();//展示

           }

          }

2.Selection是编辑器类,使用需要using UnitryEditor;且脚本要放在Editor文件夹。

(2018版本可以使用Window->PackageManager管理Packages中的文件夹)

3.打包apk

(1)安装JDK与SDK(android-sdk-windows)SDK下载地址:http://www.androiddevtools.cn

(2)配置JDK路径

变量值:C:\Java\jdk1.6.0_22\bin

(3)打开Edit-Preferences选项-External Tools-Android(填好SDK与JDK的路径)

(4)切换到Android平台下打包

4.using Random = UnityEngine.Random;解决UnityEngine与C# System中Random的冲突

5.摄像机

益智游戏:相机处于静态显示全部视角

第一人称射击游戏:相机作为玩家角色的子对象

赛车游戏:相机跟随玩家的车辆

创建多个小视图:导弹控制器、小地图、后视镜

Camera.main.layerCullDistances脚本函数设置每一层的剔除距离

Render Texture 渲染纹理:可以方便地创建体育场大屏幕、监控摄像机、倒影等等效果。用来创建一个直播监视器

渲染路径:影响灯光和阴影:Deferred Lighting延时光照(最高保真度):有很多实时灯光,使用延时光照(仅在unity上有用,在移动端无效)

Forward Rendering正向渲染:在默认设置中,少数最亮的灯  光在逐像素计算光照模式下渲染

Vertex Lit顶点光照(最低保真度):用于旧机器或受限制的移动平台上

6.light光照

某个地方的光照是不变的:光照贴图

7.判断点击的是否是UI

在窗口端进行判断时使用:

If(Input.GetMouseButtonDown(0)&&!EventSystem.current.IsPointerOverGameObject()){…}

在Android上运行时使用:

if(Input.touchCount>0&&Input.GetTouch(0).parse==TouchPhase.Began)

{

       If(!EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId()))

{ ……}

}
8.协程:等待一个加载完成 yield return www;

void Start(){

       StartCoroutine(TestFunc());

}

IEnumerator TestFunc(){

Debug.log(1);

yield return StartCoroutine(TestFunc1());

for(int i=0;i<10;i++)

{

  Debug.Log(3);

}

}

IEnumerator TestFunc1(){

       for(int i=0;i<10;i++)

{

  Debug.Log(2);

}
       yield return new WaitForSeconds(10);

Debug.Log(4);

}

答案为:1

         2(输出10次)

4

3(输出10次)

注:1.在协同中yield return 另一个协同时必须有返回值即执行完毕才能执行之后的代码。

2.协同程序慎用

3.同一时刻、一个脚本实例中可以有多个暂停的协程,但只有一个运行着的协程

4.函数体全部执行完后,协程结束

9.委托与事件:声明一个事件类似于声明一个进行了封装的委托类型的变量。

delegate可以引用静态函数,也可以引用非静态函数。

class Heater

{

       public delegate void BoilHander(int param);

       public event BoilHander boilHander;

       private int temperature;

       public void BoilWater()

{

  for(int i=0;i<100;i++)

{

    temperature=i;

if(temperature>95)

{

      If(boilEvent!=null)

             boilEvent(temperature);

}

  }

}

}

class Alarm

{

       public void MakeAlert(int param)

{

  Console.WriteLine(“Alarm:嘀嘀嘀,水已经{0}度了:”,param);

}

 

}

class Display

{

       public  static void ShowMsg(int param)

{

  Console.WriteLine(“Display:水快开了,当前温度:{0}度。”,param);

}

 

}

static void Main(string[] args){

       Heater ht=new Heater();

       Alarm al=new Alarm();

       Display dis=new Display();

       ht.boilHander+=al.MakeAlert; //注册方法

       ht.boilHander+=(new Alarm()).MakeAlert;//给匿名对象注册方法

       ht.boilHander+=dis.ShowMsg;//注册静态方法

ht.BoilWater();

}

输出结果:

Alarm:嘀嘀嘀,水已经 96 度了:

Alarm:嘀嘀嘀,水已经 96 度了:

Display:水快烧开了,当前温度:96度。

Alarm:嘀嘀嘀,水已经 97 度了:

Alarm:嘀嘀嘀,水已经 97 度了:

Display:水快烧开了,当前温度:97度。

Alarm:嘀嘀嘀,水已经 98 度了:

Alarm:嘀嘀嘀,水已经 98 度了:

Display:水快烧开了,当前温度:98度。

Alarm:嘀嘀嘀,水已经 99 度了:

Alarm:嘀嘀嘀,水已经 99 度了:

Display:水快烧开了,当前温度:99度。

10.并行与线程

命名空间:using System.Threading;

使用Stopwatch要引用System.Diagnostics命名空间

var stopWatch = new StopWatch();   //创建一个Stopwatch实例

stopWatch.Start();   //开始计时

stopWatch.Stop();   //停止计时

stopWatch.Reset();  //重置StopWatch

stopWatch.Restart(); //重新启动被停止的StopWatch

stopWatch.ElapsedMilliseconds //获取stopWatch从开始到现在的时间差,单位是毫秒

Parallel并行

a.Parallel.Invoke(Run1,Run2);//Run1,Run2是两个函数方法

b.Parallel.For(0,10000,item=>{for(int i=0;i<60000;i++){int sum=0;sum+=item;}});// 并行且同时访问全局变量,会出现资源争夺,大多数时间消耗在了资源等待上面,会比普通的for循环浪费时间。

c.Parallel.Foreach()

List<int> list=new List<int>();

list.Add(0);

Parallel.ForEach(list,item=>

{

DoWork(item);

});

并行集合(线程安全集合):

引用System.Collections.Concurrent命名空间,ConcurrentBag<T>泛型集合,其用法和List<T>类似,ConcurrentBag中的数据并不是按照顺序排列的,顺序是乱的,随机的

Public static void ConcurrentBagWithPallel()

{

       ConcurrentBag<int> list=new ConcurrentBag<int>();

       Parallel.For(0,10000,ITEM=>{List.Add(item);});    

       Console.WriteLine(“ConcurrentBag’s count is {0}”,list.Count());

}

输出的答案为:ConcurrentBag’s count is 10000

Parallel Linq的用法:

AsParallel()这个方法可以应用与任何集合,包括List<T>集合,从而提高查询速度和系统性能

List<Custom> customs = new List<Custom>();

var result = customs.Where<Custom>(c => c.Age > 26).ToList();

var result2 = customs.AsParallel().Where<Custom>(c => c.Age > 26).ToList();(更优)

ToLookup方法是将集合转换成一个只读集合,所以在大数据量分组时性能优于List

var groupByAge = customs.GroupBy(item => item.Age).ToList();

var lookupList = customs.ToLookup(i => i.Age);(更优)

Task的基本使用:需要引用System.Threading.Tasks命名空间

构造函数创建Task:

var task1=new Task(()=>{});//关键字var只能出现在局部变量声明或者是脚本代码中

task1.Start();//构造函数定义的Task必须手动启动

通过静态工厂创建Task:

Var task2=Task.Factory.StartNew(()=>{}); //自动启动

Task的生命周期:

Created:表示默认初始化任务,但是“工厂创建的”实例直接跳过

WaitinToRun:等待任务调度器分配线程给任务执行

RanToCompletion:任务执行完毕

Task的任务控制:

Task.Wait();等待任务执行完成

Task.WaitAll();等待所有的任务执行完成

Task.WaitAny();等待任何一个任务执行完成

Task.ContinueWith();第一个Task完成后自动启动下一个Task,实现Task的延续

取消Task:通过cancellation的tokens来取消一个Task

var tokenSource=new CancellationTokenSource();

tokenSource.Cancel();

Task的嵌套分为关联嵌套与非关联嵌套

非关联嵌套:

var pTask=Task.Factory.StartNew(()=>{

       var cTask=Task.Factory.StartNew(()=>{

              Thread.Sleep(2000);

              Console.WriteLine(“Children task finished!”);

});

Console.WriteLine(“Parent task finished!”);

});

pTask.Wait();

Console.WriteLine(“Flag”);

Console.Read();

输入答案为:

Parent task finished!

Flag

Children task finished!

关联嵌套:

var pTask=Task.Factory.StartNew(()=>{

       var cTask=Task.Factory.StartNew(()=>{

              Thread.Sleep(2000);

              Console.WriteLine(“Children task finished!”);

},TaskCreationOptions.AttachedToParent);

Console.WriteLine(“Parent task finished!”);

});

pTask.Wait();

Console.WriteLine(“Flag”);

Console.Read();

输入答案为:

Parent task finished!

Children task finished!

Flag

11.继承、封装与多态

继承:当类或方法有一定相同的属性,为了减少代码的冗余,可以使用继承关系。

封装:能够有效的避免外部错误对内部造成的“交叉感染”,使软件错误可以局部化。

减少耦合

类内部的结构可以自由修改

可以对成员进行更精确的控制

隐藏信息,实现细节

多态:引用变量到底是哪个类的实例,调用的是哪个类的实现方法都是在运行时确定的,程序具有多个运行状态,这就是多态。

接口与抽象类是多态的体现形式

接口:接口不能直接实例化

      接口中没有方法体的实现

      继承接口的任何非抽象类型都必须实现接口的所有成员

      接口支持多重继承

 

抽象:抽象用abstract来修饰

      抽象方法没有方法体

      抽象方法必须在抽象类中

      抽象类只能作为基类,无法实例化

      抽象类中可以有非抽象成员

      抽象类的派生类必须实现抽象方法体(大括号内的内容)

      父类是抽象类,子类继承了这个类,必须把抽象类中的抽象方法重写

12.骨骼动画

骨骼动画包括:骨骼层次数据、网格Mesh数据、网格蒙皮数据(Skin Info)、骨骼的动画关键帧数据

动画融合时的上下半身速度分离控制:分2层,0层播放基础移动动画

              1层设Mask(配置上半身节点),weight设1(会完全覆盖0层动作)

13.打包图集

需要下载TexturePacker包,导入unity3d中(或者使用自带的SpritePacker)

动态加载图集:[ExecuteInEditMode]不运行程序,也能执行脚本

14.xLua热更新

1.需要在Unity中添加HOTFIX_ENABLE宏之后,打开的步骤(File->Building Setting->Scripting Define Symbols下添加)

2.执行XLua/Generate Code才能正常运行

3.运行XLua/Hotfix Inject In Editor

4.将xLua-master中的XLua和plugins导入项目中,将xLua-master中的Tools复制到项目的根目录下

5.在需要修改的C#脚本前加[HotFix]

15.lambda表达式

委托:

public delegate int deleFun(int x,int y);

public static int Add(int a,int b)

{

       return a+b;

}

Static void Main(string[] args)

{

       deleFun dele=new deleFun(Add);

       Console.WriteLine(dele.Invoke(3,4));                    //7

}

匿名方法:

public delegate int deleFun(int x,int y);

static void Main(string[] args)

{

       deleFun dele=delegate(int x,int y){return x+y;};

       Console.WriteLine(dele.Invoke(3,4));                    //7

}

Lambda表达式:

public delegate int deleFun(int x,int y);

static void Main(string[] args)

{

       deleFun dele=(x,y)=>{return x+y;};

       Console.WriteLine(dele.Invoke(3,4));                    //7

}

泛型委托:

static void Main(string[] args)

{

       Func<int,int,int> dele=(x,y)=>{return x+y;};

       Console.WriteLine(dele.Invoke(3,4));                    //7

}

16.unity的打包

Unity的资源来源有三个方面:1.unity自动打包资源 2.Resources 3.AssetBundle

自动打包资源:

场景中直接使用到的资源,直接存放在Assets下,在场景加载的时候自动加载,静态加载的。

Resources

在Assets下建一个Resources文件夹,不论是否被用到,都会被打包,通过Resources.Load方法动态加载。

AssetBundle

通过编辑器脚本将资源打包,AssetBundle与游戏包分离,可以通过WWW类来加载。

17.字典与哈希表(Hashtable)   

1.单线程程序中使用Dictionary,有泛型优势,读取速度较快,容量利用更充分,无需装箱拆箱。

2.多线程用Hashtable,允许单线程写入,多线程读取,Hash table进一步调用Synchronized()方法可以获得安全的类型,字典非线性安全,必须人为使用lock语句进行保护,效率大减

3.Dictionary有按插入顺序排列数据的特性(注:当调用Remove()删除过节点后顺序被打乱)

Hashtable中的key/value均为object类型,由包含集合元素的存储桶组成。

优点:索引的方式,速度非常快

应用场合:对象缓存,树递归算法的替代,需提升效率的场合

Hashtable是System.Collections命名空间提供的一个容器

Dictionary是System.Collection.Generic命名空间下的

4.哈希表的简单操作:

添加:HashtableObject.Add(key,value);

删除:HashtableObject.Remove(key);

移除所有元素:HashtableObject.Clear();

判断哈希表是否包含特定键:HashtableObject.Contains(key);

5.遍历Hashtable(哈希表中的每个元素都是DictionaryEntry类型

第一种:System.Collections.Hashtable ht=new System.Collections.Hashtable();

foreach(DictionaryEntry de in ht)

{

       Console.WriteLine(de.Key);

       Console.WriteLine(de.Value);

}

第二种:System.Collections.IDictionaryEnumerator d=ht.GetEnumerator();

while(d.MoveNext())

{

       Console.WriteLine(“key:{0} value:{1}”,d.Entry.Key,d.Entry.Value);

}

允许一个线程写,多个线程读

System.Collections.Hashtable htSyn=System.Collections.Hashtable.Synchronized(new System.Collections.Hashtable());

字典的元素类型是键类型和值类型的KeyValuePair

18.如何提高代码的健壮性

1.尽早释放无用的对象

19.树的结构

1.二叉树:每个结点至多只有二棵子树(不存在度大于2的结点)

          二叉树的第i层至多有2^(i-1)个结点

          深度为k的二叉树至多有2^k-1个结点

          叶子数为n0,度为2的结点数为n2,则n0=n2+1

2.满二叉树和完全二叉树

满二叉树:深度为h,层数为k,则h=k

          叶子数为2^h

          第k层的结点数为2^(k-1)

          总结点数为2^k-1

完全二叉树:若设二叉树的深度为h,除第h层外,其他各层的结点数都达到最大,第h层的结点都集中在最左边

            具有n个结点的完全二叉树的深度为log2(n+1)

3.二叉查找树(二叉排序树、二叉搜索树):对树进行中序遍历,可得到有序的数列。

           若左子树不为空,则左子树上所有的结点的值均小于它的根结点的值

           若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值

           左、右子树也分别为二叉排序树

           没有键值相等的结点

二叉查找树的高度决定了二叉查找树的查找效率。

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值