基础

3D数学

Mathf函数库
print(Mathf.PI);
print(Mathf.Abs(-10));
print(Mathf.CeilToInt(1.2f));//向上取整
print(Mathf.FloorToInt(1.2f));//向下取整
//钳制函数
//数值 最小值 最大值
//如果数值超出范围 则返回最值
print(Mathf.Clamp(15,9,12));//返回12 最大才可以是12
print(Mathf.Clamp(10,9,15));//返回10 在合法范围中
print(Mathf.Clamp(7,10,15));//返回10 最小的是10
print(Mathf.Max(12,15,4,5,6,7));//取最大值 支持多参数
print(Mathf.Max(5,9));
print(Mathf.Min(5,9));
print(Mathf.Min(5,9,6,4,2,1));//支持多参数取最小值
print(Mathf.Pow(2,3));//2^3=8 乘方
print(Mathf.RoundToInt(3.5f));//四舍五入 4
print(Mathf.RoundToInt(3.2f));// 3
print(Mathf.Sqrt(16));//开方运算 4
print(Mathf.IsPowerOfTwo(4));//判断某数是否是2的n次方
print(Mathf.Sign(-5));//判断某数的正负 返回-1
print(Mathf.Sign(5));//正数 返回1 //插值运 Lerp
//result = start+(end-start)*t (一般是0<t<1)
//理解:start先快后慢地无线趋近与end数值
print(Mathf.Lerp(2,8,0.5f));//开始数值 结束数值 时间
float start=0;
start = Mathf.Lerp(start, 30, Time.deltaTime);
//趋近end数值 最终会等于数值 当t>=1时
//速度表现上看可以实现匀速运动效果
float time = 0;
time += Time.deltaTime;
start = Mathf.Lerp(start, 30, time);
三角函数

1.角度和弧度转换

2.三角函数和反三角函数

print(Mathf.PI / 2*Mathf.Rad2Deg);//弧度转角度 Rad表示弧度 deg表角度
print(56.5f*Mathf.Deg2Rad);//角度转弧度 //三角函数
//Mathf中计算三角函数值 传入的参数必须是弧度 Rad
print(Mathf.Sin(30*Mathf.Deg2Rad));
print(Mathf.Cos(60*Mathf.Deg2Rad));
//反三角函数 根据三角函数值 逆向求出度数
print(Mathf.Asin(0.5f));// Π/6
print(Mathf.Acos(1));//0
坐标系
  1. 物体坐标系
  2. 世界坐标系
  3. 屏幕坐标系
  4. 视口坐标系(左下角 (0,0)右上角(1,1))

不同坐标系下的相互转换:

//世界坐标系 下的坐标信息
//物体的世界坐标系坐标
print(transform.position);
//相对于父物体的位置 在父物体坐标系下的位置信息
print(transform.localPosition); //屏幕坐标系
//Input.mousePoistion
//Screen.width;
//Screen.height; //视口坐标
//摄像机面板上设置 //世界转本地(逆向的本地转世界)
transform.InverseTransformDirection(Vector3.forward);
transform.InverseTransformPoint(Vector3.down);
transform.InverseTransformVector(Vector3.forward); //本地转世界
transform.TransformDirection(Vector3.forward);
transform.TransformPoint(Vector3.forward);
transform.TransformVector(Vector3.forward); //世界转屏幕
Camera.main.WorldToScreenPoint(Vector3.forward);
//屏幕转世界
Vector3 point = new Vector3(5, 3, 15);//注意z值的提供
Camera.main.ScreenToWorldPoint(point); //世界转视口
Camera.main.WorldToViewportPoint(point);
//视口转世界
Camera.main.ViewportToWorldPoint(point); //视口转屏幕
Camera.main.ViewportToScreenPoint(point);
//屏幕转视口
Camera.main.ScreenToViewportPoint(point);
向量
  1. 向量模长和单位向量

    //Vector3 可以表示点坐标 位置方向(该点和原点(0,0,0)连接成的向量 )
    Vector3 v1 = new Vector3(5, 6, 7);
    //Vector2 二维向量
    Vector2 v2 = new Vector2(3, 5);
    //向量表示
    //AB--> 终点B-起点A 表示AB
    Vector3 bPoint = new Vector3(3, 5, 6);
    Vector3 aPoint = new Vector3(1, 1, 1);
    Vector3 AB = bPoint - aPoint;//高中知识 //零向量
    print(Vector3.zero);
    //负向量
    print(-Vector3.forward); //向量的模长
    float size = AB.magnitude;
    Vector3.Distance(bPoint, aPoint);//同为size //单位向量
    //模长为1的向量 对向量归一化得到 向量值/自己的模长
    print(AB.normalized);
  2. 向量的运算的意义

    1. 位置+位置 无意义
    2. 向量+向量=向量(首尾相连)
    3. 位置+向量 (平移位置)
    4. 位置-位置 = 向量
    5. 向量-向量 = 向量(指向被减向量)
    6. 位置-向量=位置+(-向量)(反向移动)
    7. 向量-位置 无意义
    8. A*vector2 放大A倍
    9. vector2/B 缩小B倍
  3. 向量的点乘 A·B(标量) (判断对象的方位)

    A·B=|A||B|cos<a,b>

    问:如何判断敌人是否在玩家的视角范围内?

    答:向量点乘 单位向量的点乘结果=向量夹角的余弦值

    问:如何判断敌人在自己的前方还是后方

    答:目标物体和当前位置组成的向量规格化与正前方的方向向量点乘,结果大于零则在前方,小于零则在后方。

     float dotResult = Vector3.Dot(transform.forward, (target.position - transform.position).normalized);
    if(dotResult>0)
    print("目标物体在该物体的前方 ");
  4. 向量的叉乘 AxB(叉乘)

    AXB=|A||B|Sin<a,b>

    Vector3.Cross(A,B);

    几何意义:A,B向量组成的平面的法向量

    利用:

    (AXB).y>0: B在A的右侧

    (AXB).y<0 :B在A的左侧

    //叉乘判断物体方位
    public Transform A, B;//目标物体 Vector3 c = Vector3.Cross(B.position, A.position);
    if (c.y > 0)
    {
    print("A在B的右侧");
    }
    else
    {
    print("A在B的左侧");
    }

