Godot.NET C#IOC重构(9-10):三连击,攻击框

news/2024/10/12 18:20:11

目录
  • 前言
  • AnimationPlayer和AnimatedSprite2D
    • 将导出属性添加到关键帧里面。
    • 状态机构建
      • 核心代码
      • 完整代码
      • 实现效果
  • 碰撞框和受攻击框
    • 全局类
      • HitBox:攻击框
      • HurtBox:受击框
      • 实现效果
    • 添加Player攻击
  • 总结

前言

这篇博客来深入讲解一下Godot中的AnimationPlayer

AnimationPlayer和AnimatedSprite2D

AnimatedSprite2D就是一个简单的帧动画组件,只能用于播放简单的帧动画。
AnimationPlayer是一个可以实现复杂的帧动画的组件,可以添加各种关键帧。

将导出属性添加到关键帧里面。

我们在C# 中添加导出属性

[Export]
public bool CanCombo
{get => Model.CanCombo;set { Model.CanCombo = value; }
}

我们添加这个CanCombo的用意是用来检测动画是否播放到位,有点类似于LOL里面的攻击前摇和攻击后摇的感觉。

状态机构建

核心代码

using Godot;
using GodotNet_LegendOfPaladin2.Utils;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Godot.TextServer;namespace GodotNet_LegendOfPaladin2.SceneModels
{public class PlayerSceneModel : ISceneModel{public enum AnimationEnum { REST, Idel, Running, Jump, Fall, Land, WallSliding, Attack_1, Attack_2, Attack_3 }/// <summary>/// 可以移动的状态/// </summary>public AnimationEnum[] CanMoveAnimation = [AnimationEnum.Idel, AnimationEnum.Running ,AnimationEnum.Fall,AnimationEnum.WallSliding];private AnimationEnum animationEnum = AnimationEnum.Idel;public AnimationEnum AnimationState{get => animationEnum;private set{if (value != animationEnum){printHelper.Debug($"{value}");}animationEnum = value;}}public bool IsLand { get; private set; } = true;public float Direction { get; private set; } = 0;private void SetAnimation(){isComboRequest = Input.IsActionPressed($"{ProjectSettingHelper.InputMapEnum.attack}") && canCombo;//isComboRequest = !Input.IsActionJustReleased(ProjectSettingHelper.InputMapEnum.attack.ToString());var isPlaying = animationPlayer.IsPlaying();switch (AnimationState){case AnimationEnum.Idel:if (Input.IsActionJustPressed($"{ProjectSettingHelper.InputMapEnum.attack}")){AnimationState = AnimationEnum.Attack_1;}else if (!Mathf.IsZeroApprox(Direction)){AnimationState = AnimationEnum.Running;}break;case AnimationEnum.Jump:if (characterBody2D.Velocity.Y < 0){AnimationState = AnimationEnum.Fall;}else if (characterBody2D.IsOnWall()){AnimationState = AnimationEnum.WallSliding;}break;case AnimationEnum.Running:if (Input.IsActionJustPressed($"{ProjectSettingHelper.InputMapEnum.attack}")){AnimationState = AnimationEnum.Attack_1;}else if (Mathf.IsZeroApprox(Direction)){AnimationState = AnimationEnum.Idel;}break;case AnimationEnum.Fall:if (Mathf.IsZeroApprox(characterBody2D.Velocity.Y)){AnimationState = AnimationEnum.Land;//开启异步任务,如果过了400毫秒,仍然是Land,则转为IdelTask.Run(async () =>{await Task.Delay(400);if (AnimationState == AnimationEnum.Land){AnimationState = AnimationEnum.Idel;}});}else if (characterBody2D.IsOnWall()){AnimationState = AnimationEnum.WallSliding;}break;case AnimationEnum.Land:break;case AnimationEnum.WallSliding:if (!characterBody2D.IsOnWall()){AnimationState = AnimationEnum.Fall;}break;case AnimationEnum.Attack_1:if (isComboRequest && !isPlaying){AnimationState = AnimationEnum.Attack_2;isComboRequest = false;}else if (!isPlaying){AnimationState = AnimationEnum.Idel;}break;case AnimationEnum.Attack_2:if (isComboRequest && !isPlaying){AnimationState = AnimationEnum.Attack_3;isComboRequest = false;}else if (!isPlaying){AnimationState = AnimationEnum.Idel;}break;case AnimationEnum.Attack_3:if (!isPlaying){AnimationState = AnimationEnum.Idel;}break;}if (!Mathf.IsZeroApprox(Direction)){sprite2D.FlipH = Direction < 0;}PlayAnimation();}/// <summary>/// 播放动画/// </summary>private void PlayAnimation(){//printHelper.Debug(AnimationState.ToString());animationPlayer.Play(AnimationState.ToString());}}
}

完整代码

using Godot;
using GodotNet_LegendOfPaladin2.Utils;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Godot.TextServer;namespace GodotNet_LegendOfPaladin2.SceneModels
{public class PlayerSceneModel : ISceneModel{private PrintHelper printHelper;#region 常量/// <summary>/// 速度/// </summary>public const float RUN_SPEED = 200;/// <summary>/// 加速度,为了显示明显,20秒内到达RUN_SPEED的速度/// </summary>public const float ACCELERATION = (float)(RUN_SPEED / 20);/// <summary>/// 跳跃速度/// </summary>public const float JUMP_SPEED = -350;/// <summary>/// 蹬墙跳的速度/// </summary>public readonly Vector2 WALL_JUMP_VELOCITY = new Vector2(400, -320);private bool canCombo = false;/// <summary>/// 是否能够连击/// </summary>public bool CanCombo{get => canCombo; set{printHelper.Debug($"设置canComBo:{value}");canCombo = value;}}#endregionprivate Sprite2D sprite2D;private CharacterBody2D characterBody2D;private AnimationPlayer animationPlayer;private Camera2D camera2D;private bool isComboRequest = false;public enum AnimationEnum { REST, Idel, Running, Jump, Fall, Land, WallSliding, Attack_1, Attack_2, Attack_3 }/// <summary>/// 可以移动的状态/// </summary>public AnimationEnum[] CanMoveAnimation = [AnimationEnum.Idel, AnimationEnum.Running ,AnimationEnum.Fall,AnimationEnum.WallSliding];private AnimationEnum animationEnum = AnimationEnum.Idel;public AnimationEnum AnimationState{get => animationEnum;private set{if (value != animationEnum){printHelper.Debug($"{value}");}animationEnum = value;}}public bool IsLand { get; private set; } = true;public float Direction { get; private set; } = 0;/// <summary>/// 跳跃重置时间/// </summary>public const float JudgeIsJumpTime = 0.5f;private float isJumpTime = 0;public PlayerSceneModel(PrintHelper printHelper){this.printHelper = printHelper;this.printHelper.SetTitle(nameof(PlayerSceneModel));}public override void Process(double delta){PlayerMove(delta);SetAnimation();}/// <summary>/// 角色移动/// </summary>/// <param name="delta"></param>private void PlayerMove(double delta){var velocity = characterBody2D.Velocity;velocity.Y += ProjectSettingHelper.Gravity * (float)delta;Direction = Input.GetAxis(ProjectSettingHelper.InputMapEnum.move_left.ToString(),ProjectSettingHelper.InputMapEnum.move_right.ToString());//原本直接赋值//velocity.X = direction*RUN_SPEED;//现在使用加速度velocity.X = Mathf.MoveToward(velocity.X, Direction * RUN_SPEED, ACCELERATION);//按下跳跃键,就将跳跃时间设置为判断区间if (Input.IsActionJustPressed(ProjectSettingHelper.InputMapEnum.jump.ToString())){isJumpTime = JudgeIsJumpTime;}//慢慢变成0isJumpTime = (float)Mathf.MoveToward(isJumpTime, 0, delta);//如果在跳跃时间的判断内if (isJumpTime != 0){if (characterBody2D.IsOnFloor()){//进行跳跃之后,跳跃时间结束isJumpTime = 0;velocity.Y = JUMP_SPEED;AnimationState = AnimationEnum.Jump;}else if (AnimationState == AnimationEnum.WallSliding){//进行跳跃之后,跳跃时间结束isJumpTime = 0;velocity = WALL_JUMP_VELOCITY;//获取墙面的法线的方向velocity.X *= characterBody2D.GetWallNormal().X;AnimationState = AnimationEnum.Jump;}}characterBody2D.Velocity = velocity;if (CanMoveAnimation.Contains(AnimationState)){characterBody2D.MoveAndSlide();}}private void SetAnimation(){isComboRequest = Input.IsActionPressed($"{ProjectSettingHelper.InputMapEnum.attack}") && canCombo;//isComboRequest = !Input.IsActionJustReleased(ProjectSettingHelper.InputMapEnum.attack.ToString());var isPlaying = animationPlayer.IsPlaying();switch (AnimationState){case AnimationEnum.Idel:if (Input.IsActionJustPressed($"{ProjectSettingHelper.InputMapEnum.attack}")){AnimationState = AnimationEnum.Attack_1;}else if (!Mathf.IsZeroApprox(Direction)){AnimationState = AnimationEnum.Running;}break;case AnimationEnum.Jump:if (characterBody2D.Velocity.Y < 0){AnimationState = AnimationEnum.Fall;}else if (characterBody2D.IsOnWall()){AnimationState = AnimationEnum.WallSliding;}break;case AnimationEnum.Running:if (Input.IsActionJustPressed($"{ProjectSettingHelper.InputMapEnum.attack}")){AnimationState = AnimationEnum.Attack_1;}else if (Mathf.IsZeroApprox(Direction)){AnimationState = AnimationEnum.Idel;}break;case AnimationEnum.Fall:if (Mathf.IsZeroApprox(characterBody2D.Velocity.Y)){AnimationState = AnimationEnum.Land;//开启异步任务,如果过了400毫秒,仍然是Land,则转为IdelTask.Run(async () =>{await Task.Delay(400);if (AnimationState == AnimationEnum.Land){AnimationState = AnimationEnum.Idel;}});}else if (characterBody2D.IsOnWall()){AnimationState = AnimationEnum.WallSliding;}break;case AnimationEnum.Land:break;case AnimationEnum.WallSliding:if (!characterBody2D.IsOnWall()){AnimationState = AnimationEnum.Fall;}break;case AnimationEnum.Attack_1:if (isComboRequest && !isPlaying){AnimationState = AnimationEnum.Attack_2;isComboRequest = false;}else if (!isPlaying){AnimationState = AnimationEnum.Idel;}break;case AnimationEnum.Attack_2:if (isComboRequest && !isPlaying){AnimationState = AnimationEnum.Attack_3;isComboRequest = false;}else if (!isPlaying){AnimationState = AnimationEnum.Idel;}break;case AnimationEnum.Attack_3:if (!isPlaying){AnimationState = AnimationEnum.Idel;}break;}if (!Mathf.IsZeroApprox(Direction)){sprite2D.FlipH = Direction < 0;}PlayAnimation();}/// <summary>/// 播放动画/// </summary>private void PlayAnimation(){//printHelper.Debug(AnimationState.ToString());animationPlayer.Play(AnimationState.ToString());}/// <summary>/// 是否准备好了/// </summary>public override void Ready(){characterBody2D = Scene.GetNode<CharacterBody2D>("CharacterBody2D");camera2D = characterBody2D.GetNode<Camera2D>("Camera2D");sprite2D = characterBody2D.GetNode<Sprite2D>("Sprite2D");animationPlayer = characterBody2D.GetNode<AnimationPlayer>("AnimationPlayer");printHelper.Debug("加载完成");AnimationState = AnimationEnum.Idel;PlayAnimation();}/// <summary>/// 设置相机/// </summary>/// <param name="rect2"></param>public void SetCameraLimit(Rect2 rect2){camera2D.LimitLeft = (int)rect2.Position.X;//camera2D.LimitTop = (int)rect2.Position.Y;camera2D.LimitRight = (int)rect2.End.X;camera2D.LimitBottom = (int)rect2.End.Y;//printHelper.Debug(JsonConvert.SerializeObject(rect2));}}
}

实现效果

碰撞框和受攻击框

全局类

Godot Engine 4.2 简体中文文档 编写脚本 C#/.NET C# 全局类

这个的优点就是组件化,扩展性比较的强。类似于C# 的扩展函数,在原本的基础上面进行功能的加强。

推荐大家把Godot的高级API了解一下

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{/// <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($"[Hit] {Owner.Name} => {area.Owner.Name}");}}}

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{}
}

实现效果

添加Player攻击

我们给三个Attack攻击都设置好了Disable关键帧之后,测试一下

总结

我其实省略了大部分的代码,详细的代码可以去看原视频的讲解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/27219.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

[MDP.AspNetCore] 實作OAuth協定SSO Server/Client專案範例

團隊負責的系統變多的時候,使用SSO Server提供統一身分驗證,讓團隊只需要維護一份用戶資料及一個身分驗證服務。除了減少團隊維護成本之外,也讓使用者不用記憶多個站台的帳號密碼,提供更好的使用者體驗。 本篇文章,介紹使用MDP.AspNetCore的NuGet套件,所建立的實作OAuth協…

深入 Django 模型层:数据库设计与 ORM 实践指南

title: 深入 Django 模型层:数据库设计与 ORM 实践指南 date: 2024/5/3 18:25:33 updated: 2024/5/3 18:25:33 categories:后端开发tags:Django ORM 模型设计 数据库关系 性能优化 数据安全 查询操作 模型继承第一章:引言 Django是一个基于Python的开源Web应用程序框架,它遵…

中考常见同义词和同义短语总结

about(大约)= or soa couple of=several =a fewa (large) number of= manya little =a little bita lot of=lots of= many/mucha piece of advice =a suggestiona quarter to five =four forty-fivea teacher with experience =an experienced teachera two-hour walk= two hou…

exhentai服务器不稳定怎么解决

exhentai服务器不稳定问题可以归因于以下几个原因及解决方案:网站流量过大:避开高峰时段访问,使用加速器或代理服务器。服务器硬件故障:联系技术支持,尝试不同的连接或浏览器。网络连接问题:检查连接稳定性,确保带宽充足,优先使用有线连接。浏览器缓存或cookie问题:清…

虚拟机嵌套 VMWare

VMWare Workstation Pro 开启嵌套,关键是这里,如下图:

解决创建SpringBoot工程加载较慢的问题

设置Server URL 将 https://start.spring.io 改为 https://start.aliyun.com 如图所示:启动演示 如图所示 ,启动成功

photoshop使用小技巧

如何按照选区新建文档?选区情况下,按Ctrl+C,然后新建即可看见"新建剪贴板文档",这个就是尺寸一样的了。

[转帖][PC硬件] 性能领先至多3倍!第四代AMD EPYC处理器上市:96核心

https://www.chiphell.com/thread-2459330-1-1.html AMD “Zen4”架构的锐龙7000系列在桌面处理器上已经展示了强劲的实力,现在又闪电杀到了服务器、数据中心市场。 代号“Genoa”(热那亚)的第四代AMD EPYC(霄龙)处理器——AMD EPYC 9004系列正式降临,新一轮大战正式开启!回…