从此
📄文章 #️⃣专题 🌐酷站 👨‍💻技术 📺 📱

Godot C#/Mono 语法制作游戏

Godot核心 | Godot官方文档 | Godot源码 | Godot Shaders 着色器 | 产品发布检查清单 | 游戏开发专题

核心

SDK:
  Godot SDK文件路径 - GodotSharp\Tools\nupkgs\Godot.NET.Sdk.4.3.0.nupkg

Godot NuGet离线包创建和使用:
  mkdir ~/new-project/
  cd ~/new-project/
  rm -r ~/new-project/*

  目标框架 https://learn.microsoft.com/zh-cn/dotnet/standard/frameworks
  -f指定的必须是本机已安装.NET SDK版本,手动改*.csproj则不需要安装。

  dotnet new classlib --no-restore  等更换为Godot SDK后再还原。
  Godot项目SDK需要手动更换:
    <Project Sdk="Godot.NET.Sdk/4.3.0">
    <PropertyGroup>
      <TargetFramework>net6.0</TargetFramework>
      <RestoreAdditionalProjectSources>$(MSBuildThisFileDirectory)/../temp/local-nuget/; D:\main\person\projects\temp\local-nuget</RestoreAdditionalProjectSources>
    ...

  [测试下] dotnet build
  构建命令(取自Godot IDE选项卡MSBuild构建选项):
  dotnet build -c Debug -p:GodotTargetPlatform=windows

  code -n  ~/new-project/

  说明 - *.nupkg本质是zip文件,且字节码dll不区分x64等平台: 
    GodotSharp.4.3.0.nupkg\lib\net6.0\GodotSharp.dll

  dotnet pack -c Release
  dotnet nuget push ".godot\mono\temp\bin\Release\x.1.0.0.nupkg" --api-key YOUR_GITHUB_PAT --source "https://nuget.pkg.github.com/iwangxiaodong/index.json"
  默认为Private,可在 Package settings -> Change package visibility 手动改为 Public,注意 Public 只是指能对外看到,但拉取时还需要指定 --api-key 。

  mkdir ~/nuget-fallback/  或 mkdir D:\main\person\projects\temp\local-nuget
  cp ~/new-project\bin\Release\new-project.1.0.0.nupkg  ~/local-nuget/
  或 cp .godot\mono\temp\bin\Release\x.1.0.0.nupkg  D:\main\person\projects\temp\local-nuget
  ls ~/local-nuget/

  x.csproj 指定本地源目录用 RestoreAdditionalProjectSources,见上方配置。

  测试拉取包 dotnet add package new-project
    <ItemGroup><PackageReference Include="x-y-z" Version="1.0.0" /></ItemGroup>
    重新打包后必须删除 C:\Users\person\.nuget\packages\godot-project-csharp-library\ 方生效,[可选删]C:\Users\person\AppData\Local\Temp\MetadataAsSource\;后续研究如何自动处理。
  
  【可选】在项目目录或任意上级目录定义个 nuget.config。
    说明 - 可填入私有包验证信息;配置更改后应删除.godot目录使其生效? 支持包文件格式:{id-lowercase}.{version-lowercase}.nupkg
<?xml version="1.0" encoding="utf-8"?> <configuration> <packageSources> <add key="local" value="D:\main\person\projects\temp\local-nuget" /> </packageSources> </configuration>
[其他项目使用] dotnet nuget list source 注意 - 添加时不能使用~/写法 [未测] <RestoreAdditionalProjectFallbackFolders>$(MSBuildThisFileDirectory)/../temp/nuget-fallback/; D:\main\person\projects\temp\nuget-fallback</RestoreAdditionalProjectFallbackFolders> 回退目录环境变量(无默认值) - NUGET_FALLBACK_PACKAGES 或 dotnet nuget add source -n local-nuget-fallback D:\main\person\projects\temp\nuget-fallback dotnet nuget remove source 'local-nuget-fallback'

IO

JSON:
        var v = Json.ParseString(rs); //var j = new Json(); j.Parse("{}"); GD.Print(j.Data);
        // 不支持C#的as转换 - v as Godot.Collections.Dictionary;
        var dict = v.As<Godot.Collections.Dictionary>(); GD.Print(dict);
        if (dict != null && dict.ContainsKey("msg"))
        { GD.Print(dict["msg"].As<string>()); }

读取图片文件: Image.LoadFromFile(@"D:\temp\x.jpg")

信号

信号:
  IsConnected无法判断“+=”方式连接的信号,但Connect(...)方式的则判定正确。
  C# nameof(method)效果等同Godot MethodName.method
  信号增加尾参:注意 - 只有GDScript才有bind方法!
	var t:Timer=get_node("Timer"); #t.one_shot=true;
	t.timeout.connect(Callable(self,"m").bind(123)); t.start()
  解决C#的Lambda中无法使用-=取消连接信号的语法限制:
    首选 IsConnected判断方式避免重复连接;
    或  ap.AnimationFinished += (sn) =>
	{  var list = ap.GetSignalConnectionList(AnimationPlayer.SignalName.AnimationFinished);
	   foreach (var dict in list) // signal、callable、flags
	   { ap.Disconnect(AnimationPlayer.SignalName.AnimationFinished, (Callable)dict["callable"]); }
	}; ap.Play("Idle");
        或 一次性连接信号 - ap.Connect(AnimationPlayer.SignalName.AnimationFinished, new Callable(this, MethodName.xxx), (uint)ConnectFlags.OneShot);

其他

按权重随机(刷装备等) - https://docs.godotengine.org/zh-cn/4.x/classes/class_randomnumbergenerator.html#class-randomnumbergenerator-method-rand-weighted

登记到下一帧回调首行,执行后自动断开(与call_deferred组成1头1尾的执行时机):
  get_tree().process_frame.connect(callable, CONNECT_ONE_SHOT)

树形场景:
  节点可以直接new出来并add;或通过持有id来获取 GodotObject.InstanceFromId(id)
    首选GD.Load方法;次选比GD.Load多一个CacheMode.Reuse参数的ResourceLoader.Load; 
    异步载入用ResourceLoader.LoadThreadedRequest("res://s.tscn"); 
      轮询后取用 if (ResourceLoader.LoadThreadedGetStatus("res://s.tscn") == ThreadLoadStatus.Loaded){ ResourceLoader.LoadThreadedGet("res://s.tscn"); }
    调用Load返回预载入(首次缓存)PackedScene,ps.Dispose()或ps.Free()释放缓存;执行ps.Instantiate()后则返回可使用的node了。

节点分类:
  单独使用 - Button、MeshInstance3D等
  组合使用 - CharacterBody3D需要子节点CollisionShape3D来提供碰撞,需要MeshInstance3D等提供可见性,后两者为同级;
    因MeshInstance3D无IsOnFloor()碰撞判断,且只提供可见性,故无法单独与CollisionShape3D搭配使用,可换用RigidBody3D。