点乘和叉乘来判断目标物体与自己的方位!

public Transform target;

void Update()
{
//点乘结果管前后
float dotResult = Vector3.Dot(transform.forward, (target.position - transform.position).normalized);
//叉乘纵轴管左右
float cossY = Vector3.Cross(transform.forward, (target.position - transform.position).normalized).y;
if (dotResult > 0)
{
// print("目标物体在前方");
if (cossY > 0)
{
print("目标物体在右前方");
}
else
{
print("目标物体在左前方");
}
}
else
{
if (cossY > 0)
{
print("目标物体在右后方");
}
else
{
print("目标物体在左后方");
}
}
}
  1. 线性插值

    Vector3.Lerp(startPos,endPos,t);同普通的Lerp,在向量中同时改变x,y,z的数值。

  2. 球形插值

    Vector3.Slerp(startPos,endPos,t);可以使向量沿弧线样式的移动。

四元数

欧拉角与四元数

欧拉角和四元数在Unity中用来表示物体的角度信息

四元数是简单的超复数,由实数加上三个虚数单位组成。

四元数的构成:一个标量+3维向量

可以通过轴角对来表示,

为什么要使用四元数?

欧拉角的缺点:

1.角度表示不唯一

​ 左旋45° 和右旋135°效果一样,一样的效果却有两种方式。

2.会发生万向节死锁现象

四元数是什么?

//四元数
//声明:
//绕X轴 旋转60°
Quaternion quaternion = new Quaternion(Mathf.Sin(30*Mathf.Deg2Rad),0,0,Mathf.Cos(30*Mathf.Deg2Rad));
//轴角对初始化
Quaternion quaternion1 = Quaternion.AngleAxis(60,Vector3.right);
//四元数和欧拉角的转换
// 欧拉角转四元数
Quaternion q1=Quaternion.Euler(60,0,0);
//四元数转欧拉角
print(q1.eulerAngles);

四元数相乘表示旋转四元数

//绕面朝向的轴 转1°
transform.rotation *= Quaternion.AngleAxis(1, Vector3.forward);

单位四元数、插值、向量指向四元数

//单位四元数
//没有进行任何旋转
//单位四元数
print(Quaternion.identity);//相当于欧拉角(0,0,0) //插值方法
//此物体逐渐接近 目标物体的旋转角度
transform.rotation=Quaternion.Slerp(transform.rotation,target.rotation,Time.deltaTime); //向量指数转向四元数
//旋转自身 面向目标物体
transform.rotation=Quaternion.LookRotation(target.position - transform.position);
//个人认为此功能很鸡肋 直接LookAt蛮方便的

四元数的运算:

