初识Unity

编辑器的基本使用

在游戏场景当中,长按右键拖移可以以当前点为圆心进行视角转动,长按鼠标滚轮可以进行位置的移动。

C#脚本

生命周期函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Move : MonoBehaviour
{
//当脚本组件加载的时候调用一次
private void Awake()
{

}
//当脚本组件激活的时候调用一次
private void OnEnable()
{

}
//在OnEnable调用之后调用一次
void Start()
{

}
//每帧调用一次
void Update()
{

}
//在Update每帧调用之后紧接着调用一次
private void LateUpdate()
{

}
//按固定时间间隔进行调用
private void FixedUpdate()
{

}
//当组件被禁用的时候调用一次
private void OnDisable()
{

}
//当组件被销毁的时候调用一次
private void OnDestroy()
{


}
}

向量的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void Start()
{
//三维向量
Vector3 v = new Vector3();
v = Vector3.zero;
v = Vector3.right;

Vector3 v2 = Vector3.forward;

//计算两个向量之间的夹角
Debug.Log("这两个向量之间的夹角是" + Vector3.Angle(v, v2));
//计算两点之间的距离
Debug.Log("这两点之间距离是" + Vector3.Distance(v, v2));
//点乘
Debug.Log("两个向量的点乘是" + Vector3.Dot(v, v2));
//叉乘
Debug.Log("两个向量的叉乘是" + Vector3.Cross(v, v2));
//插值
Debug.Log(Vector3.Lerp(Vector3.zero, Vector3.one, 0.8f));
//向量的模
Debug.Log(v.magnitude);
//规范化向量
Debug.Log(v.normalized);

//旋转:欧拉角,四元数
Vector3 v = new Vector3(0, 30, 0);
//相当于无旋转的四元数,即0,0,0,0
Quaternion q = Quaternion.identity;
//通过一个欧拉角创建一个四元数
q = Quaternion.Euler(v);
//四元数转为欧拉角
v = q.eulerAngles;
//看向一个物体,面向那个点
q = Quaternion.LookRotation(new Vector3(0, 0, 0));
}

物体类的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Move : MonoBehaviour
{

public GameObject cube;
public GameObject prefab;
//在OnEnable调用之后调用一次
void Start()
{
Debug.Log("");
Debug.LogWarning("");
Debug.LogError("");
Debug.Log("当前自身的激活状态:" + cube.activeSelf);
Debug.Log("相对于世界的激活状态:" + cube.activeInHierarchy);
//设置物体的active状态
cube.SetActive(false);
//获取Transform组件
Transform transform = this.transform;
//更为简便的写法
Debug.Log(transform.position);

//获取其他组件
//获取盒型碰撞体
BoxCollider collider = GetComponent<BoxCollider>();
//获取当前物体的子物体的组件
GetComponentInChildren<Collider>(collider);
//获取当前物体的父物体身上的组件
GetComponentInParent<Collider>(collider);
//添加组件
cube.AddComponent<AudioSource>();
//通过游戏物体的名称获取游戏物体
GameObject test = GameObject.Find("Test");
//通过游戏标签来获取游戏物体,多个相同标签的物体获取到的是最后一个创建的物体
test = GameObject.FindGameObjectWithTag("Enemy");

//通过预制体来实例化一个游戏物体,第二个参数为设置父物体
Instantiate(prefab, transform);
//销毁特定游戏对象
Destroy(test);
}
//每帧调用一次
void Update()
{
//绘制一条线段
Debug.DrawLine(Vector3.zero, Vector3.one);
//绘制一条射线
Debug.DrawRay(Vector3.zero, Vector3.up);
}
}

游戏时间的使用

1
2
3
4
5
6
7
8
9
10
11
void Start()
{
//游戏开始到现在所花的时间
Debug.Log(Time.time);
//时间缩放值
Debug.Log(Time.timeScale);
//获取固定时间调用的间隔时长
Debug.Log(Time.fixedDeltaTime);
//上一帧到这一帧所用的游戏时间
Debug.Log(Time.deltaTime);
}

