unity-1

news2024/12/22 22:17:54

创建游戏对象(游戏物体)

可通过unity中的菜单栏中的Gameobject创建;也可在Hierarchy(层级)中创建,

双击即可居中看到。

在Hierarchy空白处右键即可看到,能创建游戏对象。

在Scene框中,鼠标坐标选择物体;右键 旋转视角;

坐标系

 unity是以左手坐标系。

世界坐标系:类似地球的经纬度,你在场景中创建一个物体,都要有一个坐标原点把?以此确定相对位置,即(0,0,0)。

可创建一个物体,修改其坐标为(0,0,0) 。 

在Hierarchy(层级)栏中,可拖拽一个物体到另一个物体中,使其形成父子关系。

有个小三角,表示其含有父子关系。
在具有父子关系的物体中,移动子物体的位置不会影响父物体的位置;但是,若移动父物体的位置,则子物体跟着移动,以使得父子的相对位置不变。

通过右侧inspector栏修改子物体的X,Y,Z坐标之后,无论怎么移动父物体(子物体会跟着移动),但是子物体的position的X,Y,Z坐标大小不会变化, 即这个X,Y,Z表示的是相对位置(而不是世界坐标系的位置)。  这里X,Y,Z的相对位置,代表该物体相对于其父物体的位置。

其实,就是当 某个物体 没有 父物体 时,其X,Y,Z就是相对于世界坐标系的;
                  当 某个物体 有     父物体 时,其X,Y,Z就是相对于父物体的。

全局坐标轴示意图

左上角圈中的地方,可将坐标轴修改为:全局、局部。

全局就是点击物体时显示的坐标轴 同  右上角 世界的坐标轴 方向。 
局部就是点击物体时显示的坐标轴 会以物体为中心显示, 大概率与世界坐标轴方向不同。

局部坐标轴示意图

物体的基本操作

上图中,可看到三根轴,颜色及方向  与 右上角的世界坐标系 一样,
另外,还有三个面,且带有颜色,  红色的轴(X轴)与 红色的面垂直,鼠标坐标点住红色的面进行拖拽的话, 物体对应红色轴的坐标是不变的, 只有另外两个方向的坐标会变。

旋转工具

可以看到,在左侧菜单栏中点击旋转工具后,  物体旁出现三根弧线,又是与坐标轴对应的红蓝绿。  鼠标左键点击红色的弧线转, 就是绕着x轴旋转

缩放工具

拖拽红色的轴, 就是沿着x轴缩放;    点击灰色的那个小立方体,就是整体缩放(三个方向同时缩放)

矩形工具

一般矩形工具  在 2D、UI中使用,使物体沿着某个平面缩放。

Transform tool

其实就是个综合工具,看图,同时显示 沿轴移动,沿轴缩放,沿轴旋转的工具,减少切换工具的时间。

编辑碰撞器

就是图中黄色的框框, 可以拖拽放大。按照这名字,我不确定是不是用在碰撞检测上, 就像:游戏中 一个角色攻击了另一个角色, 怎么判断是否攻击到呢? 就用这个?

最后,左侧工具栏的快捷键是 QWERTY.

导入游戏模块

可在项目project栏中,右键 :show in explorer  。进而显示出左侧这个文件夹。
在该文件夹下创建文件或删除文件  等同于  在项目栏中创建文件或删除文件

需要导入文件/模块时, 可拖拽到  项目栏(project)中 Assets文件夹下, 也可拖拽到 上图左边那个资源文件夹下。 效果一样。

将自身所作的内容导出成包

导出包

在project栏中,  选中自己要导出的内容,右键 Export package , 再确认要选的内容导出即可。

正常来讲,这个立方体其实就是那些三角形的线,  我们怎么看到它这白色的面, 是软件给我们渲染的, 即上图右下角的材质球,(灰色那些,说明不能修改它这个材质球,但是可创建新的材质球)

材质创建

在project栏中,右键--》create--》Material         即可创建材质。

将材质拖拽到物体上,即应用材质。

资源商店

资源商店

在Window栏中,Asset Store 就是资源商店,打开再open online

资源有付费的也有免费的。

另外,想加载自己已经添加的资源的话,

可点击 window--》Package Manager 

便会出现包管理器

包管理器

再如上图点击My Assets , 便可看到自己拥有的资源,  右上角有搜索🔍,能利用关键字搜索自身的资源。

脚本组件

        一个物体,可能具有 特效、重力、碰撞等等,每个物体都会有其特定的功能,而其实很多功能是很基础的,每个物体都有的,即功能具有重复性。           不可能每一个物体的每一个功能都写一段代码,重复多次写。于是就有了脚本组件。

选中要添加脚本组件的物体,在检查器inspector中,下方有个 添加组件add component

如果已有的组件中不存在所要的功能,那就得编写脚本。

空物体也是物体,虽然刚创建空物体时,它像是透明的,但它也有Transform组件,表示它的位置,旋转角度,比例。   每一个创建的物体都有Transform。

其实每一个物体都是由一个空物体得来,只是在上面增加组件使其具有我们想要的形态罢了。

灯光物体 可以 由一个空物体赋予灯光组件light形成。

摄影机 可以由一个空物体赋予摄影机组件camera形成。

不同物体的表现形态不同都是因为组件。。

创建不存在的组件: 在project栏中空白处,右键creat - - C# Script 就是了。

创建组件也就是通过C#文件创建。

创建时需要注意:脚本名字和脚本内部的类名必须相同。(有时候可能改了脚本名,忘记改类名)

脚本的生命周期

        除Start、Update两个方法外,还有其他生命周期方法。如图

周期方法


物体的脚本组件,那个小勾是勾上的,则组件是激活状态(一开始默认勾上)

