游戏对象GameObject

GameObject

GameObject成员变量

组件是否激活

Debug.Log(gameObject.activeSelf);

查看物体是否静态

Debug.Log(gameObject.isStatic);

查看物体所在的层级Layer

Debug.Log(gameObject.layer);

查看物体的标签Tag

Debug.Log(gameObject.tag);

获取物体的transform

音效组件AudioSources

音效系统

Unity支持的音频格式

  • wav
  • mp3
  • ogg
  • aiff

音频文件属性设置

  • Force To Mono 多声道转单声道
    • Normalize 转换为单声道时 混合过程中被标准化
  • Load In Background在后台加载 不阻塞主线程
  • Ambisonic 立体混响声 适合360度视频和xr应用程序 如果音频文件包含立体混响声编码 请启用此选项
  • LoadType加载类型 可根据不同的平台单独设置
    • Decompress On Load 不压缩的形式存在内存 加载快 但是占用内存较高 适用小音效
    • Compress in memory 压缩形式存在内存 加载慢 内存小 适用于较大的音效文件
    • Streaming 以流形式存在 使用时解码 占用内存小 cpu消耗高 性能换内存
  • Preload Audio Data 预加载音频 勾选后进入场景就加载 不勾选 第一次使用才加载
  • Compression Format 压缩方式
    • PCM音频以最高质量存储
    • Vorbis相对PCM压缩的更小 根据质量决定
    • ADPCM 包含噪音,会被多次播放的声音,如碰撞声
  • Quality音频质量 确定要应用于压缩剪辑的压缩量 不适用于PCM/ADPCM/HEVAG格式
  • Sample Rate Setting 压缩个是允许自动优化或说动降低采样率
    • Preserve Sample Rate 此设置可保持采样率不变(默认值)
    • Optimize Sample Rate 此设置根据分析的最高频率内容自动优化采样率
    • Override Sample Rate 手动设置采样率

AudioSource

  • 需要挂载AudioListener才能听到声音
  • 一个对象可以挂载多个AudioSource
    • 使用时需要注意 如果挂载多个AudioSource 那一定要自己管理他们 控制他们的播放停止 不然我们没有办法准确的获取谁是谁

AudioSource组件面板属性

  • AudioClip 声音剪辑文件(音频文件)
  • Putput 默认将直接输出到场景中的音频监听器 可以更改为输出到混音器
  • Mute 静音开关
  • Bypass Effect开关滤波器效果
  • Bypass Listener Effects 快速开关所有监听器
  • Bypass Reverb Zones 快速开关所有混响区
  • Play On Awake 对象创建时就播放音乐
  • Loop循环
  • Priority优先级
  • Volume音量大小
  • Pitch音高
  • Stereo Pan 2D声音立体声位置 相当于左右声道
  • Spatial Blend 音效受3D空间的影响程度
  • Reverb Zone Mix 到混响区的信号输出量
  • 3D Sound Setttings 和Spatial Blend参数成正比应用
    • Doppler Level 多普勒效果等级
    • Spread 扩散角度设置为3D立体声还是多声道
    • Volume Rolloff 声音衰减速度
      • Logarithmic Rolloff 靠近音源时声音很大 离开时 降低的很快
      • Linear Rolloff 与音频源距离越远 听到的声音越小
      • Custom Rolloff 音频源的音频效果是根据曲线图的设置变化的
    • Min/Max Distance
      • 最小距离内 声音保持最大的响度
      • 最大距离外 声音开始减弱

代码控制音频源

获取AudioSource

//获取AudioSource
audioSource = GetComponent<AudioSource>();

控制播放暂停

audioSource.Play();//播放
audioSource.PlayDelayed(5);//5秒后播放 单位秒
audioSource.Stop();//停止
audioSource.Pause();//暂停
//停止暂停 和暂停后play是一个效果
audioSource.UnPause();

如何检测音效播放完毕

if(audioSource.isPlaying){
    Print("播放中");
}
else{
     Print("播放结束");
}

使用一个AudioSource控制播放不同的音效

public AudioClip clip;
audioSource.clip = clip;
audioSource.Play();

麦克风输入相关

获取设备麦克风信息

String[] strs = Microphone.devices;

开始录制

//参数1:设备名 如果使用默认设备传null
//参数2:超过录制长度后是否重新入职 一般faslse
//参数3:录制长度 单位秒
//参数4:采样率
//返回一个AudioClip
AudioClip clip = Microphone.Start(null,false,10,44100);

