C# 枚举 的用法

枚举是C#编程语言的一种类型,跟类、结构、密封类、分布类、委托、事件、抽象都是同一级别的类型。

枚举本身可以有修饰符,但枚举的成员始终是公共的,不能有访问修饰符。枚举本身的修饰符仅能使用public和internal。

枚举是值类型,隐式继承自System.Enum,不能手动修改。

枚举成员不能相同,但枚举的值可以相同,默认情况下,枚举中的第一个变量被赋值为0,其他的变量的值按定义的顺序来递增(0,12,3…),如果enum中的部分成员显式定义了值,而部分没有,那么没有定义值的成员还是会按照上一个成员的值来递增赋值。

常用方法:

枚举转换:

Enum–>String:

1.利用Object.ToString()方法:如Colors.Green.ToString()的值是”Green”字符串;

2.利用Enum的静态方法GetName与GetNames:例如:Enum.GetName(typeof(Colors),3))与Enum.GetName(typeof(Colors), Colors.Blue))的值都是”Blue”

Enum–>Int:

因为枚举的基类型是除 Char 外的整型,所以可以进行强制转换。

例如:(int)Colors.Red, (byte)Colors.Green

String–>Enum:

public static Object Parse(Type enumType,string value)

例如:(Colors)Enum.Parse(typeof(Colors), “Red”)

Int–>Enum:

可以强制转换将整型转换成枚举类型。

例如:Colors color = (Colors)2 ,那么color即为Colors.Blue

判断某个整型是否定义在枚举中的方法:Enum.IsDefined

public static bool IsDefined(Type enumType,Object value)

例如:Enum.IsDefined(typeof(Colors), 3))

枚举遍历:

foreach (int good in Enum.GetValues(typeof(ItemCode)))
{
  Debug.Log(Player.instance.GetGoodInfo().GetItemCountByItemCode(good) + “ “ + good);
}

Unity Profiler 参数说明

1.CPU Usage:
  WaitForTargetFPS:Vsync(垂直同步)功能所,即显示当前帧的CPU等待时间
  Overhead:Profiler总体时间-所有单项的记录时间总和。用于记录尚不明确的时间消耗,以帮助进一步完善Profiler的统计。
  Physics.Simulate:当前帧物理模拟的CPU占用时间。
  Camera.Render:相机渲染准备工作的CPU占用量
  RenderTexture.SetActive:设置RenderTexture操作.
  底层实现:

       1.比对当前帧与前一帧的ColorSurface和DepthSurface.
  2.如果这两个Buffer一致则不生成新的RT,否则则生成新的RT,并设置与之相对应的Viewport和空间转换矩阵.
  Monobehaviour.OnMouse_ :用于检测鼠标的输入消息接收和反馈,主要包括:SendMouseEvents和DoSendMouseEvents。(只要Edtor开起来,这个就会存在)
HandleUtility.SetViewInfo:仅用于Editor中,作用是将GUI和Editor中的显示看起来与发布版本的显示一致。
GUI.Repaint: GUI的重绘(说明有在使用原生的OnGUI)
  Event.Internal_MakeMasterEventCurrent:负责GUI的消息传送
Cleanup Unused Cached Data:清空无用的缓存数据,主要包括RenderBuffer的垃圾回收和TextRendering的垃圾回收。
1.RenderTexture.GarbageCollectTemporary:存在于RenderBuffer的垃圾回收中,清除临时的FreeTexture.
2.TextRendering.Cleanup:TextMesh的垃圾回收操作
  Application.Integrate Assets in Background:遍历预加载的线程队列并完成加载,同时,完成纹理的加载、Substance的Update等.
  Application.LoadLevelAsync Integrate:加载场景的CPU占用,通常如果此项时间长的话70%的可能是Texture过长导致.
  UnloadScene:卸载场景中的GameObjects、Component和GameManager,一般用在切换场景时.
  CollectGameObjectObjects: 执行上面M项的同时,会将场景中的GameObject和Component聚集到一个Array中.然后执行下面的Destroy.
  Destroy: 删除GameObject和Component的CPU占用.
  AssetBundle.LoadAsync Integrate: 多线程加载AwakeQueue中的内容,即多线程执行资源的AwakeFromLoad函数.
  Loading.AwakeFromLoad: 在资源被加载后调用,对每种资源进行与其对应用处理.