OnEnable在组件每次激活时都会执行一次,当取消勾选后再次勾选,也会执行。
取消勾选后,即取消激活组件,OnDisable执行一次。

Update是每一帧都会执行一次。(平时打游戏说的帧)

Update 、 LateUpdate都是与帧相关的

FixUpdate与帧无关,它是与时间相关,每隔固定时间间隔就会执行一次。

帧与电脑性能相关。假如电脑只能跑30帧,那么Update 、 LateUpdate每秒执行30次,若电脑能跑200帧,就能每秒执行200次;而FixUpdate,若间隔0.05秒执行一次,那无论你电脑能跑多少帧,其每秒都会执行20次。

OnDestroy,只有物体移除该组件时,才会执行。

脚本的执行顺序

        当一个游戏物体身上有多个脚本时,先执行哪个脚本?

游戏物体  不是  执行完一个脚本的Awake、Start、Update,再去执行另一个脚本的Awake、Start、Update。

而是 执行所有脚本的Awake,再执行所有脚本的Start......

控制顺序:第一种方法-------例如两个脚本t1,t2;想让t2先输出某些内容,就把内容放在t2的Awake方法中,t1的Awake方法不放东西。

显然,第一种方法不好。

第二种方法--------

也可点击脚本后,其右上角有个 执行顺序(execution order)

点击即可打开。

 修改脚本执行顺序

通过那个 “+” 号按钮,点击添加所要控制的脚本进去。
注意:从上到下,负数到正数, 可以把它当作一个x轴,上面是负半轴。 执行顺序是从上面到下面,即数值越小的越先执行。

(可修改数值,也可拖拽移动上下相对位置。)

别忘了 Apply 应用确定顺序。

按上图的顺序:执行顺序:t1的Awake、t2的Awake、t1的Start、t2的Start、t1的Update、t2的Update..................

做游戏就是,框架最底层的东西肯定要先初始化,所以其脚本应靠近上方。

游戏物体标记

        当游戏场景中有多个游戏物体时,可能为了分类,更好查找管理,而给它们打上标签。也方便我们下次点击该物体时,通过标签查看其是什么。例如:一类物体是“玩家”,一类是“NPC",一类是”boss“。

在inspector检查器处,Tag标签,点击Add Tag可增加标签, 并可任意自定义标签名。
点击物体后,在Tag处选择标签给物体加上标签。

图层Layer也可增加,但是给定只有32个,且个别已经被固定使用。 
图层可用于碰撞检测。--这个图层的物体可被碰撞,那个图层...
Culling Mask(剔除遮罩),选择哪些图层可以被摄像机看到。

游戏中几个物体都设为kk图层,当在Culling Mask这里把  kk 的勾取消勾选,摄像机就看不到 kk图层的物体。 如下图所示:

游戏中的向量

向量的运算和意义

        向量的加减法法则,乘法,点乘运算。

加法法则:平行四边形法则

减法法则:类似

乘法:向量*标量------------方向不变,大小缩放。

点乘:关键是能通过这样获取两向量之间的夹角

利用归一化,把两个向量转化为单位向量,再代入上式中,两个向量的模就是1,等式右边就只有cos。 很快计算出夹角。

预制体与变体

        预制体其实就是做了一个游戏物体以后,把它身上所有组件合成一个整体,作为模板使用。 当下次要制作与预制体相似的游戏物体时,可通过预制体模板,先获得一个基础物体(由预制体创建),再进行修改。

对于变体,其是预制体的变体,可通过一个预制体创建一个实体后,把这个实体又做成模板(预制体) ,那就是两个相同的预制体了,但它两是独立的, 修改旧预制体的属性值等,不会影响新预制体的属性值, 但是我们又想她两是关联的,
则要在把旧预制体做成新预制体时,选择做成变体。  
变体的话, 修改旧预制体的属性值等,会相应地修改变体的属性值。

创建预制体

通过父子关系形成整体后,把父体从Hierarchy拖拽到Project处,就会在Project处形成预制体,还会有那个蓝色的图标。

预制体在Assets文件夹下是有文件的。

可将预制体导出成包,给他人使用。

改变预制体的属性值,会相应地改变通过预制体创建的实例。
但是改变实例的值,不会影响预制体。

(其实就是模板与具体实例的关系。修改模板,那么通过模板创建的实例也会改变,但是你仅仅修改实例,不会影响到实例对应的模板)

修改模板的值会影响对应的具体实例

预制体创建的具体实例添加的组件,而预制体没有的组件,点击实例时,该组件会有个 “+” 。

可通过组件右边的 三点 按钮,----Add component ---apply to prefab 应用到 对应的预制件上

这样的话,模板也有该组件了,当模板也有该组件后, 组件的“+”号就消失了。

在实例中,有两个组件是 对应的预制件没有的。 
可以像上面一样将这两个组件应用到预制件中;
不过有多个组件时,一个一个应用添加太慢,
在上面的Prefab栏 对应的  override(覆盖)栏中, 点击 Apply All 将所有未添加的组件应用到预制件。

有时候一个预制体,需针对其修改个别点, 让其变成新的 预制体,

我们当然可以通过预制体创建一个实例, 再修改实例成新模板的样子, 将其从Hierarchy栏中拖拽到Project栏中, 建立为一个新的预制体, 这样就是两个预制体了,且它两形近。

这种做法的缺点:两个预制体互相独立,对应的实例也是独立的, 修改1号预制体会影响其对应的实例,不会影响2号预制体对应的;同理,修改2号也是如此。

可有时,我又想,2号预制体是从1号中衍生的,能不能修改1号模板,进而影响到1号、2号对应的实例呢?-------------------变体

