Godot 4 教程《勇者传说》依赖注入 学习笔记(0):环境配置

文章目录

  • 前言
  • 相关地址
  • 环境配置
  • 初始化环境配置
    • 文件夹结构
    • 代码结构
    • 代码运行
  • 资源文件导入
  • 像素风格窗口环境设置
  • 背景设置,Tileap使用
    • 自动TileMap
  • 人物场景
  • 动画节点添加
    • 站立节点添加
    • 移动动画添加
  • 通过依赖注入获取Godot的全局属性
    • 项目声明
  • 当前项目逻辑讲解
  • 角色下降
    • 添加代码
    • 位置问题的思考
      • 在Node2D上面挂载Lable节点
      • 在CharacterBody2D下面挂载
      • 解决方案
        • 修改代码
        • 动画节点的问题,需要重新绑定
        • 为什么我要这么写
  • 动画效果
    • 初始化AnimationPlayer
  • 输入映射
    • 获取输入
      • 简单移动
      • 完善输入和添加动画
      • 完善跳跃手感

前言

我之前解决了C# 的IOC的配置,现在来认真学习一个完整的Godot 项目。我看B站上面这个教程非常的好,所以打算用C# 去复刻一下,使用IOC依赖注入的想法。

相关地址

十分钟制作横版动作游戏|Godot 4 教程《勇者传说》#0

人物素材

环境素材

Gclove2000/GodotNet_LegendOfPaladin

在这里插入图片描述

环境配置

  • Windows 10
  • .net core 8.0
  • Visual Studio 2022
  • godot.net 4.2.1

初始化环境配置

Godot.NET C# 工程化开发(1):通用Nuget 导入+ 模板文件导出,包含随机数生成,日志管理,数据库连接等功能

在这里插入图片描述

文件夹结构

  • Godot:Godot项目+主要游戏逻辑代码
  • GodotProgram:帮助类

代码结构

  • GodotNet_LegndOfPaladin:Godot主要逻辑
    • SceneModels:场景IOC对象
    • SceneScirpts:场景对应脚本
    • Util: Godot API帮助类
      • PackedSceneHelper:打包场景加载
    • Program:IOC容器
  • GodotProgram:C# 主要逻辑
    • Assets:资产文件
    • DB:数据库对象
    • Interfaces:接口
    • Service:服务
    • Utils:帮助类

代码运行

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

资源文件导入

人物素材

环境素材

在这里插入图片描述

在这里插入图片描述

像素风格窗口环境设置

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

背景设置,Tileap使用

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

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

自动TileMap

Godot 官方2D C#重构(3):TileMap使用

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
大致实现效果

在这里插入图片描述
绘制TimeMap地形需要比较强的熟练度。多多联系即可

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

人物场景

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
长按左键选择站立动画

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

动画节点添加

站立节点添加

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
点击6次,添加6个关键帧
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

移动动画添加

和上面的一样

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

通过依赖注入获取Godot的全局属性

Godot的全局属性是通过字符串的方式获取的,这非常容易出问题。而且我们也希望这种配置信息能在项目启动的时候就获取

在这里插入图片描述

Godot ProjectSettings 字符串对应数据

项目声明

    public class GodotProjectSettingHelper
    {
        private NlogHelper nlogHelper;

        public readonly float Gravity = 0;


        public GodotProjectSettingHelper(NlogHelper nlogHelper)
        {
            this.nlogHelper = nlogHelper;
            Gravity = (float)ProjectSettings.GetSetting("physics/2d/default_gravity");
        }
    }

在这里插入图片描述

在这里插入图片描述

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

当前项目逻辑讲解

在这里插入图片描述
在这里插入图片描述
所以我们新建一个场景的逻辑是

  • 新增XXX.tscn
  • 挂载XXXScene.sc脚本
  • IOC注入XXXSceneModel.cs 类
  • PackedSceneHelper添加对应的PackedScene

详情请看我的Github源码

Gclove2000/GodotNet_LegendOfPaladin

角色下降

在这里插入图片描述

在这里插入图片描述

添加代码

public class PlayerSceneModel : ISceneModel
{
    private NlogHelper nlogHelper;

    private GodotProjectSettingHelper godotProjectSettingHelper;
    public PlayerSceneModel(NlogHelper nlogHelper,GodotProjectSettingHelper godotProjectSettingHelper) {
        this.nlogHelper = nlogHelper;
        this.godotProjectSettingHelper = godotProjectSettingHelper;
    }