2.GPU Usage:
Device.Present:device.PresentFrame的耗时显示,该选项出现在发布版本中.
Graphics.PresentAndSync:GPU上的显示和垂直同步耗时.该选项出现在发布版本中.
Mesh.DrawVBO: GPU中关于Mesh的Vertex Buffer Object的渲染耗时.
Shader.Parse:资源加入后引擎对Shader的解析过程.
Shader.CreateGPUProgram:根据当前设备支持的图形库来建立GPU工程.

3.Memory Profiler
Used Total: 当前帧的Unity内存、Mono内存、GfxDriver内存、Profiler内存的总和.
Reserved Total:系统在当前帧的申请内存.
Total System Memory Usage:当前帧的虚拟内存使用量.(通常是我们当前使用内存的1.5~3倍)
GameObjects in Scene:当前帧场景中的GameObject数量.
Total Objects in Scene:当前帧场景中的Object数量(除GameObject外,还有Component等).
Total Object Count: Object数据 + Asset数量

4.Detail Memory Profiler
  Texture2d:记录当前帧内存中所使用的纹理资源情况,包括各种GameObject的纹理、天空盒纹理以及场景中所用的Lightmap资源.
  Scene Memory: 记录当前场景中各个方面的内存占用情况,包括GameObject、所用资源、各种组件以及GameManager等(天般情况通过AssetBundle加载的不会显示在这里).
ManagedHeap.UseSize:代码在运行时造成的堆内存分配,表示上次GC到目前为止所分配的堆内存量.
WebStream:这个是由WWW来进行加载的内存占用.
System.ExecutableAndDlls:不同平台和不同硬件得到的值会不一样。

C#泛型

定义:通过泛型可以定义类型安全类,而不会损害类型安全、性能或工作效率。

泛型的好处

1、同样的代码,可以通过任何类型来重用它,减少重复代码

2、编译器支持和类型安全

3、不会强行对值类型进行装箱和取消装箱,或者对引用类型进行向下强制类型转换,性能提高。

泛型继承:
1、泛型类继承中,父类的类型参数已被实例化,这种情况下子类不一定必须是泛型类;
2、父类的类型参数没有被实例化,由子类指定,也就是说父类和子类都是泛型类,并且二者有相同的类型参数;

泛型写法:
泛型类

public class TestChild : Test< string, int>{ }

public class TestChild< T, S> : Test< T, S> { }

public class TestChild< T, S> : Test< String, int> { }
泛型方法:C#的泛型机制只支持在方法申明上包含类型参数,也即是泛型方法

1
2
3
4
5
6
public T getvalue<T>(T t)
{
return t;
}
使用:
Int a = getvalue<Int>(10);

泛型接口:

public interface IList
{
T[] GetElements();
}
泛型委托:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyClass<T>
{
public delegate void GenericDelegate(T t);
public void SomeMethod(T t)
{
}
}

public GenericMethodDemo()
{
MyClass<int> obj = new MyClass<int>();
MyClass<int>.GenericDelegate del;
del = new MyClass<int>.GenericDelegate(obj.SomeMethod);
del(3);
}

泛型约束:C#中的泛型只支持显示的约束,因为这样才能保证C#所要求的类型安全,但显示的约束并非时必须的,如果不加约束,泛型类型参数将只能访问System.Object类型中的公有方法。“显式约束”由where子句表达,可以指定“基类约束”,“接口约束”,“构造器约束”,“值类型/引用类型约束”共四种约束

构造器约束:

public static T CreateObject(out int objectId) where T : new()
public class CSingleton where T : new()
基类约束,还包括基类和接口:

public T FindControl(string name) where T : Component
public class CSingleton where T : IComparable
值约束:

public class MyClass where T : struct { }
引用约束:

public class MyClass where T : class { }
约束组合:

public class MyClass where T : IComparable, new() { }
public class MyClass where T : struct, IComparable { }
public class MyClass where T : B, IComparable, ICloneable { }
public class MyClass<T, U> where T : IComparable where U : class { }
约束 说明
T:struct 类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型。与接口约束同时使用,在最前面(不能与基类约束,构造函数约束一起使用)
T:class 类型参数必须是引用类型,包括任何类、接口、委托或数组类型。
T:new() 类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。
T:<基类名> 类型参数必须是指定的基类或派生自指定的基类。
T:<接口名称> 类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。
T:U 为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。这称为裸类型约束.

C# 中的深拷贝和浅拷贝

1
2
3
4
5
6
7
8
public GoodItem CloneItem()
{
MemoryStream stream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, this);
stream.Position = 0;
return formatter.Deserialize(stream) as GoodItem;
}

2.利用xml序列化和反序列化实现

1
2
3
4
5
6
7
8
9
10
11
public T DeepCopy<T>(T obj)
{
object retval;
MemoryStream ms = new MemoryStream()
XmlSerializer xml = new XmlSerializer(typeof(T));
xml.Serialize(ms, obj);
ms.Seek(0, SeekOrigin.Begin);
retval = xml.Deserialize(ms);
ms.Close();
return (T)retval;
}

3.利用反射实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static T DeepCopy<T>(T obj)
{
//如果是字符串或值类型则直接返回
if (obj is string || obj.GetType().IsValueType) return obj;

object retval = Activator.CreateInstance(obj.GetType());
FieldInfo[] fields = obj.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
foreach (FieldInfo field in fields)
{
try { field.SetValue(retval, DeepCopy(field.GetValue(obj))); }
catch { }
}
return (T)retval;
}

C#中的值类型和引用类型区别

区别:

1:在存储区上的区别: 值类型是存储在栈上面的,引用类型是存储在堆上面的。
2:在赋值上,值类型是直接赋新值给变量,但是引用类型是开辟一块新的空间来存放新值,并将之前指向旧的值的那个地址改成指向新的值的地址。这就完成了一次赋值的操作。
在对应用类型进行赋值的时候: 首先,我们先从定义和赋初始值开始说起, 引用类型在被申明的时候,首先在堆中开辟一块内存空间,然后将其在堆中的地址赋值给 栈 中的一快空间,这块空间就是来放一个引用类型在堆中的地址的。然后我 对其进行重新赋值的时候,并不是将堆中的值给改变了,而是在堆中重新开辟了一块新的空间,然后将堆中的这个地址赋值给 之前这个变量在栈中开辟那块存堆中的地址的那个地址,所以之前的那个最先开辟在堆中的空间是不会被消除的,除非.Net FrameWork这个框架中的垃圾回收站自动帮我们完成释放内存的功能,但这是在程序运行结束后才会做,所以我们如果对引用类型的变量 的话,我们应该尽量减少对其的重新赋值,就想string这个引用类型一样,我们如果需要对字符串进行重复的赋值和处理,我们一般不会用string这个字符串来处理, 我们一般使用stringBuild 来处理字符串重新赋值的问题。

值类型:C#的所有值类型均隐式派生自System.ValueType;判断是否为值类型:Type.IsValueType
1.所有整形:sbyte(System.SByte)、short(System.Int16)、、int(System.Int32)、long(System.Int64)、byte(System.Byte)、ushort(System.UInt16)、uint(System.UInt32)、ulong(System.UInt64)、char(System.Char)。
2.所有浮点型:float (System.Single)、double(System.Double)。
3.高精度类型:decimal(System.Decimal)。
4.bool(System.Boolean);
5.结构体、枚举类型。

引用类型:
1.数组(派生于System.Array)
2.类:class(派生于System.Object)
3.接口:interface
4.委托类型:delegate(派生于System.Delegate)
5.字符串:string(System.String的别名)
6.Lambda表达式:C#的Lambda 表达式都使用 Lambda 运算符 =>,该运算符读为“goes to”。语法如下:形参列表=>函数体函数体多于一条语句的可用大括号括起。如下:
delegate int del(int i);
del myDelegate = x=>{return x*x;};
int j = myDelegate(5);//j=25

装箱:将值类型转换为引用类型 。装箱时,生成的是全新的引用对象,这会有时间损耗,也就是造成效率降低,尽量避免装箱。
拆箱:将引用类型转换为值类型。