结束录制

//参数:设备名
Microphone.End(null);

AudioClip

获取音频数据

//规则 用于存储数组数据的长度 是用 声道数*剪辑长度
float[] f = new float[clip.channels * clip.samples];
clip.GetData(f,0);

协变和逆变

协变和逆变

什么是协变和逆变

  • 协变
    • 因为 里氏替换原则 父类容器可以装子类对象
    • 所以子类转换成父类 例如 string 转换成object
    • 这样的转换 是协变 是和谐的转换
  • 逆变
    • 因为 里氏替换原则 父类容器可以装子类对象 但是子类容器不可以装父类对象
    • 所以父类转换成子类 比如object转换成string
    • 这样的转换 是逆变 是不和谐的

协变和逆变的用法

  • 协变和逆变是用来修饰泛型的
  • 使用关键字out(协变)和in(逆变)
  • 用于在泛型中 修饰 泛型字母的
  • 只有泛型接口和泛型委托中可以使用

out修饰的泛型 使其只能作为返回值

//T只能作为返回值
delegate T TestOut<out T>();
delegate T TestOut<out T>(T value);//报错

//在泛型接口中使用
interfacet Test<out T>{
    T TestFun1();
    void TestFun2(T value);//报错
}

in修饰的泛型 使其只能作为参数

//T只能作为参数
delegate void TestIn<in T>(T value);
delegate T TestOIn<in T>(T value);//报错
//在泛型接口中使用
interfacet Test<in T>{
    void TestFun2(T value);
    T TestFun1();//报错
}

通过里氏替换原则 理解协变

class Fater{}
class Son:Fater{}

delegate T TestOut<out T>();
delegate T TestNoOut<T>();

TestNoOut<Son> tnos = ()=>{return new Son();}
TestNoOut<Fater> tnof = tnos;//报错

//out修饰 会去判断父子关系
TestOut<Son> tos = ()=>{return new Son();}
TestOut<Fater> tof = tos;
//返回值为父类的方法 将子类作为返回值返回
//父类装子类 协变
Fater f = tof();

//返回值为子类的方法 不可以将父类作为返回值返回
tos = tof//报错

通过里氏替换原则 理解逆变

class Fater{}
class Son:Fater{}

delegate void TestIn<in T>(T value);
delegate void TestNoIn<T>(T value);

TestNoIn<Fater> tnif = (value)=>{};
TestNoIn<Son> tnis = tnif;//报错

//in修饰 会去判断父子关系
TestIn<Fater> tif = (value)=>{};
TestIn<Son> tis = tif;
//将子类的对象  传入了参数为父类的方法里
//子类转父类 逆变
tis(new sun());

//父类对象 不可以传入 子类作为参数的方法里
tif = tis;//报错

协变和逆变有什么作用

  • 协变(out)和逆变(in)是修饰 泛型的
  • 协变 限定泛型只能作为 返回值
  • 逆变 限定泛型只能作为 参数
  • 协变:父类泛型委托容器 装 子类泛型泛型容器
    • 返回值是子类的委托 加到 返回值是为父类的委托中
  • 逆变:子类泛型泛型容器 装 父类泛型委托容器
    • 参数是父类的委托 加到 参数为子类的委托中

协程

协同程序

  • Unity是支持多线程的 但是新开的线程无法访问Unity相关对象的内容
  • Unity中新开的线程要记得关闭
public void Test(){
    Debug.log(transform.position);//报错不可以使用
}
Thread t = new Thread(Test);
t.Start();
  • 携程不是多线程
  • 他将代码分时执行 不卡主线程
  • 一般用于一些异步操作

携程的使用

  • 继承Mono的类 都可以开启携程
  • 协程返回 IEnumerator类型 及其子类
  • 函数中通过 yield return 返回值 进行返回
IEnumerator MyCoroutine(int i,string str){
	print(i);
	yield return new WaitForSeconds(5f);//五秒钟后 执行
    print(str);
}

开启携程

StartCoroutine(MyCoroutine(123,"123"));

关闭携程

//关闭所有携程
StopAllCoroutines();
//关闭指定携程
Coroutine c1 = StartCoroutine(MyCoroutine(123,"123"));
StopAllCoroutines(c1);
//也可以传入字符串  不推荐
StopAllCoroutines("MyCoroutine");

携程的返回值

