Unity学习笔记(六)

实例的处理

动态创建实例

创建 Prefab 之后,用 API 动态创建实例

  1. 准备子弹 prefab ,参考 制作演示

  2. 添加火控脚本 FireLogic.cs,添加变量,克隆实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 添加prefab变量
    public GameObject bulletPrefab;
    void Update()
    {
    if (Input.GetMouseButtonDown(0))
    {
    TestFire();
    }
    }

    private void TestFire()
    {
    Debug.Log("创建子弹实例....");
    // 创建实例,传入实例变量和父节点
    GameObject node = Object.Instantiate(bulletPrefab, null);
    // 初始化位置和角度
    node.transform.position = Vector3.zero;
    node.transform.eulerAngles = Vector3.zero;
    }
  3. 在检查器中,引用子弹的 prefab

  4. 运行游戏,鼠标每点击一下创建一个新的prefab

实例的初始化

创建 Prefab Instance 之后,应该初始化

  • parent ,父节点
  • position / localPosition ,位置
  • eulerAngles / localEulerAngles,旋转
  • Script ,自带的控制脚本
  1. parent ,设置父级节点
    为便于节点的管理,应单独创建一个父节点

  2. position ,出生点位置
    为便于操作,应显式标记一个出生点

  3. eulerAngles / localEulerAngles / rotation
    对于子弹来说,子弹角度应与炮塔旋转角度一致

  4. Script,自带脚本参数设置
    子弹节点自带了 BulletLogic ,可控制其飞行速度

最终FireLogic.cs及效果:

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
public class FireLogic : MonoBehaviour
{
// 对子弹prefab资源的引用
public GameObject bulletPrefab;

// 子弹树节点的transform组件
public Transform bulletFolder;

// 出生点位置
public Transform firePoint;

// 炮塔仰角
public Transform cannon;

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

}

// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
TestFire();
}
}

private void TestFire()
{
Debug.Log("创建子弹实例....");
GameObject node = Object.Instantiate(bulletPrefab, bulletFolder);
// 初始化位置和角度
node.transform.position = this.firePoint.position;
node.transform.eulerAngles = this.cannon.eulerAngles;
// 改变rotation,与欧拉角效果相同,但官方不推荐
// node.transform.rotation = this.cannon.rotation;

// 设置BulletLogic脚本中的speed参数
BulletLogic script = node.GetComponent<BulletLogic>();
script.speed = 0.1f;
}
}

注意:

  • 一般引用Transform 类,不常用 GameObject
  • 合理使用空物体,标记一个空间坐标。如上例中的火控节点、子弹树节点

实例的销毁

一般的,创建实例之后,也要负责销毁

对于子弹来说,

  • 当飞出屏幕时,销毁
  • 按射程 / 飞行时间
  • 当击中目标时,销毁

Object.Destroy ( obj ) ,用于销毁一个实例

在本轮帧更新 Update() 之后,执行销毁。在BulletLogic.cs中新增最大销毁距离

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public float maxDistance;
void Start()
{
float lifetime = 1;
if (speed > 0)
{
lifetime = maxDistance / speed;
}
Invoke("SelfDestroy", lifetime);
}
void Update()
{
this.transform.Translate(0, 0, speed, Space.Self);
}

// 销毁实例
private void SelfDestroy()
{
Debug.Log("执行子弹自毁...");
Object.Destroy(this.gameObject);
}

然后在FireLogic.cs的TestFire()方法中新增

1
2
// 设置最大销毁距离
script.maxDistance = script.speed * 10;

注意:

  • 区分以下两种写法:

    1
    2
    3
    4
    // correct
    Destroy (this.gameObject);
    // wrong
    Destroy (this);
  • Destroy() 不会立即执行,而是在本轮 Update 之后才执行

火控参数完善

在FireLogic.cs中添加子弹飞行速度和飞行时间变量

1
2
3
4
5
6
7
8
9
10
11
// 子弹速度
public float bulletSpeed;
// 子弹飞行时间
public float bulletLifetime;

private void TestFire()
{
// ....创建子弹实例
// 设置最大销毁距离
script.maxDistance = script.speed * this.bulletLifetime;
}

