Unified management scenario instantiation, loot generation in the right place, and allow to specify the probability and quantity.

统一管理场景实例化,战利品生成在正确的位置,且允许指定概率和数量了。
This commit is contained in:
Cold-Mint 2024-06-10 21:05:18 +08:00
parent f7ad69440c
commit 63a56cbb2c
Signed by: Cold-Mint
GPG Key ID: C5A9BF8A98E0CE99
26 changed files with 272 additions and 90 deletions

View File

@ -22,4 +22,7 @@ player_spawn_debug,玩家{0}生成在{1}。,"Player {0} spawned at {1}.",プレ
player_packed_scene_not_exist,玩家预制场景不存在。,Player packed scene does not exist.,プレイヤーのパックされたシーンが存在しません。
exit_the_room_debug,节点{0}退出房间{1}。,"Node {0} exits room {1}.",ノード{0}が部屋{1}を退出します。
enter_the_room_debug,节点{0}进入房间{1}。,"Node {0} enters room {1}.",ノード{0}が部屋{1}に入ります。
death_info,生物{0}被{1}击败。,"Creature {0} was defeated by {1}.",生物{0}が{1}によって打ち負かされました。
death_info,生物{0}被{1}击败。,"Creature {0} was defeated by {1}.",生物{0}が{1}によって打ち負かされました。
loot_list_has_no_entries,ID为{0}的战利品表,没有指定条目。,"Loot list with ID {0}, no entry specified.",ID{0}の戦利品テーブルは、エントリ指定されていません。
not_within_the_loot_spawn_range,给定的数值{0}没有在战利品{1}的生成范围{2}内。,The given value {0} is not within the spawn range {2} of loot {1}.,与えられた数値{0}は戦利品{1}の生成範囲{2}内にありません。
loot_data_quantity,有{0}个战利品数据被返回。,{0} loot data was returned.,{0}個の戦利品データが返されます。
1 id zh en ja
22 player_packed_scene_not_exist 玩家预制场景不存在。 Player packed scene does not exist. プレイヤーのパックされたシーンが存在しません。
23 exit_the_room_debug 节点{0}退出房间{1}。 Node {0} exits room {1}. ノード{0}が部屋{1}を退出します。
24 enter_the_room_debug 节点{0}进入房间{1}。 Node {0} enters room {1}. ノード{0}が部屋{1}に入ります。
25 death_info 生物{0}被{1}击败。 Creature {0} was defeated by {1}. 生物{0}が{1}によって打ち負かされました。
26 loot_list_has_no_entries ID为{0}的战利品表,没有指定条目。 Loot list with ID {0}, no entry specified. ID{0}の戦利品テーブルは、エントリ指定されていません。
27 not_within_the_loot_spawn_range 给定的数值{0}没有在战利品{1}的生成范围{2}内。 The given value {0} is not within the spawn range {2} of loot {1}. 与えられた数値{0}は戦利品{1}の生成範囲{2}内にありません。
28 loot_data_quantity 有{0}个战利品数据被返回。 {0} loot data was returned. {0}個の戦利品データが返されます。

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,8 +1,7 @@
id,zh,en,ja
slogan_0,游戏属于每一个人。,The game belongs to everyone.,ゲームはすべての人のものです。
slogan_1,如果你想要得到爱,你就播种爱。,"If you want love, you sow love.",愛を手に入れたければ、愛の種をまきます。
slogan_2,快乐?伤心?痛苦?,Happy? Sad? Pain?,楽しいですか?悲しいですか?苦しいですか?
slogan_3,Kawaii!,Kawaii!,Kawaii!
slogan_4,魔法是想象的世界。,Magic is an imaginary world.,魔法は想像の世界です。
slogan_5,也试试Minecraft,Also try Minecraft!,Minecraftもやってみて
slogan_6,也试试Terraria,Also try Terraria!,Terrariaもやってみて
slogan_1,Kawaii!,Kawaii!,Kawaii!
slogan_2,魔法是想象的世界。,Magic is an imaginary world.,魔法は想像の世界です。
slogan_3,也试试《Minecraft》,Also try 'Minecraft'!,「Minecraft」もやってみて
slogan_4,也试试《Terraria》,Also try 'Terraria'!,「Terraria」もやってみて
slogan_5,你好,Hello,こんにちは
1 id zh en ja
2 slogan_0 游戏属于每一个人。 The game belongs to everyone. ゲームはすべての人のものです。
3 slogan_1 如果你想要得到爱,你就播种爱。 Kawaii! If you want love, you sow love. Kawaii! 愛を手に入れたければ、愛の種をまきます。 Kawaii!
4 slogan_2 快乐?伤心?痛苦? 魔法是想象的世界。 Happy? Sad? Pain? Magic is an imaginary world. 楽しいですか?悲しいですか?苦しいですか? 魔法は想像の世界です。
5 slogan_3 Kawaii! 也试试《Minecraft》! Kawaii! Also try 'Minecraft'! Kawaii! 「Minecraft」もやってみて!
6 slogan_4 魔法是想象的世界。 也试试《Terraria》! Magic is an imaginary world. Also try 'Terraria'! 魔法は想像の世界です。 「Terraria」もやってみて!
7 slogan_5 也试试Minecraft! 你好 Also try Minecraft! Hello Minecraftもやってみて! こんにちは
slogan_6 也试试Terraria! Also try Terraria! Terrariaもやってみて!