Unity CanvasGroup 组件

Canvas Group可以用来控制一组不需要个别控制的UI元素的某些方面,CanvasGroup的属性会影响他所有children的GameObject

其中有四个选项:
-Alpha:这个选项很多组件都有,用处也是一样的,在美术中,这个叫做Alpha通道的东东是用来控制透明度的,他的值从0到1.0是完全透明,1是完全不透明;
-Interactable确认该组件是否接受输入,当他被设置为false时,交互功能将被禁用;
-Block Raycasts是否让该组件像collider一样接受射线检测?你需要在依赖于Canvas的图形射线检测者上唤醒射线检测方法。这个不会作用于Physics.Raycast;
-Ignore Parent Groups(忽略父级团)是否响应父级group的方法

细节:
Canvas Group的经典使用:
-在窗口的GameObject上添加一个CanvasGroup,通过控制它的Alpha值来淡入或淡出整个窗口;
-通过给父级GameObject添加一个CanvasGroup并设置它的Interactable值为false来制作一整套没有交互(灰色)的控制;
-通过将元素或元素的一个父级添加CanvasGroup并设置BlockRaycasts值为false来制作一个或多个不阻止鼠标事件的UI元素
应用:(重要的地方写大字)
结合后面两点或者1,3点,都可以实现很牛叉的功能
比如说游戏里某些情况某个按钮(或者其他UI)是不能点的,而另外一些情况可以点,这样就可以通过动态改变这个组件的BlocksRaycasts值以及Interactable来实现
再比如说游戏里点击某个按钮要让这个按钮不可点并逐渐消失掉,当然啦,也可以让别的东西消失啦,这就可以通过改变alpha值来实现

UGUI系统中Image与RawImage

UGUI系统中提供了2种显示图片的组件即Image与RawImage。本篇文章主要是介绍这2个组件含义与使用。
1.Image

(1)SourceImage:指定要显示的目标图片资源。需要注意的是,它只支持Sprite类型的图片,因此需要将目标图片资源的格式改成如下图所示的格式:

注:原文这里应有一张 Sprite 导入设置示意图,但当前仓库未保留对应图片。

(2)Color:设置Color属性值,会改变图片显示的色调。类似给图片开了某种颜色的“灯”照射该图片。

(3)Material:设定用于渲染图片的材质。
(4)RaycastTarget:决定是否接收射线碰撞检测。换句话说,就是是否能够成为事件监听目标。
(5)Image Type:用于设定图片的显示类型,如Simple/Sliced/Tiled/Filled。不同的显示类型会导致Sprite“填充”Image组件的方式不同。
a.Simple,此模式下如果Image控件大小与Sprite的不相同时,Sprite将会被拉伸到与Image控件一般大。

值得注意的是,如果勾选上PreserveAspect选项时,Sprite将会根据Sprite原宽高比例进行拉伸。

b.Sliced俗称九宫格,需要做成九宫格的图片一般都是当做背景用的(如很多游戏中都有黑色的背景)。经过九宫格处理的Sprite,在缩放过程中,会保持4个角的切片不做缩放,4个边的切片只完成拉伸,只有中间的切片做缩放操作。Fill Center选项,如果取消勾选,Image不能完整显示,只会显示切片的边缘图片。

c.Tiled俗称“地面砖平铺”,此种情况下,Sprite本身大小会保持不变,Sprite会像铺地面砖那样填充满整个Image控件。

d.Filled俗称“呈现方式”,一般设置此模式的Image,都是用做有“CD”效果的技能按钮等,让Image以一定地展现方式一部分一部分地完全展示出来。

属性
作用
Fill Method
指定填充呈现方式,选项有Horizontal[水平方向]、Vertical[竖直方向]、Radial90[1/4圆呈现]、Radial180[半圆呈现]、Radial360[整圆显现]
Fill Origin
指定填充显现操作的起点。
Fill Amount
指定了填充的进度。
Clock Wise
针对Radial90/180/190类型的填充显示方式,取消与勾选该项,会“翻转”填充显示“方向”
Preserve Aspect
根据Sprite宽高比,缩放Image

