unity进阶篇6-可收集对象开发(血量/生命值等)

如何给一个角色增加血量条,然后有一个物品可以扣血和加血

  • 给角色赋予血量条

我们先在角色上设置一个血量条

关于Mathf功能的使用 Mathf.Clamp功能

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
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class HealthLine : MonoBehaviour
{
public int MaxHealth = 5;
public int CurrentHealth;

private void Start()
{
CurrentHealth = MaxHealth;
}

public void ChangeHealth(int amount)
{
//Mathf是C#的一种功能,Mathf.Clamp相当于设定数学里的一个区间
//public static float Clamp(float value, float min, float max);
//详情看unity手册,Mathf里有很多功能可以使用,Clamp是其中一种
CurrentHealth = Mathf.Clamp(CurrentHealth + amount, 0, MaxHealth);
Debug.Log($"当前生命值:{CurrentHealth} / {MaxHealth}");
//第二种描写方式
//Debug.Log("当前生命值" + CurrentHealth + "/" + MaxHealth);
}
}

这只是在控制台显示生命值,当然我们需要一个媒介来进行扣血和加血,然后显示我们当前的血量是多少,这个媒介就是触发器

  • 设置一个扣血或加血的物体
  1. Add Component
  2. Box Collider 2D
  3. 勾选 Is Trigger
  4. 创建一个脚本,名字自定义
  5. 脚本需要包含一个OnTriggerEnter2D库,然后在这个库中编辑相关功能

当我们勾选了Is Trigger 后,这个加血的物件仍然是碰撞体,虽然无法跟它相碰撞,但是它是有和角色碰撞的,如何用代码验证是否碰撞:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Healthbox : MonoBehaviour
{
//记录碰撞次数
private int colliderCount;
////添加触发器碰撞事件,每次碰撞触发器时,执行其中的代码
//other获取的是精灵(Ruby)
private void OnTriggerEnter2D(Collider2D other)
{
colliderCount++;
Debug.Log($"当前发生碰撞的是{other},第{colliderCount}次碰撞");
}
}

每当角色与加血的物体碰撞,它将会在控制台输出以下内容:

image-20220818210836322image-20220818210755838

  • 我们接下来要给这个加血的物品添加其他代码,如人物碰到后加1点血
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
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Healthbox : MonoBehaviour
{
//赋值amout为1,当角色与物体进行碰撞时,血条的数值将加一或减一;
private int amount = 1;
//记录碰撞次数
private int colliderCount;
////添加触发器碰撞事件,每次碰撞触发器时,执行其中的代码
//other获取的是精灵(Ruby)
private void OnTriggerEnter2D(Collider2D other)
{
colliderCount++;
Debug.Log($"当前发生碰撞的是{other},第{colliderCount}次碰撞");

//获得Ruby游戏对象的脚本组件对象(这里的脚本组件指的是挂在角色上的血条组件)
//获取(连接)Ruby上的血条组件
HealthLine healthLine = other.GetComponent<HealthLine>();
//当角色与加血或扣血的物体进行碰撞后,将改变血条的数值。
healthLine.ChangeHealth(amount);
}
}

HealthLine是脚本文件名,我创建了另一个脚本文件,更好的区分不同脚本不同功能的使用。

  • 如果我们想要让角色与加血的物体进行碰撞后(类似于角色喝血瓶),该物体需要进行消除处理,该如何写代码。

我们可以使用C#的销毁函数(功能)Destory