用1号预制体的实例创建新模板时,选择其作为 预制体变体(Prefab Variant) 

图标会些许不一样

注意:  此时,变体模板是依赖于 原始预制体的。 两者并没有互相独立, 修改原始预制体的值会影响预制体变体,  即同时改变两类实例;  但是, 修改变体的属性值 不会影响原始预制体对应的实例。

        

vector3的使用

        在使用c#脚本对unity进行编程时,vector3可以代表什么?

1.代表向量

2.代表坐标
        同上图,显然也能作为坐标使用。

3.代表 旋转

4.代表缩放

x,y不要动,z变为0.5倍

遇到vector的时候,就可考虑是以上几种,判断具体是哪一种。

通过vector3.zero初始化可以获得一个(0,0,0)的向量。

通过vector3.one初始化可以获得一个(1,1,1)的向量。

通过vector3.forward初始化可以获得一个(0,0,1)的向量。

通过vector3.back初始化可以获得一个(0,0,-1)的向量。

通过vector3.left初始化可以获得一个(-1,0,0)的向量。

通过vector3.right初始化可以获得一个(1,0,0)的向量。

通过vector3.up初始化可以获得一个(0,1,0)的向量

通过vector3.down初始化可以获得一个(0,-1,0)的向量

vector3(左右,上下,前后)

使用Vector3.Angle(a,b)计算两向量a,b之间的夹角。

使用Vector3.Distance(a,b)计算两点a,b之间的距离。

计算向量的点乘,叉乘。

使用Vector3.Lerp()在两向量之间进行插值,得到一个向量.

规范化就是变为单位向量

方向的描述

        可用欧拉角、四元数描述方向。

new Vector3(0,30,0) 表示沿着y轴旋转30°,x,z方向不动。

Quaternion是四元数, 可以用new来创建,用new需要给定四个数;

用Quaternion.identity初始化的话,就是所有方向都不动。

可以将欧拉角转化为四元数,即用一个欧拉角初始化为四元数

以通过看向一个物体(向量)来获取角度。

Debug

        Debug.Log("正常输出信息")
        Debug.LogWarning("输出的警告信息")
        Debug.LogError("报错信息")

        使用Debug绘制一条线

        Debug.DrawLine(起点,终点,Color.颜色英文)

        Debug.DrawLine(Vector3.zero,Vector3.one,Color.red)

        使用Debug绘制一条射线

        Debug.DrawRay(射线起点,射线方向,颜色)

        

起点可以用三维坐标表示,方向也可以是三维坐标表示。

上面Vector3.zero, Vector3.up 虽然都是使用的Vector3,但是它们含义各不相同,一个代表点,一个代表方向

物体类的使用

        在游戏场景的物体中,都挂载了脚本,如何在脚本当中,获取到挂载了该脚本的物体?

#获取当前脚本所挂载的物体
GameObject go = this.gameObject;
Debug.Log(go.name);

甚至this都可以省略,直接用gameObject就能得到游戏物体。

Debug.Log(gameObject.name)
#输出游戏物体名称
Debug.Log(gameObject.name);

#输出游戏物体的标签
Debug.Log(gameObject.tag);

#输出游戏物体的图层
Debug.Log(gameObject.layer);

注意:输出图层是输出该图层对应的索引值,而不是图层名字。

这个球体已经添加上了名为test的脚本,如何通过球体挂载的这个脚本控制另外一个立方体呢

首先,在test脚本中创建public GameObject cube;变量,注意,这里还未初始化。

接着,在unity中,点击挂载了test脚本的球体,查看inspector检查器中的脚本

可以看到,脚本中显示,Cube还没有对应的游戏物体GameObject

即脚本test中创建的public GameObject cube变量还未初始化,

在unity中进行初始化

将Hierarchy中的Cube游戏物体拖拽到检查器inspector的Sphere的test脚本下方的cube中。

另外一种初始化方式:

在inspector栏中sphere物体中点击上图那个小圆圈,就会出现下图的窗口,选择cube即可。

注意:创建的public GameObject cube变量,最后在unity中选择物体时,一定要选择cube物体,否则可能出错。

同时,注意到,脚本代码中的public GameObject cube变量 不是在C#中用代码初始化,而是在unity中初始化

通过上述方式,就可以在一个物体里写脚本,再通过这个脚本操作另外一个物体

----------------------------------------------------------------------------------------------------------

有如下图的游戏物体父子关系

现将GameObject物体取消激活

把原来的勾取消掉就是取消激活

会看到下图中,GameObject的子物体显示也是灰色的(即使Cube本身是激活的)

此时的状态下,在脚本当中coding上述代码,在unity运行,会得到
        False;
        True;

即cube在Hierarchy中是未激活的(灰色) ,但其自身的激活按钮是勾上的。

-----------------------------------------------------------------------------------------------------------

所有物体都有transform组件,如何获取

#获取脚本挂载的物体的transform组件
Transform trans = this.transform;
#同GameObject一样,可直接使用
Debug.Log(transform.position)

只有transform组件这么好获取。

其他的:
 

#获取其他组件
BoxCollider bc = GetComponent<BoxCollider>();

#获取当前物体的子物体身上的某个组件
GetComponentInChildren<CapsuleCollider>(bc);


#获取当前物体的父物体身上的某个组件
GetComponentInParent<BoxCollider>();

#添加一个组件
GameObject.AddComponent<AudioSource(组件名)>;
Cube.AddComponent<AudioSource(组件名)>;

        上面说过在脚本中创建变量后,可在unity用拖拽的办法给它从初始化,其实还有几种其他办法进行初始化 。

1.通过游戏名称