添加用户交互逻辑

项目结构
image-20220505174834470

新建用户主控节点,挂载MainLogic.cs

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
54
55
56
public class MainLogic : MonoBehaviour
{
public Transform cannon;

public float rotateSpeed = 50;

[Tooltip("当前转角")]
private Vector3 m_eulerAngles;

// Start is called before the first frame update
void Start()
{
Application.targetFrameRate = 60;
}

// Update is called once per frame
void Update()
{
// 每两次帧更新间隔转过的角度
float delta = rotateSpeed * Time.deltaTime;
if (Input.GetKey(KeyCode.A))
{
// 左转,不超过75度
if(m_eulerAngles.y > -75)
{
m_eulerAngles.y -= delta;
}
}
if (Input.GetKey(KeyCode.D))
{
// 右转,不超过75度
if (m_eulerAngles.y < 75)
{
m_eulerAngles.y += delta;
}
}
if (Input.GetKey(KeyCode.W))
{
// 向上,不超过60度
if (m_eulerAngles.x > -60)
{
m_eulerAngles.x -= delta;
}
}
if (Input.GetKey(KeyCode.S))
{
// 向下,不超过60度
if (m_eulerAngles.x < 20)
{
m_eulerAngles.x += delta;
}
}
// 把旋转后的欧拉角赋给炮台
cannon.transform.localEulerAngles = m_eulerAngles;
}
}

火控节点FireLogic增加重复定时任务,控制当单机时开火

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
[Tooltip("子弹发射时间间隔")]
public float bulletInterval;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
StartFire();
}
}

private void TestFire()
{
Debug.Log("创建子弹实例....");
GameObject node = Object.Instantiate(bulletPrefab, bulletFolder);
// 初始化位置和角度
node.transform.position = this.firePoint.position;
node.transform.eulerAngles = this.cannon.eulerAngles;
// 改变rotation,与欧拉角效果相同,但官方不推荐
// node.transform.rotation = this.cannon.rotation;

// 设置BulletLogic脚本中的speed参数
BulletLogic script = node.GetComponent<BulletLogic>();
script.speed = this.bulletSpeed;
// 设置最大销毁距离
script.maxDistance = script.speed * this.bulletLifetime;
}

private void StartFire()
{
if (!IsInvoking("TestFire"))
{
InvokeRepeating("TestFire", bulletInterval, bulletInterval);
}
}

物理系统

物理系统概述

物理系统 Physics ,即由物理规律起作用的系统
确切地说,是牛顿运动定律 (力,质量,速度)

牛顿三大运动定律:

  • 牛顿第一运动定律:物体合力为0时,保持静止或做匀速直线运动
    image-20220505175133711
  • 物体所受外力与质量和加速度成正比
    image-20220505175145999
  • 相互作用的两个质点之间的作用力和反作用力总是大小相等,方向相反,作用在同一条直线上
    image-20220505175152936

例子:布置一个场景,添加”地面”,”苹果”

刚体组件 Rigidbody ,物理学中的物体

  1. 给”苹果”添加 Rigidbody 组件。检查器 | Add Component | Physics | Rigidbody
  2. 运行游戏。此时,物体遵循牛顿三大定律,在重力作用下使其运动

注意:当添加 Rigidbody 后,由物理引擎负责刚体的运动

物理碰撞

物理系统,不仅接管了刚体的运动,也接管了碰撞
例子:添加一个 小球 物体 ,添加 Rigidbody
碰撞体组件 Collider ,描述了物体的碰撞范围
其中,

  • Box Collider ,长方碰撞体
  • Sphere Collider ,球形碰撞体

碰撞体的范围:
image-20220505223932428
Radius可调整碰撞范围大小

注意:

  • 物理系统也负责检测物体之间的碰撞。
  • 根据 Collider 指示的范围,检查碰撞。

反弹与摩擦

刚体的反弹与摩擦,也归物理系统负责
例子:反弹小球

  • 在 Project 中新建 Physici Material ,添加给小球检察器-Sphere Collider-Material
  • 设置 Friction 摩擦力、Bounciness 反弹力
  • 观察小球的反弹。

