- 补充游戏核心逻辑

- 完成 TodOList P0
    - 添加游戏内控制台便于调试
    - 增加敌人互斥机制提高运算负载
    - 优化互斥算法将游戏性能基准线拉到 1k 敌人
This commit is contained in:
SepComet 2026-02-20 18:08:27 +08:00
parent b4bc8b0445
commit 3742110e9a
196 changed files with 4868 additions and 31326 deletions

3
.gitignore vendored
View File

@ -82,5 +82,8 @@ crashlytics-build.properties
/UI参考 /UI参考
/AGENTS.md /AGENTS.md
/bin /bin
/docs/screenshot
*.xmind
/数据表/__pycache__/
~$*.xlsx ~$*.xlsx

View File

@ -88,13 +88,7 @@ namespace UnityGameFramework.Runtime
/// <summary> /// <summary>
/// 获取已缓存的 Transform。 /// 获取已缓存的 Transform。
/// </summary> /// </summary>
public Transform CachedTransform public Transform CachedTransform => m_CachedTransform;
{
get
{
return m_CachedTransform;
}
}
/// <summary> /// <summary>
/// 实体初始化。 /// 实体初始化。

View File

@ -2,7 +2,7 @@
<UnityGameFramework> <UnityGameFramework>
<ResourceBuilder> <ResourceBuilder>
<Settings> <Settings>
<InternalResourceVersion>1</InternalResourceVersion> <InternalResourceVersion>3</InternalResourceVersion>
<Platforms>33</Platforms> <Platforms>33</Platforms>
<AssetBundleCompression>1</AssetBundleCompression> <AssetBundleCompression>1</AssetBundleCompression>
<CompressionHelperTypeName>UnityGameFramework.Runtime.DefaultCompressionHelper</CompressionHelperTypeName> <CompressionHelperTypeName>UnityGameFramework.Runtime.DefaultCompressionHelper</CompressionHelperTypeName>

View File

@ -29,6 +29,7 @@
<Resource Name="URPAssets" LoadType="0" Packed="True" /> <Resource Name="URPAssets" LoadType="0" Packed="True" />
</Resources> </Resources>
<Assets> <Assets>
<Asset Guid="0080b46050fc460cb6d6c7fc8d4e8c27" ResourceName="DataTables" />
<Asset Guid="0179316b5fc7c2946a67c5877c02fc30" ResourceName="UI/UISprites/Common" /> <Asset Guid="0179316b5fc7c2946a67c5877c02fc30" ResourceName="UI/UISprites/Common" />
<Asset Guid="04d7dde7615d71b4db1a0c8d67a62e95" ResourceName="UI/UIForms" /> <Asset Guid="04d7dde7615d71b4db1a0c8d67a62e95" ResourceName="UI/UIForms" />
<Asset Guid="04dbc0581071c254ea6564b2ff06ff9b" ResourceName="Textures" /> <Asset Guid="04dbc0581071c254ea6564b2ff06ff9b" ResourceName="Textures" />
@ -64,10 +65,11 @@
<Asset Guid="28988b1366cba3e4b947bb60d6b118f9" ResourceName="DataTables" /> <Asset Guid="28988b1366cba3e4b947bb60d6b118f9" ResourceName="DataTables" />
<Asset Guid="28c42f88cfe56e84e95c0d7545db8c1b" ResourceName="Materials" /> <Asset Guid="28c42f88cfe56e84e95c0d7545db8c1b" ResourceName="Materials" />
<Asset Guid="2cb5eef4d7d7bf6459dd13a3f8d90246" ResourceName="Textures" /> <Asset Guid="2cb5eef4d7d7bf6459dd13a3f8d90246" ResourceName="Textures" />
<Asset Guid="2d1b2cc11a8e99f42b34265765fc6d86" ResourceName="Entities" />
<Asset Guid="2ed59bf0745586248aaa89cf7d3305a7" ResourceName="Entities" />
<Asset Guid="308577c72eb4cd14ca676aeee62b0ea5" ResourceName="UI/UIItems" /> <Asset Guid="308577c72eb4cd14ca676aeee62b0ea5" ResourceName="UI/UIItems" />
<Asset Guid="312264fc6c05ca04499685cc989e533d" ResourceName="UI/UISprites/Icons" /> <Asset Guid="312264fc6c05ca04499685cc989e533d" ResourceName="UI/UISprites/Icons" />
<Asset Guid="315e8ed2db27c254cb3366ff0793cd90" ResourceName="DataTables" /> <Asset Guid="315e8ed2db27c254cb3366ff0793cd90" ResourceName="DataTables" />
<Asset Guid="3455fe69f7ddc4604b95bf9b0a1e88d7" ResourceName="Entities" />
<Asset Guid="352da872791696c48af3b21132e3e3c3" ResourceName="DataTables" /> <Asset Guid="352da872791696c48af3b21132e3e3c3" ResourceName="DataTables" />
<Asset Guid="372a8b1e52bedc64b9207b12d167afaa" ResourceName="DataTables" /> <Asset Guid="372a8b1e52bedc64b9207b12d167afaa" ResourceName="DataTables" />
<Asset Guid="3aa539b1e46111d4299a83c73ebe762c" ResourceName="UI/UIForms" /> <Asset Guid="3aa539b1e46111d4299a83c73ebe762c" ResourceName="UI/UIForms" />
@ -91,6 +93,7 @@
<Asset Guid="583ff7026dac91849b7c7b2468ba456b" ResourceName="Materials" /> <Asset Guid="583ff7026dac91849b7c7b2468ba456b" ResourceName="Materials" />
<Asset Guid="5b5a6a737c460eb4abc105d6583d405e" ResourceName="Fonts" /> <Asset Guid="5b5a6a737c460eb4abc105d6583d405e" ResourceName="Fonts" />
<Asset Guid="5dcd89912e222bf4c87f76db4044bc5e" ResourceName="Localization/Dictionaries" ResourceVariant="ko-kr" /> <Asset Guid="5dcd89912e222bf4c87f76db4044bc5e" ResourceName="Localization/Dictionaries" ResourceVariant="ko-kr" />
<Asset Guid="5ebb46af6f16ae94e87f64a7dc0a49cb" ResourceName="Entities" />
<Asset Guid="62af9e5c8f39cfa49af9e10ccf42f1da" ResourceName="UI/UISprites/Common" /> <Asset Guid="62af9e5c8f39cfa49af9e10ccf42f1da" ResourceName="UI/UISprites/Common" />
<Asset Guid="638ff8ae4a0d15047839cd265d3bc296" ResourceName="Music/Background" /> <Asset Guid="638ff8ae4a0d15047839cd265d3bc296" ResourceName="Music/Background" />
<Asset Guid="63fe6ff9ab9e1433f8db4ebd940f2442" ResourceName="Materials" /> <Asset Guid="63fe6ff9ab9e1433f8db4ebd940f2442" ResourceName="Materials" />
@ -119,6 +122,7 @@
<Asset Guid="7f5aee8da226edf4991598327cb32ce0" ResourceName="UI/UISprites/Logos" /> <Asset Guid="7f5aee8da226edf4991598327cb32ce0" ResourceName="UI/UISprites/Logos" />
<Asset Guid="7fd11dc5d29076d469d414dec2818f11" ResourceName="Configs" /> <Asset Guid="7fd11dc5d29076d469d414dec2818f11" ResourceName="Configs" />
<Asset Guid="836be25be3e1e8c41ae5545bc8a9a4d7" ResourceName="Textures" /> <Asset Guid="836be25be3e1e8c41ae5545bc8a9a4d7" ResourceName="Textures" />
<Asset Guid="83e5fce2d1e5b3e4ab6551de03cc9c22" ResourceName="Materials" />
<Asset Guid="8940b037a9b441c4cbd3d2b446838424" ResourceName="Materials" /> <Asset Guid="8940b037a9b441c4cbd3d2b446838424" ResourceName="Materials" />
<Asset Guid="8e27380ee68aa4a219b4db9018e7da31" ResourceName="Materials" /> <Asset Guid="8e27380ee68aa4a219b4db9018e7da31" ResourceName="Materials" />
<Asset Guid="91b565625868b59428114c4a5b945e80" ResourceName="UI/UISprites/SelectRoleFormRT" /> <Asset Guid="91b565625868b59428114c4a5b945e80" ResourceName="UI/UISprites/SelectRoleFormRT" />
@ -136,7 +140,6 @@
<Asset Guid="a23eef5e20ff8cb46adf33491fc443fb" ResourceName="Materials" /> <Asset Guid="a23eef5e20ff8cb46adf33491fc443fb" ResourceName="Materials" />
<Asset Guid="a6560a915ef98420e9faacc1c7438823" ResourceName="URPAssets" /> <Asset Guid="a6560a915ef98420e9faacc1c7438823" ResourceName="URPAssets" />
<Asset Guid="a6ac41115088b5946bc544429260c997" ResourceName="Entities" /> <Asset Guid="a6ac41115088b5946bc544429260c997" ResourceName="Entities" />
<Asset Guid="a6d58f54ce5934781962a4f887e834f3" ResourceName="Entities" />
<Asset Guid="a71f8bb1b1b2c51438e2bafc884cb02c" ResourceName="DataTables" /> <Asset Guid="a71f8bb1b1b2c51438e2bafc884cb02c" ResourceName="DataTables" />
<Asset Guid="a76ff56683a2bc2479ebd63dcdd658b0" ResourceName="UI/UIItems" /> <Asset Guid="a76ff56683a2bc2479ebd63dcdd658b0" ResourceName="UI/UIItems" />
<Asset Guid="a7b030cffa2dc44478c14e49a22771c2" ResourceName="Materials" /> <Asset Guid="a7b030cffa2dc44478c14e49a22771c2" ResourceName="Materials" />
@ -157,6 +160,7 @@
<Asset Guid="bf75b984df8a84987bcf3a8bf6e2862d" ResourceName="Sounds" /> <Asset Guid="bf75b984df8a84987bcf3a8bf6e2862d" ResourceName="Sounds" />
<Asset Guid="c40be3174f62c4acf8c1216858c64956" ResourceName="URPAssets" /> <Asset Guid="c40be3174f62c4acf8c1216858c64956" ResourceName="URPAssets" />
<Asset Guid="c49cffd4fc1dfb549b2b30448a0becda" ResourceName="UI/UISprites/Icons" /> <Asset Guid="c49cffd4fc1dfb549b2b30448a0becda" ResourceName="UI/UISprites/Icons" />
<Asset Guid="c4f37184fcb9306428d7d002f7dca96d" ResourceName="Materials" />
<Asset Guid="c547624e174de984882f0a14b4bb32e1" ResourceName="Materials" /> <Asset Guid="c547624e174de984882f0a14b4bb32e1" ResourceName="Materials" />
<Asset Guid="c58c9afddbd36d14d837fa218d772996" ResourceName="Materials" /> <Asset Guid="c58c9afddbd36d14d837fa218d772996" ResourceName="Materials" />
<Asset Guid="c7d1e11dd37634b48a9dd4012b8e4306" ResourceName="UI/UISounds" /> <Asset Guid="c7d1e11dd37634b48a9dd4012b8e4306" ResourceName="UI/UISounds" />

View File

@ -1,5 +1,5 @@
# 敌人基础属性表 # 敌人基础属性表
# Id MaxHealth HpAddPerLevel Speed CoinDrop ExpDrop DropPercent # Id EntityTypeId MaxHealth HpAddPerLevel Speed CoinDrop ExpDrop DropPercent
# int int int float int int float # int int int int float int int float
# 实体编号 策划备注 最大生命 每关卡增加生命 移动速度 金币掉落 经验掉落 掉落概率 # 敌人编号 策划备注 敌人实体编号 最大生命 每关卡增加生命 移动速度 金币掉落 经验掉落 掉落概率
101 近战敌人 50 50 3 5 1 0.3 1 近战敌人 101 50 50 3 5 1 0.3

View File

@ -7,5 +7,7 @@
102 远程敌人 RemoteEnemy 102 远程敌人 RemoteEnemy
11 跟随相机 FollowCamera 11 跟随相机 FollowCamera
201 武器小刀 WeaponKnife 201 武器小刀 WeaponKnife
202 武器手枪 WeaponHandgun
203 武器斧头 WeaponSlash
10001 金币实体 CoinEntity 10001 金币实体 CoinEntity
10002 经验实体 ExpEntity 10002 经验实体 ExpEntity

View File

@ -3,7 +3,25 @@
# int GoodsType int # int GoodsType int
# 商品编号 策划备注 商品类型 商品对应物品Id # 商品编号 策划备注 商品类型 商品对应物品Id
101 道具:药 Prop 101 101 道具:药 Prop 101
102 小刀 Weapon 201 102 小刀 Weapon 1
103 Prop 102 103 Prop 102
104 Prop 103 104 Prop 103
105 Prop 104 105 Prop 104
106 Weapon 2
107 Prop 105
108 Prop 106
109 Prop 107
110 Prop 108
111 Prop 109
112 Prop 110
113 Prop 111
114 Prop 112
115 Prop 113
116 Prop 114
117 Prop 115
118 Prop 116
119 Prop 117
120 Prop 118
121 Prop 119
122 Prop 120
123 Weapon 3

View File

@ -1,14 +1,14 @@
# 关卡配置表 # 关卡配置表
# Id EntityIds EntityCounts Interval Duration # Id EnemyTypes EntityCounts Interval Duration
# int int[] int[] float[] int # int int[] int[] float[] int
# 关卡号 策划备注 实体编号 每次出怪数量 每次出怪间隔 关卡时间 # 关卡号 策划备注 敌人类型 每次出怪数量 每次出怪间隔 关卡时间
1 第一关 [101] [5] [2] 60 1 第一关 [1] [5] [2] 60
2 第二关 [101] [10] [3] 60 2 第二关 [1] [10] [3] 60
3 第三关 [101] [10] [3] 60 3 第三关 [1] [10] [3] 60
4 第四关 [101] [10] [3] 60 4 第四关 [1] [10] [3] 60
5 第五关 [101] [10] [3] 60 5 第五关 [1] [10] [3] 60
6 第六关 [101] [10] [3] 60 6 第六关 [1] [10] [3] 60
7 第七关 [101] [10] [3] 60 7 第七关 [1] [10] [3] 60
8 第八关 [101] [10] [3] 60 8 第八关 [1] [10] [3] 60
9 第九关 [101] [10] [3] 60 9 第九关 [1] [10] [3] 60
10 第十关 [101] [10] [3] 60 10 第十关 [1] [10] [3] 60

View File