#通过游戏物体的名称来获取游戏物体
GameObject test = GameObject.Find("Test");

上述方法需要在unity中切实创建了一个游戏物体,且其名为Test

2.通过游戏标签

#通过游戏标签获取游戏物体
GameObject test = GameObject.FindWithTag("Enemy");

上述方法如果是“Enemy”标签有对应多个物体呢。咋办?---------尝试过后是:会把其余为Enemy标签的物体在unity中删掉,但是不善标签为Enemy的空物体。

前面我们学习过了预制体的创建(从Hierarchy栏把物体拖拽到Project栏即可)

我们可以通过预制体可创建多个长得一样的实例,之前我们是通过拖拽生成的方式,

那么,如何在脚本中用代码给我们生成呢?

先创建好一个预制体

接着在脚本中 

#获取预制体
public GameObject Prefab;

同前面那样,用拖拽的方法给其初始化即可。

#通过预制体实例化一个游戏物体
Instantiate(Prefab);

在unity运行后,就会给我们创建一个物体,并且名字带有Clone

#通过预制体实例化一个游戏物体,并且作为当前脚本挂载的物体的子物体
Instantiate(Prefab,transform);

还有很多种方式来进行实例化

GameObject go = Instantiate(Prefab,Vector3.zero,Quaternion.identity);
#销毁方法
Destory(go);

以上是实例化后,并放在初始位置,不旋转。

游戏时间类

void Start(){

    //游戏开始到现在所花的时间      
    Debug.Log(Time.time); #放在start方法是这个含义,那放在update方法中还是?
    //时间缩放值
    Debug.Log(Time.timeScale);#正常是1倍,可用于进行游戏加速减速暂停
    //固定时间间隔
    Debug.Log(Time.fixedDeltaTime);
    
}

void Update(){
    
    //上一帧到这一帧所用的游戏时间
    Debug.Log(Time.deltaTime);
}

#想要记录从开始到现在的时间可以
float timer = 0 ;
timer += Time.delataTime;

路径权限Application

用unity写游戏时,unity是跨平台的,所以在不同平台的目录结构可能不一样。

当我们在unity要写一个文件时,文件应存放到哪个路径下,要读一个文件时,从哪里读?

这些都是Application类管理的。

#游戏数据文件夹路径(只读,游戏打包发布后,这些会被加密压缩)
Debug.Log(Application.dataPath);#其实就是Asset文件夹的路径


#持久化文件夹路径(写文件的话,就写在该路径)
Debug.Log(Application.persistentDataPath);
#上面输出的这个路径就是对应了不同系统给自己的应用程序分配的空间,空间用于保存游戏的一些数据资料
#所以上面会拿到不同平台的可储存文件的路径


#StreamingAssets文件夹路径(只读,放配置文件)
Debug.Log(Application.streamingAssetsPath);
#其实就是游戏数据文件夹路径再加一个/StreamingAssets
#而StreamingAssets文件夹是要自己在Assets文件夹下创建的
#为何要这个路径?(游戏打包发布后,这个路径下的文件不会加密压缩,文件能正常显示)
#该路径适合放 配置文件,能让所有人都能正常看见

#临时文件夹
Debug.Log(Application.temporaryCachePath);

#控制是否能在后台运行
Debug.Log(Application.runInBackground);
#玩游戏到一半的时候,我切到网易云听歌,游戏在后台是否继续运行

#打开Url , 用浏览器打开对应的url链接
Application.OpenURL("https://www.bilibili.com/");

#退出游戏
Application.Quit();

场景类与场景管理类

        一个游戏中,会有多个不同的场景,需要在不同的场景进行切换,这是如何做的?

可以看到,层级Hierarchy栏下有默认场景SampleScene , 对应Project栏下Scenes文件夹下的名为SampleScene文件。

我们可以在Project栏的Assets文件夹下的Scenes文件夹下右键创建新场景 creat--Scene               

要想通过脚本的代码实现切换场景

首先:

                                           

在窗口栏的File中, 点击 Build Settings (生成设置)                 

                                                                                                                                                             在BuildSettings中点击Add Open Scenes会把当前打开的场景给加载进去,如当前打开的是MyScene,点击Add Open Scenes 会把My Scene添加进去,如下图

但是这种方式就比较慢,另一种方式是拖拽

直接把Scenes文件夹下的场景文件拖拽到那里。


接着,便能创建游戏物体,赋予脚本,在脚本中写场景有关的代码

using UnityEngine.SceneManagement;
void start(){
    #场景跳转,既可用索引值,也可用场景名称
    SceneManager.LoadScene(1);#SceneManager.LoadScene("MyScene");
    #获取当前场景
    Scene scene = SceneManager.GetActiveScene();
    #场景名称
    Debug.Log(scene.name);
    #场景是否已经被加载了
    Debug.Log(scene.isLoaded);
    #场景路径
    Debug.Log(scene.path);
    #场景在Build Settings 中的索引值
    Debug.Log(scene.buildIndex);
    #获取场景的根游戏物体
    GameObject[] gos = scene.GetRootGameObjects();
    Debug.Log(gos.Length);

//场景管理类
    Debug.Log(SceneManager.sceneCount); //当前已加载的活动场景数量
    //创建新场景
    Scene newScene = SceneManager.CreateScene("newScene");
    //卸载场景
    SceneManager.UnloadSceneAsync(newScene);
    //加载场景
    SceneManager.LoadScene("MyScene",LoadSceneMode.Single);
    //上面这种方式很简单,作为替换的形式。当前的活动场景变为MyScene这个场景
    SceneManager.LoadScene("MyScene",LoadSceneMode.Additive);
    //Additive方式是添加,活动场景变为 原来的+MyScene。 场景内容叠加在一起

    

}

