unity 3D *入门*重点笔记*

(基于阿发你好的教程做的重点笔记)

杂乱的出生
 

Y up  Z forward  X right

一般来说要求模型正脸与Z相同

Pivot 轴心 center 几何中心 一般来说,物体的轴心并不在几何中心处

组件代表了功能,需要什么功能就添加什么组件

调整摄像机角度的两种方法

Align with view

脚本命名只能用英文或者下划线等等

C#文件名必须与类名一致,否则无法挂载

若要修改类名,应在VS中右键重命名

脚本必须挂载到物体上,才会被调用

Edit Mode / Playmode

支持实时调试,临时修改,但运行时候无法保存,只有退出运行模式后才可以修改保存

You must exit play mode to save the scene

Frame 一个游戏帧  framerate 刷新率/帧率  FPS frames Per Second 每秒刷新多少帧

Update() 每更新一帧运行 帧更新  unity没法固定帧率,只能尽量,无法独占CPU/GPU只能尽可能多的努力更新,尽量照办,差不多就行


MonoBehaviour    

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class test_3 : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

//所有的unity脚本都派生自该类,基于unity engine
//消息函数,或称作事件函数  常见的消息函数: 

初始化Awake 第一阶段先执行,脚本禁用只有awake会被调用,unity调用此方法顺序不确定

(但是,可以通过指定优先级,值越小,优先级越高,在Project Settings/_Execution Order里面)

 Start  只执行一次,第一次启用

帧更新: Update每一秒调用几十次   OnEnable  OnDisable

主控脚本 可以用空节点放一个mainlogic 作为主控脚本

脚本的参数可以在Inspector中显示出来,1.需要在public下    2.参数名称即为变量名(如下图)      3.设置变量的默认值,可以在主控用reset重置   4.参数的工具提示,可以用[Tooltip(”xxx用处“)]指定

顺序:先定义,再参数工具,再awake,再start

参数的类型

值类型(默认为0):int float bool (只有引用类型可以设置为null)本质都是struct 结构体类型,string原则上属于class类型

引用类型:引用节点GameObject /组件Transform /资源Materia /数组类型

在运行时想保存好的组件或值,可以copy component然后paste Component Values

如果要引用别的组件,可以在检查器里对其进行赋值,引用目标组件 public AudioSource bgm;

脚本组件的引用,在一个脚本里访问另一个脚本组件,需要先API获取,再直接引用

FanLogic fan = node.getComponent<FanLogic>();                public FanLogic fan;


物体的运动

要控制物体的运动,需要修改他的position/localPosition 物体的匀速运动 delta Time来计算,如下面代码,需要取local position坐标pos,再增加一个增量,再把新坐标pos给localposition。

物体的运动本质 就是 在帧更新中移动物体的位置坐标

借助delta Time 可以让物体运动更匀速,速度*时间等于运动距离,距离再相加坐标得新坐标。

public class test_1 : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("** SimpleLogic Start() . .");
        Application.targetFrameRate = 60;
    }

    // Update is called once per frame
    void Update()
    {
        Debug.Log("** update 帧更新..时间差 = " + Time.deltaTime);
        float speed = 3;
        float distance = speed * Time.deltaTime;
        //核心代码
        Vector3 pos = this.transform.localPosition;
        pos.z += distance;//0.02f
        this.transform.localPosition = pos;
    }
}

更简洁的方法是用下面这一行代码实现,括号中分别代表xyz移动方向,加个- 可以反方向

this.transform.Translate(0 ,0 ,-distance);
  • transform: 是一个组件或属性,用于处理和修改物体的位置、旋转和缩放等变换信息。
  • Translate(): 是一个方法,用于将物体沿指定的轴向移动一定的距离。

space.world 沿着世界坐标系进行运动 & space.self  沿着物体自身坐标系进行运动(更常用)前后左右运动(规范模型:脸的朝向与z轴一致)

this.transform.Translate(0 ,0 ,distance,Space.Self);

如果要转动物体,看向目标物体,并沿着目标物体方向移动可以使用代码:

GameObject flag = GameObject.Find("红旗");//使用名字或者路径查找物体
this.transform.LookAt(flag.transform);//使物体的Z轴指向物体
//假如物体在空中,会原地起飞
this.transform.Translate(0 ,0 ,distance,Space.Self);//运动时使用自身轴向

