基础

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. 我真的,AI框架的编程范式怎么理解?

    我给领导汇报AI框架用函数式编程好,没讲明白,说函数式就是写函数那样方便,都被领导吊飞了,啥玩意,写啥不是写函数,狗屁不通! 网上搜说用tensorflow那就是用声明式编程,用pytorch就是命令 ...

  2. 关于开设go语言专题的说明

    这个博客账号已经停更了约5年,期间我经历了比较多的事情,开始几个github开源项目,例如go-etl已经有两年.现在已经我在这方面有了许多积累,足够开设相关的专题.不用担心博主会断更,因为相关的内容 ...

  3. [nginx]lua控制请求头

    前言 nginx原生提供expires.add_header两个指令控制请求头,在Lua API中也有类似的指令. 添加请求头 指令:ngx.req.set_header 语法:ngx.req.set ...

  4. [ABC126E] 1 or 2

    2023-01-07 题目 题目传送门 翻译 翻译 难度&重要性(1~10):2 题目来源 AtCoder 题目算法 并查集 解题思路 因为每张卡片上的数字只能是 \(1\) 或者 \(2\) ...

  5. ChatGPT如何生成可视化图表-示例中国近几年出生人口

    本教程收集于:AIGC从入门到精通教程汇总 ChatGPT本身不能直接生成可视化图表,但可以配合其他可视化工具或库 方法一:前端可视化开发库 Echarts(地址:Apache ECharts ) 方 ...

  6. WLAN-AC+AP,动态负载均衡用户量,避免某一个AP负载过重

    组网图形 动态负载均衡简介 负载均衡功能主要功能就是平衡WLAN网络中AP的负载,充分地保证每个STA的带宽.当有一个新的STA加入网络时,动态负载均衡动态将AC将所有上报该STA的AP动态组成一个组 ...

  7. Poor God Water(ACM-ICPC 2018 焦作赛区网络预赛 矩阵快速幂)

    题目描述 God Water likes to eat meat, fish and chocolate very much, but unfortunately, the doctor tells ...

  8. Unity UGUI的Scrollbar(滚动条)组件的介绍及使用

    Unity UGUI的Scrollbar(滚动条)组件的介绍及使用 一.什么是Scrollbar组件? Scrollbar组件是Unity中UGUI系统提供的一种UI组件,主要用于在UI界面中提供滚动 ...

  9. 精选版:用Java扩展Nginx(nginx-clojure 入门)

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 今天咱们以Java程序员的视角,来聊聊如何用 ...

  10. 如何get一个终身免费续期的定制数字人?

    想拥有一个"数字分身" 吗?给你一个终身免费续期的特权. 定制周期长?训练.运营成本高?成片效果生硬?无法应用于实际场景? 随着AIGC技术的快速发展,虚拟数字人的生成效率不断提高 ...