场景异步加载

加载的方式可以是同步也可以异步加载。

同步就是顺序执行所有代码;  可能会有卡顿现象。

异步则是把耗时操作交由其他线程去执行,执行完再把结果返回。

C#使用多线程实现异步,unity用协程实现异步。

using UnityEngine.SceneManagement;

AsyncOperation operation;

void start(){
    StartCoroutine(loadScene());    
}

#创建协程方法来异步加载场景
IEnumerator loadScene(){    #方法有返回值,返回值类型IEnumerator
    
    operation = SceneManager.LoadSceneAsync(1);//可用场景索引值或场景名称
    #加载完场景不要自动跳转
    operation.allowSceneActivation = false;//默认值是true,即自动跳转
    yield return operation;
}
float time = 0;

void Update(){  #获取场景加载进度。在Update方法中获取,因为它加载的时间可能长可能短
                #要时时刻刻获取它的加载进度。就可在Update里,每一帧都获取一下进度

    //输出加载进度0-0.9
        Debug.Log(operation.progress);
    //如果达到5s,再跳转
        if(time>5){
            operation.allowSceneActivation = true;
        }
    
}

了解transform

unity中的游戏物体之间可以有父子关系, 而父子关系是靠什么实现的?------transform组件

观察父子的状况,其实就是父物体与子物体的相对位置不会变化, 移动父物体时,子物体跟着动,保持相对位置。

而transform组件就是跟位置、旋转、缩放相关。

游戏物体在脚本中是GameObject类,但是该类中没有方法去控制父子关系。

使用Transform组件来控制游戏物体的父子关系,以及位置,旋转,缩放。

void Start(){

    //获取位置  position、localPosition都是vector3类型
    DebugLog(transform.position);#物体在世界坐标系的位置
    DebugLog(transform.localPosition);#物体相对于其父物体的位置

    //获取旋转 rotation、localRotation是四元数
    DebugLog(transform.rotation);  #相对于世界的
    DebugLog(transform.localRotation);#相对于父物体的
    DebugLog(transform.eulerAngles);#xyz方向旋转角
    DebugLog(transform.localEulerAngles);

    //获取缩放
    DebugLog(transform.localScale);#只有相对于父物体的缩放
}

当作一个3D游戏时,一般把(蓝)Z轴正方向当作前方,相当于世界的前方,(红)X轴正方向就是右方,(绿)Y轴正方向是上方。

//获取三个轴的方向向量
Debug.Log(transform.forward);
Debug.Log(transform.right);
Debug.Log(transform.up);

void Update(){
    
    //时时刻刻看向000点。 要时刻肯定是每帧都要,每帧就要用Update
    transform.LookAt(Vector3.zero);#是正前方的z轴始终会指向0-0-0点

    //旋转(自转)
    transform.Rotate(Vector3.up,1); #注,Update方法每帧执行一次,则会绕y轴每帧旋转1°
    
    //绕某物体旋转(公转)
    transform.RotateAround(Vector3.zero,Vector3.up,5);
    #三个参数:绕哪个点,哪个轴,旋转度数

    //移动
    transform.Translate(Vector3.forward*0.1f);//移动方向*速度
}
void Start(){

    //父子关系
    //获取父物体
    transform.parent.gameObject;
    //子物体个数。 父仅一个,子可多个
    DebugLog.(transform.childCount);
    //解除与子物体的关系
    transform.DetachChildren();
    //获取子物体
    Transform trans = transform.Find("Child");//通过子物体名称获得
    trans = transform.GetChild(0);//通过索引获取,第0个子物体
    //判断一个物体是否是另一个物体的子物体
    bool res = trans.IsChildOf(transform);
    DebugLog.(res);
    //设置为父物体
    trans.SetParent(transform);

}

电脑游戏操作方式

        对于游戏来说,每一刻都要监听键盘、鼠标的动静,即要把相应的监听代码放在Update方法中。

void Update(){
    
    #鼠标的点击
    #按下鼠标  0左键  1右键  2滚轮
    if (Input.GetMouseButtonDown(0)){
       Debug.Log("按下了鼠标左键");
    }
    
    #持续按下鼠标
    if (Input.GetMouseButton(0)){
       Debug.Log("持续按下鼠标左键");
    }

    #抬起鼠标
    if (Input.GetMouseButtonUp(0)){
       Debug.Log("抬起了鼠标左键");
    }
    

    #按下键盘按键
    if(Input.GetKeyDown(KeyCode.A)){
        Debug.Log("按下了A");
    }

    #持续按下键盘按键
    if(Input.GetKey(KeyCode.A)){
        Debug.Log("持续按下A");
    }
    
    #抬起键盘按键
    if(Input.GetKeyUp(KeyCode.A)){
        Debug.Log("松开了A");
    }
}

虚拟轴的使用

为何需要虚拟轴?

        这关乎到游戏兼容问题,我们做的一个游戏可能需要兼容多个平台,switch,任天堂,stream,有的玩家喜欢用键盘,有的喜欢用手柄。

如果代码写死,只能按”A“建左移,按"D"建右移,那游戏就只能在PC上跑了。

但是如果你设置了虚拟轴,就可以设置不同 的设备来操作这个虚拟轴。这样,随便不同的设备操作方式不同,但是都让它控制虚拟轴,代码通过判断虚拟轴做出不同的操作。比如人物的移动不通过按键来进行,而通过虚拟轴。这样无论什么游戏设备都能兼容这个游戏了。

Unity查看虚拟轴:

在窗口栏Edit - -- - - Project Settings

InputManager输入管理器中,点击展开Axes(轴) 

常用的是Horizontal 水平  、 Vertical垂直  两个。