yield return null; // 下一帧再执行后续代码
yield return 6;//(任意数字) 下一帧再执行后续代码
yield break; //直接结束该协程的后续操作
yield return asyncOperation;//等异步操作结束后再执行后续代码
yield return StartCoroution(/*某个协程*/);//等待某个协程执行完毕后再执行后续代码
yield return WWW();//等待WWW操作完成后再执行后续代码
yield return new WaitForEndOfFrame();//等待帧结束,等待直到所有的摄像机和GUI被渲染完成后,在该帧显示在屏幕之前执行
yield return new WaitForSeconds(0.3f);//等待0.3秒,一段指定的时间延迟之后继续执行,在所有的Update函数完成调用的那一帧之后(这里的时间会受到Time.timeScale的影响);
yield return new WaitForSecondsRealtime(0.3f);//等待0.3秒,一段指定的时间延迟之后继续执行,在所有的Update函数完成调用的那一帧之后(这里的时间不受到Time.timeScale的影响);
yield return WaitForFixedUpdate();//等待下一次FixedUpdate开始时再执行后续代码
yield return new WaitUntil()//将协同执行直到 当输入的参数(或者委托)为true的时候....如:yield return new WaitUntil(() => frame >= 10);
yield return new WaitWhile()//将协同执行直到 当输入的参数(或者委托)为false的时候.... 如:yield return new WaitWhile(() => frame < 10);

携程受对象和组件失活的影响

  • 携程开启后 组件和物体销毁 携程不执行
  • 物体失活携程不执行 组件失活 携程执行

携程的本质

  • 携程的本质是一个C#的迭代器方法
  • 携程的本体就是一个能够中间暂停返回的函数
//1.协程函数本体
//如果我们不通过 开启协程方法执行协程 
//Unity的协程调度器是不会帮助我们管理协程函数的
IEnumerator ie = Test();
//但是我们可以自己执行迭代器函数内容
ie.MoveNext();//会执行函数中内容遇到 yield return为止的逻辑
print(ie.Current);//得到 yield return 返回的内容
  • 协程调度器 相当于是把一个协程函数(迭代器)放入Unity的协程调度器中帮助我们管理进行执行
  • 根据yield return 返回的内容 决定了下一次在何时继续执行迭代器函数中的"下一部分

向量

  • 向量可以代表位置 也可以代表方向
  • 三维向量Vector3
  • 二维向量Vector2

两个点决定一个向量

Vector3 A = new Vector3(1, 2, 3);
Vector3 B = new Vector3(5, 1, 5);
//B相对于A的向量
Vector3 AB = B - A;
//A相对于B的向量
Vector3 BA = A - B;

向量的模长

两个点之间的长度

Vector3 C = new Vector3(5, 6, 7);
//原点到C的长度
print(C.magnitude);
//等于Vector3.Distance(Vector.zero,c);

单位向量

//Vector3中提供了获取单位向量的成员属性
AB.normalized
//等同于AB / AB.magnitude

向量点乘

  • 可通过点乘计算出|A|的值
  • 点乘的计算公式
    • 向量A(Xa,Ya,Za)
    • 向量B(Xb,Yb,Zb)
    • AB = Xa*Xb+Ya*Yb+Za*Zb
  • 点乘结果>0 两个向量的夹角为锐角
  • 点乘结果=0 两个向量夹角为直角
  • 点乘结果<0 两个向量夹角为钝角
float dotResult = Vector3.Dot(B.transform.position, target.position - this.transform.position);

通过点乘推导出夹角

使用单位向量计算点乘

延时函数Invoke

延时函数

  • 会延时执行的函数
  • 可以自己设定要执行的函数和延时的时间
  • 是Mno基类中的方法
  • 如果脚本依附的对象失活 延时函数依然可以执行
  • 如果被移除 则无法执行
Invoke("函数名",5);//单位秒 不能传入参数

延时重复执行的函数

InvokeRepeating("函数名",5,2);//第一次延迟5秒执行 然后每隔2秒执行一次

取消延时函数的执行

CancelInvoke();//取消所有的
CancelInvoke("函数名");//取消指定的延时函数方法

判断是否有延时函数

IsInvoking();//只要有就返回true
IsInvoking("函数名");//是否有指定的延时函数

相机(Camrea)

Camera