举例:设定Fill Method为Radial360,当改变FillAmount值,从0到1时,会发现图片会以圆形的方式从中间向外逐渐显示出来。

(6) SetNative Size:按下此按钮后,会发现Image控件的大小会变成与Sprite图片本身大小一致。

2.RawImage
RawImage功能上与Image组件类似,但属性并不完全相同。

属性 作用
Textture 指定要显示的图片,注意:图片类型可以是任何类型
Color 设置图片的主颜色
Material 设定Image控件的渲染材质
Raycast Target 决定是否可接收射线碰撞事件检测(取消勾选不会挡住下层UI事件)
UV Rect 可以让图片的一部分显示在RawImage组件中,x、y属性用于控制UV左右、上下偏移,W、H用于控制UV的重复次数。

UGUI sprite图九宫格设置和切割

很多时候美术给的背景图很小,需要程序进行放大(但要保持边角不模糊),这时候九宫格就有用了~~~

例如下图放大10倍,如果不用九宫格图会变得很模糊(你可以试下吧transform 的scale都设为10,效果太美。。。。。)

注:原文这里应有一张放大对比示意图,但当前仓库未保留对应图片。

不扯淡了,开搞

一、九宫格
九宫格的主要目的是处理图片拉伸效果,我们知道图片一旦被拉伸,它就会出现形变、模糊等问题,但是,有的图片它的某一些部分又是允许被拉伸的。例如,一个UI背景框,它的中间部分几乎是一个纯色,允许被拉伸(纯色被拉伸不会发生质量问题),但是,边缘的4个角可能有一些特殊花纹或者倒角不允许被任意拉伸,这个时候就可以使用九宫格,来使4个角不进行拉伸放大,只让中间部分进行拉伸放大,达到将一个小框拉大成一个大背景框,既节省了资源,又不会降低图片质量。
原理:

注:原文这里应有一张九宫格 9 宫切分示意图,但当前仓库未保留对应图片。

把纹理用4条线分割成9部分(如上图),通过观察可以发现,5是最规则的形状(矩形),其次是2,6,8,4(矩形,但是和四个角有公共边),最后是四个角1,3,9,7(圆角矩形)
规则的图形在拉伸之后的效果是比较好的,如果是不规则的图形,则会在拉伸之后变形!

根据上图做拉伸制定规则:
(1)保证四个角1,3,9,7不做任何拉伸
(2)与四个角有公共边的四个矩形2,6,8,4做单向拉伸,即保证与四个角的公共边不拉伸,例如2,8只进行横向拉伸,4,6只进行纵向拉伸
(3)中间部分5做双向拉伸,即横向,纵向同时拉伸
unity 中设置九宫格:
1.选中图片,在Inspector面板里将Texture Type改为Sprite,点击Sprite Editor按钮,通过拖动绿色的点来设置九宫格大小。
2.将Image组件的Image Type改为Sliced。

二:图片切割
图片切割使用场景有:1.美术将多张图打包到同一张图里,例如UI都放在一张图里。2.做类型地砖的时候,美术给的图是9x9的大图,程序的单元格却是1x1的,这时候就需要把图片切成九张大小一样图片并在程序中拼接成一张大图。3.UGUI要做帧动画时。
unity 中设置:
1.选中图片,在Inspector面板里将Texture Type改为Sprite或Advance。
2.图片模式sprite修改为multiple模式。
3.进入图片编辑器,点击左上角的Slice按钮,其中的Type有三种,第一种时自动的,就是不切割。
第二种是按单元格切割 就是每个单元格大小一样,如图:

注:原文这里应有一张按单元格切割的示意图,但当前仓库未保留对应图片。

通过对单元格的name的命名规则,我们可以再程序中拼接出整张大图,例如切成三行三列的图,可以命名为行数_列数或者行数*总的列数+当前列数,
取值的时候通过便利行和列就可以取到并拼接成正张图。
第三种,按行或列的count切割,适用于美术的小的ui或者帧动画,如图

注:原文这里应有一张按行或列 count 切割的示意图,但当前仓库未保留对应图片。

4.每个切割的小图可以单独设置九宫格进行拉伸。
5.使用的时候需要将image的imageType设置成Slice

Unity后台计时器不运行问题