Application类的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void Start()
{
//游戏文件夹路径->Assets文件夹
Debug.Log(Application.dataPath);
//如果想要找到其中的某个文件,比如这个脚本(只读,加密压缩,打包出去之后是找不到这个文件的)
Debug.Log(Application.dataPath + "/script/ApplicationTest.cs");

//持久化文件夹路径,可以写入数据
Debug.Log(Application.persistentDataPath);

//StreamingAssets文件夹路劲(只读,配置文件),->Assets/StreamingAssets
//其中StreamAssets这个文件夹需要手动创建
Debug.Log(Application.streamingAssetsPath);
//临时文件夹
Debug.Log(Application.temporaryCachePath);

//控制是否在后台运行
Debug.Log(Application.runInBackground);

//打开url
Application.OpenURL("www.baidu.com");

//退出游戏
Application.Quit();
}

游戏场景

如果想要在场景之间进行切换,首先需要在场景构建的设置中将这些场景都添加进来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void Start()
{
//两个类:场景类、场景管理类
//根据序号进行跳转
SceneManager.LoadScene(1);
//根据场景名进行跳转
SceneManager.LoadScene("MyScene");
//获取当前场景
Scene scene = SceneManager.GetActiveScene();
//场景名称
Debug.Log(scene.name);
//场景是否已经被加载了
Debug.Log(scene.isLoaded);
//场景路径
Debug.Log(scene.path);
//场景索引
Debug.Log(scene.buildIndex);
GameObject[] gameObjArr = scene.GetRootGameObjects();
Debug.Log(gameObjArr.Length);


//当前已经加载的场景的数量
Debug.Log(SceneManager.sceneCount);
//创建新场景
Scene newScene = SceneManager.CreateScene("newScene");
//卸载场景
SceneManager.UnloadSceneAsync(newScene);
//加载场景,加载的场景和当前的场景进行叠加,而不是切换,可能会导致卡顿
SceneManager.LoadScene("MyScene", LoadSceneMode.Additive);
//使用异步操作
SceneManager.LoadSceneAsync("MyScene", LoadSceneMode.Additive);
}

加载场景,场景跳转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class AsyncTest : MonoBehaviour
{
AsyncOperation operation;
// Start is called before the first frame update
void Start()
{
StartCoroutine(loadScene());
}

//协程方法用来异步加载场景
IEnumerator loadScene()
{
operation = SceneManager.LoadSceneAsync(1);
//加载完场景不要自动跳转(比如常见的:点击任意键继续)
//默认值为true,修改为false之后就不会自动进行跳转了
operation.allowSceneActivation = false;
yield return operation;
}

float timer = 0;

// Update is called once per frame
void Update()
{
//输出加载进度,0-0.9
Debug.Log(operation.progress);
timer += Time.deltaTime;
//五秒之后自动进行场景跳转
if (timer > 5)
{
operation.allowSceneActivation = true;
}
}
}

节点的位置、旋转和缩放(transform)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TransformTest : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
//获取节点的坐标(世界/本地)
Debug.Log(transform.position);
Debug.Log(transform.localPosition);

//获取节点的旋转角度
Debug.Log(transform.rotation);
Debug.Log(transform.localRotation);

//获取节点的欧拉角
Debug.Log(transform.eulerAngles);
Debug.Log(transform.localEulerAngles);

// 获取节点的缩放
Debug.Log(transform.localScale);

// 向量(坐标轴中指向z,x,y的方向向量)
Debug.Log(transform.forward);
Debug.Log(transform.right);
Debug.Log(transform.up);
}

// Update is called once per frame
void Update()
{
// 时时刻刻看向000点
transform.LookAt(Vector3.zero);

// 每帧旋转一度
transform.Rotate(Vector3.up, 1);

// 绕某个物体旋转
transform.RotateAround(Vector3.zero, Vector3.up, 1);

// 移动
transform.Translate(Vector3.forward * 0.1f);
}
}

