目录
- 前言
- 带上伤害
- Hitbox
- Hurtbox
- 实现效果
- 渐变淡出
- 添加受攻击效果
- Hurtbox
- 完善Enemy状态机
- 结果
- 剩下的都是逻辑完善的部分了,后面的我就跳过了。
前言
这次来深刻了解一下Godot中的伤害计算
带上伤害
我们将之前的Hitbox和HurtBox进行了一下简单的修改
Hitbox
using Godot;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace GodotNet_LegendOfPaladin2.GlobalClass
{[GlobalClass]public partial class Hitbox:Area2D{[Export]public int Damage = 1;/// <summary>/// 在实例化事件中添加委托/// </summary>public Hitbox() {AreaEntered += Hitbox_AreaEntered;}/// <summary>/// 当有Area2D进入时/// </summary>/// <param name="area"></param>private void Hitbox_AreaEntered(Area2D area){//当进入的节点是继承Area2D的HurtBox的时候if (area is Hurtbox){OnAreaEnterd((Hurtbox)area);}}/// <summary>/// 攻击判断/// </summary>/// <param name="area"></param>public void OnAreaEnterd(Hurtbox area){//GD.Print($" {Owner.Name} [Hit] {area.Owner.Name}");area.Hurt(this);}}}
Hurtbox
using Godot;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace GodotNet_LegendOfPaladin2.GlobalClass
{[GlobalClass]public partial class Hurtbox : Area2D{public event Action<Hitbox> HurtCallback;public event Action DieCallback;[Export]public int Health = 100;public Hurtbox(){HurtCallback += Hurt;DieCallback += Hurtbox_DieCallback;}private void Hurtbox_DieCallback(){GD.Print($"{Owner.Name} is Die");}/// <summary>/// 造成伤害/// </summary>/// <param name="num"></param>/// <param name="owner"></param>public void Hurt(Hitbox hitbox){Health -= hitbox.Damage;GD.Print($"{hitbox.Owner.Name} [Hit] {Owner.Name} in {hitbox.Damage} damage, Health = {Health}");if (Health <= 0){Hurtbox_DieCallback();}}}
}
实现效果
渐变淡出
添加受攻击效果
Hurtbox
按照我的想法,委托事件比信号更好用。
using Godot;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace GodotNet_LegendOfPaladin2.GlobalClass
{[GlobalClass]public partial class Hurtbox : Area2D{/// <summary>/// 注册伤害和死亡的委托事件/// </summary>public event Action<Hitbox> HurtCallback;public event Action DieCallback;[Export]public int Health = 100;public Hurtbox(){}/// <summary>/// 造成伤害/// </summary>/// <param name="num"></param>/// <param name="owner"></param>public void Hurt(Hitbox hitbox){Health -= hitbox.Damage;GD.Print($"{hitbox.Owner.Name} [Hit] {Owner.Name} in {hitbox.Damage} damage, Health = {Health}");HurtCallback?.Invoke(hitbox);if (Health <= 0){DieCallback?.Invoke();}}}
}
完善Enemy状态机
using Bogus;
using Godot;
using GodotNet_LegendOfPaladin2.GlobalClass;
using GodotNet_LegendOfPaladin2.Utils;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace GodotNet_LegendOfPaladin2.SceneModels
{public class EnemySceneModel : ISceneModel{private PrintHelper printHelper;private CharacterBody2D characterBody2D;private CollisionShape2D collisionShape2D;private Sprite2D sprite2D;private AnimationPlayer animationPlayer;public RayCast2D WallCheck { get; private set; }public RayCast2D FloorCheck { get; private set; }public RayCast2D PlayerCheck { get; private set; }public Hitbox Hitbox { get; private set; }public Hurtbox Hurtbox { get; private set; }public enum DirectionEnum{Left = -1, Right = 1}//设置正向的方向private DirectionEnum direction = DirectionEnum.Right;public DirectionEnum Direction{get => direction;//这个是一个生命周期的问题,属性的设置比树节点的加载更早//,所以我们会在Ready里面使用Direction = Direction来触发get函数set{if (characterBody2D != null && direction != value){//printHelper.Debug($"设置朝向,{value}");var scale = characterBody2D.Scale;//注意反转是X=-1。比如你左反转到右是X=-1,你右又反转到左也是X=-1。不是X=-1就是左,X=1就是右。scale.X = -1;characterBody2D.Scale = scale;direction = value;}}}public enum AnimationEnum{Hit, Idle, Run, Walk,Die}public AnimationEnum Animation = AnimationEnum.Idle;/// <summary>/// 动画持续时间/// </summary>private float animationDuration = 0;/// <summary>/// 最大速度/// </summary>public int MaxSpeed { get; set; }/// <summary>/// 加速度/// </summary>public int AccelerationSpeed { get; set; }/// <summary>/// Animation类型/// </summary>public int AnimationType { get; set; }public EnemySceneModel(PrintHelper printHelper){this.printHelper = printHelper;printHelper.SetTitle(nameof(EnemySceneModel));}public EnemySceneModel() { }public override void Process(double delta){animationDuration = (float)Mathf.MoveToward(animationDuration, 99, delta);SetAnimation();Move(delta);Direction = Direction;}public override void Ready(){characterBody2D = Scene.GetNode<CharacterBody2D>("CharacterBody2D");collisionShape2D = characterBody2D.GetNode<CollisionShape2D>("CollisionShape2D");sprite2D = characterBody2D.GetNode<Sprite2D>("Sprite2D");animationPlayer = characterBody2D.GetNode<AnimationPlayer>("AnimationPlayer");WallCheck = Scene.GetNode<RayCast2D>("CharacterBody2D/RayCast/WallCheck");FloorCheck = Scene.GetNode<RayCast2D>("CharacterBody2D/RayCast/FloorCheck");PlayerCheck = Scene.GetNode<RayCast2D>("CharacterBody2D/RayCast/PlayerCheck");Hitbox = Scene.GetNode<Hitbox>("CharacterBody2D/Hitbox");Hurtbox = Scene.GetNode<Hurtbox>("CharacterBody2D/Hurtbox");Hurtbox.HurtCallback += Hurtbox_HurtCallback;Hurtbox.DieCallback += Hurtbox_DieCallback;PlayAnimation();printHelper.Debug("加载成功!");printHelper.Debug($"当前朝向是:{Direction}");Direction = Direction;}private void Hurtbox_DieCallback(){printHelper.Debug("Boar is die");Animation = AnimationEnum.Die;}private void Hurtbox_HurtCallback(Hitbox hitbox){printHelper.Debug($"{hitbox.Owner.Name} [Hit] {Scene.Name} in {hitbox.Damage} damage, Health = {Hurtbox.Health}");Animation = AnimationEnum.Hit;}#region 动画状态机public void PlayAnimation(){var animationStr = string.Format("{0}_{1}", AnimationType, Animation);//printHelper.Debug($"播放动画,{animationStr}");animationPlayer.Play(animationStr);}public void SetAnimation(){//如果检测到玩家,就直接跑起来if (PlayerCheck.IsColliding()){//printHelper.Debug("检测到玩家,开始奔跑");Animation = AnimationEnum.Run;animationDuration = 0;}switch (Animation){//如果站立时间大于2秒,则开始散步case AnimationEnum.Idle:if (animationDuration > 2){//printHelper.Debug("站立时间过长,开始移动");Animation = AnimationEnum.Walk;animationDuration = 0;//如果撞墙,则反转if (WallCheck.IsColliding() || !FloorCheck.IsColliding()){if (Direction == DirectionEnum.Left){Direction = DirectionEnum.Right;}else{Direction = DirectionEnum.Left;}}//Direction = Direction;}break;//如果检测到墙或者没检测到地面或者动画时间超过4秒,则开始walkcase AnimationEnum.Walk:if ((WallCheck.IsColliding() || !FloorCheck.IsColliding()) || animationDuration > 4){Animation = AnimationEnum.Idle;animationDuration = 0;//printHelper.Debug("开始闲置");}break;//跑动不会立刻停下,当持续时间大于2秒后站立发呆case AnimationEnum.Run:if (animationDuration > 2){//printHelper.Debug("追逐时间到达上限,停止");Animation = AnimationEnum.Idle;animationDuration = 0;}break;case AnimationEnum.Die:if (!animationPlayer.IsPlaying()){Scene.QueueFree();}break;case AnimationEnum.Hit:if(!animationPlayer.IsPlaying()){Animation = AnimationEnum.Idle;}break;}PlayAnimation();}#endregion#region 物体移动public void Move(double delta){var velocity = characterBody2D.Velocity;velocity.Y += ProjectSettingHelper.Gravity * (float)delta;switch (Animation){case AnimationEnum.Idle:velocity.X = 0;break;case AnimationEnum.Walk:velocity.X = MaxSpeed / 3;break;case AnimationEnum.Run:velocity.X = MaxSpeed;break;}velocity.X = velocity.X * (int)Direction;characterBody2D.Velocity = velocity;//printHelper.Debug(JsonConvert.SerializeObject(characterBody2D.Velocity));characterBody2D.MoveAndSlide();}#endregion}
}