    private CharacterBody2D characterBody2D;


    public override void Process(double delta)
    {
        //给角色一个速度,因为重力是加速度,所以角色的速度会不断的增加。
        characterBody2D.Velocity += new Vector2(0, godotProjectSettingHelper.Gravity * (float)delta);

        //让物体以这个速度进行移动
        characterBody2D.MoveAndSlide();
        nlogHelper.Debug($"x:{characterBody2D.Velocity.X},y:{characterBody2D.Velocity.Y}");
    }

    public override void Ready()
    {

        nlogHelper.Debug($"当前重力值为:{godotProjectSettingHelper.Gravity}");

        characterBody2D = this.Sence.GetNode<CharacterBody2D>("CharacterBody2D");
    }
}

在这里插入图片描述

位置问题的思考

我们知道CharacterBody2D就是为了获取CollisionShape2D的位置。因为他的位置取决于重力,物理碰撞,加速度等多方面因素。相当于他的位置是自动变化的

在Node2D上面挂载Lable节点

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

在CharacterBody2D下面挂载

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

解决方案

在这里插入图片描述

我们只需要CharacterBody2D给我们的位置更改即可,而在Godot中,Position都是相对父节点的位置。所以每次Character移动的时候,我们将CharacterBody2D的位置获取,然后我们将Character的相对位置 设置为0即可

修改代码
public override void Process(double delta)
{
    //给角色一个速度,因为重力是加速度,所以角色的速度会不断的增加。
    characterBody2D.Velocity += new Vector2(0, godotProjectSettingHelper.Gravity * (float)delta);
    
    //让物体以这个速度进行移动
    characterBody2D.MoveAndSlide();

    var postion = characterBody2D.Position;
    characterBody2D.Position = new Vector2(0, 0);
    this.Sence.Position += postion;

}
动画节点的问题,需要重新绑定

主要,如果修改动画节点的位置,会导致绑定出现问题

在这里插入图片描述

为什么我要这么写

因为我们不一定会写横版战斗游戏,横版战斗是有重力的,但是俯视角战斗又没有重力了,或者说不是垂直向下的重力,而是俯视角的效果。比如【以撒的结合】

在这里插入图片描述

动画效果

在Godot中,AnimationPlayer通过【Play】这个函数来播放动画。但是Godot中,Play是通过字符串的形式调用的。为了保证字符串的正确性,我们添加一个Enum枚举类型来对其进行限制

在这里插入图片描述

初始化AnimationPlayer

在这里插入图片描述

//枚举类型,防止拼写错误
public enum AnimationFlame { REST, idel,running }


......

public override void Ready()
{

    nlogHelper.Debug($"当前重力值为:{godotProjectSettingHelper.Gravity}");
    //初始化子节点
    characterBody2D = this.Sence.GetNode<CharacterBody2D>("CharacterBody2D");
    animationPlayer = this.Sence.GetNode<AnimationPlayer>("AnimationPlayer");
    //播放动画
    animationPlayer.Play(AnimationFlame.idel.ToString());
}

在这里插入图片描述

输入映射

我们输入上下左右,一般都是wasd,但是因为我们可能要做手柄,可能也要做移动端。所以最好设置一个输入映射好一些。

在这里插入图片描述
我的输入是,wsad是上下左右,【j】/【空格】是跳跃

在这里插入图片描述

在这里插入图片描述

获取输入

Godot 输入处理

我们在任意一个节点下面去获取按钮事件


public override void Process(double delta)
{
	//获取move_left对应按下事件
    if (Input.IsActionPressed("move_left"))
    {
        nlogHelper.Debug("move_left 按下");
    }
    
}

在这里插入图片描述

简单移动

在这里插入图片描述

public const float RUN_SPEED = 200;
.......

public override void Process(double delta)
{

    var velocity = new Vector2();
    var direction = Input.GetAxis(InputMapEnum.move_left.ToString(), InputMapEnum.move_right.ToString());
    var y = godotProjectSettingHelper.Gravity * (float)delta;
    var x = direction * RUN_SPEED;

    //在C# 中,
    velocity = characterBody2D.Velocity;
    //X是最终速度,所以不需要相加
    velocity.X = x;
    //给角色一个速度,因为重力是加速度,所以角色的速度会不断的增加。
    velocity.Y += y;
    characterBody2D.Velocity = velocity;
    //让物体以这个速度进行移动
    characterBody2D.MoveAndSlide();
    //同步场景根节点位置
    var postion = characterBody2D.Position;
    characterBody2D.Position = new Vector2(0, 0);
    this.Sence.Position += postion;
}

