CombatNode 相关逻辑补充
- CombatFinishForm:战斗节点的结算页面(杀死的敌人、获得的金币、获得的道具) - CombatSelectForm:战斗节点建造防御塔的选择 UI 以及对防御塔进行操作的 UI(只实现了建造逻辑,在 MapEntity 里做射线检测) - 构建了基本的防御塔三组件(ShooterMuzzleComp/BasicBearingComp/BasicBaseComp)和防御塔主控(DefenseTowerController)以及相应的实体类(DefenseTowerEntity)
This commit is contained in:
parent
92cca14503
commit
564817d752
|
|
@ -1,5 +1,5 @@
|
|||
# Id 策划备注 EntityId BaseHp BaseDamage Speed DropCoin
|
||||
# int int int int float int
|
||||
# 敌人Id 敌人实体Id 基础血量 基地伤害 移动速度 掉落硬币
|
||||
1 1001 500 5 1 5
|
||||
2 1002 5000 50 0.5 100
|
||||
# Id 策划备注 EntityId BaseHp BaseDamage Speed DropCoin DropGold DropPercent
|
||||
# int int int int float int int float
|
||||
# 敌人Id 敌人实体Id 基础血量 基地伤害 移动速度 掉落硬币 掉落金币 掉落概率
|
||||
1 1001 500 5 1 5 1 0.2
|
||||
2 1002 5000 50 0.5 100 20 0.8
|
||||
|
|
|
|||
|
|
@ -4,5 +4,7 @@
|
|||
101 测试枪口 TestMuzzle
|
||||
201 测试轴承 TestBearing
|
||||
301 测试底座 TestBase
|
||||
401 防御塔实体 TowerEntity
|
||||
501 普通子弹 NormalBullet
|
||||
1001 测试普通敌人 TestEnemy
|
||||
1002 测试Boss TestBoss
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
# Id 列1 AssetName UIGroupName AllowMultiInstance PauseCoveredUIForm
|
||||
# int string string bool bool
|
||||
# 界面编号 策划备注 资源名称 界面组名称 是否允许多个界面实例 是否暂停被其覆盖的界面
|
||||
1 弹出框 DialogForm Default True True
|
||||
100 主菜单 MenuForm Default False True
|
||||
101 设置 SettingForm Default False True
|
||||
102 关于 AboutForm Default False True
|
||||
110 主界面 MainForm Default False True
|
||||
111 仓库UI RepoForm Default False True
|
||||
112 详细信息 ItemDescForm Default True False
|
||||
130 事件UI EventForm Default False False
|
||||
140 战斗信息UI CombatInfoForm Default False False
|
||||
141 战斗结束UI CombatFinishForm Default False True
|
||||
150 商店UI ShopForm Default False True
|
||||
1 弹出框 DialogForm Overlay True True
|
||||
100 主菜单 MenuForm Medium False True
|
||||
101 设置 SettingForm Medium False True
|
||||
102 关于 AboutForm Medium False True
|
||||
110 主界面 MainForm Medium False True
|
||||
111 仓库UI RepoForm Medium False True
|
||||
112 详细信息 ItemDescForm Medium True False
|
||||
130 事件UI EventForm Medium False False
|
||||
140 战斗信息UI CombatInfoForm Medium False False
|
||||
141 战斗结束UI CombatFinishForm Medium False True
|
||||
142 战斗选择UI CombatSelectForm Overlay False False
|
||||
150 商店UI ShopForm Medium False True
|
||||
200 测试UI TestMenuForm Test False False
|
||||
|
|
|
|||
|
|
@ -149,8 +149,8 @@ SpriteRenderer:
|
|||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingLayerID: -1869315837
|
||||
m_SortingLayer: -4
|
||||
m_SortingOrder: 0
|
||||
m_Sprite: {fileID: 7482667652216324306, guid: 311925a002f4447b3a28927169b83ea6,
|
||||
type: 3}
|
||||
|
|
@ -402,8 +402,8 @@ TilemapRenderer:
|
|||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingLayerID: -1869315837
|
||||
m_SortingLayer: -4
|
||||
m_SortingOrder: 1
|
||||
m_ChunkSize: {x: 32, y: 32, z: 32}
|
||||
m_ChunkCullingBounds: {x: 0, y: 0, z: 0}
|
||||
|
|
@ -484,8 +484,8 @@ SpriteRenderer:
|
|||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingLayerID: -1869315837
|
||||
m_SortingLayer: -4
|
||||
m_SortingOrder: 0
|
||||
m_Sprite: {fileID: 7482667652216324306, guid: 311925a002f4447b3a28927169b83ea6,
|
||||
type: 3}
|
||||
|
|
@ -7181,8 +7181,8 @@ TilemapRenderer:
|
|||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingLayerID: -1869315837
|
||||
m_SortingLayer: -4
|
||||
m_SortingOrder: 0
|
||||
m_ChunkSize: {x: 32, y: 32, z: 32}
|
||||
m_ChunkCullingBounds: {x: 0, y: 0, z: 0}
|
||||
|
|
@ -7347,8 +7347,8 @@ SpriteRenderer:
|
|||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingLayerID: -1869315837
|
||||
m_SortingLayer: -4
|
||||
m_SortingOrder: 0
|
||||
m_Sprite: {fileID: 7482667652216324306, guid: 311925a002f4447b3a28927169b83ea6,
|
||||
type: 3}
|
||||
|
|
@ -8738,8 +8738,8 @@ TilemapRenderer:
|
|||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingLayerID: -1869315837
|
||||
m_SortingLayer: -4
|
||||
m_SortingOrder: 2
|
||||
m_ChunkSize: {x: 32, y: 32, z: 32}
|
||||
m_ChunkCullingBounds: {x: 0, y: 0, z: 0}
|
||||
|
|
|
|||
|
|
@ -149,8 +149,8 @@ SpriteRenderer:
|
|||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingLayerID: -1869315837
|
||||
m_SortingLayer: -4
|
||||
m_SortingOrder: 0
|
||||
m_Sprite: {fileID: 7482667652216324306, guid: 311925a002f4447b3a28927169b83ea6,
|
||||
type: 3}
|
||||
|
|
@ -402,8 +402,8 @@ TilemapRenderer:
|
|||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingLayerID: -1869315837
|
||||
m_SortingLayer: -4
|
||||
m_SortingOrder: 1
|
||||
m_ChunkSize: {x: 32, y: 32, z: 32}
|
||||
m_ChunkCullingBounds: {x: 0, y: 0, z: 0}
|
||||
|
|
@ -7082,8 +7082,8 @@ TilemapRenderer:
|
|||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingLayerID: -1869315837
|
||||
m_SortingLayer: -4
|
||||
m_SortingOrder: 0
|
||||
m_ChunkSize: {x: 32, y: 32, z: 32}
|
||||
m_ChunkCullingBounds: {x: 0, y: 0, z: 0}
|
||||
|
|
@ -7247,8 +7247,8 @@ SpriteRenderer:
|
|||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingLayerID: -1869315837
|
||||
m_SortingLayer: -4
|
||||
m_SortingOrder: 0
|
||||
m_Sprite: {fileID: 7482667652216324306, guid: 311925a002f4447b3a28927169b83ea6,
|
||||
type: 3}
|
||||
|
|
@ -8398,8 +8398,8 @@ TilemapRenderer:
|
|||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingLayerID: -1869315837
|
||||
m_SortingLayer: -4
|
||||
m_SortingOrder: 2
|
||||
m_ChunkSize: {x: 32, y: 32, z: 32}
|
||||
m_ChunkCullingBounds: {x: 0, y: 0, z: 0}
|
||||
|
|
|
|||
|
|
@ -149,8 +149,8 @@ SpriteRenderer:
|
|||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingLayerID: -1869315837
|
||||
m_SortingLayer: -4
|
||||
m_SortingOrder: 0
|
||||
m_Sprite: {fileID: 7482667652216324306, guid: 311925a002f4447b3a28927169b83ea6,
|
||||
type: 3}
|
||||
|
|
@ -382,8 +382,8 @@ TilemapRenderer:
|
|||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingLayerID: -1869315837
|
||||
m_SortingLayer: -4
|
||||
m_SortingOrder: 1
|
||||
m_ChunkSize: {x: 32, y: 32, z: 32}
|
||||
m_ChunkCullingBounds: {x: 0, y: 0, z: 0}
|
||||
|
|
@ -464,8 +464,8 @@ SpriteRenderer:
|
|||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingLayerID: -1869315837
|
||||
m_SortingLayer: -4
|
||||
m_SortingOrder: 0
|
||||
m_Sprite: {fileID: 7482667652216324306, guid: 311925a002f4447b3a28927169b83ea6,
|
||||
type: 3}
|
||||
|
|
@ -7161,8 +7161,8 @@ TilemapRenderer:
|
|||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingLayerID: -1869315837
|
||||
m_SortingLayer: -4
|
||||
m_SortingOrder: 0
|
||||
m_ChunkSize: {x: 32, y: 32, z: 32}
|
||||
m_ChunkCullingBounds: {x: 0, y: 0, z: 0}
|
||||
|
|
@ -7327,8 +7327,8 @@ SpriteRenderer:
|
|||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingLayerID: -1869315837
|
||||
m_SortingLayer: -4
|
||||
m_SortingOrder: 0
|
||||
m_Sprite: {fileID: 7482667652216324306, guid: 311925a002f4447b3a28927169b83ea6,
|
||||
type: 3}
|
||||
|
|
@ -8608,8 +8608,8 @@ TilemapRenderer:
|
|||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingLayerID: -1869315837
|
||||
m_SortingLayer: -4
|
||||
m_SortingOrder: 2
|
||||
m_ChunkSize: {x: 32, y: 32, z: 32}
|
||||
m_ChunkCullingBounds: {x: 0, y: 0, z: 0}
|
||||
|
|
|
|||
|
|
@ -149,8 +149,8 @@ SpriteRenderer:
|
|||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingLayerID: -1869315837
|
||||
m_SortingLayer: -4
|
||||
m_SortingOrder: 0
|
||||
m_Sprite: {fileID: 7482667652216324306, guid: 311925a002f4447b3a28927169b83ea6,
|
||||
type: 3}
|
||||
|
|
@ -382,8 +382,8 @@ TilemapRenderer:
|
|||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingLayerID: -1869315837
|
||||
m_SortingLayer: -4
|
||||
m_SortingOrder: 1
|
||||
m_ChunkSize: {x: 32, y: 32, z: 32}
|
||||
m_ChunkCullingBounds: {x: 0, y: 0, z: 0}
|
||||
|
|
@ -7062,8 +7062,8 @@ TilemapRenderer:
|
|||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingLayerID: -1869315837
|
||||
m_SortingLayer: -4
|
||||
m_SortingOrder: 0
|
||||
m_ChunkSize: {x: 32, y: 32, z: 32}
|
||||
m_ChunkCullingBounds: {x: 0, y: 0, z: 0}
|
||||
|
|
@ -7227,8 +7227,8 @@ SpriteRenderer:
|
|||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingLayerID: -1869315837
|
||||
m_SortingLayer: -4
|
||||
m_SortingOrder: 0
|
||||
m_Sprite: {fileID: 7482667652216324306, guid: 311925a002f4447b3a28927169b83ea6,
|
||||
type: 3}
|
||||
|
|
@ -8258,8 +8258,8 @@ TilemapRenderer:
|
|||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingLayerID: -1869315837
|
||||
m_SortingLayer: -4
|
||||
m_SortingOrder: 2
|
||||
m_ChunkSize: {x: 32, y: 32, z: 32}
|
||||
m_ChunkCullingBounds: {x: 0, y: 0, z: 0}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,103 @@
|
|||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &8436939006141951225
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1601478963695598935}
|
||||
- component: {fileID: 3440631169294921595}
|
||||
- component: {fileID: 712345678901234567}
|
||||
m_Layer: 0
|
||||
m_Name: NormalBullet
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &1601478963695598935
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8436939006141951225}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 0.4, y: 0.4, z: 0.4}
|
||||
m_ConstrainProportionsScale: 1
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!212 &3440631169294921595
|
||||
SpriteRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8436939006141951225}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 0
|
||||
m_ReceiveShadows: 0
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 1
|
||||
m_ReflectionProbeUsage: 1
|
||||
m_RayTracingMode: 0
|
||||
m_RayTraceProcedural: 0
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 0
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: -1805677833
|
||||
m_SortingLayer: -1
|
||||
m_SortingOrder: 0
|
||||
m_Sprite: {fileID: -2413806693520163455, guid: a86470a33a6bf42c4b3595704624658b,
|
||||
type: 3}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_FlipX: 0
|
||||
m_FlipY: 0
|
||||
m_DrawMode: 0
|
||||
m_Size: {x: 1, y: 1}
|
||||
m_AdaptiveModeThreshold: 0.5
|
||||
m_SpriteTileMode: 0
|
||||
m_WasSpriteAssigned: 1
|
||||
m_MaskInteraction: 0
|
||||
m_SpriteSortPoint: 0
|
||||
--- !u!114 &712345678901234567
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8436939006141951225}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 81f9627601c091545a9b92fc7ef41f04, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
_defaultSpeed: 12
|
||||
_hitDistance: 0.15
|
||||
_maxLifetime: 3
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0739c0ecf05e8694186c640e13c346e9
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -71,8 +71,8 @@ SpriteRenderer:
|
|||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: -1869315837
|
||||
m_SortingLayer: 1
|
||||
m_SortingLayerID: -1805677833
|
||||
m_SortingLayer: -1
|
||||
m_SortingOrder: 0
|
||||
m_Sprite: {fileID: -2413806693520163455, guid: a86470a33a6bf42c4b3595704624658b,
|
||||
type: 3}
|
||||
|
|
|
|||
|
|
@ -71,8 +71,8 @@ SpriteRenderer:
|
|||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: -1869315837
|
||||
m_SortingLayer: 1
|
||||
m_SortingLayerID: -1805677833
|
||||
m_SortingLayer: -1
|
||||
m_SortingOrder: 0
|
||||
m_Sprite: {fileID: -2413806693520163455, guid: a86470a33a6bf42c4b3595704624658b,
|
||||
type: 3}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,374 @@
|
|||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &931511988754511484
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 7637292285124107611}
|
||||
- component: {fileID: 7170949026143225663}
|
||||
- component: {fileID: 6677074338184329456}
|
||||
m_Layer: 0
|
||||
m_Name: Muzzle
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &7637292285124107611
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 931511988754511484}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 5517541809701307552}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!212 &7170949026143225663
|
||||
SpriteRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 931511988754511484}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 0
|
||||
m_ReceiveShadows: 0
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 1
|
||||
m_ReflectionProbeUsage: 1
|
||||
m_RayTracingMode: 0
|
||||
m_RayTraceProcedural: 0
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 0
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: -1805677833
|
||||
m_SortingLayer: -1
|
||||
m_SortingOrder: 2
|
||||
m_Sprite: {fileID: 7482667652216324306, guid: 75f5f34dc1b5347e0b8351032682f224,
|
||||
type: 3}
|
||||
m_Color: {r: 1, g: 0.40566027, b: 0.40566027, a: 1}
|
||||
m_FlipX: 0
|
||||
m_FlipY: 0
|
||||
m_DrawMode: 0
|
||||
m_Size: {x: 1, y: 1}
|
||||
m_AdaptiveModeThreshold: 0.5
|
||||
m_SpriteTileMode: 0
|
||||
m_WasSpriteAssigned: 1
|
||||
m_MaskInteraction: 0
|
||||
m_SpriteSortPoint: 0
|
||||
--- !u!114 &6677074338184329456
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 931511988754511484}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 20a2c131403122146a41148cb72fcd43, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
_attackDamage: 10
|
||||
_attackMethodType: 1
|
||||
_bulletTypeId: 501
|
||||
_muzzlePoint: {fileID: 0}
|
||||
_bulletSpeed: 12
|
||||
--- !u!1 &1221576993898367501
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 6791423131335728073}
|
||||
- component: {fileID: 8183383920109690380}
|
||||
- component: {fileID: 3255949411223456789}
|
||||
m_Layer: 0
|
||||
m_Name: TowerEntity
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &6791423131335728073
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1221576993898367501}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 6754288440384573513}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &8183383920109690380
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1221576993898367501}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8b33f7d4136745b8bbf516ec80495b80, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
_muzzleComp: {fileID: 6677074338184329456}
|
||||
_bearingComp: {fileID: 3784879822105863121}
|
||||
_baseComp: {fileID: 9032148832249544824}
|
||||
_scanOrigin: {fileID: 6791423131335728073}
|
||||
_retargetInterval: 0.1
|
||||
_autoUpdate: 0
|
||||
--- !u!114 &3255949411223456789
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1221576993898367501}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: d87f56efd9024709a3baf8ef8a6f4fd3, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!1 &2017874305906296486
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 5517541809701307552}
|
||||
- component: {fileID: 7775658596736532582}
|
||||
- component: {fileID: 3784879822105863121}
|
||||
m_Layer: 0
|
||||
m_Name: Bearing
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &5517541809701307552
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2017874305906296486}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 7637292285124107611}
|
||||
m_Father: {fileID: 6754288440384573513}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!212 &7775658596736532582
|
||||
SpriteRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2017874305906296486}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 0
|
||||
m_ReceiveShadows: 0
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 1
|
||||
m_ReflectionProbeUsage: 1
|
||||
m_RayTracingMode: 0
|
||||
m_RayTraceProcedural: 0
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 0
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: -1805677833
|
||||
m_SortingLayer: -1
|
||||
m_SortingOrder: 1
|
||||
m_Sprite: {fileID: -2413806693520163455, guid: a86470a33a6bf42c4b3595704624658b,
|
||||
type: 3}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_FlipX: 0
|
||||
m_FlipY: 0
|
||||
m_DrawMode: 0
|
||||
m_Size: {x: 1, y: 1}
|
||||
m_AdaptiveModeThreshold: 0.5
|
||||
m_SpriteTileMode: 0
|
||||
m_WasSpriteAssigned: 1
|
||||
m_MaskInteraction: 0
|
||||
m_SpriteSortPoint: 0
|
||||
--- !u!114 &3784879822105863121
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2017874305906296486}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 6c8b3d58a8bb41f493706b40fc451558, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
_rotateSpeed: 180
|
||||
_attackRange: 5
|
||||
_aimToleranceAngle: 2
|
||||
_rotateRoot: {fileID: 0}
|
||||
_yawOnly: 1
|
||||
--- !u!1 &4867507345079921359
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 6754288440384573513}
|
||||
- component: {fileID: 6516390239457838709}
|
||||
- component: {fileID: 9032148832249544824}
|
||||
m_Layer: 0
|
||||
m_Name: Base
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &6754288440384573513
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4867507345079921359}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 5517541809701307552}
|
||||
m_Father: {fileID: 6791423131335728073}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!212 &6516390239457838709
|
||||
SpriteRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4867507345079921359}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 0
|
||||
m_ReceiveShadows: 0
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 1
|
||||
m_ReflectionProbeUsage: 1
|
||||
m_RayTracingMode: 0
|
||||
m_RayTraceProcedural: 0
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 0
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: -1805677833
|
||||
m_SortingLayer: -1
|
||||
m_SortingOrder: 0
|
||||
m_Sprite: {fileID: 7482667652216324306, guid: 311925a002f4447b3a28927169b83ea6,
|
||||
type: 3}
|
||||
m_Color: {r: 0.3584906, g: 0.3584906, b: 0.3584906, a: 1}
|
||||
m_FlipX: 0
|
||||
m_FlipY: 0
|
||||
m_DrawMode: 0
|
||||
m_Size: {x: 1, y: 1}
|
||||
m_AdaptiveModeThreshold: 0.5
|
||||
m_SpriteTileMode: 0
|
||||
m_WasSpriteAssigned: 1
|
||||
m_MaskInteraction: 0
|
||||
m_SpriteSortPoint: 0
|
||||
--- !u!114 &9032148832249544824
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4867507345079921359}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 11e3dd172f9d486194b6ca5d7f4dd5e6, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
_attackSpeed: 1
|
||||
_attackPropertyType: 1
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 08da5e1139e6a0c48913b2b093181690
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
using GeometryTD.Definition;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
public class BasicBaseComp : MonoBehaviour
|
||||
{
|
||||
[SerializeField] [Min(0.01f)] private float _attackSpeed = 1f;
|
||||
[SerializeField] private AttackPropertyType _attackPropertyType = AttackPropertyType.Physics;
|
||||
|
||||
private float _cooldownRemaining;
|
||||
|
||||
public float AttackSpeed => _attackSpeed;
|
||||
public AttackPropertyType AttackPropertyType => _attackPropertyType;
|
||||
public bool CanAttack => _cooldownRemaining <= 0f;
|
||||
|
||||
public void OnInit(float attackSpeed, AttackPropertyType attackPropertyType)
|
||||
{
|
||||
_attackSpeed = Mathf.Max(0.01f, attackSpeed);
|
||||
_attackPropertyType = attackPropertyType;
|
||||
_cooldownRemaining = 0f;
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
_attackSpeed = 1f;
|
||||
_attackPropertyType = AttackPropertyType.None;
|
||||
_cooldownRemaining = 0f;
|
||||
}
|
||||
|
||||
public void Tick(float deltaTime)
|
||||
{
|
||||
if (_cooldownRemaining <= 0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_cooldownRemaining = Mathf.Max(0f, _cooldownRemaining - Mathf.Max(0f, deltaTime));
|
||||
}
|
||||
|
||||
public bool TryAttack(BasicBearingComp bearingComp, ShooterMuzzleComp shooterMuzzleComp, Transform target, float deltaTime)
|
||||
{
|
||||
if (bearingComp == null || shooterMuzzleComp == null || target == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Tick(deltaTime);
|
||||
bool isAligned = bearingComp.TrackTarget(target, deltaTime);
|
||||
if (!isAligned || !CanAttack)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool fired = shooterMuzzleComp.Attack(target, _attackPropertyType);
|
||||
if (!fired)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_cooldownRemaining = 1f / _attackSpeed;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 11e3dd172f9d486194b6ca5d7f4dd5e6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
public class BasicBearingComp : MonoBehaviour
|
||||
{
|
||||
[SerializeField] [Min(1f)] private float _rotateSpeed = 180f;
|
||||
[SerializeField] [Min(0.1f)] private float _attackRange = 5f;
|
||||
[SerializeField] [Min(0.1f)] private float _aimToleranceAngle = 2f;
|
||||
[SerializeField] private Transform _rotateRoot;
|
||||
[SerializeField] private bool _yawOnly = true;
|
||||
|
||||
public float RotateSpeed => _rotateSpeed;
|
||||
public float AttackRange => _attackRange;
|
||||
public float AimToleranceAngle => _aimToleranceAngle;
|
||||
|
||||
public void OnInit(float rotateSpeed, float attackRange = 5f)
|
||||
{
|
||||
_rotateSpeed = Mathf.Max(1f, rotateSpeed);
|
||||
_attackRange = Mathf.Max(0.1f, attackRange);
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
_rotateSpeed = 1f;
|
||||
_attackRange = 5f;
|
||||
}
|
||||
|
||||
public bool IsTargetInRange(Transform target, Transform origin = null)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Transform originTransform = origin != null ? origin : transform;
|
||||
Vector3 delta = target.position - originTransform.position;
|
||||
return delta.sqrMagnitude <= _attackRange * _attackRange;
|
||||
}
|
||||
|
||||
public bool TrackTarget(Transform target, float deltaTime)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Transform rotateRoot = _rotateRoot != null ? _rotateRoot : transform;
|
||||
Vector3 toTarget = target.position - rotateRoot.position;
|
||||
if (_yawOnly)
|
||||
{
|
||||
toTarget.y = 0f;
|
||||
}
|
||||
|
||||
if (toTarget.sqrMagnitude <= Mathf.Epsilon)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Quaternion targetRotation = Quaternion.LookRotation(toTarget.normalized, Vector3.up);
|
||||
float rotateAngle = _rotateSpeed * Mathf.Max(0f, deltaTime);
|
||||
rotateRoot.rotation = Quaternion.RotateTowards(rotateRoot.rotation, targetRotation, rotateAngle);
|
||||
return IsTargetAligned(target);
|
||||
}
|
||||
|
||||
public bool IsTargetAligned(Transform target)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Transform rotateRoot = _rotateRoot != null ? _rotateRoot : transform;
|
||||
Vector3 toTarget = target.position - rotateRoot.position;
|
||||
if (_yawOnly)
|
||||
{
|
||||
toTarget.y = 0f;
|
||||
}
|
||||
|
||||
if (toTarget.sqrMagnitude <= Mathf.Epsilon)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
float angle = Vector3.Angle(rotateRoot.forward, toTarget.normalized);
|
||||
return angle <= _aimToleranceAngle;
|
||||
}
|
||||
|
||||
public bool TryAttack(ShooterMuzzleComp shooterMuzzleComp, Transform target, float deltaTime)
|
||||
{
|
||||
if (shooterMuzzleComp == null || target == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsTargetInRange(target))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isAligned = TrackTarget(target, deltaTime);
|
||||
if (!isAligned)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return shooterMuzzleComp.Attack(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6c8b3d58a8bb41f493706b40fc451558
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
using GeometryTD.Definition;
|
||||
using GeometryTD.Entity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
public class DefenseTowerController : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private ShooterMuzzleComp _muzzleComp;
|
||||
[SerializeField] private BasicBearingComp _bearingComp;
|
||||
[SerializeField] private BasicBaseComp _baseComp;
|
||||
[SerializeField] private Transform _scanOrigin;
|
||||
[SerializeField] [Min(0.02f)] private float _retargetInterval = 0.1f;
|
||||
[SerializeField] private bool _autoUpdate = true;
|
||||
|
||||
private Transform _currentTarget;
|
||||
private float _retargetTimer;
|
||||
|
||||
public Transform CurrentTarget => _currentTarget;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
ResolveComponents();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!_autoUpdate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OnUpdate(Time.deltaTime);
|
||||
}
|
||||
|
||||
public void OnInit(DefenseTowerStatsData stats)
|
||||
{
|
||||
ResolveComponents();
|
||||
if (stats == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_muzzleComp?.OnInit(stats.AttackDamage, stats.AttackMethodType);
|
||||
_bearingComp?.OnInit(stats.RotateSpeed, stats.AttackRange);
|
||||
_baseComp?.OnInit(stats.AttackSpeed, stats.AttackPropertyType);
|
||||
_currentTarget = null;
|
||||
_retargetTimer = 0f;
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
_currentTarget = null;
|
||||
_retargetTimer = 0f;
|
||||
_muzzleComp?.OnReset();
|
||||
_bearingComp?.OnReset();
|
||||
_baseComp?.OnReset();
|
||||
}
|
||||
|
||||
public void SetAutoUpdate(bool autoUpdate)
|
||||
{
|
||||
_autoUpdate = autoUpdate;
|
||||
}
|
||||
|
||||
public void SetTarget(Transform target)
|
||||
{
|
||||
_currentTarget = target;
|
||||
}
|
||||
|
||||
public void ClearTarget()
|
||||
{
|
||||
_currentTarget = null;
|
||||
}
|
||||
|
||||
public void OnUpdate(float deltaTime)
|
||||
{
|
||||
if (!HasCoreComponents())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsTargetValid(_currentTarget) || !_bearingComp.IsTargetInRange(_currentTarget, _scanOrigin))
|
||||
{
|
||||
TryRetarget(deltaTime);
|
||||
}
|
||||
|
||||
if (_currentTarget == null)
|
||||
{
|
||||
_baseComp.Tick(deltaTime);
|
||||
return;
|
||||
}
|
||||
|
||||
_baseComp.TryAttack(_bearingComp, _muzzleComp, _currentTarget, deltaTime);
|
||||
}
|
||||
|
||||
private void TryRetarget(float deltaTime)
|
||||
{
|
||||
_retargetTimer -= Mathf.Max(0f, deltaTime);
|
||||
if (_retargetTimer > 0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_retargetTimer = _retargetInterval;
|
||||
_currentTarget = FindNearestEnemyTarget();
|
||||
}
|
||||
|
||||
private Transform FindNearestEnemyTarget()
|
||||
{
|
||||
Vector3 origin = _scanOrigin != null ? _scanOrigin.position : transform.position;
|
||||
float maxRange = _bearingComp.AttackRange;
|
||||
float maxRangeSquared = maxRange * maxRange;
|
||||
|
||||
EnemyEntity bestEnemy = null;
|
||||
float bestDistanceSquared = float.MaxValue;
|
||||
for (int i = 0; i < EnemyEntity.ActiveEnemies.Count; i++)
|
||||
{
|
||||
EnemyEntity enemy = EnemyEntity.ActiveEnemies[i];
|
||||
if (enemy == null || !enemy.isActiveAndEnabled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Transform enemyTransform = enemy.transform;
|
||||
Vector3 delta = enemyTransform.position - origin;
|
||||
float distanceSquared = delta.sqrMagnitude;
|
||||
if (distanceSquared > maxRangeSquared || distanceSquared >= bestDistanceSquared)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bestDistanceSquared = distanceSquared;
|
||||
bestEnemy = enemy;
|
||||
}
|
||||
|
||||
return bestEnemy != null ? bestEnemy.transform : null;
|
||||
}
|
||||
|
||||
private void ResolveComponents()
|
||||
{
|
||||
if (_muzzleComp == null)
|
||||
{
|
||||
_muzzleComp = GetComponent<ShooterMuzzleComp>();
|
||||
}
|
||||
|
||||
if (_bearingComp == null)
|
||||
{
|
||||
_bearingComp = GetComponent<BasicBearingComp>();
|
||||
}
|
||||
|
||||
if (_baseComp == null)
|
||||
{
|
||||
_baseComp = GetComponent<BasicBaseComp>();
|
||||
}
|
||||
}
|
||||
|
||||
private bool HasCoreComponents()
|
||||
{
|
||||
return _muzzleComp != null && _bearingComp != null && _baseComp != null;
|
||||
}
|
||||
|
||||
private static bool IsTargetValid(Transform target)
|
||||
{
|
||||
return target != null && target.gameObject.activeInHierarchy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8b33f7d4136745b8bbf516ec80495b80
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
using GeometryTD.Definition;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
public interface IDamageReceiver
|
||||
{
|
||||
void TakeDamage(int damage, AttackPropertyType attackPropertyType);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0b8c22c0144cd4e4c887c3cf49ba35a7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
using UnityEngine;
|
||||
using GeometryTD.Definition;
|
||||
using GeometryTD.Entity.EntityData;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
public class ShooterBullet : MonoBehaviour
|
||||
{
|
||||
[SerializeField] [Min(0.1f)] private float _defaultSpeed = 12f;
|
||||
[SerializeField] [Min(0.02f)] private float _hitDistance = 0.15f;
|
||||
[SerializeField] [Min(0.1f)] private float _maxLifetime = 3f;
|
||||
|
||||
private Transform _target;
|
||||
private float _speed;
|
||||
private int _damage;
|
||||
private AttackPropertyType _attackPropertyType;
|
||||
private float _lifetime;
|
||||
private float _runtimeMaxLifetime;
|
||||
private bool _isRunning;
|
||||
private bool _despawnRequested;
|
||||
|
||||
public void OnShow(BulletData bulletData)
|
||||
{
|
||||
_target = bulletData != null ? bulletData.Target : null;
|
||||
_damage = bulletData != null ? Mathf.Max(0, bulletData.Damage) : 0;
|
||||
_attackPropertyType = bulletData != null ? bulletData.AttackPropertyType : AttackPropertyType.None;
|
||||
_speed = bulletData != null && bulletData.Speed > 0f ? bulletData.Speed : _defaultSpeed;
|
||||
_runtimeMaxLifetime = bulletData != null && bulletData.MaxLifetime > 0f ? bulletData.MaxLifetime : _maxLifetime;
|
||||
_lifetime = 0f;
|
||||
_despawnRequested = false;
|
||||
_isRunning = _target != null;
|
||||
if (!_isRunning)
|
||||
{
|
||||
_despawnRequested = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
_target = null;
|
||||
_speed = 0f;
|
||||
_damage = 0;
|
||||
_attackPropertyType = AttackPropertyType.None;
|
||||
_lifetime = 0f;
|
||||
_runtimeMaxLifetime = _maxLifetime;
|
||||
_isRunning = false;
|
||||
_despawnRequested = false;
|
||||
}
|
||||
|
||||
public void Tick(float deltaTime)
|
||||
{
|
||||
if (!_isRunning)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_lifetime += Mathf.Max(0f, deltaTime);
|
||||
if (_lifetime >= _runtimeMaxLifetime || _target == null)
|
||||
{
|
||||
_isRunning = false;
|
||||
_despawnRequested = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 toTarget = _target.position - transform.position;
|
||||
float hitDistanceSquared = _hitDistance * _hitDistance;
|
||||
if (toTarget.sqrMagnitude <= hitDistanceSquared)
|
||||
{
|
||||
HitTarget();
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 forward = toTarget.normalized;
|
||||
float moveDistance = _speed * Mathf.Max(0f, deltaTime);
|
||||
if (moveDistance * moveDistance >= toTarget.sqrMagnitude)
|
||||
{
|
||||
transform.position = _target.position;
|
||||
HitTarget();
|
||||
return;
|
||||
}
|
||||
|
||||
transform.position += forward * moveDistance;
|
||||
transform.forward = forward;
|
||||
}
|
||||
|
||||
public bool TryConsumeDespawnRequest()
|
||||
{
|
||||
if (!_despawnRequested)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_despawnRequested = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void HitTarget()
|
||||
{
|
||||
if (_target == null)
|
||||
{
|
||||
_isRunning = false;
|
||||
_despawnRequested = true;
|
||||
return;
|
||||
}
|
||||
|
||||
MonoBehaviour[] components = _target.GetComponentsInParent<MonoBehaviour>();
|
||||
for (int i = 0; i < components.Length; i++)
|
||||
{
|
||||
if (components[i] is IDamageReceiver damageReceiver)
|
||||
{
|
||||
damageReceiver.TakeDamage(_damage, _attackPropertyType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_isRunning = false;
|
||||
_despawnRequested = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 81f9627601c091545a9b92fc7ef41f04
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
using GeometryTD.Definition;
|
||||
using GeometryTD.Entity.EntityData;
|
||||
using GeometryTD;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
public class ShooterMuzzleComp : MonoBehaviour
|
||||
{
|
||||
[SerializeField] [Min(1f)] private int _attackDamage = 10;
|
||||
[SerializeField] private AttackMethodType _attackMethodType = AttackMethodType.NormalBullet;
|
||||
[SerializeField] [Min(1)] private int _bulletTypeId = 501;
|
||||
[SerializeField] private Transform _muzzlePoint;
|
||||
[SerializeField] [Min(0.1f)] private float _bulletSpeed = 12f;
|
||||
|
||||
public int AttackDamage => _attackDamage;
|
||||
public AttackMethodType AttackMethodType => _attackMethodType;
|
||||
|
||||
public void OnInit(int attackDamage, AttackMethodType attackMethodType = AttackMethodType.NormalBullet, int bulletTypeId = 501)
|
||||
{
|
||||
_attackDamage = Mathf.Max(1, attackDamage);
|
||||
_attackMethodType = attackMethodType;
|
||||
_bulletTypeId = Mathf.Max(1, bulletTypeId);
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
_attackDamage = 1;
|
||||
_attackMethodType = AttackMethodType.None;
|
||||
_bulletTypeId = 501;
|
||||
}
|
||||
|
||||
public bool Attack(Transform target)
|
||||
{
|
||||
return Attack(target, AttackPropertyType.None);
|
||||
}
|
||||
|
||||
public bool Attack(Transform target, AttackPropertyType attackPropertyType)
|
||||
{
|
||||
if (_attackMethodType != AttackMethodType.NormalBullet)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Transform spawnPoint = _muzzlePoint != null ? _muzzlePoint : transform;
|
||||
int bulletEntityId = GameEntry.Entity.GenerateSerialId();
|
||||
BulletData bulletData = new BulletData(
|
||||
bulletEntityId,
|
||||
_bulletTypeId,
|
||||
spawnPoint.position,
|
||||
spawnPoint.rotation,
|
||||
target,
|
||||
_attackDamage,
|
||||
_bulletSpeed,
|
||||
attackPropertyType);
|
||||
GameEntry.Entity.ShowBullet(bulletData);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 20a2c131403122146a41148cb72fcd43
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -20,7 +20,7 @@ namespace GeometryTD.CustomComponent
|
|||
}
|
||||
|
||||
private EntityComponent _entity;
|
||||
private CombatInfoFormUseCase _useCase;
|
||||
private CombatInfoFormUseCase _combatInfoFormUseCase;
|
||||
private int _loadingMapEntityId;
|
||||
private int _loadedMapEntityId;
|
||||
private int? _loadingCombatInfoFormSerialId;
|
||||
|
|
@ -63,7 +63,7 @@ namespace GeometryTD.CustomComponent
|
|||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -113,7 +113,8 @@ namespace GeometryTD.CustomComponent
|
|||
return EventHandleStatus.Ignored;
|
||||
}
|
||||
|
||||
if (args.EntityLogicType != typeof(MapEntity) || args.Entity == null || args.Entity.Id != _loadingMapEntityId)
|
||||
if (args.EntityLogicType != typeof(MapEntity) || args.Entity == null ||
|
||||
args.Entity.Id != _loadingMapEntityId)
|
||||
{
|
||||
return EventHandleStatus.Ignored;
|
||||
}
|
||||
|
|
@ -248,10 +249,10 @@ namespace GeometryTD.CustomComponent
|
|||
private bool TryOpenCombatInfoForm(out string errorMessage)
|
||||
{
|
||||
errorMessage = null;
|
||||
if (_useCase == null)
|
||||
if (_combatInfoFormUseCase == null)
|
||||
{
|
||||
_useCase = new CombatInfoFormUseCase();
|
||||
GameEntry.UIRouter.BindUIUseCase(UIFormType.CombatInfoForm, _useCase);
|
||||
_combatInfoFormUseCase = new CombatInfoFormUseCase();
|
||||
GameEntry.UIRouter.BindUIUseCase(UIFormType.CombatInfoForm, _combatInfoFormUseCase);
|
||||
}
|
||||
|
||||
int? serialId = GameEntry.UIRouter.OpenUI(UIFormType.CombatInfoForm);
|
||||
|
|
@ -273,4 +274,4 @@ namespace GeometryTD.CustomComponent
|
|||
_isCombatInfoFormReady = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -27,11 +27,16 @@ namespace GeometryTD.CustomComponent
|
|||
|
||||
private bool _runtimeInitialized;
|
||||
private int _currentCoin;
|
||||
private int _currentGold;
|
||||
|
||||
public LevelThemeType CurrentThemeType { get; private set; }
|
||||
public DRLevel CurrentLevel { get; private set; }
|
||||
public int CurrentCoin => _currentCoin;
|
||||
public int CurrentGold => _currentGold;
|
||||
public bool CanEndCombat => _combatScheduler.CanEndCombat;
|
||||
public int LastDefeatedEnemyCount { get; private set; }
|
||||
public int LastGainedCoin { get; private set; }
|
||||
public int LastGainedGold { get; private set; }
|
||||
|
||||
public int CurrentLevelPhaseCount
|
||||
{
|
||||
|
|
@ -66,6 +71,10 @@ namespace GeometryTD.CustomComponent
|
|||
CurrentThemeType = themeType;
|
||||
CurrentLevel = null;
|
||||
_currentCoin = 0;
|
||||
_currentGold = 0;
|
||||
LastDefeatedEnemyCount = 0;
|
||||
LastGainedCoin = 0;
|
||||
LastGainedGold = 0;
|
||||
_levelsById.Clear();
|
||||
_phasesByLevelId.Clear();
|
||||
_spawnEntriesByPhaseId.Clear();
|
||||
|
|
@ -200,13 +209,21 @@ namespace GeometryTD.CustomComponent
|
|||
|
||||
CurrentLevel = selectedLevel;
|
||||
_currentCoin = Mathf.Max(0, selectedLevel.StartCoin);
|
||||
_currentGold = 0;
|
||||
LastDefeatedEnemyCount = 0;
|
||||
LastGainedCoin = 0;
|
||||
LastGainedGold = 0;
|
||||
_combatScheduler.Start(selectedLevel, phaseList, _selectedSpawnEntriesByPhaseId);
|
||||
GameEntry.Event.Fire(this, NodeEnterEventArgs.Create());
|
||||
}
|
||||
|
||||
public void EndCombat()
|
||||
{
|
||||
LastDefeatedEnemyCount = _combatScheduler.DefeatedEnemyCount;
|
||||
LastGainedCoin = _combatScheduler.GainedCoin;
|
||||
LastGainedGold = _combatScheduler.GainedGold;
|
||||
_currentCoin = 0;
|
||||
_currentGold = 0;
|
||||
GameEntry.Event.Fire(this, NodeCompleteEventArgs.Create());
|
||||
}
|
||||
|
||||
|
|
@ -229,9 +246,54 @@ namespace GeometryTD.CustomComponent
|
|||
{
|
||||
CurrentLevel = null;
|
||||
_currentCoin = 0;
|
||||
_currentGold = 0;
|
||||
LastDefeatedEnemyCount = 0;
|
||||
LastGainedCoin = 0;
|
||||
LastGainedGold = 0;
|
||||
ShutdownCombatRuntime();
|
||||
}
|
||||
|
||||
public void ApplyEnemyDropReward(int coin, int gold)
|
||||
{
|
||||
if (coin > 0)
|
||||
{
|
||||
_currentCoin += coin;
|
||||
}
|
||||
|
||||
if (gold > 0)
|
||||
{
|
||||
_currentGold += gold;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryConsumeCoin(int coin)
|
||||
{
|
||||
int requiredCoin = Mathf.Max(0, coin);
|
||||
if (requiredCoin <= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_currentCoin < requiredCoin)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_currentCoin -= requiredCoin;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void AddCoin(int coin)
|
||||
{
|
||||
int gainCoin = Mathf.Max(0, coin);
|
||||
if (gainCoin <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_currentCoin += gainCoin;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
OnShutdown();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
using GeometryTD.DataTable;
|
||||
using GeometryTD.Definition;
|
||||
using GeometryTD.Entity;
|
||||
using GeometryTD.UI;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace GeometryTD.CustomComponent
|
||||
|
|
@ -12,8 +15,9 @@ namespace GeometryTD.CustomComponent
|
|||
Idle = 0,
|
||||
WaitingForLoading = 1,
|
||||
RunningPhase = 2,
|
||||
Completed = 3,
|
||||
Failed = 4
|
||||
WaitingForFinishReturn = 3,
|
||||
Completed = 4,
|
||||
Failed = 5
|
||||
}
|
||||
|
||||
private readonly List<DRLevelPhase> _phaseBuffer = new();
|
||||
|
|
@ -25,8 +29,11 @@ namespace GeometryTD.CustomComponent
|
|||
|
||||
private EntityComponent _entity;
|
||||
private DRLevel _currentLevel;
|
||||
private CombatFinishFormUseCase _combatFinishFormUseCase;
|
||||
private SchedulerState _state = SchedulerState.Idle;
|
||||
private bool _initialized;
|
||||
private int _gainedCoin;
|
||||
private int _gainedGold;
|
||||
|
||||
public bool IsRunning => _state == SchedulerState.WaitingForLoading || _state == SchedulerState.RunningPhase;
|
||||
public bool IsCompleted => _state == SchedulerState.Completed;
|
||||
|
|
@ -35,6 +42,9 @@ namespace GeometryTD.CustomComponent
|
|||
public MapEntity CurrentMap => _loadSession.CurrentMap;
|
||||
public int DisplayPhaseIndex => _phaseLoopRuntime.DisplayPhaseIndex;
|
||||
public bool CanEndCombat => _phaseLoopRuntime.CanEndCombat;
|
||||
public int DefeatedEnemyCount => _enemyManager.DefeatedEnemyCount;
|
||||
public int GainedCoin => _gainedCoin;
|
||||
public int GainedGold => _gainedGold;
|
||||
|
||||
public void OnInit()
|
||||
{
|
||||
|
|
@ -50,6 +60,7 @@ namespace GeometryTD.CustomComponent
|
|||
OnCloseUIFormComplete);
|
||||
_enemyManager.OnInit(this);
|
||||
_loadSession.OnInit(_entity);
|
||||
EnsureCombatFinishFormUseCaseBound();
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
|
|
@ -75,9 +86,13 @@ namespace GeometryTD.CustomComponent
|
|||
return;
|
||||
}
|
||||
|
||||
CleanupCombatEntities();
|
||||
CleanupAllCombatEntities();
|
||||
CloseCombatFinishForm();
|
||||
_enemyManager.EndPhase();
|
||||
_enemyManager.ResetCombatStats();
|
||||
ResetRuntime();
|
||||
_gainedCoin = 0;
|
||||
_gainedGold = 0;
|
||||
|
||||
_currentLevel = level;
|
||||
for (int i = 0; i < phases.Count; i++)
|
||||
|
|
@ -113,7 +128,8 @@ namespace GeometryTD.CustomComponent
|
|||
}
|
||||
|
||||
_state = SchedulerState.WaitingForLoading;
|
||||
Log.Info("CombatScheduler started. Level={0}, PhaseCount={1}.", _currentLevel.Id, _phaseLoopRuntime.PhaseCount);
|
||||
Log.Info("CombatScheduler started. Level={0}, PhaseCount={1}.", _currentLevel.Id,
|
||||
_phaseLoopRuntime.PhaseCount);
|
||||
}
|
||||
|
||||
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||
|
|
@ -143,17 +159,20 @@ namespace GeometryTD.CustomComponent
|
|||
return;
|
||||
}
|
||||
|
||||
CleanupCombatEntities();
|
||||
CleanupAllCombatEntities();
|
||||
CloseCombatFinishForm();
|
||||
_enemyManager.OnDestroy();
|
||||
ResetRuntime();
|
||||
_eventBridge.Unbind();
|
||||
_combatFinishFormUseCase = null;
|
||||
|
||||
_entity = null;
|
||||
_initialized = false;
|
||||
}
|
||||
|
||||
public bool TryEndCombatByPlayer()
|
||||
{
|
||||
if (_state == SchedulerState.Completed || _state == SchedulerState.Failed || _state == SchedulerState.Idle)
|
||||
if (_state != SchedulerState.RunningPhase)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -163,7 +182,7 @@ namespace GeometryTD.CustomComponent
|
|||
return false;
|
||||
}
|
||||
|
||||
FinishCombat("Combat ended by player.");
|
||||
EnterFinishFlow("Combat ended by player.");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -185,7 +204,8 @@ namespace GeometryTD.CustomComponent
|
|||
_phaseLoopRuntime.AdvancePhaseElapsed(elapseSeconds);
|
||||
_enemyManager.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||
|
||||
if (!_phaseLoopRuntime.ShouldEndCurrentPhase(_enemyManager.IsPhaseSpawnCompleted, _enemyManager.AliveEnemyCount))
|
||||
if (!_phaseLoopRuntime.ShouldEndCurrentPhase(_enemyManager.IsPhaseSpawnCompleted,
|
||||
_enemyManager.AliveEnemyCount))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -209,7 +229,7 @@ namespace GeometryTD.CustomComponent
|
|||
{
|
||||
if (!_phaseLoopRuntime.TryEnterNextPhase(out DRLevelPhase nextPhase))
|
||||
{
|
||||
FinishCombat("Combat ended after loop completion.");
|
||||
EnterFinishFlow("Combat ended after loop completion.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -227,17 +247,21 @@ namespace GeometryTD.CustomComponent
|
|||
spawnEntries != null ? spawnEntries.Count : 0);
|
||||
}
|
||||
|
||||
private void FinishCombat(string reason)
|
||||
private void EnterFinishFlow(string reason)
|
||||
{
|
||||
_state = SchedulerState.Completed;
|
||||
CleanupCombatEntities();
|
||||
int defeatedEnemyCount = _enemyManager.DefeatedEnemyCount;
|
||||
|
||||
// Step 1: stop runtime and clear enemy entities only.
|
||||
_enemyManager.EndPhase();
|
||||
_state = SchedulerState.WaitingForFinishReturn;
|
||||
_enemyManager.CleanupTrackedEnemies();
|
||||
|
||||
Log.Info(
|
||||
"CombatScheduler level completed. Level={0}. Reason={1}",
|
||||
"CombatScheduler entered finish flow. Level={0}. Reason={1}",
|
||||
_currentLevel != null ? _currentLevel.Id : 0,
|
||||
reason);
|
||||
GameEntry.CombatNode.EndCombat();
|
||||
|
||||
OpenCombatFinishForm(defeatedEnemyCount, _gainedGold);
|
||||
}
|
||||
|
||||
private void ResetRuntime()
|
||||
|
|
@ -250,12 +274,57 @@ namespace GeometryTD.CustomComponent
|
|||
_state = SchedulerState.Idle;
|
||||
}
|
||||
|
||||
private void CleanupCombatEntities()
|
||||
private void CleanupAllCombatEntities()
|
||||
{
|
||||
_loadSession.Cleanup();
|
||||
_enemyManager.CleanupTrackedEnemies();
|
||||
}
|
||||
|
||||
private void EnsureCombatFinishFormUseCaseBound()
|
||||
{
|
||||
_combatFinishFormUseCase ??= new CombatFinishFormUseCase(this);
|
||||
|
||||
GameEntry.UIRouter.BindUIUseCase(UIFormType.CombatFinishForm, _combatFinishFormUseCase);
|
||||
}
|
||||
|
||||
private void OpenCombatFinishForm(int defeatedEnemyCount, int gainedGold)
|
||||
{
|
||||
EnsureCombatFinishFormUseCaseBound();
|
||||
_combatFinishFormUseCase.SetSummary(
|
||||
defeatedEnemyCount,
|
||||
gainedGold);
|
||||
GameEntry.UIRouter.OpenUI(UIFormType.CombatFinishForm);
|
||||
}
|
||||
|
||||
public void OnEnemyDefeatedRewardResolved(int gainedCoin, int gainedGold)
|
||||
{
|
||||
int coin = Mathf.Max(0, gainedCoin);
|
||||
int gold = Mathf.Max(0, gainedGold);
|
||||
_gainedCoin += coin;
|
||||
_gainedGold += gold;
|
||||
GameEntry.CombatNode?.ApplyEnemyDropReward(coin, gold);
|
||||
}
|
||||
|
||||
private void CloseCombatFinishForm()
|
||||
{
|
||||
GameEntry.UIRouter.CloseUI(UIFormType.CombatFinishForm);
|
||||
}
|
||||
|
||||
public bool OnCombatFinishReturnRequested()
|
||||
{
|
||||
if (_state != SchedulerState.WaitingForFinishReturn)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 2: clear remaining map/UI resources and close finish form.
|
||||
_loadSession.Cleanup();
|
||||
CloseCombatFinishForm();
|
||||
_state = SchedulerState.Completed;
|
||||
GameEntry.CombatNode.EndCombat();
|
||||
return true;
|
||||
}
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
private void OnShowEntitySuccess(ShowEntitySuccessEventArgs args)
|
||||
|
|
@ -287,7 +356,8 @@ namespace GeometryTD.CustomComponent
|
|||
if (status == CombatLoadSession.EventHandleStatus.Failed)
|
||||
{
|
||||
_state = SchedulerState.Failed;
|
||||
Log.Error("CombatScheduler failed. LevelId={0}, {1}", _currentLevel != null ? _currentLevel.Id : 0, errorMessage);
|
||||
Log.Error("CombatScheduler failed. LevelId={0}, {1}", _currentLevel != null ? _currentLevel.Id : 0,
|
||||
errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ namespace GeometryTD.CustomComponent
|
|||
private readonly List<SpawnEntryRuntime> _spawnRuntimes = new List<SpawnEntryRuntime>();
|
||||
private readonly HashSet<int> _trackedEnemyEntityIds = new HashSet<int>();
|
||||
private readonly List<int> _trackedEnemyIdBuffer = new List<int>();
|
||||
private readonly Dictionary<int, DREnemy> _trackedEnemyConfigByEntityId = new Dictionary<int, DREnemy>();
|
||||
|
||||
private CombatScheduler _combatScheduler;
|
||||
private EntityComponent _entity;
|
||||
|
|
@ -38,6 +39,7 @@ namespace GeometryTD.CustomComponent
|
|||
|
||||
private int _spawnEnemyMaxCount = 5000;
|
||||
private int _currentEnemyCount;
|
||||
private int _defeatedEnemyCount;
|
||||
private int _nextSpawnerIndex;
|
||||
private int _currentMapEntityId;
|
||||
private bool _initialized;
|
||||
|
|
@ -47,6 +49,7 @@ namespace GeometryTD.CustomComponent
|
|||
private bool _isPhaseRunning;
|
||||
|
||||
public int AliveEnemyCount => _currentEnemyCount;
|
||||
public int DefeatedEnemyCount => _defeatedEnemyCount;
|
||||
public bool IsPhaseSpawnCompleted { get; private set; } = true;
|
||||
public bool IsPhaseRunning => _isPhaseRunning;
|
||||
|
||||
|
|
@ -61,6 +64,7 @@ namespace GeometryTD.CustomComponent
|
|||
_entity = GameEntry.Entity;
|
||||
_drEnemy = GameEntry.DataTable.GetDataTable<DREnemy>();
|
||||
_currentEnemyCount = 0;
|
||||
_defeatedEnemyCount = 0;
|
||||
_nextSpawnerIndex = 0;
|
||||
_currentMapEntityId = 0;
|
||||
_enemyConfigMissingLogged = false;
|
||||
|
|
@ -70,6 +74,7 @@ namespace GeometryTD.CustomComponent
|
|||
_spawnRuntimes.Clear();
|
||||
_trackedEnemyEntityIds.Clear();
|
||||
_trackedEnemyIdBuffer.Clear();
|
||||
_trackedEnemyConfigByEntityId.Clear();
|
||||
_phaseElapsed = 0f;
|
||||
_isPhaseRunning = false;
|
||||
IsPhaseSpawnCompleted = true;
|
||||
|
|
@ -149,13 +154,20 @@ namespace GeometryTD.CustomComponent
|
|||
_pathBuffer.Clear();
|
||||
_trackedEnemyEntityIds.Clear();
|
||||
_trackedEnemyIdBuffer.Clear();
|
||||
_trackedEnemyConfigByEntityId.Clear();
|
||||
_currentEnemyCount = 0;
|
||||
_defeatedEnemyCount = 0;
|
||||
_currentMapEntityId = 0;
|
||||
_nextSpawnerIndex = 0;
|
||||
_combatScheduler = null;
|
||||
_initialized = false;
|
||||
}
|
||||
|
||||
public void ResetCombatStats()
|
||||
{
|
||||
_defeatedEnemyCount = 0;
|
||||
}
|
||||
|
||||
public void CleanupTrackedEnemies()
|
||||
{
|
||||
if (_trackedEnemyEntityIds.Count <= 0)
|
||||
|
|
@ -171,6 +183,7 @@ namespace GeometryTD.CustomComponent
|
|||
}
|
||||
|
||||
_trackedEnemyEntityIds.Clear();
|
||||
_trackedEnemyConfigByEntityId.Clear();
|
||||
_currentEnemyCount = 0;
|
||||
|
||||
if (_entity == null)
|
||||
|
|
@ -352,6 +365,7 @@ namespace GeometryTD.CustomComponent
|
|||
|
||||
int enemyEntityId = _entity.GenerateSerialId();
|
||||
_trackedEnemyEntityIds.Add(enemyEntityId);
|
||||
_trackedEnemyConfigByEntityId[enemyEntityId] = enemyConfig;
|
||||
EnemyData enemyData = new EnemyData(
|
||||
enemyEntityId,
|
||||
enemyConfig.EntityId,
|
||||
|
|
@ -497,6 +511,7 @@ namespace GeometryTD.CustomComponent
|
|||
}
|
||||
|
||||
_trackedEnemyEntityIds.Remove(ne.EntityId);
|
||||
_trackedEnemyConfigByEntityId.Remove(ne.EntityId);
|
||||
}
|
||||
|
||||
private void OnHideEntityComplete(object sender, GameEventArgs e)
|
||||
|
|
@ -508,10 +523,33 @@ namespace GeometryTD.CustomComponent
|
|||
|
||||
if (!_trackedEnemyEntityIds.Remove(ne.EntityId))
|
||||
{
|
||||
_trackedEnemyConfigByEntityId.Remove(ne.EntityId);
|
||||
return;
|
||||
}
|
||||
|
||||
_currentEnemyCount = Mathf.Max(0, _currentEnemyCount - 1);
|
||||
bool wasKilled = EnemyEntity.TryConsumeKilledFlag(ne.EntityId);
|
||||
if (_combatScheduler != null && _combatScheduler.IsRunning && wasKilled)
|
||||
{
|
||||
_defeatedEnemyCount++;
|
||||
int droppedCoin = 0;
|
||||
int droppedGold = 0;
|
||||
if (_trackedEnemyConfigByEntityId.TryGetValue(ne.EntityId, out DREnemy enemyConfig) && enemyConfig != null)
|
||||
{
|
||||
droppedCoin = Mathf.Max(0, enemyConfig.DropCoin);
|
||||
float dropRate = enemyConfig.DropPercent > 1f
|
||||
? Mathf.Clamp01(enemyConfig.DropPercent * 0.01f)
|
||||
: Mathf.Clamp01(enemyConfig.DropPercent);
|
||||
if (enemyConfig.DropGold > 0 && dropRate > 0f && Random.value <= dropRate)
|
||||
{
|
||||
droppedGold = Mathf.Max(0, enemyConfig.DropGold);
|
||||
}
|
||||
}
|
||||
|
||||
_combatScheduler.OnEnemyDefeatedRewardResolved(droppedCoin, droppedGold);
|
||||
}
|
||||
|
||||
_trackedEnemyConfigByEntityId.Remove(ne.EntityId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,10 +35,20 @@ namespace GeometryTD.DataTable
|
|||
public float Speed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取敌人掉落金币
|
||||
/// 获取敌人掉落硬币
|
||||
/// </summary>
|
||||
public int DropCoin { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取敌人掉落金币
|
||||
/// </summary>
|
||||
public int DropGold { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取敌人掉落金币概率
|
||||
/// </summary>
|
||||
public float DropPercent { get; private set; }
|
||||
|
||||
public override bool ParseDataRow(string dataRowString, object userData)
|
||||
{
|
||||
string[] columnStrings = dataRowString.Split(DataTableExtension.DataSplitSeparators);
|
||||
|
|
@ -56,8 +66,10 @@ namespace GeometryTD.DataTable
|
|||
BaseDamage = int.Parse(columnStrings[index++]);
|
||||
Speed = float.Parse(columnStrings[index++]);
|
||||
DropCoin = int.Parse(columnStrings[index++]);
|
||||
DropGold = int.Parse(columnStrings[index++]);
|
||||
DropPercent = float.Parse(columnStrings[index++]);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -46,6 +46,16 @@
|
|||
/// 战斗信息界面。
|
||||
/// </summary>
|
||||
CombatInfoForm = 140,
|
||||
|
||||
/// <summary>
|
||||
/// 战斗结算界面。
|
||||
/// </summary>
|
||||
CombatFinishForm = 141,
|
||||
|
||||
/// <summary>
|
||||
/// 战斗选择界面
|
||||
/// </summary>
|
||||
CombatSelectForm = 142,
|
||||
|
||||
/// <summary>
|
||||
/// 测试菜单。
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
using System;
|
||||
using GeometryTD.Definition;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.Entity.EntityData
|
||||
{
|
||||
[Serializable]
|
||||
public class BulletData : EntityDataBase
|
||||
{
|
||||
[SerializeField] private Transform _target = null;
|
||||
[SerializeField] private int _damage = 0;
|
||||
[SerializeField] private float _speed = 0f;
|
||||
[SerializeField] private float _maxLifetime = 3f;
|
||||
[SerializeField] private AttackPropertyType _attackPropertyType = AttackPropertyType.None;
|
||||
|
||||
public BulletData(
|
||||
int entityId,
|
||||
int typeId,
|
||||
Vector3 position,
|
||||
Quaternion rotation,
|
||||
Transform target,
|
||||
int damage,
|
||||
float speed,
|
||||
AttackPropertyType attackPropertyType = AttackPropertyType.None,
|
||||
float maxLifetime = 3f) : base(entityId, typeId)
|
||||
{
|
||||
Position = position;
|
||||
Rotation = rotation;
|
||||
_target = target;
|
||||
_damage = damage;
|
||||
_speed = speed;
|
||||
_attackPropertyType = attackPropertyType;
|
||||
_maxLifetime = maxLifetime;
|
||||
}
|
||||
|
||||
public Transform Target
|
||||
{
|
||||
get => _target;
|
||||
set => _target = value;
|
||||
}
|
||||
|
||||
public int Damage
|
||||
{
|
||||
get => _damage;
|
||||
set => _damage = value;
|
||||
}
|
||||
|
||||
public float Speed
|
||||
{
|
||||
get => _speed;
|
||||
set => _speed = value;
|
||||
}
|
||||
|
||||
public float MaxLifetime
|
||||
{
|
||||
get => _maxLifetime;
|
||||
set => _maxLifetime = value;
|
||||
}
|
||||
|
||||
public AttackPropertyType AttackPropertyType
|
||||
{
|
||||
get => _attackPropertyType;
|
||||
set => _attackPropertyType = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6f7248061a2a4f4f8ecf8d5db99f0a82
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using GeometryTD.Definition;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.Entity.EntityData
|
||||
{
|
||||
[Serializable]
|
||||
public class DefenseTowerData : EntityDataBase
|
||||
{
|
||||
[SerializeField] private DefenseTowerStatsData _stats = new DefenseTowerStatsData();
|
||||
|
||||
public DefenseTowerData(int entityId, int typeId, Vector3 position, Quaternion rotation, DefenseTowerStatsData stats)
|
||||
: base(entityId, typeId)
|
||||
{
|
||||
Position = position;
|
||||
Rotation = rotation;
|
||||
_stats = stats ?? new DefenseTowerStatsData();
|
||||
}
|
||||
|
||||
public DefenseTowerStatsData Stats
|
||||
{
|
||||
get => _stats;
|
||||
set => _stats = value ?? new DefenseTowerStatsData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0d46035f451d4e01a5a21ed5984ce42b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -41,16 +41,21 @@ namespace GeometryTD
|
|||
entityComponent.AttachEntity(entityBase.Entity, ownerId, parentTransformPath, userData);
|
||||
}
|
||||
|
||||
public static void ShowPlayer(this EntityComponent entityComponent, PlayerData data)
|
||||
{
|
||||
entityComponent.ShowEntity(typeof(Player), "Player", Constant.AssetPriority.PlayerAsset, data);
|
||||
}
|
||||
|
||||
public static void ShowEnemy(this EntityComponent entityComponent, EnemyData data)
|
||||
{
|
||||
entityComponent.ShowEntity(typeof(EnemyEntity), "Enemy", Constant.AssetPriority.EnemyAsset, data);
|
||||
}
|
||||
|
||||
public static void ShowDefenseTower(this EntityComponent entityComponent, DefenseTowerData data)
|
||||
{
|
||||
entityComponent.ShowEntity(typeof(DefenseTowerEntity), "Tower", Constant.AssetPriority.EnemyAsset, data);
|
||||
}
|
||||
|
||||
public static void ShowBullet(this EntityComponent entityComponent, BulletData data)
|
||||
{
|
||||
entityComponent.ShowEntity(typeof(BulletEntity), "Bullet", Constant.AssetPriority.BulletAsset, data);
|
||||
}
|
||||
|
||||
public static void ShowMap(this EntityComponent entityComponent, MapData data)
|
||||
{
|
||||
ShowMap(entityComponent, data, null);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
using Components;
|
||||
using GeometryTD.Entity.EntityData;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace GeometryTD.Entity
|
||||
{
|
||||
public class BulletEntity : EntityBase
|
||||
{
|
||||
private ShooterBullet _shooterBullet;
|
||||
|
||||
protected override void OnInit(object userData)
|
||||
{
|
||||
base.OnInit(userData);
|
||||
|
||||
_shooterBullet = GetComponent<ShooterBullet>();
|
||||
if (_shooterBullet == null)
|
||||
{
|
||||
Log.Error("ShooterBullet component is missing on bullet entity '{0}'.", name);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnShow(object userData)
|
||||
{
|
||||
base.OnShow(userData);
|
||||
|
||||
if (_shooterBullet == null)
|
||||
{
|
||||
GameEntry.Entity.HideEntity(Entity);
|
||||
return;
|
||||
}
|
||||
|
||||
if (userData is not BulletData bulletData)
|
||||
{
|
||||
Log.Warning("BulletData is invalid for bullet entity '{0}'.", Id);
|
||||
GameEntry.Entity.HideEntity(Entity);
|
||||
return;
|
||||
}
|
||||
|
||||
_shooterBullet.OnShow(bulletData);
|
||||
if (_shooterBullet.TryConsumeDespawnRequest())
|
||||
{
|
||||
GameEntry.Entity.HideEntity(Entity);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||
{
|
||||
base.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||
|
||||
if (_shooterBullet == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_shooterBullet.Tick(elapseSeconds);
|
||||
if (_shooterBullet.TryConsumeDespawnRequest())
|
||||
{
|
||||
GameEntry.Entity.HideEntity(Entity);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnHide(bool isShutdown, object userData)
|
||||
{
|
||||
_shooterBullet?.OnReset();
|
||||
base.OnHide(isShutdown, userData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e4912a44cc6c4f3d85f6d54868780f2f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
using Components;
|
||||
using GeometryTD.Entity.EntityData;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace GeometryTD.Entity
|
||||
{
|
||||
public class DefenseTowerEntity : EntityBase
|
||||
{
|
||||
private DefenseTowerController _towerController;
|
||||
|
||||
protected override void OnInit(object userData)
|
||||
{
|
||||
base.OnInit(userData);
|
||||
|
||||
_towerController = GetComponent<DefenseTowerController>();
|
||||
if (_towerController == null)
|
||||
{
|
||||
Log.Error("DefenseTowerController is missing on tower entity '{0}'.", name);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnShow(object userData)
|
||||
{
|
||||
base.OnShow(userData);
|
||||
|
||||
if (_towerController == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (userData is not DefenseTowerData towerData)
|
||||
{
|
||||
Log.Warning("DefenseTowerData is invalid for tower entity '{0}'.", Id);
|
||||
_towerController.OnReset();
|
||||
GameEntry.Entity.HideEntity(Entity);
|
||||
return;
|
||||
}
|
||||
|
||||
_towerController.SetAutoUpdate(false);
|
||||
_towerController.OnInit(towerData.Stats);
|
||||
}
|
||||
|
||||
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||
{
|
||||
base.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||
_towerController?.OnUpdate(elapseSeconds);
|
||||
}
|
||||
|
||||
protected override void OnHide(bool isShutdown, object userData)
|
||||
{
|
||||
_towerController?.OnReset();
|
||||
base.OnHide(isShutdown, userData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d87f56efd9024709a3baf8ef8a6f4fd3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,21 +1,33 @@
|
|||
using Components;
|
||||
using System.Collections.Generic;
|
||||
using GeometryTD.Definition;
|
||||
using GeometryTD.Entity.EntityData;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.Entity
|
||||
{
|
||||
public class EnemyEntity : EntityBase
|
||||
public class EnemyEntity : EntityBase, IDamageReceiver
|
||||
{
|
||||
private const float WaypointReachDistance = 0.05f;
|
||||
private static readonly List<EnemyEntity> _activeEnemies = new();
|
||||
private static readonly HashSet<int> _killedEnemyEntityIds = new();
|
||||
|
||||
private Transform _target;
|
||||
private float _speed;
|
||||
private int _maxHealth;
|
||||
private int _currentHealth;
|
||||
private MovementComponent _movementComponent;
|
||||
private readonly List<Vector3> _pathPoints = new List<Vector3>();
|
||||
private readonly List<Vector3> _pathPoints = new();
|
||||
private int _pathPointIndex;
|
||||
private bool _isDespawnRequested;
|
||||
|
||||
public static IReadOnlyList<EnemyEntity> ActiveEnemies => _activeEnemies;
|
||||
|
||||
public static bool TryConsumeKilledFlag(int entityId)
|
||||
{
|
||||
return _killedEnemyEntityIds.Remove(entityId);
|
||||
}
|
||||
|
||||
protected override void OnInit(object userData)
|
||||
{
|
||||
base.OnInit(userData);
|
||||
|
|
@ -31,10 +43,14 @@ namespace GeometryTD.Entity
|
|||
_pathPoints.Clear();
|
||||
_pathPointIndex = 0;
|
||||
_isDespawnRequested = false;
|
||||
_maxHealth = 1;
|
||||
_currentHealth = 1;
|
||||
if (userData is EnemyData enemyData)
|
||||
{
|
||||
_speed = enemyData.Speed;
|
||||
_target = enemyData.Player;
|
||||
_maxHealth = Mathf.Max(1, enemyData.MaxHealth);
|
||||
_currentHealth = _maxHealth;
|
||||
if (enemyData.HasPath)
|
||||
{
|
||||
IReadOnlyList<Vector3> pathPoints = enemyData.PathPoints;
|
||||
|
|
@ -47,6 +63,11 @@ namespace GeometryTD.Entity
|
|||
|
||||
_movementComponent.OnInit(_speed, CachedTransform);
|
||||
_movementComponent.SetMove(true);
|
||||
_killedEnemyEntityIds.Remove(Id);
|
||||
if (!_activeEnemies.Contains(this))
|
||||
{
|
||||
_activeEnemies.Add(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||
|
|
@ -66,10 +87,45 @@ namespace GeometryTD.Entity
|
|||
_pathPoints.Clear();
|
||||
_pathPointIndex = 0;
|
||||
_isDespawnRequested = false;
|
||||
_maxHealth = 0;
|
||||
_currentHealth = 0;
|
||||
_activeEnemies.Remove(this);
|
||||
_killedEnemyEntityIds.Remove(Id);
|
||||
|
||||
base.OnHide(isShutdown, userData);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
_activeEnemies.Remove(this);
|
||||
if (Entity != null)
|
||||
{
|
||||
_killedEnemyEntityIds.Remove(Entity.Id);
|
||||
}
|
||||
}
|
||||
|
||||
public void TakeDamage(int damage, AttackPropertyType attackPropertyType)
|
||||
{
|
||||
_ = attackPropertyType;
|
||||
if (_isDespawnRequested || damage <= 0 || _currentHealth <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int previousHealth = _currentHealth;
|
||||
_currentHealth = Mathf.Max(0, _currentHealth - damage);
|
||||
if (_maxHealth > 0)
|
||||
{
|
||||
GameEntry.HPBar?.ShowHPBar(this, (float)previousHealth / _maxHealth, (float)_currentHealth / _maxHealth);
|
||||
}
|
||||
|
||||
if (_currentHealth <= 0)
|
||||
{
|
||||
_killedEnemyEntityIds.Add(Id);
|
||||
RequestDespawn();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePathMovement(float elapseSeconds, float realElapseSeconds)
|
||||
{
|
||||
if (_isDespawnRequested)
|
||||
|
|
@ -120,6 +176,11 @@ namespace GeometryTD.Entity
|
|||
}
|
||||
|
||||
private void DespawnOnReachHouse()
|
||||
{
|
||||
RequestDespawn();
|
||||
}
|
||||
|
||||
private void RequestDespawn()
|
||||
{
|
||||
if (_isDespawnRequested)
|
||||
{
|
||||
|
|
@ -131,4 +192,4 @@ namespace GeometryTD.Entity
|
|||
GameEntry.Entity.HideEntity(Entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GeometryTD;
|
||||
using GeometryTD.Definition;
|
||||
using GeometryTD.Entity.EntityData;
|
||||
using GeometryTD.Pathfinding;
|
||||
using GeometryTD.UI;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.Tilemaps;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
|
|
@ -12,6 +16,7 @@ namespace GeometryTD.Entity
|
|||
{
|
||||
private const string PathTileName = "Path";
|
||||
private const string FoundationTileName = "Foundation";
|
||||
private const int DefaultTowerTypeId = 401;
|
||||
private static readonly Spawner[] EmptySpawners = Array.Empty<Spawner>();
|
||||
|
||||
private readonly List<Vector3Int> _pathCells = new();
|
||||
|
|
@ -22,11 +27,25 @@ namespace GeometryTD.Entity
|
|||
private readonly List<Vector3Int> _pathCellBuffer = new();
|
||||
private readonly Dictionary<Spawner, Vector3Int> _spawnerPathStartByRef = new();
|
||||
private readonly Dictionary<Spawner, List<Vector3Int>> _defaultPathCellsBySpawner = new();
|
||||
private readonly Dictionary<Vector3Int, int> _towerEntityIdByFoundationCell = new();
|
||||
private readonly Dictionary<int, Vector3Int> _foundationCellByTowerEntityId = new();
|
||||
private readonly Dictionary<int, DefenseTowerStatsData> _towerStatsByEntityId = new();
|
||||
private readonly List<int> _towerEntityIdBuffer = new();
|
||||
|
||||
[SerializeField] private bool _enableCombatSelectInput = true;
|
||||
[SerializeField] private int _towerTypeId = DefaultTowerTypeId;
|
||||
[SerializeField] private int[] _buildTowerCosts = { 80, 120, 160, 220 };
|
||||
[SerializeField] private int _upgradeCost = 80;
|
||||
[SerializeField] private int _destroyGain = 40;
|
||||
|
||||
private MapDataRefs _mapDataRefs;
|
||||
private MapData _mapData;
|
||||
private bool _hasHousePathCell;
|
||||
private Vector3Int _housePathCell;
|
||||
private CombatSelectFormUseCase _combatSelectFormUseCase;
|
||||
private bool _hasSelectedFoundationCell;
|
||||
private Vector3Int _selectedFoundationCell;
|
||||
private int _selectedTowerEntityId;
|
||||
|
||||
public IReadOnlyList<Vector3Int> PathCells => _pathCells;
|
||||
public IReadOnlyList<Vector3Int> FoundationCells => _foundationCells;
|
||||
|
|
@ -156,6 +175,8 @@ namespace GeometryTD.Entity
|
|||
{
|
||||
Log.Error("MapDataRefs is missing on map entity '{0}'.", name);
|
||||
}
|
||||
|
||||
InitializeCombatSelectUseCase();
|
||||
}
|
||||
|
||||
protected override void OnShow(object userData)
|
||||
|
|
@ -169,14 +190,24 @@ namespace GeometryTD.Entity
|
|||
}
|
||||
|
||||
RefreshTiles();
|
||||
ConfigureCombatSelectUseCase();
|
||||
HideCombatSelectForm();
|
||||
}
|
||||
|
||||
protected override void OnHide(bool isShutdown, object userData)
|
||||
{
|
||||
HideCombatSelectForm();
|
||||
ClearPlacedTowers();
|
||||
ClearRuntimeData();
|
||||
base.OnHide(isShutdown, userData);
|
||||
}
|
||||
|
||||
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||
{
|
||||
base.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||
HandleCombatSelectInput();
|
||||
}
|
||||
|
||||
private void RefreshTiles()
|
||||
{
|
||||
ClearRuntimeData();
|
||||
|
|
@ -295,6 +326,479 @@ namespace GeometryTD.Entity
|
|||
_housePathCell = default;
|
||||
_spawnerPathStartByRef.Clear();
|
||||
_defaultPathCellsBySpawner.Clear();
|
||||
_towerEntityIdByFoundationCell.Clear();
|
||||
_foundationCellByTowerEntityId.Clear();
|
||||
_towerStatsByEntityId.Clear();
|
||||
_towerEntityIdBuffer.Clear();
|
||||
ClearSelectedObject();
|
||||
}
|
||||
|
||||
private void InitializeCombatSelectUseCase()
|
||||
{
|
||||
if (_combatSelectFormUseCase == null)
|
||||
{
|
||||
_combatSelectFormUseCase = new CombatSelectFormUseCase();
|
||||
}
|
||||
|
||||
GameEntry.UIRouter.BindUIUseCase(UIFormType.CombatSelectForm, _combatSelectFormUseCase);
|
||||
}
|
||||
|
||||
private void ConfigureCombatSelectUseCase()
|
||||
{
|
||||
if (_combatSelectFormUseCase == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_combatSelectFormUseCase.SetCoinProvider(GetCurrentCoin);
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
int buildIndex = i;
|
||||
_combatSelectFormUseCase.SetBuildAction(
|
||||
buildIndex,
|
||||
() => TryBuildTower(buildIndex),
|
||||
GetBuildTowerCost(buildIndex));
|
||||
}
|
||||
|
||||
_combatSelectFormUseCase.SetUpgradeAction(
|
||||
TryUpgradeTower,
|
||||
Mathf.Max(0, _upgradeCost));
|
||||
_combatSelectFormUseCase.SetDestroyAction(
|
||||
TryDestroyTower,
|
||||
Mathf.Max(0, _destroyGain));
|
||||
}
|
||||
|
||||
private void HandleCombatSelectInput()
|
||||
{
|
||||
if (!_enableCombatSelectInput || !Input.GetMouseButtonDown(0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (EventSystem.current != null && EventSystem.current.IsPointerOverGameObject())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryBuildCombatSelectUserData(out CombatSelectFormUserData userData))
|
||||
{
|
||||
userData = new CombatSelectFormUserData
|
||||
{
|
||||
ClickObjectType = CombatSelectClickObjectType.None
|
||||
};
|
||||
}
|
||||
|
||||
ApplySelectedObject(userData);
|
||||
GameEntry.UIRouter.OpenUI(UIFormType.CombatSelectForm, userData);
|
||||
}
|
||||
|
||||
private bool TryBuildCombatSelectUserData(out CombatSelectFormUserData userData)
|
||||
{
|
||||
userData = null;
|
||||
if (Tilemap == null || !TryGetPointerWorldPosition(out Vector3 worldPosition, out Vector2 contentPosition))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector3Int clickedCell = Tilemap.WorldToCell(worldPosition);
|
||||
CombatSelectClickObjectType clickObjectType = CombatSelectClickObjectType.None;
|
||||
int towerEntityId = 0;
|
||||
|
||||
if (_towerEntityIdByFoundationCell.TryGetValue(clickedCell, out int occupiedTowerEntityId))
|
||||
{
|
||||
clickObjectType = CombatSelectClickObjectType.Tower;
|
||||
towerEntityId = occupiedTowerEntityId;
|
||||
}
|
||||
else if (IsFoundationCell(clickedCell))
|
||||
{
|
||||
clickObjectType = CombatSelectClickObjectType.Foundation;
|
||||
}
|
||||
|
||||
userData = new CombatSelectFormUserData
|
||||
{
|
||||
ClickObjectType = clickObjectType,
|
||||
ContentPosition = contentPosition,
|
||||
WorldPosition = worldPosition,
|
||||
CellPosition = clickedCell,
|
||||
TowerEntityId = towerEntityId,
|
||||
UpgradeCost = Mathf.Max(0, _upgradeCost),
|
||||
DestroyGain = Mathf.Max(0, _destroyGain)
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryGetPointerWorldPosition(out Vector3 worldPosition, out Vector2 contentPosition)
|
||||
{
|
||||
worldPosition = Vector3.zero;
|
||||
contentPosition = Vector2.zero;
|
||||
|
||||
Camera mainCamera = GameEntry.Scene != null ? GameEntry.Scene.MainCamera : Camera.main;
|
||||
if (mainCamera == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition);
|
||||
float mapPlaneZ = Tilemap != null ? Tilemap.transform.position.z : CachedTransform.position.z;
|
||||
Vector3 planeNormal = mainCamera.transform.forward.sqrMagnitude > Mathf.Epsilon
|
||||
? -mainCamera.transform.forward
|
||||
: Vector3.forward;
|
||||
Plane mapPlane = new Plane(planeNormal, new Vector3(0f, 0f, mapPlaneZ));
|
||||
if (!mapPlane.Raycast(ray, out float enterDistance))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
worldPosition = ray.GetPoint(enterDistance);
|
||||
contentPosition = BuildContentPosition(Input.mousePosition);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Vector2 BuildContentPosition(Vector3 pointerScreenPosition)
|
||||
{
|
||||
return new Vector2(
|
||||
pointerScreenPosition.x - Screen.width * 0.5f,
|
||||
pointerScreenPosition.y - Screen.height * 0.5f);
|
||||
}
|
||||
|
||||
private void ApplySelectedObject(CombatSelectFormUserData userData)
|
||||
{
|
||||
if (userData == null)
|
||||
{
|
||||
ClearSelectedObject();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (userData.ClickObjectType)
|
||||
{
|
||||
case CombatSelectClickObjectType.Foundation:
|
||||
_hasSelectedFoundationCell = true;
|
||||
_selectedFoundationCell = userData.CellPosition;
|
||||
_selectedTowerEntityId = 0;
|
||||
break;
|
||||
case CombatSelectClickObjectType.Tower:
|
||||
_hasSelectedFoundationCell = true;
|
||||
_selectedFoundationCell = userData.CellPosition;
|
||||
_selectedTowerEntityId = userData.TowerEntityId;
|
||||
break;
|
||||
default:
|
||||
ClearSelectedObject();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryBuildTower(int buildIndex)
|
||||
{
|
||||
if (!_hasSelectedFoundationCell || !IsFoundationCell(_selectedFoundationCell))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_towerEntityIdByFoundationCell.ContainsKey(_selectedFoundationCell))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int buildCost = GetBuildTowerCost(buildIndex);
|
||||
if (!TryConsumeCoin(buildCost))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DefenseTowerStatsData towerStats = BuildTowerStats(buildIndex);
|
||||
if (!TryShowTowerEntity(_selectedFoundationCell, towerStats, out int towerEntityId))
|
||||
{
|
||||
GameEntry.CombatNode?.AddCoin(buildCost);
|
||||
return false;
|
||||
}
|
||||
|
||||
_towerEntityIdByFoundationCell[_selectedFoundationCell] = towerEntityId;
|
||||
_foundationCellByTowerEntityId[towerEntityId] = _selectedFoundationCell;
|
||||
_towerStatsByEntityId[towerEntityId] = CloneTowerStats(towerStats);
|
||||
_selectedTowerEntityId = towerEntityId;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryUpgradeTower()
|
||||
{
|
||||
if (!TryGetSelectedTower(out int towerEntityId, out Vector3Int foundationCell))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int upgradeCost = Mathf.Max(0, _upgradeCost);
|
||||
if (!TryConsumeCoin(upgradeCost))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DefenseTowerStatsData oldStats = _towerStatsByEntityId.TryGetValue(towerEntityId, out DefenseTowerStatsData cachedStats)
|
||||
? CloneTowerStats(cachedStats)
|
||||
: BuildTowerStats(0);
|
||||
DefenseTowerStatsData upgradedStats = CloneTowerStats(oldStats);
|
||||
ApplyUpgradeToStats(upgradedStats);
|
||||
|
||||
HideTowerEntity(towerEntityId);
|
||||
_towerEntityIdByFoundationCell.Remove(foundationCell);
|
||||
_foundationCellByTowerEntityId.Remove(towerEntityId);
|
||||
_towerStatsByEntityId.Remove(towerEntityId);
|
||||
|
||||
if (!TryShowTowerEntity(foundationCell, upgradedStats, out int newTowerEntityId))
|
||||
{
|
||||
if (TryShowTowerEntity(foundationCell, oldStats, out int fallbackTowerEntityId))
|
||||
{
|
||||
_towerEntityIdByFoundationCell[foundationCell] = fallbackTowerEntityId;
|
||||
_foundationCellByTowerEntityId[fallbackTowerEntityId] = foundationCell;
|
||||
_towerStatsByEntityId[fallbackTowerEntityId] = CloneTowerStats(oldStats);
|
||||
_selectedTowerEntityId = fallbackTowerEntityId;
|
||||
}
|
||||
|
||||
GameEntry.CombatNode?.AddCoin(upgradeCost);
|
||||
return false;
|
||||
}
|
||||
|
||||
_towerEntityIdByFoundationCell[foundationCell] = newTowerEntityId;
|
||||
_foundationCellByTowerEntityId[newTowerEntityId] = foundationCell;
|
||||
_towerStatsByEntityId[newTowerEntityId] = CloneTowerStats(upgradedStats);
|
||||
_hasSelectedFoundationCell = true;
|
||||
_selectedFoundationCell = foundationCell;
|
||||
_selectedTowerEntityId = newTowerEntityId;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryDestroyTower()
|
||||
{
|
||||
if (!TryGetSelectedTower(out int towerEntityId, out Vector3Int foundationCell))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
HideTowerEntity(towerEntityId);
|
||||
_towerEntityIdByFoundationCell.Remove(foundationCell);
|
||||
_foundationCellByTowerEntityId.Remove(towerEntityId);
|
||||
_towerStatsByEntityId.Remove(towerEntityId);
|
||||
GameEntry.CombatNode?.AddCoin(Mathf.Max(0, _destroyGain));
|
||||
|
||||
ClearSelectedObject();
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryGetSelectedTower(out int towerEntityId, out Vector3Int foundationCell)
|
||||
{
|
||||
towerEntityId = 0;
|
||||
foundationCell = default;
|
||||
|
||||
if (_selectedTowerEntityId == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_foundationCellByTowerEntityId.TryGetValue(_selectedTowerEntityId, out foundationCell))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
towerEntityId = _selectedTowerEntityId;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryShowTowerEntity(Vector3Int foundationCell, DefenseTowerStatsData towerStats, out int towerEntityId)
|
||||
{
|
||||
towerEntityId = 0;
|
||||
if (GameEntry.Entity == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int entityId = GameEntry.Entity.GenerateSerialId();
|
||||
int typeId = _towerTypeId > 0 ? _towerTypeId : DefaultTowerTypeId;
|
||||
Vector3 towerPosition = Tilemap != null ? Tilemap.GetCellCenterWorld(foundationCell) : foundationCell;
|
||||
towerPosition.z = 0f;
|
||||
var towerData = new DefenseTowerData(entityId, typeId, towerPosition, Quaternion.identity, towerStats);
|
||||
GameEntry.Entity.ShowDefenseTower(towerData);
|
||||
|
||||
towerEntityId = entityId;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void HideTowerEntity(int towerEntityId)
|
||||
{
|
||||
if (towerEntityId == 0 || GameEntry.Entity == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UnityGameFramework.Runtime.Entity towerEntity = GameEntry.Entity.GetEntity(towerEntityId);
|
||||
if (towerEntity != null)
|
||||
{
|
||||
GameEntry.Entity.HideEntity(towerEntity);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearPlacedTowers()
|
||||
{
|
||||
_towerEntityIdBuffer.Clear();
|
||||
foreach (KeyValuePair<int, Vector3Int> pair in _foundationCellByTowerEntityId)
|
||||
{
|
||||
_towerEntityIdBuffer.Add(pair.Key);
|
||||
}
|
||||
|
||||
for (int i = 0; i < _towerEntityIdBuffer.Count; i++)
|
||||
{
|
||||
HideTowerEntity(_towerEntityIdBuffer[i]);
|
||||
}
|
||||
|
||||
_towerEntityIdByFoundationCell.Clear();
|
||||
_foundationCellByTowerEntityId.Clear();
|
||||
_towerStatsByEntityId.Clear();
|
||||
_towerEntityIdBuffer.Clear();
|
||||
ClearSelectedObject();
|
||||
}
|
||||
|
||||
private void HideCombatSelectForm()
|
||||
{
|
||||
_combatSelectFormUseCase?.Hide();
|
||||
GameEntry.UIRouter.CloseUI(UIFormType.CombatSelectForm);
|
||||
}
|
||||
|
||||
private void ClearSelectedObject()
|
||||
{
|
||||
_hasSelectedFoundationCell = false;
|
||||
_selectedFoundationCell = default;
|
||||
_selectedTowerEntityId = 0;
|
||||
}
|
||||
|
||||
private int GetBuildTowerCost(int buildIndex)
|
||||
{
|
||||
if (_buildTowerCosts == null || buildIndex < 0 || buildIndex >= _buildTowerCosts.Length)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Mathf.Max(0, _buildTowerCosts[buildIndex]);
|
||||
}
|
||||
|
||||
private static bool TryConsumeCoin(int cost)
|
||||
{
|
||||
int requiredCoin = Mathf.Max(0, cost);
|
||||
if (requiredCoin <= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (GameEntry.CombatNode == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return GameEntry.CombatNode.TryConsumeCoin(requiredCoin);
|
||||
}
|
||||
|
||||
private static int GetCurrentCoin()
|
||||
{
|
||||
return GameEntry.CombatNode != null ? Mathf.Max(0, GameEntry.CombatNode.CurrentCoin) : 0;
|
||||
}
|
||||
|
||||
private static DefenseTowerStatsData BuildTowerStats(int buildIndex)
|
||||
{
|
||||
switch (buildIndex)
|
||||
{
|
||||
case 0:
|
||||
return new DefenseTowerStatsData
|
||||
{
|
||||
AttackDamage = 10,
|
||||
DamageRandomRate = 0f,
|
||||
RotateSpeed = 200f,
|
||||
AttackRange = 4.5f,
|
||||
AttackSpeed = 1.5f,
|
||||
AttackMethodType = AttackMethodType.NormalBullet,
|
||||
AttackPropertyType = AttackPropertyType.Physics,
|
||||
Tags = Array.Empty<TagType>()
|
||||
};
|
||||
case 1:
|
||||
return new DefenseTowerStatsData
|
||||
{
|
||||
AttackDamage = 13,
|
||||
DamageRandomRate = 0f,
|
||||
RotateSpeed = 160f,
|
||||
AttackRange = 5f,
|
||||
AttackSpeed = 1.2f,
|
||||
AttackMethodType = AttackMethodType.NormalBullet,
|
||||
AttackPropertyType = AttackPropertyType.Fire,
|
||||
Tags = Array.Empty<TagType>()
|
||||
};
|
||||
case 2:
|
||||
return new DefenseTowerStatsData
|
||||
{
|
||||
AttackDamage = 17,
|
||||
DamageRandomRate = 0f,
|
||||
RotateSpeed = 140f,
|
||||
AttackRange = 5.5f,
|
||||
AttackSpeed = 0.95f,
|
||||
AttackMethodType = AttackMethodType.NormalBullet,
|
||||
AttackPropertyType = AttackPropertyType.Ice,
|
||||
Tags = Array.Empty<TagType>()
|
||||
};
|
||||
case 3:
|
||||
return new DefenseTowerStatsData
|
||||
{
|
||||
AttackDamage = 22,
|
||||
DamageRandomRate = 0f,
|
||||
RotateSpeed = 120f,
|
||||
AttackRange = 6f,
|
||||
AttackSpeed = 0.75f,
|
||||
AttackMethodType = AttackMethodType.NormalBullet,
|
||||
AttackPropertyType = AttackPropertyType.Poison,
|
||||
Tags = Array.Empty<TagType>()
|
||||
};
|
||||
default:
|
||||
return new DefenseTowerStatsData
|
||||
{
|
||||
AttackDamage = 10,
|
||||
DamageRandomRate = 0f,
|
||||
RotateSpeed = 180f,
|
||||
AttackRange = 5f,
|
||||
AttackSpeed = 1f,
|
||||
AttackMethodType = AttackMethodType.NormalBullet,
|
||||
AttackPropertyType = AttackPropertyType.Physics,
|
||||
Tags = Array.Empty<TagType>()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static DefenseTowerStatsData CloneTowerStats(DefenseTowerStatsData source)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
return BuildTowerStats(0);
|
||||
}
|
||||
|
||||
TagType[] copiedTags = source.Tags != null ? (TagType[])source.Tags.Clone() : Array.Empty<TagType>();
|
||||
return new DefenseTowerStatsData
|
||||
{
|
||||
AttackDamage = source.AttackDamage,
|
||||
DamageRandomRate = source.DamageRandomRate,
|
||||
RotateSpeed = source.RotateSpeed,
|
||||
AttackRange = source.AttackRange,
|
||||
AttackSpeed = source.AttackSpeed,
|
||||
AttackMethodType = source.AttackMethodType,
|
||||
AttackPropertyType = source.AttackPropertyType,
|
||||
Tags = copiedTags
|
||||
};
|
||||
}
|
||||
|
||||
private static void ApplyUpgradeToStats(DefenseTowerStatsData stats)
|
||||
{
|
||||
if (stats == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
stats.AttackDamage = Mathf.Max(1, stats.AttackDamage + 3);
|
||||
stats.AttackSpeed = Mathf.Max(0.01f, stats.AttackSpeed + 0.2f);
|
||||
stats.AttackRange = Mathf.Max(0.1f, stats.AttackRange + 0.4f);
|
||||
stats.RotateSpeed = Mathf.Max(1f, stats.RotateSpeed + 10f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
using GameFramework;
|
||||
using GameFramework.Event;
|
||||
|
||||
namespace GeometryTD.CustomEvent
|
||||
{
|
||||
public class CombatFinishReturnEventArgs : GameEventArgs
|
||||
{
|
||||
public static int EventId => typeof(CombatFinishReturnEventArgs).GetHashCode();
|
||||
|
||||
public override int Id => EventId;
|
||||
|
||||
public static CombatFinishReturnEventArgs Create()
|
||||
{
|
||||
return ReferencePool.Acquire<CombatFinishReturnEventArgs>();
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8ff2a3f68ab24bf294b24907548c88de
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
using GameFramework;
|
||||
using GameFramework.Event;
|
||||
using GeometryTD.UI;
|
||||
|
||||
namespace GeometryTD.CustomEvent
|
||||
{
|
||||
public class CombatSelectItemClickEventArgs : GameEventArgs
|
||||
{
|
||||
public static int EventId => typeof(CombatSelectItemClickEventArgs).GetHashCode();
|
||||
|
||||
public override int Id => EventId;
|
||||
|
||||
public CombatSelectActionType ActionType { get; private set; } = CombatSelectActionType.None;
|
||||
|
||||
public int ActionIndex { get; private set; } = -1;
|
||||
|
||||
public static CombatSelectItemClickEventArgs Create(CombatSelectActionType actionType, int actionIndex)
|
||||
{
|
||||
CombatSelectItemClickEventArgs args = ReferencePool.Acquire<CombatSelectItemClickEventArgs>();
|
||||
args.ActionType = actionType;
|
||||
args.ActionIndex = actionIndex;
|
||||
return args;
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
ActionType = CombatSelectActionType.None;
|
||||
ActionIndex = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d8a9619fe7cdb054ea7eeac820f95ae4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -63,6 +63,7 @@ namespace GeometryTD.Procedure
|
|||
GameEntry.CombatNode.OnShutdown();
|
||||
GameEntry.Event.Unsubscribe(NodeEnterEventArgs.EventId, OnNodeEnter);
|
||||
GameEntry.Event.Unsubscribe(NodeCompleteEventArgs.EventId, OnNodeComplete);
|
||||
_repoFormUseCase = null;
|
||||
|
||||
base.OnLeave(procedureOwner, isShutdown);
|
||||
}
|
||||
|
|
@ -85,4 +86,4 @@ namespace GeometryTD.Procedure
|
|||
GameEntry.UIRouter.OpenUI(UIFormType.MainForm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
namespace GeometryTD.UI
|
||||
{
|
||||
public class CombatFinishFormContext : UIContext
|
||||
{
|
||||
public string EnemyKilledText;
|
||||
public string GoldGainedText;
|
||||
public RepoItemContext[] RewardItems;
|
||||
public bool CanReturn;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e8fd0de74a04422488e2e78615713bca
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
namespace GeometryTD.UI
|
||||
{
|
||||
public enum CombatSelectActionType : byte
|
||||
{
|
||||
None = 0,
|
||||
BuildTower = 1,
|
||||
UpgradeTower = 2,
|
||||
DestroyTower = 3
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2813faca2af4a574ea60171b6221c69c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class CombatSelectFormContext : UIContext
|
||||
{
|
||||
public bool IsVisible;
|
||||
public Vector2 ContentPosition;
|
||||
public bool ShowBuildArea;
|
||||
public bool ShowFuncArea;
|
||||
public TowerSelectItemContext[] BuildItems;
|
||||
public TowerSelectItemContext UpgradeItem;
|
||||
public TowerSelectItemContext DestroyItem;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 10d666a1cb0d16a44a6fa5e92d6e8f3f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class TowerSelectItemContext : UIContext
|
||||
{
|
||||
public Sprite Icon;
|
||||
public string PriceText;
|
||||
public bool IsVisible;
|
||||
public bool IsInteractable;
|
||||
public CombatSelectActionType ActionType;
|
||||
public int ActionIndex;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1a54121a8acc89c43b45617601960c33
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
using System.Collections.Generic;
|
||||
using GameFramework.Event;
|
||||
using GeometryTD.CustomEvent;
|
||||
using GeometryTD.Definition;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class CombatFinishFormController : UIFormControllerCommonBase<CombatFinishFormContext, CombatFinishForm>
|
||||
{
|
||||
private CombatFinishFormUseCase _useCase;
|
||||
|
||||
protected override UIFormType UIFormTypeId => UIFormType.CombatFinishForm;
|
||||
|
||||
protected override void RefreshUI(CombatFinishForm form, CombatFinishFormContext context)
|
||||
{
|
||||
form.RefreshUI(context);
|
||||
}
|
||||
|
||||
protected override void SubscribeCustomEvents()
|
||||
{
|
||||
GameEntry.Event.Subscribe(CombatFinishReturnEventArgs.EventId, OnCombatFinishReturnButtonClicked);
|
||||
}
|
||||
|
||||
protected override void UnsubscribeCustomEvents()
|
||||
{
|
||||
GameEntry.Event.Unsubscribe(CombatFinishReturnEventArgs.EventId, OnCombatFinishReturnButtonClicked);
|
||||
}
|
||||
|
||||
public override int? OpenUI(object userData = null)
|
||||
{
|
||||
if (userData is CombatFinishFormContext context)
|
||||
{
|
||||
return OpenUIInternal(context);
|
||||
}
|
||||
|
||||
if (userData is CombatFinishFormRawData rawDataFromUserData)
|
||||
{
|
||||
return OpenUI(rawDataFromUserData);
|
||||
}
|
||||
|
||||
if (userData != null)
|
||||
{
|
||||
Log.Warning("CombatFinishFormController.OpenUI() userData type is invalid.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_useCase == null)
|
||||
{
|
||||
Log.Error("CombatFinishFormController.OpenUI() useCase is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
CombatFinishFormRawData rawData = _useCase.CreateInitialModel();
|
||||
return OpenUI(rawData);
|
||||
}
|
||||
|
||||
public int? OpenUI(CombatFinishFormRawData rawData)
|
||||
{
|
||||
CombatFinishFormContext context = BuildContext(rawData);
|
||||
return OpenUIInternal(context);
|
||||
}
|
||||
|
||||
public override void BindUseCase(IUIUseCase useCase)
|
||||
{
|
||||
if (!(useCase is CombatFinishFormUseCase combatFinishFormUseCase))
|
||||
{
|
||||
Log.Error("CombatFinishFormController.BindUseCase() useCase is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
_useCase = combatFinishFormUseCase;
|
||||
}
|
||||
|
||||
private static CombatFinishFormContext BuildContext(CombatFinishFormRawData rawData)
|
||||
{
|
||||
if (rawData == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new CombatFinishFormContext
|
||||
{
|
||||
EnemyKilledText = rawData.DefeatedEnemyCount.ToString(),
|
||||
GoldGainedText = rawData.GainedGold.ToString(),
|
||||
RewardItems = BuildRewardItems(rawData.RewardInventory),
|
||||
CanReturn = rawData.CanReturn
|
||||
};
|
||||
}
|
||||
|
||||
private static RepoItemContext[] BuildRewardItems(BackpackInventoryData inventory)
|
||||
{
|
||||
if (inventory == null)
|
||||
{
|
||||
return System.Array.Empty<RepoItemContext>();
|
||||
}
|
||||
|
||||
List<RepoItemContext> itemContexts = new List<RepoItemContext>();
|
||||
|
||||
if (inventory.Towers != null)
|
||||
{
|
||||
for (int i = 0; i < inventory.Towers.Count; i++)
|
||||
{
|
||||
var tower = inventory.Towers[i];
|
||||
if (tower == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
itemContexts.Add(new RepoItemContext
|
||||
{
|
||||
Title = $"[Tower] {tower.Name} ({tower.Rarity})",
|
||||
InstanceId = tower.InstanceId,
|
||||
ComponentSlotType = TowerCompSlotType.None
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (inventory.MuzzleComponents != null)
|
||||
{
|
||||
for (int i = 0; i < inventory.MuzzleComponents.Count; i++)
|
||||
{
|
||||
var item = inventory.MuzzleComponents[i];
|
||||
if (item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
itemContexts.Add(new RepoItemContext
|
||||
{
|
||||
Title = $"[Muzzle] {item.Name} ({item.Rarity})",
|
||||
InstanceId = item.InstanceId,
|
||||
ComponentSlotType = TowerCompSlotType.Muzzle
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (inventory.BearingComponents != null)
|
||||
{
|
||||
for (int i = 0; i < inventory.BearingComponents.Count; i++)
|
||||
{
|
||||
var item = inventory.BearingComponents[i];
|
||||
if (item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
itemContexts.Add(new RepoItemContext
|
||||
{
|
||||
Title = $"[Bearing] {item.Name} ({item.Rarity})",
|
||||
InstanceId = item.InstanceId,
|
||||
ComponentSlotType = TowerCompSlotType.Bearing
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (inventory.BaseComponents != null)
|
||||
{
|
||||
for (int i = 0; i < inventory.BaseComponents.Count; i++)
|
||||
{
|
||||
var item = inventory.BaseComponents[i];
|
||||
if (item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
itemContexts.Add(new RepoItemContext
|
||||
{
|
||||
Title = $"[Base] {item.Name} ({item.Rarity})",
|
||||
InstanceId = item.InstanceId,
|
||||
ComponentSlotType = TowerCompSlotType.Base
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return itemContexts.ToArray();
|
||||
}
|
||||
|
||||
private void OnCombatFinishReturnButtonClicked(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(sender is CombatFinishForm) || !(e is CombatFinishReturnEventArgs))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_useCase?.TryReturnToMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6f9701c56f0541208d99c47b7e64e58c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,266 @@
|
|||
using GameFramework.Event;
|
||||
using GeometryTD.CustomEvent;
|
||||
using GeometryTD.Definition;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class CombatSelectFormController : UIFormControllerCommonBase<CombatSelectFormContext, CombatSelectForm>
|
||||
{
|
||||
private CombatSelectFormUseCase _useCase;
|
||||
|
||||
protected override UIFormType UIFormTypeId => UIFormType.CombatSelectForm;
|
||||
|
||||
protected override void RefreshUI(CombatSelectForm form, CombatSelectFormContext context)
|
||||
{
|
||||
form.RefreshUI(context);
|
||||
}
|
||||
|
||||
protected override void SubscribeCustomEvents()
|
||||
{
|
||||
GameEntry.Event.Subscribe(CombatSelectItemClickEventArgs.EventId, OnSelectItemClicked);
|
||||
}
|
||||
|
||||
protected override void UnsubscribeCustomEvents()
|
||||
{
|
||||
GameEntry.Event.Unsubscribe(CombatSelectItemClickEventArgs.EventId, OnSelectItemClicked);
|
||||
}
|
||||
|
||||
public int? OpenUI(CombatSelectFormRawData rawData)
|
||||
{
|
||||
CombatSelectFormContext context = BuildContext(rawData);
|
||||
if (context == null || !context.IsVisible)
|
||||
{
|
||||
CloseUI();
|
||||
return null;
|
||||
}
|
||||
|
||||
return OpenUIInternal(context);
|
||||
}
|
||||
|
||||
public override int? OpenUI(object userData = null)
|
||||
{
|
||||
if (userData is CombatSelectFormUserData clickUserData)
|
||||
{
|
||||
return OpenUI(clickUserData);
|
||||
}
|
||||
|
||||
if (userData is CombatSelectFormContext context)
|
||||
{
|
||||
if (context == null || !context.IsVisible)
|
||||
{
|
||||
CloseUI();
|
||||
return null;
|
||||
}
|
||||
|
||||
return OpenUIInternal(context);
|
||||
}
|
||||
|
||||
if (userData is CombatSelectFormRawData rawDataFromUserData)
|
||||
{
|
||||
return OpenUI(rawDataFromUserData);
|
||||
}
|
||||
|
||||
if (userData != null)
|
||||
{
|
||||
Log.Warning("CombatSelectFormController.OpenUI() userData type is invalid.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_useCase == null)
|
||||
{
|
||||
Log.Error("CombatSelectFormController.OpenUI() useCase is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
CombatSelectFormRawData rawData = _useCase.CreateInitialModel();
|
||||
return OpenUI(rawData);
|
||||
}
|
||||
|
||||
public int? OpenUI(CombatSelectFormUserData userData)
|
||||
{
|
||||
if (_useCase == null)
|
||||
{
|
||||
Log.Error("CombatSelectFormController.OpenUI() useCase is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (userData == null)
|
||||
{
|
||||
_useCase.Hide();
|
||||
CloseUI();
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (userData.ClickObjectType)
|
||||
{
|
||||
case CombatSelectClickObjectType.Foundation:
|
||||
_useCase.ShowForFoundation(userData.ContentPosition);
|
||||
break;
|
||||
case CombatSelectClickObjectType.Tower:
|
||||
_useCase.SetUpgradePrice(userData.UpgradeCost);
|
||||
_useCase.SetDestroyGain(userData.DestroyGain);
|
||||
_useCase.ShowForTower(userData.ContentPosition);
|
||||
break;
|
||||
default:
|
||||
_useCase.Hide();
|
||||
CloseUI();
|
||||
return null;
|
||||
}
|
||||
|
||||
return OpenUI(_useCase.TryRefresh());
|
||||
}
|
||||
|
||||
public override void BindUseCase(IUIUseCase useCase)
|
||||
{
|
||||
if (!(useCase is CombatSelectFormUseCase combatSelectFormUseCase))
|
||||
{
|
||||
Log.Error("CombatSelectFormController.BindUseCase() useCase is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
_useCase = combatSelectFormUseCase;
|
||||
}
|
||||
|
||||
public int? ShowForFoundation(Vector2 contentPosition)
|
||||
{
|
||||
if (_useCase == null)
|
||||
{
|
||||
Log.Error("CombatSelectFormController.ShowForFoundation() useCase is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
_useCase.ShowForFoundation(contentPosition);
|
||||
return OpenUI(_useCase.TryRefresh());
|
||||
}
|
||||
|
||||
public int? ShowForTower(Vector2 contentPosition, int upgradeCost, int destroyGain)
|
||||
{
|
||||
if (_useCase == null)
|
||||
{
|
||||
Log.Error("CombatSelectFormController.ShowForTower() useCase is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
_useCase.SetUpgradePrice(upgradeCost);
|
||||
_useCase.SetDestroyGain(destroyGain);
|
||||
_useCase.ShowForTower(contentPosition);
|
||||
return OpenUI(_useCase.TryRefresh());
|
||||
}
|
||||
|
||||
public void HideSelection()
|
||||
{
|
||||
_useCase?.Hide();
|
||||
CloseUI();
|
||||
}
|
||||
|
||||
public void RefreshFromUseCase()
|
||||
{
|
||||
if (_useCase == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CombatSelectFormRawData rawData = _useCase.TryRefresh();
|
||||
CombatSelectFormContext context = BuildContext(rawData);
|
||||
if (context == null || !context.IsVisible)
|
||||
{
|
||||
CloseUI();
|
||||
return;
|
||||
}
|
||||
|
||||
SetContext(context);
|
||||
RefreshCurrentUI();
|
||||
}
|
||||
|
||||
private static CombatSelectFormContext BuildContext(CombatSelectFormRawData rawData)
|
||||
{
|
||||
if (rawData == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new CombatSelectFormContext
|
||||
{
|
||||
IsVisible = rawData.IsVisible,
|
||||
ContentPosition = rawData.ContentPosition,
|
||||
ShowBuildArea = rawData.IsVisible && rawData.DisplayMode == CombatSelectDisplayMode.Build,
|
||||
ShowFuncArea = rawData.IsVisible && rawData.DisplayMode == CombatSelectDisplayMode.Func,
|
||||
BuildItems = BuildItemContexts(rawData.BuildItems),
|
||||
UpgradeItem = BuildItemContext(rawData.UpgradeItem),
|
||||
DestroyItem = BuildItemContext(rawData.DestroyItem)
|
||||
};
|
||||
}
|
||||
|
||||
private static TowerSelectItemContext[] BuildItemContexts(TowerSelectItemRawData[] itemRawData)
|
||||
{
|
||||
if (itemRawData == null || itemRawData.Length <= 0)
|
||||
{
|
||||
return System.Array.Empty<TowerSelectItemContext>();
|
||||
}
|
||||
|
||||
TowerSelectItemContext[] contexts = new TowerSelectItemContext[itemRawData.Length];
|
||||
for (int i = 0; i < itemRawData.Length; i++)
|
||||
{
|
||||
contexts[i] = BuildItemContext(itemRawData[i]);
|
||||
}
|
||||
|
||||
return contexts;
|
||||
}
|
||||
|
||||
private static TowerSelectItemContext BuildItemContext(TowerSelectItemRawData rawData)
|
||||
{
|
||||
if (rawData == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new TowerSelectItemContext
|
||||
{
|
||||
Icon = rawData.Icon,
|
||||
PriceText = BuildPriceText(rawData.Price, rawData.IsGain),
|
||||
IsVisible = rawData.IsVisible,
|
||||
IsInteractable = rawData.IsInteractable,
|
||||
ActionType = rawData.ActionType,
|
||||
ActionIndex = rawData.ActionIndex
|
||||
};
|
||||
}
|
||||
|
||||
private static string BuildPriceText(int price, bool isGain)
|
||||
{
|
||||
int positivePrice = Mathf.Max(0, price);
|
||||
if (isGain)
|
||||
{
|
||||
return $"+{positivePrice}";
|
||||
}
|
||||
|
||||
return positivePrice.ToString();
|
||||
}
|
||||
|
||||
private void OnSelectItemClicked(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is CombatSelectItemClickEventArgs args))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_useCase == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Form != null && sender is Component component)
|
||||
{
|
||||
CombatSelectForm ownerForm = component.GetComponentInParent<CombatSelectForm>();
|
||||
if (ownerForm != Form)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_useCase.TryExecuteAction(args.ActionType, args.ActionIndex);
|
||||
RefreshFromUseCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 03cc7fd540e32b34bad5b8786717725f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
using GeometryTD.Definition;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class CombatFinishFormRawData
|
||||
{
|
||||
public int DefeatedEnemyCount;
|
||||
public int GainedGold;
|
||||
public BackpackInventoryData RewardInventory;
|
||||
public bool CanReturn;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6ee396100a8f4c7f99e3978c7154c00b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
namespace GeometryTD.UI
|
||||
{
|
||||
public enum CombatSelectClickObjectType : byte
|
||||
{
|
||||
None = 0,
|
||||
Foundation = 1,
|
||||
Tower = 2
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d7dfced55a9048eea7df3b269e55e81e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
namespace GeometryTD.UI
|
||||
{
|
||||
public enum CombatSelectDisplayMode : byte
|
||||
{
|
||||
Hidden = 0,
|
||||
Build = 1,
|
||||
Func = 2
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cc38eaebc2c34d9428b1233aa57c653e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class CombatSelectFormRawData
|
||||
{
|
||||
public bool IsVisible;
|
||||
public Vector2 ContentPosition;
|
||||
public CombatSelectDisplayMode DisplayMode;
|
||||
public TowerSelectItemRawData[] BuildItems;
|
||||
public TowerSelectItemRawData UpgradeItem;
|
||||
public TowerSelectItemRawData DestroyItem;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0958de13e46a34742acfa4352c7b6d4e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class CombatSelectFormUserData
|
||||
{
|
||||
public CombatSelectClickObjectType ClickObjectType;
|
||||
public Vector2 ContentPosition;
|
||||
public Vector3 WorldPosition;
|
||||
public Vector3Int CellPosition;
|
||||
public int TowerEntityId;
|
||||
public int UpgradeCost;
|
||||
public int DestroyGain;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 53ae7da2d92244c893cea60fa4c05951
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class TowerSelectItemRawData
|
||||
{
|
||||
public Sprite Icon;
|
||||
public int Price;
|
||||
public bool IsGain;
|
||||
public bool IsVisible;
|
||||
public bool IsInteractable;
|
||||
public CombatSelectActionType ActionType;
|
||||
public int ActionIndex;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 583bd0c79840d6649a4f340af2a8bf5b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
using GeometryTD.CustomComponent;
|
||||
using GeometryTD.Definition;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class CombatFinishFormUseCase : IUIUseCase
|
||||
{
|
||||
private readonly RepoFormUseCase _repoFormUseCase = new RepoFormUseCase();
|
||||
private CombatScheduler _combatScheduler;
|
||||
private int _defeatedEnemyCount;
|
||||
private int _gainedGold;
|
||||
private BackpackInventoryData _rewardInventory;
|
||||
private bool _isSummaryPrepared;
|
||||
|
||||
public CombatFinishFormRawData CreateInitialModel()
|
||||
{
|
||||
return BuildModel();
|
||||
}
|
||||
|
||||
public CombatFinishFormRawData TryRefresh()
|
||||
{
|
||||
return BuildModel();
|
||||
}
|
||||
|
||||
public void SetSummary(int defeatedEnemyCount, int gainedGold, BackpackInventoryData rewardInventory = null)
|
||||
{
|
||||
_defeatedEnemyCount = Mathf.Max(0, defeatedEnemyCount);
|
||||
_gainedGold = Mathf.Max(0, gainedGold);
|
||||
_rewardInventory = rewardInventory;
|
||||
_isSummaryPrepared = true;
|
||||
}
|
||||
|
||||
public CombatFinishFormUseCase(CombatScheduler combatScheduler)
|
||||
{
|
||||
this._combatScheduler = combatScheduler;
|
||||
}
|
||||
|
||||
public bool TryReturnToMenu()
|
||||
{
|
||||
return _combatScheduler.OnCombatFinishReturnRequested();
|
||||
}
|
||||
|
||||
private CombatFinishFormRawData BuildModel()
|
||||
{
|
||||
if (!_isSummaryPrepared)
|
||||
{
|
||||
_defeatedEnemyCount = GameEntry.CombatNode != null
|
||||
? Mathf.Max(0, GameEntry.CombatNode.LastDefeatedEnemyCount)
|
||||
: 0;
|
||||
_gainedGold = GameEntry.CombatNode != null
|
||||
? Mathf.Max(0, GameEntry.CombatNode.LastGainedGold)
|
||||
: 0;
|
||||
_rewardInventory = null;
|
||||
}
|
||||
|
||||
BackpackInventoryData rewardInventory = _rewardInventory;
|
||||
if (rewardInventory == null)
|
||||
{
|
||||
RepoFormRawData repoRawData = _repoFormUseCase.CreateInitialModel();
|
||||
rewardInventory = repoRawData != null ? repoRawData.Inventory : null;
|
||||
}
|
||||
|
||||
return new CombatFinishFormRawData
|
||||
{
|
||||
DefeatedEnemyCount = _defeatedEnemyCount,
|
||||
GainedGold = _gainedGold,
|
||||
RewardInventory = rewardInventory,
|
||||
CanReturn = true
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9e5ee9e49e70480c9f98e6c77eb86f5f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,300 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class CombatSelectFormUseCase : IUIUseCase
|
||||
{
|
||||
private const int BuildOptionCount = 4;
|
||||
|
||||
private readonly Option[] _buildOptions = new Option[BuildOptionCount];
|
||||
private readonly Option _upgradeOption = new Option(CombatSelectActionType.UpgradeTower, 0, true);
|
||||
private readonly Option _destroyOption = new Option(CombatSelectActionType.DestroyTower, 0, false);
|
||||
|
||||
private Func<int> _coinProvider;
|
||||
private bool _isVisible;
|
||||
private CombatSelectDisplayMode _displayMode = CombatSelectDisplayMode.Hidden;
|
||||
private Vector2 _contentPosition;
|
||||
|
||||
public CombatSelectFormUseCase()
|
||||
{
|
||||
_coinProvider = DefaultCoinProvider;
|
||||
|
||||
for (int i = 0; i < BuildOptionCount; i++)
|
||||
{
|
||||
_buildOptions[i] = new Option(CombatSelectActionType.BuildTower, i, true);
|
||||
}
|
||||
}
|
||||
|
||||
public CombatSelectFormRawData CreateInitialModel()
|
||||
{
|
||||
return BuildModel();
|
||||
}
|
||||
|
||||
public CombatSelectFormRawData TryRefresh()
|
||||
{
|
||||
return BuildModel();
|
||||
}
|
||||
|
||||
public void SetCoinProvider(Func<int> coinProvider)
|
||||
{
|
||||
_coinProvider = coinProvider ?? DefaultCoinProvider;
|
||||
}
|
||||
|
||||
public void SetBuildAction(int buildIndex, Func<bool> buildAction, int cost, Sprite icon = null)
|
||||
{
|
||||
if (buildIndex < 0 || buildIndex >= _buildOptions.Length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Option option = _buildOptions[buildIndex];
|
||||
option.Action = buildAction;
|
||||
option.Price = Mathf.Max(0, cost);
|
||||
option.Icon = icon;
|
||||
option.IsGain = false;
|
||||
option.IsVisible = true;
|
||||
}
|
||||
|
||||
public void SetBuildAction(int buildIndex, Action buildAction, int cost, Sprite icon = null)
|
||||
{
|
||||
if (buildAction == null)
|
||||
{
|
||||
SetBuildAction(buildIndex, (Func<bool>)null, cost, icon);
|
||||
return;
|
||||
}
|
||||
|
||||
SetBuildAction(
|
||||
buildIndex,
|
||||
() =>
|
||||
{
|
||||
buildAction.Invoke();
|
||||
return true;
|
||||
},
|
||||
cost,
|
||||
icon);
|
||||
}
|
||||
|
||||
public void SetUpgradeAction(Func<bool> upgradeAction, int cost, Sprite icon = null)
|
||||
{
|
||||
_upgradeOption.Action = upgradeAction;
|
||||
_upgradeOption.Price = Mathf.Max(0, cost);
|
||||
_upgradeOption.Icon = icon;
|
||||
_upgradeOption.IsGain = false;
|
||||
_upgradeOption.IsVisible = true;
|
||||
}
|
||||
|
||||
public void SetUpgradeAction(Action upgradeAction, int cost, Sprite icon = null)
|
||||
{
|
||||
if (upgradeAction == null)
|
||||
{
|
||||
SetUpgradeAction((Func<bool>)null, cost, icon);
|
||||
return;
|
||||
}
|
||||
|
||||
SetUpgradeAction(
|
||||
() =>
|
||||
{
|
||||
upgradeAction.Invoke();
|
||||
return true;
|
||||
},
|
||||
cost,
|
||||
icon);
|
||||
}
|
||||
|
||||
public void SetDestroyAction(Func<bool> destroyAction, int gain, Sprite icon = null)
|
||||
{
|
||||
_destroyOption.Action = destroyAction;
|
||||
_destroyOption.Price = Mathf.Max(0, gain);
|
||||
_destroyOption.Icon = icon;
|
||||
_destroyOption.IsGain = true;
|
||||
_destroyOption.IsVisible = true;
|
||||
}
|
||||
|
||||
public void SetDestroyAction(Action destroyAction, int gain, Sprite icon = null)
|
||||
{
|
||||
if (destroyAction == null)
|
||||
{
|
||||
SetDestroyAction((Func<bool>)null, gain, icon);
|
||||
return;
|
||||
}
|
||||
|
||||
SetDestroyAction(
|
||||
() =>
|
||||
{
|
||||
destroyAction.Invoke();
|
||||
return true;
|
||||
},
|
||||
gain,
|
||||
icon);
|
||||
}
|
||||
|
||||
public void SetUpgradePrice(int cost)
|
||||
{
|
||||
_upgradeOption.Price = Mathf.Max(0, cost);
|
||||
}
|
||||
|
||||
public void SetDestroyGain(int gain)
|
||||
{
|
||||
_destroyOption.Price = Mathf.Max(0, gain);
|
||||
_destroyOption.IsGain = true;
|
||||
}
|
||||
|
||||
public void ShowForFoundation(Vector2 contentPosition)
|
||||
{
|
||||
_contentPosition = contentPosition;
|
||||
_displayMode = CombatSelectDisplayMode.Build;
|
||||
_isVisible = true;
|
||||
}
|
||||
|
||||
public void ShowForTower(Vector2 contentPosition)
|
||||
{
|
||||
_contentPosition = contentPosition;
|
||||
_displayMode = CombatSelectDisplayMode.Func;
|
||||
_isVisible = true;
|
||||
}
|
||||
|
||||
public void Hide()
|
||||
{
|
||||
_isVisible = false;
|
||||
_displayMode = CombatSelectDisplayMode.Hidden;
|
||||
}
|
||||
|
||||
public bool TryExecuteAction(CombatSelectActionType actionType, int actionIndex)
|
||||
{
|
||||
if (!_isVisible)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Option option = GetOption(actionType, actionIndex);
|
||||
if (option == null || !option.IsVisible)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int currentCoin = Mathf.Max(0, _coinProvider != null ? _coinProvider.Invoke() : 0);
|
||||
if (!CanExecute(option, currentCoin))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (option.Action == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = option.Action.Invoke();
|
||||
if (result)
|
||||
{
|
||||
Hide();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private CombatSelectFormRawData BuildModel()
|
||||
{
|
||||
int currentCoin = Mathf.Max(0, _coinProvider != null ? _coinProvider.Invoke() : 0);
|
||||
TowerSelectItemRawData[] buildItems = new TowerSelectItemRawData[_buildOptions.Length];
|
||||
for (int i = 0; i < _buildOptions.Length; i++)
|
||||
{
|
||||
buildItems[i] = BuildOptionRawData(_buildOptions[i], currentCoin);
|
||||
}
|
||||
|
||||
return new CombatSelectFormRawData
|
||||
{
|
||||
IsVisible = _isVisible,
|
||||
ContentPosition = _contentPosition,
|
||||
DisplayMode = _displayMode,
|
||||
BuildItems = buildItems,
|
||||
UpgradeItem = BuildOptionRawData(_upgradeOption, currentCoin),
|
||||
DestroyItem = BuildOptionRawData(_destroyOption, currentCoin)
|
||||
};
|
||||
}
|
||||
|
||||
private Option GetOption(CombatSelectActionType actionType, int actionIndex)
|
||||
{
|
||||
switch (actionType)
|
||||
{
|
||||
case CombatSelectActionType.BuildTower:
|
||||
if (actionIndex < 0 || actionIndex >= _buildOptions.Length)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return _buildOptions[actionIndex];
|
||||
case CombatSelectActionType.UpgradeTower:
|
||||
return _upgradeOption;
|
||||
case CombatSelectActionType.DestroyTower:
|
||||
return _destroyOption;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static int DefaultCoinProvider()
|
||||
{
|
||||
if (GameEntry.CombatNode == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Mathf.Max(0, GameEntry.CombatNode.CurrentCoin);
|
||||
}
|
||||
|
||||
private static bool CanExecute(Option option, int currentCoin)
|
||||
{
|
||||
if (option == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (option.RequiresAffordable && currentCoin < option.Price)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static TowerSelectItemRawData BuildOptionRawData(Option option, int currentCoin)
|
||||
{
|
||||
if (option == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new TowerSelectItemRawData
|
||||
{
|
||||
Icon = option.Icon,
|
||||
Price = option.Price,
|
||||
IsGain = option.IsGain,
|
||||
IsVisible = option.IsVisible,
|
||||
IsInteractable = option.Action != null && CanExecute(option, currentCoin),
|
||||
ActionType = option.ActionType,
|
||||
ActionIndex = option.ActionIndex
|
||||
};
|
||||
}
|
||||
|
||||
private sealed class Option
|
||||
{
|
||||
public readonly CombatSelectActionType ActionType;
|
||||
public readonly int ActionIndex;
|
||||
public readonly bool RequiresAffordable;
|
||||
|
||||
public int Price;
|
||||
public bool IsGain;
|
||||
public bool IsVisible = true;
|
||||
public Sprite Icon;
|
||||
public Func<bool> Action;
|
||||
|
||||
public Option(CombatSelectActionType actionType, int actionIndex, bool requiresAffordable)
|
||||
{
|
||||
ActionType = actionType;
|
||||
ActionIndex = actionIndex;
|
||||
RequiresAffordable = requiresAffordable;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 040ca52500c562c4d9737d44a8940050
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
using System.Collections.Generic;
|
||||
using GameFramework.ObjectPool;
|
||||
using GeometryTD.CustomEvent;
|
||||
using GeometryTD.PoolObjectBase;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class CombatFinishForm : UGuiForm
|
||||
{
|
||||
[SerializeField] private TMP_Text _enemyKilledText;
|
||||
[SerializeField] private TMP_Text _goldGainedText;
|
||||
[SerializeField] private Transform _itemGainedParent;
|
||||
[SerializeField] private RepoItem _itemTemplate;
|
||||
[SerializeField] private int _instancePoolCapacity = 16;
|
||||
|
||||
private readonly List<RepoItem> _activeItems = new List<RepoItem>();
|
||||
private IObjectPool<RepoItemObject> _itemPool;
|
||||
private string _poolName;
|
||||
private CombatFinishFormContext _context;
|
||||
|
||||
public void RefreshUI(CombatFinishFormContext context)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
if (_enemyKilledText != null)
|
||||
{
|
||||
_enemyKilledText.text = context?.EnemyKilledText ?? "0";
|
||||
}
|
||||
|
||||
if (_goldGainedText != null)
|
||||
{
|
||||
_goldGainedText.text = context?.GoldGainedText ?? "0";
|
||||
}
|
||||
|
||||
RefreshRewardItems(context?.RewardItems);
|
||||
}
|
||||
|
||||
public void OnReturnButtonClick()
|
||||
{
|
||||
if (_context != null && !_context.CanReturn)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GameEntry.Event.Fire(this, CombatFinishReturnEventArgs.Create());
|
||||
}
|
||||
|
||||
protected override void OnOpen(object userData)
|
||||
{
|
||||
base.OnOpen(userData);
|
||||
|
||||
if (userData is CombatFinishFormContext context)
|
||||
{
|
||||
RefreshUI(context);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Warning("CombatFinishForm requires CombatFinishFormContext as userData.");
|
||||
}
|
||||
|
||||
protected override void OnClose(bool isShutdown, object userData)
|
||||
{
|
||||
_context = null;
|
||||
|
||||
if (_enemyKilledText != null)
|
||||
{
|
||||
_enemyKilledText.text = string.Empty;
|
||||
}
|
||||
|
||||
if (_goldGainedText != null)
|
||||
{
|
||||
_goldGainedText.text = string.Empty;
|
||||
}
|
||||
|
||||
ClearRewardItems();
|
||||
base.OnClose(isShutdown, userData);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
ClearRewardItems();
|
||||
_itemPool?.ReleaseAllUnused();
|
||||
}
|
||||
|
||||
private void EnsurePool()
|
||||
{
|
||||
if (_itemPool != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_poolName = $"CombatFinishRepoItemPool_{GetInstanceID()}";
|
||||
_itemPool = GameEntry.ObjectPool.CreateSingleSpawnObjectPool<RepoItemObject>(_poolName, _instancePoolCapacity);
|
||||
}
|
||||
|
||||
private void RefreshRewardItems(RepoItemContext[] rewardItems)
|
||||
{
|
||||
ClearRewardItems();
|
||||
|
||||
if (rewardItems == null || rewardItems.Length <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EnsurePool();
|
||||
|
||||
for (int i = 0; i < rewardItems.Length; i++)
|
||||
{
|
||||
RepoItem item = CreateOrSpawnItem();
|
||||
if (item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
item.gameObject.SetActive(true);
|
||||
item.OnInit(rewardItems[i]);
|
||||
_activeItems.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearRewardItems()
|
||||
{
|
||||
for (int i = _activeItems.Count - 1; i >= 0; i--)
|
||||
{
|
||||
RepoItem item = _activeItems[i];
|
||||
if (item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
item.OnReset();
|
||||
item.gameObject.SetActive(false);
|
||||
_itemPool?.Unspawn(item);
|
||||
}
|
||||
|
||||
_activeItems.Clear();
|
||||
}
|
||||
|
||||
private RepoItem CreateOrSpawnItem()
|
||||
{
|
||||
if (_itemGainedParent == null)
|
||||
{
|
||||
Log.Warning("CombatFinishForm requires an item gained parent.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_itemPool == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
RepoItemObject itemObject = _itemPool.Spawn();
|
||||
RepoItem item = itemObject != null ? (RepoItem)itemObject.Target : null;
|
||||
if (item == null)
|
||||
{
|
||||
if (_itemTemplate == null)
|
||||
{
|
||||
Log.Warning("CombatFinishForm requires an item template.");
|
||||
return null;
|
||||
}
|
||||
|
||||
item = Instantiate(_itemTemplate);
|
||||
_itemPool.Register(RepoItemObject.Create(item), true);
|
||||
}
|
||||
|
||||
item.transform.SetParent(_itemGainedParent, false);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9679d912cb0ce9e4d95db41021d94463
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class CombatSelectBuildArea : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private TowerSelectItem[] _towerBuildItems;
|
||||
|
||||
public void OnInit(TowerSelectItemContext[] itemContexts)
|
||||
{
|
||||
if (_towerBuildItems == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _towerBuildItems.Length; i++)
|
||||
{
|
||||
TowerSelectItem item = _towerBuildItems[i];
|
||||
if (item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
TowerSelectItemContext itemContext =
|
||||
itemContexts != null && i < itemContexts.Length ? itemContexts[i] : null;
|
||||
bool isVisible = itemContext != null && itemContext.IsVisible;
|
||||
|
||||
item.gameObject.SetActive(isVisible);
|
||||
if (isVisible)
|
||||
{
|
||||
item.OnInit(itemContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.OnReset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
if (_towerBuildItems == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _towerBuildItems.Length; i++)
|
||||
{
|
||||
TowerSelectItem item = _towerBuildItems[i];
|
||||
if (item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
item.OnReset();
|
||||
item.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 73470d4b91d03924b9233b58311e917c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class CombatSelectForm : UGuiForm
|
||||
{
|
||||
[SerializeField] private RectTransform _content;
|
||||
|
||||
[SerializeField] private CombatSelectBuildArea _buildArea;
|
||||
|
||||
[SerializeField] private CombatSelectFuncArea _funcArea;
|
||||
|
||||
private CombatSelectFormContext _context;
|
||||
|
||||
public void RefreshUI(CombatSelectFormContext context)
|
||||
{
|
||||
_context = context;
|
||||
EnsureContentReference();
|
||||
|
||||
if (_content == null)
|
||||
{
|
||||
Log.Warning("CombatSelectForm content is missing.");
|
||||
return;
|
||||
}
|
||||
|
||||
bool isVisible = context != null && context.IsVisible;
|
||||
_content.gameObject.SetActive(isVisible);
|
||||
if (!isVisible)
|
||||
{
|
||||
_buildArea?.OnReset();
|
||||
_funcArea?.OnReset();
|
||||
return;
|
||||
}
|
||||
|
||||
_content.anchoredPosition = context.ContentPosition;
|
||||
|
||||
if (_buildArea != null)
|
||||
{
|
||||
bool showBuildArea = context.ShowBuildArea;
|
||||
_buildArea.gameObject.SetActive(showBuildArea);
|
||||
if (showBuildArea)
|
||||
{
|
||||
_buildArea.OnInit(context.BuildItems);
|
||||
}
|
||||
else
|
||||
{
|
||||
_buildArea.OnReset();
|
||||
}
|
||||
}
|
||||
|
||||
if (_funcArea != null)
|
||||
{
|
||||
bool showFuncArea = context.ShowFuncArea;
|
||||
_funcArea.gameObject.SetActive(showFuncArea);
|
||||
if (showFuncArea)
|
||||
{
|
||||
_funcArea.OnInit(context.UpgradeItem, context.DestroyItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
_funcArea.OnReset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnOpen(object userData)
|
||||
{
|
||||
base.OnOpen(userData);
|
||||
|
||||
if (userData is CombatSelectFormContext context)
|
||||
{
|
||||
RefreshUI(context);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Warning("CombatSelectForm requires CombatSelectFormContext as userData.");
|
||||
RefreshUI(null);
|
||||
}
|
||||
|
||||
protected override void OnClose(bool isShutdown, object userData)
|
||||
{
|
||||
_context = null;
|
||||
_buildArea?.OnReset();
|
||||
_funcArea?.OnReset();
|
||||
|
||||
if (_content != null)
|
||||
{
|
||||
_content.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
base.OnClose(isShutdown, userData);
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
EnsureContentReference();
|
||||
}
|
||||
|
||||
private void EnsureContentReference()
|
||||
{
|
||||
if (_content != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Transform contentTransform = transform.Find("Content");
|
||||
_content = contentTransform as RectTransform;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e62b0f7fcf3ccfd4e88da34d80cc14ab
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class CombatSelectFuncArea : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private TowerSelectItem _upgradeItem;
|
||||
|
||||
[SerializeField] private TowerSelectItem _destroyItem;
|
||||
|
||||
public void OnInit(TowerSelectItemContext upgradeItemContext, TowerSelectItemContext destroyItemContext)
|
||||
{
|
||||
RefreshItem(_upgradeItem, upgradeItemContext);
|
||||
RefreshItem(_destroyItem, destroyItemContext);
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
ResetItem(_upgradeItem);
|
||||
ResetItem(_destroyItem);
|
||||
}
|
||||
|
||||
private static void RefreshItem(TowerSelectItem item, TowerSelectItemContext context)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool isVisible = context != null && context.IsVisible;
|
||||
item.gameObject.SetActive(isVisible);
|
||||
if (isVisible)
|
||||
{
|
||||
item.OnInit(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.OnReset();
|
||||
}
|
||||
}
|
||||
|
||||
private static void ResetItem(TowerSelectItem item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
item.OnReset();
|
||||
item.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 891304d1e879da34c9b989ca2fcd14a7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using GeometryTD.CustomEvent;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class TowerSelectItem : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private Image _icon;
|
||||
|
||||
[SerializeField] private TMP_Text _price;
|
||||
|
||||
[SerializeField] private CommonButton _button;
|
||||
|
||||
private TowerSelectItemContext _context;
|
||||
private Sprite _defaultIcon;
|
||||
private bool _hasCachedDefaultIcon;
|
||||
|
||||
public void OnInit(TowerSelectItemContext context)
|
||||
{
|
||||
CacheDefaultIcon();
|
||||
_context = context;
|
||||
|
||||
if (_icon != null)
|
||||
{
|
||||
if (context != null && context.Icon != null)
|
||||
{
|
||||
_icon.sprite = context.Icon;
|
||||
}
|
||||
else if (_defaultIcon != null)
|
||||
{
|
||||
_icon.sprite = _defaultIcon;
|
||||
}
|
||||
|
||||
_icon.enabled = _icon.sprite != null;
|
||||
}
|
||||
|
||||
if (_price != null)
|
||||
{
|
||||
_price.text = context?.PriceText ?? string.Empty;
|
||||
}
|
||||
|
||||
if (_button != null)
|
||||
{
|
||||
_button.Interactive = context != null && context.IsInteractable;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
_context = null;
|
||||
CacheDefaultIcon();
|
||||
|
||||
if (_icon != null)
|
||||
{
|
||||
_icon.sprite = _defaultIcon;
|
||||
_icon.enabled = _icon.sprite != null;
|
||||
}
|
||||
|
||||
if (_price != null)
|
||||
{
|
||||
_price.text = string.Empty;
|
||||
}
|
||||
|
||||
if (_button != null)
|
||||
{
|
||||
_button.Interactive = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnClick()
|
||||
{
|
||||
if (_context == null || !_context.IsInteractable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GameEntry.Event.Fire(
|
||||
this,
|
||||
CombatSelectItemClickEventArgs.Create(_context.ActionType, _context.ActionIndex));
|
||||
}
|
||||
|
||||
private void CacheDefaultIcon()
|
||||
{
|
||||
if (_hasCachedDefaultIcon || _icon == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_defaultIcon = _icon.sprite;
|
||||
_hasCachedDefaultIcon = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4da5ef45ee18db44ebe3c015dddb61d6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f5c94ed3a9c85d04b800b859ca35ba2b
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -580,7 +580,7 @@ MonoBehaviour:
|
|||
m_Right: 0
|
||||
m_Top: 0
|
||||
m_Bottom: 0
|
||||
m_ChildAlignment: 4
|
||||
m_ChildAlignment: 5
|
||||
m_Spacing: 0
|
||||
m_ChildForceExpandWidth: 1
|
||||
m_ChildForceExpandHeight: 1
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 79b2d21e2060dfe4cb87d63dc65eccc3
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -61,7 +61,7 @@ MonoBehaviour:
|
|||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 0
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 0
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
|
|
|
|||
|
|
@ -0,0 +1,328 @@
|
|||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &5412566102378209566
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4152009404387722330}
|
||||
- component: {fileID: 3873445585384354699}
|
||||
- component: {fileID: 272351400812969041}
|
||||
m_Layer: 5
|
||||
m_Name: Icon
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &4152009404387722330
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5412566102378209566}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 274828358581623048}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 80, y: 80}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &3873445585384354699
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5412566102378209566}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &272351400812969041
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5412566102378209566}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 0
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 21300146, guid: 29c9cded5e558164aaf8c9bf08aa1510, type: 3}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!1 &7694008747632310308
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 274828358581623048}
|
||||
- component: {fileID: 4724049278947147719}
|
||||
m_Layer: 5
|
||||
m_Name: TowerSelectItem
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &274828358581623048
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7694008747632310308}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 8201350274026178469}
|
||||
- {fileID: 4152009404387722330}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 175}
|
||||
m_SizeDelta: {x: 100, y: 100}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &4724049278947147719
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7694008747632310308}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 4da5ef45ee18db44ebe3c015dddb61d6, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
_icon: {fileID: 272351400812969041}
|
||||
_price: {fileID: 6134693184638222351}
|
||||
_button: {fileID: 8644563062325311724}
|
||||
--- !u!1001 &5730140866277425490
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 274828358581623048}
|
||||
m_Modifications:
|
||||
- target: {fileID: 770565994539545022, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_Name
|
||||
value: CommonButton
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1920576543566152029, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_text
|
||||
value: 100
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[1].m_Mode
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[1].m_Target
|
||||
value:
|
||||
objectReference: {fileID: 4724049278947147719}
|
||||
- target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[1].m_MethodName
|
||||
value: OnClick
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[1].m_TargetAssemblyTypeName
|
||||
value: GeometryTD.UI.TowerSelectItem, Assembly-CSharp
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[1].m_Arguments.m_ObjectArgumentAssemblyTypeName
|
||||
value: UnityEngine.Object, UnityEngine
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_Pivot.x
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_Pivot.y
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_AnchorMax.x
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_AnchorMin.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_AnchorMin.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_SizeDelta.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7744090569424522082, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7744090569424522082, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_AnchorMin.y
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7744090569424522082, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_SizeDelta.y
|
||||
value: 60
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7744090569424522082, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
value: -80
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: 2307f223279813546a43b221ddd496cc, type: 3}
|
||||
--- !u!114 &6134693184638222351 stripped
|
||||
MonoBehaviour:
|
||||
m_CorrespondingSourceObject: {fileID: 1920576543566152029, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
m_PrefabInstance: {fileID: 5730140866277425490}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!224 &8201350274026178469 stripped
|
||||
RectTransform:
|
||||
m_CorrespondingSourceObject: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
m_PrefabInstance: {fileID: 5730140866277425490}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!114 &8644563062325311724 stripped
|
||||
MonoBehaviour:
|
||||
m_CorrespondingSourceObject: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc,
|
||||
type: 3}
|
||||
m_PrefabInstance: {fileID: 5730140866277425490}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: a5079836f95c2a44b96fa331487ebb70, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b656295a0a0134840bdcd41e203f12c7
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -490,7 +490,7 @@ PrefabInstance:
|
|||
objectReference: {fileID: 1576232203}
|
||||
- target: {fileID: 11454530, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3}
|
||||
propertyPath: m_UIGroups.Array.size
|
||||
value: 2
|
||||
value: 3
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 11454530, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3}
|
||||
propertyPath: m_UIGroupHelperTypeName
|
||||
|
|
@ -498,12 +498,16 @@ PrefabInstance:
|
|||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 11454530, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3}
|
||||
propertyPath: m_UIGroups.Array.data[0].m_Name
|
||||
value: Default
|
||||
value: Medium
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 11454530, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3}
|
||||
propertyPath: m_UIGroups.Array.data[1].m_Name
|
||||
value: Test
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 11454530, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3}
|
||||
propertyPath: m_UIGroups.Array.data[2].m_Name
|
||||
value: Overlay
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 11454530, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3}
|
||||
propertyPath: m_UIGroups.Array.data[0].m_Depth
|
||||
value: 0
|
||||
|
|
@ -512,6 +516,10 @@ PrefabInstance:
|
|||
propertyPath: m_UIGroups.Array.data[1].m_Depth
|
||||
value: 2
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 11454530, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3}
|
||||
propertyPath: m_UIGroups.Array.data[2].m_Depth
|
||||
value: 2
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 11461470, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3}
|
||||
propertyPath: m_LocalizationHelperTypeName
|
||||
value: GeometryTD.XmlLocalizationHelper
|
||||
|
|
@ -522,7 +530,7 @@ PrefabInstance:
|
|||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3}
|
||||
propertyPath: m_EntityGroups.Array.size
|
||||
value: 4
|
||||
value: 5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3}
|
||||
propertyPath: m_EntityGroups.Array.data[0].m_Name
|
||||
|
|
@ -542,7 +550,7 @@ PrefabInstance:
|
|||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3}
|
||||
propertyPath: m_EntityGroups.Array.data[4].m_Name
|
||||
value: Map
|
||||
value: Tower
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3}
|
||||
propertyPath: m_EntityGroups.Array.data[5].m_Name
|
||||
|
|
@ -578,7 +586,7 @@ PrefabInstance:
|
|||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3}
|
||||
propertyPath: m_EntityGroups.Array.data[4].m_InstanceCapacity
|
||||
value: 2
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3}
|
||||
propertyPath: m_EntityGroups.Array.data[5].m_InstanceCapacity
|
||||
|
|
@ -682,7 +690,7 @@ PrefabInstance:
|
|||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 11499388, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3}
|
||||
propertyPath: m_RunInBackground
|
||||
value: 0
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 11499388, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3}
|
||||
propertyPath: m_EditorResourceMode
|
||||
|
|
@ -812,6 +820,79 @@ MonoBehaviour:
|
|||
type: 3}
|
||||
_updateResourceFormTemplate: {fileID: 11487720, guid: a6c731de80e9d824d8f657301a357269,
|
||||
type: 3}
|
||||
--- !u!1001 &571687048
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 0}
|
||||
m_Modifications:
|
||||
- target: {fileID: 7091080056390424735, guid: 0cc71b1087c7dfd42a8233a7101fc27e,
|
||||
type: 3}
|
||||
propertyPath: m_Name
|
||||
value: Level1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7091080056390424735, guid: 0cc71b1087c7dfd42a8233a7101fc27e,
|
||||
type: 3}
|
||||
propertyPath: m_IsActive
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7948599801963745741, guid: 0cc71b1087c7dfd42a8233a7101fc27e,
|
||||
type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7948599801963745741, guid: 0cc71b1087c7dfd42a8233a7101fc27e,
|
||||
type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7948599801963745741, guid: 0cc71b1087c7dfd42a8233a7101fc27e,
|
||||
type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7948599801963745741, guid: 0cc71b1087c7dfd42a8233a7101fc27e,
|
||||
type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7948599801963745741, guid: 0cc71b1087c7dfd42a8233a7101fc27e,
|
||||
type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7948599801963745741, guid: 0cc71b1087c7dfd42a8233a7101fc27e,
|
||||
type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7948599801963745741, guid: 0cc71b1087c7dfd42a8233a7101fc27e,
|
||||
type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7948599801963745741, guid: 0cc71b1087c7dfd42a8233a7101fc27e,
|
||||
type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7948599801963745741, guid: 0cc71b1087c7dfd42a8233a7101fc27e,
|
||||
type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7948599801963745741, guid: 0cc71b1087c7dfd42a8233a7101fc27e,
|
||||
type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: 0cc71b1087c7dfd42a8233a7101fc27e, type: 3}
|
||||
--- !u!850595691 &819759088
|
||||
LightingSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
|
|
@ -1218,8 +1299,8 @@ Canvas:
|
|||
m_GameObject: {fileID: 1576232202}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_RenderMode: 0
|
||||
m_Camera: {fileID: 0}
|
||||
m_RenderMode: 1
|
||||
m_Camera: {fileID: 1376238058}
|
||||
m_PlaneDistance: 100
|
||||
m_PixelPerfect: 0
|
||||
m_ReceivesEvents: 1
|
||||
|
|
@ -1229,7 +1310,7 @@ Canvas:
|
|||
m_VertexColorAlwaysGammaSpace: 0
|
||||
m_AdditionalShaderChannelsFlag: 25
|
||||
m_UpdateRectTransformForStandalone: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayerID: 583862207
|
||||
m_SortingOrder: 0
|
||||
m_TargetDisplay: 0
|
||||
--- !u!1 &1604812192
|
||||
|
|
@ -1421,3 +1502,4 @@ SceneRoots:
|
|||
- {fileID: 1852670053}
|
||||
- {fileID: 120093242}
|
||||
- {fileID: 1376238059}
|
||||
- {fileID: 571687048}
|
||||
|
|
|
|||
|
|
@ -38,15 +38,30 @@ TagManager:
|
|||
-
|
||||
-
|
||||
m_SortingLayers:
|
||||
- name: MapLayer 1
|
||||
uniqueID: 2425651459
|
||||
locked: 0
|
||||
- name: MapLayer 2
|
||||
uniqueID: 4250941897
|
||||
locked: 0
|
||||
- name: MapLayer 3
|
||||
uniqueID: 4189426099
|
||||
locked: 0
|
||||
- name: Entity
|
||||
uniqueID: 2489289463
|
||||
locked: 0
|
||||
- name: Default
|
||||
uniqueID: 0
|
||||
locked: 0
|
||||
- name: Layer 1
|
||||
uniqueID: 2425651459
|
||||
- name:
|
||||
uniqueID: 1359116331
|
||||
locked: 0
|
||||
- name: Layer 2
|
||||
uniqueID: 4250941897
|
||||
- name: Medium
|
||||
uniqueID: 578558901
|
||||
locked: 0
|
||||
- name: Layer 3
|
||||
uniqueID: 4189426099
|
||||
- name: Overlay
|
||||
uniqueID: 583862207
|
||||
locked: 0
|
||||
- name: Test
|
||||
uniqueID: 1320507645
|
||||
locked: 0
|
||||
|
|
|
|||
12
docs/TODO.md
12
docs/TODO.md
|
|
@ -73,7 +73,11 @@
|
|||
| [ ] | D-08 | P0 | 先定义数值预算(经济/敌强/掉落)再调参 | `docs/NumericBudget.md`<br>`Assets/GameMain/DataTables/*.txt` | 关键曲线有目标区间,3 局测试结果可与目标对比复盘 |
|
||||
|
||||
## 小目标
|
||||
1. GameHud:游戏内的 UI,展示当前的金币数
|
||||
2. CombatHud:战斗场景的 UI,展示关卡相关信息
|
||||
- 硬币数:Coin
|
||||
- 关卡当前波次与总波次(1/5 -> 12/无限)
|
||||
1. CombatNodeComponent:战斗节点的逻辑补全
|
||||
- 完整塔防流程:初始硬币,敌人掉落硬币数量、基地生命与失败设计
|
||||
- 敌人随机掉落局外资源(金币、组件/配件)
|
||||
- 无限波次下敌人血量、资源爆率的增加
|
||||
- 基地血量满足一定条件的额外奖励
|
||||
2. ShopNodeComponent:商店节点的逻辑补全
|
||||
3. RepoForm:仓库的逻辑补全
|
||||
- 组装防御塔逻辑的落实
|
||||
Loading…
Reference in New Issue