当前位置:   article > 正文

Unity入门学习(四)_unity给飞船添加生命值代码

unity给飞船添加生命值代码

世界交互

        现已经完成对世界的基本装饰了,接下来去实现更多玩法,比如角色的生命值。

向角色添加生命值统计功能

         添加角色生命值系统能更好的反应角色与世界的互动,比如角色的生命值的减少或者提供一些道具恢复生命值。

        打开角色脚本,修改代码如下:

  1. public class RubyController : MonoBehaviour
  2. {
  3. public int maxHealth = 5;
  4. int currentHealth;
  5. Rigidbody2D rigidbody2d;
  6. float horizontal;
  7. float vertical;
  8. // 在第一次帧更新之前调用 Start
  9. void Start()
  10. {
  11. rigidbody2d = GetComponent<Rigidbody2D>();
  12. currentHealth = maxHealth;
  13. }
  14. // 每帧调用一次 Update
  15. void Update()
  16. {
  17. horizontal = Input.GetAxis("Horizontal");
  18. vertical = Input.GetAxis("Vertical");
  19. }
  20. void FixedUpdate()
  21. {
  22. Vector2 position = rigidbody2d.position;
  23. position.x = position.x + 3.0f* horizontal * Time.deltaTime;
  24. position.y = position.y + 3.0f * vertical * Time.deltaTime;
  25. rigidbody2d.MovePosition(position);
  26. }
  27. void ChangeHealth(int amount)
  28. {
  29. currentHealth = Mathf.Clamp(currentHealth + amount, 0, maxHealth);
  30. Debug.Log(currentHealth + "/" + maxHealth);
  31. }
  32. }

        在这里我们创建了maxHealth来储存角色的最大生命值,而currentHealth用来更新角色当前的生命值,由于在游戏开始的时候角色即是最大生命值。

  1. void Start()
  2. {
  3. rigidbody2d = GetComponent<Rigidbody2D>();
  4. currentHealth = maxHealth;
  5. }

        此时我们设置好了生命值,就需要增加函数来更改生命值。

  1. void ChangeHealth(int amount)
  2. {
  3. currentHealth = Mathf.Clamp(currentHealth + amount, 0, maxHealth);
  4. Debug.Log(currentHealth + "/" + maxHealth);
  5. }

        让我们看下函数内的主体,这里使用了Mathf.Clamp的内置函数来设置当前的生命值,这是为了防止生命值超过我们设置的最大值和低于我们的最小值(即防止生命值出现负数)

        Camp可以确保第一个参数(currentHealth + amount)绝不会小于第二个参数(0),也绝对不会大于第三个参数(maxHealth)。

        此时我们回到Unity编译器,启动用游戏并点击Console窗口进行查看,可以看到我们们的生命值与最大生命值。

 触发器

         接下来我们介绍触发器的使用,触发器是一种特殊类型的碰撞体。触发器不会阻止移动,但是物理系统仍会检查角色是否会与触发器碰撞。当你的角色进入触发器时,你将收到一条消息,以便你可以处理该事件。

 创建可收集的生命值游戏对象

  •   在Project窗口中,选择Assets > Art > Sprites > VFX ,并找到CollectibleHealth

  • 将这个游戏对象导入到场景中,并调整PPU值设置为适当大小。

  • Box Collider 2D组件添加到新游戏对象,调整碰撞体大小,以便更好的适应精灵。

         现在,如果我们运行游戏,角色与这个物体也会出现碰撞,但是我们需要的是拾取它,所以不能有这种碰撞。所以我们点击这个物体,在Inspector中,找到Box Collider 2D组件。启用Is Trigger属性复选框。

 创建可收集对象脚本

         现在,我们需要添加一下代码来处理碰撞。

  1. Project窗口中,选择Assets > Scripts,右键选择Create > C# Script
  2. 重命名脚本为HealthCollectible。在Hierarchy中,选择CollectibleHealth游戏对象,将HealthCollectible脚本从Project窗口中拖放到Inspector,从而将这个脚本作为组件挂载到游戏对象上。
  3. 双击脚本文件,删除StartUpdate函数,因为我们不需要再游戏开始时或者游戏每一帧中处理任何事情。

         我们需要此脚本检测与角色的碰撞情况,并为角色增加生命值,故添加以下函数。

void OnTriggerEnter2D(Collider2D other)

        提示:确保函数名称和参数类型的拼写正确,因为 Unity 在需要调用函数时会使用这些名称在脚本中“查找”函数。

与 Unity 每帧调用 Update 函数的方式相同,当检测到新的刚体进入触发器时,Unity 将在第一帧调用此 OnTriggerEnter2D 函数。名为 other 的参数将包含刚进入触发器的碰撞体。

提供生命值

         在HealthCollectible脚本中修改如下代码:

  1. void OnTriggerEnter2D(Collider2D other)
  2. {
  3. RubyController controller = other.GetComponent<RubyController>();
  4. if (controller != null)
  5. {
  6. controller.ChangeHealth(1);
  7. Destroy(gameObject);
  8. }
  9. }

 Destroy Unity 的一个内置函数,可销毁你作为参数传递给这个函数的任何对象;在此示例中为 gameObject。这是将脚本附加到的游戏对象(可收集的生命值包)