在这里插入图片描述

完善输入和添加动画

using Godot;
using GodotNet_LegendOfPaladin.Utils;
using GodotProgram.Interfaces;
using GodotProgram.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static GodotNet_LegendOfPaladin.Utils.GodotProjectSettingHelper;

namespace GodotNet_LegendOfPaladin.SceneModels
{
    public class PlayerSceneModel : ISceneModel
    {

        

        public const float RUN_SPEED = 200;
        public const float JUMP_VELOCITY = -300;

        //枚举类型,防止拼写错误
        public enum AnimationFlame {  idel, running,jump }




        #region IOC注入

        private NlogHelper nlogHelper;
        private GodotProjectSettingHelper godotProjectSettingHelper;
        public PlayerSceneModel(NlogHelper nlogHelper, GodotProjectSettingHelper godotProjectSettingHelper)
        {
            this.nlogHelper = nlogHelper;
            this.godotProjectSettingHelper = godotProjectSettingHelper;
        }
        #endregion


        #region 子节点获取

        private CharacterBody2D characterBody2D;

        private AnimationPlayer animationPlayer;

        private Sprite2D sprite2D;

        public override void Ready()
        {

            nlogHelper.Debug($"当前重力值为:{godotProjectSettingHelper.Gravity}");
            //初始化子节点
            characterBody2D = this.Sence.GetNode<CharacterBody2D>("CharacterBody2D");
            animationPlayer = this.Sence.GetNode<AnimationPlayer>("AnimationPlayer");
            sprite2D = this.Sence.GetNode<Sprite2D>("Sprite2D");
            //播放动画
            animationPlayer.Play(AnimationFlame.idel.ToString());


        }
        #endregion
        public override void Process(double delta)
        {
            //初始化速度
            var velocity = new Vector2();
            //初始化动画节点
            var animation = AnimationFlame.idel;
            var direction = Input.GetAxis(InputMapEnum.move_left.ToString(), InputMapEnum.move_right.ToString());
            var y = godotProjectSettingHelper.Gravity * (float)delta;
            var x = direction * RUN_SPEED;
            var isOnFloor = characterBody2D.IsOnFloor();
            //在C# 中,
            velocity = characterBody2D.Velocity;
            //X是最终速度,所以不需要相加
            velocity.X = x;
            //给角色一个速度,因为重力是加速度,所以角色的速度会不断的增加。
            velocity.Y += y;

            //如果在地上并且按下跳跃,则直接给一个y轴的速度
            if(isOnFloor && Input.IsActionJustPressed(InputMapEnum.jump.ToString()))
            {
                velocity.Y = JUMP_VELOCITY;
            }
            if (isOnFloor)
            {
                if (Mathf.IsZeroApprox(direction))
                {
                    animation = AnimationFlame.idel;
                }
                else
                {
                    animation = AnimationFlame.running;
                }
            }
            else
            {
                animation = AnimationFlame.jump;
            }

            //方向翻转
            if (!Mathf.IsZeroApprox(direction))
            {
                sprite2D.FlipH = direction < 0;
            }

            characterBody2D.Velocity = velocity;
            //让物体以这个速度进行移动
            characterBody2D.MoveAndSlide();
            //同步场景根节点位置
            var postion = characterBody2D.Position;
            characterBody2D.Position = new Vector2(0, 0);
            this.Sence.Position += postion;

            animationPlayer.Play(animation.ToString());
        }

    }
}

在这里插入图片描述

完善跳跃手感

如果玩过超级马里奥或者别的平台跳跃游戏,都知道有一个手感的东西。就是有个跳跃的提前量。我们现在是正好落地的时候按下跳跃才能跳起来,现在我们将跳跃的按钮进行存储,给与一定的缓冲间隔。

/// <summary>
/// 最长跳跃等待时间
/// </summary>
public const int JUMP_WAIT_TIME = 3000;
/// <summary>
/// 初始化的时候让时间往后退一点,防止时间过快
/// </summary>
private DateTime jumpLastTime = DateTime.Now.AddDays(-1);