@ -1,6 +1,7 @@
# 武器表 # 武器表
# Id Title IconAssetName Rarity Price PriceRandomPercent Attack Cooldown AttackRange AttackSoundId Pramas Modifiers # Id EntityTypeId Title IconAssetName Rarity Price PriceRandomPercent Attack Cooldown AttackRange AttackSoundId Pramas Modifiers
# int string string RarityType int float int float float int string[] StatModifier[] # int int string string RarityType int float int float float int string[] StatModifier[]
# 武器编号 策划备注 武器名 图标资源名 道具品质 武器价格 价格浮动 伤害 冷却 范围 攻击音效编号 额外参数 额外属性 # 武器编号 策划备注 武器实体编号 武器名 图标资源名 道具品质 武器价格 价格浮动 伤害 冷却 范围 攻击音效编号 额外参数 额外属性
201 玩家武器 小刀 Almighty_Icon White 120 0.05 100 1.5 5 10000 [] [] 1 玩家武器 201 小刀 Almighty_Icon White 120 0.05 100 1.5 5 10000 [hitRadius:2] []
202 手枪 Almighty_Icon White 130 0.05 120 1 15 10000 [] [] 2 202 手枪 Almighty_Icon White 130 0.05 120 1 15 10000 [] []
3 203 斧头 Almighty_Icon White 100 0.1 150 2 5 10000 [SectorAngle:120] []

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,139 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &566519323009123551
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1130502818594560503}
- component: {fileID: 406614133024396657}
- component: {fileID: 4827071670030023186}
- component: {fileID: 4923292553483202712}
m_Layer: 0
m_Name: Sphere
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &1130502818594560503
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 566519323009123551}
serializedVersion: 2
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0.3, y: 0.3, z: 0.3}
m_ConstrainProportionsScale: 1
m_Children: []
m_Father: {fileID: 8253353784175897923}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &406614133024396657
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 566519323009123551}
m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &4827071670030023186
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 566519323009123551}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
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: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!135 &4923292553483202712
SphereCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 566519323009123551}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 1
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Radius: 0.5
m_Center: {x: 0, y: 0, z: 0}
--- !u!1 &7075265919660176486
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8253353784175897923}
m_Layer: 0
m_Name: BulletHandgun
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &8253353784175897923
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7075265919660176486}
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: 1
m_Children:
- {fileID: 1130502818594560503}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 2d1b2cc11a8e99f42b34265765fc6d86
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -25,7 +25,7 @@ Transform:
m_GameObject: {fileID: 50638171685441296} m_GameObject: {fileID: 50638171685441296}
serializedVersion: 2 serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -0.86, y: 1.5, z: 0.5} m_LocalPosition: {x: -1.72, y: 1.5, z: 1}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0 m_ConstrainProportionsScale: 0
m_Children: [] m_Children: []
@ -56,7 +56,7 @@ Transform:
m_GameObject: {fileID: 526023732862709228} m_GameObject: {fileID: 526023732862709228}
serializedVersion: 2 serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0.86, y: 1.5, z: 0.5} m_LocalPosition: {x: 1.72, y: 1.5, z: 1}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0 m_ConstrainProportionsScale: 0
m_Children: [] m_Children: []
@ -87,7 +87,7 @@ Transform:
m_GameObject: {fileID: 886755583121495434} m_GameObject: {fileID: 886755583121495434}
serializedVersion: 2 serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 1.5, z: -1} m_LocalPosition: {x: 0, y: 1.5, z: -2}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0 m_ConstrainProportionsScale: 0
m_Children: [] m_Children: []
@ -118,7 +118,7 @@ Transform:
m_GameObject: {fileID: 5243258899397488202} m_GameObject: {fileID: 5243258899397488202}
serializedVersion: 2 serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 1.5, z: 1} m_LocalPosition: {x: 0, y: 1.5, z: 2}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0 m_ConstrainProportionsScale: 0
m_Children: [] m_Children: []
@ -150,13 +150,13 @@ Transform:
m_PrefabAsset: {fileID: 0} m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5383497626468778460} m_GameObject: {fileID: 5383497626468778460}
serializedVersion: 2 serializedVersion: 2
m_LocalRotation: {x: 0.42261827, y: 0, z: 0, w: 0.9063079} m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068}
m_LocalPosition: {x: 0, y: 10, z: -8} m_LocalPosition: {x: 0, y: 15, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0 m_ConstrainProportionsScale: 0
m_Children: [] m_Children: []
m_Father: {fileID: 9112716898534404901} m_Father: {fileID: 9112716898534404901}
m_LocalEulerAnglesHint: {x: 50, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0}
--- !u!20 &4064848608618185461 --- !u!20 &4064848608618185461
Camera: Camera:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -189,8 +189,8 @@ Camera:
width: 1 width: 1
height: 1 height: 1
near clip plane: 0.3 near clip plane: 0.3
far clip plane: 1000 far clip plane: 100
field of view: 60 field of view: 80
orthographic: 0 orthographic: 0
orthographic size: 5 orthographic size: 5
m_Depth: 0 m_Depth: 0
@ -277,7 +277,7 @@ Transform:
m_GameObject: {fileID: 6372140121958224629} m_GameObject: {fileID: 6372140121958224629}
serializedVersion: 2 serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -1, y: 1.5, z: 0} m_LocalPosition: {x: -2, y: 1.5, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0 m_ConstrainProportionsScale: 0
m_Children: [] m_Children: []
@ -308,7 +308,7 @@ Transform:
m_GameObject: {fileID: 6961884676133552519} m_GameObject: {fileID: 6961884676133552519}
serializedVersion: 2 serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 1, y: 1.5, z: 0} m_LocalPosition: {x: 2, y: 1.5, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0 m_ConstrainProportionsScale: 0
m_Children: [] m_Children: []
@ -523,6 +523,9 @@ MonoBehaviour:
_isMoving: 0 _isMoving: 0
_direction: {x: 0, y: 0, z: 0} _direction: {x: 0, y: 0, z: 0}
_cachedTransform: {fileID: 0} _cachedTransform: {fileID: 0}
_avoidEnemyOverlap: 0
_enemyBodyRadius: 0.45
_separationIterations: 2
_speedBase: 0 _speedBase: 0
--- !u!114 &582203051508165858 --- !u!114 &582203051508165858
MonoBehaviour: MonoBehaviour:

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 3455fe69f7ddc4604b95bf9b0a1e88d7
timeCreated: 1528026126
licenseType: Pro
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,201 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &1813281596125460147
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6411073038556792567}
- component: {fileID: 6751076590942288719}
- component: {fileID: 2005122658108926300}
m_Layer: 11
m_Name: Sphere
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &6411073038556792567
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1813281596125460147}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0.3}
m_LocalScale: {x: 0.3, y: 0.3, z: 0.3}
m_ConstrainProportionsScale: 1
m_Children: []
m_Father: {fileID: 1074967493716089666}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &6751076590942288719
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1813281596125460147}
m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &2005122658108926300
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1813281596125460147}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
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: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!1 &4357566619712708754
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 3446496929708001105}
- component: {fileID: 813568690088023650}
- component: {fileID: 5268918582646976821}
m_Layer: 11
m_Name: Cube
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &3446496929708001105
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4357566619712708754}
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.6}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1074967493716089666}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &813568690088023650
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4357566619712708754}
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &5268918582646976821
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4357566619712708754}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: c4f37184fcb9306428d7d002f7dca96d, type: 2}
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: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!1 &4668848878531932975
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1074967493716089666}
m_Layer: 11
m_Name: WeaponHandgun
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &1074967493716089666
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4668848878531932975}
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: 3446496929708001105}
- {fileID: 6411073038556792567}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 2ed59bf0745586248aaa89cf7d3305a7
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -27,8 +27,8 @@ Transform:
m_GameObject: {fileID: 1813281596125460147} m_GameObject: {fileID: 1813281596125460147}
serializedVersion: 2 serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0.5} m_LocalPosition: {x: 0, y: 0, z: 0.25}
m_LocalScale: {x: 0.5, y: 0.5, z: 0.5} m_LocalScale: {x: 0.2, y: 0.2, z: 0.2}
m_ConstrainProportionsScale: 1 m_ConstrainProportionsScale: 1
m_Children: [] m_Children: []
m_Father: {fileID: 1074967493716089666} m_Father: {fileID: 1074967493716089666}
@ -111,8 +111,8 @@ Transform:
serializedVersion: 2 serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 0.5, y: 0.5, z: 0.5}
m_ConstrainProportionsScale: 0 m_ConstrainProportionsScale: 1
m_Children: [] m_Children: []
m_Father: {fileID: 1074967493716089666} m_Father: {fileID: 1074967493716089666}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
@ -144,7 +144,7 @@ MeshRenderer:
m_RenderingLayerMask: 1 m_RenderingLayerMask: 1
m_RendererPriority: 0 m_RendererPriority: 0
m_Materials: m_Materials:
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2} - {fileID: 2100000, guid: c4f37184fcb9306428d7d002f7dca96d, type: 2}
m_StaticBatchInfo: m_StaticBatchInfo:
firstSubMesh: 0 firstSubMesh: 0
subMeshCount: 0 subMeshCount: 0
@ -175,8 +175,6 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 1074967493716089666} - component: {fileID: 1074967493716089666}
- component: {fileID: 8372979338474897700}
- component: {fileID: 7352622698489128642}
m_Layer: 11 m_Layer: 11
m_Name: WeaponKnife m_Name: WeaponKnife
m_TagString: Untagged m_TagString: Untagged
@ -201,51 +199,3 @@ Transform:
- {fileID: 6411073038556792567} - {fileID: 6411073038556792567}
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!54 &8372979338474897700
Rigidbody:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4668848878531932975}
serializedVersion: 4
m_Mass: 1
m_Drag: 0
m_AngularDrag: 0.05
m_CenterOfMass: {x: 0, y: 0, z: 0}
m_InertiaTensor: {x: 1, y: 1, z: 1}
m_InertiaRotation: {x: 0, y: 0, z: 0, w: 1}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_ImplicitCom: 1
m_ImplicitTensor: 1
m_UseGravity: 0
m_IsKinematic: 1
m_Interpolate: 0
m_Constraints: 0
m_CollisionDetection: 0
--- !u!135 &7352622698489128642
SphereCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4668848878531932975}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 1
m_ProvidesContacts: 0
m_Enabled: 0
serializedVersion: 3
m_Radius: 0.625
m_Center: {x: 0, y: 0, z: 0.125}

View File

@ -0,0 +1,201 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &6354441506395502586
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8872382261416578947}
- component: {fileID: 1092941560137749238}
- component: {fileID: 2293075059394330032}
m_Layer: 11
m_Name: Cylinder
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &8872382261416578947
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6354441506395502586}
serializedVersion: 2
m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068}
m_LocalPosition: {x: 0, y: 0, z: 0.4}
m_LocalScale: {x: 0.5, y: 0.5, z: 0.5}
m_ConstrainProportionsScale: 1
m_Children: []
m_Father: {fileID: 5097192555115739519}
m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0}
--- !u!33 &1092941560137749238
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6354441506395502586}
m_Mesh: {fileID: 10206, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &2293075059394330032
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6354441506395502586}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: c4f37184fcb9306428d7d002f7dca96d, type: 2}
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: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!1 &6788977933968318564
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1468523594039852396}
- component: {fileID: 8867298502386598020}
- component: {fileID: 6130698427720623983}
m_Layer: 11
m_Name: Sphere
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &1468523594039852396
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6788977933968318564}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 1}
m_LocalScale: {x: 0.3, y: 0.3, z: 0.3}
m_ConstrainProportionsScale: 1
m_Children: []
m_Father: {fileID: 5097192555115739519}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &8867298502386598020
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6788977933968318564}
m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &6130698427720623983
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6788977933968318564}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
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: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!1 &7825103691467368365
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 5097192555115739519}
m_Layer: 11
m_Name: WeaponSlash
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &5097192555115739519
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7825103691467368365}
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: 8872382261416578947}
- {fileID: 1468523594039852396}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 5ebb46af6f16ae94e87f64a7dc0a49cb
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,6 +1,7 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Entity; using Entity;
using Entity.Weapon;
using UnityEngine; using UnityEngine;
namespace Components namespace Components

View File

@ -1,6 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using CustomEvent;
using Definition.DataStruct; using Definition.DataStruct;
using Entity.Weapon;
using Entity; using Entity;
using UnityEngine; using UnityEngine;

View File

@ -1,8 +1,10 @@
using System; using System;
using CustomUtility;
using Definition.DataStruct; using Definition.DataStruct;
using Definition.Enum; using Definition.Enum;
using Unity.Profiling;
using UnityEngine; using UnityEngine;
using UnityGameFramework.Runtime; using CustomDebugger;
namespace Components namespace Components
{ {
@ -11,6 +13,9 @@ namespace Components
[SerializeField] private bool _isMoving; [SerializeField] private bool _isMoving;
[SerializeField] private Vector3 _direction; [SerializeField] private Vector3 _direction;
[SerializeField] private Transform _cachedTransform; [SerializeField] private Transform _cachedTransform;
[SerializeField] private bool _avoidEnemyOverlap;
[SerializeField] private float _enemyBodyRadius = 0.45f;
[SerializeField] private int _separationIterations = 2;
public float Speed => (_speedBase + _movementStat.Value) * _movementStat.Percent; public float Speed => (_speedBase + _movementStat.Value) * _movementStat.Percent;
[SerializeField] private float _speedBase; [SerializeField] private float _speedBase;
@ -20,11 +25,15 @@ namespace Components
private StatProperty _movementStat; private StatProperty _movementStat;
private Action<StatModifier, bool> _movementStatCallback; private Action<StatModifier, bool> _movementStatCallback;
public void OnInit(float speed, Transform target, StatComponent statComponent = null) public void OnInit(float speed, Transform target, StatComponent statComponent = null,
bool avoidEnemyOverlap = false, float enemyBodyRadius = 0.45f, int separationIterations = 2)
{ {
_speedBase = speed; _speedBase = speed;
_cachedTransform = target; _cachedTransform = target;
_direction = Vector3.forward; _direction = Vector3.forward;
_avoidEnemyOverlap = avoidEnemyOverlap;
_enemyBodyRadius = Mathf.Max(0.01f, enemyBodyRadius);
_separationIterations = Mathf.Max(1, separationIterations);
_statComponent = statComponent; _statComponent = statComponent;
if (_statComponent != null) if (_statComponent != null)
@ -38,6 +47,8 @@ namespace Components
{ {
_movementStat = new StatProperty(); _movementStat = new StatProperty();
} }
RefreshEnemyRegistration();
} }
public void OnUpdate(float elapseSeconds, float realElapseSeconds) public void OnUpdate(float elapseSeconds, float realElapseSeconds)
@ -54,6 +65,9 @@ namespace Components
_cachedTransform = null; _cachedTransform = null;
_direction = Vector3.zero; _direction = Vector3.zero;
_isMoving = false; _isMoving = false;
_avoidEnemyOverlap = false;
_enemyBodyRadius = 0.45f;
_separationIterations = 2;
if (_statComponent != null) if (_statComponent != null)
{ {
@ -62,14 +76,44 @@ namespace Components
} }
_statComponent = null; _statComponent = null;
UnregisterEnemyMover();
} }
private void Move(float deltaTime = 0) private void Move(float deltaTime = 0)
{ {
this.transform.Translate(Speed * deltaTime * _direction); using (CustomProfilerMarker.Movement_Update.Auto())
{
if (_cachedTransform == null) return;
Vector3 displacement = Speed * deltaTime * _direction;
Vector3 nextPosition = _cachedTransform.position + displacement;
if (_avoidEnemyOverlap)
{
nextPosition = EnemySeparationSolverProvider.Resolve(
this,
nextPosition,
_direction,
_separationIterations);
}
_cachedTransform.position = nextPosition;
}
} }
public void SetMove(bool isMoving) => _isMoving = isMoving; public void SetMove(bool isMoving) => _isMoving = isMoving;
public void SetDirection(Vector3 direction) => _direction = direction; public void SetDirection(Vector3 direction) => _direction = direction;
private void RefreshEnemyRegistration()
{
UnregisterEnemyMover();
if (!_avoidEnemyOverlap) return;
EnemySeparationSolverProvider.Register(this, _cachedTransform, _enemyBodyRadius);
}
private void UnregisterEnemyMover()
{
EnemySeparationSolverProvider.Unregister(this);
}
} }
} }

