Unity学习-常用组件Time、Transform

511 阅读10分钟

一.引言

前面介绍了GameObject相关的API,下面整理一下Unity中常用的一些组件

二.Time相关API

Time相关内容主要用于游戏中参与位移、记时、时间暂停等

  1. 时间缩放比例
            //时间停止
            //Time.timeScale = 0;
            //回复正常
            //Time.timeScale = 1;
            //2倍速
            //Time.timeScale = 2;
    
    时间停止通常用于游戏暂停,因为路程 = 时间*速度,时间是0倍,那自然是静止的
  2. 帧间隔时间
    帧间隔时间主要是用来计算位移,根据需求选择参与计算的间隔时间
    如果希望游戏暂停时就不动的那就使用deltaTime
    如果希望不受暂停影响就使用unscaledDeltaTime
            //帧间隔时间:最近的一帧 用了多长时间(秒)
            //受scale影响
            print("帧间隔时间" + Time.deltaTime);
            //不受scale影响的帧间隔时间
            print("不受scale影响的帧间隔时间" + Time.unscaledDeltaTime);
    
  3. 游戏开始到现在的时间
    主要用来计时单机游戏中计时
            //受scale影响
            print("游戏开始到现在的时间:" + Time.time);
            //不受scale影响
            print("不受scale影响的游戏开始到现在的时间:" + Time.unscaledTime);
    
  4. 物理帧间隔时间 FixedUpdate
            //受scale影响
            print(Time.fixedDeltaTime);
            //不受scale影响
            print(Time.fixedUnscaledDeltaTime);
    
  5. 帧数
    从开始到现在游戏跑了多少帧(次循环),常用于进行帧同步
    Time.frameCount

三.基础组件Transform