问题:最近在做一个倒计时的功能,发现倒计时在程序切换到后台是会停止计时,发现unity 安卓后台运行时,计时器不在运行,再次切回App时从切到后台的时间点计时,例如切到后台时位10,后台切回app还是从10开始

,原因是unity在安卓后台环境下,Update和协程不会执行。
解决:
1.在Update和协程中,计算计时器的时间用当前的Time.realtimeSinceStartup减去上一次计时的Time.realtimeSinceStartup,而不是直接减去固定数值,例如减去1
2.使用unity自带的方法void OnApplicationFocus(bool hasFocus),该方法会在App进入后台和进入程序的时候调用,当进入程序时hasFocus = true,否则hasFocus=false;(参考Start()方法)

3.使用unity自带的方法

void OnApplicationPause(bool pauseStatus)
该方法会在App暂停和停止暂停的时候调用,当暂停时pauseStatus= true,否则pauseStatus=false;

OnApplicationFocus is called when the application loses or gains focus. Alt-tabbing or Cmd-tabbing can take focus away from the Unity application to another desktop application. This causes the GameObjects to receive an OnApplicationFocus call with the argument set to false. When the user switches back to the Unity application, the GameObjects receive an OnApplicationFocus call with the argument set to true.
hasFocus True if the GameObjects have focus, else False.

代码如下:只需要监听OnApplicationEnterFocus和OnApplicationQuitFocus事件即可,FocusContinueTime为进入后台的总时间,计算倒计时时只要减掉这个数就可以了

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
67
68
69
70
71
72
73
74
public static float FocusContinueTime
{
get
{
return focusContinueTime;
}
}

public static float PauseContinueTime
{
get
{
return pauseContinueTime;
}
}
#endregion

private static float preFocusTime = 0;
private static float prePauseTime = 0;
private static float focusContinueTime = 0;
private static float pauseContinueTime = 0;

void Awake()
{
this.gameObject.name = "ApplicationEvent";
prePauseTime = Time.realtimeSinceStartup;
preFocusTime = Time.realtimeSinceStartup;
DontDestroyOnLoad(this);
}
public void Init()
{
}
public void Release()
{
}
void OnApplicationFocus(bool hasFocus)
{
if (hasFocus)
{
focusContinueTime = Time.realtimeSinceStartup - preFocusTime;
if (OnApplicationEnterFocus != null)
{
OnApplicationEnterFocus();
}
}
else
{
preFocusTime = Time.realtimeSinceStartup;
if (OnApplicationQuitFocus != null)
{
OnApplicationQuitFocus();
}
}
}

void OnApplicationPause(bool pauseStatus)
{
if (pauseStatus)
{
prePauseTime = Time.realtimeSinceStartup;
if (OnApplicationEnterPause != null)
{
OnApplicationEnterPause();
}
}
else
{
pauseContinueTime = Time.realtimeSinceStartup - prePauseTime;
if (OnApplicationQuitPause != null)
{
OnApplicationQuitPause();
}
}
}

Unity 小技巧总结

定时重复处理,比如, 启动0.5秒后每隔1秒执行一次 DoSomeThing 函数:
1.用InvokeRepeating 函数实现
void Start() { InvokeRepeating(“DoSomeThing”, 0.5, 1.0); }
CancelInvoke(“你调用的方法”); 停止InvokeRepeating

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void Start()
{
StartCoroutine("Repeating");开始调用
StopCoroutine("Repeating");停止调用
}
IEnumerator Repeating()
{
yield return new WaitForSeconds(0.5f);

while (true)
{
yield return new WaitForSeconds(1f);
DoSomeThing();
}
}

尽量避免每帧处理,可以每隔几帧处理一次
比如:
void Update() { DoSomeThing(); }

可改为每5帧处理一次:
void Update() { if(Time.frameCount % 5 == 0) { DoSomeThing(); } }

主动回收垃圾
给某个 GameObject 绑上以下的代码:
void Update() { if(Time.frameCount % 50 == 0) { System.GC.Collect(); } }

不要使用SendMessage之类的方法,他比直接调用方法慢了100倍,你可以直接调用或通过C#的委托来实现。

