当前位置: 首页 > news >正文

godot+c#实现玩家动画

前言

在前一个文章,我们实现了玩家的基础移动,那么这次我们来实现玩家的动画,实现行走,攻击,跳跃状态。效果如下

在这里插入图片描述

素材

首先,我们先下载一个素材

像素艺术

  • 打开之后如下,点击Download Now

在这里插入图片描述

  • 里面有一个No thanks, just take me to the downloads,点击它下载
    在这里插入图片描述
  • 然后把素材解压到自己游戏项目的根目录即可

在这里插入图片描述

动画创建

首先,先对我们之前的玩家角色进行改造在Player节点下新增AnimatedSprite2D子节点,然后点击该子节点检查器有一个AnimationSprite Frame属性,在它右键新增,并点击新增的Sprite Frame属性
注意:请把我们之前的Sprite2D节点删除

在这里插入图片描述

点击之后中下方会出现一个动画面板

其中左边部分,上部分分别是
新增动画:要实现不同的玩家操作展示不一样的画面,我们需要新增不同的动画,如攻击,受伤,待机等
复制动画:对已有的动画进行复制,可以减少操作
删除动画:顾名思义,删除已有动画
加载自动播放:进入动画默认播放,一般只有待机动画才开启
动画循环:因为动画有固定的帧数,播放完如果不选中,就直接停止播放动画了
5.0FPS:这个指动画播放速度,数字越大播放越快

左边下面的动画列表,可以分别点击动画进行命名

其中右边部分分别是
靠左部分:分别代表逐帧播放,还是直接播放,播放可以看到动画效果
从文件添加帧:从文件中找图片资源,添加帧
从精灵表添加帧:针对图片资料对图片进行分割添加帧,我们接下来需要用这个功能添加
再右边有复制帧 粘贴帧 左边插入空白帧 右边插入空白帧 删除帧等就不多赘述。顾名思义

在这里插入图片描述

对于第一个default动画,我们点击它重命名idle,待机,然后我们点击从精灵表添加帧找到我们的图片资源,这里我找的是Free Pixel Art Asset Pack/characters/Characters/Wizard/Wizard这个是法师角色,如果更喜欢剑士角色,可以找到Kinght

在这里插入图片描述

找到里面的Idle.png图片,然后点开,会看到图片被分割成了4 * 4
右边的操作我们会看到有水平和垂直,它代表的是我们水平和垂直需要分割成几份,分割成几份,我们需要看精灵图水平和垂直各占几个角色,如当前图片是一行六排,因此垂直设置1,水平设置6,其他属性保持不变

在这里插入图片描述
在这里插入图片描述

然后从左到右依次点击,注意这里会有标数字,代表不同的图片播放的顺序
然后点击添加即可

在这里插入图片描述

按照如下,idle我们勾选加载自动播放,和循环动画
按照如上方法找到run.pngattack.png
分别添加行走和攻击
只不过
行走只打开循环动画
攻击加载自动播放和循环动画都不勾选
如果有兴趣,可以把死亡和受伤动画也一起加上,不要忘记改名称

在这里插入图片描述

如果这个时候拖到背景场景,会发现人物很小

在这里插入图片描述

因此,我们选中AnimatedSprite2D节点,然后找到Transformscale属性,如我设置2.5倍

在这里插入图片描述

再次启动

在这里插入图片描述

输入映射修改

为了适配我们的攻击,跳跃,我加了两个输入映射,鼠标左键作为攻击,空格作为跳跃,详情添加方式,请参照我上一个文章
注意:我后续计划以2D横版进行开发,因此我把上下移动给去掉了

在这里插入图片描述

脚本编写

在我的脚本代码中,我有播放动画,播放动画需要播放我们前面动画的命名

Player.cs代码