注意:碰撞双方都设置弹性系数,使弹性明显。

碰撞

运动学刚体

运动学刚体 Kinematic ,即质量为 0 的刚体。由于质量为0,所以此刚体不受牛顿约束。
image-20220505225506400

此时,需要用脚本使其运动。

碰撞检测

对于运动学刚体,也支持碰撞检测。
由 物理引擎 负责检测。
image-20220505225900098

实现:

  1. Rigidbody ☑ Is Kinematic

  2. Collider ☑ Is Trigger 触发器

  3. 挂一个脚本,添加消息函数 OnTriggerEnter()

    1
    void OnTriggerEnter (Collider other){    }

其中,
Collider other ,表示对方的碰撞体
other.gameObject ,对方节点
other.name ,对方节点的名字

如对小球来说,对方节点就是Ground
image-20220505230640339

注意:

  • 物理引擎只负责探测 ( Trigger ) ,不会阻止物体或者反弹
  • 物体引擎计算的是 Collider 之间的碰撞(绿框范围),和物体自身形状无关
  • 当检测到碰撞时,会调用当前节点 的脚本中的 OnTriggerEnter 消息

碰撞体的编辑

碰撞体 Collider 的形状,规定了碰撞的边界。碰撞体的形状是可以编辑的。

  • Box Collider ,盒形
  • Sphere Collider ,球形

Box Collider ,盒形碰撞体:

  • Center 中心位置,相当于物体的轴心点
  • Size 长宽高
  • 点 Edit Collider ,可以直接编辑 碰撞体(绿色框)

Sphere Collider ,球形碰撞体:

  • Center 中心位置,相当于物体的轴心点
  • Radius 半径大小
  • 点 Edit Collider ,可以直接编辑 碰撞体(绿色框)

例子:添加子弹物体

  1. 检查原先有没有碰撞体,如果有,则先移除
  2. 根据体型,选择合适形状的碰撞体
    此处,添加一个 Box Collider
  3. 编辑碰撞体,调整边界
    一般无需调整,自动创建合适的尺寸

注意:碰撞体的范围,不用太精确,大致覆盖物体即可

实例

发射子弹,击毁目标

子弹BulletLogic.cs

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
public class BulletLogic : MonoBehaviour
{
public Vector3 speed;

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

// Update is called once per frame
void Update()
{
this.transform.Translate(speed * Time.deltaTime, Space.Self);
}

private void OnTriggerEnter(Collider other)
{
// 如果碰到目标才销毁
if (other.name.Equals("目标"))
{
// 销毁碰撞接触到的节点
Object.Destroy(other.gameObject);
// 自身节点(子弹)也销毁
Object.Destroy(this.gameObject);
}
Debug.Log("子弹发生碰撞, other=" + other.name);
}
}

游戏项目实例

射击游戏

制作一个射击游戏。包含要素:

  • 海空背景
  • 玩家
  • 子弹,无限数量
  • 敌机,蛇皮走位,无限数量
  • 子弹特效,爆炸特效
  • 背景音乐

添加角色

添加玩家和敌人

天空盒

天空盒 Skybox ,即游戏的背景。
Window | Rendering | Lighting ,光照设置
Environment | Skybox Material ,天空盒材质

子弹和碰撞

  • 添加子弹
  • 添加子弹脚本
  • 添加碰撞检测

注意:敌机的碰撞体默认大小是(1, 1, 1),其碰撞体要手工编辑

子弹的连续发射

  1. 子弹
  • 增加自毁时间 lifetime
  • 把子弹做成 prefab
  1. 玩家
  • 定义发射点 fire point
  • 定义子弹目录 bullet folder
  • 使用定时器,子弹连发

按键控制

添加按键控制,让玩家左右移动

敌机走位

给敌机添加控制脚本,控制左右移动

敌机生成

添加怪兽生成器,定时生成怪兽

子弹特效

添加子弹特效。粒子特效 Particle System
注意:修改之后要应用 Prefab

爆炸特效

添加爆炸特效。粒子特效 Particle System
在击中目标时,创建特效节点

0%