- 补充游戏核心逻辑
- 完成 TodOList P0
- 添加游戏内控制台便于调试
- 增加敌人互斥机制提高运算负载
- 优化互斥算法将游戏性能基准线拉到 1k 敌人
This commit is contained in:
parent
b4bc8b0445
commit
3742110e9a
|
|
@ -82,5 +82,8 @@ crashlytics-build.properties
|
|||
/UI参考
|
||||
/AGENTS.md
|
||||
/bin
|
||||
/docs/screenshot
|
||||
*.xmind
|
||||
/数据表/__pycache__/
|
||||
|
||||
~$*.xlsx
|
||||
|
|
|
|||
|
|
@ -88,13 +88,7 @@ namespace UnityGameFramework.Runtime
|
|||
/// <summary>
|
||||
/// 获取已缓存的 Transform。
|
||||
/// </summary>
|
||||
public Transform CachedTransform
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_CachedTransform;
|
||||
}
|
||||
}
|
||||
public Transform CachedTransform => m_CachedTransform;
|
||||
|
||||
/// <summary>
|
||||
/// 实体初始化。
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<UnityGameFramework>
|
||||
<ResourceBuilder>
|
||||
<Settings>
|
||||
<InternalResourceVersion>1</InternalResourceVersion>
|
||||
<InternalResourceVersion>3</InternalResourceVersion>
|
||||
<Platforms>33</Platforms>
|
||||
<AssetBundleCompression>1</AssetBundleCompression>
|
||||
<CompressionHelperTypeName>UnityGameFramework.Runtime.DefaultCompressionHelper</CompressionHelperTypeName>
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
<Resource Name="URPAssets" LoadType="0" Packed="True" />
|
||||
</Resources>
|
||||
<Assets>
|
||||
<Asset Guid="0080b46050fc460cb6d6c7fc8d4e8c27" ResourceName="DataTables" />
|
||||
<Asset Guid="0179316b5fc7c2946a67c5877c02fc30" ResourceName="UI/UISprites/Common" />
|
||||
<Asset Guid="04d7dde7615d71b4db1a0c8d67a62e95" ResourceName="UI/UIForms" />
|
||||
<Asset Guid="04dbc0581071c254ea6564b2ff06ff9b" ResourceName="Textures" />
|
||||
|
|
@ -64,10 +65,11 @@
|
|||
<Asset Guid="28988b1366cba3e4b947bb60d6b118f9" ResourceName="DataTables" />
|
||||
<Asset Guid="28c42f88cfe56e84e95c0d7545db8c1b" ResourceName="Materials" />
|
||||
<Asset Guid="2cb5eef4d7d7bf6459dd13a3f8d90246" ResourceName="Textures" />
|
||||
<Asset Guid="2d1b2cc11a8e99f42b34265765fc6d86" ResourceName="Entities" />
|
||||
<Asset Guid="2ed59bf0745586248aaa89cf7d3305a7" ResourceName="Entities" />
|
||||
<Asset Guid="308577c72eb4cd14ca676aeee62b0ea5" ResourceName="UI/UIItems" />
|
||||
<Asset Guid="312264fc6c05ca04499685cc989e533d" ResourceName="UI/UISprites/Icons" />
|
||||
<Asset Guid="315e8ed2db27c254cb3366ff0793cd90" ResourceName="DataTables" />
|
||||
<Asset Guid="3455fe69f7ddc4604b95bf9b0a1e88d7" ResourceName="Entities" />
|
||||
<Asset Guid="352da872791696c48af3b21132e3e3c3" ResourceName="DataTables" />
|
||||
<Asset Guid="372a8b1e52bedc64b9207b12d167afaa" ResourceName="DataTables" />
|
||||
<Asset Guid="3aa539b1e46111d4299a83c73ebe762c" ResourceName="UI/UIForms" />
|
||||
|
|
@ -91,6 +93,7 @@
|
|||
<Asset Guid="583ff7026dac91849b7c7b2468ba456b" ResourceName="Materials" />
|
||||
<Asset Guid="5b5a6a737c460eb4abc105d6583d405e" ResourceName="Fonts" />
|
||||
<Asset Guid="5dcd89912e222bf4c87f76db4044bc5e" ResourceName="Localization/Dictionaries" ResourceVariant="ko-kr" />
|
||||
<Asset Guid="5ebb46af6f16ae94e87f64a7dc0a49cb" ResourceName="Entities" />
|
||||
<Asset Guid="62af9e5c8f39cfa49af9e10ccf42f1da" ResourceName="UI/UISprites/Common" />
|
||||
<Asset Guid="638ff8ae4a0d15047839cd265d3bc296" ResourceName="Music/Background" />
|
||||
<Asset Guid="63fe6ff9ab9e1433f8db4ebd940f2442" ResourceName="Materials" />
|
||||
|
|
@ -119,6 +122,7 @@
|
|||
<Asset Guid="7f5aee8da226edf4991598327cb32ce0" ResourceName="UI/UISprites/Logos" />
|
||||
<Asset Guid="7fd11dc5d29076d469d414dec2818f11" ResourceName="Configs" />
|
||||
<Asset Guid="836be25be3e1e8c41ae5545bc8a9a4d7" ResourceName="Textures" />
|
||||
<Asset Guid="83e5fce2d1e5b3e4ab6551de03cc9c22" ResourceName="Materials" />
|
||||
<Asset Guid="8940b037a9b441c4cbd3d2b446838424" ResourceName="Materials" />
|
||||
<Asset Guid="8e27380ee68aa4a219b4db9018e7da31" ResourceName="Materials" />
|
||||
<Asset Guid="91b565625868b59428114c4a5b945e80" ResourceName="UI/UISprites/SelectRoleFormRT" />
|
||||
|
|
@ -136,7 +140,6 @@
|
|||
<Asset Guid="a23eef5e20ff8cb46adf33491fc443fb" ResourceName="Materials" />
|
||||
<Asset Guid="a6560a915ef98420e9faacc1c7438823" ResourceName="URPAssets" />
|
||||
<Asset Guid="a6ac41115088b5946bc544429260c997" ResourceName="Entities" />
|
||||
<Asset Guid="a6d58f54ce5934781962a4f887e834f3" ResourceName="Entities" />
|
||||
<Asset Guid="a71f8bb1b1b2c51438e2bafc884cb02c" ResourceName="DataTables" />
|
||||
<Asset Guid="a76ff56683a2bc2479ebd63dcdd658b0" ResourceName="UI/UIItems" />
|
||||
<Asset Guid="a7b030cffa2dc44478c14e49a22771c2" ResourceName="Materials" />
|
||||
|
|
@ -157,6 +160,7 @@
|
|||
<Asset Guid="bf75b984df8a84987bcf3a8bf6e2862d" ResourceName="Sounds" />
|
||||
<Asset Guid="c40be3174f62c4acf8c1216858c64956" ResourceName="URPAssets" />
|
||||
<Asset Guid="c49cffd4fc1dfb549b2b30448a0becda" ResourceName="UI/UISprites/Icons" />
|
||||
<Asset Guid="c4f37184fcb9306428d7d002f7dca96d" ResourceName="Materials" />
|
||||
<Asset Guid="c547624e174de984882f0a14b4bb32e1" ResourceName="Materials" />
|
||||
<Asset Guid="c58c9afddbd36d14d837fa218d772996" ResourceName="Materials" />
|
||||
<Asset Guid="c7d1e11dd37634b48a9dd4012b8e4306" ResourceName="UI/UISounds" />
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# 敌人基础属性表
|
||||
# Id MaxHealth HpAddPerLevel Speed CoinDrop ExpDrop DropPercent
|
||||
# int int int float int int float
|
||||
# 实体编号 策划备注 最大生命 每关卡增加生命 移动速度 金币掉落 经验掉落 掉落概率
|
||||
101 近战敌人 50 50 3 5 1 0.3
|
||||
# Id EntityTypeId MaxHealth HpAddPerLevel Speed CoinDrop ExpDrop DropPercent
|
||||
# int int int int float int int float
|
||||
# 敌人编号 策划备注 敌人实体编号 最大生命 每关卡增加生命 移动速度 金币掉落 经验掉落 掉落概率
|
||||
1 近战敌人 101 50 50 3 5 1 0.3
|
||||
|
|
|
|||
|
|
@ -7,5 +7,7 @@
|
|||
102 远程敌人 RemoteEnemy
|
||||
11 跟随相机 FollowCamera
|
||||
201 武器小刀 WeaponKnife
|
||||
202 武器手枪 WeaponHandgun
|
||||
203 武器斧头 WeaponSlash
|
||||
10001 金币实体 CoinEntity
|
||||
10002 经验实体 ExpEntity
|
||||
|
|
|
|||
|
|
@ -3,7 +3,25 @@
|
|||
# int GoodsType int
|
||||
# 商品编号 策划备注 商品类型 商品对应物品Id
|
||||
101 道具:药 Prop 101
|
||||
102 小刀 Weapon 201
|
||||
102 小刀 Weapon 1
|
||||
103 Prop 102
|
||||
104 Prop 103
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
# 关卡配置表
|
||||
# Id EntityIds EntityCounts Interval Duration
|
||||
# Id EnemyTypes EntityCounts Interval Duration
|
||||
# int int[] int[] float[] int
|
||||
# 关卡号 策划备注 实体编号 每次出怪数量 每次出怪间隔 关卡时间
|
||||
1 第一关 [101] [5] [2] 60
|
||||
2 第二关 [101] [10] [3] 60
|
||||
3 第三关 [101] [10] [3] 60
|
||||
4 第四关 [101] [10] [3] 60
|
||||
5 第五关 [101] [10] [3] 60
|
||||
6 第六关 [101] [10] [3] 60
|
||||
7 第七关 [101] [10] [3] 60
|
||||
8 第八关 [101] [10] [3] 60
|
||||
9 第九关 [101] [10] [3] 60
|
||||
10 第十关 [101] [10] [3] 60
|
||||
# 关卡号 策划备注 敌人类型 每次出怪数量 每次出怪间隔 关卡时间
|
||||
1 第一关 [1] [5] [2] 60
|
||||
2 第二关 [1] [10] [3] 60
|
||||
3 第三关 [1] [10] [3] 60
|
||||
4 第四关 [1] [10] [3] 60
|
||||
5 第五关 [1] [10] [3] 60
|
||||
6 第六关 [1] [10] [3] 60
|
||||
7 第七关 [1] [10] [3] 60
|
||||
8 第八关 [1] [10] [3] 60
|
||||
9 第九关 [1] [10] [3] 60
|
||||
10 第十关 [1] [10] [3] 60
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# 武器表
|
||||
# Id Title IconAssetName Rarity Price PriceRandomPercent Attack Cooldown AttackRange AttackSoundId Pramas Modifiers
|
||||
# int string string RarityType int float int float float int string[] StatModifier[]
|
||||
# 武器编号 策划备注 武器名 图标资源名 道具品质 武器价格 价格浮动 伤害 冷却 范围 攻击音效编号 额外参数 额外属性
|
||||
201 玩家武器 小刀 Almighty_Icon White 120 0.05 100 1.5 5 10000 [] []
|
||||
202 手枪 Almighty_Icon White 130 0.05 120 1 15 10000 [] []
|
||||
# Id EntityTypeId Title IconAssetName Rarity Price PriceRandomPercent Attack Cooldown AttackRange AttackSoundId Pramas Modifiers
|
||||
# int int string string RarityType int float int float float int string[] StatModifier[]
|
||||
# 武器编号 策划备注 武器实体编号 武器名 图标资源名 道具品质 武器价格 价格浮动 伤害 冷却 范围 攻击音效编号 额外参数 额外属性
|
||||
1 玩家武器 201 小刀 Almighty_Icon White 120 0.05 100 1.5 5 10000 [hitRadius:2] []
|
||||
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
|
|
@ -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}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2d1b2cc11a8e99f42b34265765fc6d86
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -25,7 +25,7 @@ Transform:
|
|||
m_GameObject: {fileID: 50638171685441296}
|
||||
serializedVersion: 2
|
||||
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_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
|
|
@ -56,7 +56,7 @@ Transform:
|
|||
m_GameObject: {fileID: 526023732862709228}
|
||||
serializedVersion: 2
|
||||
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_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
|
|
@ -87,7 +87,7 @@ Transform:
|
|||
m_GameObject: {fileID: 886755583121495434}
|
||||
serializedVersion: 2
|
||||
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_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
|
|
@ -118,7 +118,7 @@ Transform:
|
|||
m_GameObject: {fileID: 5243258899397488202}
|
||||
serializedVersion: 2
|
||||
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_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
|
|
@ -150,13 +150,13 @@ Transform:
|
|||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5383497626468778460}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0.42261827, y: 0, z: 0, w: 0.9063079}
|
||||
m_LocalPosition: {x: 0, y: 10, z: -8}
|
||||
m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068}
|
||||
m_LocalPosition: {x: 0, y: 15, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 9112716898534404901}
|
||||
m_LocalEulerAnglesHint: {x: 50, y: 0, z: 0}
|
||||
m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0}
|
||||
--- !u!20 &4064848608618185461
|
||||
Camera:
|
||||
m_ObjectHideFlags: 0
|
||||
|
|
@ -189,8 +189,8 @@ Camera:
|
|||
width: 1
|
||||
height: 1
|
||||
near clip plane: 0.3
|
||||
far clip plane: 1000
|
||||
field of view: 60
|
||||
far clip plane: 100
|
||||
field of view: 80
|
||||
orthographic: 0
|
||||
orthographic size: 5
|
||||
m_Depth: 0
|
||||
|
|
@ -277,7 +277,7 @@ Transform:
|
|||
m_GameObject: {fileID: 6372140121958224629}
|
||||
serializedVersion: 2
|
||||
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_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
|
|
@ -308,7 +308,7 @@ Transform:
|
|||
m_GameObject: {fileID: 6961884676133552519}
|
||||
serializedVersion: 2
|
||||
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_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
|
|
@ -523,6 +523,9 @@ MonoBehaviour:
|
|||
_isMoving: 0
|
||||
_direction: {x: 0, y: 0, z: 0}
|
||||
_cachedTransform: {fileID: 0}
|
||||
_avoidEnemyOverlap: 0
|
||||
_enemyBodyRadius: 0.45
|
||||
_separationIterations: 2
|
||||
_speedBase: 0
|
||||
--- !u!114 &582203051508165858
|
||||
MonoBehaviour:
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3455fe69f7ddc4604b95bf9b0a1e88d7
|
||||
timeCreated: 1528026126
|
||||
licenseType: Pro
|
||||
NativeFormatImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2ed59bf0745586248aaa89cf7d3305a7
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -27,8 +27,8 @@ Transform:
|
|||
m_GameObject: {fileID: 1813281596125460147}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0.5}
|
||||
m_LocalScale: {x: 0.5, y: 0.5, z: 0.5}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0.25}
|
||||
m_LocalScale: {x: 0.2, y: 0.2, z: 0.2}
|
||||
m_ConstrainProportionsScale: 1
|
||||
m_Children: []
|
||||
m_Father: {fileID: 1074967493716089666}
|
||||
|
|
@ -111,8 +111,8 @@ Transform:
|
|||
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_LocalScale: {x: 0.5, y: 0.5, z: 0.5}
|
||||
m_ConstrainProportionsScale: 1
|
||||
m_Children: []
|
||||
m_Father: {fileID: 1074967493716089666}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
|
|
@ -144,7 +144,7 @@ MeshRenderer:
|
|||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
|
||||
- {fileID: 2100000, guid: c4f37184fcb9306428d7d002f7dca96d, type: 2}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
|
|
@ -175,8 +175,6 @@ GameObject:
|
|||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1074967493716089666}
|
||||
- component: {fileID: 8372979338474897700}
|
||||
- component: {fileID: 7352622698489128642}
|
||||
m_Layer: 11
|
||||
m_Name: WeaponKnife
|
||||
m_TagString: Untagged
|
||||
|
|
@ -201,51 +199,3 @@ Transform:
|
|||
- {fileID: 6411073038556792567}
|
||||
m_Father: {fileID: 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}
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5ebb46af6f16ae94e87f64a7dc0a49cb
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Entity;
|
||||
using Entity.Weapon;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Components
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using CustomEvent;
|
||||
using Definition.DataStruct;
|
||||
using Entity.Weapon;
|
||||
using Entity;
|
||||
using UnityEngine;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
using System;
|
||||
using CustomUtility;
|
||||
using Definition.DataStruct;
|
||||
using Definition.Enum;
|
||||
using Unity.Profiling;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
using CustomDebugger;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
|
|
@ -11,6 +13,9 @@ namespace Components
|
|||
[SerializeField] private bool _isMoving;
|
||||
[SerializeField] private Vector3 _direction;
|
||||
[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;
|
||||
[SerializeField] private float _speedBase;
|
||||
|
|
@ -20,11 +25,15 @@ namespace Components
|
|||
private StatProperty _movementStat;
|
||||
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;
|
||||
_cachedTransform = target;
|
||||
_direction = Vector3.forward;
|
||||
_avoidEnemyOverlap = avoidEnemyOverlap;
|
||||
_enemyBodyRadius = Mathf.Max(0.01f, enemyBodyRadius);
|
||||
_separationIterations = Mathf.Max(1, separationIterations);
|
||||
|
||||
_statComponent = statComponent;
|
||||
if (_statComponent != null)
|
||||
|
|
@ -38,6 +47,8 @@ namespace Components
|
|||
{
|
||||
_movementStat = new StatProperty();
|
||||
}
|
||||
|
||||
RefreshEnemyRegistration();
|
||||
}
|
||||
|
||||
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||
|
|
@ -54,6 +65,9 @@ namespace Components
|
|||
_cachedTransform = null;
|
||||
_direction = Vector3.zero;
|
||||
_isMoving = false;
|
||||
_avoidEnemyOverlap = false;
|
||||
_enemyBodyRadius = 0.45f;
|
||||
_separationIterations = 2;
|
||||
|
||||
if (_statComponent != null)
|
||||
{
|
||||
|
|
@ -62,14 +76,44 @@ namespace Components
|
|||
}
|
||||
|
||||
_statComponent = null;
|
||||
|
||||
UnregisterEnemyMover();
|
||||
}
|
||||
|
||||
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 SetDirection(Vector3 direction) => _direction = direction;
|
||||
|
||||
private void RefreshEnemyRegistration()
|
||||
{
|
||||
UnregisterEnemyMover();
|
||||
if (!_avoidEnemyOverlap) return;
|
||||
EnemySeparationSolverProvider.Register(this, _cachedTransform, _enemyBodyRadius);
|
||||
}
|
||||
|
||||
private void UnregisterEnemyMover()
|
||||
{
|
||||
EnemySeparationSolverProvider.Unregister(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,8 @@ using System.Linq;
|
|||
using DataTable;
|
||||
using Definition.DataStruct;
|
||||
using Entity;
|
||||
using Game.Utility;
|
||||
using CustomUtility;
|
||||
using Procedure;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
|
|
@ -105,6 +106,7 @@ namespace CustomComponent
|
|||
{
|
||||
EnsurePropList(true);
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
ApplyFilter(_searchText);
|
||||
|
|
@ -135,12 +137,14 @@ namespace CustomComponent
|
|||
{
|
||||
AddSelectedBuffToPlayer(selectedProp, _addCount);
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private void DrawBattleSection()
|
||||
{
|
||||
GUILayout.Label("Battle Debug");
|
||||
ProcedureGame procedure = GameEntry.Procedure.CurrentProcedure as ProcedureGame;
|
||||
EnemyManagerComponent enemyManager = GameEntry.EnemyManager;
|
||||
Player player = FindPlayer();
|
||||
|
||||
|
|
@ -150,8 +154,15 @@ namespace CustomComponent
|
|||
return;
|
||||
}
|
||||
|
||||
if (procedure == null)
|
||||
{
|
||||
GUILayout.Label("ProcedureGame unavailable.");
|
||||
return;
|
||||
}
|
||||
|
||||
GUILayout.Label($"Spawn Rate: {enemyManager.SpawnRateScale:F2}");
|
||||
GUILayout.Label($"Battle Time: {enemyManager.ElapsedBattleTime:F1}s / {enemyManager.BattleDuration:F1}s");
|
||||
GUILayout.Label($"Enemy Count: {enemyManager.CurrentEnemyCount}");
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label("Rate", GUILayout.Width(52f));
|
||||
|
|
@ -165,16 +176,19 @@ namespace CustomComponent
|
|||
{
|
||||
enemyManager.SetSpawnRateScale(_spawnRateScaleInput);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("x0.5", GUILayout.Width(60f)))
|
||||
{
|
||||
_spawnRateScaleInput = Mathf.Max(MinSpawnRate, enemyManager.SpawnRateScale * 0.5f);
|
||||
enemyManager.SetSpawnRateScale(_spawnRateScaleInput);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("x2", GUILayout.Width(60f)))
|
||||
{
|
||||
_spawnRateScaleInput = enemyManager.SpawnRateScale * 2f;
|
||||
enemyManager.SetSpawnRateScale(_spawnRateScaleInput);
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
|
|
@ -187,21 +201,43 @@ namespace CustomComponent
|
|||
|
||||
if (GUILayout.Button("Extend Battle", GUILayout.Height(24f)))
|
||||
{
|
||||
enemyManager.AddBattleDuration(_extendDurationSeconds);
|
||||
if (procedure.CurrentGameState is GameStateBattle gameState)
|
||||
{
|
||||
gameState.AddBattleDuration(_extendDurationSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
GUI.enabled = player != null;
|
||||
if (GUILayout.Button("Disable Weapons", GUILayout.Height(24f)))
|
||||
{
|
||||
player.SetWeaponEnabled(false);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Enable Weapons", GUILayout.Height(24f)))
|
||||
{
|
||||
player.SetWeaponEnabled(true);
|
||||
}
|
||||
|
||||
GUI.enabled = true;
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using DataTable;
|
||||
using Definition.Enum;
|
||||
using Entity;
|
||||
using Entity.EntityData;
|
||||
using GameFramework.Event;
|
||||
|
|
@ -23,11 +24,11 @@ namespace CustomComponent
|
|||
|
||||
private float _spawnEnemyTimer;
|
||||
|
||||
private int _spawnEnemyMaxCount = 5000;
|
||||
[SerializeField] private int _spawnEnemyMaxCount = 5000;
|
||||
|
||||
private int _currentEnemyCount;
|
||||
|
||||
private int _spawnDistanceFromPlayer = 20;
|
||||
[SerializeField] private int _spawnDistanceFromPlayer = 20;
|
||||
|
||||
private int _currentSpawnEnemyId;
|
||||
|
||||
|
|
@ -35,7 +36,7 @@ namespace CustomComponent
|
|||
|
||||
private float[] _baseSpawnEnemyIntervals;
|
||||
private float[] _spawnEnemyIntervals;
|
||||
private int[] _spawnEnemyIds;
|
||||
private EnemyType[] _spawnEnemyTypes;
|
||||
private int[] _spawnEnemyCounts;
|
||||
private float _duration;
|
||||
private float _baseDuration;
|
||||
|
|
@ -45,11 +46,10 @@ namespace CustomComponent
|
|||
|
||||
private Transform _player;
|
||||
|
||||
private GameStateBattle _battle;
|
||||
|
||||
public float SpawnRateScale => _spawnRateScale;
|
||||
public float BattleDuration => _duration;
|
||||
public float ElapsedBattleTime => _spawnEnemyTimer;
|
||||
public int CurrentEnemyCount => _currentEnemyCount;
|
||||
|
||||
#region FSM
|
||||
|
||||
|
|
@ -71,22 +71,18 @@ namespace CustomComponent
|
|||
_entity = null;
|
||||
}
|
||||
|
||||
public void OnInit(int level, GameStateBattle battle)
|
||||
public void OnInit(DRLevel level)
|
||||
{
|
||||
_battle = battle;
|
||||
|
||||
_currentLevel = level;
|
||||
|
||||
DRLevel levelData = GameEntry.DataTable.GetDataTableRow<DRLevel>(_currentLevel);
|
||||
_baseSpawnEnemyIntervals = (float[])levelData.Intervals.Clone();
|
||||
_baseSpawnEnemyIntervals = (float[])level.Intervals.Clone();
|
||||
_spawnEnemyIntervals = (float[])_baseSpawnEnemyIntervals.Clone();
|
||||
_spawnEnemyIds = levelData.EntityIds;
|
||||
_spawnEnemyCounts = levelData.EntityCounts;
|
||||
_baseDuration = levelData.Duration;
|
||||
_spawnEnemyTypes = level.EntityTypes;
|
||||
_spawnEnemyCounts = level.EntityCounts;
|
||||
_baseDuration = level.Duration;
|
||||
_duration = _baseDuration;
|
||||
|
||||
SetSpawnRateScale(_spawnRateScale);
|
||||
|
||||
_currentEnemyCount = 0;
|
||||
_currentSpawnEnemyId = 0;
|
||||
}
|
||||
|
||||
|
|
@ -94,19 +90,13 @@ namespace CustomComponent
|
|||
{
|
||||
_spawnEnemyTimer += elapseSeconds;
|
||||
|
||||
if (_spawnEnemyTimer > _duration)
|
||||
{
|
||||
_battle.LevelOver();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _nextSpawnTimes.Length; i++)
|
||||
{
|
||||
float nextSpawnTime = _nextSpawnTimes[i];
|
||||
if (_spawnEnemyTimer < nextSpawnTime) continue;
|
||||
for (int j = 0; j < _spawnEnemyCounts[i]; j++)
|
||||
{
|
||||
SpawnEnemy(_spawnEnemyIds[i]);
|
||||
SpawnEnemy(_spawnEnemyTypes[i]);
|
||||
}
|
||||
|
||||
_nextSpawnTimes[i] += _spawnEnemyIntervals[i];
|
||||
|
|
@ -115,24 +105,57 @@ namespace CustomComponent
|
|||
|
||||
public void OnReset()
|
||||
{
|
||||
_currentEnemyCount = 0;
|
||||
|
||||
_spawnEnemyTimer = 0;
|
||||
_currentSpawnEnemyId = 0;
|
||||
_currentLevel = 0;
|
||||
|
||||
_baseSpawnEnemyIntervals = null;
|
||||
_spawnEnemyIntervals = null;
|
||||
_spawnEnemyIds = null;
|
||||
_spawnEnemyTypes = null;
|
||||
_spawnEnemyCounts = null;
|
||||
_baseDuration = 0;
|
||||
_duration = 0;
|
||||
|
||||
_nextSpawnTimes = null;
|
||||
|
||||
_battle = null;
|
||||
|
||||
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)
|
||||
|
|
@ -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)
|
||||
{
|
||||
float safeScale = Mathf.Max(MinSpawnRateScale, scale);
|
||||
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
|
||||
|
||||
private void OnShowEntitySuccess(object sender, GameEventArgs e)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 16371cc6e2112964d8b5bf7400c862cd
|
||||
timeCreated: 1528026152
|
||||
licenseType: Pro
|
||||
guid: 537419cc75316184b84b9f806b114976
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
|
|
@ -5,12 +5,21 @@ namespace DataTable
|
|||
public class DREnemy : DataRowBase
|
||||
{
|
||||
private int m_id;
|
||||
|
||||
public override int Id => m_id;
|
||||
|
||||
public int EntityTypeId { get; private set; }
|
||||
|
||||
public int MaxHealth { get; private set; }
|
||||
|
||||
public int HpAddPerLevel { get; private set; }
|
||||
|
||||
public float Speed { get; private set; }
|
||||
|
||||
public int DropCoin { get; private set; }
|
||||
|
||||
public int DropExp { get; private set; }
|
||||
|
||||
public float DropPercent { get; private set; }
|
||||
|
||||
public override bool ParseDataRow(string dataRowString, object userData)
|
||||
|
|
@ -21,6 +30,7 @@ namespace DataTable
|
|||
index++;
|
||||
m_id = int.Parse(columnStrings[index++]);
|
||||
index++;
|
||||
EntityTypeId = int.Parse(columnStrings[index++]);
|
||||
MaxHealth = int.Parse(columnStrings[index++]);
|
||||
HpAddPerLevel = int.Parse(columnStrings[index++]);
|
||||
Speed = float.Parse(columnStrings[index++]);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
using Definition.Enum;
|
||||
using StarForce;
|
||||
using CustomUtility;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace DataTable
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
using System;
|
||||
using Definition.DataStruct;
|
||||
using CustomUtility;
|
||||
using Definition.Enum;
|
||||
using StarForce;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace DataTable
|
||||
|
|
@ -18,7 +17,7 @@ namespace DataTable
|
|||
/// <summary>
|
||||
/// 获取关卡内会生成的实体编号
|
||||
/// </summary>
|
||||
public int[] EntityIds { get; private set; }
|
||||
public EnemyType[] EntityTypes { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取关卡内每次生成实体的数量
|
||||
|
|
@ -43,7 +42,7 @@ namespace DataTable
|
|||
index++;
|
||||
m_Id = int.Parse(columnStrings[index++]);
|
||||
index++;
|
||||
GenerateEntityIds(columnStrings[index++]);
|
||||
GenerateEntityTypes(columnStrings[index++]);
|
||||
GenerateEntityCounts(columnStrings[index++]);
|
||||
GenerateIntervals(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(']'))
|
||||
{
|
||||
|
|
@ -66,10 +65,10 @@ namespace DataTable
|
|||
if (raw.Length == 2) return;
|
||||
string[] entityIds = raw.Substring(1, raw.Length - 2).Split(",");
|
||||
int length = entityIds.Length;
|
||||
EntityIds = new int[length];
|
||||
EntityTypes = new EnemyType[length];
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
EntityIds[i] = int.Parse(entityIds[i]);
|
||||
EntityTypes[i] = EnumUtility<EnemyType>.Get(entityIds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
using Definition.DataStruct;
|
||||
using Definition.Enum;
|
||||
using Newtonsoft.Json;
|
||||
using StarForce;
|
||||
using CustomUtility;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace DataTable
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
using Definition.DataStruct;
|
||||
using GameFramework;
|
||||
using Newtonsoft.Json;
|
||||
using StarForce;
|
||||
using CustomUtility;
|
||||
using UnityGameFramework.Runtime;
|
||||
using Definition.Enum;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Definition.DataStruct;
|
||||
using Definition.Enum;
|
||||
using Entity;
|
||||
using GameFramework;
|
||||
using StarForce;
|
||||
using CustomUtility;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace DataTable
|
||||
|
|
@ -22,6 +22,8 @@ namespace DataTable
|
|||
/// </summary>
|
||||
public override int Id => m_Id;
|
||||
|
||||
public int EntityTypeId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取武器名称。
|
||||
/// </summary>
|
||||
|
|
@ -32,8 +34,19 @@ namespace DataTable
|
|||
/// </summary>
|
||||
public string IconAssetName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取武器稀有度
|
||||
/// </summary>
|
||||
public ItemRarity Rarity { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取武器价值
|
||||
/// </summary>
|
||||
public int Price { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取武器价值浮动率
|
||||
/// </summary>
|
||||
public float PriceRandomPercent { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -59,7 +72,7 @@ namespace DataTable
|
|||
/// <summary>
|
||||
/// 获取武器额外参数。
|
||||
/// </summary>
|
||||
public string Pramas { get; private set; }
|
||||
public Dictionary<string, string> Pramas { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取武器额外属性。
|
||||
|
|
@ -74,6 +87,7 @@ namespace DataTable
|
|||
index++;
|
||||
m_Id = int.Parse(columnStrings[index++]);
|
||||
index++;
|
||||
EntityTypeId = int.Parse(columnStrings[index++]);
|
||||
Title = columnStrings[index++];
|
||||
IconAssetName = columnStrings[index++];
|
||||
Rarity = EnumUtility<ItemRarity>.Get(columnStrings[index++]);
|
||||
|
|
@ -83,7 +97,7 @@ namespace DataTable
|
|||
Cooldown = float.Parse(columnStrings[index++]);
|
||||
AttackRange = float.Parse(columnStrings[index++]);
|
||||
AttackSoundId = int.Parse(columnStrings[index++]);
|
||||
Pramas = columnStrings[index++];
|
||||
Pramas = DeserializeParams(columnStrings[index++]);
|
||||
Modifiers = Utility.Json.ToObject<StatModifier[]>(columnStrings[index++]);
|
||||
|
||||
GeneratePropertyArray();
|
||||
|
|
@ -94,5 +108,30 @@ namespace DataTable
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 822be177ff3fd674fa34da0755f1a427
|
||||
timeCreated: 1528026153
|
||||
licenseType: Pro
|
||||
guid: cbb99f0b076af384291a10730e51e365
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
using System;
|
||||
using Definition.Enum;
|
||||
using StarForce;
|
||||
using CustomUtility;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace Definition.DataStruct
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
namespace Definition.Enum
|
||||
{
|
||||
public enum BulletType : byte
|
||||
{
|
||||
None = 0,
|
||||
BulletHandgun = 1
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2248c02c2c5529445992a2c2fef799dd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -3,7 +3,7 @@ namespace Definition.Enum
|
|||
public enum EnemyType : byte
|
||||
{
|
||||
None = 0,
|
||||
MeleeEnemy = 101,
|
||||
RemoteEnemy = 102,
|
||||
MeleeEnemy = 1,
|
||||
RemoteEnemy = 2,
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,8 @@ namespace Definition.Enum
|
|||
public enum WeaponType : byte
|
||||
{
|
||||
None = 0,
|
||||
WeaponKnife = 201,
|
||||
Remote = 202,
|
||||
WeaponKnife = 1,
|
||||
WeaponHandgun = 2,
|
||||
WeaponSlash = 3,
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,8 @@ using CustomComponent;
|
|||
using DataTable;
|
||||
using Definition.DataStruct;
|
||||
using Entity;
|
||||
using Game.Utility;
|
||||
using CustomUtility;
|
||||
using Procedure;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
|
|
@ -80,19 +81,29 @@ namespace StarForce.Editor
|
|||
|
||||
EnemyManagerComponent enemyManager = GameEntry.EnemyManager;
|
||||
Player player = FindPlayer();
|
||||
ProcedureGame procedure = GameEntry.Procedure.CurrentProcedure as ProcedureGame;
|
||||
|
||||
if (enemyManager == null)
|
||||
{
|
||||
EditorGUILayout.HelpBox("EnemyManager is unavailable.", MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (procedure == null)
|
||||
{
|
||||
EditorGUILayout.HelpBox("ProcedureGame is unavailable.", MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
EditorGUILayout.LabelField("Current Spawn Rate Scale", enemyManager.SpawnRateScale.ToString("F2"));
|
||||
EditorGUILayout.LabelField("Battle Time",
|
||||
$"{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);
|
||||
_extendDurationSeconds = Mathf.Clamp(EditorGUILayout.FloatField("Add Duration (Seconds)", _extendDurationSeconds),
|
||||
_extendDurationSeconds = Mathf.Clamp(
|
||||
EditorGUILayout.FloatField("Add Duration (Seconds)", _extendDurationSeconds),
|
||||
1f, 3600f);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
|
@ -113,16 +124,21 @@ namespace StarForce.Editor
|
|||
_spawnRateScaleInput = enemyManager.SpawnRateScale * 2f;
|
||||
enemyManager.SetSpawnRateScale(_spawnRateScaleInput);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
if (GUILayout.Button("Add Battle Duration"))
|
||||
{
|
||||
enemyManager.AddBattleDuration(_extendDurationSeconds);
|
||||
ShowNotification(new GUIContent($"+{_extendDurationSeconds:F0}s Battle Duration"));
|
||||
if (procedure.CurrentGameState is GameStateBattle gameState)
|
||||
{
|
||||
gameState.AddBattleDuration(_extendDurationSeconds);
|
||||
ShowNotification(new GUIContent($"+{_extendDurationSeconds:F0}s Battle Duration"));
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,10 @@ namespace Entity.EntityData
|
|||
[Serializable]
|
||||
public class EnemyData : TargetableObjectData
|
||||
{
|
||||
[SerializeField] private EnemyType _enemyType;
|
||||
|
||||
[SerializeField] private int _entityTypeId;
|
||||
|
||||
[SerializeField] private float _speedBase = 0;
|
||||
|
||||
[SerializeField] private int _dropCoin = 0;
|
||||
|
|
@ -16,42 +20,39 @@ namespace Entity.EntityData
|
|||
|
||||
[SerializeField] private float _dropPercent = 0;
|
||||
|
||||
public EnemyData(int entityId, int typeId, int level) : base(
|
||||
entityId, typeId, CampType.Enemy)
|
||||
public EnemyData(int entityId, EnemyType enemyType, int level) : base(
|
||||
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);
|
||||
|
||||
_enemyType = enemyType;
|
||||
_entityTypeId = enemyRow.EntityTypeId;
|
||||
MaxHealthBase = enemyRow.MaxHealth + enemyRow.HpAddPerLevel * (effectiveLevel - 1);
|
||||
SpeedBase = enemyRow.Speed;
|
||||
DropCoin = enemyRow.DropCoin;
|
||||
DropExp = enemyRow.DropExp;
|
||||
DropPercent = enemyRow.DropPercent;
|
||||
_speedBase = enemyRow.Speed;
|
||||
_dropCoin = enemyRow.DropCoin;
|
||||
_dropExp = enemyRow.DropExp;
|
||||
_dropPercent = enemyRow.DropPercent;
|
||||
}
|
||||
|
||||
public EnemyType EnemyType => _enemyType;
|
||||
|
||||
public int EntityTypeId => _entityTypeId;
|
||||
|
||||
public override int MaxHealthBase { get; }
|
||||
|
||||
public float SpeedBase
|
||||
{
|
||||
get => _speedBase;
|
||||
set => _speedBase = value;
|
||||
}
|
||||
public float SpeedBase => _speedBase;
|
||||
|
||||
public int DropCoin
|
||||
{
|
||||
get => _dropCoin;
|
||||
set => _dropCoin = value;
|
||||
}
|
||||
public int DropCoin => _dropCoin;
|
||||
|
||||
public int DropExp
|
||||
{
|
||||
get => _dropExp;
|
||||
set => _dropExp = value;
|
||||
}
|
||||
public int DropExp => _dropExp;
|
||||
|
||||
public float DropPercent
|
||||
{
|
||||
get => _dropPercent;
|
||||
set => _dropPercent = value;
|
||||
}
|
||||
public float DropPercent => _dropPercent;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using DataTable;
|
||||
using Definition.DataStruct;
|
||||
using Definition.Enum;
|
||||
|
|
@ -10,10 +11,31 @@ namespace Entity.EntityData
|
|||
{
|
||||
private DRWeapon _drWeapon;
|
||||
|
||||
public WeaponData(int entityId, int typeId, int ownerId, CampType ownerCamp)
|
||||
: base(entityId, typeId, ownerId, ownerCamp)
|
||||
private int _entityTypeId = 0;
|
||||
|
||||
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>
|
||||
|
|
@ -21,6 +43,8 @@ namespace Entity.EntityData
|
|||
/// </summary>
|
||||
public int Attack => _drWeapon.Attack;
|
||||
|
||||
public int EntityTypeId => _entityTypeId;
|
||||
|
||||
/// <summary>
|
||||
/// 武器名称。
|
||||
/// </summary>
|
||||
|
|
@ -53,7 +77,7 @@ namespace Entity.EntityData
|
|||
/// <summary>
|
||||
/// 额外参数。
|
||||
/// </summary>
|
||||
public string Params => _drWeapon.Pramas;
|
||||
public Dictionary<string, string> Params => _drWeapon.Pramas;
|
||||
|
||||
/// <summary>
|
||||
/// 额外属性。
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 574d105bf89bf7546a682ed407708034
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -4,7 +4,7 @@ namespace Entity.EntityData
|
|||
{
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4aa6473525dc4c62aeee02ff562ec453
|
||||
timeCreated: 1771484661
|
||||
|
|
@ -1,35 +1,25 @@
|
|||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Definition;
|
||||
using Entity.EntityData;
|
||||
using UnityGameFramework.Runtime;
|
||||
using DataTable;
|
||||
using Definition.Enum;
|
||||
using Game.Utility;
|
||||
using CustomUtility;
|
||||
|
||||
namespace Entity
|
||||
{
|
||||
public static class EntityExtension
|
||||
{
|
||||
// 关于 EntityId 的约定:
|
||||
// 0 为无效
|
||||
// 正值用于和服务器通信的实体(如玩家角色、NPC、怪等,服务器只产生正值)
|
||||
// 负值用于本地生成的临时实体(如特效、FakeObject等)
|
||||
private static int s_SerialId = -10;
|
||||
|
||||
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()
|
||||
{
|
||||
_typeDict = new Dictionary<string, Type>();
|
||||
_assetNameDict = new Dictionary<int, string>();
|
||||
}
|
||||
|
||||
public static EntityBase GetGameEntity(this EntityComponent entityComponent, int entityId)
|
||||
|
|
@ -61,44 +51,63 @@ namespace Entity
|
|||
|
||||
public static void ShowEnemy(this EntityComponent entityComponent, EnemyData data)
|
||||
{
|
||||
string typeName = EntityNamespace + (EnemyType)data.TypeId;
|
||||
if (!_typeDict.TryGetValue(typeName, out Type enemyType))
|
||||
if (data == null)
|
||||
{
|
||||
enemyType = Type.GetType(typeName);
|
||||
if (enemyType == null)
|
||||
{
|
||||
Log.Warning("Can not load entity type '{0}'.", typeName);
|
||||
return;
|
||||
}
|
||||
|
||||
_typeDict.Add(typeName, enemyType);
|
||||
Log.Warning("Enemy data is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
string typeName = EntityNamespace + (WeaponType)data.TypeId;
|
||||
if (!_typeDict.TryGetValue(typeName, out Type weaponType))
|
||||
if (data == null)
|
||||
{
|
||||
weaponType = Type.GetType(typeName);
|
||||
if (weaponType == null)
|
||||
{
|
||||
Log.Warning("Can not load entity type '{0}'.", typeName);
|
||||
return;
|
||||
}
|
||||
|
||||
_typeDict.Add(typeName, weaponType);
|
||||
Log.Warning("Weapon data is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
entityComponent.ShowEntity(typeof(Bullet), "Bullet", Constant.AssetPriority.BulletAsset, data);
|
||||
}
|
||||
// public static void ShowBullet(this EntityComponent entityComponent, BulletData 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)
|
||||
{
|
||||
|
|
@ -135,6 +144,42 @@ namespace Entity
|
|||
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)
|
||||
{
|
||||
return --s_SerialId;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
using Components;
|
||||
using Definition.DataStruct;
|
||||
using Entity.EntityData;
|
||||
using Entity.Weapon;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
|
|
@ -39,7 +40,7 @@ namespace Entity
|
|||
{
|
||||
_meleeEnemyData = enemyData;
|
||||
_healthComponent.OnInit(enemyData.MaxHealthBase);
|
||||
_movementComponent.OnInit(_meleeEnemyData.SpeedBase, this.CachedTransform);
|
||||
_movementComponent.OnInit(_meleeEnemyData.SpeedBase, this.CachedTransform, null, true);
|
||||
_movementComponent.SetMove(true);
|
||||
_attackRangeSquared = _attackRange * _attackRange;
|
||||
this.CachedTransform.position = enemyData.Position;
|
||||
|
|
@ -55,6 +56,13 @@ namespace Entity
|
|||
{
|
||||
base.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||
|
||||
if (_target == null)
|
||||
{
|
||||
_movementComponent.SetMove(false);
|
||||
_movementComponent.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||
return;
|
||||
}
|
||||
|
||||
float distanceSquared = (this.CachedTransform.position - _target.position).sqrMagnitude;
|
||||
if (distanceSquared < _attackRangeSquared)
|
||||
{
|
||||
|
|
@ -105,6 +113,11 @@ namespace Entity
|
|||
|
||||
private Vector3 GetTargetDirection()
|
||||
{
|
||||
if (_target == null)
|
||||
{
|
||||
return Vector3.zero;
|
||||
}
|
||||
|
||||
return new Vector3(
|
||||
_target.position.x - this.CachedTransform.position.x,
|
||||
0f,
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ namespace Entity
|
|||
{
|
||||
_remoteEnemyData = enemyData;
|
||||
_healthComponent.OnInit(enemyData.MaxHealthBase);
|
||||
_movementComponent.OnInit(_remoteEnemyData.SpeedBase, this.CachedTransform);
|
||||
_movementComponent.OnInit(_remoteEnemyData.SpeedBase, this.CachedTransform, null, true);
|
||||
_movementComponent.SetMove(true);
|
||||
_attackRangeSquared = _attackRange * _attackRange;
|
||||
this.CachedTransform.position = enemyData.Position;
|
||||
|
|
@ -52,6 +52,13 @@ namespace Entity
|
|||
{
|
||||
base.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||
|
||||
if (_target == null)
|
||||
{
|
||||
_movementComponent.SetMove(false);
|
||||
_movementComponent.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||
return;
|
||||
}
|
||||
|
||||
float distanceSquared = (this.CachedTransform.position - _target.position).sqrMagnitude;
|
||||
if (distanceSquared < _attackRangeSquared)
|
||||
{
|
||||
|
|
@ -77,6 +84,11 @@ namespace Entity
|
|||
|
||||
private Vector3 GetTargetDirection()
|
||||
{
|
||||
if (_target == null)
|
||||
{
|
||||
return Vector3.zero;
|
||||
}
|
||||
|
||||
return new Vector3(
|
||||
_target.position.x - this.CachedTransform.position.x,
|
||||
0f,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using Definition.DataStruct;
|
|||
using Entity.EntityData;
|
||||
using GameFramework.Event;
|
||||
using UnityGameFramework.Runtime;
|
||||
using Entity.Weapon;
|
||||
|
||||
namespace Entity
|
||||
{
|
||||
|
|
@ -45,6 +46,7 @@ namespace Entity
|
|||
/// 玩家是否启用。
|
||||
/// </summary>
|
||||
private bool _enable;
|
||||
|
||||
private bool _weaponEnabled = true;
|
||||
|
||||
public int Coin
|
||||
|
|
@ -89,6 +91,22 @@ namespace Entity
|
|||
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
|
||||
{
|
||||
get => _enable;
|
||||
|
|
@ -122,8 +140,7 @@ namespace Entity
|
|||
// BackpackComponent
|
||||
Coin = role.Coin;
|
||||
_backpackComponent.OnInit(this, role.WeaponCapacity);
|
||||
GameEntry.Entity.ShowWeapon(new WeaponKnifeData(GameEntry.Entity.GenerateSerialId(), 201,
|
||||
this.Id, _playerData.Camp));
|
||||
GameEntry.Entity.ShowWeapon(new WeaponKnifeData(GameEntry.Entity.GenerateSerialId(), Id, _playerData.Camp));
|
||||
|
||||
// StatComponent
|
||||
foreach (var modifier in role.InitialProperties)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
using Components;
|
||||
using Definition.DataStruct;
|
||||
using Entity.EntityData;
|
||||
using Game.Utility;
|
||||
using CustomUtility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Entity
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: abdf25a9782ebf844a65b2348f8f566d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d944f33383dc4c1ebe850a12d4211afa
|
||||
timeCreated: 1771481130
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace Entity.Weapon
|
||||
{
|
||||
public interface IWeaponAttackEffect
|
||||
{
|
||||
void Play(WeaponBase weapon, Vector3 position, EntityBase target, float radius);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 231a935a484c4351b707b3e359074a47
|
||||
timeCreated: 1771481068
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8f48328040644546bf0a866cc337d3b2
|
||||
timeCreated: 1771481100
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 329af2c156b345f28a7465ef29c943da
|
||||
timeCreated: 1771485834
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 371bc24a18a3447686043843fdd6fbe5
|
||||
timeCreated: 1771427642
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 54932952c9fa3fd4dbe6d21f2a484b4d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Entity.Weapon
|
||||
{
|
||||
public interface ITargetSelector
|
||||
{
|
||||
EntityBase SelectTarget(WeaponBase weapon, IEnumerable<EntityBase> candidates, float maxSqrRange);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1ed801e4816c03b4597756dc36d6aba8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 081d67a491f5792479306d88091168e3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a70602535b318e74396a6c2ea282834d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
namespace Entity.Weapon
|
||||
{
|
||||
public enum TargetSelectorType
|
||||
{
|
||||
Nearest = 0,
|
||||
HighestHealth = 1,
|
||||
LowestHealth = 2
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4eb1e0b99139fdd4280c8616804a8f77
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,18 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Components;
|
||||
using Definition.DataStruct;
|
||||
using Definition.Enum;
|
||||
using Entity.EntityData;
|
||||
using GameFramework;
|
||||
using CustomUtility;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace Entity
|
||||
namespace Entity.Weapon
|
||||
{
|
||||
public enum WeaponStateType
|
||||
{
|
||||
Disabled,
|
||||
Check,
|
||||
Attack,
|
||||
Transition,
|
||||
Idle,
|
||||
Check_OutRange,
|
||||
Check_InRange,
|
||||
|
|
@ -29,37 +30,94 @@ namespace Entity
|
|||
|
||||
[SerializeField] protected bool _isEnabled = false;
|
||||
|
||||
public WeaponData WeaponData;
|
||||
public WeaponData WeaponData { get; protected set; }
|
||||
|
||||
public bool IsAttacking => _isAttacking;
|
||||
|
||||
protected ITargetSelector TargetSelector { get; set; }
|
||||
|
||||
protected Dictionary<WeaponStateType, WeaponStateBase> _states;
|
||||
|
||||
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)
|
||||
{
|
||||
base.OnInit(userData);
|
||||
|
||||
if (TargetSelector == null)
|
||||
{
|
||||
TargetSelector = CreateSelector(DefaultTargetSelectorType);
|
||||
}
|
||||
}
|
||||
|
||||
//protected abstract void OnShow(object userData);
|
||||
// {
|
||||
// base.OnShow(userData);
|
||||
//
|
||||
// _weaponData = userData as WeaponData;
|
||||
// if (_weaponData == null)
|
||||
// {
|
||||
// Log.Error("Weapon data is invalid.");
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
protected sealed override void OnShow(object userData)
|
||||
{
|
||||
base.OnShow(userData);
|
||||
|
||||
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);
|
||||
|
||||
Name = Utility.Text.Format("Weapon of {0}", parentEntity.Name);
|
||||
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
|
||||
|
|
@ -67,12 +125,147 @@ namespace Entity
|
|||
public abstract ImpactData GetImpactData();
|
||||
protected abstract void Attack();
|
||||
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)
|
||||
{
|
||||
this._isEnabled = value;
|
||||
TransitionTo(WeaponStateType.Idle);
|
||||
_isEnabled = value;
|
||||
|
||||
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 override string ToString() => State.ToString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0b289e88dc5243aeb564df941485c5cf
|
||||
timeCreated: 1771579168
|
||||
|
|
@ -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() { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4e72df067466464fa3be92bfd049dd0d
|
||||
timeCreated: 1771579220
|
||||
|
|
@ -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() { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bfd76c0fb7284ceaa28f78c702f595da
|
||||
timeCreated: 1771579231
|
||||
|
|
@ -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() { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3a9f936d3c8643f7bf632734548f800c
|
||||
timeCreated: 1771579241
|
||||
|
|
@ -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() { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9c5341944d1a467b8553d31bc3d81d5b
|
||||
timeCreated: 1771579252
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 44ae1bdd1d43e274980743df21658730
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f89893f21b3046b7a93a368cd688451e
|
||||
timeCreated: 1771578597
|
||||
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 417516ffe5574e0c998105fcaa073de7
|
||||
timeCreated: 1771578955
|
||||
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 59644093364644768c26261b24bdb820
|
||||
timeCreated: 1771578916
|
||||
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d9e1a63869694e72a360afe29cc94cfb
|
||||
timeCreated: 1771578994
|
||||
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b4d66ec44b064640aa95c6aa71ff3846
|
||||
timeCreated: 1771578662
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
Loading…
Reference in New Issue