Camera 参数

  • Clear Flags
    • skybox天空盒
    • Solid Color颜色填充
    • Depth only 只渲染该层,背景透明
    • Dont Clear 不移除,覆盖渲染
  • Culling Mask 选择性渲染部分层级
    • 可以指定只渲染对应层级的对象(UI、Player)
  • Projection透视方式
    • 透视模式
      • FOV Axis 视口计算方式(横向和纵向)
      • Field of View 视口大小
      • Physical Camera(物理摄像机)勾选后模拟真实世界中的摄像机
        • Focal Length 焦距
        • Sensor Type 传感器类型
        • Sensor Size传感器尺寸
        • Lens Shift 透镜移位
        • Gate Fit 闸门配合
    • 正交模式(一般做2D游戏)
      • Size 摄影范围
  • Clipping Planes(裁剪平面距离)
    • 摄像机只会渲染一定范围内的物体
    • 超出范围的物体会被裁剪
    • Near(最近)Far(最远)
  • Depth 渲染顺序上的深度
    • 层级较高的摄像机会后被渲染 可以理解为是图层一样
    • 后被渲染的摄像机画面 会挡住先渲染的画面
  • Target Texture 渲染纹理
    • 可以把摄像机画面 渲染到一张图上 可以用于制作小地图
    • 在Project右键创建Render Texture
  • Occlusion Culling 是否启用剔除遮挡
    • 默认开启的 被遮挡的物体不渲染 可以节约一定的性能
  • 不常用功能
    • Viewport Rect 视口范围
      • 屏幕上绘制该摄像机视图的位置和大小
        • 主要用于双摄像机游戏 0~1相当于宽高百分比
    • Allow HDR 是否允许高动态范围渲染
    • Allow MSAA 是否允许抗锯齿
    • Allow Dynamic Resolution 是否允许动态分辨率
    • Target Display 用于哪个显示器 主要用来开发多个屏幕的平台游戏
    • Target Eye 在哪个眼睛上渲染 VR游戏

Camera代码相关

获取摄像机

//得到主摄像机 摄像机的tag一定要是maincamera
Camera c = Camera.main;
//获取摄像机的数量
Camera.allCamerasCount;
//得到所有的摄像机
Camera[] allCamrea = Camera.allCameras;

渲染相关委托

//摄像机剔除前处理的委托函数
//有一个参数camrea
Camera.onPreCull += (c)=>{

};
//摄像机 渲染前处理的委托
Camera.onPreRender +=(c)=>{
        
};
//摄像机 渲染后 处理的委托
Camera.onPostRender +=(c)=>{
        
};

重要成员

界面上的参数 都可以在Camera中获取到

位移(Transform)

计算位移量

路程=方向*时间*速度

方式一:自己计算

自己当前的位置+向量(方向)*速度*每一帧需要的时间 = 1秒移动1

transform.position = transform.position + transform.forward(本地坐标向前 对应世界坐标的向量) * 1 * Time.deltaTime;
//transform.forward(本地坐标向前 对应世界坐标的向量)

方法二:API移动

//参数一:位移多少 方向*速度*时间
//参数二:表示相对坐标系 默认参数是 物体自己的坐标系Space.Self 可选世界坐标Space.World
transform.Translate(Vector3.forward*1*Time.deltaTime);
//也可以使用世界坐标系 使用transform.forward作为向量
transform.Translate(transform.forward*1*Time.deltaTime,Space.World);

角度和旋转

  • 和position一样也有相对世界坐标的角度和相对父对象的角度
  • transform.rotation的值是一个四元数 面板上显示的是欧拉角可通过transform.eulerAngles参数获取或者修改
  • 和设置位置一样不可以单独设置xyz transform.eulerAngles.x = 50
  • 通过localEulerAngles获取的角度不会是负数虽然面板显示负数,只能获得0~360之间的数

修改角度(欧拉角)

//世界坐标角度
transform.eulerAngles = new Vector3(0,1,0);
//相对父对象角度
transform.localEulerAngles = new Vector3(0,1,0);

通过API旋转

位运算符

位运算符

  • 位运算符主要用数值类型进行计算的
  • 将数值转换为2进制 再进行位运算
  • 如果一个数的位数和另一个数不一样 则补0
//对位计算    &有0则0  |有1则1   异或^相同为0 不同为1
int a = 1; //001
int b = 5; //101
int c = a & b;//结果:001   1

//补0
a = 3; //  011
b = 19;//10011
c = a & b;//结果00011  3

位取反