//四元数的相乘
//表示对象的旋转 绕自身的轴的旋转
Quaternion q2 = Quaternion.AngleAxis(60, Vector3.up);
transform.rotation *= q2;
//四元数* 向量
Vector3 v3=Vector3.forward;
print(v3);
//二者相乘 向量在后
v3 = Quaternion.AngleAxis(45, Vector3.up) * v3;
print(v3);
//相当于Z轴向绕着y轴右转向45°
//应用 散弹枪的子弹方向 子弹方向的偏移

MonoBehavior中重要内容

延迟函数
  1. 可以定时执行的函数

  2. 该函数在同一个类中

  3. 延迟重复执行函数与延迟函数相似

  4. 判断延迟函数是否执行 ,取消延迟执行

  5. 延迟函数的“顽固”

    //延时重复执行函数
    //函数名称 第一次执行的时间 之后每次执行的时间间隔
    InvokeRepeating("FunDelay",5,2);
    //取消延迟函数
    //取消该脚本中所有的延迟函数
    CancelInvoke();
    //取消指定的延迟函数
    //只要有取消指定的延迟函数 尽管该延迟函数被多次开启 均会被取消(只要取消一定会取消)
    CancelInvoke("FunDelay");
    //判断该脚本中是否存在延迟函数
    if (IsInvoking())
    {
    print("该脚本中存在延迟函数");
    } if (IsInvoking("FunDelay"))
    {
    print("存在FunDelay函数延迟执行");
    }
    //同脚本类中的延迟执行的延迟函数
    public void FunDelay()
    {
    print("FunDelay执行了!");
    //间接地执行有参函数
    FunDelay(true);
    }
    public void FunDelay(bool flag)
    {
    print("FunDelay执行了!"+flag);
    }
    //挂载脚本上有延迟函数执行
    //1. 挂载脚本销毁 (死了就不行了吧!哈哈哈哈)
    // 无法执行
    //2. 失活对象、失活脚本 不影响(小子挺顽固啊!!)
    // 延迟函数依旧
    //综上 我们使用时候在OnEable()中开启延时函数执行
    //在OnDisable()中关闭延时函数的执行
    //这样就做到失活就不再让顽固的延迟函数执行了
协同程序

unity中支持使用多线程吗?

答:支持多线程。但是只有主线程才有权限来访问unity的内容。但是我们可以开启新的线程来完成相关的功能的设置,如完成消息的收发,进行复杂的算法计算,完成之后存储结果,供主线程访问使用。

协程:协程不是一个新的线程,它可以将代码逻辑进行分布执行,不会造成对主线程的卡顿

协同程序的使用:

  1. 异步加载文件
  2. 异步下载文件
  3. 场景异步加载
  4. 批量创建时候的防止卡顿

注意:协同程序不是多线程,而是在主线程中开启,对代码逻辑分时分步执行。而多线程是与主线程之间是并行执行。

private Thread thread;
void Start()
{
thread = new Thread(TestFun);
thread.Start();
}
void Update()
{
//在update函数中获取副线程
//运算结果
if (queue.Count > 0)
{
//获取thread中计算的位置信息
transform.position = queue.Peek();
queue.Dequeue();
}
} public void TestFun()
{
while (true)
{
Thread.Sleep(1000);
queue.Enqueue(new Vector3(1,2,3));
//企图在新线程中给物体坐标赋值
//会报错!
//transform.position = new Vector3(4, 5, 6);
}
}

企图在副线程中访问设置物体坐标,会报错!除主线程外的线程不被允许访问unity包里的内容!

协程函数使用

//协程函数的返回值是 IEnumerator类型
IEnumerator myCoroutine(int i, string str)
{
print(i);
yield return new WaitForSeconds(2f);
print(str);
} //协程的开启
//开启协程方法一
StartCoroutine(myCoroutine(1,"协程函数"));
//开启协程方法二
IEnumerator enumerable = myCoroutine(1, "123");
StartCoroutine(enumerable);//开启协程 //同时开启三个协程
//注意看协程函数
//都是先执行第一步打印 然后等5s 再同时执行第二部打印
//这样印证它是在主线程中执行的
Coroutine c1 = StartCoroutine(myCoroutine(1, "协程"));
Coroutine c2 = StartCoroutine(myCoroutine(1, "协程"));
Coroutine c3 = StartCoroutine(myCoroutine(1, "协程")); //结束协程
//结束所有的协程
StopAllCoroutines();
//结束指定的协程
StopCoroutine(c2); //协程中的yield return 不同内容含义
//1. 下一帧执行
yield return 10;//数字
yield return null;
//2. 等待指定秒数执行
yield return new WaitForSeconds(5f);//等待指定秒数执行
//在update 和LateUpdate之间执行 //3. 等待下一个固定物理函数帧更新时候执行
yield return new WaitForFixedUpdate();
//在FixedUpdate和碰撞检测相关函数之后执行 //4. 等待摄像机和GUI渲染完成之后执行
yield return new WaitForEndOfFrame();
//可以在此进行截图功能调用
//在LateUpdate之后的渲染相关处理完毕之后执行 //5.一些册数类型的对象 比如异步加载相关函数
//返回之后 再执行 //6 跳出协程
yield break;