Binary file not shown.

Binary file not shown.

View File

@ -34,6 +34,7 @@ script = ExtResource("1_ubaid")
metadata/CampId = "Mazoku"
metadata/MaxHp = 50
metadata/Name = "死灵法师"
metadata/LootListId = "Test"
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
position = Vector2(0, 4)

View File

@ -45,6 +45,3 @@ position = Vector2(65, 0)
position = Vector2(26, -8)
target_position = Vector2(0, 20)
collision_mask = 34
[node name="RayCast2D2" type="RayCast2D" parent="."]
target_position = Vector2(0, -29)

View File

@ -147,7 +147,6 @@ hotbar_previous={
[internationalization]
locale/translations=PackedStringArray("res://locals/DeathInfo.en.translation", "res://locals/DeathInfo.ja.translation", "res://locals/DeathInfo.zh.translation", "res://locals/InputMapping.en.translation", "res://locals/InputMapping.ja.translation", "res://locals/InputMapping.zh.translation", "res://locals/Log.en.translation", "res://locals/Log.ja.translation", "res://locals/Log.zh.translation", "res://locals/Slogan.en.translation", "res://locals/Slogan.ja.translation", "res://locals/Slogan.zh.translation", "res://locals/UI.en.translation", "res://locals/UI.ja.translation", "res://locals/UI.zh.translation", "res://locals/Weapon.en.translation", "res://locals/Weapon.ja.translation", "res://locals/Weapon.zh.translation")
locale/test="ja"
[layer_names]

View File

@ -18,7 +18,20 @@ public static class Config
/// <para>巡逻</para>
/// <para>Patrol</para>
/// </summary>
public const string? Patrol = "Patrol";
public const string Patrol = "Patrol";
}
/// <summary>
/// <para>Loot table ID</para>
/// <para>战利品表ID</para>
/// </summary>
public static class LootListId
{
/// <summary>
/// <para>A trophy table for testing</para>
/// <para>测试用的战利品表</para>
/// </summary>
public const string Test = "Test";
}
/// <summary>

View File