调整角色脚本

         返回到Unity编译器中,控制台上会报错:“RubyController.ChangeHealth(int)' is inaccessible due to its protection level

         打开RubyController脚本。对于函数和变量都可以在其之前添加“public”来使这个变量或者函数显示在Inspector中,默认情况下,HealthCollectible脚本无法访问Ruby中的函数,所以我们需要将ChangeHealth函数前添加“public”。在Start函数中,将currentHealth设置为1,使角色最初具有最小的生命值。

  1. void Start()
  2. {
  3. rigidbody2d = GetComponent<Rigidbody2D>();
  4. currentHealth = maxHealth;
  5. currentHealth = 1;
  6. }
  7. // 每帧调用一次 Update
  8. void Update()
  9. {
  10. horizontal = Input.GetAxis("Horizontal");
  11. vertical = Input.GetAxis("Vertical");
  12. }
  13. void FixedUpdate()
  14. {
  15. Vector2 position = rigidbody2d.position;
  16. position.x = position.x + speed * horizontal * Time.deltaTime;
  17. position.y = position.y + speed * vertical * Time.deltaTime;
  18. rigidbody2d.MovePosition(position);
  19. }
  20. public void ChangeHealth(int amount)
  21. {
  22. currentHealth = Mathf.Clamp(currentHealth + amount, 0, maxHealth);
  23. Debug.Log(currentHealth + "/" + maxHealth);
  24. }

        现在完成了可收集生命值包,并且生命值绝不会超过maxHealth。顺便将我们制作好的HealthCollectible游戏对象创建一个预制件,我们可以用这个预制件放置多个对象了。

        不过在这里存在一点小问题,就是当角色的生命值满时去拾取生命值包也可以拾取,所以我们需要增加一项来检测Ruby是否需要生命值。故我们打开HealthCollectible脚本修改代码

  1. void OnTriggerEnter2D(Collider2D other)
  2. {
  3. RubyController controller = other.GetComponent<RubyController>();
  4. if (controller != null)
  5. {
  6. if(controller.currentHealth < controller.maxHealth)
  7. {
  8. controller.ChangeHealth(1);
  9. Destroy(gameObject);
  10. }
  11. }
  12. }

这个脚本中添加了另一项测试以查看 Ruby 的生命值是否小于最大生命值。如果相等,该测试的结果将为 false,并且脚本将跳过 if 语句(因为无需更改生命值或销毁可收集对象)。

但在切换回 Unity 之前请先等等!你可能已经猜到在控制台中会有一个错误,这是正常的;currentHealth 是私有变量,因此你无法从外部访问这个变量。

你可以像以前一样将这个变量设置为公共 (public) 变量。但是,将所有内容都公开会导致错误,尤其是对于某些内容,你可能并不希望从任何位置都可以访问和更改它们。 例如,如果将 currentHealth 设置为 public,则可以在另一个脚本中将这个变量更改为 10,即使 maxHealth 仅为 5 的情况下也可以这样修改。为避免这种情况,最好只允许通过函数进行更改。这样做便于执行检查,就像 ChangeHealth 函数一样,可以使用钳制功能确保生命值绝不会小于 0,也不会大于 maxHealth

因此,你不想将这个变量公开(以防止其他脚本进行更改),但是仍然希望允许其他脚本读取 currentHealth 的值(但这些脚本无法写入值)。为此,你可以使用属性。

在角色脚本定义一个属性

  1. public class RubyController : MonoBehaviour
  2. {
  3. public float speed = 3.0f;
  4. public int maxHealth = 5;
  5. public int health { get { return currentHealth; }}
  6. int currentHealth;
  7. Rigidbody2D rigidbody2d;
  8. // 在第一次帧更新之前调用 Start
  9. void Start()
  10. {

你通过以下要素开始了像变量一样的属性定义:

  • 访问级别(public)
  • 类型(int)
  • 名称(health)

但是,此处添加了两个 { } 代码块,而未使用 ; 来结束代码行。

在第一个代码块中,你使用了 get 关键字来获取第二个代码块中的任何内容。

第二个代码块就像普通函数一样,因此只需返回 currentHealth 值。

编译器完全像函数一样处理此代码行,因此你可以在 get 代码块内的函数中编写所需的任何内容(例如声明变量、执行计算和调用其他函数)。

使用HealthCollectible中的属性 

 打开HealthCollectible脚本,在代码中找到if语句:

if(controller.health < controller.maxHealth)

属性的用法很像变量,而不是像函数。此处,health 向你提供 currentHealth。但是,只有你读取 health 时,才有这样的作用。

如果我们尝试修改代码为:

controller.health = 10;

Unity 的控制台中会显示一个错误。这是因为你只为 health 指定了“get”操作,因此它是只读的。

注意:set 关键字的原理与 get 完全相同,但作用是使属性变为可写。如果提供 set 而没有 get,则只能写入属性,而无法读取属性。

 最终我们的HealthCollectible脚本代码如下:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class HealthCollectible : MonoBehaviour
  5. {
  6. void OnTriggerEnter2D(Collider2D other)
  7. {
  8. RubyController controller = other.GetComponent<RubyController>();
  9. if (controller != null)
  10. {
  11. if(controller.health < controller.maxHealth)
  12. {
  13. controller.ChangeHealth(1);
  14. Destroy(gameObject);
  15. }
  16. }
  17. }
  18. }

 在下一章节中,我们将学会如何设计敌人和伤害区域!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/235879
推荐阅读
相关标签
  

闽ICP备14008679号