协程开启后,脚本或者物体销毁,协程不执行;

物体失活协程不执行,脚本失活协程继续执行。(其余都不执行)

协程常见应用---计时器

//协程应用--计时器
//开启协程
StartCoroutine(MyCoroutine()); IEnumerator MyCoroutine()
{
int time=0;
while(true)
{
print(time+"秒");
time++;
yield return new WaitForSeconds(1);
}
}
协同程序原理
  1. 协程本质

    1.1 协程函数本体

    协程是一个能够中间暂停执行的函数

    1.2 协程调度器

    协程调度器是unity内部实现的,会在对应时机帮助我们继续执行写成函数的一个调度器。unity仅仅是实现协程调度部分,协程本体本质上是 C#的迭代器方法。

  2. 协程本体是迭代器方法的体现

    //协程本体----迭代器函数
    public class Test3
    {
    public Test3()
    { }
    } IEnumerator Test()
    {
    print("第一次执行");
    yield return 1;
    print("第二次执行");
    yield return 2;
    print("第三次执行");
    yield return "333";
    print("第四次执行");
    yield return new Test3();//可以返回对象
    } IEnumerator ie = Test(); //自行迭代器函数内容
    ie.MoveNext();//执行函数逻辑直至遇到返回条件
    print(ie.Current);//得到返回的内容 //自行迭代器函数内容
    ie.MoveNext();//执行函数逻辑直至遇到返回条件
    print(ie.Current);//得到返回的内容
    //自行迭代器函数内容
    ie.MoveNext();//执行函数逻辑直至遇到返回条件
    print(ie.Current);//得到返回的内容 //自行迭代器函数内容
    ie.MoveNext();//执行函数逻辑直至遇到返回条件
    Test3 t3=ie.Current as Test3; //等同于 迭代器函数是否迭代完毕
    while(ie.MoveNext())
    {
    print(ie.Current);
    }
    //而Unity中是根据返回的终止条件来进行下一次执行的检查
    //满足yeild return 的条件之后继续执行函数中的下一个逻辑块

    练习:自行实现协程。(仅支持延时时间返回)

    //分时分步 执行函数
    //只支持 yeild return int类型的处理
    //存储迭代器和下一次执行时间的数据结构
    public class YieldReturnTime
    {
    public IEnumerator ie;//当前位置的迭代器
    public float time;//下一次执行的时间
    }
    //设置成单例类
    //这里继承MonoBehaviour来使用其Update函数进行计时判断
    public class CoroutineManager :MonoBehaviour
    {
    private static CoroutineManager instance; public static CoroutineManager Instance => instance; //存储多个协程同时执行时候各自的迭代器时间对象
    private List<YieldReturnTime> list = new List<YieldReturnTime>(); private void Awake()
    {
    instance = this;
    } public void MyStartCoroutine(IEnumerator ie)
    {
    //首先进行函数的执行
    //遇到第一个等待时间条件再存储执行节点
    //等待时机再次执行
    if (ie.MoveNext())
    {
    if (ie.Current is int)
    {
    YieldReturnTime yi = new YieldReturnTime();
    yi.ie = ie;
    //下一次执行的时间节点
    yi.time = Time.time + (int) ie.Current;
    //存储到容器中
    list.Add(yi);
    }
    }
    } private void Update()
    {
    //时刻检查是否有满足执行条件的迭代器 让其执行
    //遍历检查 (尾部遍历,因为要对list可能存在移除操作,移除操作之后尾部的内容会逐个前移填补空位)
    for (int i = list.Count-1; i >=0; i--)
    {
    //当前时间点满足执行的条件
    if (list[i].time <= Time.time)
    {
    //则进行执行
    if (list[i].ie.MoveNext())
    {
    //只支持返回int类型的数据
    if (list[i].ie.Current is int)
    {
    list[i].time = Time.time + (int) list[i].ie.Current;
    }
    else
    {
    //返回其它类型的数据则移除
    list.RemoveAt(i);
    }
    }
    else
    {
    //执行完毕当前逻辑段之后
    //说明整个函数逻辑完全执行完毕!
    list.RemoveAt(i);
    }
    }
    }
    }
    }
    //测试脚本中的测试代码
    IEnumerator MyTest()
    {
    print("第一次执行");
    yield return 1;
    print("第二次执行");
    yield return 2;
    print("第三次执行");
    yield return 3;
    print("第四次执行");
    }
    void Start()
    {
    //使用自定义的管理执行迭代器开始协程
    CoroutineManager.Instance.MyStartCoroutine(MyTest());
    }