@ -112,6 +112,7 @@ public partial class CharacterTemplate : CharacterBody2D
public string CampId = null!;
private DamageNumberNodeSpawn? _damageNumber;
/// <summary>
/// <para>Character referenced loot table</para>
/// <para>角色引用的战利品表</para>
@ -212,13 +213,14 @@ public partial class CharacterTemplate : CharacterBody2D
CharacterName = GetMeta("Name", Name).AsString();
CampId = GetMeta("CampId", Config.CampId.Default).AsString();
MaxHp = GetMeta("MaxHp", Config.DefaultMaxHp).AsInt32();
string lootListId = GetMeta("LootListId", string.Empty).AsString();
var lootListId = GetMeta("LootListId", string.Empty).AsString();
if (!string.IsNullOrEmpty(lootListId))
{
//If a loot table is specified, get the loot table.
//如果指定了战利品表,那么获取战利品表。
_lootList = LootListManager.GetLootList(lootListId);
}
if (MaxHp <= 0)
{
//If Max blood volume is 0 or less, set Max blood volume to 10
@ -450,6 +452,7 @@ public partial class CharacterTemplate : CharacterBody2D
//角色死亡
OnDie(damageTemplate);
ThrowAllItemOnDie();
return true;
}
@ -457,6 +460,42 @@ public partial class CharacterTemplate : CharacterBody2D
return true;
}
/// <summary>
/// <para>Create Loot Object</para>
/// <para>创建战利品对象</para>
/// </summary>
protected void CreateLootObject()
{
if (_lootList == null)
{
return;
}
var lootDataArray = _lootList.GenerateLootData();
if (lootDataArray.Length == 0)
{
return;
}
var finalGlobalPosition = GlobalPosition;
CallDeferred("GenerateLootObjects", this,lootDataArray, finalGlobalPosition);
}
/// <summary>
/// <para>GenerateLootObjects</para>
/// <para>生成战利品对象</para>
/// </summary>
/// <param name="parentNode"></param>
/// <param name="lootDataArray"></param>
/// <param name="position"></param>
public void GenerateLootObjects(Node parentNode,
LootData[] lootDataArray,
Vector2 position)
{
LootListManager.GenerateLootObjects(parentNode, lootDataArray, position);
}
/// <summary>
/// <para>Add power to the character</para>
/// <para>在角色身上添加力</para>
@ -493,6 +532,7 @@ public partial class CharacterTemplate : CharacterBody2D
}
}
CreateLootObject();
QueueFree();
return Task.CompletedTask;
}

View File

@ -396,28 +396,29 @@ public partial class Player : CharacterTemplate
//If there is a scene of floating text, then we generate floating text.
//如果有悬浮文本的场景,那么我们生成悬浮文本。
_floatLabel?.QueueFree();
_floatLabel = (Control)_floatLabelPackedScene.Instantiate();
var rotationDegreesNode2D = node2D.RotationDegrees;
var rotationDegreesNode2DAbs = Math.Abs(rotationDegreesNode2D);
_floatLabel.Position = rotationDegreesNode2DAbs > 90
? new Vector2(0, PromptTextDistance)
: new Vector2(0, -PromptTextDistance);
_floatLabel.RotationDegrees = 0 - rotationDegreesNode2D;
var label = _floatLabel.GetNode<Label>("Label");
if (node is WeaponTemplate weapon)
_floatLabel = NodeUtils.InstantiatePackedScene<Control>(_floatLabelPackedScene, node);
if (_floatLabel != null)
{
var stringBuilder = new StringBuilder();
if (weapon.Owner is CharacterTemplate characterTemplate)
var rotationDegreesNode2D = node2D.RotationDegrees;
var rotationDegreesNode2DAbs = Math.Abs(rotationDegreesNode2D);
_floatLabel.Position = rotationDegreesNode2DAbs > 90
? new Vector2(0, PromptTextDistance)
: new Vector2(0, -PromptTextDistance);
_floatLabel.RotationDegrees = 0 - rotationDegreesNode2D;
var label = _floatLabel.GetNode<Label>("Label");
if (node is WeaponTemplate weapon)
{
stringBuilder.Append(characterTemplate.ReadOnlyCharacterName);
stringBuilder.Append(TranslationServerUtils.Translate("de"));
var stringBuilder = new StringBuilder();
if (weapon.Owner is CharacterTemplate characterTemplate)
{
stringBuilder.Append(characterTemplate.ReadOnlyCharacterName);
stringBuilder.Append(TranslationServerUtils.Translate("de"));
}
stringBuilder.Append(TranslationServerUtils.Translate(weapon.Name));
label.Text = stringBuilder.ToString();
}
stringBuilder.Append(TranslationServerUtils.Translate(weapon.Name));
label.Text = stringBuilder.ToString();
}
node.AddChild(_floatLabel);
}
UpdateOperationTip();

View File

@ -1,3 +1,4 @@
using ColdMint.scripts.utils;
using Godot;
namespace ColdMint.scripts.damage;
@ -99,7 +100,8 @@ public partial class DamageNumberNodeSpawn : Marker2D
return;
}
if (_damageNumberPackedScene.Instantiate() is not DamageNumber damageNumber)
var damageNumber = NodeUtils.InstantiatePackedScene<DamageNumber>(_damageNumberPackedScene);
if (damageNumber == null)
{
return;
}

View File