int a = 5;//0101
int b = ~a; //b=-6
//完整的int 2进制 第一位是符号位
//0000 0000 0000 0000 0000 0000 0000 0101
//取反后
//1111 1111 1111 1111 1111 1111 1111 1010

坐移« 和 右移»

委托和事件

委托和事件

委托是什么

  • 委托 函数(方法)的容器
  • 用来 存储、传递函数(方法)
  • 委托的本质是一个类 用来定义函数(方法)的类型(返回值和参数的类型)
  • 不同的 函数(方法)必须对应各自"格式"一致的委托
  • 委托常用于类的成员或作为函数的参数使用
  • 委托变量可以存储多个函数

委托基本语法

  • 关键字:delegate
  • 语法:访问修饰符 delegate 返回值 委托名(参数列表);
  • 可以声明在namespace中或class中 一般写在namespace中
  • 委托的访问修饰符 默认为public
  • 不可以有同名委托(不存在重载这个概念)

定义自定义委托

无参无返回值

//声明一个 可以用来存储无参无返回值函数的容器
public delegate void MyFun();
//使用委托
MyFun f1 = new MyFun();
//另一种写法
//My Fun f1 = ()=>{Console.WirteLin("我没有参数和返回值")};  
f1 += ()=>{Console.WirteLin("我没有参数和返回值")};

//委托的调用
f1();//方法1
f1.Invoke();//方法2

有参数 有返回值

//声明一个 可以用来存储无参无返回值函数的容器
public delegate int MyFun(int value);
//使用委托
MyFun f2 = new MyFun((value)=>{return value+1});
int a = f2(3);
Console.WriteLine(a);//4

委托可以使用复合运算符

析构函数

析构函数

  • 当引用类型的堆内存被回收时,会调用该函数
  • 对于需要手动管理内存的语言(例如c++),需要再析构函数中做一些内存回收处理
  • C#中存在自动垃圾回收机制GC 所以再C#中几步会使用析构函数 除非需要在某一个对象被垃圾回收时 做一些特殊的处理
  • Unity开发中几乎不会使用
//基本语法
//~类名(){}
~Person(){
    
}

物理材质

物理材质

创建物理材质

Project面板->右键Create->Physic Material

物理材质参数

  • Dynamic Friction 在移动时使用的摩擦力 通常在0到1之间
  • Static Friction对象在静止时的摩擦力 通常在0到1之间
  • Bounciness 表面弹性如何 通常在0到1之间
  • Friction Combine 两个碰撞对象的摩擦力的组合方式
    • Average对两个摩擦值求平均值
    • Minimum 使用两个值中的最小值
    • Maximum使用两个值中的最大值
    • Multiply 两个摩擦值相乘
  • unce Combine 两个对象碰撞弹性的组合方式 模式和Friction Combine相同

万物之父Object

object万物之父

  • object是所有类型的基类
  • object是引用类型
  • 可以用来传 不确定类型的参数 通过is和as 判断和转换
  • 存在装箱拆箱

装箱和拆箱

  • 装箱 就是把值类型存入引用类型中(值类型转引用类型)
  • 拆箱 就是把值类型从引用类型中取出来(引用类型转值类型)
//object是引用类型
object a = 3;//装箱
int b = (int)a;//拆箱

object中的静态方法

判断连个对象是否相等的方法Equals

  • 不管值类型还是引用类型 都会按照左侧对象Equals方法的规则来进行比较
  • 返回值是一个bool值
  • 引用类型默认比较的是内存地址是否相同
Object.Equals(1,1);

获取对象类型GetType

  • 通过反射获取类型
  • 主要就是在运行时获取对象的类型Type
Test t = new Test();
//因为所有的类型都继承自object 所以可以使用GetType();
Type type = t.GetType();

克隆方法MemberwiseClone

数学公共类Mathf

Mathf公共类

  • Math是C#中封装好的用于计算的工具类 位于System命名空间中
  • Mathf是Unity中封装好的 用于数学计算的工具结构体 位于UnityEngine命名空间中
  • 在Unity开发中 一般使用Mathf

Mathf中的常用方法

Π

Mathf.PI

取绝对值 abs

Mathf.Abs(-5);

向上取整

Mathf.CeilToInt(1.3f);//2

向下取整

Mathf.FloorToInt(1.8f);//1

钳制函数 Clamp

Mathf.Clamp(value,min,max);
//比最小值小 返回最小值 比最大值大 返回最大值 