节点的父子级关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void Start()
{
// 获取父物体
Debug.Log(transform.parent.gameObject);
// 子物体的个数
Debug.Log(transform.childCount);
// 解除与子物体的父子关系
transform.DetachChildren();
// 获取子物体
Transform tran = transform.Find("child");
tran = transform.GetChild(0);
// 判断一个物体是不是另一个物体的子物体
bool res = tran.IsChildOf(transform);
// 设置为父物体
tran.SetParent(transform);
}

键盘鼠标事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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.B))
{
Debug.Log("持续按下了B键");
}

// 抬起按键
if (Input.GetKeyUp(KeyCode.A))
{
Debug.Log("A键抬起了");
}

虚拟轴和虚拟按键

虚拟轴就是一个在数值1-1之间的一个数轴,这个数轴上最重要的数值就是0,1,-1。当使用按键模拟一个完整的虚拟轴时需要用到两个按键,即将按键1设置为负轴按键,按键2设置为正轴按键。在没有按下任意键的时候,虚拟轴的数值为0,在按下按键1的时候,虚拟轴的数值会从0-1过渡;在按下按键2的时候,虚拟轴的值会从0~1进行过渡。

虚拟轴的作用主要是为解决了不同设备之间的操作兼容性问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void Update()
{
// 获取水平轴,参数都是与unity设置中的轴名相对应的
float horizontal = Input.GetAxis("Horizontal");
// 获取垂直轴
float vertical = Input.GetAxis("Vertical");

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

if (Input.GetButtonUp("Jump"))
{
Debug.Log("空格键抬起了");
}

if (Input.GetButton("Jump"))
{
Debug.Log("空格键被长按了");
}
}

触摸方法的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TouchTest : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
// 开启多点触摸
Input.multiTouchEnabled = true;
}

// Update is called once per frame
void Update()
{
// 判断单点触摸
if (Input.touchCount == 1)
{
// 返回单点触摸的对象,因为是单点触摸,只有一个触摸对象,所以直接调数组第0位就行
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;
}
}
else if (Input.touchCount == 2)
{
// 判断为多点触摸
}
}
}

场景灯光的使用

Light灯光组件属性:

Type:灯光类型。共有三种类型的灯光。

  • 场景自带的灯光组件默认为定向光(Directional),可以像太阳一样,模拟从无穷远处打来的平行光。当组件为此属性时,移动灯光组件的位置不会产生影响,只有此节点发生旋转之后,才会产生影响(像太阳东升西落一样,地面影子的变化)。
  • 聚光(Spot),类似于一个手电筒。
  • 点光源(Point),类似于一个灯泡。
  • 区域(Area),是以一个平面发射的平行光。

Color:发射出的灯光的颜色。

Mode:

  • 实时(Realtime),实时渲染光源的照射情况,比较耗费性能。
  • 混合(Mixed),处于实时和烘焙之间。
  • 烘焙(Baked),把当前的灯光信息保存下来,之后不再进行阴影计算,就算是将光源删除或者取消激活,光源也会依旧存在在场景当中。

Intensity:灯光强度。

Indirect Multiplier:

Shadow Type:阴影类型。

  • 默认为软阴影(Soft Shadows),边缘会被处理的羽化(比较消耗系统性能)。
  • 硬阴影(Hard Shadow),阴影的边缘为锯齿状。
  • 无阴影(No Shadows)。

Draw Halo:绘制光晕。

Culling Mask:剔除遮罩。将所在分组的节点去掉光照的影响,默认为全部(Everything)都会接收到光照。

灯光烘焙步骤:

1.先给需要灯光烘焙的节点勾选分组。

2.在弹出的提示框选择同步到子节点。

3.打开光照选项卡。

