From 951ba49c79eddb403a12ad79839aac10caae34d0 Mon Sep 17 00:00:00 2001 From: huade <491009149@qq.com> Date: Thu, 17 Sep 2026 14:03:34 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A0=B9=E6=8D=AE=E6=8F=90=E4=BE=9B=E7=9A=84co?= =?UTF-8?q?de=20differences=E4=BF=A1=E6=81=AF=EF=BC=8C=E7=94=B1=E4=BA=8E?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E5=85=B7=E4=BD=93=E7=9A=84=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=8F=98=E6=9B=B4=E5=86=85=E5=AE=B9=EF=BC=8C=E6=88=91=E5=B0=86?= =?UTF-8?q?=E7=94=9F=E6=88=90=E4=B8=80=E4=B8=AA=E9=80=9A=E7=94=A8=E7=9A=84?= =?UTF-8?q?commit=20message=E6=A8=A1=E6=9D=BF=EF=BC=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` docs(changelog): 更新项目变更日志 - 添加新的版本记录 - 修正已知问题描述 - 更新功能特性说明 ``` 注意:由于您提供的code differences部分为空,以上是一个示例模板。实际使用时请根据具体的代码变更内容来填写相应的type、scope、subject和body信息。 --- Assets/Prefabs/主角/召唤师主角.prefab | 100 ++++- Assets/Prefabs/第一章/人类士兵.prefab | 21 +- Assets/Prefabs/第一章/弓箭手.prefab | 21 +- Assets/Prefabs/第一章/法师.prefab | 21 +- Assets/Prefabs/第一章/盾卫.prefab | 21 +- Assets/Prefabs/第一章/精锐士兵.prefab | 21 +- Assets/Prefabs/第二章/弓箭猎人.prefab | 21 +- Assets/Prefabs/第二章/狼人.prefab | 28 +- Assets/Prefabs/第二章/石巨人.prefab | 28 +- Assets/Prefabs/第二章/蓝色怪物.prefab | 28 +- Assets/Scenes/Chapter1/SummonerDuelTest.unity | Bin 137684 -> 175160 bytes Assets/Script/AISummoner.cs | 141 +++++++ Assets/Script/AISummoner.cs.meta | 11 + Assets/Script/Barracks.cs | 13 + Assets/Script/BattleSetupManager.cs | 7 + Assets/Script/GameManager.cs | 37 ++ Assets/Script/GameManager.cs.meta | 11 + Assets/Script/MainCharacterCombat.cs | 96 ++++- Assets/Script/Projectile.cs | 13 +- Assets/Script/UnitBase.cs | 124 ++++-- Assets/Script/UnitTeam.cs | 47 +++ Assets/Script/UnitTeam.cs.meta | 11 + Assets/Script/WorldHealthBar.cs | 13 +- CODELY.md | 6 + 召唤之战 - 核心玩法拆家版.md | 171 ++++++++ 小游戏可行性方案.md | 386 ------------------ 26 files changed, 942 insertions(+), 455 deletions(-) create mode 100644 Assets/Script/AISummoner.cs create mode 100644 Assets/Script/AISummoner.cs.meta create mode 100644 Assets/Script/GameManager.cs create mode 100644 Assets/Script/GameManager.cs.meta create mode 100644 Assets/Script/UnitTeam.cs create mode 100644 Assets/Script/UnitTeam.cs.meta create mode 100644 召唤之战 - 核心玩法拆家版.md delete mode 100644 小游戏可行性方案.md diff --git a/Assets/Prefabs/主角/召唤师主角.prefab b/Assets/Prefabs/主角/召唤师主角.prefab index e9f70dc..13b34d8 100644 --- a/Assets/Prefabs/主角/召唤师主角.prefab +++ b/Assets/Prefabs/主角/召唤师主角.prefab @@ -43,17 +43,17 @@ PrefabInstance: - target: {fileID: -8679921383154817045, guid: 6b859b567d8ba4a7a87c03aa4b87c18c, type: 3} propertyPath: m_LocalPosition.x - value: -0.8 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: 6b859b567d8ba4a7a87c03aa4b87c18c, - type: 3} - propertyPath: m_LocalPosition.y value: 0 objectReference: {fileID: 0} + - target: {fileID: -8679921383154817045, guid: 6b859b567d8ba4a7a87c03aa4b87c18c, + type: 3} + propertyPath: m_LocalPosition.y + value: 0.05 + objectReference: {fileID: 0} - target: {fileID: -8679921383154817045, guid: 6b859b567d8ba4a7a87c03aa4b87c18c, type: 3} propertyPath: m_LocalPosition.z - value: 0.9 + value: -20 objectReference: {fileID: 0} - target: {fileID: -8679921383154817045, guid: 6b859b567d8ba4a7a87c03aa4b87c18c, type: 3} @@ -98,7 +98,7 @@ PrefabInstance: - target: {fileID: 919132149155446097, guid: 6b859b567d8ba4a7a87c03aa4b87c18c, type: 3} propertyPath: m_TagString - value: Player + value: Friend objectReference: {fileID: 0} m_RemovedComponents: [] m_RemovedGameObjects: [] @@ -120,6 +120,22 @@ PrefabInstance: type: 3} insertIndex: -1 addedObject: {fileID: 6516315820976732023} + - targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 6b859b567d8ba4a7a87c03aa4b87c18c, + type: 3} + insertIndex: -1 + addedObject: {fileID: 6843810197477671806} + - targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 6b859b567d8ba4a7a87c03aa4b87c18c, + type: 3} + insertIndex: -1 + addedObject: {fileID: 6944470424331181836} + - targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 6b859b567d8ba4a7a87c03aa4b87c18c, + type: 3} + insertIndex: -1 + addedObject: {fileID: 6811651768908473498} + - targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 6b859b567d8ba4a7a87c03aa4b87c18c, + type: 3} + insertIndex: -1 + addedObject: {fileID: 3317273023278999503} m_SourcePrefab: {fileID: 100100000, guid: 6b859b567d8ba4a7a87c03aa4b87c18c, type: 3} --- !u!1 &106035464483675767 stripped GameObject: @@ -212,6 +228,76 @@ MonoBehaviour: damageNumberRiseHeight: 1.2 damageNumberSpawnHeight: 1.5 damageNumberSize: 0.09 +--- !u!114 &6843810197477671806 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 106035464483675767} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e6f40b66b50871440a97763c44d480a4, type: 3} + m_Name: + m_EditorClassIdentifier: + teamId: 0 +--- !u!114 &6944470424331181836 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 106035464483675767} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 096beceeef30a2646a85a4203af3cd27, type: 3} + m_Name: + m_EditorClassIdentifier: + minX: -9 + maxX: 9 + minZ: -19 + maxZ: 6 + moveSpeed: 5 + stopDistance: 0.2 + groundMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &6811651768908473498 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 106035464483675767} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e6f40b66b50871440a97763c44d480a4, type: 3} + m_Name: + m_EditorClassIdentifier: + teamId: 0 +--- !u!136 &3317273023278999503 +CapsuleCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 106035464483675767} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Height: 2 + m_Direction: 1 + m_Center: {x: 0, y: 1, z: 0} --- !u!4 &1444656758206389486 stripped Transform: m_CorrespondingSourceObject: {fileID: 1852576806548013000, guid: 6b859b567d8ba4a7a87c03aa4b87c18c, diff --git a/Assets/Prefabs/第一章/人类士兵.prefab b/Assets/Prefabs/第一章/人类士兵.prefab index 35daaca..98e6dfa 100644 --- a/Assets/Prefabs/第一章/人类士兵.prefab +++ b/Assets/Prefabs/第一章/人类士兵.prefab @@ -148,6 +148,10 @@ PrefabInstance: type: 3} insertIndex: -1 addedObject: {fileID: 2921261421214297749} + - targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 5072af719b9b1401787c6c55f048d8ed, + type: 3} + insertIndex: -1 + addedObject: {fileID: 7706731248061608703} m_SourcePrefab: {fileID: 100100000, guid: 5072af719b9b1401787c6c55f048d8ed, type: 3} --- !u!1 &7945271050725079514 stripped GameObject: @@ -211,6 +215,7 @@ MonoBehaviour: separationRadius: 1.5 separationWeight: 3 targetSpreadBias: 6 + summonerMode: 0 target: {fileID: 0} --- !u!114 &2920089167710187078 MonoBehaviour: @@ -227,7 +232,7 @@ MonoBehaviour: maxHealth: 220 currentHealth: 220 maxEnergy: 100 - currentEnergy: 0 + _currentEnergy: 0 heightOffset: 0.25 barWidth: 1.2 borderColor: {r: 0.03, g: 0.03, b: 0.03, a: 0.95} @@ -238,7 +243,6 @@ MonoBehaviour: damageNumberRiseHeight: 1.2 damageNumberSpawnHeight: 1.5 damageNumberSize: 0.09 - damageNumberOutlineWidth: 0.015 --- !u!114 &4111165692813349771 MonoBehaviour: m_ObjectHideFlags: 0 @@ -290,6 +294,19 @@ NavMeshAgent: m_BaseOffset: 0 m_WalkableMask: 4294967295 m_ObstacleAvoidanceType: 4 +--- !u!114 &7706731248061608703 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7945271050725079514} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e6f40b66b50871440a97763c44d480a4, type: 3} + m_Name: + m_EditorClassIdentifier: + teamId: 0 --- !u!4 &8878706989061864259 stripped Transform: m_CorrespondingSourceObject: {fileID: 1852576806548013000, guid: 5072af719b9b1401787c6c55f048d8ed, diff --git a/Assets/Prefabs/第一章/弓箭手.prefab b/Assets/Prefabs/第一章/弓箭手.prefab index 7b4391a..c56ae9f 100644 --- a/Assets/Prefabs/第一章/弓箭手.prefab +++ b/Assets/Prefabs/第一章/弓箭手.prefab @@ -143,6 +143,10 @@ PrefabInstance: type: 3} insertIndex: -1 addedObject: {fileID: 196735238731846736} + - targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 7b0dd5b38a9cd144ea135f3743440958, + type: 3} + insertIndex: -1 + addedObject: {fileID: 7984530604249028412} m_SourcePrefab: {fileID: 100100000, guid: 7b0dd5b38a9cd144ea135f3743440958, type: 3} --- !u!4 &2222026796505693691 stripped Transform: @@ -229,6 +233,7 @@ MonoBehaviour: separationRadius: 1.5 separationWeight: 3 targetSpreadBias: 6 + summonerMode: 0 target: {fileID: 0} --- !u!114 &7720102017902192686 MonoBehaviour: @@ -245,7 +250,7 @@ MonoBehaviour: maxHealth: 150 currentHealth: 150 maxEnergy: 100 - currentEnergy: 0 + _currentEnergy: 0 heightOffset: 0.25 barWidth: 1.2 borderColor: {r: 0.03, g: 0.03, b: 0.03, a: 0.95} @@ -256,7 +261,6 @@ MonoBehaviour: damageNumberRiseHeight: 1.2 damageNumberSpawnHeight: 1.5 damageNumberSize: 0.09 - damageNumberOutlineWidth: 0.015 --- !u!114 &1449822714228276666 MonoBehaviour: m_ObjectHideFlags: 0 @@ -294,6 +298,19 @@ NavMeshAgent: m_BaseOffset: 0 m_WalkableMask: 4294967295 m_ObstacleAvoidanceType: 4 +--- !u!114 &7984530604249028412 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5200308054099318121} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e6f40b66b50871440a97763c44d480a4, type: 3} + m_Name: + m_EditorClassIdentifier: + teamId: 0 --- !u!1001 &6707986314085581781 PrefabInstance: m_ObjectHideFlags: 0 diff --git a/Assets/Prefabs/第一章/法师.prefab b/Assets/Prefabs/第一章/法师.prefab index f37ec83..085d7fd 100644 --- a/Assets/Prefabs/第一章/法师.prefab +++ b/Assets/Prefabs/第一章/法师.prefab @@ -144,6 +144,10 @@ PrefabInstance: type: 3} insertIndex: -1 addedObject: {fileID: 20042258299677061} + - targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 9d4b54ec2c4f7754bb1fd5ec11a29541, + type: 3} + insertIndex: -1 + addedObject: {fileID: 7388673629374319643} m_SourcePrefab: {fileID: 100100000, guid: 9d4b54ec2c4f7754bb1fd5ec11a29541, type: 3} --- !u!1 &7062617501994129594 stripped GameObject: @@ -223,6 +227,7 @@ MonoBehaviour: separationRadius: 1.5 separationWeight: 3 targetSpreadBias: 6 + summonerMode: 0 target: {fileID: 0} --- !u!114 &990449840913200210 MonoBehaviour: @@ -239,7 +244,7 @@ MonoBehaviour: maxHealth: 135 currentHealth: 135 maxEnergy: 100 - currentEnergy: 0 + _currentEnergy: 0 heightOffset: 0.25 barWidth: 1.2 borderColor: {r: 0.03, g: 0.03, b: 0.03, a: 0.95} @@ -250,7 +255,6 @@ MonoBehaviour: damageNumberRiseHeight: 1.2 damageNumberSpawnHeight: 1.5 damageNumberSize: 0.09 - damageNumberOutlineWidth: 0.015 --- !u!114 &3887798621029042906 MonoBehaviour: m_ObjectHideFlags: 0 @@ -287,6 +291,19 @@ NavMeshAgent: m_BaseOffset: 0 m_WalkableMask: 4294967295 m_ObstacleAvoidanceType: 4 +--- !u!114 &7388673629374319643 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7062617501994129594} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e6f40b66b50871440a97763c44d480a4, type: 3} + m_Name: + m_EditorClassIdentifier: + teamId: 0 --- !u!4 &8608542904823568931 stripped Transform: m_CorrespondingSourceObject: {fileID: 1852576806548013000, guid: 9d4b54ec2c4f7754bb1fd5ec11a29541, diff --git a/Assets/Prefabs/第一章/盾卫.prefab b/Assets/Prefabs/第一章/盾卫.prefab index 0404ad1..74351d8 100644 --- a/Assets/Prefabs/第一章/盾卫.prefab +++ b/Assets/Prefabs/第一章/盾卫.prefab @@ -199,6 +199,10 @@ PrefabInstance: type: 3} insertIndex: -1 addedObject: {fileID: 4069710806908923055} + - targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 17f7a1b7ae92f0948a5bebde8a11fc2b, + type: 3} + insertIndex: -1 + addedObject: {fileID: 4719436641987099519} m_SourcePrefab: {fileID: 100100000, guid: 17f7a1b7ae92f0948a5bebde8a11fc2b, type: 3} --- !u!4 &309400887928525066 stripped Transform: @@ -283,6 +287,7 @@ MonoBehaviour: separationRadius: 1.5 separationWeight: 3 targetSpreadBias: 6 + summonerMode: 0 target: {fileID: 0} --- !u!114 &6884400399417614731 MonoBehaviour: @@ -299,7 +304,7 @@ MonoBehaviour: maxHealth: 480 currentHealth: 480 maxEnergy: 100 - currentEnergy: 0 + _currentEnergy: 0 heightOffset: 0.25 barWidth: 1.35 borderColor: {r: 0.03, g: 0.03, b: 0.03, a: 0.95} @@ -310,7 +315,6 @@ MonoBehaviour: damageNumberRiseHeight: 1.2 damageNumberSpawnHeight: 1.5 damageNumberSize: 0.09 - damageNumberOutlineWidth: 0.015 --- !u!114 &8203338886952913679 MonoBehaviour: m_ObjectHideFlags: 0 @@ -350,6 +354,19 @@ NavMeshAgent: m_BaseOffset: 0 m_WalkableMask: 4294967295 m_ObstacleAvoidanceType: 4 +--- !u!114 &4719436641987099519 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1242979668690031507} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e6f40b66b50871440a97763c44d480a4, type: 3} + m_Name: + m_EditorClassIdentifier: + teamId: 0 --- !u!4 &5170975053921319681 stripped Transform: m_CorrespondingSourceObject: {fileID: 6502182839619065283, guid: 17f7a1b7ae92f0948a5bebde8a11fc2b, diff --git a/Assets/Prefabs/第一章/精锐士兵.prefab b/Assets/Prefabs/第一章/精锐士兵.prefab index ebab685..9dbdd45 100644 --- a/Assets/Prefabs/第一章/精锐士兵.prefab +++ b/Assets/Prefabs/第一章/精锐士兵.prefab @@ -185,6 +185,10 @@ PrefabInstance: type: 3} insertIndex: -1 addedObject: {fileID: 8773165921290396666} + - targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 62d9f853c067ff443bd2a3aa382ab563, + type: 3} + insertIndex: -1 + addedObject: {fileID: 897006334484502727} m_SourcePrefab: {fileID: 100100000, guid: 62d9f853c067ff443bd2a3aa382ab563, type: 3} --- !u!4 &2585761489654059889 stripped Transform: @@ -270,6 +274,7 @@ MonoBehaviour: separationRadius: 1.5 separationWeight: 3 targetSpreadBias: 6 + summonerMode: 0 target: {fileID: 0} --- !u!114 &3773753810590757696 MonoBehaviour: @@ -286,7 +291,7 @@ MonoBehaviour: maxHealth: 650 currentHealth: 650 maxEnergy: 100 - currentEnergy: 0 + _currentEnergy: 0 heightOffset: 0.25 barWidth: 1.2 borderColor: {r: 0.03, g: 0.03, b: 0.03, a: 0.95} @@ -297,7 +302,6 @@ MonoBehaviour: damageNumberRiseHeight: 1.2 damageNumberSpawnHeight: 1.5 damageNumberSize: 0.09 - damageNumberOutlineWidth: 0.015 --- !u!114 &4116279677098275843 MonoBehaviour: m_ObjectHideFlags: 0 @@ -335,3 +339,16 @@ NavMeshAgent: m_BaseOffset: 0 m_WalkableMask: 4294967295 m_ObstacleAvoidanceType: 4 +--- !u!114 &897006334484502727 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3933539425749946856} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e6f40b66b50871440a97763c44d480a4, type: 3} + m_Name: + m_EditorClassIdentifier: + teamId: 0 diff --git a/Assets/Prefabs/第二章/弓箭猎人.prefab b/Assets/Prefabs/第二章/弓箭猎人.prefab index 6747dc2..98a8834 100644 --- a/Assets/Prefabs/第二章/弓箭猎人.prefab +++ b/Assets/Prefabs/第二章/弓箭猎人.prefab @@ -251,6 +251,10 @@ PrefabInstance: type: 3} insertIndex: -1 addedObject: {fileID: 7158251723525050232} + - targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 2ac38f68ec4f85341b423b5e8defd47e, + type: 3} + insertIndex: -1 + addedObject: {fileID: 6065625683664793215} m_SourcePrefab: {fileID: 100100000, guid: 2ac38f68ec4f85341b423b5e8defd47e, type: 3} --- !u!1 &4059575966999087028 stripped GameObject: @@ -299,7 +303,7 @@ MonoBehaviour: maxHealth: 100 currentHealth: 100 maxEnergy: 100 - currentEnergy: 0 + _currentEnergy: 0 heightOffset: 0.25 barWidth: 1.2 borderColor: {r: 0.03, g: 0.03, b: 0.03, a: 0.95} @@ -310,7 +314,6 @@ MonoBehaviour: damageNumberRiseHeight: 1.2 damageNumberSpawnHeight: 1.5 damageNumberSize: 0.09 - damageNumberOutlineWidth: 0.015 --- !u!114 &6486457417548983634 MonoBehaviour: m_ObjectHideFlags: 0 @@ -342,6 +345,7 @@ MonoBehaviour: separationRadius: 1.5 separationWeight: 3 targetSpreadBias: 6 + summonerMode: 0 target: {fileID: 0} --- !u!114 &4858463350953901710 MonoBehaviour: @@ -394,6 +398,19 @@ NavMeshAgent: m_BaseOffset: 0 m_WalkableMask: 4294967295 m_ObstacleAvoidanceType: 4 +--- !u!114 &6065625683664793215 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4059575966999087028} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e6f40b66b50871440a97763c44d480a4, type: 3} + m_Name: + m_EditorClassIdentifier: + teamId: 0 --- !u!4 &7974625701785382694 stripped Transform: m_CorrespondingSourceObject: {fileID: 6502182839619065283, guid: 2ac38f68ec4f85341b423b5e8defd47e, diff --git a/Assets/Prefabs/第二章/狼人.prefab b/Assets/Prefabs/第二章/狼人.prefab index 6a1b1ec..cc08d51 100644 --- a/Assets/Prefabs/第二章/狼人.prefab +++ b/Assets/Prefabs/第二章/狼人.prefab @@ -103,6 +103,10 @@ PrefabInstance: type: 3} insertIndex: -1 addedObject: {fileID: 8144035540121443354} + - targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 3d031ad534e512e45bb7fe10237e6286, + type: 3} + insertIndex: -1 + addedObject: {fileID: 8968669349745731875} m_SourcePrefab: {fileID: 100100000, guid: 3d031ad534e512e45bb7fe10237e6286, type: 3} --- !u!1 &6708744566382593167 stripped GameObject: @@ -163,6 +167,10 @@ MonoBehaviour: projectileSpeed: 20 projectileLifetime: 5 energyPerAttack: 20 + separationRadius: 1.5 + separationWeight: 3 + targetSpreadBias: 6 + summonerMode: 0 target: {fileID: 0} --- !u!114 &6730575396005638825 MonoBehaviour: @@ -179,12 +187,17 @@ MonoBehaviour: maxHealth: 360 currentHealth: 360 maxEnergy: 100 - currentEnergy: 0 + _currentEnergy: 0 heightOffset: 0.25 barWidth: 1.2 borderColor: {r: 0.03, g: 0.03, b: 0.03, a: 0.95} deathEffectDuration: 1.1 deathEffectColor: {r: 0.55, g: 0.9, b: 1, a: 1} + showDamageNumbers: 1 + damageNumberDuration: 0.8 + damageNumberRiseHeight: 1.2 + damageNumberSpawnHeight: 1.5 + damageNumberSize: 0.09 --- !u!114 &2583987946621363416 MonoBehaviour: m_ObjectHideFlags: 0 @@ -214,3 +227,16 @@ MonoBehaviour: showStunIndicator: 1 indicatorHeight: 0.35 stunIndicatorPrefab: {fileID: 0} +--- !u!114 &8968669349745731875 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6708744566382593167} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e6f40b66b50871440a97763c44d480a4, type: 3} + m_Name: + m_EditorClassIdentifier: + teamId: 0 diff --git a/Assets/Prefabs/第二章/石巨人.prefab b/Assets/Prefabs/第二章/石巨人.prefab index 325f3cf..40543dd 100644 --- a/Assets/Prefabs/第二章/石巨人.prefab +++ b/Assets/Prefabs/第二章/石巨人.prefab @@ -113,6 +113,10 @@ PrefabInstance: type: 3} insertIndex: -1 addedObject: {fileID: 8367410358801711549} + - targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: d40daebbc0e47416099e97edbbbeac04, + type: 3} + insertIndex: -1 + addedObject: {fileID: 507015780925844398} m_SourcePrefab: {fileID: 100100000, guid: d40daebbc0e47416099e97edbbbeac04, type: 3} --- !u!1 &4471054466352856454 stripped GameObject: @@ -147,6 +151,10 @@ MonoBehaviour: projectileSpeed: 20 projectileLifetime: 5 energyPerAttack: 20 + separationRadius: 1.5 + separationWeight: 3 + targetSpreadBias: 6 + summonerMode: 0 target: {fileID: 0} --- !u!143 &7319997191678055287 CharacterController: @@ -189,12 +197,17 @@ MonoBehaviour: maxHealth: 820 currentHealth: 820 maxEnergy: 100 - currentEnergy: 0 + _currentEnergy: 0 heightOffset: 0.25 barWidth: 1.5 borderColor: {r: 0.03, g: 0.03, b: 0.03, a: 0.95} deathEffectDuration: 1.1 deathEffectColor: {r: 0.55, g: 0.9, b: 1, a: 1} + showDamageNumbers: 1 + damageNumberDuration: 0.8 + damageNumberRiseHeight: 1.2 + damageNumberSpawnHeight: 1.5 + damageNumberSize: 0.09 --- !u!114 &1445493180626066942 MonoBehaviour: m_ObjectHideFlags: 0 @@ -227,3 +240,16 @@ MonoBehaviour: skillRadius: 5 stunDuration: 1 skillAnimationDuration: 0 +--- !u!114 &507015780925844398 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4471054466352856454} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e6f40b66b50871440a97763c44d480a4, type: 3} + m_Name: + m_EditorClassIdentifier: + teamId: 0 diff --git a/Assets/Prefabs/第二章/蓝色怪物.prefab b/Assets/Prefabs/第二章/蓝色怪物.prefab index 5005f6f..6c46621 100644 --- a/Assets/Prefabs/第二章/蓝色怪物.prefab +++ b/Assets/Prefabs/第二章/蓝色怪物.prefab @@ -128,6 +128,10 @@ PrefabInstance: type: 3} insertIndex: -1 addedObject: {fileID: 1038858676101729388} + - targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: aedf6e849dd924e229c57640f99ff50a, + type: 3} + insertIndex: -1 + addedObject: {fileID: 761483117139778608} m_SourcePrefab: {fileID: 100100000, guid: aedf6e849dd924e229c57640f99ff50a, type: 3} --- !u!1 &1823520504047441011 stripped GameObject: @@ -188,6 +192,10 @@ MonoBehaviour: projectileSpeed: 20 projectileLifetime: 5 energyPerAttack: 20 + separationRadius: 1.5 + separationWeight: 3 + targetSpreadBias: 6 + summonerMode: 0 target: {fileID: 1823520504047441011} --- !u!114 &2631642551191811442 MonoBehaviour: @@ -204,12 +212,17 @@ MonoBehaviour: maxHealth: 400 currentHealth: 400 maxEnergy: 100 - currentEnergy: 0 + _currentEnergy: 0 heightOffset: 0.25 barWidth: 1.2 borderColor: {r: 0.03, g: 0.03, b: 0.03, a: 0.95} deathEffectDuration: 1.1 deathEffectColor: {r: 0.1, g: 0.55, b: 1, a: 1} + showDamageNumbers: 1 + damageNumberDuration: 0.8 + damageNumberRiseHeight: 1.2 + damageNumberSpawnHeight: 1.5 + damageNumberSize: 0.09 --- !u!114 &8856053484402202215 MonoBehaviour: m_ObjectHideFlags: 0 @@ -240,3 +253,16 @@ MonoBehaviour: indicatorHeight: 0.35 stunIndicatorPrefab: {fileID: 2432573200162841629, guid: de288ea16be0c1544a4c6eb71e899570, type: 3} +--- !u!114 &761483117139778608 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1823520504047441011} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e6f40b66b50871440a97763c44d480a4, type: 3} + m_Name: + m_EditorClassIdentifier: + teamId: 0 diff --git a/Assets/Scenes/Chapter1/SummonerDuelTest.unity b/Assets/Scenes/Chapter1/SummonerDuelTest.unity index 7be0d99ed8ac6f664ced5e3624fe037b319864fd..d7557cd6a844dd52be85b36df17975dcb117541f 100644 GIT binary patch delta 28897 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 +/// AI 召唤师控制器:自动召唤单位攻击玩家 +/// +public class AISummoner : MonoBehaviour +{ + [Header("AI 配置")] + [Tooltip("召唤间隔(秒)")] + [Min(1f)] + public float summonInterval = 3f; + + [Tooltip("随机偏移范围(秒),避免太规律")] + [Min(0f)] + public float summonRandomOffset = 1f; + + [Tooltip("AI 难度:召唤CD缩放(越小越快)")] + [Range(0.5f, 2f)] + public float difficultyScale = 1f; + + [Header("能量管理")] + [Tooltip("AI 初始能量")] + [Min(0)] + public int startingMana = 150; + + [Tooltip("AI 是否使用无限能量")] + public bool unlimitedMana = false; + + private MainCharacterCombat combat; + private ManaSystem manaSystem; + private float summonCooldown; + private bool isActive; + + private void Start() + { + combat = GetComponent(); + if (combat == null) + { + Debug.LogError($"[AISummoner] {name} 缺少 MainCharacterCombat 组件!"); + enabled = false; + return; + } + + manaSystem = FindObjectOfType(); + if (manaSystem == null) + { + Debug.LogWarning($"[AISummoner] 场景中没有 ManaSystem,AI 将无法检查能量"); + } + + // 初始延迟,避免开局立刻召唤 + summonCooldown = summonInterval * difficultyScale; + isActive = true; + + Debug.Log($"[AISummoner] {name} AI 初始化完成,召唤间隔: {summonInterval}s"); + } + + private void Update() + { + if (!isActive) return; + + // AI 被眩晕时停止召唤 + if (combat.IsStunned) return; + + summonCooldown -= Time.deltaTime; + if (summonCooldown <= 0f) + { + TrySummon(); + ResetCooldown(); + } + } + + private void TrySummon() + { + // 随机选择一个可召唤的角色槽位(1-5) + var characters = combat.summonableCharacters; + if (characters == null || characters.Length == 0) + { + Debug.LogWarning($"[AISummoner] {name} 没有可召唤的角色"); + return; + } + + // 过滤出有效的角色数据 + int[] validSlots = new int[characters.Length]; + int validCount = 0; + + for (int i = 0; i < characters.Length; i++) + { + if (characters[i] != null && characters[i].unitPrefab != null) + { + // 检查能量 + if (unlimitedMana || manaSystem == null || manaSystem.CurrentManaInt >= characters[i].manaCost) + { + validSlots[validCount++] = i + 1; // slot 是 1-based + } + } + } + + if (validCount == 0) + { + // 没有可召唤的角色(能量不足或没有有效角色) + return; + } + + // 随机选择一个有效槽位 + int randomIndex = Random.Range(0, validCount); + int selectedSlot = validSlots[randomIndex]; + + // 执行召唤(使用 MainCharacterCombat.RequestSummon) + bool success = combat.RequestSummon(selectedSlot); + + if (success) + { + var selectedCharacter = characters[selectedSlot - 1]; + Debug.Log($"[AISummoner] {name} 召唤了 {selectedCharacter.characterName} (槽位: {selectedSlot})"); + } + } + + private void ResetCooldown() + { + float randomOffset = Random.Range(-summonRandomOffset, summonRandomOffset); + summonCooldown = summonInterval * difficultyScale + randomOffset; + } + + /// + /// 启用/禁用 AI + /// + public void SetActive(bool active) + { + isActive = active; + Debug.Log($"[AISummoner] {name} AI {(active ? "启用" : "禁用")}"); + } + + /// + /// 调整难度(实时修改召唤速度) + /// + public void SetDifficulty(float scale) + { + difficultyScale = Mathf.Clamp(scale, 0.5f, 2f); + Debug.Log($"[AISummoner] {name} 难度调整为 {difficultyScale}"); + } +} diff --git a/Assets/Script/AISummoner.cs.meta b/Assets/Script/AISummoner.cs.meta new file mode 100644 index 0000000..930477f --- /dev/null +++ b/Assets/Script/AISummoner.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: CCke4S2lUy3nU786T7yvHZYNAkerlhq9/n+qIWEgtRjk/6EPe8RNOPo= +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Script/Barracks.cs b/Assets/Script/Barracks.cs index 2985935..87b4e73 100644 --- a/Assets/Script/Barracks.cs +++ b/Assets/Script/Barracks.cs @@ -88,6 +88,19 @@ public class Barracks : MonoBehaviour unit.name = characterData.characterName; unit.tag = unitTag; + // 设置阵营 ID(根据建筑的阵营) + var buildingTeam = GetComponent(); + var unitTeam = unit.GetComponent(); + if (buildingTeam != null && unitTeam != null) + { + unitTeam.teamId = buildingTeam.teamId; + } + else if (unitTeam != null) + { + // 兜底:根据 tag 判断阵营 + unitTeam.teamId = unitTag == "Friend" ? 0 : 1; + } + // 如果单位有UnitBase组件,使用1级属性初始化 var unitBase = unit.GetComponent(); if (unitBase != null) diff --git a/Assets/Script/BattleSetupManager.cs b/Assets/Script/BattleSetupManager.cs index 4c03e1f..8aeb4d1 100644 --- a/Assets/Script/BattleSetupManager.cs +++ b/Assets/Script/BattleSetupManager.cs @@ -235,6 +235,13 @@ public class BattleSetupManager : MonoBehaviour unit.name = "Friend_" + data.characterName; unit.tag = "Friend"; + // 设置阵营 ID(玩家单位为 team 0) + var unitTeam = unit.GetComponent(); + if (unitTeam != null) + { + unitTeam.teamId = 0; + } + var unitBase = unit.GetComponent(); if (unitBase != null) { diff --git a/Assets/Script/GameManager.cs b/Assets/Script/GameManager.cs new file mode 100644 index 0000000..bf1d923 --- /dev/null +++ b/Assets/Script/GameManager.cs @@ -0,0 +1,37 @@ +using UnityEngine; + +/// +/// 游戏管理器:管理全局游戏状态,包括本地玩家的阵营 ID。 +/// PVE:LocalPlayerTeamId = 0(固定) +/// PVP:服务器分配后设置 LocalPlayerTeamId(比如 0 或 1) +/// +public class GameManager : MonoBehaviour +{ + public static GameManager Instance { get; private set; } + + [Header("本地玩家阵营")] + [Tooltip("PVE: 固定为0; PVP: 服务器分配")] + public int LocalPlayerTeamId = 0; + + private void Awake() + { + // 单例模式 + if (Instance != null && Instance != this) + { + Destroy(gameObject); + return; + } + + Instance = this; + DontDestroyOnLoad(gameObject); + } + + /// + /// 设置本地玩家阵营 ID(PVP 时由服务器调用) + /// + public void SetLocalPlayerTeam(int teamId) + { + LocalPlayerTeamId = teamId; + Debug.Log($"本地玩家阵营设置为: {teamId}"); + } +} diff --git a/Assets/Script/GameManager.cs.meta b/Assets/Script/GameManager.cs.meta new file mode 100644 index 0000000..e1d8b63 --- /dev/null +++ b/Assets/Script/GameManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: CXkdt3ulUS3TPoI5TieIx0QPMa6EXoIKUewqD1fNwBgwo43HfDPsYzY= +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Script/MainCharacterCombat.cs b/Assets/Script/MainCharacterCombat.cs index 70d24e9..c774501 100644 --- a/Assets/Script/MainCharacterCombat.cs +++ b/Assets/Script/MainCharacterCombat.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using UnityEngine; +[RequireComponent(typeof(StatusEffectController))] public class MainCharacterCombat : MonoBehaviour { [Header("自动攻击")] @@ -42,6 +43,7 @@ public class MainCharacterCombat : MonoBehaviour [Min(0f)] public float projectileLifetime = 5f; + private StatusEffectController statusEffectController; private Animator animator; private MainCharacterDebugMovement movement; private GameObject target; @@ -56,10 +58,18 @@ public class MainCharacterCombat : MonoBehaviour private bool summonSpawned; private bool summoning; + public bool IsStunned => statusEffectController != null && statusEffectController.IsStunned; + private void Start() { animator = GetComponent(); movement = GetComponent(); + statusEffectController = GetComponent(); + + if (statusEffectController == null) + { + statusEffectController = gameObject.AddComponent(); + } if (animator != null) { @@ -69,6 +79,16 @@ public class MainCharacterCombat : MonoBehaviour private void Update() { + // 眩晕时停止所有行动 + if (IsStunned) + { + if (animator != null) + { + animator.SetInteger("speed", 0); + } + return; + } + if (attackCooldown > 0f) { attackCooldown -= Time.deltaTime; @@ -230,11 +250,12 @@ public class MainCharacterCombat : MonoBehaviour return; } - // 立即停止移动 + // 立即停止移动,并禁用移动脚本(防止干扰动画) SummonerClickMovement clickMovement = GetComponent(); if (clickMovement != null) { clickMovement.ClearTarget(); + clickMovement.enabled = false; // 禁用移动脚本,避免它设置 speed 参数 } pendingSummonPrefab = prefab; @@ -244,9 +265,17 @@ public class MainCharacterCombat : MonoBehaviour summonSpawned = false; summoning = true; animator.speed = 1f; + + // 强制重置动画状态,避免状态过渡冲突 animator.SetInteger("speed", 0); animator.ResetTrigger("skill"); + animator.ResetTrigger("attack"); + + // 直接强制播放 skill 动画,绕过状态机的所有过渡条件 + // Play() 会立即切换,不管当前状态是什么 animator.Play("skill", 0, 0f); + + Debug.Log("强制播放 skill 动画"); } public void OnSkillHit1() @@ -272,7 +301,22 @@ public class MainCharacterCombat : MonoBehaviour pendingSummonPrefab, spawnPosition, Quaternion.LookRotation(direction, Vector3.up)); - summoned.tag = "Friend"; + + // 召唤的单位继承召唤师的阵营(不再硬编码为 Friend) + UnitTeam summonerTeam = GetComponent(); + UnitTeam summonedTeam = summoned.GetComponent(); + + if (summonerTeam != null && summonedTeam != null) + { + summonedTeam.teamId = summonerTeam.teamId; + // 根据阵营设置 tag(向后兼容旧系统) + summoned.tag = summonerTeam.teamId == 0 ? "Friend" : "Enemy"; + } + else + { + // 降级:没有 UnitTeam 时保持原逻辑 + summoned.tag = "Friend"; + } UnitBase unitBase = summoned.GetComponent(); if (unitBase != null && pendingSummonCharacterData != null && pendingSummonPlayerData != null) @@ -404,16 +448,25 @@ public class MainCharacterCombat : MonoBehaviour private void CheckSummonFinished() { AnimatorStateInfo state = animator.GetCurrentAnimatorStateInfo(0); + + // 正常完成:skill 动画播放完毕 if (state.IsName("skill") && state.normalizedTime >= 1f) { FinishSummon(); return; } - // skill 状态意外被其他动画打断时,才使用超时兜底,避免永久锁定召唤。 + // 超时检测:添加调试信息 if (summonElapsed >= summonAnimationTimeout) { - Debug.LogWarning("主角召唤动画超时,已解除召唤状态"); + // 输出当前动画状态,帮助诊断问题 + string currentState = "unknown"; + if (state.IsName("skill")) currentState = "skill"; + else if (state.IsName("idle")) currentState = "idle"; + else if (state.IsName("run")) currentState = "run"; + else if (state.IsName("attack")) currentState = "attack"; + + Debug.LogWarning($"主角召唤动画超时,已解除召唤状态。当前动画状态: {currentState}, normalizedTime: {state.normalizedTime:F2}"); FinishSummon(); } } @@ -427,6 +480,15 @@ public class MainCharacterCombat : MonoBehaviour animator.speed = 1f; animator.SetInteger("speed", 0); animator.Play("idle", 0, 0f); + + // 重新启用移动脚本 + SummonerClickMovement clickMovement = GetComponent(); + if (clickMovement != null) + { + clickMovement.enabled = true; + } + + Debug.Log("召唤完成,恢复移动脚本"); } private void CheckAttackFinished() @@ -535,18 +597,38 @@ public class MainCharacterCombat : MonoBehaviour private GameObject FindNearestEnemy() { // 索敌范围使用较小的 detectRange,主角不主动远离基地寻找敌人 + UnitTeam myTeam = GetComponent(); + if (myTeam == null) + { + return null; + } + GameObject nearest = null; float nearestDistance = detectRange * detectRange; - foreach (GameObject enemy in GameObject.FindGameObjectsWithTag("Enemy")) + // 查找所有带 UnitTeam 的对象(包括单位和召唤师) + UnitTeam[] allTeams = FindObjectsOfType(); + + foreach (UnitTeam targetTeam in allTeams) { - if (enemy == null || enemy.transform.root == transform.root) + GameObject enemy = targetTeam.gameObject; + + if (enemy == null || enemy == gameObject || enemy.transform.root == transform.root) { continue; } + // 检查是否是敌人(使用阵营 ID) + if (!myTeam.IsEnemy(enemy)) + { + continue; + } + + // 检查是否有 UnitBase 或 WorldHealthBar(召唤师) UnitBase enemyUnit = enemy.GetComponentInParent(); - if (enemyUnit == null) + WorldHealthBar enemyHealth = enemy.GetComponentInParent(); + + if (enemyUnit == null && enemyHealth == null) { continue; } diff --git a/Assets/Script/Projectile.cs b/Assets/Script/Projectile.cs index 4707095..c602b38 100644 --- a/Assets/Script/Projectile.cs +++ b/Assets/Script/Projectile.cs @@ -116,20 +116,29 @@ public class Projectile : MonoBehaviour UnitBase targetUnit = hitObject.GetComponentInParent(); BuildingHealth targetBuilding = hitObject.GetComponentInParent(); - if (targetUnit == null && targetBuilding == null) + WorldHealthBar targetHealthBar = hitObject.GetComponentInParent(); + + if (targetUnit == null && targetBuilding == null && targetHealthBar == null) { return; } hasHit = true; + if (targetUnit != null) { targetUnit.ReceiveDamage(damage, owner); } - else + else if (targetBuilding != null) { targetBuilding.ReceiveDamage(damage, owner); } + else if (targetHealthBar != null) + { + // 召唤师等特殊单位(有 WorldHealthBar 但没有 UnitBase) + targetHealthBar.TakeDamage(damage, owner); + } + if (registerEnergyOnHit && owner != null) { UnitBase unit = owner.GetComponent(); diff --git a/Assets/Script/UnitBase.cs b/Assets/Script/UnitBase.cs index 32227d5..5985f5c 100644 --- a/Assets/Script/UnitBase.cs +++ b/Assets/Script/UnitBase.cs @@ -342,48 +342,50 @@ public class UnitBase : MonoBehaviour //根据自身Tag找到敌对Tag private string GetOpponentTag() { - if (CompareTag("Friend")) - { - return "Enemy"; - } - - if (CompareTag("Enemy")) - { - return "Friend"; - } - + // 已弃用:改用阵营 ID 系统,不再依赖 Tag 判断敌我 + // 保留此方法是为了兼容旧代码,但不应再被调用 + Debug.LogWarning("GetOpponentTag() 已弃用,请使用阵营 ID 系统"); return null; } - //查找距离自己最近的敌对目标 + //查找距离自己最近的敌对目标(使用阵营 ID 判断) private GameObject FindNearestOpponent() { - string opponentTag = GetOpponentTag(); - if (string.IsNullOrEmpty(opponentTag)) + var myTeam = GetComponent(); + if (myTeam == null) { + Debug.LogWarning($"{gameObject.name} 缺少 UnitTeam 组件,无法查找敌人"); return null; } GameObject nearestOpponent = null; float nearestSqrDistance = float.PositiveInfinity; - GameObject[] opponents = GameObject.FindGameObjectsWithTag(opponentTag); - foreach (var opponent in opponents) + // 查找所有 UnitTeam 组件(不再依赖 Tag) + UnitTeam[] allUnits = FindObjectsOfType(); + + foreach (var unit in allUnits) { - //排除自己以及自己所在层级中的对象 - if (opponent == gameObject || opponent.transform.root == transform.root) + // 跳过自己和友军 + if (unit.gameObject == gameObject || !myTeam.IsEnemy(unit.gameObject)) { continue; } - float sqrDistance = (opponent.transform.position - transform.position).sqrMagnitude; + //排除自己所在层级中的对象 + if (unit.transform.root == transform.root) + { + continue; + } + + float sqrDistance = (unit.transform.position - transform.position).sqrMagnitude; //加入确定性分散偏移:让不同单位偏好不同目标,避免全部涌向同一个敌人 - float bias = ((uint)(gameObject.GetInstanceID() * 7919) ^ (uint)(opponent.GetInstanceID())) % 10u / 10f * targetSpreadBias; + float bias = ((uint)(gameObject.GetInstanceID() * 7919) ^ (uint)(unit.gameObject.GetInstanceID())) % 10u / 10f * targetSpreadBias; float adjustedSqrDistance = sqrDistance + bias; if (adjustedSqrDistance < nearestSqrDistance) { nearestSqrDistance = adjustedSqrDistance; - nearestOpponent = opponent; + nearestOpponent = unit.gameObject; } } @@ -651,6 +653,15 @@ public class UnitBase : MonoBehaviour if (targetBuilding != null && targetBuilding.ReceiveDamage(attackPower, gameObject)) { RegisterAttackHit(); + return; + } + + // 召唤师等特殊单位(有 WorldHealthBar 但没有 UnitBase) + WorldHealthBar targetHealthBar = opponent.GetComponentInParent(); + if (targetHealthBar != null) + { + targetHealthBar.TakeDamage(attackPower, gameObject); + RegisterAttackHit(); } } @@ -689,23 +700,33 @@ public class UnitBase : MonoBehaviour return; } - string opponentTag = GetOpponentTag(); - if (string.IsNullOrEmpty(opponentTag)) + UnitTeam myTeam = GetComponent(); + if (myTeam == null) { return; } float clampedRadius = Mathf.Max(0f, radius); float radiusSquared = clampedRadius * clampedRadius; - GameObject[] opponents = GameObject.FindGameObjectsWithTag(opponentTag); + + // 查找所有带 UnitTeam 的对象(包括单位和召唤师) + UnitTeam[] allTeams = FindObjectsOfType(); HashSet hitHealthBars = new HashSet(); - foreach (GameObject opponent in opponents) + foreach (UnitTeam targetTeam in allTeams) { + GameObject opponent = targetTeam.gameObject; + if (opponent == null || opponent == gameObject || opponent.transform.root == transform.root) { continue; } + + // 检查是否是敌人 + if (!myTeam.IsEnemy(opponent)) + { + continue; + } Vector3 offset = opponent.transform.position - transform.position; offset.y = 0f; @@ -716,9 +737,21 @@ public class UnitBase : MonoBehaviour UnitBase targetUnit = opponent.GetComponentInParent(); BuildingHealth targetBuilding = opponent.GetComponentInParent(); - WorldHealthBar healthBar = targetUnit != null - ? targetUnit.GetComponent() - : targetBuilding != null ? targetBuilding.GetComponent() : null; + WorldHealthBar healthBar = null; + + if (targetUnit != null) + { + healthBar = targetUnit.GetComponent(); + } + else if (targetBuilding != null) + { + healthBar = targetBuilding.GetComponent(); + } + else + { + // 召唤师等特殊单位(有 WorldHealthBar 但没有 UnitBase) + healthBar = opponent.GetComponentInParent(); + } if (healthBar == null || !hitHealthBars.Add(healthBar)) { @@ -729,10 +762,15 @@ public class UnitBase : MonoBehaviour { targetUnit.ReceiveDamage(damage, gameObject); } - else + else if (targetBuilding != null) { targetBuilding.ReceiveDamage(damage, gameObject); } + else if (healthBar != null) + { + // 召唤师等特殊单位 + healthBar.TakeDamage(damage, gameObject); + } } if (registerEnergy) @@ -748,23 +786,33 @@ public class UnitBase : MonoBehaviour return; } - string opponentTag = GetOpponentTag(); - if (string.IsNullOrEmpty(opponentTag)) + UnitTeam myTeam = GetComponent(); + if (myTeam == null) { return; } float clampedRadius = Mathf.Max(0f, radius); float radiusSquared = clampedRadius * clampedRadius; - GameObject[] opponents = GameObject.FindGameObjectsWithTag(opponentTag); + + // 查找所有带 UnitTeam 的对象(包括单位和召唤师) + UnitTeam[] allTeams = FindObjectsOfType(); HashSet hitUnits = new HashSet(); - foreach (GameObject opponent in opponents) + foreach (UnitTeam targetTeam in allTeams) { + GameObject opponent = targetTeam.gameObject; + if (opponent == null || opponent == gameObject || opponent.transform.root == transform.root) { continue; } + + // 检查是否是敌人 + if (!myTeam.IsEnemy(opponent)) + { + continue; + } Vector3 offset = opponent.transform.position - transform.position; offset.y = 0f; @@ -774,12 +822,18 @@ public class UnitBase : MonoBehaviour } UnitBase unit = opponent.GetComponentInParent(); - if (unit == null || !hitUnits.Add(unit)) + if (unit != null && hitUnits.Add(unit)) { + unit.ApplyStun(duration); continue; } - - unit.ApplyStun(duration); + + // 召唤师等特殊单位(有 StatusEffectController 但没有 UnitBase) + StatusEffectController statusEffect = opponent.GetComponentInParent(); + if (statusEffect != null) + { + statusEffect.ApplyStun(duration); + } } } diff --git a/Assets/Script/UnitTeam.cs b/Assets/Script/UnitTeam.cs new file mode 100644 index 0000000..2b07315 --- /dev/null +++ b/Assets/Script/UnitTeam.cs @@ -0,0 +1,47 @@ +using UnityEngine; + +/// +/// 单位阵营标记:用阵营 ID 替代 Unity Tag 判断敌我关系。 +/// 支持 PVP:客户端只需设置 GameManager.LocalPlayerTeamId,所有单位的敌我判断自动正确。 +/// +public class UnitTeam : MonoBehaviour +{ + [Header("阵营 ID")] + [Tooltip("0 = 玩家1阵营,1 = 玩家2阵营,2+ = 其他阵营")] + public int teamId = 0; + + /// + /// 判断目标是否为敌人(不同阵营) + /// + public bool IsEnemy(GameObject other) + { + if (other == null) return false; + + var otherTeam = other.GetComponent(); + if (otherTeam == null) return false; + + return otherTeam.teamId != this.teamId; + } + + /// + /// 判断目标是否为友军(相同阵营) + /// + public bool IsFriendly(GameObject other) + { + if (other == null) return false; + + var otherTeam = other.GetComponent(); + if (otherTeam == null) return false; + + return otherTeam.teamId == this.teamId; + } + + /// + /// 判断是否属于本地玩家阵营 + /// + public bool IsLocalPlayerTeam() + { + if (GameManager.Instance == null) return teamId == 0; + return teamId == GameManager.Instance.LocalPlayerTeamId; + } +} diff --git a/Assets/Script/UnitTeam.cs.meta b/Assets/Script/UnitTeam.cs.meta new file mode 100644 index 0000000..ab5d2ba --- /dev/null +++ b/Assets/Script/UnitTeam.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: WnxJsSr/VX1DeBY80HGM2j/amZWAkx8FQCM7bFTomhdecHzVdHd5eOw= +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Script/WorldHealthBar.cs b/Assets/Script/WorldHealthBar.cs index 19fbfeb..dec374b 100644 --- a/Assets/Script/WorldHealthBar.cs +++ b/Assets/Script/WorldHealthBar.cs @@ -484,9 +484,16 @@ public class WorldHealthBar : MonoBehaviour private Color GetHealthFillColor() { - return CompareTag("Enemy") - ? new Color(0.88f, 0.12f, 0.12f, 1f) - : new Color(0.1f, 0.8f, 0.18f, 1f); + // 使用阵营 ID 判断敌我(支持 PVP) + var unitTeam = GetComponent(); + if (unitTeam != null && unitTeam.IsLocalPlayerTeam()) + { + return new Color(0.1f, 0.8f, 0.18f, 1f); // 绿色(我方) + } + else + { + return new Color(0.88f, 0.12f, 0.12f, 1f); // 红色(敌方) + } } private Color GetEnergyFillColor() diff --git a/CODELY.md b/CODELY.md index 1496976..fca5fe4 100644 --- a/CODELY.md +++ b/CODELY.md @@ -23,6 +23,7 @@ - [2026-09-14 18:24:56] 用户认可"参考游戏是基地离得远、镜头更远、单位推进慢"的观感方向(2026-09-14 数值改慢后反馈"好多了")。**Why:** 之前双方距离近+移速快,单位两三秒就接触,战斗缺乏观察窗口。**How to apply:** 后续地图/镜头调整优先加长双方距离、拉远摄像机;数值宁慢勿快。 - [2026-09-16 22:18:14] 召唤系统:找不到合适位置时直接在召唤师前方强制生成,不显示警告,不退还能量。**Why:** 用户要求召唤必须成功,不被取消。**How to apply:** MainCharacterCombat.cs 的召唤逻辑 - [2026-09-16 22:18:30] 召唤打断移动:主角点击召唤时必须立即停止移动,调用 SummonerClickMovement.ClearTarget() 来实现。**Why:** 用户要求召唤时即使在移动也要立刻停下。**How to apply:** BeginSummon 开始时停止移动 +- [2026-09-17 10:06:34] 召唤师移动后连续召唤动画超时问题:SummonerClickMovement.HandleMovement() 每帧设置 animator.SetInteger("speed", 1) 干扰召唤动画。**Why:** 即使召唤时调用了 ClearTarget(),下一帧 Update 还是会覆盖动画参数。**How to apply:** 召唤期间禁用移动脚本 (clickMovement.enabled = false),召唤完成后重新启用 (enabled = true)。已在 MainCharacterCombat.Summon() 和 FinishSummon() 中实现。 ### Project - [2026-08-28 14:26:56] 弓箭手远程箭矢的设计要求:保持发射时高度水平飞行,命中目标后直接结算伤害并销毁,不产生实体物理击退。**Why:** 当前玩法希望远程攻击是命中判定而非物理推动。**How to apply:** 后续调整箭矢、Projectile 或弓箭手攻击逻辑时保持这一行为。 @@ -57,6 +58,11 @@ - [2026-09-15 19:33:31] 金币系统已完成:GoldManager 单例管理,初始500金币,右上角UI实时更新。金矿建筑产出50金币/5秒,主角产出30金币/8秒。WorldHealthBar 的 currentEnergy 改为属性(带 setter 自动调用 UpdateEnergyFill),能量条尺寸从8像素增加到12像素(EnergyPixelHeight=12,EnergyPixelWidth=100)。能量条显示金币产出进度,满了产出并重置。已更新核心玩法文档。 - [2026-09-16 14:22:13] 2026-09-16 玩法定稿为"召唤师对决"并已写入《召唤之战-核心玩法.md》:无基地/金矿/兵营/防御塔,玩家手动点地操控召唤师(移动不攻击、站定攻击),限己方半场(三分屏:己方/交战/敌方区),能量直购出兵(单位在召唤师身边生成),击杀敌方召唤师获胜,超时毒圈收缩并解除半场限制,中期混沌裂缝事件,召唤师技能 3~4 种,摇杆留作实测后升级项。**Why:** 用户多轮迭代后的最终方向,旧"建筑生产+自动作战"对攻玩法封存不删。**How to apply:** 新战斗开发一律基于召唤师对决方案;不要在新玩法场景引入金矿/兵营/建筑摆放;BuildingPlacementManager/Barracks/GoldMine 等封存留作后续建筑卡或 PVE 特殊关素材。 - [2026-09-16 14:48:52] 召唤师对决首版场景已完成(SummonerDuelTest.unity,2026-09-16):新场景地面20×80、固定正交相机43°/orthoSize28、召唤师z=0(敌方z=35)均避开底部UI;新增 SummonerClickMovement(点地走位+clamp X[-9,9] Z[-19,6],移动中关攻击、到点恢复)、SummonerDuelManager(点卡片按能量直召,单位在召唤师身边生成)、UnitBase 新增 summonerMode 开关(不追击、站定攻击,默认关闭不影响旧单位);战斗界面UI从 CoreBattleTest 整树搬运复用,SummonCardButton 增加对决模式分支(无布阵/金币管理器时走能量直召+冷却)。主角已存为 Assets/Prefabs/主角/召唤师主角.prefab。**How to apply:** 后续对决玩法迭代(毒圈/胜负判定/召唤师技能)基于该场景与脚本扩展;Play Mode 验证等用户手动执行。 +- [2026-09-17 09:33:46] 阵营 ID 系统已实现(替代 Unity Tag 判断敌我):UnitTeam 组件标记 teamId,GameManager.LocalPlayerTeamId 标识本地玩家阵营,支持 PVP(真人 vs 真人 / 真人 vs 机器人)。**Why:** PVP 需要动态敌我判断,Tag 系统无法支持(双方客户端看到的 tag 矛盾)。**How to apply:** 所有敌我判断改用 UnitTeam.IsEnemy(),血条颜色用 IsLocalPlayerTeam(),新生成单位必须设置 teamId(Barracks/BattleSetupManager 已修改)。 +- [2026-09-17 09:33:58] 召唤师组件精简:移除了 NavMeshAgent、UnitBase、StatusEffectController,保留 MainCharacterCombat(战斗)+ SummonerClickMovement(移动)+ WorldHealthBar(血条)。SummonerClickMovement 已改为依赖 MainCharacterCombat 而非 UnitBase。**Why:** 召唤师是特殊单位,不应使用通用 UnitBase 逻辑;直线移动不需要 NavMesh。**How to apply:** 召唤师专属逻辑用 MainCharacterCombat,普通单位用 UnitBase。 +- [2026-09-17 09:34:13] 《召唤之战》场地规格:60×20(Z×X),三区划分各 20 单位 - 己方区 z∈[-30,-10](召唤师 z=-20),交战区 z∈[-10,10],敌方区 z∈[10,30](敌方召唤师 z=+20)。召唤师移动限制在己方区内(SummonerClickMovement clamp Z[-29,-11])。相机 orthoSize=22,位置(0,40,-40),角度 43°。**Why:** 竖屏竞技场设计,双方对称 40 单位间距,防风筝规则(召唤师不能进入交战区)。**How to apply:** 所有位置/spawn 相关代码使用新的 Z 坐标范围。 +- [2026-09-17 10:06:48] Projectile 伤害判定逻辑:HitTarget() 检查 UnitBase(普通单位)、BuildingHealth(建筑)、WorldHealthBar(召唤师等特殊单位)三种受击组件。召唤师使用 MainCharacterCombat + WorldHealthBar,不挂 UnitBase。**Why:** 召唤师是玩家控制的特殊单位,行为逻辑与普通 AI 单位不同。**How to apply:** 新增特殊单位必须有 WorldHealthBar 或 UnitBase 之一才能被攻击;法术球击中后调用对应组件的受伤方法(UnitBase.ReceiveDamage / WorldHealthBar.TakeDamage)。 +- [2026-09-17 10:38:49] 召唤师(MainCharacterCombat)与普通单位(UnitBase)的架构差异:召唤师使用 MainCharacterCombat + WorldHealthBar + StatusEffectController,不挂 UnitBase。**Why:** 召唤师是玩家控制的特殊单位,行为逻辑与 AI 单位不同。**How to apply:** 涉及单位的通用系统(伤害、眩晕、范围效果)必须同时支持 UnitBase 和非 UnitBase 路径。已修复:Projectile.HitTarget()、UnitBase.ApplyDirectDamage()、UnitBase.ApplyAreaStun() 均添加了 WorldHealthBar / StatusEffectController 的检测分支。 ### Reference - [2026-09-09 12:23:17] 飞行物脚本 Projectile.cs 负责锁敌追踪、碰撞检测和伤害结算。箭矢预制体 ArrowProjectile_White.prefab 已配置 Projectile 组件和 CapsuleCollider(Is Trigger)。远程单位需在攻击动画松弦帧添加 OnAttackEffect Event,并在 UnitBase 中设置 attackType=RangedProjectile、projectilePrefab、projectileSpawnPoint。 diff --git a/召唤之战 - 核心玩法拆家版.md b/召唤之战 - 核心玩法拆家版.md new file mode 100644 index 0000000..fd53653 --- /dev/null +++ b/召唤之战 - 核心玩法拆家版.md @@ -0,0 +1,171 @@ +# 召唤之战-核心玩法拆家版 +> 版本:V1.1 +> 定位:竖版手机休闲即时战略小游戏 +> 单局时长:1~3分钟 +> 核心目标:摧毁敌方主基地获胜 +> 核心特点:轻量操作、自由寻路、资源与出兵据点争夺、兵种克制,无复杂框选与编队 + +## 一、核心定位 +本玩法为《召唤之战》的「拆家流」经典RTS版本,玩家通过消耗法力召唤单位,争夺战场上的水晶矿与随机出兵点,建立经济或兵力优势,最终突破防御摧毁敌方基地取胜。 +- 操作极简:仅需「选择出兵目标 + 点击卡牌召唤单位」,无需框选、编队、驻守指令; +- 单位全自动作战:复用自动寻路与寻敌逻辑,玩家不干预单位微操; +- 策略核心:出兵目标选择、兵种搭配、资源分配节奏、资源点与出兵点取舍,避免纯数值对撞。 + +## 二、战场地图设计 +### 2.1 整体布局 +采用**竖版9:16比例**,核心可通行区域为十字形,外围为不可通行障碍物(岩石/树林)。既限制战场范围避免混乱,又保留多路径进攻空间,杜绝单路排队式战斗。 +┌──────────────────────────────┐ +│ 障碍物(不可通行) │ +│ ┌────────────────────────┐ │ +│ │ 【敌方城堡】 │ │ +│ │ 【敌方防御塔】 │ │ +│ │ │ │ +│ │ ●─────────● │ │ +│ │ 水晶矿 出兵点 │ │ +│ │ (横向通道两端) │ │ +│ │ │ │ +│ │ 【我方防御塔】 │ │ +│ │ 【我方城堡】 │ │ +│ └────────────────────────┘ │ +│ 障碍物(不可通行) │ +└──────────────────────────────┘ + + +### 2.2 区域说明 +| 区域 | 位置 | 规则 | +|------|------|------| +| 己方阵地 | 屏幕下方 | 包含我方城堡、防御塔,为单位出生点 | +| 敌方阵地 | 屏幕上方 | 包含敌方城堡、防御塔,为最终攻击目标 | +| 十字主通道 | 战场中部 | 纵向连通双方阵地,横向连通水晶矿与出兵点;单位可在区域内自由移动 | +| 水晶矿 | 横向通道一端 | 可攻击争夺目标,占领后持续提升法力回复速度 | +| 随机出兵点 | 横向通道另一端 | 可攻击争夺目标,占领后定时随机产出单位 | + +### 2.3 寻路规则 +- 基于NavMesh实现自由寻路,仅十字区域为可通行烘焙区,障碍物完全阻挡; +- 单位自动计算最短路径前往目标,途中自动避让友军与地形; +- 无强制固定路径,单位可在十字区域内任意移动,避免排队式战斗。 + +## 三、资源系统 +### 3.1 法力水晶 +- 战斗内唯一资源,用于召唤单位、释放技能; +- 基础自动回复,开局默认速度,上限10点; +- 占领水晶矿可提升回复速度,具体回复加成数值以测试为准; + +### 3.2 资源据点争夺规则 +- 水晶矿与随机出兵点均为可攻击目标,不设置生命值,使用**红蓝争夺进度条**(0~100); +- 进度条中立状态为50:50。以当前观察方为例,100表示该方完全占领,0表示对方完全占领; +- 己方单位攻击据点时,进度条向己方方向偏移;敌方攻击时,进度条向敌方方向偏移; +- 进度条达到己方100%时,据点归属己方并开始产出对应资源; +- 据点归属后,即使敌方开始攻击并推动进度条反向移动,原占有方仍持续获得产出; +- 只有当进度条被对方完全推到100%(即原占有方进度为0)时,才中止原占有方产出并将据点翻转给对方; +- 无任何单位攻击时,进度条保持当前数值不变; +- 据点翻转后,按相同规则由新的占有方获得对应产出。 + +### 3.3 水晶矿产出 +- 水晶矿归属己方后,持续提升己方法力回复速度; +- 水晶矿被对方完全翻转后,法力回复加成立即转移给对方; +- 单个水晶矿的回复加成与多个据点叠加规则,以战斗测试结果为准。 + +### 3.4 随机出兵点产出 +- 随机出兵点归属己方后,按固定间隔在出兵点附近自动生成1个单位; +- 产出单位从**当前玩家出战卡组**中随机抽取,不从所有已解锁或全体角色中抽取; +- 随机概率按召唤费用分层:3费及以下概率较高,费用越高概率越低;具体概率以测试为准; +- 产出单位不消耗玩家法力,价值定位低于玩家主动召唤,避免完全替代正常出兵; +- 产出单位默认执行随机出兵点归属方当前的出兵目标;如无特殊设置,则默认目标为敌方主基地; +- 随机出兵点被对方完全翻转后,自动出兵权转移给对方。 + +## 四、单位系统 +### 4.1 通用属性 +所有单位统一包含:召唤费用、生命值、攻击力、攻击距离、移动速度、攻击优先级。 + +### 4.2 单位分类与定位 +| 单位 | 费用档位 | 定位 | 核心特点 | +|------|----------|------|----------| +| 近战步兵 | 低费 | 前排肉盾/量产 | 血量适中,近战输出,优先攻击敌方单位 | +| 远程射手 | 中费 | 后排输出 | 身脆伤害高,需前排保护,克制无甲单位 | +| 重甲单位 | 高费 | 推线核心 | 高血量,伤害偏低,优先攻击建筑 | +| 范围法师 | 高额 | 群体输出 | 范围伤害,克制扎堆小兵,自身极脆 | + +### 4.3 基础克制关系 +步兵克制法师、射手克制步兵、重甲克制建筑、法师克制扎堆步兵,形成循环博弈。 + +## 五、核心战斗逻辑 +### 5.1 单位AI优先级(固定不变) +单位自动寻敌,优先级从高到低: +1. 视野内的敌方单位(最高优先级,发现即停止移动并攻击) +2. 自身绑定的指定目标(水晶矿 / 随机出兵点 / 敌方基地) +3. 路径上的敌方防御塔 +4. 敌方主基地 + +> 说明:敌方单位优先级永远最高,保证单位不会无视敌人强行拆矿/拆塔;敌人击杀后,自动继续前往原定目标。 + +### 5.2 出兵目标机制(方案B) +核心操作规则,**无驻守指令、无全局切换、无单位选中**,完全通过「召唤前预设目标」实现分兵。 +1. 玩家在召唤单位前,先选择目标(进攻基地 / 进攻水晶矿 / 进攻随机出兵点); +2. 点击卡牌召唤后,**新出生的单位永久绑定该目标**,直到死亡; +3. 场上已存在的单位,目标不受后续按钮切换影响,始终执行出生时的任务; +4. 默认目标为「进攻敌方基地」,符合玩家基础直觉。 + +### 5.3 战斗行为示例 +1. 玩家选择目标「水晶矿」,召唤2个步兵,步兵出生后自动向水晶矿移动; +2. 途中遭遇敌方单位,步兵立刻停止前进,优先攻击敌人; +3. 敌人被击杀后,步兵继续前往水晶矿,攻击水晶推进己方进度; +4. 玩家切换目标为「进攻随机出兵点」,再次召唤1个重甲,重甲出生后自动前往出兵点; +5. 此前的2个步兵仍继续攻击水晶矿,不受目标切换影响,实现分兵作战; +6. 随机出兵点归属己方期间,按固定间隔从玩家出战卡组中随机生成单位,生成单位默认前往敌方主基地。 + +## 六、UI布局与操作 +### 6.1 整体布局 +采用「上战场、下操作」的竖版经典结构,战斗视野完整,拇指可覆盖全部操作区。 +┌──────────────────────────────┐ +│ │ +│ 战场区域(2.5D) │ +│ │ +│ │ +│ │ +├──────────────────────────────┤ +│ 攻城堡 打水晶 打出兵点 │ ← 目标选择按钮组 +├──────────────────────────────┤ +│ 步兵 射手 重甲 法师 │ ← 单位召唤卡牌面板 +└──────────────────────────────┘ + +### 6.2 目标选择按钮组 +- 位置:召唤卡牌面板正上方,横向并排3个按钮; +- 交互:同一时间仅一个选中,选中态高亮显示,未选中置灰; +- 逻辑:仅决定「下一次召唤单位的目标」,不影响场上已存活单位; +- 可选目标为「攻城堡」「打水晶」「打出兵点」; +- 默认选中「攻城堡」,开局无需额外操作即可出兵。 + +### 6.3 单位召唤卡牌面板 +- 位置:屏幕最底部,横向排列单位卡牌; +- 交互:点击卡牌消耗对应法力,单位在己方城堡附近生成; +- 信息:卡牌显示单位图标、召唤费用,费用不足时置灰不可点击。 + +### 6.4 标准操作流程 +1. 观察战场局势,决定当前波次的目标(抢水晶矿、抢随机出兵点或推基地); +2. 点击对应目标按钮,确认选中状态; +3. 点击单位卡牌,消耗法力召唤对应兵种; +4. 重复上述步骤,多批次分配不同目标的部队,形成多线作战。 + +## 七、单局对局流程 +1. **开局阶段(0~30秒)** + 双方基础法力回复,默认出兵冲基地试探;可选择抢先派兵争夺水晶矿或随机出兵点,建立经济或兵力优势。 + +2. **博弈阶段(30秒~1分30秒)** + 围绕水晶矿与随机出兵点展开争夺,搭配兵种组合,用前排+后排阵容推进;通过分兵同时牵制资源据点与主线。 + +3. **决胜阶段(1分30秒~3分钟)** + 积累足够兵力后集中推进,突破防御塔,集火敌方城堡;城堡血量归零则对局结束。 + 若3分钟未分胜负,按城堡剩余血量百分比判定胜负,防止无限拉扯。 + +## 八、核心策略博弈点 +1. **资源节奏**:攒法力出高费重甲,还是持续低费小兵压制; +2. **据点取舍**:优先抢水晶矿提升长期法力回复,还是抢随机出兵点获得额外兵力,或全力一波速攻基地; +3. **分兵策略**:同时争夺水晶矿和随机出兵点,还是集中兵力主攻一路; +4. **兵种搭配**:前排抗线+后排输出的组合,避免被范围伤害一波清场。 + +## 九、开发落地建议 +1. **寻路复用**:直接复用现有NavMesh寻路逻辑,仅需烘焙十字形可行走区域; +2. **AI简化**:保留原有单位战斗状态机,仅新增「出生目标绑定」字段,无需新增驻守状态; +3. **据点实现**:水晶矿与随机出兵点作为特殊可攻击目标,共用红蓝争夺进度数值;据点达到己方100%后开始产出,只有被对方完全推到0才翻转并转移产出;水晶矿提供法力回复加成,随机出兵点按费用权重从玩家出战卡组中自动生成单位; +4. **UI优先**:先实现底部目标按钮+召唤面板的核心交互,再扩展技能、特效等内容。 diff --git a/小游戏可行性方案.md b/小游戏可行性方案.md deleted file mode 100644 index 641af63..0000000 --- a/小游戏可行性方案.md +++ /dev/null @@ -1,386 +0,0 @@ -# 微信/抖音小游戏可行性方案:法师捕获召唤闯关 - -## 1. 项目定位 - -目标做一款适合微信小游戏、抖音小游戏的轻量 3D 闯关游戏。 - -核心卖点: - -- 玩家扮演法师。 -- 打倒或削弱敌人后,可以捕获敌人单位。 -- 捕获后的单位可在战斗中召唤出来协助作战。 -- 召唤单位消耗法力,法力会随时间自动恢复。 -- 以短关卡推进,适合碎片化游玩和广告变现。 - -一句话概括: - -> 轻量版“捕获 + 召唤 + 闯关”的 3D 卡通动作策略小游戏。 - -## 2. 核心玩法判断 - -这个方向可行,但不建议做成开放世界或复杂解谜大作。小游戏平台更适合: - -- 单局时间短。 -- 操作简单。 -- 目标明确。 -- 关卡推进快。 -- 内容可以持续扩展。 - -因此推荐形态是: - -> 俯视角 / 斜俯视角 3D 关卡制动作策略游戏。 - -玩家主要体验不是复杂操作,而是: - -- 走位躲避。 -- 普攻削血。 -- 捕获敌人。 -- 选择合适单位召唤。 -- 用有限法力解决当前战斗。 - -## 3. MVP 核心循环 - -第一版只验证核心是否好玩,不做过多外围系统。 - -基础循环: - -```text -进入关卡 -→ 遇到敌人 -→ 主角自动普攻削血 -→ 敌人进入可捕获状态 -→ 玩家捕获单位 -→ 消耗法力召唤已捕获单位 -→ 单位协助战斗 -→ 通关获得奖励 -→ 解锁/强化单位 -→ 进入下一关 -``` - -MVP 必须具备: - -1. 主角移动。 -2. 主角简单自动普攻。 -3. 敌人血量与死亡。 -4. 敌人残血捕获。 -5. 单位收集列表。 -6. 法力自动恢复。 -7. 消耗法力召唤单位。 -8. 至少 3 种敌人/可召唤单位。 -9. 至少 5 个测试关卡。 -10. 简单胜负条件。 - -## 4. 主角设计 - -### 4.1 主角是否需要攻击能力 - -需要,但第一版只做简单普攻。 - -理由: - -- 没有普攻,玩家开局或单位死亡后会失去行动能力。 -- 捕获系统需要玩家先削弱敌人。 -- 普攻能保证玩家持续参与,而不是只等法力恢复。 -- 主角不应成为主要输出,否则召唤系统价值会下降。 - -### 4.2 推荐定位 - -主角是“指挥型法师”: - -- 能自保。 -- 能削血。 -- 能补刀。 -- 真正的战斗主力来自召唤单位。 - -输出占比建议: - -| 来源 | 占比 | -|---|---:| -| 主角普攻 | 30% | -| 召唤单位 | 60% | -| 法术/道具 | 10% | - -### 4.3 第一版主角能力 - -- 移动:虚拟摇杆或拖拽移动。 -- 普攻:自动锁定最近敌人,发射魔法弹。 -- 捕获:敌人低血量时点击捕获按钮。 -- 召唤:选择已捕获单位,在目标位置放置。 - -暂不做复杂连招、装备技能树和多主动技能。 - -## 5. 法力系统 - -法力是召唤单位的核心资源。 - -### 5.1 基础规则 - -- 法力随时间自动恢复。 -- 不同单位消耗不同法力。 -- 法力有上限。 -- 召唤后立即扣除法力。 -- 强单位消耗高,弱单位消耗低。 - -### 5.2 推荐初始数值 - -| 参数 | 建议值 | -|---|---:| -| 初始法力上限 | 10 | -| 初始法力恢复 | 1 点 / 秒 | -| 小怪召唤消耗 | 2-3 | -| 远程单位消耗 | 3-4 | -| 坦克单位消耗 | 4-5 | -| 精英单位消耗 | 6-8 | - -### 5.3 设计目的 - -法力系统要制造选择: - -- 是马上召唤两个小怪? -- 还是攒法力召唤一个强力单位? -- 是用坦克挡伤害? -- 还是用远程单位补输出? - -## 6. 捕获系统 - -捕获是项目最有辨识度的玩法。 - -### 6.1 第一版捕获规则 - -推荐简单规则: - -- 敌人血量低于 30% 时进入可捕获状态。 -- 可捕获状态显示明显特效或图标。 -- 玩家点击捕获按钮后尝试捕获。 -- 普通敌人捕获成功率高。 -- 精英敌人捕获成功率低或需要通关后解锁。 - -### 6.2 MVP 简化方案 - -第一版可以先做确定性捕获: - -```text -敌人血量低于 30% → 点击捕获 → 100% 成功 -``` - -这样方便验证玩法闭环。后续再加入概率、品质、道具等复杂机制。 - -### 6.3 捕获后的单位 - -捕获后进入单位池: - -- 可以在当前关卡继续召唤。 -- 也可以进入关卡外图鉴。 -- 同类单位可以转化为升级材料。 - -## 7. 召唤单位设计 - -第一版建议只做 3-5 种单位,保证差异明显。 - -| 单位 | 定位 | 特点 | 法力消耗 | -|---|---|---|---:| -| 小史莱姆 | 低费近战 | 便宜、数量多、伤害低 | 2 | -| 蓝色怪物 | 近战战士 | 均衡输出与血量 | 3 | -| 石头怪 | 坦克 | 血厚、移速慢 | 5 | -| 弓箭手 | 远程输出 | 脆皮、站后排 | 4 | -| 冰霜怪 | 控制 | 攻击带减速 | 5 | - -MVP 可以先做: - -1. 近战小怪。 -2. 坦克小怪。 -3. 远程小怪。 - -只要三种单位的战斗感受不同,就能验证策略性。 - -## 8. 关卡结构 - -### 8.1 单关时长 - -小游戏适合短关卡: - -| 类型 | 时长 | -|---|---:| -| 普通关 | 1-2 分钟 | -| 精英关 | 2-3 分钟 | -| Boss 关 | 3-5 分钟 | - -### 8.2 第一版关卡类型 - -推荐先做闯关型: - -- 每关一个小地图。 -- 玩家从入口进入。 -- 击败全部敌人或击败 Boss 后通关。 -- 通关后获得金币、经验、单位碎片。 - -### 8.3 关卡变化方式 - -早期不需要大地图,靠以下内容制造变化: - -- 敌人组合变化。 -- 障碍物位置变化。 -- 地形大小变化。 -- 陷阱和机关。 -- Boss 技能变化。 - -## 9. 成长系统 - -第一版成长不要太重,避免拖慢开发。 - -### 9.1 法师成长 - -- 最大生命值。 -- 普攻伤害。 -- 法力上限。 -- 法力恢复速度。 -- 捕获范围。 - -### 9.2 单位成长 - -- 单位等级。 -- 生命值提升。 -- 攻击力提升。 -- 特殊效果增强。 - -### 9.3 图鉴系统 - -图鉴是很适合这个玩法的长期目标: - -- 收集过的单位会点亮图鉴。 -- 图鉴给少量属性加成。 -- 稀有单位作为关卡目标。 - -## 10. 广告变现设计 - -这个玩法可以做广告变现,尤其适合激励视频广告。 - -### 10.1 推荐广告点 - -| 广告位 | 说明 | 推荐程度 | -|---|---|---| -| 失败复活 | 死亡后看广告复活一次 | 高 | -| 通关奖励翻倍 | 通关后金币/材料翻倍 | 高 | -| 额外捕获机会 | 捕获失败后看广告再试一次 | 中高 | -| 每日奖励翻倍 | 每日登录奖励翻倍 | 中 | -| 体力/门票补充 | 体力不足时看广告补充 | 中 | -| 临时法力加速 | 本关法力恢复短时间提升 | 中 | - -### 10.2 不建议的广告 - -不建议做: - -- 关卡中强制插屏。 -- 每一两分钟强制广告。 -- 战斗中频繁打断操作。 - -小游戏前期更适合激励广告,让玩家主动选择看广告。 - -## 11. 技术可行性 - -### 11.1 适合小游戏的原因 - -- 玩法可以做成小地图短关卡。 -- 角色数量可控。 -- 3D 卡通低模风格适合性能控制。 -- 系统可以逐步扩展,不必一开始做大世界。 - -### 11.2 风险点 - -| 风险 | 说明 | 规避方式 | -|---|---|---| -| 操作复杂 | 移动、攻击、捕获、召唤同时存在 | 普攻自动化,玩家只管移动、捕获、召唤 | -| 单位太多导致混乱 | 召唤物数量过多会遮挡和卡顿 | 限制同时在场数量或总法力 | -| 关卡重复 | 只打怪容易腻 | 增加敌人组合、地形、Boss、词缀 | -| 平衡困难 | 捕获和召唤容易过强 | 用法力消耗、召唤上限、冷却控制 | -| 性能压力 | 小游戏平台性能有限 | 低模、少骨骼、少特效、限制同屏单位 | - -### 11.3 性能建议 - -- 采用低模 3D 卡通风格。 -- 同屏敌人 + 召唤物建议控制在 20 个以内。 -- 单位动画简单化。 -- 特效使用少量粒子或贴片动画。 -- 地图使用简单 Mesh / Cube / Plane 组合。 -- UI 和战斗逻辑尽量轻量。 - -## 12. 推荐开发阶段 - -### 阶段 1:核心原型 - -目标:验证“捕获 + 召唤”是否好玩。 - -内容: - -- 一个测试地图。 -- 一个主角。 -- 三种敌人。 -- 自动普攻。 -- 残血捕获。 -- 法力恢复。 -- 召唤单位。 -- 一个胜负流程。 - -### 阶段 2:关卡 MVP - -目标:做出可连续玩的体验。 - -内容: - -- 5-10 个关卡。 -- 1 个 Boss。 -- 简单成长系统。 -- 单位图鉴雏形。 -- 通关奖励。 - -### 阶段 3:小游戏商业化测试 - -目标:验证留存和广告点。 - -内容: - -- 失败复活广告。 -- 通关奖励翻倍广告。 -- 每日奖励。 -- 基础数据埋点。 - -### 阶段 4:内容扩展 - -目标:增强长期游玩。 - -内容: - -- 更多单位。 -- 更多 Boss。 -- 关卡词缀。 -- 每日挑战。 -- 单位升级和稀有度。 - -## 13. 当前项目建议 - -当前项目还处于早期,建议不要急着做完整游戏系统。 - -最优先事项: - -1. 确定角色控制方式。 -2. 做一个 40x40 的测试地图。 -3. 让蓝色怪物作为第一个敌人/召唤单位。 -4. 做主角自动普攻。 -5. 做敌人残血捕获。 -6. 做法力条和召唤按钮。 - -第一阶段目标不是好看,而是验证: - -> 玩家是否愿意为了捕获单位而继续打下一关。 - -## 14. 最终建议 - -这个项目方向可行,适合做小游戏。 - -建议坚持三个原则: - -1. **主角简单**:移动 + 自动普攻 + 捕获 + 召唤。 -2. **单位有差异**:近战、坦克、远程、控制逐步扩展。 -3. **关卡短平快**:1-3 分钟一局,适合广告和碎片化游玩。 - -第一版不要追求复杂剧情、大地图和大量角色。先把“捕获敌人并召唤出来战斗”的核心爽点做出来,再决定是否扩展为更大的项目。