• 【Unity入门计划】制作RubyAdventure03-使用碰撞体&触发器实现世界交互


    目录

     8 添加生命系统

    演示效果

    9 使用触发器实现吃血包

    9.1 添加一个可以被吃掉的血包

    10 使用触发器添加伤害区域

    10.1 设置伤害区域

    10.2 设置无敌时间

    关于无敌时间的讨论

    10.3 平铺伤害区域/平铺精灵

    1 精灵渲染器Draw Mode -> Tiled

    2 精灵素材中Mesh Type -> Full Rect

    3 2D碰撞体组件 -> 勾选Auto Tilling

    11 添加敌人

    11.1 敌人来回移动

    11.2 敌人伤害

    Collision2D 2D碰撞类


    学习的教程

    【unity2021入门教程】68-2D游戏开发教程系列-03-RubyAdventure2DRpg官方教程-16-完善触发器代码_哔哩哔哩_bilibili

    已进行步骤

    【Unity入门计划】制作RubyAdventure01-玩家的创建&移动_flashinggg的博客-CSDN博客

    【Unity入门计划】制作RubyAdventure02-处理瓦片地图&碰撞_flashinggg的博客-CSDN博客

     8 添加生命系统

    先在之前的Ruby Controller脚本中,加上一个简单的生命上限&规定初始生命值

    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. public class RubyController : MonoBehaviour
    5. {
    6. //设置最大生命值(生命上限)
    7. public int maxHealth = 5;
    8. //当前生命值,默认为0
    9. private int currentHealth;
    10. private void Start()
    11. {
    12. //初始化当前生命值
    13. currentHealth = maxHealth;
    14. }
    15. // 每帧都会执行一次Update函数
    16. void Update()
    17. {
    18. ...
    19. }
    20. //控制速度,比update快一点
    21. void FixedUpdate()
    22. {
    23. ChangeHealth(1);
    24. }
    25. //更改生命值
    26. //amount是游戏中加血/减血的操作
    27. void ChangeHealth(int amount)
    28. {
    29. //限制当前生命值范围为[0,maxHealth]
    30. currentHealth = Mathf.Clamp(currentHealth + amount, 0, maxHealth);
    31. //输出
    32. Debug.Log("当前生命值为:" + currentHealth + "/" + maxHealth);
    33. }
    34. }

    演示效果

    9 使用触发器实现吃血包

    之前写过一个触发器的简单博客:【Unity入门计划】基本概念(2)-触发器 Trigger_flashinggg的博客-CSDN博客

    它是一个特殊的碰撞体,只检测碰撞,不阻止移动。

    9.1 添加一个可以被吃掉的血包

    关于数据保护内容,我另开了一个博客进行简单的记录介绍:【Unity入门计划】Unity实例-C#如何通过封装实现对数据成员的保护

    先从Project中加入一个可以充当血包的精灵,同时创建一个预制件,给他一个2D碰撞体并设置为触发器。

    挂一个检测触发并进行相应设置的脚本:

    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. public class HealthCollectible : MonoBehaviour
    5. {
    6. //设置每次碰撞加的血量
    7. public int amount = 1;
    8. private int CollideTimes;
    9. //添加触发器事件,每次碰撞触发器时执行其中的代码
    10. private void OnTriggerEnter2D(Collider2D other)
    11. {
    12. CollideTimes++;
    13. Debug.Log($"和当前物体发生碰撞的是:{other},当前是第{CollideTimes}次发生碰撞!");
    14. //获取Ruby对象的脚本组件,还是用GetComponent方法
    15. RubyController rubyController=other.GetComponent();
    16. if(rubyController != null)
    17. {
    18. //血量+1
    19. if (rubyController.health
    20. {
    21. rubyController.ChangeHealth(amount);
    22. //销毁血包
    23. Destroy(gameObject);
    24. }
    25. else Debug.Log("血量已满!");
    26. }
    27. else
    28. {
    29. Debug.LogError("未获取到当前游戏对象");
    30. }
    31. }
    32. }

    同时RubyController中也要定义一个属性,访问私有变量currentHealth。

    1. //设置最大生命值(生命上限)
    2. public int maxHealth = 5;
    3. //当前生命值,默认为0
    4. internal int currentHealth;
    5. //C#中支持面向对象程序设计中的封装,实现对数据成员进行保护
    6. //数据成员变量本身是私有的,只能通过某一种方法或者属性访问
    7. //属性是共有的,可以通过取值器--get,赋值器--set,设定对应字段的访问规则
    8. //满足规则才能访问该成员变量
    9. public int health { get { return currentHealth; } }

    这里只贴出了结果代码,具体过程直接参考对应的教程即可。

    10 使用触发器添加伤害区域

    10.1 设置伤害区域

    还是跟之前一样,添加伤害区域精灵,变成预制体,勾选Is Trigger,再挂脚本实现一些进入后减血的操作。

    挂一个减血脚本

    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. public class DamageZone : MonoBehaviour
    5. {
    6. public int damageHealth = -1;
    7. public int damageSpeed;
    8. private double TruedamageHealth = -0.02;
    9. private double True = 0;
    10. int round=1;
    11. private void OnTriggerStay2D(Collider2D other)
    12. {
    13. RubyController rubyController=other.GetComponent();
    14. if(rubyController != null)
    15. {
    16. True += damageSpeed*TruedamageHealth;
    17. if (round==1||True < round * damageHealth)
    18. {
    19. round++;
    20. rubyController.ChangeHealth(damageHealth);
    21. }
    22. }
    23. }
    24. }

    这里我优化了一下,加入了可以控制减血速度的变量,详情可以看:【Unity入门计划】用双血条方法控制伤害区域减血速度_flashinggg的博客-CSDN博客

    10.2 设置无敌时间

    关于无敌时间的讨论

    教程中把无敌时间设置在了玩家减血的方法里,意思就是无论伤害来自敌人还是来自陷阱,只要受到伤害,就会进入2秒的无敌时间。

    先讨论一下这个方法是否无敌:

    1. 我们假设Ruby走入了陷阱,血量-1,由于受到伤害进入了无敌时间(教程中设置为2s),此时Ruby有2s的时间赶快离开陷阱!这2s里Ruby将不受任何攻击的影响,过了无敌时间后就恢复到会受到攻击的状态。同时,如果想要更贴近现有游戏的情景,无敌时间也会伴随着玩家的闪烁,以此展示当前处于无敌状态了。
    2. 我们假设Ruby被敌人发射炮弹/近身攻击了!血量-1,此时Ruby进入了无敌状态,此时敌人发射的连续炮弹、进行的连招攻击将不再有多重伤害的减血效果。

    因此,我认为对于一个基础教程的游戏项目制作,把脚本直接挂在玩家对象上是可行的,意味着敌人也是基础的敌人、没有可以“连招攻击”的敌人,也没有叠加伤害一说。

    如果希望敌人进行的连招攻击会对玩家造成叠加伤害,最好是将无敌时间的脚本挂在陷阱、敌人身上,便于修改。

    这里因为只是跟着教程学习,因此我就跟着教程思路走,还是把脚本挂在玩家身上。

    加入了无敌时间系统的玩家脚本代码如下:

    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. public class RubyController : MonoBehaviour
    5. {
    6. //设置无敌时间
    7. public float timeInvincible = 2.0f;
    8. //设置判断是否无敌的bool变量
    9. bool isInvincible;
    10. //规定一个无敌时间计数器,倒计时
    11. float invincibleTimer;
    12. //声明刚体对象
    13. Rigidbody2D rigidbody2D;
    14. //在方法外声明水平和数值量
    15. float horizontal;
    16. float vertical;
    17. //设置最大生命值(生命上限)
    18. public int maxHealth = 5;
    19. //当前生命值,默认为0
    20. private int currentHealth;
    21. //C#中支持面向对象程序设计中的封装,实现对数据成员进行保护
    22. //数据成员变量本身是私有的,只能通过某一种方法或者属性访问
    23. //属性是共有的,可以通过取值器--get,赋值器--set,设定对应字段的访问规则
    24. //满足规则才能访问该成员变量
    25. public int health { get { return currentHealth; } }
    26. private void Start()
    27. {
    28. //游戏运行前,获取当前游戏对象的刚体组件
    29. //这里用到了GetComponent获取当前所在对象的吗某个组件 - GetComponent<>()
    30. rigidbody2D = GetComponent();
    31. //初始化当前生命值
    32. currentHealth = maxHealth;
    33. }
    34. // 每帧都会执行一次Update函数
    35. void Update()
    36. {
    37. //判断是否无敌
    38. if (isInvincible)
    39. {
    40. //是,则计时器开始倒计时:deltaTime获取两帧之间的时间间隔
    41. invincibleTimer -= Time.deltaTime;
    42. if (invincibleTimer < 0)
    43. {
    44. isInvincible = false;//倒计时结束,不再是无敌时间
    45. }
    46. }
    47. //声明新变量
    48. //获取水平轴并储存在新变量中
    49. horizontal = Input.GetAxis("Horizontal");
    50. //获取垂直轴
    51. vertical = Input.GetAxis("Vertical");
    52. }
    53. //控制速度,比update快一点
    54. void FixedUpdate()
    55. {
    56. Vector2 position = transform.position;
    57. //对x轴移动距离做了细微的更改
    58. position.x += horizontal * 3.0f * Time.deltaTime;
    59. //对y轴也做相应更改
    60. position.y += vertical * 3.0f * Time.deltaTime;
    61. rigidbody2D.position = position;
    62. }
    63. //更改生命值
    64. //amount是游戏中加血/减血的操作
    65. public void ChangeHealth(int amount)
    66. {
    67. if (amount < 0)//if >0加血,则不进入无敌时间
    68. {
    69. if (isInvincible)//当前处于无敌状态,则不减血
    70. {
    71. return;//直接中段当前方法
    72. }
    73. //不是无敌状态,接下来会减血,因此提前改成true
    74. isInvincible = true;
    75. //重置计时器
    76. invincibleTimer = timeInvincible;
    77. }
    78. //限制当前生命值范围为[0,maxHealth]
    79. currentHealth = Mathf.Clamp(currentHealth + amount, 0, maxHealth);
    80. //输出
    81. Debug.Log("当前生命值为:" + currentHealth + "/" + maxHealth);
    82. }
    83. }

    10.3 平铺伤害区域/平铺精灵

    有时候需要平铺伤害区域,Unity自带有平铺精灵的功能。

    1 精灵渲染器Draw Mode -> Tiled

    还记得精灵渲染器里的Draw Mode,默认是Simple,选择Tiled

    2 精灵素材中Mesh Type -> Full Rect

    在对应的精灵素材属性中,Mesh Type选择Full Rect

    再进入Scene中调整当前游戏对象,T键框选,发现可以实现平铺了。

    还需要将碰撞体也平铺对应

    3 2D碰撞体组件 -> 勾选Auto Tilling

    此时就完成了伤害区域平铺的操作了。

    11 添加敌人

    首先需要在官网下载敌人Robot的图片。

    世界交互 - 伤害区域和敌人 - Unity Learn

    同样的操作,再Unity中把他变成精灵,挂上Order的脚本。

    11.1 敌人来回移动

    关于来回移动代码的编写,可以看看我的这篇博客:【Unity入门计划】2D游戏实现敌人来回移动控制脚本

    11.2 敌人伤害

     这里想要实现Ruby碰撞敌人后,掉血的操作。由于需要真实的碰撞感,因此不能使用之前的OnTriggerEnter,而是采取碰撞检测。

    1. //碰撞检测
    2. //添加敌人伤害的逻辑
    3. private void OnCollisionEnter2D(Collision2D other)
    4. {
    5. //获取和当前collision碰撞的对象的脚本组件
    6. RubyController player = other.gameObject.GetComponent();
    7. if (player != null)
    8. {
    9. player.ChangeHealth(Hurt);
    10. }
    11. }

    Collision2D 2D碰撞类

    官方文档:UnityEngine.Collision2D - Unity 脚本 API

    在之前的一篇【Unity入门计划】基本概念(2)-触发器 Trigger中就有说到碰撞检测的三种类别。 

    【Unity入门计划】Collision2D类&Collider2D类这篇博客中针对涉及到两个不同类的碰撞检测和触发检测,我对它们的回调函数在脚本中的使用进行一个简单的讨论。

     

  • 相关阅读:
    C语言从头学16——数据类型(二)
    C 语言实现 UDP
    上海亚商投顾:沪指创反弹新高 房地产板块掀涨停潮
    早上空腹喝水,比不吃早餐更伤胃?起床后先做2件事
    VUE3项目启动过程中遇到的问题记录
    Java回顾-集合概述-Collection的使用
    【润学】计算机网络八股文英文版(3)
    线上FullGC问题排查实践——手把手教你排查线上问题
    微服务间通信
    【算法与数据结构】701、LeetCode二叉搜索树中的插入操作
  • 原文地址:https://blog.csdn.net/qq_41835314/article/details/126209605