打开horizontal水平轴

left是键盘的上下左右键的←键。   备用按键就是 a

注:并不是Axes下的所有项都能作虚拟轴, 例如Jump

Jump只存在一个按键,你就没法做成虚拟轴, 因为上面介绍虚拟轴时说过了,要两个按键来作虚拟轴。 Jump就只能作成一个虚拟按键了。

void Update(){
    //获取水平轴】 垂直轴
    float horizontal = Input.GetAxis("Horizontal");//用水平轴的名称获取
    float vertical = Input.GetAxis("Vertical");
    Debug.Log(horizontal+"     "+vertical);


    //虚拟按键
    if(Input.GetButtonDown("Jump")){
        Debug.Log("空格");
    }
     if(Input.GetButton("Jump")){
        Debug.Log("空格");
    }
     if(Input.GetButtonUp("Jump")){
        Debug.Log("空格");
    }



}

触摸方法的使用

        移动端游戏,手机平板要用触屏来玩游戏。

void Start(){

    //开启多点触摸
    Input.multiTouchEnabled =  true;

    }

void Update(){
    //判断单点触摸
    if(Input.touchCount = = 1){
        //触摸对象
        Touch touch = Input.touches[0];
        //触摸位置
        Debug.Log(touch.position);
        //触摸阶段
        switch(touch.phase)
        {
            case TouchPhase.Began:
                break;
            case TouchPhase.Moved:
                break;
            case TouchPhase.Stationary:
                break;
            case TouchPhase.Ended:
                break;
            case TouchPhase.Canceled:
                break;
        }
    }

    //判断多点触摸(假如这里是判断两点)
    if(Input.touchCount = = 2){
        Touch touch = Input.touches[0];
        Touch touch1 = Input.touches[1];
    
}

灯光与烘焙

                灯光有几种类型。Directional light ; Spot light; Point Light, Area light.(定向光,聚光灯,点光源,区域(仅烘焙))

定向光: 就是从无穷远处照射过来。   这是无论把灯光源物体放在哪里,整个场景的灯光都是从无穷远处按照灯光物体设置的方向照射过来。

对于这四个物体而言,影子都是一样的,影子的偏移量和方向是完全一样的。

无论把上图中的灯光物体放在哪里,它们影子都不变,只有灯光物体旋转方向才会改变影子

color可修改灯光的颜色,intensity修改灯光强度;

shadow type改变阴影类型:无阴影,硬阴影:阴影边缘是锯齿一样,对电脑性能消耗低;软阴影:阴影边缘被柔化,更真实,对电脑性能消耗大。

resolution:分辨率。低中高,非常高。越高性能消耗越大

Cookie:(剪影) 选一张镂空图片,再把灯光照射到上面进行投影,有点像皮影戏那种。

Draw Halo:(光晕)就是平常的光晕。勾上后,光源物体所在位置有光晕显示。

Flare:(炫光)之后用资源实现。

culling mask:(剔除遮罩) everything就是所有的图层都能受到照射。取消勾选的图层就会照射不到,接受不到灯光,一身黑。

聚光灯:就像是拿手电筒照射。中间那个点控制照射距离,环上的四点控制照射半径

点光源:顾名思义,就像是放个灯泡在那,向四面八方,360°发出光线。

看到这个球体了嘛,就是从点光源向四面八方照射,最远照射到球面。

区域灯光(仅烘焙):就是个平面,从这个平面发出平行光。形状可选矩形或圆形。

类似于影楼灯。

区域灯光怎么开启? 这就要说到 模式了。

Mode:realtime,mixed,baked(实时,混合,烘焙)

混合就是将 实时和烘焙 都用上。

实时和烘焙的区别:

实时就是在游戏场景中,实时计算生成的灯光。(每一帧的灯光都可能不同)像赛车游戏,车灯就需要实时的更贴切,因为照射的内容在变,物体形状变,那影子就会变。显然,实时灯光很耗性能。

烘焙灯光: 先调好灯光,在这个场景中此时有这个灯光照射下,会有一张阴影图或者说灯光照射的情况图,保存场景当前的灯光照射信息,这就是烘焙-----------把场景当前的灯光照射信息实时计算一次保存下来,之后作为场景的灯光效果打在场景上,相当于保存了一张灯光照射图再贴上去,以后不会变化灯光

区域灯光是仅烘焙的, 那么烘焙灯光怎么做:

选中区域灯光,确定对哪个物体进行烘焙, 选中物体

选中后,边界会有橙色线框着(选中父,就会把子也选上)

接着在检查器inspector中右边的static处把 Contribute GI 勾上。

现在就支持灯光烘焙了

点击window(窗口) --Rendering(渲染)--lighting(光照)

最后再点击Generate lighting(生成照明)

最后便有文件生成出来,就是场景当前光照的烘焙信息

这时,即使删掉灯光物体,场景还会有烘焙的灯光效果存在。

(灯光物体已经删掉)

这时就省性能,还有光照信息,它就是把烘焙灯光的贴图给贴上了。

摄影机

摄影机物体,在inspector检查器栏中,projection(投影)可选择为:perspective(透视)、orthographic(正交)

透视摄影机(perspective)

正交摄影机(orthographic)

正交摄影机就是一个矩形过去

接下来说一些细节

clear Flags(清除标志):skybox / solid color / depth only / Don't clear(天空盒/纯色/仅深度/不清除)

skybox的意思:通过相机,把天空盒的内容清除掉 , 然后自己添加一个天空盒skybox组件,弄一个非常好看的天空

可以到资源商店下载天空盒资源使用

虽然场景当中没有任何东西,但是对于相机而言,它就可以显示这个天空盒了。

solid color:纯色,相机看到的就是纯色,可以在background调整颜色。

Don't clear :不清除,就是拍摄不到,那就显示不了,都是黑色   一般不怎么 用

 depth only:仅深度,就是拍摄不到,我就不拍摄了(主要是用来叠加的)

(看不出来跟不清除有什么区别.....................)

仅深度的说明:(用来叠加)

上图中,相机只有一个主相机,场景有有一个平面和一个立方体,然而因为位置关系,主相机只拍摄到平面,拍摄不到立方体。

接下来,我们添加一个相机

第二个相机让其拍到立方体但是拍不到平面。

(注意:现在两个相机的clear flags 都是 skybox)

小的红框是你当前点击的相机拍摄到的

而大红框是看你设置的几个相机中,深度值最大的那个相机所拍摄到的内容

目前,camera 的深度depth 比 Main camera 大, 所以大红框显示camera拍摄到的内容

把camera的depth值设为-0.08, 

Main camera的值设为0.17. 此时Main camera的Depth值就比camera的大

此时,显示的内容应该是Main camera拍的内容

所以吗,,注意:相机的深度值越高,它拍到的内容就优先显示

把camera的Depth值重新调大

显示的内容是camera拍到的

现在,做一个操作,(让camera只收集它拍摄到的信息,而camera拍摄不到的那些区域,则不显示,只显示它拍摄到的内容------怎么做:将camera的 Clear Flages选为 Depth only)

这时,显示的内容:

camera拍摄到的立方体显示出来了,其他部分(camera没拍到的)不显示,其他部分的信息就找下一个相机(深度值比camera小)拍到的内容补充

所以仅深度就是把两个相机的拍摄内容叠在一起。

深度值较高的相机 我们就可以将其clear Flags改为 Depth only, 这样的话,我深度高,优先显示我,我拍摄到的内容显示我的,我拍摄不到的就找下一个相机(按深度递减) 

culling mask剔除遮罩:之前说过了,取消勾选的那些图层,如果有物体使用这些图层,这些物体将不会被拍摄到

FOV Axis:(FOV轴)可选为垂直、水平(vertical、horizontal)

选择垂直或水平,影响的是 下面那项:Field of View(视野)

视野值拖动修改,会改变相机形成的锥形视野空间。

clipping planes(剪裁平面)、Near(近面)、Far(远面)

就是视野棱柱的上下底面----近远面       在这个棱柱内的才能拍到

这是关于显示的内容的起点位置(  X,Y),WH是宽高比例

Target Texture 目标纹理, Target Display目标显示

目标纹理就是i相机拍摄到的内容渲染到目标显示上,

目标显示 是Display 1 , 游戏窗口也是Display 1

 这两者要一致,否则就是显示不了任何内容

相机拍摄到的内容不想让其显示在窗口怎么办?

目标纹理那里现在是  无(None),给它一个目标纹理,就会取代目标显示了。

在Project栏中,右键 create--Render Texture(渲染器纹理)、

把新建的渲染器纹理拖到相机的Target Texture上

这时,虽然我们有且仅有一个相机,但是其拍摄到的内容看不见了

相机拍到的内容都时时刻刻放到刚新建的纹理里面了

这个纹理可以拖到任何游戏物体上使用

原来的游戏物体

拖拽纹理到游戏问题上后

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1487401.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ubuntu环境下docker容器详细安装使用

文章目录 一、简介二、ubuntu安装docker1.删除旧版本2.安装方法一3. 安装方法二&#xff08;推荐使用&#xff09;4.运行Docker容器5. 配置docker加速器 三、Docker镜像操作1. 拉取镜像2. 查看本地镜像3. 删除镜像4. 镜像打标签5. Dockerfile生成镜像 四、Docker容器操作1. 获取…

07OpenCV 图像模糊

文章目录 图像掩膜操作模糊原理均值滤波高斯滤波中值滤波双边滤波算子代码 图像掩膜操作 图像掩膜操作 模糊原理 Smooth/Blur是图像处理中最简单和常用的操作之一 使用操作的原因之一就是为了给图像预处理时候减低噪声 图像噪声是指存在于图像数据中的不必要的或多余的干扰信…

docker基线安全修复和容器逃逸修复

一、docker安全基线存在的问题和修复建议 1、将容器的根文件系统挂载为只读 修复建议&#xff1a; 添加“ --read-only”标志&#xff0c;以允许将容器的根文件系统挂载为只读。 可以将其与卷结合使用&#xff0c;以强制容器的过程仅写入要保留的位置。 可以使用命令&#x…

C++笔试题(选择+编程)

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 选择题 请找出下面程序中有哪些错误&#xff08;&#xff09; int main() {int i 10;int j 1;const int *p1;//(1)int const *p2 &i; //(2)p2 &j;//(3)int *const p3 &i;//(4)*p3 20;//(5)*p2 30;//(6…

uniapp和vue项目配置多语言,实现前端切换语言

在uniapp中配置多语言功能&#xff0c;实现前端切换语言&#xff0c;可以按照以下步骤进行&#xff1a; 1. 创建语言包 首先&#xff0c;创建一个名为 lang 的目录&#xff0c;并在该目录下为每种支持的语言创建对应的JSON或JS文件。例如&#xff1a; lang/en.js&#xff08…

Vue3和ElementPlus封装table组件

最近学习vue3.2并自己在写一个项目&#xff0c;然后发现好几个页面都是列表页&#xff0c;重复写table和column也是觉得累&#xff0c;学习的项目列表页不算多&#xff0c;要是公司项目就不一样了&#xff0c;所以就想着自己封装一个table组件&#xff0c;免去大量重复工作和co…

综合实验nginx+nfs+kpa

综合实验 实验目的&#xff1a; 静态资源和动态资源分别存放在远端存储NFS上&#xff0c;NFS上数据实现实时备份&#xff0c;用户通过负载访问后端的web服务。实现ngixn负载高可用&#xff0c;当keepalived master宕机&#xff0c;vip能自动跳转到备用节点 实验环境&#xff…

游戏引擎分层简介

游戏引擎分层架构&#xff08;自上而下&#xff09; 工具层&#xff08;Tool Layer&#xff09; 在一个现代游戏引擎中&#xff0c;我们最先看到的可能不是复杂的代码&#xff0c;而是各种各样的编辑器&#xff0c;利用这些编辑器&#xff0c;我们可以制作设计关卡、角色、动画…

多功能声学综合馆:塑造未来城市空间的先锋

现代城市需要多功能声学综合馆&#xff0c;这不仅是一座建筑&#xff0c;更是空间的变革者&#xff0c;为城市注入活力&#xff0c;展现着未来的发展方向。让我们一同探讨多功能声学综合馆的种种优势&#xff0c;它是如何为城市带来独特的价值。 1. 灵活性与多功能性的典范&am…

【RT-DETR有效改进】结合SOTA思想利用双主干网络改进RT-DETR(全网独家创新,重磅更新)

一、本文介绍 本文给大家带来的改进机制是结合目前SOTAYOLOv9的思想利用双主干网络来改进RT-DETR&#xff08;本专栏目前发布以来改进最大的内容&#xff0c;同时本文内容为我个人一手整理全网独家首发 | 就连V9官方不支持的模型宽度和深度修改我都均已提供&#xff0c;本文内…

RK3568平台开发系列讲解(基础篇)中断线程化

🚀返回专栏总目录 文章目录 一、什么是中断线程化二、中断线程化接口三、中断线程化案例沉淀、分享、成长,让自己和他人都能有所收获!😄 一、什么是中断线程化 中断线程化是一种优化技术, 用于提高多线程程序的性能。 中断线程化的核心思想是将中断处理和主线程的工作分…

paimon取消hive转filesystem

目录 概述实践关键配置spark sql 结束 概述 公司上一版本保留了 hive &#xff0c;此版优化升级后&#xff0c;取消 hive。 实践 关键配置 同步数据时&#xff0c;配置如下&#xff0c;将形成两个库 # ods库 CREATE CATALOG paimon WITH (type paimon,warehouse hdfs:///d…

ospf协议以及案例

OSPF协议网络类型 OSPF协议支持四种网络类型&#xff0c;分别是点到点网络&#xff0c;广播型网络&#xff0c;NBMA网络和点到多点网络。 1、点到点网络是指只把两台路由器直接相连的网络。一个运行PPP的64K串行线路就是一个点到点网络的例子。 2、广播型网络是指支持两台以上…

第二篇【传奇开心果系列】Python的自动化办公库技术点案例示例:深度解读Pandas金融数据分析

传奇开心果博文系列 系列博文目录Python的自动化办公库技术点案例示例系列 博文目录前言一、Pandas 在金融数据分析中的常见用途和功能介绍二、金融数据清洗和准备示例代码三、金融数据索引和选择示例代码四、金融数据时间序列分析示例代码五、金融数据可视化示例代码六、金融数…

第四届信息通信与软件工程国际会议(ICICSE 2024)即将召开!

2024年第四届信息通信与软件工程国际会议&#xff08;ICICSE 2024&#xff09;将于2024年5月10-12日在中国北京举办。本次会议由北京工业大学、IEEE以及Comsoc 联合主办。随着当今社会信息化的高速发展&#xff0c;电子信息技术的应用更是随处可见。其中&#xff0c;信息通信与…

虚拟化之CPU

一 cpu 1 如何查看内核版本&#xff1a;uname -r 2 如何查看操作系统的发行版本&#xff1a;cat /etc/redhat-release 3 计算机系统子的系统 cpu处理器memory内存storage存储network 网络Display显示 4 进程模式 用户模式&#xff08;user mode&#xff09;主要处理I/O的模…

TypeScript11:类型兼容性

类型的兼容性 B -> A&#xff0c;将 B 赋值给 A &#xff0c;如果能完成赋值&#xff0c;则 B 和 A 类型兼容。 鸭子辨型法&#xff08;子结构辨型法&#xff09;&#xff1a; 目标类型需要某一些特征&#xff0c;赋值的类型只要能满足该特征即可。 TS如何进行类型兼容…

SINAMICS V90 PN 指导手册 第7章 位置跟踪

位置跟踪 位置跟踪的主要作用有以下几点&#xff1a; 为单圈绝对值编码器设置一个虚拟多圈扩展位置区域使用齿轮箱时编码器可以重复负载位置 当位置跟踪功能使能后&#xff0c;那么实际位置值在驱动断电时会保存在驱动的掉电存储区中。当驱动器重新上电后&#xff0c;驱动可…

【计算机考研】408学到什么程度才能考130?

408考130要比考研数学考130难的多 我想大部分考过408的考生都是这么认为的。408的难点在于他涉及的范围太广了&#xff0c;首先如果你要备考408&#xff0c;你要准备四门课程&#xff0c;分别是数据结构&#xff0c;计算机组成原理&#xff0c;操作系统和计算机网络。 这四门…

RabbitMQ队列

RabbitMQ队列 1、死信的概念 ​ 先从概念解释上搞清楚这个定义&#xff0c;死信&#xff0c;顾名思义就是无法被消费的消息&#xff0c;字面意思可以这样理解&#xff0c;一般来说,producer将消息投递到broker或者直接到queue里了&#xff0c;consumer 从 queue取出消息进行消…