它的作用是Removes a GameObject, component or asset.

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
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Healthbox : MonoBehaviour
{
//赋值amout为1,当角色与物体进行碰撞时,血条的数值将加一或减一;
private int amount = 1;
//记录碰撞次数
private int colliderCount;
////添加触发器碰撞事件,每次碰撞触发器时,执行其中的代码
//other获取的是精灵(Ruby)
private void OnTriggerEnter2D(Collider2D other)
{
colliderCount++;
Debug.Log($"当前发生碰撞的是{other},第{colliderCount}次碰撞");

//获得Ruby游戏对象的脚本组件对象(这里的脚本组件指的是挂在角色上的血条组件)
//获取(连接)Ruby上的血条组件
HealthLine healthLine = other.GetComponent<HealthLine>();
//当角色与加血或扣血的物体进行碰撞后,将改变血条的数值。
healthLine.ChangeHealth(amount);
//销毁(移除)该物体
Destroy(gameObject);
}
}
  • 消除功能也有了,那如果角色是满血的,该如何判断此状况,所以我们还需要写判断语句。
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
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Healthbox : MonoBehaviour
{
//赋值amout为1,当角色与物体进行碰撞时,血条的数值将加一或减一;
private int amount = 1;
//记录碰撞次数
private int colliderCount;
////添加触发器碰撞事件,每次碰撞触发器时,执行其中的代码
//other获取的是精灵(Ruby)
private void OnTriggerEnter2D(Collider2D other)
{
colliderCount++;
Debug.Log($"当前发生碰撞的是{other},第{colliderCount}次碰撞");

//获得Ruby游戏对象的脚本组件对象(这里的脚本组件指的是挂在角色上的血条组件)
//获取(连接)Ruby上的血条组件
HealthLine healthLine = other.GetComponent<HealthLine>();
if (healthLine != null)
{
if (healthLine.CurrentHealth<healthLine.MaxHealth)
{
//当角色与加血或扣血的物体进行碰撞后,将改变血条的数值。
healthLine.ChangeHealth(amount);
//销毁(移除)该物体
Destroy(gameObject);
}
else
{
Debug.Log("当前玩家角色血条已满,无需加血");
}
}
else
{
Debug.LogError("healthLine脚本并未获取到。。");
}

}
}

直接使用if语句

  • 如何防止别人擅自进入更改数值呢,比如把加血加1点变成加10点,我们不能允许这样的更改

除了默认的private外,我们还可以使用封装语句

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
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class HealthLine : MonoBehaviour
{
public int MaxHealth = 5;
//设置当前生命值属性health
//C#中支持面向对象程序设计中的封装概念,对数据成员对保护
//数据成员变量,默认一般都应该设置为私有,只能通过当前类都方法或属性进行访问
//属性是公有的,可以通过取值器get、赋值器set设定对应字段的访问规则,满足规则才能够访问。
public int health
{
get { return CurrentHealth; }
//set { CurrentHealth = value; }
}

private int CurrentHealth;
private void Start()
{
CurrentHealth = MaxHealth;
}

public void ChangeHealth(int amount)
{
//Mathf是C#的一种功能,Mathf.Clamp相当于设定数学里的一个区间
//public static float Clamp(float value, float min, float max);
//详情看unity手册,Mathf里有很多功能可以使用,Clamp是其中一种
CurrentHealth = Mathf.Clamp(CurrentHealth + amount, 0, MaxHealth);
Debug.Log($"当前生命值:{CurrentHealth} / {MaxHealth}");
//第二种描写方式
//Debug.Log("当前生命值" + CurrentHealth + "/" + MaxHealth);
}
}

代码中可以看到多添加了public int health, 然后将CurrentHealth更改为private,为什么要这么做呢。

1
2
3
4
5
6
7
8
9
10
11
12
//设置当前生命值属性health
//C#中支持面向对象程序设计中的封装概念,对数据成员对保护
//数据成员变量,默认一般都应该设置为私有,只能通过当前类都方法或属性进行访问
//属性是公有的,可以通过取值器get、赋值器set设定对应字段的访问规则,满足规则才能够访问。
//health现在是只读属性
public int health
{
get { return CurrentHealth; }
//set { CurrentHealth = value; }
}

private int CurrentHealth;

当我们设置完后,如果我们想更改CurrentHealth的值,只能通过ChangeHealth来改,CurrentHealth只能通过Health来取(return)。

这是一个数据的保护,就很难通过外挂来更改数值(如果没有访问Health的访问权限)。

我们可以发现我们通过更改了CurrentHealth的封装后,在Healthbox里的判断句出现了错误