Resources资源动态加载

Unity中的特殊文件夹
  1. 工程路径的获取

    print(Application.dataPath);//编辑项目时候的路径 供开发阶段使用

  2. Resources资源文件夹

    • 需要通过Resources相关的API动态加载的资源需要放在其中
    • 该文件夹下所有的文件都会被打包出去
    • 打包时Unity会对其进行压缩
    • 该文件夹打包后只读 并且只能通过Resources相关API加载
  3. StreamingAssets(流文件夹)

    print(Application.streamingAssetsPath);

    • 打包出去不会被压缩加密
    • 移动平台只读,PC平台可读可写
    • 可以存放一些需要自定义加载的初始资源
  4. persistentDataPath持久数据文件夹无需自己手动创建

    print(Application.persistentDataPath);//获取文件路径

    • 存储持续性数据的文件夹(一般是再运行设备的用户名相关的文件目录下自动创建)
    • 所有平台都是可读可写
    • 一般用处放置动态下载或者动态创建的文件,游戏中创建的或者获取的文件都放在其中
  5. Plugins插件文件夹(手动创建 存放插件资源 例如Ios Android SDK工具包)

  6. Editor编辑器文件夹(手动创建 )

    • 开发扩充unity编辑器功能(方便开发)时,存放编辑器相关脚本
    • 该文件夹内容不会被打包出去
  7. 默认资源文件夹 Standard Assets (不常用,了解即可)

Resource
  1. 资源动态加载

    可以避免繁琐的拖拽加载。

  2. 资源类型

    1. 预设体对象--GameObjet

      2. 音效文件--AudioClip

      3. 文本文件--TextAsset

      4. 图片文件--Texture

      5. 其它类型

    注意:预设体加载需要实例化

  3. 资源同步加载--普通方法

    //1. 预制体资源的加载
    Object obj=Resources.Load("Cube");//记载资源
    Instantiate(obj);//实例化之后才会出现到场景中
    //Resource文件夹可以有多个(可以存放在其它文件目录下) 但每次系统都可以找到加载内容 //2. 音效文件夹的加载
    Object obj2=Resources.Load("Music/xxxMusic");
    public AudioSource audioSource;
    audioSource.clip=obj2 as AudioClip;
    //播放
    audioSource.Play(); //3.文本资源
    //文本文件类型 txt xml bytes json html csv
    //此处读取的是.txt文件
    TextAsset textAsset = Resources.Load("Text/test") as TextAsset;
    print(textAsset.text); //4.图片资源
    Texture texture = Resources.Load("Text/text.jpg") as Texture;
    GUI.DrawTexture(new Rect(0,0,300,300),texture); //其它 --动画文件等 //如何解决重名问题?
    //使用指定类型来加载
    Texture tex=Resources.Load("Tet/TestJPJ",typeof(Texture)) as Texture;
    //加载指定名字的所有资源(极少使用)
    object[] objs=Resources.LoadAll("Tex/TestJPG");
    for(Object item in objs)
    {
    if(item is Texture)
    {
    //...
    }
    }
  4. 资源同步加载 泛型方法

     //指定类型加载
    TextAsset textAsset = Resources.Load<TextAsset>("Text/test") as TextAsset;
    print(textAsset.text);
异步加载

异步加载,不会等待加载完成之后继续走之后的逻辑,而是在加载资源时候,主线程继续走接下来的代码逻辑,检查是否加载完毕,然后获取使用,异步加载不会产生卡顿。