最大值

Mathf.Max(1,2,3); //3

最小值

Mathf.Min(1,2,3);//1

一个数的n次幂

Mathf.Pow(3,2);//9

四舍五入

Mathf.RoundToInt(1.4f);//1
Mathf.RoundToInt(1.5f);//2

返回一个数的平方根 Sqrt

Mathf.Sqrt(4);//2

判断一个数是2的几次幂

Mathf.IsPowerOfTwo(256);//true

判断正负数

Mathf.Sign(-1);//正数返回1 负数返回-1

插值计算

Mathf.Lerp(0,10,0.5f); //0.5

弧度角度转换

//Unity默认使用弧度作为单位
//弧度转角度
float rad = 1;
float anger = rad * Mathf.Rad2Deg;
//角度转弧度
anger = 1;
rad = anger * Mathf.Deg2Rad;

三角函数

  • 正弦函数(sin)

特性

特性

特性是什么

  • 特性是一种允许我们向程序的程序集添加元数据的语言结构
  • 它是用于保存程序结构信息的某种特殊类型的类
  • 特性提供功能强大的方法以将声明信息与C#代码(类型、方法、属性等)相关联
  • 特性与程序实体关联后,即可在运行时使用反射查询特性信息
  • 特性的目的是告诉编译器把程序结构的某族元素嵌入程序集中
  • 它可以放置在几乎所有的声明中(类、变量、函数等)
  • 特性的本质是个类 我们可以利用特性类为元数据添加额外信息 之后可以通过反射来获取这些信息

自定义特性

需要继承特性基类Attribute

//本质是一个类 类名中的Attribute会被省略
class MyCustomAttribute : Attribute{
	//特性中的成员 一般根据需求来写
    public string info;
    public MyCustomAttribute(string info){
        this.info = info;
    }
}

特性的使用

  • 基本语法:[特性名(参数列表)]
    • 本质上 就是在调用特性类的构造函数
  • 写在 类、函数、变量的上一行,表示他们具有该特性信息
[MyCustom("这是我自己写的特性")]
class MyClass{
    
    [MyCustom("特性可以写在成员变量前面")]
    public int value;
    
	[MyCustom("特性可以写在函数前面")]
	public void TestFun([MyCustom("特性可以写在参数前面")] int a){
		
	}
}
MyClass mc = new MyClass();
Type type = mc.GetType();
//判断是否使用了某个特性
//参数一:特性的类型
//参数二:是否搜索继承链(属性和事件忽略此参数)
if(type.IsDefined(typeof(MyCustomAttribute),false)){
    Console.WriteLine("该类型应用了MyCustom特性")
    //获取type元素中的所有特性 参数是否搜索继承链
    object[] array = type.GetCustomAttributes(true);
    foreach(var item in array){
        if(item is MyCustomAttribute){
            Console.WriteLine((item as MyCustomAttribute).info);
        }
    }
}

为特性类 加特性 限制其使用范围

  • 可在特性类前添加AttributeUsage特性
    • AttributeUsage构造方法参数:
      • AttributeTargets 限制特性能够用在那些地方
      • AllowMultiple 是否允许多个特性实例用在同一个目标上
      • Inherited 特性是否能被派生类和重写成员继承
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct , AllowMultiple = true , Inherited = true)]
class MyCustomAttribute : Attribute{
	//特性中的成员 一般根据需求来写
    public string info;
    public MyCustomAttribute(string info){
        this.info = info;
    }
}

系统自带特性

过时特性Obsolete

  • 用于提示用户使用的方法成员等已经过时 建议使用新方法
  • 一般加载函数前的特性
class Test{
	//参数1提示 参数2 true-使用时会报错   false-警告
	[Obsolete("OldSpeak已经过时,请使用新方法",false)]
	public void OldSpeak(){
	
	}
}

调用者信息特性

  • CallerFilePath 哪个文件调用
  • CallerLineNumber 哪一行调用
  • CallerMemberName 哪个函数调用
  • 调用这个函数或者变量时 会自动打印信息出来 很少使用

条件编译特性

  • Conditional 他会和预处理指令 #define配合使用
  • 需要引用命名空间System.Diagnostics;
  • 主要可以用在一些调试代码上 有时想执行有时不想执行的代码
#define test
class Test(){
	[Conditional("test")] //有test这个符号这个方法才会被执行
	static void Func(){
	
	}
}