关于渲染队列和Batch的非官方经验总结是,一帧的渲染队列的生成,依次决定于每个渲染物体的:
Shader的RenderType tag,
Renderer.SortingLayerID,
Renderer.SortingOrder,
Material.renderQueue(默认值为Shader里的”Queue”),
Transform.z(ViewSpace)(默认为按z值从前到后,但当Queue是“Transparent”的时候,按z值从后到前)。
这个渲染队列决定了之后(可能有dirty flag的机制?)渲染器再依次遍历这个渲染队列,“同一种”材质的渲染物体合到一个Batch里。

Unity渲染路径(Rendering Path)种类
概述
开发者可以在Unity工程的PlayerSettings设置对渲染路径进行3选1:
Deferred Lighting,延迟光照路径。3者中最高质量地还原光照阴影。光照性能只与最终像素数目有关,光源数量再多都不会影响性能。
Forward Rendering,顺序渲染路径。能发挥出Shader全部特性的渲染路径,当然也就支持像素级光照。最常用、功能最自由,性能与光源数目*受光照物体数目有关,具体性能视乎其具体使用到的Shader的复杂度。
Vertex Lit,顶点光照路径。顶点级光照。性能最高、兼容性最强、支持特性最少、品质最差。

unity 点击2D物体
1.给物体添加对应的2D碰撞体,例如Box Collider 2D;
2.Update检测鼠标是否点下;
3.如果鼠标点下,创建一条射线,通过Physics2D.Raycast获取点击时鼠标处的对象;

1
2
3
4
5
6
7
8
9
10
11
12
13
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Vector3 mousePos = Input.mousePosition;
Ray ray = Camera.main.ScreenPointToRay(mousePos);
RaycastHit2D hit = Physics2D.Raycast(ray.origin, ray.direction);
if (hit.collider != null)
{
Debug.Log(hit.collider.name);
}
}
}

从游戏开始到当前帧经历的时间 Time.realtimeSinceStartup;
从游戏开始到当前帧经历的帧总数 Time.frameCount;

计算FPS:

1
2
3
4
5
6
7
8
9
10
11
12
_countFrame += 1
void Update()
{
float time = Time.realtimeSinceStartup;
if (time - _lastTime >= 1.0f)
{
_fps = (float)(_countFrame/(time - _lastTime));
Debug.Log("FPS: " + _countFrame);
_countFrame = 0;
_lastTime = time;
}
}

得到当前帧鼠标的位移量:Input.GetAxis()

1
2
3
4
5
6
7
8
9
void Update()
{
if (Input.GetMouseButton(0) && _isRotation){
float x = Input.GetAxis("Mouse X")*10;
float y = Input.GetAxis("Mouse Y")*10;
Debug.Log("x: " + x + " y: " + y);

}
}

对象跟随鼠标运动:

1
2
3
4
5
6
7
8
Vector3 wp = Camera.main.WorldToScreenPoint(transform.position);
Vector3 sp = Input.mousePosition;
sp.z = wp.z;

if (Input.GetMouseButton(0))
{
transform.position = Camera.main.ScreenToWorldPoint(sp);
}

协同程序的开启与终止:
在Unity3D中,使用MonoBehaviour.StartCoroutine方法即可开启一个协同程序,也就是说该方法必须在MonoBehaviour或继承于MonoBehaviour的类中调用。
在Unity3D中,使用StartCoroutine(string methodName)和StartCoroutine(IEnumerator routine)都可以开启一个线程。区别在于使用字符串作为参数可以开启线程并在线程结束前终止线程,相反使用IEnumerator 作为参数只能等待线程的结束而不能随时终止(除非使用StopAllCoroutines()方法);另外使用字符串作为参数时,开启线程时最多只能传递一个参数,并且性能消耗会更大一点,而使用IEnumerator 作为参数则没有这个限制。
在Unity3D中,使用StopCoroutine(string methodName)来终止一个协同程序,使用StopAllCoroutines()来终止所有可以终止的协同程序,但这两个方法都只能终止该MonoBehaviour中的协同程序。
还有一种方法可以终止协同程序,即将协同程序所在gameobject的active属性设置为false,当再次设置active为ture时,协同程序并不会再开启;如是将协同程序所在脚本的enabled设置为false则不会生效。这是因为协同程序被开启后作为一个线程在运行,而MonoBehaviour也是一个线程,他们成为互不干扰的模块,除非代码中用调用,他们共同作用于同一个对象,只有当对象不可见才能同时终止这两个线程。然而,为了管理我们额外开启的线程,Unity3D将协同程序的调用放在了MonoBehaviour中,这样我们在编程时就可以方便的调用指定脚本中的协同程序,而不是无法去管理,特别是对于只根据方法名来判断线程的方式在多人开发中很容易出错,这样的设计保证了对象、脚本的条理化管理,并防止了重名。