//异步加载
//1. 通过异步加载完成事件监听
//进行异步加载
//可以理解unity内部开启一个线程来进行资源的加载
//加载完毕之后会执行completed事件
ResourceRequest rq=Resources.LoadAsync<Texture>("Text/TestJPJ");
rq.completed += LoadOver;
//资源加载结束加载的事件添加之后的调用函数
private void LoadOver(AsyncOperation asyncOperation)
{
print("加载完成了!");
Texture texture = (asyncOperation as ResourceRequest).asset as Texture;
}
//2. 通过协程来异步加载
//可以在协程中处理其它的相关加载内容函数
IEnumerator Load()
{
ResourceRequest rq=Resources.LoadAsync<Texture>("Text/TestJPJ");
//unity内部中本身知道实在加载资源
//因为加载资源和协程有继承同一个基类
yield return rq;
Texture tex=rq.asset as Texture;
} IEnumerator Load1()
{
ResourceRequest rq=Resources.LoadAsync<Texture>("Text/TestJPJ");
//不停的检查是否结束
//如果未加载结束就每一帧都检查
while (!rq.isDone)
{
print(rq.priority);
yield return null;
}
Texture tex=rq.asset as Texture;
}
资源卸载

重复加载资源会浪费内存吗?

答:不会。因为每一次加载都会在内存中检查该资源是否已经加载到内存中,有的话不会重复再次加载到内存中,所以不会浪费内存。但是重复加载会耗费性能,每一次的加载都面临对资源的查找。

如何手动释放掉缓存中的资源?

答:1. 卸载已使用的资源

Resources.UnloadAsset();

该方法不能释放掉GameObject对象,但是可以卸载图片、文本、音频,而GameObject因为实例化场景中。

  1. 卸载未使用的资源(使用较多)

    Resources.UnloadUnusedAssets();

    一般在过场景时候搭配GC使用

    GC.Collect();

场景异步切换
  1. 场景同步切换
//同步加载
SceneManager.LoadScene("Game");

​ 同步切换的缺点:

​ 同步切换场景时候,unity会删除当前场景中的所有对象,然后进行新场景资源的加载,处理事件较长,造成卡顿。

  1. 场景的异步加载

2.1 通过事件回调函数异步加载

//加载完毕之后自动跳转到新场景
AsyncOperation asyncOperation = SceneManager.LoadSceneAsync("Game");
asyncOperation.completed += (a) =>
{
print("加载结束!!");
};

2.2 通过协程进行异步加载

IEnumerator LoadScene(string name)
{
//异步加载函数
AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(name);
yield return asyncOperation;
//异步加载会把场景中的资源删除
//该脚本也可能被消除 所以场景加载完成之后
//协程的逻辑可能之后不会执行
print("可能失效 不打印"); while (!asyncOperation.isDone)
{
print(asyncOperation.progress);
yield return null;
}
//也可以根据自己的规则来进行场景过度
//比如 动态加载怪物 这时候的进度条更新20%
//等到怪物加载完成 动态加载模型 完成之后再拉满 隐藏进度条
//进入场景
} //保证过场景不会被消除
//协程中的逻辑执行完整
DontDestroyOnLoad(this.gameObject);
StartCoroutine(LoadScene("Game001"));
//可以在协程中的执行逻辑
//进行进度条设置
LineRender

LineRenderer是Unity中专门用来绘制线的组件/类。

LineRender代码相关

//LineRender划线API

GameObject objLine = new GameObject();
LineRenderer line=objLine.AddComponent<LineRenderer>(); //是否首尾相连
line.loop = true;
//首位宽度
line.startWidth = 0.02f;
line.endWidth = 0.02f; //首位颜色
line.startColor=Color.cyan;
line.endColor=Color.cyan; //设置材质
m = Resources.Load<Material>("M");
line.material = m; //先设置点的个数
//再给点坐标
line.positionCount = 4;
line.SetPositions(new Vector3[]
{
new Vector3(0,0,0),
new Vector3(0,0,4),
new Vector3(5,6,7),
});
//单独设置一个点
line.SetPosition(2,new Vector3(1,2,3)); //不使用世界坐标系
line.useWorldSpace = false;
//线段受光影响
line.generateLightingData = true;

核心系统

范围检测

游戏中的顺势攻击时候会判断杀伤力范围,再杀伤力范围的物体要进行伤害。