外部dll包函数特性

  • DllImport用来标记非.Net(c#)函数,表明该函数在一个外部的dll中定义
  • 一般用来调用 C或者C++的dll包中的函数
  • 需要引用命名空间 System.Runtime.InteropServices
class Test(){
	[DllImport("dll包路径名")] 
	static void extern int Add(int a,int b)();
    //相当于是把这个dll包里的方法 映射到了c#中来
}

索引器

索引器

  • 让对象可以像数组一样通过索引访问其中元素,使程序看起来更直观,更容易编写
  • 索引器的参数类型可以是任意类型

基本语法

class Person{
    private string name;
    private int age;
    private Person[] friends;
    private int[,] array;
    public Person this[int index] {
        get{
            return friend[index];
        }
        set{
            friends[index] = value;
        }
    }
    //索引器重载
    public int this[int i,int j]{
        get{
            return array[i,j];
        }
        set{
            array[i,j] = value;
        } 
    }
}
Person p = new Person();
p[0] = new Person();//访问set方法
Console.WriteLine(p[0].name);//访问get方法
p[0,0] = 10;

随机数

随机数

  • Unity当中的Random是继承自UnityEngine命名空间 和C#中的Random是不同的
//int 重载 左包含 右不包含
int randomNum = Random.Range(0,100);
//float 重载 左包含 右包含
float singleRandomNum = Random.Range(1.1f,99,9f);

数据持久化-PlayerPrefs

PlayerPrefs数据持久化

什么是数据持久化

数据持久化就是将 就是将内存中的数据模型转换为存储模型,以及将存储模型转换为内存中的数据模型的统称 例如将游戏数据存到硬盘、硬盘中的数据再读取到游戏中

PlayerPrefs是什么?

PlayerPrefs是Unity提供用于存储读取玩家数据的公共类

PlayerPrefs 使用方法

  • PlayerPrefs的数据存储 类似于键值对存储 一个键对应一个值
  • 提供了存储三种数据的类型 int float string
  • 键:string类型
  • 值:int float string 对应3中api

添加数据

//参数 string key,int 值
PlayerPrefs.SetInt("myAge",18);//存到了内存中
PlayerPrefs.SetFloat("myHeight",188.5f);
PlayerPrefs.SetString("myName","路飞");

保存数据

  • 当游戏结束时Unity会自动把数据存到硬盘中
  • 如果游戏不是正常结束的 而是崩溃 则会丢失数据
  • 可调用.Save()方法手动保存到硬盘中
PlayerPrefs.Save();

PlayerPrefs的局限性

  • PlayerPrefs是由局限性的 它只可以保存3种类型的数据
  • 如果想要保存其他类型的数据 智能降低精度或上升精度
bool sex = true;
PlayerPrefs.SetInt("mySex",sex?1:0);
//键名一样 会被覆盖
PlayerPrefs.SetFloat("mySex",2);//

PlayerPrefs读取

  • 运行时只要Set了对应的键值对
  • 即使没有Save到本地 也可以读取出数据 从内存中
//如果 没有这个值 返回默认值 当int被float覆盖键相同也找不到
int age = PlayerPrefs.GetInt("myAge");
//指定默认值
int age = PlayerPrefs.GetInt("myAge",100);

判断数据是否存在

  • 一般是用来判断这个键对应的键值对数据是不是存在
  • 如果存在就不创建新的
PlayerPrefs.HasKey("key");

删除数据

PlayerPrefs.DeleteKey("key");
//删除所有数据
PlayerPrefs.DeleteAll("key");

PlayPrefs存储的数据在哪里?

PlayPrefs再不同的平台下 存储的路径是不同的

数据持久化-XML

XML

格式

<?xml version="1.0" encoding="UTF-8"?>
<Test>测试</Test>

规则

  • 每个元素都必须有关闭标签
  • 元素命名规则 基本遵循C#中文变量名命名规则
  • XML标签对大小写敏感
  • XML为胆囊必须带有根元素
  • 特殊符号应该用实体引用
    • &lt 小于号
    • &gl 大于号
    • &aml &符号
    • &apos 单引号
    • &quot 引号

属性

<Item>
    <id>1</id>
    <num>2</num>
</Item>
<!-- 属性的写法-->
<Item id="1" num="10"/>

Xml文件的存放位置

  • 只读不写的xml文件 可以放在Resources或者StreamingAssets文件夹下
  • 动态存储的xml文件可以放在Application.persistenDataPath路径下

C#读取xml的方式

  • XmlDocument把数据加载到内存中方便读取
  • XMLTextReader 以流形式加载 占用内存更少 但是单向只读 一般不会使用
  • Linq

XmlDocument读取xml文件信息

加载xml文件

XmlDocument xml = new XmlDocument();
TextAsset asset = Resources.Load<TextAsset>("TestXml");
xml.LoadXml(text.text);
//通过路径来加载xml 不可以加载resources文件夹下面的
xml.load(Application.persistenDataPath+"TestXml.xml");

读取xml文件

//通过XmlDocument对象 得到XmlNode 传入根节点
XmlNode root = xml.SelectSingleNode("Root");
//在通过根节点的对象 找到下面的子节点
XmlNode name = root.SelectSingleNode("name");
//获取内容
print(name.InnerText);
//获取属性
XmlNode item = root.SelectSingleNode("item");
print(item.Attributes["id"].Value);
//第二种方式
print(item.Attributes.GetNamedItem("id").Value);
//找到所有的xmlnode
XmlNodeList nodelist = xml.SelectNodes("Root");

存储Xml文件

  • 存储Xml文件 在Unity中一定是使用各平台都可读可写可找到的路径
  • Resources 可读 但是不可写 打包后找不到
  • Application.streamingAssetsPath 可读 Pc可写 可找到
  • Application.dataPath 打包后找不到
  • Application.persistentDataPath 可读可写找得到

关键类

  • XmlDocument 用于创建节点 存储文件
  • XmlDeclaration用于添加版本信息
  • XmlElement 节点类

创建xml

//创建文本对象
XmlDocument xml = new XmlDocument();
//创建xml 版本信息
XmlDeclaration xmlDec = xml.CreateXmlDeclaration("1.0","UTF-8","");
//将版本信息添加到xml中
xml.AppendChild(xmlDec);
//添加根节点
XmlElment root = xml.CreateElement("Root");
xml.AppendChild(root);
//为根节点添加子节点  <name>路飞</name>
XmlElement name = xml.CreateElement("name");
name.InnerTest = "路飞";
//将name添加到root节点中
root.AppendChild(name);

保存xml

xml.Save("路径");

修改xml

//判断文件是否存在
if(File.Exists(path)){
	//创建xml文本对象
	XmlDocument xml = new XmlDocument();
    xml.load(path);  
    //修改 就是在原有文件的基础上 删除 或者添加
    XmlNode root = xml.SelectSingleNode("Root");
    XmlNode node = root.SelectSingleNode("name");
    XmlNode node2 = xml.SelectSingleNode("Root/name");//第二种写法 
    //移除节点
    root.RemoveChild(node);
    XmlElement name = xml.CreateElement("name");
	name.InnerTest = "王路飞";
    root.AppendChild(name);
   //保存
    xml.Save(path);
}

XML序列化

using关键字

  • using关键字 一般是配合 内存占用较大 或者又读写操作时 使用的
  • using关键字 将会在大括号内代码执行完毕后 自动释放掉小括号中的对象
  • 可以理解为是一种语法糖
using(){

}

创建文件写入流

//首先确认存储的路径
string path = Application.persistentDataPath+"/Test.xml";
//创建文件写入流
StreamWriter stream = new StreamWriter(path)

XmlSerializer用于序列化对象为xml的关键类

  • 构造函数需要传入一个Type类型的参数
  • 这种方式只可以 保存公共的成员
  • 默认不可以序列化字典
XmlSerializer xs = new XmlSerializer(type);
//序列化
s.Serialize(文件流对象,对象);

可通过添加特性 使字段实例化成xml的属性 还可以修改名字

鼠标公共类(Cusor)

鼠标公共类Cusor

隐藏鼠标

Cusor.visible= false;

锁定鼠标

编辑器模式按esc摆脱锁定

//CursorLockMode枚举
//none不锁定 Locked将锁定在屏幕中心点(并隐藏鼠标) Confined限制在窗口范围内
Cursor.lockState = CursorLockMode.Locked;

设置鼠标图片

  • 参数1:光标图片(宽高要一样否则会变形 Texture Type选择Cursor)
  • 参数2:偏移位置 相对图片左上角
  • 参数3:平台支持的光标模式(硬件或软件)
    • 一般使用自动模式
Cursor.SetCursor(texture,vector2.zero,CursorMode.Auto);