4.点击选项卡右下方的Generate Lighting(生成照明),等待渲染完成就可以了。

摄像机

投影(Projection):摄像机的类型。

  • 透视摄像机

    3D项目中场景中默认使用的就是透视摄像机,有近大远小的效果。

  • 正交摄像机

    不论距离有多远,在屏幕上显示的都是原本大小的物体,常用在2D项目中。

清除标志(Clear Falg):相机拍摄不到的区域的样式

  • 天空盒(Skybox),可以直接改变天空的材质、样式等,但是不会在场景中显示。
  • 纯色(Solid Color)。
  • 仅深度(Depth Only),常用于多个相机拍摄进行叠加。例如,其中一个摄像机只能拍摄到一个球体,主摄像机能排到一个平面,则最后呈现出来的样式是平面和球体在同一个画面当中。

FOV轴(FOV Axis):有水平和垂直两个属性,数值越大视野越广。

剪裁平面(Clipping Planes),摄像机有近面和远面,只有在这两个面之间的物体才会被摄像机渲染出来。

  • 近面(Near)离摄像机的距离。
  • 远面(Far)离摄像机的距离。

类似于拍照的玩法

在文件中新建一个纹理文件,然后将纹理文件拖到摄像机属性中的Target Texture上。

接着,将创建好的纹理文件直接拖到场景上的一个平面,就可以将摄像机的内容贴在平面上(拍照)。

音频播放

摄像机上默认会挂载一个AudioListener音频监听器,如果有多个摄像机,则只需要在主摄像机上保留该组件即可,否则会不停报警告。

Audio Source组件

  • AudioClip,音频文件挂载的地方。
  • 3D Sounds Settings:有一个球体范围,负责最小到最大的声音收录范围。

一个Audio Source组件同时只能播放一个背景音乐,但是可以播放多个音效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AudioTest : MonoBehaviour
{
// 获取AudioClip
public AudioClip music;

public AudioClip se;

public AudioSource player;

// Start is called before the first frame update
private void Start()
{
player = GetComponent<AudioSource>();
// 设定播放的音频片段
player.clip = music;
// 循环
player.loop = true;
// 音量
player.volume = 0.5f;
// 播放音乐
player.Play();
}

// Update is called once per frame
private void Update()
{
// 按空格切换声音的播放和暂停
if (Input.GetKeyDown(KeyCode.Space))
{
if (player.isPlaying == false)
player.Play();
else
player.Pause();
}
// 按鼠标左键播放声音
if (Input.GetMouseButtonDown(0))
{
player.PlayOneShot(se);
}
}
}

视频播放

Video Player组件,需要将Target Texture修改为新建的纹理文件,然后将文件拖到一个平面上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Video;

public class VideoTest : MonoBehaviour
{
private VideoPlayer videoPlayer;

// Start is called before the first frame update
private void Start()
{
videoPlayer = GetComponent<VideoPlayer>();
videoPlayer.Play();
videoPlayer.Pause();
Debug.Log(videoPlayer.isPlaying);
}

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

控制角色的移动

在角色的节点上先添加上Character Controller组件,然后挂载上脚本进行控制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerControl : MonoBehaviour
{
private CharacterController controller;
// Start is called before the first frame update
void Start()
{
controller = GetComponent<CharacterController>();
}

// Update is called once per frame
void Update()
{
// 水平轴
float horizontal = Input.GetAxis("Horizontal");
// 垂直轴
float vertical = Input.GetAxis("Vertical");
// 创建一个方向向量
Vector3 movement = new Vector3(horizontal, 0, vertical);
// 检测射线的方向是否朝向按键方向
Debug.DrawRay(transform.position, movement, Color.red);
// 朝向改方向移动
controller.SimpleMove(movement);
}
}

物理系统

为需要加上物理特性的节点添加上Rigidbody组件,然后该物体就获得了物理特性。

刚体属性:

  • Mass(质量),表示当前物体的质量大小。
  • Drag,该物体运动所收到的阻力。
  • Angular Drag,旋转所收到的阻力。
  • Use Gravity,是否使用重力。
  • is Kinematic,是否不受外力影响。
  • Interpolate,是否使用插值。
  • Collision Detection:是否使用连续碰撞检测(消耗性能),默认为间隔检测。
  • Constraints:冻结某个轴或者旋转轴。

碰撞检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

public class FireTest : MonoBehaviour
{
public GameObject firePart;
// Start is called before the first frame update
void Start()
{

}

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

}

// 当碰撞体开始碰撞的时候
private void OnCollisionEnter(Collision collision)
{
// 碰到地面之后创建一个爆炸的预制体
Instantiate(firePart, collision.contacts[0].point, Quaternion.identity);
// 销毁自身
Destroy(gameObject);
}
// 当碰撞体持续碰撞的时候
private void OnCollisionStay(Collision collision)
{

}
// 当碰撞体结束碰撞的时候
private void OnCollisionExit(Collision collision)
{

}
}

触发器

在游戏物体上的Collider组件上勾选is Trigger即可开启触发器。(类似于Cocos上的sensor),监听触发器触发的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private void OnTriggerEnter(Collider other)
{
Debug.Log("有东西碰到我了");
}

private void OnTriggerExit(Collider other)
{

}

private void OnTriggerStay(Collider other)
{

}

物理关节

铰链

为需要添加该物理关节的游戏对象添加组件Hinge Joint,如下图所示,可以修改铰链所在的位置以及朝向的方向。在模型上有一条褐色的箭头,便是铰链朝向的方向以及位置。

弹簧

为需要添加该物理关节的游戏对象添加组件Spring Joint,需要在下图中红框所选位置将另一刚体连接上。

固定关节

为需要添加该物理关节的游戏对象添加组件Fixed Joint,需要在途中红框所选位置将另一链接的刚体挂载上。

物理材质

物理材质可以调整游戏物体的摩擦力,弹力等属性。
首先在文件目录中新建一个物理材质(Physic Material),然后调整好相关的参数拖到对应的游戏对象对应的碰撞器上的材质选项(Material)。

射线检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RayTest : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
//创建一条射线
Ray ray = new Ray(Vector3.zero, Vector3.up);

}

// Update is called once per frame
void Update()
{
//鼠标点击的时候发射一条射线
if (Input.GetMouseButtonDown(0))
{
//通过摄像机获取一个向量
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
//声明一个碰撞信息类
RaycastHit hit;
bool res = Physics.Raycast(ray, out hit);
//如果碰撞到之后hit就有信息
if (res)
{
Debug.Log(hit.point);
//让小球移动到射线碰撞到的地方
transform.position = hit.point;
}

//多检测,检测射线碰撞到的所有物体,第二个参数可以设置射线的长度
RaycastHit[] hits = Physics.RaycastAll(ray);

}
}

粒子系统

右键场景新建效果(Effect)->粒子系统(Particle System),即可以在在场景中创建一个带有粒子系统组件的空游戏对象。
粒子系统组件的属性:

  1. Duration:持续时间,表示粒子发射器的生存时间。
  2. Loop:循环播放。
  3. Prewarm:是否预热,如果勾选上,粒子就会从完全施展开之后进行播放,而不会有从无到有的扩散过程。
  4. Start Delay:启动延迟,延迟多少秒之后启动粒子播放。
  5. Start Lifetime:起始周期,粒子的存活周期。
  6. Start Speed:起始速度。
  7. Start Size:起始大小。
  8. Start Rotation:起始角度。
  9. Simulation Space:模拟空间,默认为世界空间。
  10. Simulation Speed:模拟速度,默认为1,即正常播放。
  11. Play On Wake:唤醒播放,默认为勾选,即在游戏启动的时候就播放粒子。

划线功能(Line Render)

即线段渲染器,