using Godot;
using System;public partial class Player : CharacterBody2D
{[Export] public float Speed = 300.0f;[Export] public float JumpVelocity = -400.0f;[Export] public float DoubleJumpVelocity = -300.0f;[Export] public float GroundFriction = 0.2f;[Export] public float AirResistance = 0.05f;[Export] public float Gravity = 1000.0f;[Export] public AnimatedSprite2D AnimatedSprite;private enum PlayerState { Idle, Moving, Attacking, Jumping, Falling }private PlayerState _currentState = PlayerState.Idle;private int _jumpCount = 0;private const int MAX_JUMPS = 2;private bool _wasOnFloor = false;// 添加一个标志来跟踪攻击动画是否已完成private bool _attackAnimationFinished = false;public override void _Ready(){base._Ready();if (AnimatedSprite != null){AnimatedSprite.AnimationFinished += OnAnimationFinished;}}public override void _PhysicsProcess(double delta){Vector2 direction = GameInputEvent.MovementInput();bool justLanded = !_wasOnFloor && IsOnFloor();_wasOnFloor = IsOnFloor();// 应用重力if (!IsOnFloor()){Velocity = Velocity with { Y = Velocity.Y + Gravity * (float)delta };}else{_jumpCount = 0;}// 处理攻击 - 只有在非攻击状态且在地面时才能发起攻击if (GameInputEvent.IsAttacking() && _currentState != PlayerState.Attacking && IsOnFloor()){_currentState = PlayerState.Attacking;_attackAnimationFinished = false;AnimatedSprite.Play("attack");}// 处理跳跃 - 不能在攻击状态下跳跃if (GameInputEvent.IsJumping() && _currentState != PlayerState.Attacking){if (IsOnFloor() || _jumpCount < MAX_JUMPS){_jumpCount++;Velocity = Velocity with { Y = _jumpCount == 1 ? JumpVelocity : DoubleJumpVelocity };_currentState = PlayerState.Jumping;AnimatedSprite.Play("idle");}}// 处理水平移动和摩擦HandleHorizontalMovement(direction, delta);// 状态处理HandleState(direction);MoveAndSlide();}private void HandleHorizontalMovement(Vector2 direction, double delta){if (IsOnFloor()){if (direction != Vector2.Zero){float targetSpeed = direction.X * Speed;Velocity = Velocity with { X = (float)Mathf.Lerp(Velocity.X, targetSpeed, 0.2f) };}else{Velocity = Velocity with { X = Velocity.X * (1 - GroundFriction) };if (Mathf.Abs(Velocity.X) < 1.0f){Velocity = Velocity with { X = 0 };}}}else{if (direction != Vector2.Zero){float targetSpeed = direction.X * Speed;Velocity = Velocity with { X = (float)Mathf.Lerp(Velocity.X, targetSpeed, 0.1f) };}else{Velocity = Velocity with { X = Velocity.X * (1 - AirResistance) };}}}private void HandleState(Vector2 direction){switch (_currentState){case PlayerState.Attacking:// 检查攻击动画是否已完成if (_attackAnimationFinished){TransitionFromAttack(direction);}break;case PlayerState.Jumping:if (Velocity.Y >= 0){_currentState = PlayerState.Falling;AnimatedSprite.Play("idle");}break;case PlayerState.Falling:if (IsOnFloor()){_currentState = direction == Vector2.Zero ? PlayerState.Idle : PlayerState.Moving;AnimatedSprite.Play(direction == Vector2.Zero ? "idle" : "run");}break;case PlayerState.Moving:if (direction == Vector2.Zero){_currentState = PlayerState.Idle;AnimatedSprite.Play("idle");}else if (!IsOnFloor()){_currentState = Velocity.Y < 0 ? PlayerState.Jumping : PlayerState.Falling;AnimatedSprite.Play("idle");}else{// 确保面向正确方向AnimatedSprite.FlipH = direction.X < 0;}break;case PlayerState.Idle:if (direction != Vector2.Zero){_currentState = PlayerState.Moving;AnimatedSprite.FlipH = direction.X < 0;AnimatedSprite.Play("run");}else if (!IsOnFloor()){_currentState = Velocity.Y < 0 ? PlayerState.Jumping : PlayerState.Falling;AnimatedSprite.Play("idle");}break;}}private void TransitionFromAttack(Vector2 direction){if (IsOnFloor()){_currentState = direction == Vector2.Zero ? PlayerState.Idle : PlayerState.Moving;AnimatedSprite.Play(direction == Vector2.Zero ? "idle" : "run");}else{_currentState = Velocity.Y < 0 ? PlayerState.Jumping : PlayerState.Falling;AnimatedSprite.Play("idle");}}private void OnAnimationFinished(){// 检查当前播放的动画是否是攻击动画if (AnimatedSprite.Animation == "attack"){_attackAnimationFinished = true;}}
}

GameInput.cs代码(输入映射)