lookat尽量再start里调用,因为start只运行一次,效率更高,update则会执行多次。

(tips:运行时候使用edit里的lock view to selected观察效果会更好)


物体的旋转

在unity官方文档中可知,unity底层使用四元数来表示旋转,结构紧凑,不受万向锁影响,可以轻松插值,unity内部使用四元数来表示所有旋转,但是基于复数不易理解,建议使用欧拉角进行旋转

*Quaternion 四元组
transform.rotation = ...*/
//欧拉角,和unity的transform栏完全对应
transform.localEulerAngles = new Vector3(0, 45, 0);

(45,405,-315效果完全一样,加减360度罢了)

在update()里修改角度,使物体持续旋转,使用rotatespeed可以使旋转的更加匀速,因为基于根据时间增量来计算角度增量。

float rotateSpeed = 100;//优化代码,使每秒转100°角
Vector3 angles = this.transform.localEulerAngles;
angles.y += rotateSpeed * Time.deltaTime;//0.5f
this.transform.localEulerAngles = angles;

localEulerAngles(easy,使用多) / rotation(麻烦,使用少)

使用rotate方法更简洁

this.transform.Rotate(0,rotateSpeed*Time.deltaTime,0,Space.Self);

自转与公转

公转的本质是控制父物体转动,带动子物体转动,可以设置空物体作为中心点,创建物体

float rotateSpeed = 60;
Transform parent = this.transform.parent;
parent.Rotate(0, rotateSpeed* Time.deltaTime, 0, Space.Self);

物体&资源

如何获取一个物体:不建议使用GameObject.Find()因为执行效率低,不能自适应变化,当目标节点改名时会出错,建议使用添加引用,不容易出错

public GameObject WingNode;

层级关系,父子关系是由transform来维持的,获取父级或者父级节点(GameObject类似于充当了一个容器,核心都来源于各个组件):

Transform parent = this.transform.parent;
GameObject parentNode = this.transform.parent.gameObject;

找子物体 transform.Find(),按名称查找      二级子物体需要指定路径(for each遍历                 

get child() 按照索引获取:Transform aa = this.transform.GetChild(0) )顶级节点parent = NULL

Transform aa = this.transform.find("aa")
Transform bb = this.transform.find("bb")
Transform cc = this.transform.find("bb/cc")

activeself 判断当前状态         setactive 可以控制inspector物体(对勾)是否显示

transform.Find("/222")从场景下查找   小案例:俄罗斯方块的颜色切换和运动

public class PlayerLogic : MonoBehaviour
{
    int m_index = 0; // 表示显示的是哪一个形状

    // Start is called before the first frame update
    void Start()
    {
    }
    // Update is called once per frame
    void Update()
    {
        if(Input.GetKeyDown(KeyCode.Space))
        {
            ChangeShape();
        }

        // 向前运动 
        float speed = 1;
        this.transform.Translate(0, 0, speed * Time.deltaTime, Space.Self);
    }

    private void ChangeShape()
    {
        // 先把原来的形状,隐藏
        Transform child = this.transform.GetChild(m_index);
        child.gameObject.SetActive(false);

        m_index += 1;
        int count = this.transform.childCount;
        if (m_index >= count)
            m_index = 0;

        // 显示新的形状
        child = this.transform.GetChild(m_index);
        child.gameObject.SetActive(true);
    }
}

资源管理:音频资源:在全局public class下写 public AudioClip audioSuccess; 在update里

if(Input.GetKeyDown(KeyCode.X)){
AudioSource audioSource = GetComponent<AudioSource>();
audioSource.PlayOnShot(audioSuccess);}

定时调用函数:InvokeRepeating          unity核心是单线程的,不用考虑多线程(互斥/并发等)

每次InvokeRepeating,都会添加一个新的调度,多个重复写会重复调用,效果会叠加

isInvoking(func)判断func是否在Invoke队列         cancelinvoke(func)取消func的Invoke调用

Cancellinvoke()取消当前脚本的所有Invoke 调用

if(!IsInvoking(func)){ //判断是否在队列里
InvokeRepeating(func, delay, interval)}

红绿灯切换小练习: 

public class LightLogic : MonoBehaviour
{
    [Tooltip("红、绿、黄按顺序指定")]
    public Material[] colors;

    int m_index = 0; // 红灯开始

    // Start is called before the first frame update
    void Start()
    {
        ChangeColor();
    }

