C# và .NET
Đã kiểm chứng
tools/verify_csharp.sh biên dịch wrapper đi kèm bằng chính SDK .NET của Godot rồi chạy nó với addon: C# mở hội thoại kèm tham số, nhận blocking event, rẽ nhánh ngay trong handler, và đọc được trạng thái khoá người chơi. Đã chạy trên Godot 4.7.2 .NET với .NET SDK 8.0.425.
Thứ không làm được
C# không tham chiếu được một class_name của GDScript như một kiểu. Không có kiểu NPCTalkKit trong C#; bạn giữ node đó dưới dạng Node.
Gọi method qua Call(), đọc ghi property qua Get() / Set(), nối signal qua Connect() bằng tên chuỗi. Gõ sai thì tới lúc chạy mới lộ, không phải lúc biên dịch.
Phát một hội thoại
// Docs: /guide/csharp
//
// The addon is GDScript. It loads and runs in a Godot .NET project, but C#
// cannot reference a GDScript class_name as a type: you hold the node as Node
// and go through Call(), Get()/Set() and Connect(). Mistakes surface at run
// time, not at compile time.
//
// Compiled by tools/verify_csharp.sh against the real Godot .NET SDK, so a
// signature that stops existing fails there rather than in your game.
using Godot;
public partial class Blacksmith : Node2D
{
private Node _talk;
public override void _Ready()
{
_talk = GetNode("NPCTalkKit");
ConnectTalk();
}
// #region play
public void Greet(string playerName)
{
var args = new Godot.Collections.Dictionary { { "player_name", playerName } };
_talk.Call("play", "greeting", args);
}
// #endregion
// #region signals
private void ConnectTalk()
{
_talk.Connect("event_blocked",
Callable.From((StringName id, Variant payload) => OnBlocked(id, payload)));
_talk.Connect("request_player_lock",
Callable.From((bool locked) => SetPlayerLocked(locked)));
}
private void OnBlocked(StringName eventId, Variant payload)
{
if (eventId == "offer_job")
_talk.Call("play", PlayerAccepts() ? "accepted" : "declined");
}
// #endregion
// #region wrapper
// A thin wrapper puts the rest of your code back in typed C#. The surface
// is small because v0.4 authors content in the editor, not in code.
private bool IsTalking() => _talk.Call("is_running").AsBool();
private bool IsBlocked() => _talk.Call("is_blocked").AsBool();
private void Resume() => _talk.Call("resume");
// #endregion
private bool PlayerAccepts() => true;
private void SetPlayerLocked(bool locked) { }
}Signal
// Docs: /guide/csharp
//
// The addon is GDScript. It loads and runs in a Godot .NET project, but C#
// cannot reference a GDScript class_name as a type: you hold the node as Node
// and go through Call(), Get()/Set() and Connect(). Mistakes surface at run
// time, not at compile time.
//
// Compiled by tools/verify_csharp.sh against the real Godot .NET SDK, so a
// signature that stops existing fails there rather than in your game.
using Godot;
public partial class Blacksmith : Node2D
{
private Node _talk;
public override void _Ready()
{
_talk = GetNode("NPCTalkKit");
ConnectTalk();
}
// #region play
public void Greet(string playerName)
{
var args = new Godot.Collections.Dictionary { { "player_name", playerName } };
_talk.Call("play", "greeting", args);
}
// #endregion
// #region signals
private void ConnectTalk()
{
_talk.Connect("event_blocked",
Callable.From((StringName id, Variant payload) => OnBlocked(id, payload)));
_talk.Connect("request_player_lock",
Callable.From((bool locked) => SetPlayerLocked(locked)));
}
private void OnBlocked(StringName eventId, Variant payload)
{
if (eventId == "offer_job")
_talk.Call("play", PlayerAccepts() ? "accepted" : "declined");
}
// #endregion
// #region wrapper
// A thin wrapper puts the rest of your code back in typed C#. The surface
// is small because v0.4 authors content in the editor, not in code.
private bool IsTalking() => _talk.Call("is_running").AsBool();
private bool IsBlocked() => _talk.Call("is_blocked").AsBool();
private void Resume() => _talk.Call("resume");
// #endregion
private bool PlayerAccepts() => true;
private void SetPlayerLocked(bool locked) { }
}Addon có kèm sẵn một wrapper
addons/npc_talkkit/csharp/TalkKit.cs là lớp bọc có kiểu cho những lời gọi ở trên:
_talk = TalkKit.Of(this);
_talk.EventBlocked += OnBlocked;
_talk.Play("greeting", TalkKit.Params("player_name", "Alex"));Tự chạy kiểm tra bằng:
tools/verify_csharp.sh /path/to/Godot_mono.app/Contents/MacOS/GodotNó biên dịch wrapper trong một project rỗng rồi dùng nó điều khiển addon, nên chữ ký nào không còn tồn tại sẽ hỏng ở đó chứ không hỏng trong game của bạn.
File .cs trong bản Godot standard không có importer nào nhận nên bị bỏ qua. Điều này đã được kiểm bằng cách import project trên 4.7.2 standard với file đó: không lỗi, không cảnh báo.
Hoặc tự viết
// Docs: /guide/csharp
//
// The addon is GDScript. It loads and runs in a Godot .NET project, but C#
// cannot reference a GDScript class_name as a type: you hold the node as Node
// and go through Call(), Get()/Set() and Connect(). Mistakes surface at run
// time, not at compile time.
//
// Compiled by tools/verify_csharp.sh against the real Godot .NET SDK, so a
// signature that stops existing fails there rather than in your game.
using Godot;
public partial class Blacksmith : Node2D
{
private Node _talk;
public override void _Ready()
{
_talk = GetNode("NPCTalkKit");
ConnectTalk();
}
// #region play
public void Greet(string playerName)
{
var args = new Godot.Collections.Dictionary { { "player_name", playerName } };
_talk.Call("play", "greeting", args);
}
// #endregion
// #region signals
private void ConnectTalk()
{
_talk.Connect("event_blocked",
Callable.From((StringName id, Variant payload) => OnBlocked(id, payload)));
_talk.Connect("request_player_lock",
Callable.From((bool locked) => SetPlayerLocked(locked)));
}
private void OnBlocked(StringName eventId, Variant payload)
{
if (eventId == "offer_job")
_talk.Call("play", PlayerAccepts() ? "accepted" : "declined");
}
// #endregion
// #region wrapper
// A thin wrapper puts the rest of your code back in typed C#. The surface
// is small because v0.4 authors content in the editor, not in code.
private bool IsTalking() => _talk.Call("is_running").AsBool();
private bool IsBlocked() => _talk.Call("is_blocked").AsBool();
private void Resume() => _talk.Call("resume");
// #endregion
private bool PlayerAccepts() => true;
private void SetPlayerLocked(bool locked) { }
}Bề mặt mà C# phải chạm rất nhỏ, vì v0.4 soạn nội dung trong editor chứ không trong code. Với phần lớn game thì chỉ là play() và hai signal.
Thứ không bị ảnh hưởng
Mọi thứ soạn trong workspace TalkKit — hội thoại, style, anchor, loại hộp — chạy y hệt. Không cái nào đi qua C# cả.