//范围检测
//创建一个瞬时的碰撞器 执行代码时刻创建 之后消失(相当于拍照)
//不会真实的创建一个碰撞器,而是进行计算检测
//1. 盒状范围检测
//中心点 长宽高 旋转角 检测层(二进制表示1<<层号 |是叠加)
//是否忽略触发器 UseGlobal-使用全局设置(默认) Collider-检测触发器 Ignore-忽略触发器
//返回值是个数组 内容是处于当前范围中所有对象
Collider[] colliders = Physics.OverlapBox(Vector3.zero, new Vector3(2, 5, 7), Quaternion.AngleAxis(45, Vector3.up),
1 << LayerMask.NameToLayer("UI") |
1 << LayerMask.NameToLayer("Default"), QueryTriggerInteraction.Ignore); //返回值:碰撞到的碰撞器的数目
//传入一个要检测的碰撞体对象数组
//检测在范围中的数目
//后两个参数是 层级 是否忽略触发器
if (Physics.OverlapBoxNonAlloc(Vector3.zero, Vector3.one, colliders) != 0)
{ } //2.球状范围检测
Collider[] colliders2=Physics.OverlapSphere(Vector3.zero, 5f, 1 << LayerMask.NameToLayer("Default"));
//传入一个要检测的碰撞体对象数组
//检测在范围中的数目
//后两个参数是 层级 是否忽略触发器
if (Physics.OverlapSphereNonAlloc(Vector3.zero, 3f, colliders2) != 0)
{ }
//3.胶囊
//上半圆的中心点
//下半圆中心点
//半径
colliders2 = Physics.OverlapCapsule(Vector3.zero, Vector3.up, 2f, 1 << LayerMask.NameToLayer("UI")); //传入一个要检测的碰撞体对象数组
//检测在范围中的数目
//后两个参数是 层级 是否忽略触发器
if (Physics.OverlapCapsuleNonAlloc(Vector3.zero,Vector3.up, 1f, colliders) != 0)
{ }
射线检测

射线检测,可以指定发射射线,检测碰撞器是否被射中。

(FPS中的枪械射击)

//射线机发射射线检测
//从鼠标点的位置发射射线
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); //射线检测函数
//执行代码进行射线检测
Ray ray2 = new Ray(Vector3.zero, Vector3.forward);
//物理检测
//传入射线 设置射线距离 检测层
if(Physics.Raycast(ray2,1000,1<<LayerMask.NameToLayer("Monster"),QueryTriggerInteraction.Collide))
{
print("射中了");
}
//给两个点 确定发射点和射线方向
if(Physics.Raycast(Vector3.zero, Vector3.forward,1000,1<<LayerMask.NameToLayer("Monster")))
{
print("射中了");
}
//返回被射中的碰撞体
//可以获取物体碰撞的信息
RaycastHit hitInfo;//存储被射线击中的信息
//也可以不赋值给射线 传入两个点 确定发射点 发射方向
if (Physics.Raycast(ray2, out hitInfo, 1000))
{
print("碰撞到"+hitInfo.collider.name);
//碰撞体的点
//方便 子弹击中受伤效果
print(hitInfo.point);
//法线信息
//方便贴图
print(hitInfo.normal);
//碰撞到的对象位置
print(hitInfo.transform.position);
//击中点距离
print(hitInfo.distance);
} //获取相交的多个物体
//也可以将射线 替换成两个点
//后面参数设置检测层 是否忽略触发器
RaycastHit[] hits=Physics.RaycastAll(ray2, 1000);
for (int i = 0; i < hits.Length; i++)
{
print(hits[i].collider.name);
} //返回射线射中的物体数目
RaycastHit[] hits1=Physics.RaycastAll(ray2, 1000);
if (Physics.RaycastNonAlloc(ray2, hits1, 1000) > 0)
{
//射中物体了
}

注意:API参数 传入射线或者两个点(起点,确定方向的第二个点),最远检测距离

检测层级,使用二进制表示1<<,是否忽略触发器。