......
public override void Process(double delta)
{
	......
    if (Input.IsActionJustPressed(InputMapEnum.jump.ToString()))
    {
        jumpLastTime = DateTime.Now;
    }

    if (isOnFloor)
    {
        //如果在地上并且按下跳跃,则直接给一个y轴的速度
		//超时判断
        if (jumpLastTime.AddMilliseconds(JUMP_WAIT_TIME) > DateTime.Now)
        {
        	//如果刚好触发了跳跃,给个速度,将jumpLastTime推前
            velocity.Y = JUMP_VELOCITY;
            jumpLastTime = DateTime.Now.AddDays(-1);
        }
		......
    }
    ......
}

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

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

相关文章

MCGS学习——水位控制

要求 插入一个水罐&#xff0c;液位最大值为37插入一个滑动输入器&#xff0c;用来调节水罐水位&#xff0c;滑动输入器最大调节为液位最大值&#xff0c;并能清楚的显示出液位情况用仪表显示水位变化情况&#xff0c;仪表最大显示设置直观清楚方便读数&#xff0c;主划线为小…

CAJViewer8.1下载地址及安装教程

CAJViewer是中国学术期刊&#xff08;CAJ&#xff09;全文数据库的专用阅读软件。CAJViewer是中国知识资源总库&#xff08;CNKI&#xff09;开发的一款软件&#xff0c;旨在方便用户在线阅读和下载CAJ数据库中的学术论文、期刊和会议论文等文献资源。 CAJViewer具有直观的界面…

Linux系统——Mysql数据库锁的拓展

目录 一、锁的概述 二、锁的分类 1.按锁粒度分类 2.按性能分类 3.按对数据库操作类型 三、全局锁 1.定义 2.操作 3.特点 四、表级锁 1.表级锁分类 2.表锁分类 2.1表共享读锁&#xff08;read lock&#xff09; 2.2表独占写锁&#xff08;write lock&#xff09; …

随便注【强网杯2019】

大佬的完整wp&#xff1a;buuctf-web-[强网杯 2019]随便注-wp_取材于某次真实环境渗透,只说一句话:开发和安全缺一不可-CSDN博客 知识点&#xff1a; 单引号字符型绕过堆叠注入 可以执行多条语句multi_query()&#xff1a;该函数可能引发堆叠注入handler用法 mysql专属&#…

计算机基础系列 —— 虚拟机代码翻译器(2)

I believe that at the end of the century the use of words and general educated opinion will have altered so much that one will be able to speak of machines thinking without expecting to be contradicted. —— Alan Turing 文中提到的所有实现都可以参考&#xf…

【MATLAB源码-第173期】基于matlab的RS编码的2FSK通信系统误码率仿真,通过AWGN信道输出误码率曲线。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 通信系统的基本框架 在现代通信系统中&#xff0c;数据的传输通常涉及四个基本步骤&#xff1a;源编码、信道编码、调制和传输。源编码主要负责压缩数据&#xff0c;减少传输的数据量。信道编码则通过添加冗余信息来提高传输…

【Blockchain】区块链浏览器 | 以太坊Etherscan比特币Blockchain门罗币Monero

区块链浏览器概述 区块链浏览器是一种软件,它使用API(应用程序编程接口)和区块链节点从区块链中提取各种数据&#xff0c;然后使用数据库来排列搜索到的数据&#xff0c;并以可搜索的格式将数据呈现给用户。 用户的输入是资源管理器上的可搜索项&#xff0c;然后通过数据库上…

【力扣hot100】128-最长连续序列、283-移动零