@ -339,6 +339,7 @@ public partial class HotBar : HBoxContainer, IItemContainer
{
return false;
}
return itemSlotNode.SetItem(item);
}
@ -396,7 +397,7 @@ public partial class HotBar : HBoxContainer, IItemContainer
return;
}
var itemSlotNode = _itemSlotPackedScene.Instantiate() as ItemSlotNode;
var itemSlotNode = NodeUtils.InstantiatePackedScene<ItemSlotNode>(_itemSlotPackedScene);
if (itemSlotNode == null)
{
return;

View File

@ -1,6 +1,8 @@
namespace ColdMint.scripts.inventory;
using Godot;
public class LootData
namespace ColdMint.scripts.inventory;
public partial class LootData : GodotObject
{
public string? ResPath { get; set; }
public int Quantity { get; set; }

View File

@ -1,4 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using ColdMint.scripts.debug;
using Godot;
namespace ColdMint.scripts.inventory;
@ -14,7 +16,7 @@ public class LootList
/// <para>战利品表的Id</para>
/// </summary>
public string? Id { get; set; }
private List<LootEntry>? _lootEntrieList;
/// <summary>
@ -31,19 +33,20 @@ public class LootList
_lootEntrieList.Add(lootEntry);
}
/// <summary>
/// <para>GenerateLootData</para>
/// <para>生成战利品数据</para>
/// </summary>
/// <returns></returns>
public List<LootData> GenerateLootData()
public LootData[] GenerateLootData()
{
var lootDataList = new List<LootData>();
if (_lootEntrieList == null)
{
return lootDataList;
LogCat.LogWithFormat("loot_list_has_no_entries", Id);
return lootDataList.ToArray();
}
foreach (var lootEntry in _lootEntrieList)
@ -53,8 +56,10 @@ public class LootList
{
//If the random number is greater than the generation probability, skip the current loop.
//如果随机数大于生成概率,则跳过当前循环。
LogCat.LogWithFormat("not_within_the_loot_spawn_range", chance, lootEntry.ResPath, lootEntry.Chance);
continue;
}
//We generate a loot data for each loot entry.
//我们为每个战利品条目生成一个战利品数据。
var quantity = GD.RandRange(lootEntry.MinQuantity, lootEntry.MaxQuantity);
@ -66,9 +71,10 @@ public class LootList
lootDataList.Add(lootData);
}
return lootDataList;
LogCat.LogWithFormat("loot_data_quantity", lootDataList.Count);
return lootDataList.ToArray();
}
/// <summary>
/// <para>Remove loot entry</para>

View File

@ -1,4 +1,6 @@
using System.Collections.Generic;
using ColdMint.scripts.debug;
using ColdMint.scripts.utils;
using Godot;
namespace ColdMint.scripts.inventory;
@ -16,10 +18,15 @@ public static class LootListManager
/// <para>Register loot table</para>
/// <para>注册战利品表</para>
/// </summary>
/// <param name="id"></param>
/// <param name="lootList"></param>
public static bool RegisterLootList(string id, LootList lootList)
public static bool RegisterLootList(LootList lootList)
{
var id = lootList.Id;
if (id == null)
{
return false;
}
if (_lootListDictionary != null) return _lootListDictionary.TryAdd(id, lootList);
_lootListDictionary = new Dictionary<string, LootList> { { id, lootList } };
return true;
@ -40,25 +47,19 @@ public static class LootListManager
/// <para>Generate loot objects</para>
/// <para>生成战利品对象</para>
/// </summary>
/// <param name="lootDataArray">
///<para>lootDataArray</para>
///<para>战利品数组</para>
/// </param>
/// <param name="parentNode">
///<para>parentNode</para>
///<para>父节点</para>
/// </param>
public static void GenerateLootObjects(LootData[] lootDataArray, Node parentNode)
public static void GenerateLootObjects(Node parentNode, LootData[] lootDataArray, Vector2 position)
{
if (lootDataArray.Length == 0)
{
return;
}
//Cache the loaded PackedScene object.
//缓存已加载的PackedScene对象。
Dictionary<string, PackedScene> packedSceneDictionary = new();
foreach (var lootData in lootDataArray)
{
if (string.IsNullOrEmpty(lootData.ResPath))
if (string.IsNullOrEmpty(lootData.ResPath) || lootData.Quantity == 0)
{
continue;
}
@ -69,24 +70,34 @@ public static class LootListManager
packedSceneDictionary.TryAdd(lootData.ResPath, packedScene);
}
CreateLootObject(packedScene, parentNode);
for (var i = 0; i < lootData.Quantity; i++)
{
//Generate as many loot instance objects as there are loot.
//有多少个战利品就生成多少个战利品实例对象。
CreateLootInstanceObject(parentNode, packedScene, position);
}
}
}
private static void CreateLootObject(PackedScene? packedScene, Node parent)
/// <summary>
/// <para>Create a loot instance object</para>
/// <para>创建战利品实例对象</para>
/// </summary>
private static void CreateLootInstanceObject(Node parent, PackedScene? packedScene, Vector2 position)
{
if (packedScene == null)
{
return;
}
var lootObject = packedScene.Instantiate();
if (lootObject is not Node2D node2D)
var lootObject = NodeUtils.InstantiatePackedScene<Node2D>(packedScene, parent);
if (lootObject == null)
{
return;
}
parent.AddChild(node2D);
lootObject.Position = position;
}
/// <summary>