1
2
3
4
5
6
7
8
9
10
if (healthLine != null)
{
//由于CurrentHealth进行了封装,我们需要将CurrentHealth更改为health.
if (healthLine.Currenthealth<healthLine.MaxHealth)
{
//当角色与加血或扣血的物体进行碰撞后,将改变血条的数值。
healthLine.ChangeHealth(amount);
//销毁(移除)该物体
Destroy(gameObject);
}

//由于CurrentHealth进行了封装,我们需要将CurrentHealth更改为health.

1
2
3
4
5
6
7
8
9
if (healthLine != null)
{
if (healthLine.health<healthLine.MaxHealth)
{
//当角色与加血或扣血的物体进行碰撞后,将改变血条的数值。
healthLine.ChangeHealth(amount);
//销毁(移除)该物体
Destroy(gameObject);
}

设置一个掉血区域

b站视频68-71 unity官方教学文档

  • 设置一个掉血区域

首先我们需要选取一张图片来作为我们的掉血区域

  1. Art - Sprite - Environment - Damagable
  2. 添加为预制件(Prefabs)
  3. 添加Box Collider2D
  4. 勾选Is Trigger
  • 在自己创建的掉血区域里添加脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DamageArea : MonoBehaviour
{
private int DamageNum = -1;
private void OnTriggerStay2D(Collider2D other)
{
//获取血条组件
HealthLine healthLine = other.GetComponent<HealthLine>();
//判断组件是否获取到
if (healthLine != null)
{
//扣1滴血
healthLine.ChangeHealth(DamageNum);
}
}
}
  • 我们在unity试运行,发现掉血太快了,如何解决

我们需要设置时间间隔,每两秒扣一次血

所以我们需要在HealthLine脚本上两秒间隔代码。Time.deltaTime的官方文档

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor.UIElements;
using UnityEngine;

public class HealthLine : MonoBehaviour
{
public int MaxHealth = 5;
//设置当前生命值属性health
//C#中支持面向对象程序设计中的封装概念,对数据成员对保护
//数据成员变量,默认一般都应该设置为私有,只能通过当前类都方法或属性进行访问
//属性是公有的,可以通过取值器get、赋值器set设定对应字段的访问规则,满足规则才能够访问。
//health现在是只读属性
public int health
{
get { return CurrentHealth; }
//set { CurrentHealth = value; }
}

//判断无敌状态
private bool isInvincible;
//无敌时间
private float TimeInvincible = 2.0f;
//无敌时间计时
private float InvincibleTimer;

int CurrentHealth;
private void Start()
{
CurrentHealth = MaxHealth;
}

private void Update()
{
//判断无敌状态
if (isInvincible)
{
//如果无敌,进入倒计时
//invincibleTimer = invincibleTimer - Time.deltaTime;
//每次update减去一帧所消耗到时间
InvincibleTimer -= Time.deltaTime; //Time.deltaTime的意思是从最后一帧到当前帧到间隔
//直到计时器中的时间用完
if (InvincibleTimer < 0)
{
//取消无敌状态
isInvincible = false;
}
}
}

public void ChangeHealth(int amount)
{
//判断是否在扣血的区域
if (amount<0)
{
//判断是否在无敌状态
if (isInvincible)
{
//在无敌状态直接弹出函数
return;
}
//重制无敌状态
isInvincible = true;
//重制无敌时间
InvincibleTimer = TimeInvincible;
}

//Mathf是C#的一种功能,Mathf.Clamp相当于设定数学里的一个区间
//public static float Clamp(float value, float min, float max);
//详情看unity手册,Mathf里有很多功能可以使用,Clamp是其中一种
CurrentHealth = Mathf.Clamp(CurrentHealth + amount, 0, MaxHealth);
Debug.Log($"当前生命值:{CurrentHealth} / {MaxHealth}");
//第二种描写方式
//Debug.Log("当前生命值" + CurrentHealth + "/" + MaxHealth);

//假设区域伤害为两秒一次
}
}
  • Copyrights © 2022-2024 Jessy Huang
  • Visitors: | Views:

请我喝杯咖啡吧~