View File

@ -4,7 +4,8 @@ using System.Linq;
using DataTable; using DataTable;
using Definition.DataStruct; using Definition.DataStruct;
using Entity; using Entity;
using Game.Utility; using CustomUtility;
using Procedure;
using UnityEngine; using UnityEngine;
using UnityGameFramework.Runtime; using UnityGameFramework.Runtime;
#if ENABLE_INPUT_SYSTEM #if ENABLE_INPUT_SYSTEM
@ -105,6 +106,7 @@ namespace CustomComponent
{ {
EnsurePropList(true); EnsurePropList(true);
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
ApplyFilter(_searchText); ApplyFilter(_searchText);
@ -135,12 +137,14 @@ namespace CustomComponent
{ {
AddSelectedBuffToPlayer(selectedProp, _addCount); AddSelectedBuffToPlayer(selectedProp, _addCount);
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
private void DrawBattleSection() private void DrawBattleSection()
{ {
GUILayout.Label("Battle Debug"); GUILayout.Label("Battle Debug");
ProcedureGame procedure = GameEntry.Procedure.CurrentProcedure as ProcedureGame;
EnemyManagerComponent enemyManager = GameEntry.EnemyManager; EnemyManagerComponent enemyManager = GameEntry.EnemyManager;
Player player = FindPlayer(); Player player = FindPlayer();
@ -150,8 +154,15 @@ namespace CustomComponent
return; return;
} }
if (procedure == null)
{
GUILayout.Label("ProcedureGame unavailable.");
return;
}
GUILayout.Label($"Spawn Rate: {enemyManager.SpawnRateScale:F2}"); GUILayout.Label($"Spawn Rate: {enemyManager.SpawnRateScale:F2}");
GUILayout.Label($"Battle Time: {enemyManager.ElapsedBattleTime:F1}s / {enemyManager.BattleDuration:F1}s"); GUILayout.Label($"Battle Time: {enemyManager.ElapsedBattleTime:F1}s / {enemyManager.BattleDuration:F1}s");
GUILayout.Label($"Enemy Count: {enemyManager.CurrentEnemyCount}");
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUILayout.Label("Rate", GUILayout.Width(52f)); GUILayout.Label("Rate", GUILayout.Width(52f));
@ -165,16 +176,19 @@ namespace CustomComponent
{ {
enemyManager.SetSpawnRateScale(_spawnRateScaleInput); enemyManager.SetSpawnRateScale(_spawnRateScaleInput);
} }
if (GUILayout.Button("x0.5", GUILayout.Width(60f))) if (GUILayout.Button("x0.5", GUILayout.Width(60f)))
{ {
_spawnRateScaleInput = Mathf.Max(MinSpawnRate, enemyManager.SpawnRateScale * 0.5f); _spawnRateScaleInput = Mathf.Max(MinSpawnRate, enemyManager.SpawnRateScale * 0.5f);
enemyManager.SetSpawnRateScale(_spawnRateScaleInput); enemyManager.SetSpawnRateScale(_spawnRateScaleInput);
} }
if (GUILayout.Button("x2", GUILayout.Width(60f))) if (GUILayout.Button("x2", GUILayout.Width(60f)))
{ {
_spawnRateScaleInput = enemyManager.SpawnRateScale * 2f; _spawnRateScaleInput = enemyManager.SpawnRateScale * 2f;
enemyManager.SetSpawnRateScale(_spawnRateScaleInput); enemyManager.SetSpawnRateScale(_spawnRateScaleInput);
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
@ -187,21 +201,43 @@ namespace CustomComponent
if (GUILayout.Button("Extend Battle", GUILayout.Height(24f))) if (GUILayout.Button("Extend Battle", GUILayout.Height(24f)))
{ {
enemyManager.AddBattleDuration(_extendDurationSeconds); if (procedure.CurrentGameState is GameStateBattle gameState)
{
gameState.AddBattleDuration(_extendDurationSeconds);
}
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.Label($"Player Weapon: {(player == null ? "Player not found" : (player.WeaponEnabled ? "Enabled" : "Disabled"))}"); GUILayout.Space(4f);
GUILayout.Label($"Enemy Separation Solver: {EnemySeparationSolverProvider.CurrentSolverName}");
GUILayout.BeginHorizontal();
if (GUILayout.Button("Use Naive O(N^2)", GUILayout.Height(24f)))
{
EnemySeparationSolverProvider.UseNaiveSolver();
}
if (GUILayout.Button("Use Grid Bucket", GUILayout.Height(24f)))
{
EnemySeparationSolverProvider.UseGridBucketSolver();
}
GUILayout.EndHorizontal();
GUILayout.Label(
$"Player Weapon: {(player == null ? "Player not found" : (player.WeaponEnabled ? "Enabled" : "Disabled"))}");
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUI.enabled = player != null; GUI.enabled = player != null;
if (GUILayout.Button("Disable Weapons", GUILayout.Height(24f))) if (GUILayout.Button("Disable Weapons", GUILayout.Height(24f)))
{ {
player.SetWeaponEnabled(false); player.SetWeaponEnabled(false);
} }
if (GUILayout.Button("Enable Weapons", GUILayout.Height(24f))) if (GUILayout.Button("Enable Weapons", GUILayout.Height(24f)))
{ {
player.SetWeaponEnabled(true); player.SetWeaponEnabled(true);
} }
GUI.enabled = true; GUI.enabled = true;
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }

View File

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using DataTable; using DataTable;
using Definition.Enum;
using Entity; using Entity;
using Entity.EntityData; using Entity.EntityData;
using GameFramework.Event; using GameFramework.Event;
@ -23,11 +24,11 @@ namespace CustomComponent
private float _spawnEnemyTimer; private float _spawnEnemyTimer;
private int _spawnEnemyMaxCount = 5000; [SerializeField] private int _spawnEnemyMaxCount = 5000;
private int _currentEnemyCount; private int _currentEnemyCount;
private int _spawnDistanceFromPlayer = 20; [SerializeField] private int _spawnDistanceFromPlayer = 20;
private int _currentSpawnEnemyId; private int _currentSpawnEnemyId;
@ -35,7 +36,7 @@ namespace CustomComponent
private float[] _baseSpawnEnemyIntervals; private float[] _baseSpawnEnemyIntervals;
private float[] _spawnEnemyIntervals; private float[] _spawnEnemyIntervals;
private int[] _spawnEnemyIds; private EnemyType[] _spawnEnemyTypes;
private int[] _spawnEnemyCounts; private int[] _spawnEnemyCounts;
private float _duration; private float _duration;
private float _baseDuration; private float _baseDuration;
@ -45,11 +46,10 @@ namespace CustomComponent
private Transform _player; private Transform _player;
private GameStateBattle _battle;
public float SpawnRateScale => _spawnRateScale; public float SpawnRateScale => _spawnRateScale;
public float BattleDuration => _duration; public float BattleDuration => _duration;
public float ElapsedBattleTime => _spawnEnemyTimer; public float ElapsedBattleTime => _spawnEnemyTimer;
public int CurrentEnemyCount => _currentEnemyCount;
#region FSM #region FSM
@ -71,22 +71,18 @@ namespace CustomComponent
_entity = null; _entity = null;
} }
public void OnInit(int level, GameStateBattle battle) public void OnInit(DRLevel level)
{ {
_battle = battle; _baseSpawnEnemyIntervals = (float[])level.Intervals.Clone();
_currentLevel = level;
DRLevel levelData = GameEntry.DataTable.GetDataTableRow<DRLevel>(_currentLevel);
_baseSpawnEnemyIntervals = (float[])levelData.Intervals.Clone();
_spawnEnemyIntervals = (float[])_baseSpawnEnemyIntervals.Clone(); _spawnEnemyIntervals = (float[])_baseSpawnEnemyIntervals.Clone();
_spawnEnemyIds = levelData.EntityIds; _spawnEnemyTypes = level.EntityTypes;
_spawnEnemyCounts = levelData.EntityCounts; _spawnEnemyCounts = level.EntityCounts;
_baseDuration = levelData.Duration; _baseDuration = level.Duration;
_duration = _baseDuration; _duration = _baseDuration;
SetSpawnRateScale(_spawnRateScale); SetSpawnRateScale(_spawnRateScale);
_currentEnemyCount = 0;
_currentSpawnEnemyId = 0; _currentSpawnEnemyId = 0;
} }
@ -94,19 +90,13 @@ namespace CustomComponent
{ {
_spawnEnemyTimer += elapseSeconds; _spawnEnemyTimer += elapseSeconds;
if (_spawnEnemyTimer > _duration)
{
_battle.LevelOver();
return;
}
for (int i = 0; i < _nextSpawnTimes.Length; i++) for (int i = 0; i < _nextSpawnTimes.Length; i++)
{ {
float nextSpawnTime = _nextSpawnTimes[i]; float nextSpawnTime = _nextSpawnTimes[i];
if (_spawnEnemyTimer < nextSpawnTime) continue; if (_spawnEnemyTimer < nextSpawnTime) continue;
for (int j = 0; j < _spawnEnemyCounts[i]; j++) for (int j = 0; j < _spawnEnemyCounts[i]; j++)
{ {
SpawnEnemy(_spawnEnemyIds[i]); SpawnEnemy(_spawnEnemyTypes[i]);
} }
_nextSpawnTimes[i] += _spawnEnemyIntervals[i]; _nextSpawnTimes[i] += _spawnEnemyIntervals[i];
@ -115,24 +105,57 @@ namespace CustomComponent
public void OnReset() public void OnReset()
{ {
_currentEnemyCount = 0;
_spawnEnemyTimer = 0; _spawnEnemyTimer = 0;
_currentSpawnEnemyId = 0; _currentSpawnEnemyId = 0;
_currentLevel = 0; _currentLevel = 0;
_baseSpawnEnemyIntervals = null; _baseSpawnEnemyIntervals = null;
_spawnEnemyIntervals = null; _spawnEnemyIntervals = null;
_spawnEnemyIds = null; _spawnEnemyTypes = null;
_spawnEnemyCounts = null; _spawnEnemyCounts = null;
_baseDuration = 0; _baseDuration = 0;
_duration = 0; _duration = 0;
_nextSpawnTimes = null; _nextSpawnTimes = null;
_battle = null;
ClearEnemies(); ClearEnemies();
_currentEnemyCount = 0;
}
#endregion
private void SpawnEnemy(EnemyType enemyType)
{
if (_player == null) return;
if (_currentEnemyCount >= _spawnEnemyMaxCount) return;
int entityPoolId = _currentSpawnEnemyId % _spawnEnemyMaxCount;
var enemyData = new EnemyData(entityPoolId, enemyType, _currentLevel)
{
Position = GetRandomPosition()
};
_entity.ShowEnemy(enemyData);
_currentSpawnEnemyId++;
}
private Vector3 GetRandomPosition()
{
float x = Random.Range(-1f, 1f);
float z = Random.Range(-1f, 1f);
Vector3 dir = new Vector3(x, 0, z).normalized;
return _player.position + dir * _spawnDistanceFromPlayer;
}
public void ClearEnemies()
{
foreach (var enemy in _enemies)
{
if (enemy == null || !enemy.Available) continue;
_entity.HideEntity(enemy);
}
_enemies.Clear();
} }
public void SetSpawnRateScale(float scale) public void SetSpawnRateScale(float scale)
@ -174,53 +197,12 @@ namespace CustomComponent
} }
} }
public void AddBattleDuration(float seconds)
{
if (seconds <= 0f) return;
_duration += seconds;
}
private static float GetScaledInterval(float baseInterval, float scale) private static float GetScaledInterval(float baseInterval, float scale)
{ {
float safeScale = Mathf.Max(MinSpawnRateScale, scale); float safeScale = Mathf.Max(MinSpawnRateScale, scale);
return baseInterval / safeScale; return baseInterval / safeScale;
} }
#endregion
private void SpawnEnemy(int entityId)
{
if (_player == null) return;
if (_currentEnemyCount >= _spawnEnemyMaxCount) return;
int entityPoolId = _currentSpawnEnemyId % _spawnEnemyMaxCount;
var enemyData = new EnemyData(entityPoolId, entityId, _currentLevel)
{
Position = GetRandomPosition()
};
_entity.ShowEnemy(enemyData);
_currentSpawnEnemyId++;
}
private Vector3 GetRandomPosition()
{
float x = Random.Range(-1f, 1f);
float z = Random.Range(-1f, 1f);
Vector3 dir = new Vector3(x, 0, z).normalized;
return _player.position + dir * _spawnDistanceFromPlayer;
}
public void ClearEnemies()
{
foreach (var enemy in _enemies)
{
if (enemy == null || !enemy.Available) continue;
_entity.HideEntity(enemy);
}
_enemies.Clear();
}
#region Event Handler #region Event Handler
private void OnShowEntitySuccess(object sender, GameEventArgs e) private void OnShowEntitySuccess(object sender, GameEventArgs e)

View File

@ -0,0 +1,44 @@
using UnityGameFramework.Runtime;
namespace DataTable
{
/// <summary>
/// Bullet config table.
/// Id uses BulletType value.
/// </summary>
public class DRBullet : DataRowBase
{
private int m_Id;
public override int Id => m_Id;
/// <summary>
/// 子弹实体 Id用于在 DREntity 中查询资源路径
/// </summary>
public int EntityTypeId { get; private set; }
/// <summary>
/// 子弹速度
/// </summary>
public float Speed { get; private set; }
/// <summary>
/// 子弹生存时间
/// </summary>
public float MaxAliveTime { get; private set; }
public override bool ParseDataRow(string dataRowString, object userData)
{
string[] columnStrings = dataRowString.Split(DataTableExtension.DataSplitSeparators);
int index = 0;
index++;
m_Id = int.Parse(columnStrings[index++]);
index++;
EntityTypeId = int.Parse(columnStrings[index++]);
Speed = float.Parse(columnStrings[index++]);
MaxAliveTime = float.Parse(columnStrings[index++]);
return true;
}
}
}

View File

@ -1,8 +1,7 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 16371cc6e2112964d8b5bf7400c862cd guid: 537419cc75316184b84b9f806b114976
timeCreated: 1528026152
licenseType: Pro
MonoImporter: MonoImporter:
externalObjects: {}
serializedVersion: 2 serializedVersion: 2
defaultReferences: [] defaultReferences: []
executionOrder: 0 executionOrder: 0

View File

@ -5,12 +5,21 @@ namespace DataTable
public class DREnemy : DataRowBase public class DREnemy : DataRowBase
{ {
private int m_id; private int m_id;
public override int Id => m_id; public override int Id => m_id;
public int EntityTypeId { get; private set; }
public int MaxHealth { get; private set; } public int MaxHealth { get; private set; }
public int HpAddPerLevel { get; private set; } public int HpAddPerLevel { get; private set; }
public float Speed { get; private set; } public float Speed { get; private set; }
public int DropCoin { get; private set; } public int DropCoin { get; private set; }
public int DropExp { get; private set; } public int DropExp { get; private set; }
public float DropPercent { get; private set; } public float DropPercent { get; private set; }
public override bool ParseDataRow(string dataRowString, object userData) public override bool ParseDataRow(string dataRowString, object userData)
@ -21,6 +30,7 @@ namespace DataTable
index++; index++;
m_id = int.Parse(columnStrings[index++]); m_id = int.Parse(columnStrings[index++]);
index++; index++;
EntityTypeId = int.Parse(columnStrings[index++]);
MaxHealth = int.Parse(columnStrings[index++]); MaxHealth = int.Parse(columnStrings[index++]);
HpAddPerLevel = int.Parse(columnStrings[index++]); HpAddPerLevel = int.Parse(columnStrings[index++]);
Speed = float.Parse(columnStrings[index++]); Speed = float.Parse(columnStrings[index++]);

View File

@ -1,5 +1,5 @@
using Definition.Enum; using Definition.Enum;
using StarForce; using CustomUtility;
using UnityGameFramework.Runtime; using UnityGameFramework.Runtime;
namespace DataTable namespace DataTable

View File

@ -1,7 +1,6 @@
using System; using System;
using Definition.DataStruct; using CustomUtility;
using Definition.Enum; using Definition.Enum;
using StarForce;
using UnityGameFramework.Runtime; using UnityGameFramework.Runtime;
namespace DataTable namespace DataTable
@ -18,7 +17,7 @@ namespace DataTable
/// <summary> /// <summary>
/// 获取关卡内会生成的实体编号 /// 获取关卡内会生成的实体编号
/// </summary> /// </summary>
public int[] EntityIds { get; private set; } public EnemyType[] EntityTypes { get; private set; }
/// <summary> /// <summary>
/// 获取关卡内每次生成实体的数量 /// 获取关卡内每次生成实体的数量
@ -43,7 +42,7 @@ namespace DataTable
index++; index++;
m_Id = int.Parse(columnStrings[index++]); m_Id = int.Parse(columnStrings[index++]);
index++; index++;
GenerateEntityIds(columnStrings[index++]); GenerateEntityTypes(columnStrings[index++]);
GenerateEntityCounts(columnStrings[index++]); GenerateEntityCounts(columnStrings[index++]);
GenerateIntervals(columnStrings[index++]); GenerateIntervals(columnStrings[index++]);
Duration = int.Parse(columnStrings[index++]); Duration = int.Parse(columnStrings[index++]);
@ -56,7 +55,7 @@ namespace DataTable
{ {
} }
private void GenerateEntityIds(string raw) private void GenerateEntityTypes(string raw)
{ {
if (!raw.StartsWith('[') || !raw.EndsWith(']')) if (!raw.StartsWith('[') || !raw.EndsWith(']'))
{ {
@ -66,10 +65,10 @@ namespace DataTable
if (raw.Length == 2) return; if (raw.Length == 2) return;
string[] entityIds = raw.Substring(1, raw.Length - 2).Split(","); string[] entityIds = raw.Substring(1, raw.Length - 2).Split(",");
int length = entityIds.Length; int length = entityIds.Length;
EntityIds = new int[length]; EntityTypes = new EnemyType[length];
for (int i = 0; i < length; i++) for (int i = 0; i < length; i++)
{ {
EntityIds[i] = int.Parse(entityIds[i]); EntityTypes[i] = EnumUtility<EnemyType>.Get(entityIds[i]);
} }
} }

View File

@ -1,7 +1,7 @@
using Definition.DataStruct; using Definition.DataStruct;
using Definition.Enum; using Definition.Enum;
using Newtonsoft.Json; using Newtonsoft.Json;
using StarForce; using CustomUtility;
using UnityGameFramework.Runtime; using UnityGameFramework.Runtime;
namespace DataTable namespace DataTable

View File

@ -1,7 +1,6 @@
using Definition.DataStruct; using Definition.DataStruct;
using GameFramework;
using Newtonsoft.Json; using Newtonsoft.Json;
using StarForce; using CustomUtility;
using UnityGameFramework.Runtime; using UnityGameFramework.Runtime;
using Definition.Enum; using Definition.Enum;

View File

@ -1,11 +1,11 @@
using System; using System;
using System.IO; using System.Collections.Generic;
using System.Text; using System.Globalization;
using Definition.DataStruct; using Definition.DataStruct;
using Definition.Enum; using Definition.Enum;
using Entity;
using GameFramework; using GameFramework;
using StarForce; using CustomUtility;
using UnityEngine;
using UnityGameFramework.Runtime; using UnityGameFramework.Runtime;
namespace DataTable namespace DataTable
@ -22,6 +22,8 @@ namespace DataTable
/// </summary> /// </summary>
public override int Id => m_Id; public override int Id => m_Id;
public int EntityTypeId { get; private set; }
/// <summary> /// <summary>
/// 获取武器名称。 /// 获取武器名称。
/// </summary> /// </summary>
@ -32,8 +34,19 @@ namespace DataTable
/// </summary> /// </summary>
public string IconAssetName { get; private set; } public string IconAssetName { get; private set; }
/// <summary>
/// 获取武器稀有度
/// </summary>
public ItemRarity Rarity { get; private set; } public ItemRarity Rarity { get; private set; }
/// <summary>
/// 获取武器价值
/// </summary>
public int Price { get; private set; } public int Price { get; private set; }
/// <summary>
/// 获取武器价值浮动率
/// </summary>
public float PriceRandomPercent { get; private set; } public float PriceRandomPercent { get; private set; }
/// <summary> /// <summary>
@ -59,7 +72,7 @@ namespace DataTable
/// <summary> /// <summary>
/// 获取武器额外参数。 /// 获取武器额外参数。
/// </summary> /// </summary>
public string Pramas { get; private set; } public Dictionary<string, string> Pramas { get; private set; }
/// <summary> /// <summary>
/// 获取武器额外属性。 /// 获取武器额外属性。
@ -74,6 +87,7 @@ namespace DataTable
index++; index++;
m_Id = int.Parse(columnStrings[index++]); m_Id = int.Parse(columnStrings[index++]);
index++; index++;
EntityTypeId = int.Parse(columnStrings[index++]);
Title = columnStrings[index++]; Title = columnStrings[index++];
IconAssetName = columnStrings[index++]; IconAssetName = columnStrings[index++];
Rarity = EnumUtility<ItemRarity>.Get(columnStrings[index++]); Rarity = EnumUtility<ItemRarity>.Get(columnStrings[index++]);
@ -83,7 +97,7 @@ namespace DataTable
Cooldown = float.Parse(columnStrings[index++]); Cooldown = float.Parse(columnStrings[index++]);
AttackRange = float.Parse(columnStrings[index++]); AttackRange = float.Parse(columnStrings[index++]);
AttackSoundId = int.Parse(columnStrings[index++]); AttackSoundId = int.Parse(columnStrings[index++]);
Pramas = columnStrings[index++]; Pramas = DeserializeParams(columnStrings[index++]);
Modifiers = Utility.Json.ToObject<StatModifier[]>(columnStrings[index++]); Modifiers = Utility.Json.ToObject<StatModifier[]>(columnStrings[index++]);
GeneratePropertyArray(); GeneratePropertyArray();
@ -94,5 +108,30 @@ namespace DataTable
private void GeneratePropertyArray() private void GeneratePropertyArray()
{ {
} }
private Dictionary<string, string> DeserializeParams(string rawParams)
{
if (!rawParams.StartsWith('[') || !rawParams.EndsWith(']'))
{
throw new ArgumentException("Input must be enclosed in square brackets.");
}
var dict = new Dictionary<string, string>();
if (string.IsNullOrEmpty(rawParams)) return dict;
string[] items = rawParams.Substring(1, rawParams.Length - 2).Split(";");
foreach (var item in items)
{
string entry = item.Trim();
if (string.IsNullOrEmpty(entry)) continue;
string[] pair = entry.Split(':' , StringSplitOptions.RemoveEmptyEntries);
if (pair.Length != 2) continue;
dict.Add(pair[0].ToLower(), pair[1]);
}
return dict;
}
} }
} }

View File

@ -0,0 +1,11 @@
using Unity.Profiling;
namespace CustomDebugger
{
public static class CustomProfilerMarker
{
public static readonly ProfilerMarker Movement_Update = new ProfilerMarker("Movement_Update");
public static readonly ProfilerMarker ShopUI_Update = new("UGF.ShopUI.Update");
public static readonly ProfilerMarker Inventory_Refresh = new("UGF.Inventory.Refresh");
}
}

View File

@ -1,8 +1,7 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 822be177ff3fd674fa34da0755f1a427 guid: cbb99f0b076af384291a10730e51e365
timeCreated: 1528026153
licenseType: Pro
MonoImporter: MonoImporter:
externalObjects: {}
serializedVersion: 2 serializedVersion: 2
defaultReferences: [] defaultReferences: []
executionOrder: 0 executionOrder: 0

View File

@ -1,6 +1,6 @@
using System; using System;
using Definition.Enum; using Definition.Enum;
using StarForce; using CustomUtility;
using UnityGameFramework.Runtime; using UnityGameFramework.Runtime;
namespace Definition.DataStruct namespace Definition.DataStruct

View File

@ -0,0 +1,8 @@
namespace Definition.Enum
{
public enum BulletType : byte
{
None = 0,
BulletHandgun = 1
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2248c02c2c5529445992a2c2fef799dd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -3,7 +3,7 @@ namespace Definition.Enum
public enum EnemyType : byte public enum EnemyType : byte
{ {
None = 0, None = 0,
MeleeEnemy = 101, MeleeEnemy = 1,
RemoteEnemy = 102, RemoteEnemy = 2,
} }
} }

View File

@ -3,7 +3,8 @@ namespace Definition.Enum
public enum WeaponType : byte public enum WeaponType : byte
{ {
None = 0, None = 0,
WeaponKnife = 201, WeaponKnife = 1,
Remote = 202, WeaponHandgun = 2,
WeaponSlash = 3,
} }
} }

View File

@ -5,7 +5,8 @@ using CustomComponent;
using DataTable; using DataTable;
using Definition.DataStruct; using Definition.DataStruct;
using Entity; using Entity;
using Game.Utility; using CustomUtility;
using Procedure;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
@ -80,19 +81,29 @@ namespace StarForce.Editor
EnemyManagerComponent enemyManager = GameEntry.EnemyManager; EnemyManagerComponent enemyManager = GameEntry.EnemyManager;
Player player = FindPlayer(); Player player = FindPlayer();
ProcedureGame procedure = GameEntry.Procedure.CurrentProcedure as ProcedureGame;
if (enemyManager == null) if (enemyManager == null)
{ {
EditorGUILayout.HelpBox("EnemyManager is unavailable.", MessageType.Warning); EditorGUILayout.HelpBox("EnemyManager is unavailable.", MessageType.Warning);
return; return;
} }
if (procedure == null)
{
EditorGUILayout.HelpBox("ProcedureGame is unavailable.", MessageType.Warning);
return;
}
EditorGUILayout.LabelField("Current Spawn Rate Scale", enemyManager.SpawnRateScale.ToString("F2")); EditorGUILayout.LabelField("Current Spawn Rate Scale", enemyManager.SpawnRateScale.ToString("F2"));
EditorGUILayout.LabelField("Battle Time", EditorGUILayout.LabelField("Battle Time",
$"{enemyManager.ElapsedBattleTime:F1}s / {enemyManager.BattleDuration:F1}s"); $"{enemyManager.ElapsedBattleTime:F1}s / {enemyManager.BattleDuration:F1}s");
_spawnRateScaleInput = Mathf.Clamp(EditorGUILayout.FloatField("Spawn Rate Scale", _spawnRateScaleInput), 0.1f, _spawnRateScaleInput = Mathf.Clamp(EditorGUILayout.FloatField("Spawn Rate Scale", _spawnRateScaleInput),
0.1f,
50f); 50f);
_extendDurationSeconds = Mathf.Clamp(EditorGUILayout.FloatField("Add Duration (Seconds)", _extendDurationSeconds), _extendDurationSeconds = Mathf.Clamp(
EditorGUILayout.FloatField("Add Duration (Seconds)", _extendDurationSeconds),
1f, 3600f); 1f, 3600f);
EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginHorizontal();
@ -113,16 +124,21 @@ namespace StarForce.Editor
_spawnRateScaleInput = enemyManager.SpawnRateScale * 2f; _spawnRateScaleInput = enemyManager.SpawnRateScale * 2f;
enemyManager.SetSpawnRateScale(_spawnRateScaleInput); enemyManager.SetSpawnRateScale(_spawnRateScaleInput);
} }
EditorGUILayout.EndHorizontal(); EditorGUILayout.EndHorizontal();
if (GUILayout.Button("Add Battle Duration")) if (GUILayout.Button("Add Battle Duration"))
{ {
enemyManager.AddBattleDuration(_extendDurationSeconds); if (procedure.CurrentGameState is GameStateBattle gameState)
ShowNotification(new GUIContent($"+{_extendDurationSeconds:F0}s Battle Duration")); {
gameState.AddBattleDuration(_extendDurationSeconds);
ShowNotification(new GUIContent($"+{_extendDurationSeconds:F0}s Battle Duration"));
}
} }
EditorGUILayout.Space(6f); EditorGUILayout.Space(6f);
EditorGUILayout.LabelField("Player Weapon", player == null ? "Player not found" : (player.WeaponEnabled ? "Enabled" : "Disabled")); EditorGUILayout.LabelField("Player Weapon",
player == null ? "Player not found" : (player.WeaponEnabled ? "Enabled" : "Disabled"));
using (new EditorGUI.DisabledScope(player == null)) using (new EditorGUI.DisabledScope(player == null))
{ {
EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginHorizontal();

View File

@ -1,71 +0,0 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using System;
using Definition.Enum;
using StarForce;
using UnityEngine;
namespace Entity.EntityData
{
[Serializable]
public class BulletData : EntityDataBase
{
[SerializeField]
private int m_OwnerId = 0;
[SerializeField]
private CampType m_OwnerCamp = CampType.Unknown;
[SerializeField]
private int m_Attack = 0;
[SerializeField]
private float m_Speed = 0f;
public BulletData(int entityId, int typeId, int ownerId, CampType ownerCamp, int attack, float speed)
: base(entityId, typeId)
{
m_OwnerId = ownerId;
m_OwnerCamp = ownerCamp;
m_Attack = attack;
m_Speed = speed;
}
public int OwnerId
{
get
{
return m_OwnerId;
}
}
public CampType OwnerCamp
{
get
{
return m_OwnerCamp;
}
}
public int Attack
{
get
{
return m_Attack;
}
}
public float Speed
{
get
{
return m_Speed;
}
}
}
}

View File

@ -8,6 +8,10 @@ namespace Entity.EntityData
[Serializable] [Serializable]
public class EnemyData : TargetableObjectData public class EnemyData : TargetableObjectData
{ {
[SerializeField] private EnemyType _enemyType;
[SerializeField] private int _entityTypeId;
[SerializeField] private float _speedBase = 0; [SerializeField] private float _speedBase = 0;
[SerializeField] private int _dropCoin = 0; [SerializeField] private int _dropCoin = 0;
@ -16,42 +20,39 @@ namespace Entity.EntityData
[SerializeField] private float _dropPercent = 0; [SerializeField] private float _dropPercent = 0;
public EnemyData(int entityId, int typeId, int level) : base( public EnemyData(int entityId, EnemyType enemyType, int level) : base(
entityId, typeId, CampType.Enemy) entityId, (int)enemyType, CampType.Enemy)
{ {
DREnemy enemyRow = GameEntry.DataTable.GetDataTableRow<DREnemy>(typeId); DREnemy enemyRow = GameEntry.DataTable.GetDataTableRow<DREnemy>((int)enemyType);
if (enemyRow == null)
{
throw new Exception($"Enemy data table row is missing, EnemyType='{enemyType}'.");
}
int effectiveLevel = Mathf.Max(1, level); int effectiveLevel = Mathf.Max(1, level);
_enemyType = enemyType;
_entityTypeId = enemyRow.EntityTypeId;
MaxHealthBase = enemyRow.MaxHealth + enemyRow.HpAddPerLevel * (effectiveLevel - 1); MaxHealthBase = enemyRow.MaxHealth + enemyRow.HpAddPerLevel * (effectiveLevel - 1);
SpeedBase = enemyRow.Speed; _speedBase = enemyRow.Speed;
DropCoin = enemyRow.DropCoin; _dropCoin = enemyRow.DropCoin;
DropExp = enemyRow.DropExp; _dropExp = enemyRow.DropExp;
DropPercent = enemyRow.DropPercent; _dropPercent = enemyRow.DropPercent;
} }
public EnemyType EnemyType => _enemyType;
public int EntityTypeId => _entityTypeId;
public override int MaxHealthBase { get; } public override int MaxHealthBase { get; }
public float SpeedBase public float SpeedBase => _speedBase;
{
get => _speedBase;
set => _speedBase = value;
}
public int DropCoin public int DropCoin => _dropCoin;
{
get => _dropCoin;
set => _dropCoin = value;
}
public int DropExp public int DropExp => _dropExp;
{
get => _dropExp;
set => _dropExp = value;
}
public float DropPercent public float DropPercent => _dropPercent;
{
get => _dropPercent;
set => _dropPercent = value;
}
} }
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using DataTable; using DataTable;
using Definition.DataStruct; using Definition.DataStruct;
using Definition.Enum; using Definition.Enum;
@ -10,10 +11,31 @@ namespace Entity.EntityData
{ {
private DRWeapon _drWeapon; private DRWeapon _drWeapon;
public WeaponData(int entityId, int typeId, int ownerId, CampType ownerCamp) private int _entityTypeId = 0;
: base(entityId, typeId, ownerId, ownerCamp)
public WeaponData(int entityId, WeaponType weaponType, int ownerId, CampType ownerCamp)
: base(entityId, (int)weaponType, ownerId, ownerCamp)
{ {
_drWeapon = GameEntry.DataTable.GetDataTableRow<DRWeapon>(TypeId); _drWeapon = GameEntry.DataTable.GetDataTableRow<DRWeapon>((int)weaponType);
if (_drWeapon == null)
{
throw new Exception($"Weapon data table row is missing, WeaponType='{weaponType}'.");
}
_entityTypeId = _drWeapon.EntityTypeId;
}
public WeaponType WeaponType => (WeaponType)_drWeapon.Id;
public string GetParamsString(string paramsName)
{
if (!Params.TryGetValue(paramsName.ToLower(), out var value))
{
throw new Exception($"Parameter '{paramsName}' not found.");
}
return value;
} }
/// <summary> /// <summary>
@ -21,6 +43,8 @@ namespace Entity.EntityData
/// </summary> /// </summary>
public int Attack => _drWeapon.Attack; public int Attack => _drWeapon.Attack;
public int EntityTypeId => _entityTypeId;
/// <summary> /// <summary>
/// 武器名称。 /// 武器名称。
/// </summary> /// </summary>
@ -53,7 +77,7 @@ namespace Entity.EntityData
/// <summary> /// <summary>
/// 额外参数。 /// 额外参数。
/// </summary> /// </summary>
public string Params => _drWeapon.Pramas; public Dictionary<string, string> Params => _drWeapon.Pramas;
/// <summary> /// <summary>
/// 额外属性。 /// 额外属性。

View File

@ -0,0 +1,12 @@
using Definition.Enum;
namespace Entity.EntityData
{
public class WeaponHandgunData : WeaponData
{
public WeaponHandgunData(int entityId, int ownerId, CampType ownerCamp)
: base(entityId, WeaponType.WeaponHandgun, ownerId, ownerCamp)
{
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 574d105bf89bf7546a682ed407708034
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -4,7 +4,7 @@ namespace Entity.EntityData
{ {
public class WeaponKnifeData : WeaponData public class WeaponKnifeData : WeaponData
{ {
public WeaponKnifeData(int entityId, int typeId, int ownerId, CampType ownerCamp) : base(entityId, typeId, public WeaponKnifeData(int entityId, int ownerId, CampType ownerCamp) : base(entityId, WeaponType.WeaponKnife,
ownerId, ownerCamp) ownerId, ownerCamp)
{ {
} }

View File

@ -0,0 +1,12 @@
using Definition.Enum;
namespace Entity.EntityData
{
public class WeaponSlashData : WeaponData
{
public WeaponSlashData(int entityId, int ownerId, CampType ownerCamp)
: base(entityId, WeaponType.WeaponSlash, ownerId, ownerCamp)
{
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4aa6473525dc4c62aeee02ff562ec453
timeCreated: 1771484661

View File

@ -1,35 +1,25 @@
//------------------------------------------------------------ using System;
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using System;
using System.Collections.Generic; using System.Collections.Generic;
using Definition; using Definition;
using Entity.EntityData; using Entity.EntityData;
using UnityGameFramework.Runtime; using UnityGameFramework.Runtime;
using DataTable; using DataTable;
using Definition.Enum; using CustomUtility;
using Game.Utility;
namespace Entity namespace Entity
{ {
public static class EntityExtension public static class EntityExtension
{ {
// 关于 EntityId 的约定:
// 0 为无效
// 正值用于和服务器通信的实体如玩家角色、NPC、怪等服务器只产生正值
// 负值用于本地生成的临时实体如特效、FakeObject等
private static int s_SerialId = -10; private static int s_SerialId = -10;
private const string EntityNamespace = "Entity."; private const string EntityNamespace = "Entity.";
private static Dictionary<string, Type> _typeDict; private static readonly Dictionary<string, Type> _typeDict;
private static readonly Dictionary<int, string> _assetNameDict;
static EntityExtension() static EntityExtension()
{ {
_typeDict = new Dictionary<string, Type>(); _typeDict = new Dictionary<string, Type>();
_assetNameDict = new Dictionary<int, string>();
} }
public static EntityBase GetGameEntity(this EntityComponent entityComponent, int entityId) public static EntityBase GetGameEntity(this EntityComponent entityComponent, int entityId)
@ -61,44 +51,63 @@ namespace Entity
public static void ShowEnemy(this EntityComponent entityComponent, EnemyData data) public static void ShowEnemy(this EntityComponent entityComponent, EnemyData data)
{ {
string typeName = EntityNamespace + (EnemyType)data.TypeId; if (data == null)
if (!_typeDict.TryGetValue(typeName, out Type enemyType))
{ {
enemyType = Type.GetType(typeName); Log.Warning("Enemy data is invalid.");
if (enemyType == null) return;
{
Log.Warning("Can not load entity type '{0}'.", typeName);
return;
}
_typeDict.Add(typeName, enemyType);
} }
entityComponent.ShowEntity(enemyType, "Enemy", Constant.AssetPriority.EnemyAsset, data); var enemyType = TryGetType(data.EnemyType.ToString());
var assetName = TryGetAssetName(data.EntityTypeId);
entityComponent.ShowEntity(
entityId: data.Id,
entityLogicType: enemyType,
entityAssetName: AssetUtility.GetEntityAsset(assetName),
entityGroupName: "Enemy",
priority: Constant.AssetPriority.BulletAsset,
userData: data);
} }
public static void ShowWeapon(this EntityComponent entityComponent, WeaponData data) public static void ShowWeapon(this EntityComponent entityComponent, WeaponData data)
{ {
string typeName = EntityNamespace + (WeaponType)data.TypeId; if (data == null)
if (!_typeDict.TryGetValue(typeName, out Type weaponType))
{ {
weaponType = Type.GetType(typeName); Log.Warning("Weapon data is invalid.");
if (weaponType == null) return;
{
Log.Warning("Can not load entity type '{0}'.", typeName);
return;
}
_typeDict.Add(typeName, weaponType);
} }
entityComponent.ShowEntity(weaponType, "Weapon", Constant.AssetPriority.WeaponAsset, data); var weaponType = TryGetType("Weapon." + data.WeaponType);
var assetName = TryGetAssetName(data.EntityTypeId);
entityComponent.ShowEntity(
entityId: data.Id,
entityLogicType: weaponType,
entityAssetName: AssetUtility.GetEntityAsset(assetName),
entityGroupName: "Weapon",
priority: Constant.AssetPriority.BulletAsset,
userData: data);
} }
public static void ShowBullet(this EntityComponent entityComponent, BulletData data) // public static void ShowBullet(this EntityComponent entityComponent, BulletData data)
{ // {
entityComponent.ShowEntity(typeof(Bullet), "Bullet", Constant.AssetPriority.BulletAsset, data); // if (data == null)
} // {
// Log.Warning("Bullet data is invalid.");
// return;
// }
//
// var bulletType = TryGetType(data.BulletType.ToString());
// string assetName = TryGetAssetName(data.EntityTypeId);
//
// entityComponent.ShowEntity(
// entityId: data.Id,
// entityLogicType: bulletType,
// entityAssetName: AssetUtility.GetEntityAsset(assetName),
// entityGroupName: "Bullet",
// priority: Constant.AssetPriority.BulletAsset,
// userData: data);
// }
public static void ShowEffect(this EntityComponent entityComponent, EffectData data) public static void ShowEffect(this EntityComponent entityComponent, EffectData data)
{ {
@ -135,6 +144,42 @@ namespace Entity
priority, data); priority, data);
} }
private static Type TryGetType(string rawTypeName)
{
string typeName = EntityNamespace + rawTypeName;
if (!_typeDict.TryGetValue(typeName, out Type type))
{
type = Type.GetType(typeName);
if (type == null)
{
Log.Warning("Can not load entity type '{0}'.", typeName);
return null;
}
_typeDict.Add(typeName, type);
}
return type;
}
private static string TryGetAssetName(int entityId)
{
if (!_assetNameDict.TryGetValue(entityId, out string assetName))
{
DREntity drEntity = GameEntry.DataTable.GetDataTableRow<DREntity>(entityId);
if (drEntity == null)
{
Log.Warning("Can not load bullet entity id '{0}' from data table.", entityId.ToString());
return null;
}
assetName = drEntity.AssetName;
_assetNameDict.Add(entityId, assetName);
}
return assetName;
}
public static int GenerateSerialId(this EntityComponent entityComponent) public static int GenerateSerialId(this EntityComponent entityComponent)
{ {
return --s_SerialId; return --s_SerialId;

View File

@ -1,53 +0,0 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using Definition.DataStruct;
using Entity.EntityData;
using StarForce;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace Entity
{
/// <summary>
/// 子弹类。
/// </summary>
public class Bullet : EntityBase
{
[SerializeField] private BulletData m_BulletData = null;
public ImpactData GetImpactData()
{
//TODO:子弹的 AttackStat 不该为 null
return new ImpactData(m_BulletData.OwnerCamp, m_BulletData.Attack, null);
}
protected override void OnInit(object userData)
{
base.OnInit(userData);
}
protected override void OnShow(object userData)
{
base.OnShow(userData);
m_BulletData = userData as BulletData;
if (m_BulletData == null)
{
Log.Error("Bullet data is invalid.");
return;
}
}
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(elapseSeconds, realElapseSeconds);
CachedTransform.Translate(Vector3.forward * m_BulletData.Speed * elapseSeconds, Space.World);
}
}
}

View File

@ -1,6 +1,7 @@
using Components; using Components;
using Definition.DataStruct; using Definition.DataStruct;
using Entity.EntityData; using Entity.EntityData;
using Entity.Weapon;
using UnityEngine; using UnityEngine;
using UnityGameFramework.Runtime; using UnityGameFramework.Runtime;
@ -39,7 +40,7 @@ namespace Entity
{ {
_meleeEnemyData = enemyData; _meleeEnemyData = enemyData;
_healthComponent.OnInit(enemyData.MaxHealthBase); _healthComponent.OnInit(enemyData.MaxHealthBase);
_movementComponent.OnInit(_meleeEnemyData.SpeedBase, this.CachedTransform); _movementComponent.OnInit(_meleeEnemyData.SpeedBase, this.CachedTransform, null, true);
_movementComponent.SetMove(true); _movementComponent.SetMove(true);
_attackRangeSquared = _attackRange * _attackRange; _attackRangeSquared = _attackRange * _attackRange;
this.CachedTransform.position = enemyData.Position; this.CachedTransform.position = enemyData.Position;
@ -55,6 +56,13 @@ namespace Entity
{ {
base.OnUpdate(elapseSeconds, realElapseSeconds); base.OnUpdate(elapseSeconds, realElapseSeconds);
if (_target == null)
{
_movementComponent.SetMove(false);
_movementComponent.OnUpdate(elapseSeconds, realElapseSeconds);
return;
}
float distanceSquared = (this.CachedTransform.position - _target.position).sqrMagnitude; float distanceSquared = (this.CachedTransform.position - _target.position).sqrMagnitude;
if (distanceSquared < _attackRangeSquared) if (distanceSquared < _attackRangeSquared)
{ {
@ -105,6 +113,11 @@ namespace Entity
private Vector3 GetTargetDirection() private Vector3 GetTargetDirection()
{ {
if (_target == null)
{
return Vector3.zero;
}
return new Vector3( return new Vector3(
_target.position.x - this.CachedTransform.position.x, _target.position.x - this.CachedTransform.position.x,
0f, 0f,

View File

@ -37,7 +37,7 @@ namespace Entity
{ {
_remoteEnemyData = enemyData; _remoteEnemyData = enemyData;
_healthComponent.OnInit(enemyData.MaxHealthBase); _healthComponent.OnInit(enemyData.MaxHealthBase);
_movementComponent.OnInit(_remoteEnemyData.SpeedBase, this.CachedTransform); _movementComponent.OnInit(_remoteEnemyData.SpeedBase, this.CachedTransform, null, true);
_movementComponent.SetMove(true); _movementComponent.SetMove(true);
_attackRangeSquared = _attackRange * _attackRange; _attackRangeSquared = _attackRange * _attackRange;
this.CachedTransform.position = enemyData.Position; this.CachedTransform.position = enemyData.Position;
@ -52,6 +52,13 @@ namespace Entity
{ {
base.OnUpdate(elapseSeconds, realElapseSeconds); base.OnUpdate(elapseSeconds, realElapseSeconds);
if (_target == null)
{
_movementComponent.SetMove(false);
_movementComponent.OnUpdate(elapseSeconds, realElapseSeconds);
return;
}
float distanceSquared = (this.CachedTransform.position - _target.position).sqrMagnitude; float distanceSquared = (this.CachedTransform.position - _target.position).sqrMagnitude;
if (distanceSquared < _attackRangeSquared) if (distanceSquared < _attackRangeSquared)
{ {
@ -77,6 +84,11 @@ namespace Entity
private Vector3 GetTargetDirection() private Vector3 GetTargetDirection()
{ {
if (_target == null)
{
return Vector3.zero;
}
return new Vector3( return new Vector3(
_target.position.x - this.CachedTransform.position.x, _target.position.x - this.CachedTransform.position.x,
0f, 0f,

View File

@ -6,6 +6,7 @@ using Definition.DataStruct;
using Entity.EntityData; using Entity.EntityData;
using GameFramework.Event; using GameFramework.Event;
using UnityGameFramework.Runtime; using UnityGameFramework.Runtime;
using Entity.Weapon;
namespace Entity namespace Entity
{ {
@ -45,6 +46,7 @@ namespace Entity
/// 玩家是否启用。 /// 玩家是否启用。
/// </summary> /// </summary>
private bool _enable; private bool _enable;
private bool _weaponEnabled = true; private bool _weaponEnabled = true;
public int Coin public int Coin
@ -89,6 +91,22 @@ namespace Entity
return _backpackComponent.AttachProp(prop); return _backpackComponent.AttachProp(prop);
} }
public bool RemoveWeapon(WeaponBase weapon)
{
if (weapon == null || _backpackComponent == null)
{
return false;
}
if (!_backpackComponent.DetachWeapon(weapon))
{
return false;
}
GameEntry.Entity.HideEntity(weapon);
return true;
}
public bool Enable public bool Enable
{ {
get => _enable; get => _enable;
@ -122,8 +140,7 @@ namespace Entity
// BackpackComponent // BackpackComponent
Coin = role.Coin; Coin = role.Coin;
_backpackComponent.OnInit(this, role.WeaponCapacity); _backpackComponent.OnInit(this, role.WeaponCapacity);
GameEntry.Entity.ShowWeapon(new WeaponKnifeData(GameEntry.Entity.GenerateSerialId(), 201, GameEntry.Entity.ShowWeapon(new WeaponKnifeData(GameEntry.Entity.GenerateSerialId(), Id, _playerData.Camp));
this.Id, _playerData.Camp));
// StatComponent // StatComponent
foreach (var modifier in role.InitialProperties) foreach (var modifier in role.InitialProperties)

View File

@ -8,7 +8,7 @@
using Components; using Components;
using Definition.DataStruct; using Definition.DataStruct;
using Entity.EntityData; using Entity.EntityData;
using Game.Utility; using CustomUtility;
using UnityEngine; using UnityEngine;
namespace Entity namespace Entity

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: abdf25a9782ebf844a65b2348f8f566d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,54 @@
using UnityEngine;
namespace Entity.Weapon
{
public sealed class HandgunHitMarkerAttackEffect : IWeaponAttackEffect
{
private readonly float _size;
private readonly float _yOffset;
private readonly float _duration;
private readonly Color _color;
public HandgunHitMarkerAttackEffect(float size, float yOffset, float duration, Color color)
{
_size = size;
_yOffset = yOffset;
_duration = duration;
_color = color;
}
public void Play(WeaponBase weapon, Vector3 position, EntityBase target, float radius)
{
if (target == null) return;
GameObject marker = GameObject.CreatePrimitive(PrimitiveType.Sphere);
marker.name = "HandgunHitMarker";
Collider collider = marker.GetComponent<Collider>();
if (collider != null)
{
Object.Destroy(collider);
}
marker.transform.SetParent(target.CachedTransform, false);
marker.transform.localPosition = new Vector3(0f, _yOffset, 0f);
marker.transform.localScale = Vector3.one * Mathf.Max(0.01f, _size);
Renderer renderer = marker.GetComponent<Renderer>();
if (renderer != null)
{
Shader shader = Shader.Find("Sprites/Default");
if (shader == null)
{
shader = Shader.Find("Unlit/Color");
}
Material material = new Material(shader);
material.color = _color;
renderer.material = material;
}
Object.Destroy(marker, Mathf.Max(0.01f, _duration));
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d944f33383dc4c1ebe850a12d4211afa
timeCreated: 1771481130

View File

@ -0,0 +1,9 @@
using UnityEngine;
namespace Entity.Weapon
{
public interface IWeaponAttackEffect
{
void Play(WeaponBase weapon, Vector3 position, EntityBase target, float radius);
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 231a935a484c4351b707b3e359074a47
timeCreated: 1771481068

View File

@ -0,0 +1,63 @@
using UnityEngine;
namespace Entity.Weapon
{
public sealed class KnifeRangeAttackEffect : IWeaponAttackEffect
{
private readonly float _duration = 0.2f;
private readonly float _yOffset = 0.05f;
private readonly float _lineWidth = 0.05f;
private readonly int _segments = 48;
private readonly Color _color = new(0.1f, 1f, 0.1f, 0.9f);
public KnifeRangeAttackEffect()
{
}
public KnifeRangeAttackEffect(float duration, float yOffset, float lineWidth, int segments, Color color)
{
_duration = duration;
_yOffset = yOffset;
_lineWidth = lineWidth;
_segments = Mathf.Max(8, segments);
_color = color;
}
public void Play(WeaponBase weapon, Vector3 position, EntityBase target, float radius)
{
float safeRadius = Mathf.Max(0.1f, radius);
GameObject indicator = new GameObject("KnifeRangeIndicator");
indicator.transform.position = new Vector3(position.x, position.y + _yOffset, position.z);
LineRenderer line = indicator.AddComponent<LineRenderer>();
line.loop = true;
line.useWorldSpace = true;
line.positionCount = _segments;
line.startWidth = _lineWidth;
line.endWidth = _lineWidth;
line.startColor = _color;
line.endColor = _color;
Shader shader = Shader.Find("Sprites/Default");
if (shader == null)
{
shader = Shader.Find("Unlit/Color");
}
Material material = new Material(shader);
material.color = _color;
line.material = material;
float step = Mathf.PI * 2f / _segments;
for (int i = 0; i < _segments; i++)
{
float angle = i * step;
Vector3 offset = new Vector3(Mathf.Cos(angle) * safeRadius, 0f, Mathf.Sin(angle) * safeRadius);
line.SetPosition(i, indicator.transform.position + offset);
}
Object.Destroy(indicator, Mathf.Max(0.01f, _duration));
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8f48328040644546bf0a866cc337d3b2
timeCreated: 1771481100

View File

@ -0,0 +1,86 @@
using UnityEngine;
namespace Entity.Weapon
{
public sealed class SlashSectorAttackEffect : IWeaponAttackEffect
{
private readonly float _duration = 0.2f;
private readonly float _yOffset = 0.06f;
private readonly float _lineWidth = 0.05f;
private readonly int _segments = 24;
private readonly Color _color = new(1f, 0.45f, 0.2f, 0.9f);
public SlashSectorAttackEffect()
{
}
public SlashSectorAttackEffect(float duration, float yOffset, float lineWidth, int segments, Color color)
{
_duration = duration;
_yOffset = yOffset;
_lineWidth = lineWidth;
_segments = Mathf.Max(6, segments);
_color = color;
}
public void Play(WeaponBase weapon, Vector3 position, EntityBase target, float radius)
{
if (weapon is not WeaponSlash slash) return;
float safeRadius = Mathf.Max(0.1f, radius);
float sectorAngle = Mathf.Clamp(slash.SectorAngle, 1f, 360f);
GameObject indicator = new GameObject("SlashSectorIndicator");
Vector3 center = new Vector3(position.x, position.y + _yOffset, position.z);
indicator.transform.position = center;
LineRenderer line = indicator.AddComponent<LineRenderer>();
line.loop = false;
line.useWorldSpace = true;
line.startWidth = _lineWidth;
line.endWidth = _lineWidth;
line.startColor = _color;
line.endColor = _color;
Shader shader = Shader.Find("Sprites/Default");
if (shader == null)
{
shader = Shader.Find("Unlit/Color");
}
Material material = new Material(shader);
material.color = _color;
line.material = material;
float half = sectorAngle * 0.5f;
int arcPoints = _segments + 1;
int positionCount = arcPoints + 2;
line.positionCount = positionCount;
Vector3 forward = weapon.CachedTransform.forward;
forward.y = 0f;
if (forward.sqrMagnitude <= Mathf.Epsilon)
{
forward = Vector3.forward;
}
forward.Normalize();
Quaternion baseRotation = Quaternion.LookRotation(forward, Vector3.up);
line.SetPosition(0, center);
for (int i = 0; i < arcPoints; i++)
{
float t = arcPoints <= 1 ? 0f : i / (float)(arcPoints - 1);
float angle = Mathf.Lerp(-half, half, t);
Vector3 dir = Quaternion.Euler(0f, angle, 0f) * Vector3.forward;
Vector3 worldDir = baseRotation * dir;
line.SetPosition(i + 1, center + worldDir * safeRadius);
}
line.SetPosition(positionCount - 1, center);
UnityEngine.Object.Destroy(indicator, Mathf.Max(0.01f, _duration));
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 329af2c156b345f28a7465ef29c943da
timeCreated: 1771485834

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 371bc24a18a3447686043843fdd6fbe5
timeCreated: 1771427642

View File

@ -0,0 +1,49 @@
using System.Collections.Generic;
using Components;
using CustomUtility;
namespace Entity.Weapon
{
public sealed class HighestHealthTargetSelector : ITargetSelector
{
public EntityBase SelectTarget(WeaponBase weapon, IEnumerable<EntityBase> candidates, float maxSqrRange)
{
if (weapon == null || candidates == null)
{
return null;
}
EntityBase target = null;
float maxHp = float.MinValue;
float maxSqr = maxSqrRange > 0f ? maxSqrRange : float.MaxValue;
foreach (var candidate in candidates)
{
if (candidate == null || !candidate.Available) continue;
if (maxSqrRange > 0f && AIUtility.GetSqrMagnitudeXZ(weapon, candidate) >= maxSqr) continue;
if (!TryGetCurrentHealth(candidate, out var hp)) continue;
if (hp <= maxHp) continue;
maxHp = hp;
target = candidate;
}
return target;
}
private static bool TryGetCurrentHealth(EntityBase target, out float health)
{
health = 0f;
if (target == null) return false;
var healthComponent = target.GetComponent<HealthComponent>();
if (healthComponent == null) return false;
health = healthComponent.CurrentHealth;
return true;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 54932952c9fa3fd4dbe6d21f2a484b4d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace Entity.Weapon
{
public interface ITargetSelector
{
EntityBase SelectTarget(WeaponBase weapon, IEnumerable<EntityBase> candidates, float maxSqrRange);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1ed801e4816c03b4597756dc36d6aba8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,53 @@
using System.Collections.Generic;
using Components;
using Definition.Enum;
using CustomUtility;
namespace Entity.Weapon
{
public sealed class LowestHealthTargetSelector : ITargetSelector
{
public EntityBase SelectTarget(WeaponBase weapon, IEnumerable<EntityBase> candidates, float maxSqrRange)
{
if (weapon == null || candidates == null)
{
return null;
}
EntityBase target = null;
float minHp = float.MaxValue;
float maxSqr = maxSqrRange > 0f ? maxSqrRange : float.MaxValue;
foreach (var candidate in candidates)
{
if (candidate == null || !candidate.Available) continue;
if (maxSqrRange > 0f && AIUtility.GetSqrMagnitudeXZ(weapon, candidate) >= maxSqr) continue;
if (!TryGetHealth(candidate, out var hp)) continue;
if (hp >= minHp) continue;
minHp = hp;
target = candidate;
}
return target;
}
private static bool TryGetHealth(EntityBase target, out float health)
{
health = 0f;
if (target == null) return false;
var statComponent = target.GetComponent<StatComponent>();
if (statComponent == null) return false;
var hpStat = statComponent.GetStat(StatType.MaxHealth);
if (hpStat == null) return false;
health = hpStat.Value;
return true;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 081d67a491f5792479306d88091168e3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,32 @@
using System.Collections.Generic;
using CustomUtility;
namespace Entity.Weapon
{
public sealed class NearestTargetSelector : ITargetSelector
{
public EntityBase SelectTarget(WeaponBase weapon, IEnumerable<EntityBase> candidates, float maxSqrRange)
{
if (weapon == null || candidates == null)
{
return null;
}
EntityBase target = null;
float minSqrMagnitude = maxSqrRange > 0f ? maxSqrRange : float.MaxValue;
foreach (var candidate in candidates)
{
if (candidate == null || !candidate.Available) continue;
float sqrMagnitude = AIUtility.GetSqrMagnitudeXZ(weapon, candidate);
if (sqrMagnitude >= minSqrMagnitude) continue;
minSqrMagnitude = sqrMagnitude;
target = candidate;
}
return target;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a70602535b318e74396a6c2ea282834d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
namespace Entity.Weapon
{
public enum TargetSelectorType
{
Nearest = 0,
HighestHealth = 1,
LowestHealth = 2
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4eb1e0b99139fdd4280c8616804a8f77
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,18 +1,19 @@
using System; using System.Collections.Generic;
using Components;
using Definition.DataStruct; using Definition.DataStruct;
using Definition.Enum;
using Entity.EntityData; using Entity.EntityData;
using GameFramework; using GameFramework;
using CustomUtility;
using UnityEngine; using UnityEngine;
using UnityGameFramework.Runtime; using UnityGameFramework.Runtime;
namespace Entity namespace Entity.Weapon
{ {
public enum WeaponStateType public enum WeaponStateType
{ {
Disabled, Disabled,
Check,
Attack, Attack,
Transition,
Idle, Idle,
Check_OutRange, Check_OutRange,
Check_InRange, Check_InRange,
@ -29,37 +30,94 @@ namespace Entity
[SerializeField] protected bool _isEnabled = false; [SerializeField] protected bool _isEnabled = false;
public WeaponData WeaponData; public WeaponData WeaponData { get; protected set; }
public bool IsAttacking => _isAttacking; public bool IsAttacking => _isAttacking;
protected ITargetSelector TargetSelector { get; set; }
protected Dictionary<WeaponStateType, WeaponStateBase> _states;
protected WeaponStateBase _currentState; protected WeaponStateBase _currentState;
#region FSM protected EntityBase _target;
protected float _currAttackTimer;
protected float _sqrRange;
protected StatProperty AttackStat { get; private set; } = new();
private StatComponent _attackStatComponent;
private System.Action<StatModifier, bool> _attackStatCallback;
private static readonly List<EntityBase> s_EmptyCandidates = new();
#region Lifecycle
protected override void OnInit(object userData) protected override void OnInit(object userData)
{ {
base.OnInit(userData); base.OnInit(userData);
if (TargetSelector == null)
{
TargetSelector = CreateSelector(DefaultTargetSelectorType);
}
} }
//protected abstract void OnShow(object userData); protected sealed override void OnShow(object userData)
// { {
// base.OnShow(userData); base.OnShow(userData);
//
// _weaponData = userData as WeaponData;
// if (_weaponData == null)
// {
// Log.Error("Weapon data is invalid.");
// return;
// }
// }
protected override void OnAttachTo(EntityLogic parentEntity, Transform parentTransform, object userData) if (!OnWeaponShow(userData)) return;
EnsureStatesBuilt();
TransitionTo(InitialState);
}
protected sealed override void OnHide(bool isShutdown, object userData)
{
ReleaseAttackStatSubscription();
OnWeaponHide(userData);
base.OnHide(isShutdown, userData);
}
protected sealed override void OnAttachTo(EntityLogic parentEntity, Transform parentTransform, object userData)
{ {
base.OnAttachTo(parentEntity, parentTransform, userData); base.OnAttachTo(parentEntity, parentTransform, userData);
Name = Utility.Text.Format("Weapon of {0}", parentEntity.Name); Name = Utility.Text.Format("Weapon of {0}", parentEntity.Name);
CachedTransform.localPosition = Vector3.zero; CachedTransform.localPosition = Vector3.zero;
OnWeaponAttach(parentEntity, parentTransform, userData);
}
protected sealed override void OnDetachFrom(EntityLogic parentEntity, object userData)
{
OnWeaponDetach(parentEntity, userData);
base.OnDetachFrom(parentEntity, userData);
}
protected virtual bool OnWeaponShow(object userData) => true;
protected virtual void OnWeaponHide(object userData)
{
}
protected virtual void OnWeaponAttach(EntityLogic parentEntity, Transform parentTransform, object userData)
{
}
protected virtual void OnWeaponDetach(EntityLogic parentEntity, object userData)
{
}
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(elapseSeconds, realElapseSeconds);
if (!_isEnabled) return;
_currentState?.OnUpdate(elapseSeconds, realElapseSeconds);
} }
#endregion #endregion
@ -67,12 +125,147 @@ namespace Entity
public abstract ImpactData GetImpactData(); public abstract ImpactData GetImpactData();
protected abstract void Attack(); protected abstract void Attack();
protected abstract void Check(); protected abstract void Check();
protected abstract void TransitionTo(WeaponStateType newState); protected abstract void BuildStates();
protected virtual WeaponStateType InitialState => WeaponStateType.Idle;
protected virtual TargetSelectorType DefaultTargetSelectorType => TargetSelectorType.Nearest;
protected void RegisterState(WeaponStateBase state)
{
if (state == null)
{
Log.Error("Weapon state is invalid.");
return;
}
state.OnInit(this);
_states[state.State] = state;
}
protected void EnsureStatesBuilt()
{
if (_states != null) return;
_states = new Dictionary<WeaponStateType, WeaponStateBase>();
BuildStates();
}
protected virtual void TransitionTo(WeaponStateType newState)
{
if (_states == null || !_states.TryGetValue(newState, out var nextState))
{
Log.Error("Weapon state not found: {0}", newState);
return;
}
_currentState?.OnLeave();
_currentState = nextState;
_currentState.OnEnter();
}
public void SetEnabled(bool value) public void SetEnabled(bool value)
{ {
this._isEnabled = value; _isEnabled = value;
TransitionTo(WeaponStateType.Idle);
OnEnabledChanged(value);
if (_states == null || _states.Count == 0) return;
if (value)
{
TransitionTo(InitialState);
return;
}
if (_states.ContainsKey(WeaponStateType.Disabled))
{
TransitionTo(WeaponStateType.Disabled);
}
}
protected virtual void OnEnabledChanged(bool enabled)
{
}
protected T RequireWeaponData<T>(object userData) where T : WeaponData
{
var data = userData as T;
if (data == null)
{
Log.Error("{0} data is invalid.", typeof(T).Name);
}
return data;
}
protected virtual IEnumerable<EntityBase> GetCandidates()
{
var enemyManager = GameEntry.EnemyManager;
return enemyManager != null ? enemyManager.Enemies : s_EmptyCandidates;
}
protected EntityBase SelectTarget(float maxSqrRange)
{
return TargetSelector != null ? TargetSelector.SelectTarget(this, GetCandidates(), maxSqrRange) : null;
}
protected float GetSqrDistance(EntityBase target)
{
return target != null ? AIUtility.GetSqrMagnitudeXZ(this, target) : float.MaxValue;
}
protected bool IsInRange(EntityBase target, float sqrRange)
{
if (target == null) return false;
return AIUtility.GetSqrMagnitudeXZ(this, target) < sqrRange;
}
protected void SetTargetSelector(TargetSelectorType selectorType)
{
TargetSelector = CreateSelector(selectorType);
}
protected static ITargetSelector CreateSelector(TargetSelectorType selectorType)
{
switch (selectorType)
{
case TargetSelectorType.HighestHealth:
return new HighestHealthTargetSelector();
case TargetSelectorType.LowestHealth:
return new LowestHealthTargetSelector();
case TargetSelectorType.Nearest:
default:
return new NearestTargetSelector();
}
}
protected void BindAttackStatFromOwner(EntityLogic parentEntity)
{
ReleaseAttackStatSubscription();
AttackStat = new StatProperty();
if (parentEntity is not Player player) return;
var statComponent = player.GetComponent<StatComponent>();
if (statComponent == null) return;
AttackStat = statComponent.GetStat(StatType.Attack);
_attackStatComponent = statComponent;
_attackStatCallback = (modifier, isApply) =>
statComponent.UpdateStat(AttackStat, modifier, isApply);
statComponent.Subscribe(StatType.Attack, _attackStatCallback);
}
protected void ReleaseAttackStatSubscription()
{
if (_attackStatComponent != null && _attackStatCallback != null)
{
_attackStatComponent.Unsubscribe(StatType.Attack, _attackStatCallback);
}
_attackStatComponent = null;
_attackStatCallback = null;
AttackStat = new StatProperty();
} }
} }
@ -85,4 +278,12 @@ namespace Entity
public abstract void OnLeave(); public abstract void OnLeave();
public override string ToString() => State.ToString(); public override string ToString() => State.ToString();
} }
} }

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0b289e88dc5243aeb564df941485c5cf
timeCreated: 1771579168

View File

@ -0,0 +1,25 @@
namespace Entity.Weapon
{
public partial class WeaponHandgun
{
private class AttackState : WeaponStateBase
{
private WeaponHandgun _weapon;
public override WeaponStateType State => WeaponStateType.Attack;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponHandgun;
public override void OnEnter()
{
_weapon._currAttackTimer = 0f;
_weapon.Attack();
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.TransitionTo(WeaponStateType.Check_InRange);
}
public override void OnLeave() { }
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4e72df067466464fa3be92bfd049dd0d
timeCreated: 1771579220

View File

@ -0,0 +1,46 @@
namespace Entity.Weapon
{
public partial class WeaponHandgun
{
private class CheckInRangeState : WeaponStateBase
{
private WeaponHandgun _weapon;
public override WeaponStateType State => WeaponStateType.Check_InRange;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponHandgun;
public override void OnEnter()
{
if (_weapon._currAttackTimer >= _weapon._weaponData.Cooldown)
{
_weapon.TransitionTo(WeaponStateType.Attack);
}
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.Check();
_weapon.RotateToTarget(elapseSeconds);
_weapon._currAttackTimer += elapseSeconds;
if (_weapon._target == null || !_weapon._target.Available)
{
_weapon.TransitionTo(WeaponStateType.Idle);
return;
}
if (!_weapon.IsInRange(_weapon._target, _weapon._sqrRange))
{
_weapon.TransitionTo(WeaponStateType.Check_OutRange);
return;
}
if (_weapon._currAttackTimer >= _weapon._weaponData.Cooldown)
{
_weapon.TransitionTo(WeaponStateType.Attack);
}
}
public override void OnLeave() { }
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bfd76c0fb7284ceaa28f78c702f595da
timeCreated: 1771579231

View File

@ -0,0 +1,33 @@
namespace Entity.Weapon
{
public partial class WeaponHandgun
{
private class CheckOutRangeState : WeaponStateBase
{
private WeaponHandgun _weapon;
public override WeaponStateType State => WeaponStateType.Check_OutRange;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponHandgun;
public override void OnEnter() { }
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.Check();
_weapon.RotateToTarget(elapseSeconds);
_weapon._currAttackTimer += elapseSeconds;
if (_weapon._target == null || !_weapon._target.Available)
{
_weapon.TransitionTo(WeaponStateType.Idle);
return;
}
if (_weapon.IsInRange(_weapon._target, _weapon._sqrRange))
{
_weapon.TransitionTo(WeaponStateType.Check_InRange);
}
}
public override void OnLeave() { }
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3a9f936d3c8643f7bf632734548f800c
timeCreated: 1771579241

View File

@ -0,0 +1,27 @@
namespace Entity.Weapon
{
public partial class WeaponHandgun
{
private class IdleState : WeaponStateBase
{
private WeaponHandgun _weapon;
public override WeaponStateType State => WeaponStateType.Idle;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponHandgun;
public override void OnEnter() { }
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.Check();
_weapon.RotateToOrigin(elapseSeconds);
_weapon._currAttackTimer += elapseSeconds;
if (_weapon._target != null && _weapon._target.Available)
{
_weapon.TransitionTo(WeaponStateType.Check_OutRange);
}
}
public override void OnLeave() { }
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9c5341944d1a467b8553d31bc3d81d5b
timeCreated: 1771579252

View File

@ -0,0 +1,143 @@
using Definition.DataStruct;
using Definition.Enum;
using Entity.EntityData;
using CustomUtility;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace Entity.Weapon
{
public partial class WeaponHandgun : WeaponBase
{
#region Property
private WeaponHandgunData _weaponData;
private Quaternion _cachedRotation;
[SerializeField] private float _rotateSpeed = 6f;
[SerializeField] private LayerMask _hitMask = ~0;
[SerializeField] private Vector3 _fireOriginOffset = Vector3.zero;
[SerializeField] private float _hitMarkerSize = 0.2f;
[SerializeField] private float _hitMarkerYOffset = 1.2f;
[SerializeField] private float _hitMarkerDuration = 0.15f;
[SerializeField] private Color _hitMarkerColor = new(1f, 0f, 0f, 0.95f);
private IWeaponAttackEffect _attackEffect;
#endregion
public override ImpactData GetImpactData()
{
return new ImpactData(_weaponData.OwnerCamp, _weaponData.Attack, AttackStat);
}
protected override void BuildStates()
{
RegisterState(new IdleState());
RegisterState(new CheckOutRangeState());
RegisterState(new CheckInRangeState());
RegisterState(new AttackState());
}
protected override void Attack()
{
FaceTargetImmediately();
Vector3 fireOrigin = CachedTransform.TransformPoint(_fireOriginOffset);
Vector3 fireDirection = CachedTransform.forward;
float maxDistance = Mathf.Max(0.1f, _weaponData.AttackRange);
if (Physics.Raycast(fireOrigin, fireDirection, out RaycastHit hit, maxDistance, _hitMask,
QueryTriggerInteraction.Collide))
{
TargetableObject targetable = hit.collider.GetComponentInParent<TargetableObject>();
if (targetable != null && targetable.Available && !targetable.IsDead)
{
_attackEffect?.Play(this, hit.point, targetable, 0f);
_isAttacking = true;
AIUtility.PerformCollision(targetable, this);
_isAttacking = false;
}
}
}
protected override void Check()
{
_target = SelectTarget(_sqrRange);
}
private void RotateToTarget(float elapseSeconds)
{
if (_target == null || !_target.Available) return;
Vector3 directionToTarget = (_target.CachedTransform.position - CachedTransform.position).normalized;
Quaternion targetRotation = Quaternion.LookRotation(directionToTarget, Vector3.up);
CachedTransform.rotation = Quaternion.Slerp(CachedTransform.rotation, targetRotation,
_rotateSpeed * elapseSeconds);
}
private void RotateToOrigin(float elapseSeconds)
{
CachedTransform.rotation = Quaternion.Slerp(CachedTransform.rotation, _cachedRotation,
_rotateSpeed * elapseSeconds);
}
private void FaceTargetImmediately()
{
if (_target == null || !_target.Available) return;
Vector3 directionToTarget = _target.CachedTransform.position - CachedTransform.position;
if (directionToTarget.sqrMagnitude <= Mathf.Epsilon) return;
CachedTransform.rotation = Quaternion.LookRotation(directionToTarget.normalized, Vector3.up);
}
#region Lifecycle
protected override bool OnWeaponShow(object userData)
{
_weaponData = RequireWeaponData<WeaponHandgunData>(userData);
if (_weaponData == null) return false;
WeaponData = _weaponData;
_currAttackTimer = 0f;
_sqrRange = _weaponData.AttackRange * _weaponData.AttackRange;
_cachedRotation = CachedTransform.rotation;
_attackEffect = new HandgunHitMarkerAttackEffect(_hitMarkerSize, _hitMarkerYOffset, _hitMarkerDuration,
_hitMarkerColor);
if (_weaponData.OwnerCamp == CampType.Player)
{
gameObject.layer = LayerMask.NameToLayer("PlayerWeapon");
_hitMask = LayerMask.GetMask("Enemy");
}
else if (_weaponData.OwnerCamp == CampType.Enemy)
{
gameObject.layer = LayerMask.NameToLayer("EnemyWeapon");
_hitMask = LayerMask.GetMask("Player");
}
return true;
}
protected override void OnWeaponHide(object userData)
{
_attackEffect = null;
}
protected override void OnWeaponAttach(EntityLogic parentEntity, Transform parentTransform, object userData)
{
BindAttackStatFromOwner(parentEntity);
}
protected override void OnWeaponDetach(EntityLogic parentEntity, object userData)
{
ReleaseAttackStatSubscription();
}
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 44ae1bdd1d43e274980743df21658730
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,333 +0,0 @@
using System;
using System.Collections.Generic;
using Components;
using CustomComponent;
using Definition.DataStruct;
using Definition.Enum;
using DG.Tweening;
using Entity.EntityData;
using Game.Utility;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace Entity
{
public class WeaponKnife : WeaponBase
{
private WeaponKnifeData _weaponData;
private float _currAttackTimer = 0f;
private EnemyManagerComponent _enemy;
private EntityBase _target;
private float _sqrRange;
private Collider _collider;
private Rigidbody _rigidbody;
private Quaternion _cachedRotation;
[SerializeField] private float _rotateSpeed = 2f;
private Dictionary<WeaponStateType, WeaponStateBase> _weaponStates;
private StatProperty _attackStat = new();
private Action<StatModifier, bool> _attackStatCallback;
[SerializeField] private float attackDuration = 0.15f; // 戳刺持续时间
[SerializeField] private float returnDuration = 0.2f; // 回归持续时间
public override ImpactData GetImpactData()
{
return new ImpactData(_weaponData.OwnerCamp, _weaponData.Attack, _attackStat);
}
private void InitStates()
{
_weaponStates = new Dictionary<WeaponStateType, WeaponStateBase>
{
{ WeaponStateType.Idle, new IdleState() },
{ WeaponStateType.Check_InRange, new CheckInRangeState() },
{ WeaponStateType.Check_OutRange, new CheckOutRangeState() },
{ WeaponStateType.Attack, new AttackState() }
};
foreach (var state in _weaponStates.Values)
{
state.OnInit(this);
}
}
#region FSM Methods
protected override void Attack()
{
Vector3 targetPos = CachedTransform.position + CachedTransform.forward * _weaponData.AttackRange;
_isAttacking = true;
_collider.enabled = true;
_rigidbody.detectCollisions = true;
Transform parentTransform = CachedTransform.parent;
CachedTransform.SetParent(null);
var sequence = DOTween.Sequence();
sequence.Append(CachedTransform.DOMove(targetPos, attackDuration).SetEase(Ease.OutQuad));
sequence.AppendCallback(() =>
{
_collider.enabled = false;
_rigidbody.detectCollisions = false;
CachedTransform.SetParent(parentTransform);
});
sequence.Append(CachedTransform.DOLocalMove(Vector3.zero, returnDuration / 2f).SetEase(Ease.InQuad));
sequence.AppendCallback(() => { _isAttacking = false; });
}
protected override void Check()
{
_target = null;
float minSqrMagnitude = float.MaxValue;
var enemies = _enemy.Enemies;
foreach (var enemy in enemies)
{
if (enemy == null || !enemy.Available) continue;
float sqrMagnitude = AIUtility.GetSqrMagnitude(this, enemy);
if (minSqrMagnitude < sqrMagnitude) continue;
_target = enemy;
minSqrMagnitude = sqrMagnitude;
}
}
private void RotateToTarget(float elapseSeconds)
{
if (_target == null || !_target.Available) return;
Vector3 directionToTarget = (_target.CachedTransform.position - transform.position).normalized;
Quaternion targetRotation = Quaternion.LookRotation(directionToTarget, Vector3.up);
var quaternion = Quaternion.Slerp(CachedTransform.rotation, targetRotation, _rotateSpeed * elapseSeconds);
CachedTransform.rotation = quaternion;
}
private void RotateToOrigin(float elapseSeconds)
{
var quaternion = Quaternion.Slerp(CachedTransform.rotation, _cachedRotation, _rotateSpeed * elapseSeconds);
CachedTransform.rotation = quaternion;
}
protected override void TransitionTo(WeaponStateType state)
{
_currentState?.OnLeave();
_currentState = _weaponStates[state];
_currentState.OnEnter();
}
#endregion
#region FSM
protected override void OnShow(object userData)
{
base.OnShow(userData);
_weaponData = userData as WeaponKnifeData;
if (_weaponData == null)
{
Log.Error("WeaponKnife data is invalid.");
return;
}
WeaponData = _weaponData;
_collider = GetComponent<Collider>();
_rigidbody = GetComponent<Rigidbody>();
_currAttackTimer = 0;
_enemy = GameEntry.EnemyManager;
_sqrRange = _weaponData.AttackRange * _weaponData.AttackRange;
_cachedRotation = transform.rotation;
_collider.enabled = false;
_rigidbody.detectCollisions = false;
if (_weaponData.OwnerCamp == CampType.Player)
this.gameObject.layer = LayerMask.NameToLayer("PlayerWeapon");
if (_weaponData.OwnerCamp == CampType.Enemy)
this.gameObject.layer = LayerMask.NameToLayer("EnemyWeapon");
InitStates();
TransitionTo(WeaponStateType.Idle);
}
protected override void OnAttachTo(EntityLogic parentEntity, Transform parentTransform, object userData)
{
base.OnAttachTo(parentEntity, parentTransform, userData);
if (parentEntity is Player player)
{
var statComponent = player.GetComponent<StatComponent>();
if (statComponent != null)
{
this._attackStat = statComponent.GetStat(StatType.Attack);
this._attackStatCallback = (modifier, b) => statComponent.UpdateStat(_attackStat, modifier, b);
statComponent.Subscribe(StatType.Attack, this._attackStatCallback);
}
}
}
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(elapseSeconds, realElapseSeconds);
if (!_isEnabled) return;
Log.Debug($"current state: {_currentState}");
_currentState?.OnUpdate(elapseSeconds, realElapseSeconds);
}
protected override void OnDetachFrom(EntityLogic parentEntity, object userData)
{
base.OnDetachFrom(parentEntity, userData);
if (parentEntity is Player player)
{
var statComponent = player.GetComponent<StatComponent>();
if (statComponent != null)
{
statComponent.Unsubscribe(StatType.Attack, this._attackStatCallback);
}
}
this._attackStat = null;
this._attackStatCallback = null;
}
#endregion
private class IdleState : WeaponStateBase
{
private WeaponKnife _weapon;
public override WeaponStateType State => WeaponStateType.Idle;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponKnife;
public override void OnEnter()
{
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.Check();
_weapon.RotateToOrigin(elapseSeconds);
if (_weapon._target != null && _weapon._target.Available)
{
_weapon.TransitionTo(WeaponStateType.Check_OutRange);
}
}
public override void OnLeave()
{
}
}
private class CheckOutRangeState : WeaponStateBase
{
private WeaponKnife _weapon;
public override WeaponStateType State => WeaponStateType.Check_OutRange;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponKnife;
public override void OnEnter()
{
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.Check();
_weapon.RotateToTarget(elapseSeconds);
if (_weapon._target == null || !_weapon._target.Available)
{
_weapon.TransitionTo(WeaponStateType.Idle);
return;
}
if (AIUtility.GetSqrMagnitude(_weapon._target, _weapon) < _weapon._sqrRange)
{
_weapon.TransitionTo(WeaponStateType.Check_InRange);
return;
}
}
public override void OnLeave()
{
}
}
private class CheckInRangeState : WeaponStateBase
{
private WeaponKnife _weapon;
public override WeaponStateType State => WeaponStateType.Check_InRange;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponKnife;
public override void OnEnter()
{
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.Check();
_weapon.RotateToTarget(elapseSeconds);
_weapon._currAttackTimer += elapseSeconds;
if (_weapon._target == null || !_weapon._target.Available)
{
_weapon.TransitionTo(WeaponStateType.Idle);
return;
}
if (AIUtility.GetSqrMagnitude(_weapon._target, _weapon) >= _weapon._sqrRange)
{
_weapon.TransitionTo(WeaponStateType.Check_OutRange);
return;
}
if (_weapon._currAttackTimer >= _weapon._weaponData.Cooldown)
{
_weapon.TransitionTo(WeaponStateType.Attack);
return;
}
}
public override void OnLeave()
{
_weapon._currAttackTimer = 0;
}
}
private class AttackState : WeaponStateBase
{
private WeaponKnife _weapon;
public override WeaponStateType State => WeaponStateType.Attack;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponKnife;
public override void OnEnter()
{
_weapon.Attack();
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
if (!_weapon._isAttacking)
{
_weapon.TransitionTo(WeaponStateType.Check_InRange);
}
}
public override void OnLeave()
{
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f89893f21b3046b7a93a368cd688451e
timeCreated: 1771578597

View File

@ -0,0 +1,31 @@
namespace Entity.Weapon
{
public partial class WeaponKnife
{
private class AttackState : WeaponStateBase
{
private WeaponKnife _weapon;
public override WeaponStateType State => WeaponStateType.Attack;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponKnife;
public override void OnEnter()
{
_weapon._currAttackTimer = 0f;
_weapon.Attack();
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
if (!_weapon._isAttacking)
{
_weapon.TransitionTo(WeaponStateType.Check_InRange);
}
}
public override void OnLeave()
{
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 417516ffe5574e0c998105fcaa073de7
timeCreated: 1771578955

View File

@ -0,0 +1,49 @@
namespace Entity.Weapon
{
public partial class WeaponKnife
{
private class CheckInRangeState : WeaponStateBase
{
private WeaponKnife _weapon;
public override WeaponStateType State => WeaponStateType.Check_InRange;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponKnife;
public override void OnEnter()
{
if (_weapon._currAttackTimer >= _weapon._weaponData.Cooldown)
{
_weapon.TransitionTo(WeaponStateType.Attack);
}
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.Check();
_weapon.RotateToTarget(elapseSeconds);
_weapon._currAttackTimer += elapseSeconds;
if (_weapon._target == null || !_weapon._target.Available)
{
_weapon.TransitionTo(WeaponStateType.Idle);
return;
}
if (!_weapon.IsInRange(_weapon._target, _weapon._sqrRange))
{
_weapon.TransitionTo(WeaponStateType.Check_OutRange);
return;
}
if (_weapon._currAttackTimer >= _weapon._weaponData.Cooldown)
{
_weapon.TransitionTo(WeaponStateType.Attack);
}
}
public override void OnLeave()
{
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 59644093364644768c26261b24bdb820
timeCreated: 1771578916

View File

@ -0,0 +1,39 @@
namespace Entity.Weapon
{
public partial class WeaponKnife
{
private class CheckOutRangeState : WeaponStateBase
{
private WeaponKnife _weapon;
public override WeaponStateType State => WeaponStateType.Check_OutRange;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponKnife;
public override void OnEnter()
{
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.Check();
_weapon.RotateToTarget(elapseSeconds);
_weapon._currAttackTimer += elapseSeconds;
if (_weapon._target == null || !_weapon._target.Available)
{
_weapon.TransitionTo(WeaponStateType.Idle);
return;
}
if (_weapon.IsInRange(_weapon._target, _weapon._sqrRange))
{
_weapon.TransitionTo(WeaponStateType.Check_InRange);
}
}
public override void OnLeave()
{
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d9e1a63869694e72a360afe29cc94cfb
timeCreated: 1771578994

View File

@ -0,0 +1,33 @@
namespace Entity.Weapon
{
public partial class WeaponKnife
{
public class IdleState : WeaponStateBase
{
private WeaponKnife _weapon;
public override WeaponStateType State => WeaponStateType.Idle;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponKnife;
public override void OnEnter()
{
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.Check();
_weapon.RotateToOrigin(elapseSeconds);
_weapon._currAttackTimer += elapseSeconds;
if (_weapon._target != null && _weapon._target.Available)
{
_weapon.TransitionTo(WeaponStateType.Check_OutRange);
}
}
public override void OnLeave()
{
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b4d66ec44b064640aa95c6aa71ff3846
timeCreated: 1771578662

View File

@ -0,0 +1,227 @@
using System.Collections.Generic;
using CustomUtility;
using Definition.DataStruct;
using Definition.Enum;
using DG.Tweening;
using Entity.EntityData;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace Entity.Weapon
{
public partial class WeaponKnife : WeaponBase
{
private const string HitRadiusParamKey = "HitRadius";
private WeaponKnifeData _weaponData;
private Quaternion _cachedRotation;
[SerializeField] private float _rotateSpeed = 4f;
private Sequence _attackSequence;
private Transform _attackParent;
private Vector3 _attackCenter;
[SerializeField] private float attackDuration = 0.15f;
[SerializeField] private float returnDuration = 0.2f;
[SerializeField] private LayerMask _hitMask = ~0;
[SerializeField] private int _maxHitColliders = 32;
private IWeaponAttackEffect _attackEffect;
private Collider[] _hitResults;
private readonly HashSet<int> _hitEntityIds = new();
private float _hitRadius;
private float _hitRadiusSqr;
public override ImpactData GetImpactData()
{
return new ImpactData(_weaponData.OwnerCamp, _weaponData.Attack, AttackStat);
}
protected override void BuildStates()
{
RegisterState(new IdleState());
RegisterState(new CheckInRangeState());
RegisterState(new CheckOutRangeState());
RegisterState(new AttackState());
}
protected override void Attack()
{
StopAttackTween(false);
FaceTargetImmediately();
_isAttacking = true;
Transform parentTransform = CachedTransform.parent;
_attackParent = parentTransform;
_attackCenter = _target != null ? _target.CachedTransform.position : CachedTransform.position;
CachedTransform.SetParent(null);
Vector3 targetPos = CachedTransform.position + CachedTransform.forward * _weaponData.AttackRange;
_attackSequence = DOTween.Sequence();
_attackSequence.Append(CachedTransform.DOMove(targetPos, attackDuration).SetEase(Ease.OutQuad));
_attackSequence.AppendCallback(() =>
{
_attackEffect?.Play(this, _attackCenter, null, _hitRadius);
ApplyGroundAreaDamage();
if (_attackParent != null)
{
CachedTransform.SetParent(_attackParent);
}
});
_attackSequence.Append(
CachedTransform.DOLocalMove(Vector3.zero, returnDuration * 0.5f).SetEase(Ease.InQuad));
_attackSequence.AppendCallback(() =>
{
_isAttacking = false;
_attackSequence = null;
_attackParent = null;
});
}
protected override void Check()
{
_target = SelectTarget(_sqrRange);
}
private void RotateToTarget(float elapseSeconds)
{
if (_target == null || !_target.Available) return;
Vector3 directionToTarget = _target.CachedTransform.position - CachedTransform.position;
if (directionToTarget.sqrMagnitude <= Mathf.Epsilon) return;
Quaternion targetRotation = Quaternion.LookRotation(directionToTarget.normalized, Vector3.up);
CachedTransform.rotation =
Quaternion.Slerp(CachedTransform.rotation, targetRotation, _rotateSpeed * elapseSeconds);
}
public void RotateToOrigin(float elapseSeconds)
{
CachedTransform.rotation =
Quaternion.Slerp(CachedTransform.rotation, _cachedRotation, _rotateSpeed * elapseSeconds);
}
private void FaceTargetImmediately()
{
if (_target == null || !_target.Available) return;
Vector3 directionToTarget = _target.CachedTransform.position - CachedTransform.position;
if (directionToTarget.sqrMagnitude <= Mathf.Epsilon) return;
CachedTransform.rotation = Quaternion.LookRotation(directionToTarget.normalized, Vector3.up);
}
private void ApplyGroundAreaDamage()
{
if (_hitRadius <= 0f || _hitResults == null || _hitResults.Length == 0) return;
int hitCount = Physics.OverlapSphereNonAlloc(_attackCenter, _hitRadius, _hitResults, _hitMask,
QueryTriggerInteraction.Collide);
_hitEntityIds.Clear();
for (int i = 0; i < hitCount; i++)
{
Collider collider = _hitResults[i];
if (collider == null) continue;
TargetableObject targetable = collider.GetComponentInParent<TargetableObject>();
if (targetable == null || !targetable.Available || targetable.IsDead) continue;
if (!_hitEntityIds.Add(targetable.Id)) continue;
Vector3 delta = targetable.CachedTransform.position - _attackCenter;
delta.y = 0f;
if (delta.sqrMagnitude > _hitRadiusSqr) continue;
AIUtility.PerformCollision(targetable, this);
}
}
protected override bool OnWeaponShow(object userData)
{
_weaponData = RequireWeaponData<WeaponKnifeData>(userData);
if (_weaponData == null) return false;
WeaponData = _weaponData;
_currAttackTimer = 0f;
_sqrRange = _weaponData.AttackRange * _weaponData.AttackRange;
_cachedRotation = CachedTransform.rotation;
string hitRadiusRaw = _weaponData.GetParamsString(HitRadiusParamKey);
if (!float.TryParse(hitRadiusRaw, out _hitRadius))
{
_hitRadius = _weaponData.AttackRange;
}
_hitRadius = Mathf.Max(0.1f, _hitRadius);
_hitRadiusSqr = _hitRadius * _hitRadius;
_attackEffect = new KnifeRangeAttackEffect();
int colliderCapacity = Mathf.Max(1, _maxHitColliders);
if (_hitResults == null || _hitResults.Length != colliderCapacity)
{
_hitResults = new Collider[colliderCapacity];
}
if (_weaponData.OwnerCamp == CampType.Player)
{
gameObject.layer = LayerMask.NameToLayer("PlayerWeapon");
_hitMask = LayerMask.GetMask("Enemy");
}
else if (_weaponData.OwnerCamp == CampType.Enemy)
{
gameObject.layer = LayerMask.NameToLayer("EnemyWeapon");
_hitMask = LayerMask.GetMask("Player");
}
return true;
}
protected override void OnWeaponHide(object userData)
{
StopAttackTween(true);
_attackEffect = null;
}
protected override void OnWeaponAttach(EntityLogic parentEntity, Transform parentTransform, object userData)
{
BindAttackStatFromOwner(parentEntity);
}
protected override void OnWeaponDetach(EntityLogic parentEntity, object userData)
{
StopAttackTween(true);
ReleaseAttackStatSubscription();
}
protected override void OnEnabledChanged(bool enabled)
{
if (!enabled)
{
StopAttackTween(true);
}
}
private void StopAttackTween(bool resetTransform)
{
if (_attackSequence != null)
{
_attackSequence.Kill();
_attackSequence = null;
}
_isAttacking = false;
if (resetTransform && _attackParent != null)
{
CachedTransform.SetParent(_attackParent);
CachedTransform.localPosition = Vector3.zero;
}
_attackParent = null;
_hitEntityIds.Clear();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9289c110a12b456aabb374427caa9e1a
timeCreated: 1771579177

Some files were not shown because too many files have changed in this diff Show More