View File

@ -19,7 +19,7 @@ public partial class LevelGraphEditorLoader : UiLoaderTemplate
{
private string? _defaultRoomName;
private readonly LevelGraphEditorBinding _nodeBinding = new LevelGraphEditorBinding();
private readonly LevelGraphEditorBinding _nodeBinding = new();
/// <summary>
/// <para>Index of the room</para>
@ -35,7 +35,7 @@ public partial class LevelGraphEditorLoader : UiLoaderTemplate
private PackedScene? _roomNodeScene;
private readonly List<Node> _selectedNodes = new List<Node>();
private readonly List<Node> _selectedNodes = new();
/// <summary>
/// <para>Displays the time to enter the suggestion</para>
@ -47,7 +47,7 @@ public partial class LevelGraphEditorLoader : UiLoaderTemplate
/// <para>Offset to append when a new node is created.</para>
/// <para>创建新节点时追加的偏移量。</para>
/// </summary>
private Vector2 _positionOffset = new Vector2(100, 100);
private Vector2 _positionOffset = new(100, 100);
/// <summary>
/// <para>Is the press event of an active button saved?</para>
@ -99,7 +99,7 @@ public partial class LevelGraphEditorLoader : UiLoaderTemplate
return null;
}
var node = _roomNodeScene.Instantiate();
var node = NodeUtils.InstantiatePackedScene<Node>(_roomNodeScene);
if (node == null)
{
return null;

View File

@ -4,6 +4,7 @@ using System.Text;
using ColdMint.scripts.camp;
using ColdMint.scripts.deathInfo;
using ColdMint.scripts.debug;
using ColdMint.scripts.inventory;
using ColdMint.scripts.map;
using ColdMint.scripts.map.roomInjectionProcessor;
using Godot;
@ -39,6 +40,23 @@ public partial class MainMenuLoader : UiLoaderTemplate
LogCat.MinLogLevel = LogCat.DisableAllLogLevel;
}
//注册测试使用的战利品表
if (Config.IsDebug())
{
var testLootList = new LootList
{
Id = Config.LootListId.Test
};
var lootEntry = new LootEntry
{
Chance = 1f,
MaxQuantity = 5,
MinQuantity = 1,
ResPath = "res://prefab/weapons/staffOfTheUndead.tscn"
};
testLootList.AddLootEntry(lootEntry);
LootListManager.RegisterLootList(testLootList);
}
DeathInfoGenerator.RegisterDeathInfoHandler(new SelfDeathInfoHandler());
MapGenerator.RegisterRoomInjectionProcessor(new ChanceRoomInjectionProcessor());
MapGenerator.RegisterRoomInjectionProcessor(new TimeIntervalRoomInjectorProcessor());

View File