游戏对象(GameObject)位移、旋转、缩放、父子关系、坐标转换等相关操作都由它处理

  1. Vector3基础
    Vector3主要是用来表示三维坐标系中的 一个点 或者一个向量
            //申明
            Vector3 v = new Vector3();
            v.x = 10;
            v.y = 10;
            v.z = 10;
            //只传xy 默认z是0
            Vector3 v2 = new Vector3(10, 10);
            //一步到位
            Vector3 v3 = new Vector3(10, 10, 10);
    
            Vector3 v4;
            v4.x = 10;
            v4.y = 10;
            v4.z = 10;
    
            //Vector的基本计算
            // + - * /
            Vector3 v1 = new Vector3(1, 1, 1);
            Vector3 v12 = new Vector3(2, 2, 2);
            // 对应用x+或者-x.....
            print(v1 + v12);
            print(v1 - v12);
    
            print(v1 * 10);
            print(v12 / 2);
    
            //常用
            print(Vector3.zero);//000
            print(Vector3.right);//100
            print(Vector3.left);//-100
            print(Vector3.forward);//001
            print(Vector3.back);//00-1
            print(Vector3.up);//010
            print(Vector3.down);//0-10
    
            //常用的一个方法 
            //计算两个点之间的距离的方法
            print(Vector3.Distance(v1, v12));
    
  2. 位置相关概念
    • 相对世界坐标系
      this.gameObject.transform.position
      通过position得到的位置是相对于世界坐标系的原点的位置,可能和面板上显示的是不一样的,因为如果对象有父子关系,并且父对象位置不在原点,那么和面板上肯定就是不一样的
    • 相对于父对象的坐标系
      this.transform.localPosition
      这两个坐标对于我们来说很重要,如果你想以面板坐标为准来进行位置设置,那一定是通过localPosition来进行设置的
    • 相对世界和相对父对象可能出现是一样的情况如下:
      1. 父对象的坐标就是世界坐标系原点(0,0,0)
      2. 对象没有父对象
    • 位置的修改
      注意:位置的赋值不能直接改变x,y,z 只能整体改变,不能单独改 x y z某一个值
          //this.transform.position.x = 10; //报错
          this.transform.position = new Vector3(10, 10, 10);
          this.transform.localPosition = Vector3.up * 10;
          
          //如果只想改一个值x y和z要保持原有坐标一致
          //1.直接赋值
          this.transform.position = new Vector3(19, this.transform.position.y, this.transform.position.z);
          //2.先取出来 再赋值
          //虽然不能直接改 transform的 xyz 但是 Vector3是可以直接改 xyz的
          //所以可以先取出来改Vector3 再重新赋值
          Vector3 vPos = this.transform.localPosition;
          vPos.x = 10;
          this.transform.localPosition = vPos;
      
    • 获取当前游戏物体的各个方向的朝向
      我们知道世界坐标系是死的,但是游戏物体是活的,所以游戏物体不可能每时每刻都是和世界坐标系重合
          //如果你想得到对象当前的 一个朝向 
          //那么就是通过 trnasform.出来的 
          //对象当前的各朝向
          //对象当前的面朝向
          print(this.transform.forward);
          //对象当前的头顶朝向
          print(this.transform.up);
          //对象当前的右手边
          print(this.transform.right);
      
  3. 位移相关
    • 理解坐标系下的位移计算公式
      路程 = 方向 * 速度 * 时间
    • 实现位移的两种方式:
      1. 自己计算
        用当前的位置 + 我要动多长距离 = 最终所在的位置
        //this.transform.position = this.transform.position + this.transform.up * 1 * Time.deltaTime;
        
        //因为我用的是 this.transform.forward 所以它始终会朝向相对于自己的面朝向去动
        //this.transform.position += this.transform.forward * 1 * Time.deltaTime;
        
        //方向非常重要 因为 它决定了你的前进方向
        //this.transform.position += Vector3.forward * 1 * Time.deltaTime;
        
      2. 使用Unity提供的API
        参数一:表示位移多少 路程 = 方向 * 速度 * 时间
        参数二:表示 相对坐标系 默认 该参数 是相对于自己坐标系的
        //1相对于世界坐标系的 Z轴 动  始终是朝 世界坐标系 的 Z轴正方向移动
        this.transform.Translate(Vector3.forward * 1 * Time.deltaTime, Space.World);
        
        //2相对于世界坐标的 自己的面朝向去动   始终朝自己的面朝向移动
        this.transform.Translate(this.transform.forward * 1 * Time.deltaTime, Space.World);
        
        //3相对于自己的坐标系 下的 自己的面朝向向量移动 (一定不会这样让物体移动) XXXXXXX
        this.transform.Translate(this.transform.forward * 1 * Time.deltaTime, Space.Self);
        
        //4相对于自己的坐标系 下的 Z轴正方向移动  始终朝自己的面朝向移动
        this.transform.Translate(Vector3.forward * 1 * Time.deltaTime, Space.Self);
        
        记住这4种排列组合,一般使用API来进行位移
  4. 角度和旋转角
    角度和位移类似,也有相对世界和相对父对象两个参考系
    • 角度相关
          //相对世界坐标角度
          print(this.transform.eulerAngles);
      
          //相对父对象角度
          print(this.transform.localEulerAngles);
      
          //注意:设置角度和设置位置一样 不能单独设置xyz 要一起设置
          //如果我们希望改变的 角度 是面板上显示的内容 那一点是改变 相对父对象的角度
          //this.transform.localEulerAngles = new Vector3(10, 10, 10);
          //this.transform.eulerAngles = new Vector3(10, 10, 10);
          print(this.transform.localEulerAngles);
      
    注意这里使用的是欧拉角,并不是四元数,欧拉角得到的角度只有正数,0~360度
    • 旋转相关
          // 也有不通过API进行旋转的,方式和位移相似,这里就不做介绍了,下面就直接展示API计算角度
          //1、自转
          //每个轴 具体转多少度
          //第一个参数 相当于 是旋转的角度 每一帧 
          //第二个参数 默认不填 就是相对于自己坐标系 进行的旋转
          //this.transform.Rotate(new Vector3(0, 10, 0) * Time.deltaTime);
          //this.transform.Rotate(new Vector3(0, 10, 0) * Time.deltaTime, Space.World);
      
          //2、相对于某个轴 转多少度
          //参数一:是相对哪个轴进行转动
          //参数二:是转动的 角度 是多少
          //参数三:默认不填 就是相对于自己的坐标系 进行旋转
          //       如果填  可以填写相对于 世界坐标系进行旋转
          //this.transform.Rotate(Vector3.right, 10 * Time.deltaTime);
          //this.transform.Rotate(Vector3.right, 10 * Time.deltaTime, Space.World);
      
          //相对于某一个点转
          //参数一:相当于哪一个点 转圈圈
          //参数二:相对于那一个点的 哪一个轴转圈圈
          //参数三:转的度数  旋转速度 * 时间
          this.transform.RotateAround(Vector3.zero, Vector3.right, 10 * Time.deltaTime);
      
  5. 缩放和看向
    这里补充一个点,脚本中可以定义一个Transform的对象,在Inspector窗口中,可以直接将一个游戏物体拖入,也就是说一个游戏物体就是一个Transform
    • 缩放
      缩放也是Transform的一部分,自然也会遵循世界坐标和相对父坐标两种情况
          //相对世界坐标系
          print(this.transform.lossyScale);
          //相对本地坐标系(父对象)
          print(this.transform.localScale);
      
          //注意:
          //1.同样缩放不能只改xyz 只能一起改(相对于世界坐标系的缩放大小只能得 不能改)
          //所以 我们一般要修改缩放大小 都是改的 相对于父对象的 缩放大小 localScale
          //this.transform.localScale = new Vector3(3, 3, 3);
      
      注:缩放没有相关的API可以直接进行修改,只能手动去进行修改
      this.transform.localScale += Vector3.one * Time.deltaTime;
    • 看向
      让一个物体盯着一个物体看,这个在VR中很有用,普通游戏开发也会用到,比如AI盯着玩家攻击,看向比较简单,就仅仅是一个API的使用
          //让一个对象的面朝向 可以一直看向某一个点或者某一个对象
          //看向一个点 相对于世界坐标系的
          //this.transform.LookAt(Vector3.zero);
          //看向一个对象 就传入一个对象的  Transform信息
          public Transform lookAtObj; // 定义并拖入关联我们要看向的那个物体
          this.transform.LookAt(lookAtObj);
      