Unity学习笔记--基础的更多相关文章

  1. jQuery学习笔记 - 基础知识扫盲入门篇

    jQuery学习笔记 - 基础知识扫盲入门篇 2013-06-16 18:42 by 全新时代, 11 阅读, 0 评论, 收藏, 编辑 1.为什么要使用jQuery? 提供了强大的功能函数解决浏览器 ...

  2. Python学习笔记基础篇——总览

    Python初识与简介[开篇] Python学习笔记——基础篇[第一周]——变量与赋值.用户交互.条件判断.循环控制.数据类型.文本操作 Python学习笔记——基础篇[第二周]——解释器.字符串.列 ...

  3. 数论算法 剩余系相关 学习笔记 (基础回顾,(ex)CRT,(ex)lucas,(ex)BSGS,原根与指标入门,高次剩余,Miller_Rabin+Pollard_Rho)

    注:转载本文须标明出处. 原文链接https://www.cnblogs.com/zhouzhendong/p/Number-theory.html 数论算法 剩余系相关 学习笔记 (基础回顾,(ex ...

  4. 《python基础教程(第二版)》学习笔记 基础部分(第1章)

    <python基础教程(第二版)>学习笔记 基础部分(第1章)python常用的IDE:Windows: IDLE(gui), Eclipse+PyDev; Python(command ...

  5. Java学习笔记——基础篇

    Tips1:eclipse中会经常用到System.out.println方法,可以先输入syso,然后eclipse就会自动联想出这个语句了!! 学习笔记: *包.权限控制 1.包(package) ...

  6. iOS学习笔记——基础控件(上)

    本篇简单罗列一下一些常用的UI控件以及它们特有的属性,事件等等.由于是笔记,相比起来不会太详细 UIView 所有UI控件都继承于这个UIView,它所拥有的属性必是所有控件都拥有,这些属性都是控件最 ...

  7. 微软企业库Unity学习笔记

    本文主要介绍: 关于Unity container配置,注册映射关系.类型,单实例.已存在对象和指出一些container的基本配置,这只是我关于Unity的学习心得和笔记,希望能够大家多交流相互学习 ...

  8. Unity学习笔记

    『 知识点』 [射线] 射线检测碰撞 『游戏实战』 个例 [E]<愤怒的小鸟> 资源 免费Unity基础教程(中文电子书) [E] noobtus(Unity游戏教程)

  9. iOS开发学习笔记:基础篇

    iOS开发需要一台Mac电脑.Xcode以及iOS SDK.因为苹果设备都具有自己封闭的环境,所以iOS程序的开发必须在Mac设备上完成(当然,黑苹果应该也是可以的,但就需要花很多的精力去折腾基础环境 ...

  10. Python学习笔记——基础篇【第一周】——变量与赋值、用户交互、条件判断、循环控制、数据类型、文本操作

    目录 Python第一周笔记 1.学习Python目的 2.Python简史介绍 3.Python3特性 4.Hello World程序 5.变量与赋值 6.用户交互 7.条件判断与缩进 8.循环控制 ...

随机推荐

  1. Lock同步_小记

    使用同步机制的这种方式解决线程安全问题,但是不知道具体的锁对象在哪里添加,并且锁对象在哪里释放锁对象,对于这种情况Jdk5以后Java提供了一个更具体的锁对象:Lock Lock 实现提供了比使用 s ...

  2. iptables防火墙调试,想打印个日志就这么难

    背景 怎么会讲这个话题,这个说来真的长了.但是,长话短说,也是可以的. 我前面的文章提到,线上的服务用了c3p0数据库连接池,会偶发连接泄露问题,而分析到最后,又怀疑是db侧主动关闭连接,或者是服务所 ...

  3. Cilium 系列-3-Cilium 的基本组件和重要概念

    系列文章 Cilium 系列文章 前言 安装完了,我们看看 Cilium 有哪些组件和重要概念. Cilium 组件 如上所述,安装 Cilium 时,会安装几个运行组件(有些是可选组件), 它们各是 ...

  4. burp抓包iPhone手机

    https://blog.csdn.net/weixin_43965597/article/details/107864200

  5. vim 配色调整

    ~/.vimrc " Configuration file for vim set modelines=0 " CVE-2007-2438 set number " se ...

  6. 需求太多处理不过来?MoSCoW模型帮你

    一.MoSCoW模型是什么 MoSCoW模型是在项目管理.软件开发中使用的一种排序优先级的方法,以便开发人员.产品经理.客户对每个需求交付的重要性达成共识. MoSCoW是一个首字母缩略词,代表: M ...

  7. C# 中关于 T 泛型【C# 基础】

    〇.前言 C# 里面的泛型不仅可以使用泛型函数.泛型接口,也可以使用泛型类.泛型委托等等.在使用泛型的时候,它们会自行检测你传入参数的类型,因此它可以为我们省去大量的时间,不用一个个编写方法的重载.与 ...

  8. 如何在达梦数据库中追踪慢SQL

    在达梦数据库中,我们可以通过开启日志记录和设置最小执行时间来追踪慢SQL.下面是具体的步骤: 1. 修改dm.ini文件 使用以下命令编辑dm.ini文件: cd /home/dmdba/dmdbms ...

  9. [ABC126F] XOR Matching

    2023-01-07 题目 题目传送门 翻译 翻译 难度&重要性(1~10):1 题目来源 AtCoder 题目算法 位运算 解题思路 因为两个相同数异或为 \(0\),所以中间放一个 \(k ...

  10. P2024 [NOI2001] 食物链 || #576. 食物链【NOI2001】 (并查集)

    空降锣鼓 空降OJ 题解: #include<bits/stdc++.h> using namespace std; int n,k; int d,x,y; int ans; int fa ...