godot 学习 Csharp戈多 凝雨 2025-03-15 2025-02-07 声明 Export (PropertyHint) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 [Exprot ] public int age{set ;get ;}[Export(PropertyHint.Range,"0,100,1" ) ] [Export(PropertyHint.Enum,"A,B" ) ] [Export(PropertyHint.EnumSuggestion ,"A,B" ) ] [Export(PropertyHint.ExpEasing,"positive_only" ) ] [Export(PropertyHint.Link) ] [Export(PropertyHint.Flags,"A:16,B,C" ) ] [GlobalClass ] [Icon("icon.svg" ) ]
File 路径
Dir dir
GlobalFile 觉得路径
ResourceType 资源路径
MultilineText 大文本编辑器
Expression 字符是Expression
枚举值
描述
None
该属性没有编辑器提示。
Range
表示一个整数或浮点数属性应该在指定的范围内,通过提示字符串 “min,max” 或 “min,max,step” 来指定。还可以包括 “or_greater” 和/或 “or_less”。
Enum
表示一个整数或字符串属性是一个枚举值,可以从提示字符串指定的列表中选择。
EnumSuggestion
类似于 Enum,但是属性仍然接受任意值并且可以为空。值列表用于建议可能的取值。
ExpEasing
表示一个单精度浮点数属性应该通过指数缓动函数进行编辑。可以包括 “attenuation” 和 “positive_only”。
Link
表示一个向量属性应该允许其组件进行链接。
Flags
表示一个整数属性是一个具有命名位标志的位掩码。
Layers2DRender
表示一个整数属性是使用命名的 2D 渲染层的位掩码。
Layers2DPhysics
表示一个整数属性是使用命名的 2D 物理层的位掩码。
Layers2DNavigation
表示一个整数属性是使用命名的 2D 导航层的位掩码。
Layers3DRender
表示一个整数属性是使用命名的 3D 渲染层的位掩码。
Layers3DPhysics
表示一个整数属性是使用命名的 3D 物理层的位掩码。
Layers3DNavigation
表示一个整数属性是使用命名的 3D 导航层的位掩码。
LayersAvoidance
表示一个整数属性是使用命名的避免层的位掩码。
File
表示一个字符串属性是一个文件路径。编辑时会弹出文件对话框。
Dir
表示一个字符串属性是一个目录路径。编辑时会弹出目录对话框。
GlobalFile
类似于 File,但用于全局路径。
GlobalDir
类似于 Dir,但用于全局路径。
SaveFile
类似于 File,但用于保存文件的对话框。
GlobalSaveFile
类似于 SaveFile,但用于全局路径。
ResourceType
表示一个属性是衍生自 Godot.Resource 的类型的实例。
MultilineText
表示一个字符串属性是带有换行的文本。
Expression
表示一个字符串属性是一个 Godot.Expression。
PlaceholderText
表示一个字符串属性的输入字段为空时应显示占位文本。
ColorNoAlpha
表示一个 Godot.Color 属性应该在不影响其透明度的情况下进行编辑。
ObjectId
表示属性的值是作为对象 ID 编码的对象,其类型在提示字符串中指定。
TypeString
表示属性是字符串时,表明该属性代表特定的类型(类)。
NodePathToEditedNode, ObjectTooBig, NodePathValidTypes, IntIsObjectid, IntIsPointer, ArrayType, LocaleId, LocalizableString, NodeType, HideQuaternionEdit, Password
这些已经废弃或将在未来删除的提示,具体功能不再使用。
Max
表示 PropertyHint 枚举的大小(通常用于内部使用,不是用于实际属性提示)。
请注意,”Max” 通常不是用于实际属性编辑提示的枚举值,而是可能用于枚举内部的计数或边界检查。在实际使用中,你不会将 “Max” 分配给任何属性的 PropertyHint。
信号
1 2 3 4 5 6 [Signal ] public delegate void 我的信号EventHandler(); EmitSignal(nameof (我的信号)); 我的信号+=函数
1 2 3 4 5 6 7 8 9 public delegate 委托函数(int age); public event 委托函数 信号; 信号.Invoke(); 信号+=函数; public void 函数(int age);
计时器
1 2 3 4 5 6 7 8 public async Task SomeFunction () { GD.Print("开始计时" ); await ToSignal(GetTree().CreateTimer(5.0f ,true ), SceneTreeTimer.SignalName.Timeout); GD.Print("计时结束" ); }
1 System.Threading.Thread.Sleep(5000 );
1 2 3 4 5 6 7 8 9 10 11 12 13 static async Task<string > FetchDataFromWebAsync (){ Console.WriteLine($"FetchDataFromWebAsync: 启动web请求..." ); var response = await client.GetAsync("https://example.com" ); var content = await response.Content.ReadAsStringAsync(); Console.WriteLine($"FetchDataFromWebAsync: Web请求完成." ); return content; }
1 2 3 4 5 6 7 System.Timers.Timer countdownTimer; int countdownSeconds = 1 ; countdownTimer=new System.Timers.Timer(1000 ); countdownTimer.Elapsed+=函数; countdownTimer.AutoReset = true ; countdownTimer.Enabled = true ;
读写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public static Variant 读取数据(string paths){ FileAccess dile= FileAccess.Open(paths,FileAccess.ModeFlags.Read); if (FileAccess.GetOpenError()==Error.Ok) { GD.Print("打开成功" ); }else { GD.Print("打开失败" ); return false ; } string 解析=dile.GetAsText(); Variant? 转换=Json.ParseString(解析); if (转换==null ) { GD.Print("解析数据失败" ); return false ; } dile.Close(); return (Variant)转换; }
写入
1 2 3 4 5 6 7 8 public static bool 写入数据(string paths,Variant datas){ FileAccess dile= FileAccess.Open(paths,FileAccess.ModeFlags.Write); string 转换=Json.Stringify(datas); dile.StoreString(转换); dile.Close(); return true ; }
CS-API实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public void duqu (){ using (var workbook = new XLWorkbook("./Json/xxxc.xlsx" )) { var worksheet = workbook.Worksheet(1 ); var dataTable = new System.Data.DataTable(); foreach (var row in worksheet.RowsUsed()) { if (row.RowNumber() == 1 ) { foreach (var cell in row.CellsUsed()) { dataTable.Columns.Add(cell.Value.ToString()); } } else { var dataRow = dataTable.NewRow(); foreach (var cell in row.CellsUsed()) { dataRow[cell.Address.ColumnNumber - 1 ] = cell.Value.ToString(); } dataTable.Rows.Add(dataRow); } } string json = JsonConvert.SerializeObject(dataTable, Formatting.Indented); File.WriteAllText("./Json/output.json" , json); GD.Print(json); } }
设计模式 对象池
泛型对象池
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 public partial class OBjiectPool <T > : Node where T : Node , new (){ private List<T> _pool; private int _maxSize; public OBjiectPool (int maxSize = 100 ) { _pool = new List<T>(); _maxSize = maxSize; } public T GetObject () { if (_pool.Count > 0 ) { int lastIndex = _pool.Count - 1 ; T obj = _pool[lastIndex]; _pool.RemoveAt(lastIndex); return obj; } else if (_pool.Count < _maxSize) { return new T(); } else { throw new InvalidOperationException("Object pool is full" ); } } public void RetuenObject (T obj ) { _pool.Add(obj); } public void ClearPool () { _pool.Clear(); } }
数组对象池
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public class ObjectPond { public interface Pood { void InitPond () ; } public Enemy[] enemies { get ; set ; } = new Enemy[10 ]; public void Init (Godot.Collections.Dictionary EnemyValuerArray ) { enemies[0 ] = ActivityObject.CreateObject(EnemyValuerArray) as Enemy; enemies[1 ] = ActivityObject.CreateObject(EnemyValuerArray) as Enemy; enemies[2 ] = ActivityObject.CreateObject(EnemyValuerArray) as Enemy; enemies[3 ] = ActivityObject.CreateObject(EnemyValuerArray) as Enemy; enemies[4 ] = ActivityObject.CreateObject(EnemyValuerArray) as Enemy; enemies[5 ] = ActivityObject.CreateObject(EnemyValuerArray) as Enemy; enemies[6 ] = ActivityObject.CreateObject(EnemyValuerArray) as Enemy; enemies[7 ] = ActivityObject.CreateObject(EnemyValuerArray) as Enemy; enemies[8 ] = ActivityObject.CreateObject(EnemyValuerArray) as Enemy; enemies[9 ] = ActivityObject.CreateObject(EnemyValuerArray) as Enemy; } public Enemy CreaObject () { for (int i = 0 ; i < enemies.Length; i++) { if (enemies[i] != null ) { Enemy enemy = enemies[i]; enemies[i] = null ; enemy.InitPond(); return enemy; } } return null ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public void Ret (Enemy enemy ){ for (int i = 0 ; i < enemies.Length; i++) { if (enemies[i] == null && enemy is Enemy) { enemies[i] = enemy; return ; } } } public bool isObject (){ foreach (Enemy i in enemies) { if (i != null ) { return true ; } } return false ; }
方法 算法 随机 1 2 3 4 float x = PlayerP1.Position.X + GD.Randf() * 600 - 10 ;float y = PlayerP1.Position.Y + GD.Randf() * 600 - 10 ;Vector2 pos = new Vector2(x, y);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public Vector3 Get_Vector (Vector3 center, float range ) { Vector3 random_offset =new Vector3( GetRandomFloat(-range, range), GetRandomFloat(-range, range), GetRandomFloat(-range, range) ); return center + random_offset; } static float GetRandomFloat (float min, float max ) { Random random = new Random(); return (float )(random.NextDouble() * (max - min) + min); }
追踪
1 2 3 4 5 public Vector3 Get_Vleor (NodeA,NodeB ){ Vector3 dir=(NodeA-NodeB).初始化向量(); return dir; }
模板 怪物根节点
怪物的根节点,通过怪物对象生成怪物,他只负责生成,不负责怪物逻辑,他存储了玩家对象,供怪物对象调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 using Godot;using System;public partial class EnemyFactory : Node3D { [Export ] public CharacterBody3D EnemyBody { get ; set ; } [Export ] public Vector3 GenerateLocation { get ; set ;} [Export ] public float GenerateScope { get ; set ; } Node3D EnemyRoot { get ; set ; } [Export ] public int MaxEnemyCount { get ; set ; } public Timer timer { get ; set ; }=new Timer(); [Export ] public float time { get ; set ; }=1 ; public override void _EnterTree() { } public override void _Ready() { timer.WaitTime = time; timer.Autostart = true ; timer.Timeout += On_Timer_timeout; AddChild(timer); EnemyRoot = new Node3D(); EnemyRoot.Name="EnemyRoot" ; AddChild(EnemyRoot); } public override void _Process(double delta) { } private void On_Timer_timeout () { if (EnemyRoot.GetChildren().Count >= 50 ) { return ; } if (EnemyBody != null ) { CharacterBody3D EnemyBody1 = (CharacterBody3D)EnemyBody.Duplicate(); EnemyBody1.Position = Get_Vector(GenerateLocation, GenerateScope); EnemyRoot.AddChild(EnemyBody1); } } public Vector3 Get_Vector (Vector3 center, float range ) { Vector3 random_offset =new Vector3( GetRandomFloat(-range, range), GetRandomFloat(-range, range), GetRandomFloat(-range, range) ); return center + random_offset; } static float GetRandomFloat (float min, float max ) { Random random = new Random(); return (float )(random.NextDouble() * (max - min) + min); } }
怪物 快速制作一个怪物追踪玩家的怪物对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 using Godot;[Tool ] public partial class Enemy : CharacterBody3D { [Export ] public PackedScene EnemyTscn { get ; set ; } [Export ] public Mesh mesh { get ; set ; } [Export ] public CollisionShape3D shape3D { get ; set ; } [Export ] public Shape3D shape { get ; set ; } [Export ] public Node3D Player { get ; set ; } public const float Speed = 5.0f ; public const float JumpVelocity = 4.5f ; public override void _EnterTree() { shape3D = new CollisionShape3D(); if (shape3D != null ) { shape3D.Shape=shape; AddChild(shape3D); } if (mesh != null ) { MeshInstance3D EnemyMesh =new MeshInstance3D(); EnemyMesh.Mesh = mesh; AddChild(EnemyMesh); } } public override void _PhysicsProcess(double delta) { Vector3 velocity = Velocity; if (!IsOnFloor()) { velocity += GetGravity() * (float )delta; } if (Player != null ) { Vector3 dir = (Player.GlobalPosition - GlobalPosition).Normalized(); velocity = dir * (float )delta; } Velocity = velocity; MoveAndSlide(); } }
武器
自动瞄准武器,通过获取怪物对象来调整方向并向怪物发射子弹对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 using Godot;using Godot.Collections;public partial class SelfFiringWeapon : Node3D { [Export ] public CharacterBody3D Bullet { get ; set ; } [Export ] public MeshInstance3D Weapon { get ; set ; } [Export ] public Marker3D BulletLocation { get ; set ; } [Export ] public Marker3D BulletDirection { get ; set ; } [Export ] public EnemyFactory EnemyRoot { get ; set ; } [Export ] public Timer Timer { get ; set ; } public override void _Ready() { Timer.Timeout += ON_TimerTimeout; } public override void _Process(double delta) { CharacterBody3D enemy = GetNearestEnemy(this ); if (enemy != null ) { LookAt(enemy.GlobalPosition); GD.Print("正在跟踪敌人" ); } } public void ON_TimerTimeout () { CharacterBody3D weapon = Bullet.Duplicate() as CharacterBody3D; GetParent().AddChild(weapon); weapon.GlobalPosition = BulletLocation.GlobalPosition; Vector3 Dir=(BulletDirection.GlobalPosition-BulletLocation.GlobalPosition).Normalized(); weapon.Velocity=Dir * 10f ; GD.Print("发射子弹" ); } public CharacterBody3D GetNearestEnemy (Node3D player ) { if (EnemyRoot != null && EnemyRoot.EnemyRoot == null ) { return null ; } Array<Node> enemiesArray = EnemyRoot.EnemyRoot.GetChildren(); Enemy nearestEnemy = null ; float nearestDistance = float .PositiveInfinity; foreach (Enemy enemy in enemiesArray) { if (enemy is Enemy) { float distance = player.GlobalPosition.DistanceTo(enemy.GlobalPosition); if (distance < nearestDistance) { nearestDistance = distance; nearestEnemy = (Enemy)enemy; } } } return nearestEnemy; } }