UGUI空格导致换行:将空格转换为全角空格,半角空格会使Text自动换行。

实现text显示不同颜色的字,如我是程序员小白,其中程序员为红色,其余自为黑色。使用透明颜色标签<color=#0000ff00>111,其中的111不会显示在Text上但会占据格子,作用类似于空格,通过这个标签的颜色值可以改变相应的字体颜色。

unity 编辑状态下暂停:

1.Debug.Break();或者UnityEditor.EditorApplication.isPaused = false;

2.使用编辑器类:EditorApplication.ExecuteMenuItem(“Edit/Play”);
3.UnityEditor.EditorApplication.isPlaying = false;

问题:
unity中通过Application.dataPath + “/Resources/Config/xxx”)作为将要读取文件的路径时,在编辑器上正常运行,但在打包后却无法正确读取文件
解决:
这是因为不同平台Application.dataPath所指定的路径不一样,editor模式下代表的是Asset,打包后对应的路径是打包时生成的文件夹“xxx_data”,这是需要把对应的文件拷贝到“xxx_data”文件夹下,例如打包后文件路径“E:\Unity\TowerDefense\Build\塔防_Data\Resources\Config”对应编辑器下的“E:\Unity\TowerDefense\Assets\Resources\Config”。

问题:
UNITY 无法导入视频解决方法
解决
1.unity 导入视频时无法转换成movieTexture, 这时候unity会提示影片导入失败,这时候需要安转QuickTime Player,这个软件可以把unity的影片转换成unity能用的材质。
2.QuickTime 无法正常运行时并且提示 “please install apple application support”时,打开下载的QuickTime安装包,选择安装“apple application support.msi”,安装选择“修复”。
3.unity支持的影片格式有.mov .mpg .mpeg .mp4 .avi .asf。
4.成功导入的影片会生成一个对应的Movie Audio,这个文件是该影片的声音文件。

问题:Scroll Rect中滑动事件是通过EventTrigger响应,所以当item也是通过EventTrigger响应点击事件时,就会遮挡住后面的Scroll Rect的滑动事件。
UGUI的ScrollRect组件会和放在它上面的button或者toggle等组件有事件响应的冲突,具体体现为上面的组件会遮挡下面的响应,在button或者toggle等组件上出现只能点击不能滑动的效果,要想滑动必须点旁边才行。
解决:
(1).Button: Button.onClick.AddListener(delegate () {ButtonOnClick(Button.gameObject); }); onClick回调函数 是继承自UnityEngine.Events.UnityAction.委托。由于同一个点击事件通过不同的事件模块去响应,很好的处理了item挡住Scroll Rect 事件的问题。委托格式为不带任何参数和返回的函数。
这里的Button是指你要按下的那个按钮,ButtonOnClick是该按钮按下时要触发的事件。
第二种解决方案是:不用按钮,1.新建一个继承MonoBehaviour和IPointerClickHandler接口的脚本(例如ClickObject),2.这个脚本实现public void OnPointerClick(PointerEventData eventData)方法:3.创建一个名为Panel_IPointer的空对象,这个空对象就是你的按钮。并且将ClickObject脚本附加到对象上。这时候点击这个对象就会调用OnPointerClick方法。
第三种:创建一个Image(例如名为Btn),Btn对象添加EventTrigger组件,” Add New” -> 选择” PointerClick”。将Btn对象拖拽到触发者位置。然后点击”No Function”选择我们要触发的OnTestClick事件。
(2)toggle:使用isOn的方法,例如if(toggle.ison){debug.log(“click”)};