    // Update is called once per frame
    void Update()
    {        
    }

    void ChangeColor()
    {
        // 当前材质
        Material color = this.colors[m_index];
        MeshRenderer renderer = GetComponent<MeshRenderer>();
        renderer.material = color;
        Debug.Log("* Change -> " + m_index + ", time=" + Time.time);
                
        if (m_index == 0)
        {
            // 红 -> 绿,间隔3秒钟
            Invoke("ChangeColor", 4);
        }        
        else if (m_index == 1)
        {
            // 绿 -> 黄,间隔1秒钟
            Invoke("ChangeColor", 4);
        }        
        else if (m_index == 2)
        {
            // 黄 -> 红,间隔1秒钟
            Invoke("ChangeColor", 1);
        }

        // 切换
        m_index++;
        if (m_index >= 3) m_index = 0;
    }
}

 小风扇练习:

public class FanLogic : MonoBehaviour
{
    // 最大转速
    public float maxRotateSpeed = 720;

    float m_speed = 0; // 当前转速
    bool m_speedUp = false; // true 加速 , false 减速

    // Start is called before the first frame update
    void Start()
    {
        InvokeRepeating("AdjustSpeed", 0.1f, 0.1f);
    }

    // Update is called once per frame
    void Update()
    {
        // 点一下,加速。再点一下,减速。
        if(Input.GetMouseButtonDown(0))
        {
            m_speedUp = !m_speedUp;
        }

        // 旋转
        if(m_speed > 0)
        {
            this.transform.Rotate(0, m_speed * Time.deltaTime, 0, Space.Self);
        }
    }

    // 速度调整
    private void AdjustSpeed()
    {
        if(m_speedUp)
        {
            if (m_speed < maxRotateSpeed)
                m_speed += 10;
        }
        else
        {
            m_speed -= 10;
            if (m_speed < 0)
                m_speed = 0;
        }
    }
}

向量

API:Vector3 v = new Vector(x,y,z);    float len = v.magnitude;(求向量的长度,封装了方法,属性)

单位向量,长度为1的向量        标准化 Normalize:缩放一个向量,使其长度为1

Vector3 v = new Vector(x,y,z);          Vector3 v2 = v.normalized;

向量加法,乘法:标量乘法b=a * 2,点积c=Vector3.Dot(a,b),差积c=Vector3.Cross(a,b)

向量测距,用于求两物体间的距离,本质是轴心点间的距离

// 取得目标 , 也可以用 this.transform.Find("/红旗")
        GameObject target = GameObject.Find("红旗");

        // 两个物体的坐标点
        Vector3 p1 = this.transform.position;
        Vector3 p2 = target.transform.position;

        // 求距离 
        Vector3 direction = p2 - p1;  // 方向向量
        float distance = direction.magnitude; // 距离

        // 直接用 Vector3.Distance() 也可以求距离
        float distance = Vector3.Distance(p2, p1);

预制体 Prefab 只包含节点信息,文件中不包含材质,贴图数据,仅包含引用     

导出时候需要勾选包括依赖(dependencies)prefab ->fbx mat cs               

Prefab instance 预制体实例  实例会多选项和inspector中的三个按钮,可以反向定位关联的资源    

prefab修改之后,预制体创建的实例也会发生变化,所以如果要单独修改可以unpack

创建实例也需要销毁,Object.Destroy(this.gameObject);


物理系统

rigidbody 变成了物理系统里的刚体,由物理引擎根据质量,力和速度的关系,接管刚体的运动~

物理系统不仅接管了刚体的运动,也接管了碰撞,

collider 下落碰撞体,会停止,指示的是边界和范围,有碰撞的计算

刚体的反弹和摩擦,弹性系数bounciness(最大是1)球和地面都设置弹性系数,会好很多,摩擦力friction(dynamic static 动静)

碰撞体组件

1 Rigidbody  ☑ Is Kinematic 声明为运动学刚体

2 Collider      ☑ Is Trigger  触发器

3 挂一个脚本,添加消息函数     void OnTriggerEnter (Collider other)     {             }

物理引擎探测trigger ,不会阻止物体或者反弹,物理引擎计算的是collider之间的碰撞,和物体自身形状无关,当检测到碰撞时,会调用当前节点脚本中的 OnTrigger 消息

猜你喜欢

转载自blog.csdn.net/weixin_71572744/article/details/140607308