@ -1,6 +1,7 @@
using ColdMint.scripts.character;
using ColdMint.scripts.debug;
using ColdMint.scripts.map.events;
using ColdMint.scripts.utils;
using Godot;
namespace ColdMint.scripts.map;
@ -32,18 +33,16 @@ public partial class AiCharacterSpawn : Marker2D
/// <param name="aiCharacterGenerateEvent"></param>
public void OnAiCharacterGenerateEvent(AiCharacterGenerateEvent aiCharacterGenerateEvent)
{
var node = _packedScene?.Instantiate();
if (node is not AiCharacter aiCharacter)
if (_packedScene == null)
{
return;
}
if (GameSceneNodeHolder.AiCharacterContainer == null)
var aiCharacter = NodeUtils.InstantiatePackedScene<AiCharacter>(_packedScene,GameSceneNodeHolder.AiCharacterContainer);
if (aiCharacter == null)
{
return;
}
GameSceneNodeHolder.AiCharacterContainer.AddChild(aiCharacter);
aiCharacter.Position = GlobalPosition;
}

View File

@ -1,6 +1,7 @@
using ColdMint.scripts.character;
using ColdMint.scripts.debug;
using ColdMint.scripts.map.events;
using ColdMint.scripts.utils;
using Godot;
namespace ColdMint.scripts.map;
@ -30,6 +31,7 @@ public partial class PlayerSpawn : Marker2D
GameSceneNodeHolder.Player.Revive(GameSceneNodeHolder.Player.MaxHp);
return;
}
SpawnPlayer();
}
@ -50,17 +52,16 @@ public partial class PlayerSpawn : Marker2D
return;
}
var playerNode = _playerPackedScene.Instantiate();
if (playerNode is not Player player)
var playerNode =
NodeUtils.InstantiatePackedScene<Player>(_playerPackedScene, GameSceneNodeHolder.PlayerContainer);
if (playerNode == null)
{
return;
}
player.ItemContainer = GameSceneNodeHolder.HotBar;
GameSceneNodeHolder.PlayerContainer.AddChild(player);
GameSceneNodeHolder.Player = player;
player.Position = GlobalPosition;
LogCat.LogWithFormat("player_spawn_debug", player.ReadOnlyCharacterName, player.Position);
playerNode.ItemContainer = GameSceneNodeHolder.HotBar;
GameSceneNodeHolder.Player = playerNode;
playerNode.Position = GlobalPosition;
LogCat.LogWithFormat("player_spawn_debug", playerNode.ReadOnlyCharacterName, playerNode.Position);
}
private void MapGenerationCompleteEvent(MapGenerationCompleteEvent mapGenerationCompleteEvent)

View File