四.父子关系及其操作

这个与之前提到的父子关系差不多,但是操作不同,并且通过Transform来操作父子关系更加强大

  1. 获取和设置父对象
            //获取父对象
            //print(this.transform.parent.name);
            //设置父对象 断绝父子关系
            //this.transform.parent = null;
            //设置父对象 认爸爸
            //this.transform.parent = GameObject.Find("Father2").transform;
    
            //通过API来进行父子关系的设置
            //this.transform.SetParent(null);//断绝父子关系
            //this.transform.SetParent(GameObject.Find("Father2").transform);//认爸爸
    
            //参数一:我的父亲
            //参数二:是否保留世界坐标的 位置 角度 缩放 信息
            //       true  会保留 世界坐标下的状态  和 父对象 进行计算 得到本地坐标系的信息
            //       false 不会保留 会直接把世界坐标系下的 位置角度缩放 直接赋值到 本地坐标系下 
            //this.transform.SetParent(GameObject.Find("Father3").transform, false);
    
    注:通过API设置父对象时,第二个参数比较重要,决定添加父子依赖关系后,子对象所在的位置,区别就是是否会进行一个相对关系的转换
  2. 一键断绝与孩子的关联
    使用一个API即可
    this.transform.DetachChildren();
  3. 获取子对象的操作
            //按名字查找儿子
            //找到儿子的 transform信息
            //Find方法 是能够找到 失活的对象的 !!!!! GameObject相关的 查找 是不能找到失活对象的
            print(this.transform.Find("Cube (1)").name);
            //他只能找到自己的儿子 找不到自己的孙子 !!!!!!
            //print(this.transform.Find("GameObject").name);
            //虽然它的效率 比GameObject.Find相关 要高一些 但是 前提是你必须知道父亲是谁 才能找
    
            //遍历儿子
            //如何得到有多少个儿子
            //1.失活的儿子也会算数量
            //2.找不到孙子 所以孙子不会算数量
            print(this.transform.childCount);
            //通过索引号 去得到自己对应的儿子
            //如果编号 超出了儿子数量的范围 那会直接报错的 
            //返回值 是 transform 可以得到对应儿子的 位置相关信息
            this.transform.GetChild(0);
    
            for (int i = 0; i < this.transform.childCount; i++)
            {
                print("儿子的名字:" + this.transform.GetChild(i).name);
            }
    
    注:Find不能够寻找儿子以下的子物体,下面提供一个拓展方法来进行实现
    /// <summary>
    /// 根据名字找到 子对象 不管藏多深
    /// </summary>
    /// <param name="father">调用方法的对象</param>
    /// <param name="childName">要找的对象名字</param>
    /// <returns></returns>
    public static Transform CustomFind( this Transform father, string childName)
    {
        //如何查找子对象的子对象
        //我要找的对象
        Transform target = null;
        //先从自己身上的子对象找
        target = father.Find(childName);
        if (target != null)
            return target;
    
        //如果在自己身上没有找到 那就去找自己的子对象的子对象
        for (int i = 0; i < father.childCount; i++)
        {
            //让子对象去帮我找 有没有这个名字的对象
            //递归
            target = father.GetChild(i).CustomFind(childName);
            //找到了 直接返回
            if (target != null)
                return target;
        }
    
        return target;
    }
    
    这里主要是使用一个递归,来完成对孩子深度查询
  4. 子物体的相关操作
            //判断自己的爸爸是谁
            //一个对象 判断自己是不是另一个对象的儿子
            if(son.IsChildOf(this.transform))
            {
                print("是我的儿子");
            }
            //得到自己作为儿子的编号
            print(son.GetSiblingIndex());
            //把自己设置为第一个儿子
            son.SetAsFirstSibling();
            //把自己设置为最后一个儿子
            son.SetAsLastSibling();
            //把自己设置为指定个儿子
            //就算你填的数量 超出了范围(负数或者更大的数) 不会报错 会直接设置成最后一个编号
            son.SetSiblingIndex(1);
    

