创建游戏对象(游戏物体)
可通过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上
这时,虽然我们有且仅有一个相机,但是其拍摄到的内容看不见了
相机拍到的内容都时时刻刻放到刚新建的纹理里面了
这个纹理可以拖到任何游戏物体上使用
原来的游戏物体
拖拽纹理到游戏问题上后