@ -104,9 +104,14 @@ public class Room
/// <param name="packedScene"></param>
private void AnalyzeRoomData(PackedScene? packedScene)
{
var node = packedScene?.Instantiate();
if (node is not Node2D node2D)
if (packedScene == null)
{
return;
}
var node2D = NodeUtils.InstantiatePackedScene<Node2D>(packedScene);
if (node2D == null)
{
//The room node is not of Node2D type. An exception is thrown
//房间节点不是Node2D类型抛出异常
LogCat.LogError("room_root_node_must_be_node2d");
return;

View File

@ -1,5 +1,8 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using ColdMint.scripts.debug;
using ColdMint.scripts.weapon;
using Godot;
namespace ColdMint.scripts.utils;
@ -95,4 +98,85 @@ public static class NodeUtils
return closestNode;
}
/// <summary>
/// <para>Find the corresponding container node based on the child node</para>
/// <para>根据子节点查找对应的容器节点</para>
/// </summary>
/// <remarks>
///<para>We want child nodes to be placed under a specific parent node to facilitate the same management. For example, the weapon node should be placed inside the Weapon container node. We call a parent node of the same type as a child node a "container". This method is used to find the corresponding node container based on the type of the child node.</para>
///<para>我们希望子节点被放置在特定的父节点下,方便同一管理。例如:武器节点应该被放置在“武器容器”节点内。我们将子节点的类型相同的父节点叫做“容器”。此方法用于根据子节点的类型查找对应的节点容器。</para>
/// </remarks>
/// <param name="childNode">
///<para>childNode</para>
///<para>子节点</para>
/// </param>
/// <param name="defaultParentNode">
///<para>Default parent, which returns the default node if it cannot be matched by type.</para>
///<para>默认父节点,当按照类型无法匹配时,将返回默认节点。</para>
/// </param>
/// <returns></returns>
public static Node FindContainerNode(Node childNode, Node defaultParentNode)
{
if (GameSceneNodeHolder.WeaponContainer != null && childNode is WeaponTemplate)
{
return GameSceneNodeHolder.WeaponContainer;
}
return defaultParentNode;
}
/// <summary>
/// <para>Instantiate Packed Scene</para>
/// <para>实例化场景</para>
/// </summary>
/// <remarks>
///<para>This method is recommended in place of all packedScene.Instantiate() calls within a project, using it to instantiate a scene, optionally assigned to a container that matches the type of the root node.</para>
///<para>推荐使用此方法代替项目内所有的packedScene.Instantiate()调用,使用此方法实例化场景,可选择将其分配到与根节点类型相匹配的容器内。</para>
/// </remarks>
/// <param name="packedScene">
///<para>packedScene</para>
///<para>打包的场景</para>
/// </param>
/// <param name="assignedByRootNodeType">
///<para>Enabled by default, whether to place a node into a container node that matches the type of the root node after it is instantiated. If the assignment fails by type, it is placed under the default parent node.</para>
///<para>默认启用,实例化节点后,是否将其放置到与根节点类型相匹配的容器节点内。如果按照类型分配失败,则放置在默认父节点下。</para>
/// </param>
/// <param name="defaultParentNode">
/// <para>Default parent. If null is passed, the instantiated node is not put into the parent. You need to place it manually, which is quite useful for delaying setting the parent node.</para>
/// <para>默认父节点如果传入null则不会将实例化后的节点放入父节点内。您需要手动放置这对于延迟设置父节点相当有用。</para>
/// </param>
/// <returns></returns>
public static T? InstantiatePackedScene<T>(PackedScene packedScene, Node? defaultParentNode = null,
bool assignedByRootNodeType = true) where T : Node
{
try
{
var instantiateNode = packedScene.Instantiate<T>();
//An attempt is made to place an instantiated node under a husband node only after the default parent node is set.
//只有设定了默认父节点后才会尝试将实例化的节点放置到夫节点下。
if (defaultParentNode != null)
{
if (assignedByRootNodeType)
{
var containerNode = FindContainerNode(instantiateNode, defaultParentNode);
containerNode.AddChild(instantiateNode);
}
else
{
//If you do not need to assign by type, place it under the default parent node.
//如果不需要按照类型分配,那么将其放到默认父节点下。
defaultParentNode.AddChild(instantiateNode);
}
}
return instantiateNode;
}
catch (InvalidCastException e)
{
//null is returned if the type conversion fails.
//在类型转换失败时返回null。
LogCat.WhenCaughtException(e);
return null;
}
}
}

View File

@ -1,6 +1,7 @@
using System.Collections.Generic;
using ColdMint.scripts.debug;
using ColdMint.scripts.projectile;
using ColdMint.scripts.utils;
using Godot;
namespace ColdMint.scripts.weapon;
@ -20,12 +21,13 @@ public partial class ProjectileWeapon : WeaponTemplate
/// <para>抛射体的生成位置</para>
/// </summary>
private Marker2D? _marker2D;
/// <summary>
/// <para>List of projectiles</para>
/// <para>抛射体列表</para>
/// </summary>
private string[]? _projectiles;
private Dictionary<string, PackedScene>? _projectileCache;
private Node2D? _projectileContainer;
@ -42,6 +44,7 @@ public partial class ProjectileWeapon : WeaponTemplate
{
continue;
}
_projectileCache.Add(projectileItem, packedScene);
}
@ -56,23 +59,20 @@ public partial class ProjectileWeapon : WeaponTemplate
{
return;
}
if (_projectiles.IsEmpty())
{
LogCat.LogError("projectiles_is_empty");
return;
}
//Get the first projectile
//获取第一个抛射体
var projectileScene = _projectileCache[_projectiles[0]];
var projectile = projectileScene.Instantiate() as ProjectileTemplate;
if (projectile != null)
{
projectile.Owner = owner;
projectile.Velocity = (enemyGlobalPosition - _marker2D.GlobalPosition).Normalized() * projectile.Speed;
projectile.Position = _marker2D.GlobalPosition;
}
_projectileContainer.AddChild(projectile);
var projectile = NodeUtils.InstantiatePackedScene<ProjectileTemplate>(projectileScene, _projectileContainer);
if (projectile == null) return;
projectile.Owner = owner;
projectile.Velocity = (enemyGlobalPosition - _marker2D.GlobalPosition).Normalized() * projectile.Speed;
projectile.Position = _marker2D.GlobalPosition;
}
}