五.世界坐标系和本地坐标系的相互转换

该点主要是本地坐标系转世界坐标系比较有用,比如玩家释放一个技能后,应该出现在正前方的一个特效,这是相对于本地坐标系的,但是所有游戏物体都是建立在世界坐标系的基础上进行展现的,所以得转一下

  1. 世界坐标转本地坐标 image.png
    如图所示,世界转本地的图解,这是xz平面,其他平面也同理,做垂线即可,那么本地转世界也是同理,下面就主要整理API的使用
        print(Vector3.forward);
    
        //世界坐标系 转本地坐标系 可以帮助我们大概判断一个相对位置
    
        //世界坐标系的点 转换 为相对本地坐标系的点  
        //受到缩放影响
        print("转换后的点 " + this.transform.InverseTransformPoint(Vector3.forward));
    
        //世界坐标系的方向 转换 为相对本地坐标系的方向 
        //不受缩放影响
        print("转换后的方向" + this.transform.InverseTransformDirection(Vector3.forward));
        //受缩放影响
        print("转换后的方向(受缩放影响)" + this.transform.InverseTransformVector(Vector3.forward));
    
    简单记忆就是Vector的转换是会受游戏物体的缩放而导致结果不同,这里有向量的转换,其实就是将向量进行平移,比如世界向量转本地向量,直接平移世界坐标中的向量到本地坐标系中,与本地坐标系的原点重合得到的本地坐标系的向量
  2. 本地坐标转世界坐标
            //本地坐标系的点 转换 为相对世界坐标系的点 受到缩放影响
            print("本地 转 世界 点" + this.transform.TransformPoint(Vector3.forward));
    
            //本地坐标系的方向 转换 为相对世界坐标系的方向 
            //不受缩放影响
            print("本地 转 世界 方向" + this.transform.TransformDirection(Vector3.forward));
            //受缩放影响
            print("本地 转 世界 方向" + this.transform.TransformVector(Vector3.forward));