using Godot;
using System;public class GameInputEvent
{public static Vector2 direction;public static Vector2 MovementInput(){if (Input.IsActionPressed("move_left")){direction = Vector2.Left;}else if (Input.IsActionPressed("move_right")){direction = Vector2.Right;}else{direction = Vector2.Zero;}return direction;}public static bool IsAttacking(){return Input.IsActionJustPressed("attack");}public static bool IsJumping() {return Input.IsActionJustPressed("jump");}public static bool isMove(){return direction != Vector2.Zero;}
}

配置并启动

代码写好之后,记得编译一下,因为我使用了Export属性,在代码中,该代码的意思是可以让我们在检查器中设置需要的属性值,我的代码需要引用动画,需要告诉它是哪个动画,默认是空的,因此我们需要点击主节点,因为主节点才有挂载我们的Player.cs脚本,点击它选中我们的AnimateSprite2D节点

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

需要注意的是,我们因为有跳跃,并且加入了重力检测,所以,我们需要有一个碰撞体来接住玩家,我在背景场景新建了staticBody2D节点,然后在里面添加了方形碰撞体,如我演示的图片,为了更清楚看到它,我修改了颜色,改变了长度并调整位置如下

在这里插入图片描述

然后启动我们的游戏就可以正常的实现玩家播放待机,攻击和进行跳跃了

结语

如上我使用godot+c#实现玩家动画播放


文章转载自:

http://GcztRUJ0.cmcjp.cn
http://ryxor23l.cmcjp.cn
http://nCgPB9zF.cmcjp.cn
http://tRFQB3ON.cmcjp.cn
http://x5s3p4Wr.cmcjp.cn
http://QJclltvu.cmcjp.cn
http://7T4dvqAy.cmcjp.cn
http://BDKUgW2D.cmcjp.cn
http://HzyvQtUk.cmcjp.cn
http://JUvwNQ3l.cmcjp.cn
http://gqLyoXGS.cmcjp.cn
http://1vN3xEX1.cmcjp.cn
http://CTrvRSpw.cmcjp.cn
http://Pl5yasic.cmcjp.cn
http://iq0X70Zi.cmcjp.cn
http://RkKymMFU.cmcjp.cn
http://l3FYKhxY.cmcjp.cn
http://g8KbgDiW.cmcjp.cn
http://pItGmYtu.cmcjp.cn
http://x0bbCUAh.cmcjp.cn
http://NyU3dV8H.cmcjp.cn
http://OE4i9r0V.cmcjp.cn
http://w1BqvXEb.cmcjp.cn
http://2Ra1Kbvg.cmcjp.cn
http://l68jRKu0.cmcjp.cn
http://FrATo6hU.cmcjp.cn
http://xEAtShvr.cmcjp.cn
http://T4L3FMeo.cmcjp.cn
http://m43ZN6ih.cmcjp.cn
http://b9drl7qk.cmcjp.cn
http://www.dtcms.com/a/386599.html

相关文章:

  • 【Axure高保真原型】标签树分类查询案例
  • 系统架构设计(一)
  • RK3568下QT实简易文件浏览器
  • 设备综合效率(OEE)讲解与计算案例
  • STM32G4 电流环闭环(二) 霍尔有感运行
  • git-gui --批量处理文件
  • 【代码随想录day 28】 力扣 55.跳跃游戏
  • Python Flask 项目实战
  • whisper.cpp参数调优
  • C语言第13讲
  • brew install太慢的解决办法
  • vite+vue3中使用FFmpeg@0.12.15实现视频编辑功能,不依赖SharedArrayBuffer!!!
  • AI智能问数能力全面升级,DataEase开源BI工具v2.10.13 LTS版本发布
  • 【pytorch】tensor的定义与属性
  • 【问题】使用腾讯宝塔部署并启动Nodejs应用异常处理Cannot find module ‘express‘
  • vue-office 在线预览
  • 嵌入式基本概念:什么是指令集,微架构,IDE,DFP等等是什么意思,有什么关系???
  • Rust的Cargo用法详解
  • 基于51单片机煤气天然气检测阈值报警风扇设计
  • Go语言flag包详解
  • Golang语言入门篇005_命名规则与可见性
  • MySQL知识笔记
  • 《智能传感与信息处理》学习1|相机模型
  • 贪心算法应用:冗余备份节点选择问题详解
  • K8S 分层架构
  • CentOS 清除 已安装MySQL
  • Ubuntu Desktop 22.04.5 LTS 使用默认的 VNC 远程桌面
  • 【脚本注入网页】XSS
  • 设计模式之:备忘录模式
  • 网页抓包怎么做?网页抓包工具推荐、HTTPS 抓包、本机代理抓包与实战流程