128. 最长连续序列 import java.util.*;public class Test {public static void main(String[] args) {int[] nums {0, 3, 7, 2, 5, 8, 4, 6, 0, 1};int res new Solution().longestConsecutive(nums);System.out.println(res);} }class Solution {public int longestConsecu…

3.31学习总结

算法 解题思路 使用dfs,对蛋糕每层可能的高度和半径进行穷举.通过观察我们可以知道第一层的圆面积是它上面所有蛋糕层的圆面积之和,所以我们只要去求每层的侧面积就行了. 因为题目要求Ri > Ri1且Hi > Hi1,所以我们可以求出每层的最小体积和侧面积,用两个数组分别储存起来…

教你一键轻松领取阿里云优惠券

随着云计算的普及&#xff0c;越来越多的企业和个人开始选择使用云服务。阿里云作为国内领先的云计算服务提供商&#xff0c;以其稳定、高效、安全的服务赢得了广大用户的信赖。为了吸引用户上云&#xff0c;阿里云推出了优惠券活动&#xff0c;本文将教大家如何一键领取阿里云…

【Linux】深入理解进程状态、优先级和调度:Linux 内核中的实现原理探析

文章目录 前言1. 进程状态1.1. 轻量进程排队这件事情——队列1.2. 进程状态的表述及其影响&#xff1a;1.3. 挂起状态及处理&#xff1a;1.4.理解 Linux 内核源代码中的状态表述&#xff1a; 2. 进程优先级Linux 为什么要调整优先级是要受限的&#xff1f; 3. Linux的调度与切换…

Typora下载激活方案

一、下载 1.在typora官网下载最新版本&#xff0c;并安装: 官网地址 2.获取激活工具 感谢Typora激活方法&#xff08;2023年最新版&#xff09; - AI小智的文章 - 知乎 https://zhuanlan.zhihu.com/p/669618741 二、激活 1.把两个.exe文件复制到typora安装目录下 2.在typor…

ubuntu下给不同串口设置别名

目录 一、绑定设备ID 1.查看设备ID 2.编写usev规则 3.重新加载usev规则 4.查看 二、绑定USB端口号 1.先插入一个串口&#xff0c;查看USB设备信息 2.查看USB转串口信息 3.编写usev规则 4.重新加载usev规则 5.查看 在Ubuntu环境下&#xff0c;有时候工控机或者arm开…

推挽输出与开漏输出

推挽输出与开漏输出 文章目录 推挽输出与开漏输出前言一、推挽输出二、开漏输出总结 前言 在使用GPIO口时&#xff0c;会遇到两种配置&#xff0c;一种叫推挽输出&#xff0c;一种叫开漏输出&#xff0c;今天就简聊一聊这两种模式的差异和选择。 一、推挽输出 如图所示&#…

Lazarus远控组件NukeSped分析

静态信息&#xff1a; 样本md5&#xff1a;9b656f5d7e679b94e7b91fc3c4f313e4 由此可见为假的Adobe Flash Player 的攻击样本 样本分析 通过五个函数&#xff0c;内部调用sub_40159D函数动态获取API函数 利用IDA python解密字符串。。 完整python代码 Python> idc.get_…

扫雷(蓝桥杯)

题目描述 小明最近迷上了一款名为《扫雷》的游戏。其中有一个关卡的任务如下&#xff0c; 在一个二维平面上放置着 n 个炸雷&#xff0c;第 i 个炸雷 (xi , yi ,ri) 表示在坐标 (xi , yi) 处存在一个炸雷&#xff0c;它的爆炸范围是以半径为 ri 的一个圆。 为了顺利通过这片土…

Mac air 个人免费版VMWare Fusion安装及配置教程

Mac air 安装免费版VMWare Fusion教程及问题解决 1、下载VMWare Fusion2、下载wins镜像文件3、开始配置4、出现的问题及解决方法4.1 如何跳过启动时的网络连接4.2 启动后&#xff0c;无法连接网络怎么办4.3 怎么实现将文件拖拽到虚拟机中 当你手上是一台Mac电脑&#xff0c;却需…

【博弈论3——二人博弈的纳什均衡】

1.俾斯麦海之战 2. 零和博弈的定义 零和博弈&#xff08;Zero-Sum Game&#xff09;是一种博弈论的基本概念&#xff0c;指的是在博弈过程中&#xff0c;博弈参与者之间的收益和损失之和总是一个常数&#xff0c;特别是总和为零。即博弈一方的收益必然等于另一方的损失&#x…

RCG自条件是如何添加到 Pixel Generator上的?

在自条件的训练过程中&#xff0c;需要将图像经过Pretrained encoder的表征Rep输入进已有的Pixel Generator上&#xff0c;目前RCG是向四种Pixel Generator上加入了自条件&#xff0c;关于它是如何将rep加到Pixel Generator上的&#xff0c;我来总结一下&#xff1a; 一、Pixel…

[SpringCloud] Feign Client 的创建 (一) (四)

文章目录 1.FeignClientsRegistrar2.完成配置注册2.1 registerDefaultConfiguration方法2.2 迭代稳定性2.3 registerFeignClients方法 1.FeignClientsRegistrar FeignClientsRegistrar实现ImportBeanDefinitionRegistrar接口。 2.完成配置注册 public void registerBeanDefinit…