修复已知功能

1. 添加章节开始 UI 介绍
2. 清理不使用的资源
3. 补充 AIChat 入口 UI
4. 接上所有的正常对话流程
This commit is contained in:
SepComet 2026-04-21 10:36:47 +08:00
parent ef68ccc9e9
commit a1b6b4a823
137 changed files with 27967 additions and 26332 deletions

View File

@ -1,23 +1,23 @@
# 对话标识表 筛选用数据
# 对话标识表 列1 列2 列3 筛选用数据
# Id Title UIMode ChapterId
# int string DialogUIMode int
# 对话编号 策划备注 对话标识 对话形式 章节编号
1001 第一章介绍 Ch1_Time Mask 1.001
1001 第一章介绍 无规矩,则屋不立 Mask 1.001
1002 第一章主流程 Ch1_Dia01 BottomBox 1.002
1003 Ch1_Dia02 BottomBox 1.003
1004 Ch1_Dia03 BottomBox 1.004
1005 Ch1_Dia04 BottomBox 1.005
2001 Ch2_Time Mask 2.001
2001 凡构屋之制,皆以材为祖 Mask 2.001
2002 Ch2_Dia01 BottomBox 2.002
2003 Ch2_Dia02 BottomBox 2.003
2004 Ch2_Dia03 BottomBox 2.004
2005 Ch2_Dia04 BottomBox 2.005
3001 Ch3_Time Mask 3.001
3001 图文并重,以辅文字之不足 Mask 3.001
3002 Ch3_Dia01 BottomBox 3.002
3003 Ch3_Dia02 BottomBox 3.003
3004 Ch3_Dia03 BottomBox 3.004
3005 Ch3_Dia04 BottomBox 3.005
4001 Ch4_Time Mask 4.001
4001 建筑是凝固的历史 Mask 4.001
4002 Ch4_Dia01 BottomBox 4.002
4003 Ch4_Dia02 BottomBox 4.003
4004 Ch4_Dia03 BottomBox 4.004

View File

@ -28,18 +28,18 @@
100300016 Player 玩家 0 属下遵命。 None 1.00300016 1003.00016
100300017 Other 旁白 0 你正式踏入将作监,一段以法度正营造的历程,自此开始。 None 1.00300017 1003.00017
100400001 LiJie 李诫 1 这里是库房,存放着营造时用到的所有工具。 None 1.00400001 1004.00001
100400002 Subtitle 字幕 0 檀木质地,刻度以朱砂标注,一端为方、一端为折,刻有 “材份” 刻度。 None 1.00400002 1004.00002
100400002 Subtitle 字幕 0 檀木质地,刻度以朱砂标注,一端为方、一端为折,刻有 “材份” 刻度。 EntityCue 1.00400002 1004.00002
100400003 LiJie 李诫 1 你看,构屋先定 “方”,柱枋是否垂直、斗拱是否方正,全凭这曲尺核验。 None 1.00400003 1004.00003
100400004 LiJie 李诫 1 此前工地偷换木料,若早以曲尺量其形制,便知柳木径寸不足,断断用不得啊。 None 1.00400004 1004.00004
100400005 LiJie 李诫 1 此乃方斗,也叫墨斗,配绳墨使用。 None 1.00400005 1004.00005
100400004 LiJie 李诫 1 此前工地偷换木料,若早以曲尺量其形制,便知柳木径寸不足,断断用不得啊。 EntityCueClose 1.00400004 1004.00004
100400005 LiJie 李诫 1 此乃方斗,也叫墨斗,配绳墨使用。 EntityCue 1.00400005 1004.00005
100400006 LiJie 李诫 1 绳墨牵直,以墨斗弹线,便知木料是否平直、墙体是否端正。 None 1.00400006 1004.00006
100400007 Subtitle 字幕 0 方斗为楠木所制,斗身刻 “将作监” 款识,墨仓密封,绳墨以生丝浸松烟墨制成,耐磨不褪。 None 1.00400007 1004.00007
100400007 Subtitle 字幕 0 方斗为楠木所制,斗身刻 “将作监” 款识,墨仓密封,绳墨以生丝浸松烟墨制成,耐磨不褪。 EntityCueClose 1.00400007 1004.00007
100400008 LiJie 李诫 1 营造之事,“直” 为要。 None 1.00400008 1004.00008
100400009 LiJie 李诫 1 工头偷工减料,常以歪木充数,若以绳墨弹线,歪直立现,断无蒙混可能。 None 1.00400009 1004.00009
100400010 Player 玩家 0 那这是何物? None 1.0040001 1004.0001
100400010 Player 玩家 0 那这是何物? EntityCue 1.0040001 1004.0001
100400011 LiJie 李诫 1 这是水准,也名 “水尺”,测地势高低、屋基平正之用。 None 1.00400011 1004.00011
100400012 Subtitle 字幕 0 青铜制的水准器,形制为长槽,内盛水,槽侧刻刻度,槽端有通气孔,是宋代成熟的水准测量工具。 None 1.00400012 1004.00012
100400013 LiJie 李诫 1 汴京地势多洼,营建房宇先以水准定基址 None 1.00400013 1004.00013
100400013 LiJie 李诫 1 汴京地势多洼,营建房宇先以水准定基址 EntityCueClose 1.00400013 1004.00013
100400014 LiJie 李诫 1 若基址倾斜,纵是木料精良,也难逃倾颓之祸。 None 1.00400014 1004.00014
100400015 LiJie 李诫 1 水之性平,这水准便是借水定平。 None 1.00400015 1004.00015
100400016 LiJie 李诫 1 除了这些器具,库房更藏有 “料例” 册籍, None 1.00400016 1004.00016
@ -50,15 +50,15 @@
100400021 LiJie 李诫 1 明日随我去工地,以器验工,看看那些贪弊工头还能藏多少猫腻。 None 1.00400021 1004.00021
100400022 Player 玩家 0 是,李主簿。 None 1.00400022 1004.00022
100500001 LiJie 李诫 0 这工地是谁负责! None 1.00500001 1005.00001
100500002 GongTou 工头 0 李主簿,您怎么又来了… None 1.00500002 1005.00002
100500002 GongTou 工头 1 李主簿,您怎么又来了… None 1.00500002 1005.00002
100500003 LiJie 李诫 0 我不来,怎知你们又在如何糊弄? None 1.00500003 1005.00003
100500004 LiJie 李诫 0 今日来便是要验工。 None 1.00500004 1005.00004
100500005 LiJie 李诫 0 你且取来昨日库房的曲尺、绳墨、水准,逐一核验梁柱基址。 None 1.00500005 1005.00005
100500006 Player 玩家 0 是,李主簿。 None 1.00500006 1005.00006
100500007 Player 玩家 0 李主簿,这根立柱偏斜三分,枋木榫口尺寸不符,根本无法咬合! None 1.00500007 1005.00007
100500008 GongTou 工头 0 这… None 1.00500008 1005.00008
100500009 GongTou 工头 0 … None 1.00500009 1005.00009
100500010 GongTou 工头 0 这只是小偏差,匠人凭手眼便可调整… None 1.0050001 1005.0001
100500008 GongTou 工头 1 这… None 1.00500008 1005.00008
100500009 GongTou 工头 1 … None 1.00500009 1005.00009
100500010 GongTou 工头 1 这只是小偏差,匠人凭手眼便可调整… None 1.0050001 1005.0001
100500011 LiJie 李诫 0 营造无差毫厘! None 1.00500011 1005.00011
100500012 LiJie 李诫 0 榫卯不合、梁柱不直,今日可凑活,明日必倾塌! None 1.00500012 1005.00012
100500013 LiJie 李诫 0 你看好 ——屋之牢固,全在榫卯。 None 1.00500013 1005.00013
@ -72,8 +72,8 @@
100500021 LiJie 李诫 0 这便是法度之力。 None 1.00500021 1005.00021
100500022 LiJie 李诫 0 构件按图预制,到工地只需精准拼装,既省工,又让贪弊者无可乘之机。 None 1.00500022 1005.00022
100500023 LiJie 李诫 0 往后所有构件,必须先以尺量、以图定、以榫卯合,不合格一律不准上架! None 1.00500023 1005.00023
100500024 GongTou 工头 0 是… None 1.00500024 1005.00024
100500025 GongTou 工头 0 小人遵命… None 1.00500025 1005.00025
100500024 GongTou 工头 1 是… None 1.00500024 1005.00024
100500025 GongTou 工头 1 小人遵命… None 1.00500025 1005.00025
100500026 LiJie 李诫 0 这是你第一次亲手完成榫卯拼装,以图正匠,以器验工,以榫卯固屋 None 1.00500026 1005.00026
100500027 LiJie 李诫 0 这条路,你才刚刚开始啊。 None 1.00500027 1005.00027
200100001 Time 时间 0 绍圣四年,北宋汴京。 None 2.00100001 2001.00001

View File

@ -1,21 +0,0 @@
# 实体表
# Id AssetName
# int string
# 实体编号 策划备注 资源名称
10000 玩家战机 PlayerShip
10001 敌人战机 EnemyShip
20000 玩家推进器 PlayerEngine
20001 敌人推进器 EnemyEngine
30000 默认武器 DefaultWeapon
40000 默认装甲 DefaultArmor
50000 玩家子弹 PlayerBolt
50001 敌人子弹 EnemyBolt
60000 小行星 Asteroid01
60001 小行星 Asteroid02
60002 小行星 Asteroid03
70000 玩家死亡特效 PlayerExplosion
70001 敌人死亡特效 EnemyExplosion
70002 小行星死亡特效 AsteroidExplosion
101 测试玩家 Player
1001 测试敌人 Enemy
11 跟随相机 FollowCamera

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: cfe53cf384344bd47a8680f8c5f97a7b
timeCreated: 1528026124
licenseType: Pro
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -13,3 +13,5 @@
106 Bubble对话UI BubbleDialogForm Dialog True False
107 游戏场景覆盖UI MainOverlayForm Overlay False False
108 AI对话UI AIChatForm Overlay False False
109 章节标题UI ChapterTitleForm Overlay False False
110 AI入口UI AIChatEntryForm Top False False

View File

@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: 50bb427da72e6164c995404fac1a139c
folderAsset: yes
timeCreated: 1528026124
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,122 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &9166462022471897675
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 7683855655592166216}
- component: {fileID: 6418687210998749921}
- component: {fileID: 4710806460657047075}
- component: {fileID: 1932268889601128120}
- component: {fileID: 557030043145096197}
m_Layer: 7
m_Name: Enemy
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &7683855655592166216
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 9166462022471897675}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &6418687210998749921
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 9166462022471897675}
m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &4710806460657047075
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 9166462022471897675}
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: 28c42f88cfe56e84e95c0d7545db8c1b, 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!136 &1932268889601128120
CapsuleCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 9166462022471897675}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 2
m_Radius: 0.5
m_Height: 2
m_Direction: 1
m_Center: {x: 0, y: 0, z: 0}
--- !u!114 &557030043145096197
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 9166462022471897675}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 9baf591df06b9bb44ba5313457ba84b3, type: 3}
m_Name:
m_EditorClassIdentifier:

View File

@ -1,273 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &5383497626468778460
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 5901618247908801032}
- component: {fileID: 4064848608618185461}
- component: {fileID: 142028612608113928}
- component: {fileID: 4294245010031275705}
m_Layer: 7
m_Name: Camera
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &5901618247908801032
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
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_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 9112716898534404901}
m_LocalEulerAnglesHint: {x: 50, y: 0, z: 0}
--- !u!20 &4064848608618185461
Camera:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5383497626468778460}
m_Enabled: 1
serializedVersion: 2
m_ClearFlags: 1
m_BackGroundColor: {r: 0, g: 0, b: 0, a: 1}
m_projectionMatrixMode: 1
m_GateFitMode: 2
m_FOVAxisMode: 0
m_Iso: 200
m_ShutterSpeed: 0.005
m_Aperture: 16
m_FocusDistance: 10
m_FocalLength: 50
m_BladeCount: 5
m_Curvature: {x: 2, y: 11}
m_BarrelClipping: 0.25
m_Anamorphism: 0
m_SensorSize: {x: 36, y: 24}
m_LensShift: {x: 0, y: 0}
m_NormalizedViewPortRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
near clip plane: 0.3
far clip plane: 1000
field of view: 60
orthographic: 0
orthographic size: 5
m_Depth: 0
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_RenderingPath: -1
m_TargetTexture: {fileID: 0}
m_TargetDisplay: 0
m_TargetEye: 3
m_HDR: 1
m_AllowMSAA: 1
m_AllowDynamicResolution: 0
m_ForceIntoRT: 0
m_OcclusionCulling: 1
m_StereoConvergence: 10
m_StereoSeparation: 0.022
--- !u!81 &142028612608113928
AudioListener:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5383497626468778460}
m_Enabled: 1
--- !u!114 &4294245010031275705
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5383497626468778460}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3}
m_Name:
m_EditorClassIdentifier:
m_RenderShadows: 1
m_RequiresDepthTextureOption: 2
m_RequiresOpaqueTextureOption: 2
m_CameraType: 0
m_Cameras: []
m_RendererIndex: -1
m_VolumeLayerMask:
serializedVersion: 2
m_Bits: 1
m_VolumeTrigger: {fileID: 0}
m_VolumeFrameworkUpdateModeOption: 2
m_RenderPostProcessing: 0
m_Antialiasing: 0
m_AntialiasingQuality: 2
m_StopNaN: 0
m_Dithering: 0
m_ClearDepth: 1
m_AllowXRRendering: 1
m_AllowHDROutput: 1
m_UseScreenCoordOverride: 0
m_ScreenSizeOverride: {x: 0, y: 0, z: 0, w: 0}
m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0}
m_RequiresDepthTexture: 0
m_RequiresColorTexture: 0
m_Version: 2
m_TaaSettings:
m_Quality: 3
m_FrameInfluence: 0.1
m_JitterScale: 1
m_MipBias: 0
m_VarianceClampScale: 0.9
m_ContrastAdaptiveSharpening: 0
--- !u!1 &8198962528757305497
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 9112716898534404901}
- component: {fileID: 6822287017757374257}
- component: {fileID: 4750262518011961108}
- component: {fileID: 2658714253019925332}
- component: {fileID: 8169181699668835961}
- component: {fileID: 5456587199021485293}
m_Layer: 7
m_Name: Player
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &9112716898534404901
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8198962528757305497}
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: 5901618247908801032}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &6822287017757374257
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8198962528757305497}
m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &4750262518011961108
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8198962528757305497}
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!136 &2658714253019925332
CapsuleCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8198962528757305497}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 2
m_Radius: 0.5
m_Height: 2
m_Direction: 1
m_Center: {x: 0, y: 0, z: 0}
--- !u!114 &8169181699668835961
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8198962528757305497}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f754cc5f339dcfa4aaec3aa32260c4b8, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &5456587199021485293
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8198962528757305497}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 9baf591df06b9bb44ba5313457ba84b3, type: 3}
m_Name:
m_EditorClassIdentifier:

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: f04cfd53e22bdc14b808906f4d98571b
guid: a84959718d612514583680c96ee8271a
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000

View File

@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: cbbffe476d7482a40a31a236538e23b9
folderAsset: yes
timeCreated: 1528026129
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,136 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Red
m_Shader: {fileID: 4800000, guid: f17bc7e2694233349a8b22e96c5c43de, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 1
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BlendOp: 0
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _FlashAmount: 0
- _GlossMapScale: 0
- _Glossiness: 0
- _GlossyReflections: 0
- _Metallic: 0
- _OcclusionStrength: 1
- _Parallax: 0.005
- _QueueOffset: 0
- _ReceiveShadows: 1
- _SampleGI: 0
- _Smoothness: 0.5
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _WorkflowMode: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 1, g: 0.2924527, b: 0.2924527, a: 1}
- _Color: {r: 1, g: 0.29245266, b: 0.29245266, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _FlashColor: {r: 1, g: 1, b: 1, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
--- !u!114 &4582237545370899445
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 7

View File

@ -1,91 +0,0 @@
Shader "Custom/SimpleInstancedFlash"
{
Properties
{
_BaseColor ("Base Color", Color) = (1,1,1,1)
_FlashColor ("Flash Color", Color) = (1,1,1,1) // 闪白颜色
_FlashAmount ("Flash Amount", Range(0, 1)) = 0 // 0=不闪, 1=全闪
}
SubShader
{
Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" "Queue"="Geometry+10" }
LOD 100
Pass
{
Name "ForwardLit"
Tags { "LightMode"="UniversalForward" }
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
// 开启 GPU Instancing 必须的宏
#pragma multi_compile_instancing
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
UNITY_VERTEX_INPUT_INSTANCE_ID // 1. 输入结构体必须包含 Instance ID
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float3 normalWS : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID // 2. 输出结构体必须包含 Instance ID
};
// 3. 定义 Instancing 缓冲区 (这里定义的数据每个物体都可以不同)
UNITY_INSTANCING_BUFFER_START(Props)
UNITY_DEFINE_INSTANCED_PROP(float4, _BaseColor)
UNITY_DEFINE_INSTANCED_PROP(float4, _FlashColor)
UNITY_DEFINE_INSTANCED_PROP(float, _FlashAmount)
UNITY_INSTANCING_BUFFER_END(Props)
Varyings vert(Attributes input)
{
Varyings output;
// 4. 设置 Instance ID
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
output.positionCS = vertexInput.positionCS;
VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS, float4(0,0,0,0));
output.normalWS = normalInput.normalWS;
return output;
}
half4 frag(Varyings input) : SV_Target
{
// 5. 片元着色器也要 Setup Instance ID
UNITY_SETUP_INSTANCE_ID(input);
// 6. 使用宏读取属性 (代替直接写 _BaseColor)
float4 baseColor = UNITY_ACCESS_INSTANCED_PROP(Props, _BaseColor);
float4 flashColor = UNITY_ACCESS_INSTANCED_PROP(Props, _FlashColor);
float flashAmount = UNITY_ACCESS_INSTANCED_PROP(Props, _FlashAmount);
// 简单光照计算 (Lambert)
Light mainLight = GetMainLight();
float NdotL = saturate(dot(input.normalWS, mainLight.direction));
float3 lighting = baseColor.rgb * (mainLight.color * NdotL + unity_AmbientSky.rgb); // 加上环境光防止背光全黑
// 混合闪白效果
float3 finalColor = lerp(lighting, flashColor.rgb, flashAmount);
return float4(finalColor, baseColor.a);
}
ENDHLSL
}
}
}

View File

@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: f17bc7e2694233349a8b22e96c5c43de
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: 8d68657bbcaf3164a9868d3041485557
folderAsset: yes
timeCreated: 1528026132
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,356 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was auto-generated by com.unity.inputsystem:InputActionCodeGenerator
// version 1.14.2
// from Assets/PlayerInputActions.inputactions
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Utilities;
/// <summary>
/// Provides programmatic access to <see cref="InputActionAsset" />, <see cref="InputActionMap" />, <see cref="InputAction" /> and <see cref="InputControlScheme" /> instances defined in asset "Assets/PlayerInputActions.inputactions".
/// </summary>
/// <remarks>
/// This class is source generated and any manual edits will be discarded if the associated asset is reimported or modified.
/// </remarks>
/// <example>
/// <code>
/// using namespace UnityEngine;
/// using UnityEngine.InputSystem;
///
/// // Example of using an InputActionMap named "Player" from a UnityEngine.MonoBehaviour implementing callback interface.
/// public class Example : MonoBehaviour, MyActions.IPlayerActions
/// {
/// private MyActions_Actions m_Actions; // Source code representation of asset.
/// private MyActions_Actions.PlayerActions m_Player; // Source code representation of action map.
///
/// void Awake()
/// {
/// m_Actions = new MyActions_Actions(); // Create asset object.
/// m_Player = m_Actions.Player; // Extract action map object.
/// m_Player.AddCallbacks(this); // Register callback interface IPlayerActions.
/// }
///
/// void OnDestroy()
/// {
/// m_Actions.Dispose(); // Destroy asset object.
/// }
///
/// void OnEnable()
/// {
/// m_Player.Enable(); // Enable all actions within map.
/// }
///
/// void OnDisable()
/// {
/// m_Player.Disable(); // Disable all actions within map.
/// }
///
/// #region Interface implementation of MyActions.IPlayerActions
///
/// // Invoked when "Move" action is either started, performed or canceled.
/// public void OnMove(InputAction.CallbackContext context)
/// {
/// Debug.Log($"OnMove: {context.ReadValue&lt;Vector2&gt;()}");
/// }
///
/// // Invoked when "Attack" action is either started, performed or canceled.
/// public void OnAttack(InputAction.CallbackContext context)
/// {
/// Debug.Log($"OnAttack: {context.ReadValue&lt;float&gt;()}");
/// }
///
/// #endregion
/// }
/// </code>
/// </example>
public partial class @PlayerInputActions: IInputActionCollection2, IDisposable
{
/// <summary>
/// Provides access to the underlying asset instance.
/// </summary>
public InputActionAsset asset { get; }
/// <summary>
/// Constructs a new instance.
/// </summary>
public @PlayerInputActions()
{
asset = InputActionAsset.FromJson(@"{
""version"": 1,
""name"": ""PlayerInputActions"",
""maps"": [
{
""name"": ""Game"",
""id"": ""c320222a-39c4-47d5-bb95-1702176bdb69"",
""actions"": [
{
""name"": ""Move"",
""type"": ""Value"",
""id"": ""4875d1e6-7f0e-490d-88f4-b97f322ce0dd"",
""expectedControlType"": ""Vector2"",
""processors"": """",
""interactions"": """",
""initialStateCheck"": true
}
],
""bindings"": [
{
""name"": ""2D Vector"",
""id"": ""c2c107da-b6b9-4daf-be56-c0fc0a06b4d9"",
""path"": ""2DVector"",
""interactions"": """",
""processors"": """",
""groups"": """",
""action"": ""Move"",
""isComposite"": true,
""isPartOfComposite"": false
},
{
""name"": ""up"",
""id"": ""a3b11b4d-c157-417a-a38a-d89a034da71b"",
""path"": ""<Keyboard>/w"",
""interactions"": """",
""processors"": """",
""groups"": """",
""action"": ""Move"",
""isComposite"": false,
""isPartOfComposite"": true
},
{
""name"": ""down"",
""id"": ""0a0a8e30-3cf7-4889-aeb1-e8ddebcd944b"",
""path"": ""<Keyboard>/s"",
""interactions"": """",
""processors"": """",
""groups"": """",
""action"": ""Move"",
""isComposite"": false,
""isPartOfComposite"": true
},
{
""name"": ""left"",
""id"": ""2eeac222-1829-4c5b-9829-5e36b3fe989e"",
""path"": ""<Keyboard>/a"",
""interactions"": """",
""processors"": """",
""groups"": """",
""action"": ""Move"",
""isComposite"": false,
""isPartOfComposite"": true
},
{
""name"": ""right"",
""id"": ""89d5e951-0db5-4f1e-806b-f29f84507a34"",
""path"": ""<Keyboard>/d"",
""interactions"": """",
""processors"": """",
""groups"": """",
""action"": ""Move"",
""isComposite"": false,
""isPartOfComposite"": true
}
]
}
],
""controlSchemes"": []
}");
// Game
m_Game = asset.FindActionMap("Game", throwIfNotFound: true);
m_Game_Move = m_Game.FindAction("Move", throwIfNotFound: true);
}
~@PlayerInputActions()
{
UnityEngine.Debug.Assert(!m_Game.enabled, "This will cause a leak and performance issues, PlayerInputActions.Game.Disable() has not been called.");
}
/// <summary>
/// Destroys this asset and all associated <see cref="InputAction"/> instances.
/// </summary>
public void Dispose()
{
UnityEngine.Object.Destroy(asset);
}
/// <inheritdoc cref="UnityEngine.InputSystem.InputActionAsset.bindingMask" />
public InputBinding? bindingMask
{
get => asset.bindingMask;
set => asset.bindingMask = value;
}
/// <inheritdoc cref="UnityEngine.InputSystem.InputActionAsset.devices" />
public ReadOnlyArray<InputDevice>? devices
{
get => asset.devices;
set => asset.devices = value;
}
/// <inheritdoc cref="UnityEngine.InputSystem.InputActionAsset.controlSchemes" />
public ReadOnlyArray<InputControlScheme> controlSchemes => asset.controlSchemes;
/// <inheritdoc cref="UnityEngine.InputSystem.InputActionAsset.Contains(InputAction)" />
public bool Contains(InputAction action)
{
return asset.Contains(action);
}
/// <inheritdoc cref="UnityEngine.InputSystem.InputActionAsset.GetEnumerator()" />
public IEnumerator<InputAction> GetEnumerator()
{
return asset.GetEnumerator();
}
/// <inheritdoc cref="IEnumerable.GetEnumerator()" />
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <inheritdoc cref="UnityEngine.InputSystem.InputActionAsset.Enable()" />
public void Enable()
{
asset.Enable();
}
/// <inheritdoc cref="UnityEngine.InputSystem.InputActionAsset.Disable()" />
public void Disable()
{
asset.Disable();
}
/// <inheritdoc cref="UnityEngine.InputSystem.InputActionAsset.bindings" />
public IEnumerable<InputBinding> bindings => asset.bindings;
/// <inheritdoc cref="UnityEngine.InputSystem.InputActionAsset.FindAction(string, bool)" />
public InputAction FindAction(string actionNameOrId, bool throwIfNotFound = false)
{
return asset.FindAction(actionNameOrId, throwIfNotFound);
}
/// <inheritdoc cref="UnityEngine.InputSystem.InputActionAsset.FindBinding(InputBinding, out InputAction)" />
public int FindBinding(InputBinding bindingMask, out InputAction action)
{
return asset.FindBinding(bindingMask, out action);
}
// Game
private readonly InputActionMap m_Game;
private List<IGameActions> m_GameActionsCallbackInterfaces = new List<IGameActions>();
private readonly InputAction m_Game_Move;
/// <summary>
/// Provides access to input actions defined in input action map "Game".
/// </summary>
public struct GameActions
{
private @PlayerInputActions m_Wrapper;
/// <summary>
/// Construct a new instance of the input action map wrapper class.
/// </summary>
public GameActions(@PlayerInputActions wrapper) { m_Wrapper = wrapper; }
/// <summary>
/// Provides access to the underlying input action "Game/Move".
/// </summary>
public InputAction @Move => m_Wrapper.m_Game_Move;
/// <summary>
/// Provides access to the underlying input action map instance.
/// </summary>
public InputActionMap Get() { return m_Wrapper.m_Game; }
/// <inheritdoc cref="UnityEngine.InputSystem.InputActionMap.Enable()" />
public void Enable() { Get().Enable(); }
/// <inheritdoc cref="UnityEngine.InputSystem.InputActionMap.Disable()" />
public void Disable() { Get().Disable(); }
/// <inheritdoc cref="UnityEngine.InputSystem.InputActionMap.enabled" />
public bool enabled => Get().enabled;
/// <summary>
/// Implicitly converts an <see ref="GameActions" /> to an <see ref="InputActionMap" /> instance.
/// </summary>
public static implicit operator InputActionMap(GameActions set) { return set.Get(); }
/// <summary>
/// Adds <see cref="InputAction.started"/>, <see cref="InputAction.performed"/> and <see cref="InputAction.canceled"/> callbacks provided via <param cref="instance" /> on all input actions contained in this map.
/// </summary>
/// <param name="instance">Callback instance.</param>
/// <remarks>
/// If <paramref name="instance" /> is <c>null</c> or <paramref name="instance"/> have already been added this method does nothing.
/// </remarks>
/// <seealso cref="GameActions" />
public void AddCallbacks(IGameActions instance)
{
if (instance == null || m_Wrapper.m_GameActionsCallbackInterfaces.Contains(instance)) return;
m_Wrapper.m_GameActionsCallbackInterfaces.Add(instance);
@Move.started += instance.OnMove;
@Move.performed += instance.OnMove;
@Move.canceled += instance.OnMove;
}
/// <summary>
/// Removes <see cref="InputAction.started"/>, <see cref="InputAction.performed"/> and <see cref="InputAction.canceled"/> callbacks provided via <param cref="instance" /> on all input actions contained in this map.
/// </summary>
/// <remarks>
/// Calling this method when <paramref name="instance" /> have not previously been registered has no side-effects.
/// </remarks>
/// <seealso cref="GameActions" />
private void UnregisterCallbacks(IGameActions instance)
{
@Move.started -= instance.OnMove;
@Move.performed -= instance.OnMove;
@Move.canceled -= instance.OnMove;
}
/// <summary>
/// Unregisters <param cref="instance" /> and unregisters all input action callbacks via <see cref="GameActions.UnregisterCallbacks(IGameActions)" />.
/// </summary>
/// <seealso cref="GameActions.UnregisterCallbacks(IGameActions)" />
public void RemoveCallbacks(IGameActions instance)
{
if (m_Wrapper.m_GameActionsCallbackInterfaces.Remove(instance))
UnregisterCallbacks(instance);
}
/// <summary>
/// Replaces all existing callback instances and previously registered input action callbacks associated with them with callbacks provided via <param cref="instance" />.
/// </summary>
/// <remarks>
/// If <paramref name="instance" /> is <c>null</c>, calling this method will only unregister all existing callbacks but not register any new callbacks.
/// </remarks>
/// <seealso cref="GameActions.AddCallbacks(IGameActions)" />
/// <seealso cref="GameActions.RemoveCallbacks(IGameActions)" />
/// <seealso cref="GameActions.UnregisterCallbacks(IGameActions)" />
public void SetCallbacks(IGameActions instance)
{
foreach (var item in m_Wrapper.m_GameActionsCallbackInterfaces)
UnregisterCallbacks(item);
m_Wrapper.m_GameActionsCallbackInterfaces.Clear();
AddCallbacks(instance);
}
}
/// <summary>
/// Provides a new <see cref="GameActions" /> instance referencing this action map.
/// </summary>
public GameActions @Game => new GameActions(this);
/// <summary>
/// Interface to implement callback methods for all input action callbacks associated with input actions defined by "Game" which allows adding and removing callbacks.
/// </summary>
/// <seealso cref="GameActions.AddCallbacks(IGameActions)" />
/// <seealso cref="GameActions.RemoveCallbacks(IGameActions)" />
public interface IGameActions
{
/// <summary>
/// Method invoked when associated input action "Move" is either <see cref="UnityEngine.InputSystem.InputAction.started" />, <see cref="UnityEngine.InputSystem.InputAction.performed" /> or <see cref="UnityEngine.InputSystem.InputAction.canceled" />.
/// </summary>
/// <seealso cref="UnityEngine.InputSystem.InputAction.started" />
/// <seealso cref="UnityEngine.InputSystem.InputAction.performed" />
/// <seealso cref="UnityEngine.InputSystem.InputAction.canceled" />
void OnMove(InputAction.CallbackContext context);
}
}

View File

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

View File

@ -122,7 +122,92 @@ NavMeshSettings:
debug:
m_Flags: 0
m_NavMeshData: {fileID: 0}
--- !u!1 &2105673978
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2105673979}
- component: {fileID: 2105673980}
m_Layer: 0
m_Name: GameObject
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &2105673979
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2105673978}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 5000, y: 5000, z: 5000}
m_ConstrainProportionsScale: 1
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!212 &2105673980
SpriteRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2105673978}
m_Enabled: 1
m_CastShadows: 0
m_ReceiveShadows: 0
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 0
m_RayTraceProcedural: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 0
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_Sprite: {fileID: 21300000, guid: ab45c3f613f388d43bbf43ec05eb92e2, type: 3}
m_Color: {r: 0, g: 0, b: 0, a: 1}
m_FlipX: 0
m_FlipY: 0
m_DrawMode: 0
m_Size: {x: 8, y: 8}
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 0
--- !u!1660057539 &9223372036854775807
SceneRoots:
m_ObjectHideFlags: 0
m_Roots: []
m_Roots:
- {fileID: 2105673979}

View File

@ -1,104 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-32820517437979890
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 0b2db86121404754db890f4c8dfe81b2, type: 3}
m_Name: Bloom
m_EditorClassIdentifier:
active: 1
skipIterations:
m_OverrideState: 0
m_Value: 1
threshold:
m_OverrideState: 0
m_Value: 0.9
intensity:
m_OverrideState: 1
m_Value: 1
scatter:
m_OverrideState: 0
m_Value: 0.7
clamp:
m_OverrideState: 0
m_Value: 65472
tint:
m_OverrideState: 0
m_Value: {r: 1, g: 1, b: 1, a: 1}
highQualityFiltering:
m_OverrideState: 0
m_Value: 0
downscale:
m_OverrideState: 0
m_Value: 0
maxIterations:
m_OverrideState: 0
m_Value: 6
dirtTexture:
m_OverrideState: 0
m_Value: {fileID: 0}
dimension: 1
dirtIntensity:
m_OverrideState: 0
m_Value: 0
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d7fd9488000d3734a9e00ee676215985, type: 3}
m_Name: GlobalVolumeProfile
m_EditorClassIdentifier:
components:
- {fileID: 1881579288749618558}
- {fileID: -32820517437979890}
--- !u!114 &1881579288749618558
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 97c23e3b12dc18c42a140437e53d3951, type: 3}
m_Name: Tonemapping
m_EditorClassIdentifier:
active: 1
mode:
m_OverrideState: 1
m_Value: 1
neutralHDRRangeReductionMode:
m_OverrideState: 0
m_Value: 2
acesPreset:
m_OverrideState: 0
m_Value: 3
hueShiftAmount:
m_OverrideState: 0
m_Value: 0
detectPaperWhite:
m_OverrideState: 0
m_Value: 0
paperWhite:
m_OverrideState: 0
m_Value: 300
detectBrightnessLimits:
m_OverrideState: 0
m_Value: 1
minNits:
m_OverrideState: 0
m_Value: 0.005
maxNits:
m_OverrideState: 0
m_Value: 1000

View File

@ -197,173 +197,6 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 70728207}
m_CullTransparentMesh: 1
--- !u!1 &203844586
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 203844589}
- component: {fileID: 203844588}
- component: {fileID: 203844587}
m_Layer: 0
m_Name: Directional Light
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &203844587
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 203844586}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Version: 3
m_UsePipelineSettings: 1
m_AdditionalLightsShadowResolutionTier: 2
m_LightLayerMask: 1
m_RenderingLayers: 1
m_CustomShadowLayers: 0
m_ShadowLayerMask: 1
m_ShadowRenderingLayers: 1
m_LightCookieSize: {x: 1, y: 1}
m_LightCookieOffset: {x: 0, y: 0}
m_SoftShadowQuality: 1
--- !u!108 &203844588
Light:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 203844586}
m_Enabled: 1
serializedVersion: 10
m_Type: 1
m_Shape: 0
m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1}
m_Intensity: 1
m_Range: 10
m_SpotAngle: 30
m_InnerSpotAngle: 21.80208
m_CookieSize: 10
m_Shadows:
m_Type: 2
m_Resolution: -1
m_CustomResolution: -1
m_Strength: 1
m_Bias: 0.05
m_NormalBias: 0.4
m_NearPlane: 0.2
m_CullingMatrixOverride:
e00: 1
e01: 0
e02: 0
e03: 0
e10: 0
e11: 1
e12: 0
e13: 0
e20: 0
e21: 0
e22: 1
e23: 0
e30: 0
e31: 0
e32: 0
e33: 1
m_UseCullingMatrixOverride: 0
m_Cookie: {fileID: 0}
m_DrawHalo: 0
m_Flare: {fileID: 0}
m_RenderMode: 0
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_RenderingLayerMask: 1
m_Lightmapping: 4
m_LightShadowCasterMode: 0
m_AreaSize: {x: 1, y: 1}
m_BounceIntensity: 1
m_ColorTemperature: 6570
m_UseColorTemperature: 0
m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
m_UseBoundingSphereOverride: 0
m_UseViewFrustumForShadowCasterCull: 1
m_ShadowRadius: 0
m_ShadowAngle: 0
--- !u!4 &203844589
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 203844586}
serializedVersion: 2
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
m_LocalPosition: {x: 0, y: 3, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
--- !u!1 &1160234425
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1160234427}
- component: {fileID: 1160234426}
m_Layer: 0
m_Name: Global Volume
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &1160234426
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1160234425}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 172515602e62fb746b5d573b38a5fe58, type: 3}
m_Name:
m_EditorClassIdentifier:
m_IsGlobal: 1
priority: 0
blendDistance: 0
weight: 1
sharedProfile: {fileID: 11400000, guid: dde468eff36cc784fa023279ae4d4d33, type: 2}
--- !u!4 &1160234427
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1160234425}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1779441773
GameObject:
m_ObjectHideFlags: 0
@ -470,6 +303,4 @@ RectTransform:
SceneRoots:
m_ObjectHideFlags: 0
m_Roots:
- {fileID: 203844589}
- {fileID: 1160234427}
- {fileID: 1779441777}

View File

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

View File

@ -1,51 +0,0 @@
using UnityEngine;
namespace Components
{
public class InputComponent : MonoBehaviour
{
private PlayerInputActions _inputActions;
private bool _isListening = false;
private Vector3 _direction = Vector3.zero;
public Vector3 Direction => _direction;
public void OnInit()
{
_inputActions = new();
}
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
if (_isListening && _inputActions != null)
{
ReadInput();
}
}
public void OnReset()
{
_inputActions = null;
_isListening = false;
}
private void ReadInput()
{
Vector2 rawDir = _inputActions.Game.Move.ReadValue<Vector2>();
_direction = new Vector3(rawDir.x, 0, rawDir.y);
}
public void SetListening(bool isListening)
{
_isListening = isListening;
if (_isListening)
{
_inputActions.Enable();
}
else
{
_inputActions.Disable();
}
}
}
}

View File

@ -1,41 +0,0 @@
using UnityEngine;
namespace Components
{
public class MovementComponent : MonoBehaviour
{
private bool _isMoving = false;
private Vector3 _direction = Vector3.forward;
private float _speed = 0;
private Transform _cachedTransform;
public void OnInit(float speed, Transform transform)
{
_speed = speed;
_cachedTransform = transform;
}
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
if (_isMoving && _cachedTransform != null)
{
Move(elapseSeconds);
}
}
public void OnReset()
{
_speed = 0;
_cachedTransform = null;
_isMoving = false;
}
private void Move(float deltaTime = 0)
{
_cachedTransform.Translate(_direction * _speed * deltaTime);
}
public void SetMove(bool isMoving) => _isMoving = isMoving;
public void SetDirection(Vector3 direction) => _direction = direction;
}
}

View File

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using DataTable;
using Definition;
@ -14,22 +15,45 @@ namespace CustomComponent
[DisallowMultipleComponent]
public class DialogComponent : GameFrameworkComponent
{
[Serializable]
private sealed class SpeakerPortraitBinding
{
[SerializeField] private string _speakerId = string.Empty;
[SerializeField] private List<Sprite> _portraits = new List<Sprite>();
public string SpeakerId => _speakerId;
public List<Sprite> Portraits => _portraits;
}
#region Property
private const int DialogChapterDivisor = 1000;
private const int LineChapterDivisor = 100000000;
private const int LineDialogDivisor = 100000;
[Header("角色立绘配置(拖拽 Sprite")]
[SerializeField] private List<SpeakerPortraitBinding> _speakerPortraitBindings = new List<SpeakerPortraitBinding>();
private readonly Dictionary<int, DRDialog> _dialogMap = new();
private readonly Dictionary<int, List<DRDialogLine>> _dialogLinesMap = new();
private readonly Dictionary<int, int> _dialogFirstLineIdMap = new();
private static readonly HashSet<string> NonPortraitSpeakerIds = new(System.StringComparer.OrdinalIgnoreCase)
{
"Other",
"Subtitle",
"Time"
};
private IDataTable<DRDialog> _dtDialog;
private IDataTable<DRDialogLine> _dtDialogLine;
private DialogFormController _formController;
private DialogFormContext _formContext;
private int _pendingStartDialogId;
private int _activeChapterTitleRequestId;
private int _nextChapterTitleRequestId = 1;
private bool _chapterTitleShownAfterInit;
private int _currentChapterId;
private int _currentLineIndex = -1;
@ -46,6 +70,31 @@ namespace CustomComponent
public int CurrentLineId => _formContext != null ? _formContext.CurrentLineId : 0;
public int GetSpeakerPortraitCount(string speakerId)
{
if (string.IsNullOrWhiteSpace(speakerId))
{
return 0;
}
SpeakerPortraitBinding binding = FindSpeakerPortraitBinding(speakerId);
if (binding == null || binding.Portraits == null)
{
return 0;
}
int validCount = 0;
for (int i = 0; i < binding.Portraits.Count; i++)
{
if (binding.Portraits[i] != null)
{
validCount++;
}
}
return validCount;
}
#endregion
@ -54,6 +103,7 @@ namespace CustomComponent
GameEntry.Event.Subscribe(DialogNextLineRequestEventArgs.EventId, OnDialogNextLineRequest);
GameEntry.Event.Subscribe(DialogSkipRequestEventArgs.EventId, OnDialogSkipRequest);
GameEntry.Event.Subscribe(DialogStopRequestEventArgs.EventId, OnDialogStopRequest);
GameEntry.Event.Subscribe(ChapterTitleCompletedEventArgs.EventId, OnChapterTitleCompleted);
}
private void OnDestroy()
@ -61,6 +111,7 @@ namespace CustomComponent
GameEntry.Event.Unsubscribe(DialogNextLineRequestEventArgs.EventId, OnDialogNextLineRequest);
GameEntry.Event.Unsubscribe(DialogSkipRequestEventArgs.EventId, OnDialogSkipRequest);
GameEntry.Event.Unsubscribe(DialogStopRequestEventArgs.EventId, OnDialogStopRequest);
GameEntry.Event.Unsubscribe(ChapterTitleCompletedEventArgs.EventId, OnChapterTitleCompleted);
}
public bool Init(int chapterId)
@ -159,10 +210,14 @@ namespace CustomComponent
return false;
}
PreloadChapterPortraitSprites();
EnsureFormController();
_formContext = null;
_currentChapterId = chapterId;
_currentLineIndex = -1;
_pendingStartDialogId = 0;
_activeChapterTitleRequestId = 0;
_chapterTitleShownAfterInit = false;
_isInitialized = true;
_isPlaying = false;
return true;
@ -196,6 +251,24 @@ namespace CustomComponent
return false;
}
if (_activeChapterTitleRequestId > 0)
{
_pendingStartDialogId = dialogId;
return true;
}
if (!_chapterTitleShownAfterInit)
{
_chapterTitleShownAfterInit = true;
_pendingStartDialogId = dialogId;
if (TryOpenChapterTitle(dialogRow))
{
return true;
}
_pendingStartDialogId = 0;
}
if (_isPlaying)
{
StopDialog();
@ -267,6 +340,21 @@ namespace CustomComponent
public void StopDialog()
{
_pendingStartDialogId = 0;
if (_activeChapterTitleRequestId > 0)
{
_activeChapterTitleRequestId = 0;
if (GameEntry.UI != null)
{
UGuiForm chapterTitleForm = GameEntry.UI.GetUIForm(UIFormId.ChapterTitleForm);
if (chapterTitleForm != null)
{
GameEntry.UI.CloseUIForm(chapterTitleForm.UIForm);
}
}
}
if (!_isPlaying)
{
return;
@ -286,6 +374,9 @@ namespace CustomComponent
_formContext = null;
_currentChapterId = 0;
_currentLineIndex = -1;
_pendingStartDialogId = 0;
_activeChapterTitleRequestId = 0;
_chapterTitleShownAfterInit = false;
_isInitialized = false;
_isPlaying = false;
}
@ -368,6 +459,138 @@ namespace CustomComponent
_formContext.IsLastLine = lineIndex >= totalLines - 1;
}
private void PreloadChapterPortraitSprites()
{
if (GameEntry.SpriteCache == null)
{
return;
}
HashSet<string> speakerIds = new HashSet<string>(System.StringComparer.OrdinalIgnoreCase);
foreach (KeyValuePair<int, List<DRDialogLine>> pair in _dialogLinesMap)
{
List<DRDialogLine> lines = pair.Value;
if (lines == null)
{
continue;
}
for (int i = 0; i < lines.Count; i++)
{
DRDialogLine line = lines[i];
if (line == null || !IsHumanSpeakerId(line.SpeakerId))
{
continue;
}
speakerIds.Add(line.SpeakerId);
}
}
if (speakerIds.Count == 0)
{
return;
}
foreach (string speakerId in speakerIds)
{
RegisterSpeakerPortraitsToCache(speakerId);
}
}
private void RegisterSpeakerPortraitsToCache(string speakerId)
{
SpeakerPortraitBinding binding = FindSpeakerPortraitBinding(speakerId);
if (binding == null || binding.Portraits == null || binding.Portraits.Count == 0)
{
Log.Warning("Portrait preload skipped. Missing speaker portrait binding. speakerId='{0}'.", speakerId);
return;
}
int portraitIndex = 1;
for (int i = 0; i < binding.Portraits.Count; i++)
{
Sprite portrait = binding.Portraits[i];
if (portrait == null)
{
continue;
}
string cacheKey = $"Characters/{speakerId}-{portraitIndex}.png";
GameEntry.SpriteCache.RegisterSprite(cacheKey, portrait, true);
portraitIndex++;
}
}
private SpeakerPortraitBinding FindSpeakerPortraitBinding(string speakerId)
{
if (string.IsNullOrWhiteSpace(speakerId) || _speakerPortraitBindings == null)
{
return null;
}
for (int i = 0; i < _speakerPortraitBindings.Count; i++)
{
SpeakerPortraitBinding binding = _speakerPortraitBindings[i];
if (binding == null || string.IsNullOrWhiteSpace(binding.SpeakerId))
{
continue;
}
if (string.Equals(binding.SpeakerId.Trim(), speakerId.Trim(), StringComparison.OrdinalIgnoreCase))
{
return binding;
}
}
return null;
}
private static bool IsHumanSpeakerId(string speakerId)
{
if (string.IsNullOrWhiteSpace(speakerId))
{
return false;
}
return !NonPortraitSpeakerIds.Contains(speakerId);
}
private bool TryOpenChapterTitle(DRDialog dialogRow)
{
if (GameEntry.UI == null)
{
return false;
}
int requestId = _nextChapterTitleRequestId++;
string subtitle = dialogRow == null || string.IsNullOrWhiteSpace(dialogRow.Title)
? string.Empty
: dialogRow.Title.Trim();
ChapterTitleFormContext context = new ChapterTitleFormContext
{
ChapterId = _currentChapterId,
RequestId = requestId,
Title = BuildChapterTitle(_currentChapterId),
Subtitle = subtitle
};
int? formSerialId = GameEntry.UI.OpenUIForm(UIFormId.ChapterTitleForm, context);
if (!formSerialId.HasValue)
{
Log.Warning("Open chapter title form failed. chapterId='{0}'.", _currentChapterId.ToString());
return false;
}
_activeChapterTitleRequestId = requestId;
return true;
}
private static string BuildChapterTitle(int chapterId)
{
return chapterId > 0 ? $"第{chapterId}章" : string.Empty;
}
private static int ParseChapterIdFromDialogId(int dialogId)
{
return dialogId / DialogChapterDivisor;
@ -415,6 +638,29 @@ namespace CustomComponent
StopDialog();
}
private void OnChapterTitleCompleted(object sender, GameEventArgs e)
{
if (!(e is ChapterTitleCompletedEventArgs args))
{
return;
}
if (args.RequestId <= 0 || args.RequestId != _activeChapterTitleRequestId)
{
return;
}
_activeChapterTitleRequestId = 0;
int pendingDialogId = _pendingStartDialogId;
_pendingStartDialogId = 0;
if (pendingDialogId <= 0 || !_isInitialized)
{
return;
}
StartDialog(pendingDialogId);
}
#endregion
}
}

View File

@ -14,58 +14,191 @@ namespace CustomComponent
[SerializeField] private Vector2 _defaultPivot = new(0.5f, 0.5f);
private Dictionary<string, Sprite> _spriteCache;
private Dictionary<string, List<Action<Sprite>>> _pendingCallbacks;
private HashSet<string> _loadingSpriteAssets;
private ResourceComponent _resource;
void Start()
{
_spriteCache = new Dictionary<string, Sprite>();
_resource = GameEntry.Resource;
EnsureRuntimeCaches();
}
private void EnsureRuntimeCaches()
{
_spriteCache ??= new Dictionary<string, Sprite>();
_pendingCallbacks ??= new Dictionary<string, List<Action<Sprite>>>();
_loadingSpriteAssets ??= new HashSet<string>();
_resource ??= GameEntry.Resource;
}
public bool TryGetCachedSprite(string assetName, out Sprite sprite)
{
EnsureRuntimeCaches();
if (string.IsNullOrWhiteSpace(assetName))
{
sprite = null;
return false;
}
return _spriteCache.TryGetValue(assetName, out sprite);
}
public void GetSprite(string assetName, Action<Sprite> callback)
{
EnsureRuntimeCaches();
if (string.IsNullOrWhiteSpace(assetName))
{
callback?.Invoke(null);
return;
}
if (_spriteCache.TryGetValue(assetName, out var sprite))
{
callback?.Invoke(sprite);
return;
}
else
if (callback != null)
{
_resource.LoadAsset
(
AssetUtility.GetUISpriteAsset(assetName),
Constant.AssetPriority.UIFormAsset,
new LoadAssetCallbacks(
(resourcePath, asset, duration, userData) =>
AddPendingCallback(assetName, callback);
}
if (_loadingSpriteAssets.Contains(assetName))
{
return;
}
_loadingSpriteAssets.Add(assetName);
StartLoadSprite(assetName);
}
public void PreloadSprite(string assetName)
{
EnsureRuntimeCaches();
if (string.IsNullOrWhiteSpace(assetName) || _spriteCache.ContainsKey(assetName) ||
_loadingSpriteAssets.Contains(assetName))
{
return;
}
_loadingSpriteAssets.Add(assetName);
StartLoadSprite(assetName);
}
public void PreloadSprites(IEnumerable<string> assetNames)
{
EnsureRuntimeCaches();
if (assetNames == null)
{
return;
}
foreach (string assetName in assetNames)
{
PreloadSprite(assetName);
}
}
public void RegisterSprite(string assetName, Sprite sprite, bool overwrite = true)
{
EnsureRuntimeCaches();
if (string.IsNullOrWhiteSpace(assetName) || sprite == null)
{
return;
}
if (!overwrite && _spriteCache.ContainsKey(assetName))
{
return;
}
_spriteCache[assetName] = sprite;
InvokeAndClearPendingCallbacks(assetName, sprite);
_loadingSpriteAssets.Remove(assetName);
}
private void StartLoadSprite(string assetName)
{
EnsureRuntimeCaches();
if (_resource == null)
{
InvokeAndClearPendingCallbacks(assetName, null);
_loadingSpriteAssets.Remove(assetName);
return;
}
_resource.LoadAsset
(
AssetUtility.GetUISpriteAsset(assetName),
Constant.AssetPriority.UIFormAsset,
new LoadAssetCallbacks(
(resourcePath, asset, duration, userData) =>
{
Texture2D texture = asset as Texture2D;
Sprite loadedSprite = null;
if (texture != null)
{
Log.Debug(resourcePath);
Texture2D texture = asset as Texture2D;
if (texture != null)
{
Sprite newSprite = Sprite.Create(
texture,
new Rect(0, 0, texture.width, texture.height),
_defaultPivot,
_pixelsPerUnit);
_spriteCache.Add(assetName, newSprite);
callback?.Invoke(newSprite);
}
},
(resourcePath, status, errorMessage, userData) =>
{
Log.Error("Can not load icon '{0}' from '{1}' with error message '{2}'.",
assetName,
resourcePath,
errorMessage);
loadedSprite = Sprite.Create(
texture,
new Rect(0, 0, texture.width, texture.height),
_defaultPivot,
_pixelsPerUnit);
_spriteCache[assetName] = loadedSprite;
}
)
);
else
{
Log.Warning("Load sprite succeeded but asset is not Texture2D. AssetName='{0}', Path='{1}'.",
assetName, resourcePath);
}
InvokeAndClearPendingCallbacks(assetName, loadedSprite);
_loadingSpriteAssets.Remove(assetName);
},
(resourcePath, status, errorMessage, userData) =>
{
Log.Error("Can not load icon '{0}' from '{1}' with error message '{2}'.",
assetName,
resourcePath,
errorMessage);
InvokeAndClearPendingCallbacks(assetName, null);
_loadingSpriteAssets.Remove(assetName);
}
)
);
}
private void AddPendingCallback(string assetName, Action<Sprite> callback)
{
EnsureRuntimeCaches();
if (!_pendingCallbacks.TryGetValue(assetName, out List<Action<Sprite>> callbackList))
{
callbackList = new List<Action<Sprite>>();
_pendingCallbacks.Add(assetName, callbackList);
}
callbackList.Add(callback);
}
private void InvokeAndClearPendingCallbacks(string assetName, Sprite sprite)
{
EnsureRuntimeCaches();
if (!_pendingCallbacks.TryGetValue(assetName, out List<Action<Sprite>> callbackList))
{
return;
}
_pendingCallbacks.Remove(assetName);
for (int i = 0; i < callbackList.Count; i++)
{
callbackList[i]?.Invoke(sprite);
}
}
private void OnDestroy()
{
_spriteCache.Clear();
_spriteCache?.Clear();
_pendingCallbacks?.Clear();
_loadingSpriteAssets?.Clear();
_resource = null;
}
}

View File

@ -9,5 +9,7 @@ namespace Definition.Enum
None,
Shake,
Blink,
EntityCue,
EntityCueClose,
}
}

View File

@ -1,14 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Definition.Enum
{
public enum ExpressionType
{
None,
Normal,
Shock,
Angry,
}
}

View File

@ -59,5 +59,9 @@ namespace UI
MainOverlayForm = 107,
AIChatForm = 108,
ChapterTitleForm = 109,
AIChatEntryForm = 110,
}
}

View File

@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: f7c4b051e63f0c2418a55e8817bf4ba9
folderAsset: yes
timeCreated: 1528026151
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: 58efe0589d7e90f498c8bcbbf94c6be4
folderAsset: yes
timeCreated: 1528026151
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,62 +0,0 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using System;
using UnityEngine;
namespace Entity.EntityData
{
[Serializable]
public abstract class EntityDataBase
{
[SerializeField]
private int m_Id = 0;
[SerializeField]
private int m_TypeId = 0;
[SerializeField]
private Vector3 m_Position = Vector3.zero;
[SerializeField]
private Quaternion m_Rotation = Quaternion.identity;
public EntityDataBase(int entityId, int typeId)
{
m_Id = entityId;
m_TypeId = typeId;
}
/// <summary>
/// 实体编号。
/// </summary>
public int Id => m_Id;
/// <summary>
/// 实体类型编号。
/// </summary>
public int TypeId => m_TypeId;
/// <summary>
/// 实体位置。
/// </summary>
public Vector3 Position
{
get => m_Position;
set => m_Position = value;
}
/// <summary>
/// 实体朝向。
/// </summary>
public Quaternion Rotation
{
get => m_Rotation;
set => m_Rotation = value;
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: afb91fe3ed8105e4a96b7bd5917ecc2b
timeCreated: 1528026152
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,70 +0,0 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using GameFramework.DataTable;
using System;
using CustomUtility;
using DataTable;
using Entity;
using Entity.EntityData;
using UnityGameFramework.Runtime;
namespace StarForce
{
public static class EntityExtension
{
private static int s_SerialId = 0;
public static EntityBase GetGameEntity(this EntityComponent entityComponent, int entityId)
{
UnityGameFramework.Runtime.Entity entity = entityComponent.GetEntity(entityId);
if (entity == null)
{
return null;
}
return (EntityBase)entity.Logic;
}
public static void HideEntity(this EntityComponent entityComponent, EntityBase entity)
{
entityComponent.HideEntity(entity.Entity);
}
public static void AttachEntity(this EntityComponent entityComponent, EntityBase entityBase, int ownerId,
string parentTransformPath = null, object userData = null)
{
entityComponent.AttachEntity(entityBase.Entity, ownerId, parentTransformPath, userData);
}
private static void ShowEntity(this EntityComponent entityComponent, Type logicType, string entityGroup,
int priority, EntityDataBase data)
{
if (data == null)
{
Log.Warning("Data is invalid.");
return;
}
IDataTable<DREntity> dtEntity = GameEntry.DataTable.GetDataTable<DREntity>();
DREntity drEntity = dtEntity.GetDataRow(data.TypeId);
if (drEntity == null)
{
Log.Warning("Can not load entity id '{0}' from data table.", data.TypeId.ToString());
return;
}
entityComponent.ShowEntity(data.Id, logicType, AssetUtility.GetEntityAsset(drEntity.AssetName), entityGroup,
priority, data);
}
public static int GenerateSerialId(this EntityComponent entityComponent)
{
return --s_SerialId;
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 626632079980ad34595c3b43a062a664
timeCreated: 1528026153
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: 74fbdf043ed3bc141a0a0b09756bc7d2
folderAsset: yes
timeCreated: 1528026153
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,122 +0,0 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using Entity.EntityData;
using GameFramework;
using StarForce;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace Entity
{
public abstract class EntityBase : EntityLogic
{
[SerializeField] private EntityDataBase _entityData = null;
public int Id => Entity.Id;
public Animation CachedAnimation
{
get;
private set;
}
#if UNITY_2017_3_OR_NEWER
protected override void OnInit(object userData)
#else
protected internal override void OnInit(object userData)
#endif
{
base.OnInit(userData);
CachedAnimation = GetComponent<Animation>();
}
#if UNITY_2017_3_OR_NEWER
protected override void OnRecycle()
#else
protected internal override void OnRecycle()
#endif
{
base.OnRecycle();
}
#if UNITY_2017_3_OR_NEWER
protected override void OnShow(object userData)
#else
protected internal override void OnShow(object userData)
#endif
{
base.OnShow(userData);
_entityData = userData as EntityDataBase;
if (_entityData == null)
{
Log.Error("Entity data is invalid.");
return;
}
Name = Utility.Text.Format("[Entity {0}]", Id);
CachedTransform.localPosition = _entityData.Position;
CachedTransform.localRotation = _entityData.Rotation;
CachedTransform.localScale = Vector3.one;
}
#if UNITY_2017_3_OR_NEWER
protected override void OnHide(bool isShutdown, object userData)
#else
protected internal override void OnHide(bool isShutdown, object userData)
#endif
{
base.OnHide(isShutdown, userData);
}
#if UNITY_2017_3_OR_NEWER
protected override void OnAttached(EntityLogic childEntity, Transform parentTransform, object userData)
#else
protected internal override void OnAttached(EntityLogic childEntity, Transform parentTransform, object userData)
#endif
{
base.OnAttached(childEntity, parentTransform, userData);
}
#if UNITY_2017_3_OR_NEWER
protected override void OnDetached(EntityLogic childEntity, object userData)
#else
protected internal override void OnDetached(EntityLogic childEntity, object userData)
#endif
{
base.OnDetached(childEntity, userData);
}
#if UNITY_2017_3_OR_NEWER
protected override void OnAttachTo(EntityLogic parentEntity, Transform parentTransform, object userData)
#else
protected internal override void OnAttachTo(EntityLogic parentEntity, Transform parentTransform, object userData)
#endif
{
base.OnAttachTo(parentEntity, parentTransform, userData);
}
#if UNITY_2017_3_OR_NEWER
protected override void OnDetachFrom(EntityLogic parentEntity, object userData)
#else
protected internal override void OnDetachFrom(EntityLogic parentEntity, object userData)
#endif
{
base.OnDetachFrom(parentEntity, userData);
}
#if UNITY_2017_3_OR_NEWER
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
#else
protected internal override void OnUpdate(float elapseSeconds, float realElapseSeconds)
#endif
{
base.OnUpdate(elapseSeconds, realElapseSeconds);
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 878fbbe82b4bde944812bcb7c964cff8
timeCreated: 1528026154
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,35 @@
using GameFramework;
using GameFramework.Event;
namespace Event
{
public class ChapterTitleCompletedEventArgs : GameEventArgs
{
public static readonly int EventId = typeof(ChapterTitleCompletedEventArgs).GetHashCode();
public override int Id => EventId;
public int ChapterId { get; private set; }
public int RequestId { get; private set; }
public ChapterTitleCompletedEventArgs()
{
ChapterId = 0;
RequestId = 0;
}
public static ChapterTitleCompletedEventArgs Create(int chapterId, int requestId)
{
ChapterTitleCompletedEventArgs args = ReferencePool.Acquire<ChapterTitleCompletedEventArgs>();
args.ChapterId = chapterId;
args.RequestId = requestId;
return args;
}
public override void Clear()
{
ChapterId = 0;
RequestId = 0;
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 729ec3f897dd27948a735906cd0c185a
guid: 2de1f87831fa4a81ad93a2f5277cfaa2
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -23,7 +23,6 @@ namespace Procedure
{
public static readonly string[] DataTableNames = new string[]
{
"Entity",
"BGM",
"Scene",
"UIForm",

View File

@ -31,6 +31,7 @@ namespace Procedure
protected override void OnEnter(ProcedureOwner procedureOwner)
{
base.OnEnter(procedureOwner);
AIChatEntryRuntime.EnsureOpen();
GameEntry.Dialog.Init(1);

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using GameFramework.Fsm;
using GameFramework.Procedure;
using UnityEngine;
using UI;
namespace Procedure
{
@ -13,6 +14,7 @@ namespace Procedure
protected override void OnEnter(IFsm<IProcedureManager> procedureOwner)
{
base.OnEnter(procedureOwner);
AIChatEntryRuntime.EnsureOpen();
GameEntry.Dialog.Init(1);
GameEntry.Dialog.StartDialog(1001);

View File

@ -61,6 +61,7 @@ namespace Procedure
protected override void OnEnter(IFsm<IProcedureManager> procedureOwner)
{
base.OnEnter(procedureOwner);
AIChatEntryRuntime.EnsureOpen();
var e = GameEntry.Event;
e.Subscribe(MenuStartEventArgs.EventId, MenuStart);

View File

@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: 2ff69ece58f4d3f4e8cf9a40f813e745
folderAsset: yes
timeCreated: 1528026159
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,30 +0,0 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using Entity;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace StarForce
{
public class HideByBoundary : MonoBehaviour
{
private void OnTriggerExit(Collider other)
{
GameObject go = other.gameObject;
EntityBase entity = go.GetComponent<EntityBase>();
if (entity == null)
{
Log.Warning("Unknown GameObject '{0}', you must use entity only.", go.name);
Destroy(go);
return;
}
GameEntry.Entity.HideEntity(entity);
}
}
}

View File

@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 649621152579e224a955822793b63aca
timeCreated: 1528026159
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,7 +1,6 @@
using CustomUtility;
using DataTable;
using Definition;
using Entity;
using GameFramework.DataTable;
using GameFramework.Sound;
using UnityGameFramework.Runtime;

View File

@ -0,0 +1,28 @@
namespace UI
{
public static class AIChatEntryRuntime
{
private static AIChatEntryFormController s_entryController;
private static AIChatEntryFormContext s_entryContext;
public static void EnsureOpen()
{
if (GameEntry.UI == null)
{
return;
}
if (s_entryController == null)
{
s_entryController = new AIChatEntryFormController();
}
if (s_entryContext == null)
{
s_entryContext = new AIChatEntryFormContext();
}
s_entryController.OpenUI(s_entryContext);
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: f754cc5f339dcfa4aaec3aa32260c4b8
guid: 8f5f8c4efc7e47a8b0354c7f89fef0b9
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -0,0 +1,8 @@
namespace UI
{
public sealed class AIChatEntryFormContext : UIContext
{
public AIChatEntryFormController Controller;
public bool IsChatOpened;
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 9baf591df06b9bb44ba5313457ba84b3
guid: 0b5fd3ce4a3e4e7a8ed2ab9f9472e0a1
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -0,0 +1,10 @@
namespace UI
{
public class ChapterTitleFormContext : UIContext
{
public int ChapterId = 0;
public int RequestId = 0;
public string Title = string.Empty;
public string Subtitle = string.Empty;
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 174e046d822a14d49843dc415067f13c
guid: d2f2414b0ec34d2cb7a64c46c18cb48c
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -0,0 +1,188 @@
using System.Collections.Generic;
using GameFramework.Event;
using UnityGameFramework.Runtime;
namespace UI
{
public sealed class AIChatEntryFormController : IFormController<AIChatEntryFormContext>
{
private AIChatEntryFormContext _entryContext;
private AIChatEntryForm _entryForm;
private int? _entryFormSerialId;
private bool _pendingEntryRefresh;
private AIChatFormController _chatFormController;
private AIChatFormContext _chatContext;
public AIChatEntryFormController()
{
GameEntry.Event.Subscribe(OpenUIFormSuccessEventArgs.EventId, OnOpenUIFormSuccess);
GameEntry.Event.Subscribe(CloseUIFormCompleteEventArgs.EventId, OnCloseUIFormComplete);
}
~AIChatEntryFormController()
{
GameEntry.Event.Unsubscribe(OpenUIFormSuccessEventArgs.EventId, OnOpenUIFormSuccess);
GameEntry.Event.Unsubscribe(CloseUIFormCompleteEventArgs.EventId, OnCloseUIFormComplete);
}
public int? OpenUI(AIChatEntryFormContext context)
{
if (context == null)
{
Log.Warning("AIChatEntryFormController open failed. context is null.");
return null;
}
context.Controller = this;
_entryContext = context;
SyncEntryContextState();
if (_entryForm != null)
{
_entryForm.RefreshUI(_entryContext);
return _entryFormSerialId;
}
UGuiForm openedForm = GameEntry.UI.GetUIForm(UIFormId.AIChatEntryForm);
if (openedForm is AIChatEntryForm existingForm)
{
_entryForm = existingForm;
_entryFormSerialId = existingForm.UIForm.SerialId;
_entryForm.RefreshUI(_entryContext);
return _entryFormSerialId;
}
_pendingEntryRefresh = true;
_entryFormSerialId = GameEntry.UI.OpenUIForm(UIFormId.AIChatEntryForm, _entryContext);
return _entryFormSerialId;
}
public void CloseUI()
{
_pendingEntryRefresh = false;
if (_entryFormSerialId.HasValue)
{
GameEntry.UI.CloseUIForm(_entryFormSerialId.Value);
return;
}
if (_entryForm != null)
{
_entryForm.Close();
}
}
public void ToggleAIChatForm()
{
UGuiForm openedChatForm = GameEntry.UI.GetUIForm(UIFormId.AIChatForm);
if (openedChatForm != null)
{
if (_chatFormController != null)
{
_chatFormController.CloseUI();
}
else
{
GameEntry.UI.CloseUIForm(openedChatForm.UIForm);
}
RefreshEntryState();
return;
}
EnsureChatContext();
_chatContext.Controller = _chatFormController;
_chatFormController.OpenUI(_chatContext);
RefreshEntryState();
}
private void EnsureChatContext()
{
if (_chatFormController == null)
{
_chatFormController = new AIChatFormController();
}
if (_chatContext == null)
{
_chatContext = new AIChatFormContext
{
ClearHistoryOnOpen = false,
LanguageMode = 0,
Messages = new List<AIChatMessageContext>()
};
}
_chatContext.Controller = _chatFormController;
_chatContext.Messages ??= new List<AIChatMessageContext>();
}
private void SyncEntryContextState()
{
if (_entryContext == null)
{
return;
}
_entryContext.IsChatOpened = GameEntry.UI.GetUIForm(UIFormId.AIChatForm) != null;
}
private void RefreshEntryState()
{
SyncEntryContextState();
if (_entryForm == null || _entryContext == null)
{
return;
}
_entryForm.SetChatOpened(_entryContext.IsChatOpened);
}
private void OnOpenUIFormSuccess(object sender, GameEventArgs e)
{
if (!(e is OpenUIFormSuccessEventArgs args))
{
return;
}
if (_entryFormSerialId.HasValue &&
args.UIForm != null &&
args.UIForm.SerialId == _entryFormSerialId.Value &&
args.UserData == _entryContext)
{
_entryForm = args.UIForm.Logic as AIChatEntryForm;
if (_entryForm == null)
{
Log.Warning("AIChatEntryFormController open success but form logic is invalid.");
}
else if (_pendingEntryRefresh)
{
_entryForm.RefreshUI(_entryContext);
_pendingEntryRefresh = false;
}
}
RefreshEntryState();
}
private void OnCloseUIFormComplete(object sender, GameEventArgs e)
{
if (!(e is CloseUIFormCompleteEventArgs args))
{
return;
}
if (args.SerialId == _entryFormSerialId)
{
_entryForm = null;
_entryFormSerialId = null;
_pendingEntryRefresh = false;
}
RefreshEntryState();
}
}
}

View File

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

View File

@ -17,6 +17,7 @@ namespace UI
private static TMP_FontAsset _mainTMPFont = null;
private Canvas _cachedCanvas = null;
private CanvasGroup _canvasGroup = null;
private Coroutine _canvasFadeCoroutine = null;
private List<Canvas> _cachedCanvasContainer = new List<Canvas>();
public int OriginalDepth { get; private set; }
@ -30,7 +31,7 @@ namespace UI
public void Close(bool ignoreFade)
{
StopAllCoroutines();
StopCanvasFadeCoroutine();
if (ignoreFade)
{
@ -38,7 +39,7 @@ namespace UI
}
else
{
StartCoroutine(CloseCo(FadeTime));
_canvasFadeCoroutine = StartCoroutine(CloseCo(FadeTime));
}
}
@ -136,8 +137,7 @@ namespace UI
base.OnOpen(userData);
_canvasGroup.alpha = 0f;
StopAllCoroutines();
StartCoroutine(_canvasGroup.FadeToAlpha(1f, FadeTime));
PlayCanvasFade(1f, FadeTime);
}
#if UNITY_2017_3_OR_NEWER
@ -146,6 +146,7 @@ namespace UI
protected internal override void OnClose(bool isShutdown, object userData)
#endif
{
StopCanvasFadeCoroutine();
base.OnClose(isShutdown, userData);
}
@ -167,8 +168,7 @@ namespace UI
base.OnResume();
_canvasGroup.alpha = 0f;
StopAllCoroutines();
StartCoroutine(_canvasGroup.FadeToAlpha(1f, FadeTime));
PlayCanvasFade(1f, FadeTime);
}
#if UNITY_2017_3_OR_NEWER
@ -229,7 +229,31 @@ namespace UI
private IEnumerator CloseCo(float duration)
{
yield return _canvasGroup.FadeToAlpha(0f, duration);
_canvasFadeCoroutine = null;
GameEntry.UI.CloseUIForm(this);
}
private void PlayCanvasFade(float alpha, float duration)
{
StopCanvasFadeCoroutine();
_canvasFadeCoroutine = StartCoroutine(FadeCanvasCo(alpha, duration));
}
private IEnumerator FadeCanvasCo(float alpha, float duration)
{
yield return _canvasGroup.FadeToAlpha(alpha, duration);
_canvasFadeCoroutine = null;
}
private void StopCanvasFadeCoroutine()
{
if (_canvasFadeCoroutine == null)
{
return;
}
StopCoroutine(_canvasFadeCoroutine);
_canvasFadeCoroutine = null;
}
}
}

View File

@ -0,0 +1,86 @@
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UnityGameFramework.Runtime;
namespace UI
{
public sealed class AIChatEntryForm : UGuiForm
{
[SerializeField] private Button _entryButton;
[SerializeField] private TMP_Text _entryLabel;
[SerializeField] private string _openLabel = "问 AI";
[SerializeField] private string _closeLabel = "关闭 AI";
private AIChatEntryFormController _controller;
protected override void OnOpen(object userData)
{
base.OnOpen(userData);
BindEntryButton();
if (!(userData is AIChatEntryFormContext context))
{
Log.Error("AIChatEntryFormContext is invalid.");
return;
}
RefreshUI(context);
}
protected override void OnClose(bool isShutdown, object userData)
{
UnbindEntryButton();
_controller = null;
base.OnClose(isShutdown, userData);
}
public void RefreshUI(AIChatEntryFormContext context)
{
if (context == null)
{
return;
}
_controller = context.Controller;
SetChatOpened(context.IsChatOpened);
}
public void SetChatOpened(bool isChatOpened)
{
if (_entryLabel == null)
{
return;
}
_entryLabel.text = isChatOpened ? _closeLabel : _openLabel;
}
public void OnEntryButtonClick()
{
_controller?.ToggleAIChatForm();
}
private void BindEntryButton()
{
if (_entryButton == null)
{
Log.Warning("AIChatEntryForm entry button is not assigned.");
return;
}
_entryButton.onClick.RemoveListener(OnEntryButtonClick);
_entryButton.onClick.AddListener(OnEntryButtonClick);
}
private void UnbindEntryButton()
{
if (_entryButton == null)
{
return;
}
_entryButton.onClick.RemoveListener(OnEntryButtonClick);
}
}
}

View File

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

View File

@ -57,6 +57,7 @@ namespace UI
}
int requestId = context.TransitionRequestId;
bool shouldFadeTransition = _currentSprite != null;
if (context.Sprite != null)
{
@ -87,7 +88,7 @@ namespace UI
}
int version = ++_refreshVersion;
FadeToBlack(() =>
TweenCallback loadAndApplySprite = () =>
{
if (version != _refreshVersion)
{
@ -115,9 +116,25 @@ namespace UI
Log.Warning("BgForm refresh failed. Can not load sprite '{0}'.", targetAssetName);
}
FadeFromBlack(() => { PublishTransitionCompleted(requestId); });
if (shouldFadeTransition)
{
FadeFromBlack(() => { PublishTransitionCompleted(requestId); });
}
else
{
PublishTransitionCompleted(requestId);
}
});
});
};
if (shouldFadeTransition)
{
FadeToBlack(loadAndApplySprite);
}
else
{
loadAndApplySprite.Invoke();
}
}
private void RefreshWithSprite(Sprite sprite, string backgroundAssetName, int requestId)
@ -137,8 +154,9 @@ namespace UI
return;
}
bool shouldFadeTransition = _currentSprite != null;
int version = ++_refreshVersion;
FadeToBlack(() =>
TweenCallback applySprite = () =>
{
if (version != _refreshVersion)
{
@ -153,8 +171,25 @@ namespace UI
{
_bgImage.sprite = sprite;
}
FadeFromBlack(() => { PublishTransitionCompleted(requestId); });
});
if (shouldFadeTransition)
{
FadeFromBlack(() => { PublishTransitionCompleted(requestId); });
}
else
{
PublishTransitionCompleted(requestId);
}
};
if (shouldFadeTransition)
{
FadeToBlack(applySprite);
}
else
{
applySprite.Invoke();
}
}
private void FadeToBlack(TweenCallback onComplete)

View File

@ -4,6 +4,7 @@ using DG.Tweening;
using Definition.Enum;
using TMPro;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UI;
using UnityGameFramework.Runtime;
@ -19,9 +20,9 @@ namespace UI
[SerializeField] private TMP_Text _contentText;
[SerializeField] private Image _leftSprite;
[FormerlySerializedAs("_leftSprite")] [SerializeField] private Image _leftImage;
[SerializeField] private Image _rightSprite;
[FormerlySerializedAs("_rightSprite")] [SerializeField] private Image _rightImage;
[SerializeField] private int _leftSpritePosition = 450;
@ -35,8 +36,6 @@ namespace UI
[SerializeField] private GameObject _speakerNameArea;
private readonly int _singleSpeakerCenterPosition = Screen.width / 2;
private string _leftSpeakerToken = string.Empty;
private string _rightSpeakerToken = string.Empty;
private Sequence _layoutSequence;
@ -95,6 +94,7 @@ namespace UI
}
string speakerName = _context.SpeakerName;
bool isHumanSpeaker = IsHumanSpeaker(_context.SpeakerId);
if (_speakerArea != null)
{
@ -103,7 +103,7 @@ namespace UI
if (_speakerNameArea != null)
{
_speakerNameArea.SetActive(!string.IsNullOrEmpty(speakerName) && IsHumanSpeaker(_context.SpeakerId));
_speakerNameArea.SetActive(!string.IsNullOrEmpty(speakerName) && isHumanSpeaker);
}
if (_speakerNameText != null)
@ -112,6 +112,7 @@ namespace UI
}
PlayTypewriter(_contentText, _context.Text, _currentPlayingSpeed);
MainOverlayForm.DispatchCue(_context.Emphasis);
if (string.IsNullOrEmpty(speakerName))
{
@ -120,6 +121,13 @@ namespace UI
return;
}
if (!isHumanSpeaker)
{
ClearSpeakerState();
ApplySpeakerLayout(false, false, true);
return;
}
bool isRightSpeaker = context.Direction > 0;
if (isRightSpeaker)
{
@ -133,21 +141,35 @@ namespace UI
bool hasLeftSpeaker = !string.IsNullOrEmpty(_leftSpeakerToken);
bool hasRightSpeaker = !string.IsNullOrEmpty(_rightSpeakerToken);
UpdateSpeakerPortrait(isRightSpeaker ? _rightSprite : _leftSprite, _context.SpeakerId,
UpdateSpeakerPortrait(isRightSpeaker ? _rightImage : _leftImage, _context.SpeakerId,
_context.CurrentLineId, isRightSpeaker);
ApplySpeakerLayout(hasLeftSpeaker, hasRightSpeaker, false);
}
protected override void OnOpen(object userData)
{
ResetSpeakerVisualState();
base.OnOpen(userData);
}
protected override void OnClose(bool isShutdown, object userData)
{
ClearSpeakerState();
KillLayoutTween();
ApplySpeakerLayout(false, false, true);
ResetSpeakerVisualState();
base.OnClose(isShutdown, userData);
}
private void ClearSpeakerState()
{
if (_leftImage != null)
{
_leftImage.sprite = null;
}
if (_rightImage != null)
{
_rightImage.sprite = null;
}
_leftSpeakerToken = string.Empty;
_rightSpeakerToken = string.Empty;
_leftPortraitRequestVersion++;
@ -161,17 +183,27 @@ namespace UI
_layoutSequence.Kill();
_layoutSequence = null;
}
if (_leftImage != null && _leftImage.rectTransform != null)
{
_leftImage.rectTransform.DOKill();
}
if (_rightImage != null && _rightImage.rectTransform != null)
{
_rightImage.rectTransform.DOKill();
}
}
private void ApplySpeakerLayout(bool hasLeftSpeaker, bool hasRightSpeaker, bool instant)
{
if (_leftSprite == null || _rightSprite == null)
if (_leftImage == null || _rightImage == null)
{
return;
}
bool leftCurrentlyVisible = _leftSprite.gameObject.activeSelf;
bool rightCurrentlyVisible = _rightSprite.gameObject.activeSelf;
bool leftCurrentlyVisible = _leftImage.gameObject.activeSelf;
bool rightCurrentlyVisible = _rightImage.gameObject.activeSelf;
bool leftTargetVisible = hasLeftSpeaker;
bool rightTargetVisible = hasRightSpeaker;
@ -191,22 +223,22 @@ namespace UI
if (instant || _moveDuration <= 0f)
{
SetSpritePosition(_leftSprite.rectTransform, leftTargetX);
SetSpritePosition(_rightSprite.rectTransform, rightTargetX);
SetSpriteVisible(_leftSprite, leftTargetVisible);
SetSpriteVisible(_rightSprite, rightTargetVisible);
SetSpritePosition(_leftImage.rectTransform, leftTargetX);
SetSpritePosition(_rightImage.rectTransform, rightTargetX);
SetSpriteVisible(_leftImage, leftTargetVisible);
SetSpriteVisible(_rightImage, rightTargetVisible);
return;
}
_layoutSequence = DOTween.Sequence();
Tween leftTween = CreateMoveTween(_leftSprite.rectTransform, leftTargetX);
Tween leftTween = CreateMoveTween(_leftImage.rectTransform, leftTargetX);
if (leftTween != null)
{
_layoutSequence.Join(leftTween);
}
Tween rightTween = CreateMoveTween(_rightSprite.rectTransform, rightTargetX);
Tween rightTween = CreateMoveTween(_rightImage.rectTransform, rightTargetX);
if (rightTween != null)
{
_layoutSequence.Join(rightTween);
@ -216,17 +248,17 @@ namespace UI
{
_layoutSequence.OnComplete(() =>
{
SetSpriteVisible(_leftSprite, leftTargetVisible);
SetSpriteVisible(_rightSprite, rightTargetVisible);
SetSpriteVisible(_leftImage, leftTargetVisible);
SetSpriteVisible(_rightImage, rightTargetVisible);
_layoutSequence = null;
});
}
else
{
SetSpritePosition(_leftSprite.rectTransform, leftTargetX);
SetSpritePosition(_rightSprite.rectTransform, rightTargetX);
SetSpriteVisible(_leftSprite, leftTargetVisible);
SetSpriteVisible(_rightSprite, rightTargetVisible);
SetSpritePosition(_leftImage.rectTransform, leftTargetX);
SetSpritePosition(_rightImage.rectTransform, rightTargetX);
SetSpriteVisible(_leftImage, leftTargetVisible);
SetSpriteVisible(_rightImage, rightTargetVisible);
_layoutSequence.Kill();
_layoutSequence = null;
}
@ -243,25 +275,25 @@ namespace UI
if (leftTargetVisible && !leftCurrentlyVisible)
{
float leftStartX = GetAppearStartX(true, rightCurrentlyVisible, hasLeftSpeaker, hasRightSpeaker);
SetSpritePosition(_leftSprite.rectTransform, leftStartX);
SetSpriteVisible(_leftSprite, true);
SetSpritePosition(_leftImage.rectTransform, leftStartX);
SetSpriteVisible(_leftImage, true);
}
if (rightTargetVisible && !rightCurrentlyVisible)
{
float rightStartX = GetAppearStartX(false, leftCurrentlyVisible, hasLeftSpeaker, hasRightSpeaker);
SetSpritePosition(_rightSprite.rectTransform, rightStartX);
SetSpriteVisible(_rightSprite, true);
SetSpritePosition(_rightImage.rectTransform, rightStartX);
SetSpriteVisible(_rightImage, true);
}
if (leftCurrentlyVisible && !leftTargetVisible)
{
SetSpriteVisible(_leftSprite, true);
SetSpriteVisible(_leftImage, true);
}
if (rightCurrentlyVisible && !rightTargetVisible)
{
SetSpriteVisible(_rightSprite, true);
SetSpriteVisible(_rightImage, true);
}
}
@ -273,7 +305,7 @@ namespace UI
// single -> multi: hidden side starts from center, then both move to side positions.
if (otherCurrentlyVisible)
{
return _singleSpeakerCenterPosition;
return GetSingleSpeakerCenterPosition(isLeft);
}
return isLeft ? _leftSpritePosition : _rightSpritePosition;
@ -293,7 +325,7 @@ namespace UI
if (hasLeftSpeaker || hasRightSpeaker)
{
// multi -> single: both move to center first, then inactive side hides.
return _singleSpeakerCenterPosition;
return GetSingleSpeakerCenterPosition(isLeft);
}
return isLeft ? _leftSpritePosition : _rightSpritePosition;
@ -350,7 +382,8 @@ namespace UI
return;
}
string portraitAssetName = $"Characters/{speakerId}-{ResolvePortraitIndex(speakerId, lineId)}.png";
int portraitCount = ResolveAvailablePortraitCount(speakerId);
string portraitAssetName = $"Characters/{speakerId}-{ResolvePortraitIndex(speakerId, lineId, portraitCount)}.png";
if (GameEntry.SpriteCache == null)
{
return;
@ -360,32 +393,33 @@ namespace UI
{
if (isRightSpeaker)
{
if (requestVersion != _rightPortraitRequestVersion || _rightSprite == null)
if (requestVersion != _rightPortraitRequestVersion || _rightImage == null)
{
return;
}
if (sprite != null)
{
_rightSprite.sprite = sprite;
_rightImage.sprite = sprite;
}
return;
}
if (requestVersion != _leftPortraitRequestVersion || _leftSprite == null)
if (requestVersion != _leftPortraitRequestVersion || _leftImage == null)
{
return;
}
if (sprite != null)
{
_leftSprite.sprite = sprite;
_leftImage.sprite = sprite;
}
});
}
private static int ResolvePortraitIndex(string speakerId, int lineId)
private static int ResolvePortraitIndex(string speakerId, int lineId, int portraitCount)
{
int safePortraitCount = portraitCount > 0 ? portraitCount : 3;
unchecked
{
int hash = 17;
@ -393,16 +427,26 @@ namespace UI
hash = hash * 31 + (string.IsNullOrEmpty(speakerId)
? 0
: System.StringComparer.OrdinalIgnoreCase.GetHashCode(speakerId));
int mod = hash % 3;
int mod = hash % safePortraitCount;
if (mod < 0)
{
mod += 3;
mod += safePortraitCount;
}
return mod + 1;
}
}
private static int ResolveAvailablePortraitCount(string speakerId)
{
if (GameEntry.Dialog == null)
{
return 0;
}
return GameEntry.Dialog.GetSpeakerPortraitCount(speakerId);
}
private static bool IsHumanSpeaker(string speakerId)
{
if (string.IsNullOrWhiteSpace(speakerId))
@ -412,5 +456,37 @@ namespace UI
return !NonPortraitSpeakerIds.Contains(speakerId);
}
private void ResetSpeakerVisualState()
{
KillLayoutTween();
ClearSpeakerState();
ApplySpeakerLayout(false, false, true);
}
private float GetSingleSpeakerCenterPosition(bool isLeft)
{
RectTransform rectTransform = isLeft
? _leftImage != null ? _leftImage.rectTransform : null
: _rightImage != null ? _rightImage.rectTransform : null;
return GetCenterAnchoredX(rectTransform);
}
private static float GetCenterAnchoredX(RectTransform rectTransform)
{
if (rectTransform == null)
{
return 0f;
}
if (!(rectTransform.parent is RectTransform parent))
{
return 0f;
}
float anchorX = rectTransform.anchorMin.x;
return (0.5f - anchorX) * parent.rect.width;
}
}
}

View File

@ -0,0 +1,286 @@
using System.Collections;
using Event;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UnityGameFramework.Runtime;
namespace UI
{
public class ChapterTitleForm : UGuiForm
{
[SerializeField] private CanvasGroup _contentGroup;
[SerializeField] private Image _dimImage;
[SerializeField] private TMP_Text _titleText;
[SerializeField] private TMP_Text _subtitleText;
[SerializeField] private float _fadeInDuration = 0.3f;
[SerializeField] private float _holdDuration = 1.2f;
[SerializeField] private float _fadeOutDuration = 0.3f;
private Coroutine _playCoroutine;
private int _requestId;
private int _chapterId;
private bool _hasPublishedCompletion;
protected override void OnOpen(object userData)
{
base.OnOpen(userData);
EnsureView();
ApplyContext(userData as ChapterTitleFormContext);
StartPlay();
}
protected override void OnClose(bool isShutdown, object userData)
{
StopPlay();
SetContentAlpha(0f);
if (!isShutdown)
{
PublishCompletionIfNeeded();
}
base.OnClose(isShutdown, userData);
}
private void EnsureView()
{
RectTransform rootRect = transform as RectTransform;
if (rootRect == null)
{
return;
}
if (_contentGroup == null)
{
_contentGroup = CreateContentGroup(rootRect);
}
RectTransform contentRect = _contentGroup != null && _contentGroup.transform is RectTransform contentTransform
? contentTransform
: rootRect;
if (_dimImage == null)
{
_dimImage = CreateImage("Dim", contentRect, new Color(0f, 0f, 0f, 0.72f), true);
}
if (_titleText == null)
{
_titleText = CreateText("Title", contentRect, 108f, FontStyles.Bold, TextAlignmentOptions.Center);
RectTransform titleRect = _titleText.rectTransform;
titleRect.anchorMin = new Vector2(0.5f, 0.5f);
titleRect.anchorMax = new Vector2(0.5f, 0.5f);
titleRect.pivot = new Vector2(0.5f, 0.5f);
titleRect.sizeDelta = new Vector2(2200f, 220f);
titleRect.anchoredPosition = new Vector2(0f, 40f);
}
if (_subtitleText == null)
{
_subtitleText = CreateText("Subtitle", contentRect, 42f, FontStyles.Normal, TextAlignmentOptions.Center);
RectTransform subtitleRect = _subtitleText.rectTransform;
subtitleRect.anchorMin = new Vector2(0.5f, 0.5f);
subtitleRect.anchorMax = new Vector2(0.5f, 0.5f);
subtitleRect.pivot = new Vector2(0.5f, 0.5f);
subtitleRect.sizeDelta = new Vector2(2200f, 120f);
subtitleRect.anchoredPosition = new Vector2(0f, -60f);
}
if (_dimImage != null)
{
_dimImage.transform.SetAsFirstSibling();
}
if (_subtitleText != null)
{
_subtitleText.transform.SetAsLastSibling();
}
if (_titleText != null)
{
_titleText.transform.SetAsLastSibling();
}
}
private void ApplyContext(ChapterTitleFormContext context)
{
_hasPublishedCompletion = false;
if (context == null)
{
_requestId = 0;
_chapterId = 0;
SetTitleText(string.Empty, string.Empty);
return;
}
_requestId = context.RequestId;
_chapterId = context.ChapterId;
string title = string.IsNullOrWhiteSpace(context.Title)
? BuildDefaultChapterTitle(_chapterId)
: context.Title.Trim();
string subtitle = string.IsNullOrWhiteSpace(context.Subtitle) ? string.Empty : context.Subtitle.Trim();
SetTitleText(title, subtitle);
}
private void SetTitleText(string title, string subtitle)
{
if (_titleText != null)
{
_titleText.text = title;
}
if (_subtitleText != null)
{
_subtitleText.text = subtitle;
_subtitleText.gameObject.SetActive(!string.IsNullOrEmpty(subtitle));
}
}
private void StartPlay()
{
StopPlay();
_playCoroutine = StartCoroutine(PlayRoutine());
}
private void StopPlay()
{
if (_playCoroutine == null)
{
return;
}
StopCoroutine(_playCoroutine);
_playCoroutine = null;
}
private IEnumerator PlayRoutine()
{
SetContentAlpha(0f);
yield return FadeAlpha(0f, 1f, _fadeInDuration);
yield return WaitUnscaled(_holdDuration);
yield return FadeAlpha(1f, 0f, _fadeOutDuration);
_playCoroutine = null;
PublishCompletionIfNeeded();
if (GameEntry.UI != null && UIForm != null)
{
GameEntry.UI.CloseUIForm(UIForm);
}
}
private IEnumerator FadeAlpha(float from, float to, float duration)
{
float safeDuration = Mathf.Max(0f, duration);
if (safeDuration <= 0f)
{
SetContentAlpha(to);
yield break;
}
float elapsed = 0f;
while (elapsed < safeDuration)
{
elapsed += Time.unscaledDeltaTime;
float t = Mathf.Clamp01(elapsed / safeDuration);
SetContentAlpha(Mathf.Lerp(from, to, t));
yield return null;
}
SetContentAlpha(to);
}
private static IEnumerator WaitUnscaled(float duration)
{
float safeDuration = Mathf.Max(0f, duration);
if (safeDuration <= 0f)
{
yield break;
}
float elapsed = 0f;
while (elapsed < safeDuration)
{
elapsed += Time.unscaledDeltaTime;
yield return null;
}
}
private void SetContentAlpha(float alpha)
{
if (_contentGroup == null)
{
return;
}
_contentGroup.alpha = Mathf.Clamp01(alpha);
}
private void PublishCompletionIfNeeded()
{
if (_hasPublishedCompletion || _requestId <= 0)
{
return;
}
_hasPublishedCompletion = true;
GameEntry.Event.Fire(this, ChapterTitleCompletedEventArgs.Create(_chapterId, _requestId));
}
private static string BuildDefaultChapterTitle(int chapterId)
{
return chapterId > 0 ? $"第{chapterId}章" : string.Empty;
}
private static CanvasGroup CreateContentGroup(RectTransform parent)
{
GameObject go = new GameObject("ContentRoot", typeof(RectTransform), typeof(CanvasGroup));
RectTransform rect = go.GetComponent<RectTransform>();
rect.SetParent(parent, false);
rect.anchorMin = Vector2.zero;
rect.anchorMax = Vector2.one;
rect.offsetMin = Vector2.zero;
rect.offsetMax = Vector2.zero;
return go.GetComponent<CanvasGroup>();
}
private static Image CreateImage(string name, RectTransform parent, Color color, bool raycastTarget)
{
GameObject go = new GameObject(name, typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
RectTransform rect = go.GetComponent<RectTransform>();
rect.SetParent(parent, false);
rect.anchorMin = Vector2.zero;
rect.anchorMax = Vector2.one;
rect.offsetMin = Vector2.zero;
rect.offsetMax = Vector2.zero;
Image image = go.GetComponent<Image>();
image.color = color;
image.raycastTarget = raycastTarget;
return image;
}
private static TMP_Text CreateText(string name, RectTransform parent, float fontSize, FontStyles style,
TextAlignmentOptions alignment)
{
GameObject go = new GameObject(name, typeof(RectTransform), typeof(CanvasRenderer), typeof(TextMeshProUGUI));
RectTransform rect = go.GetComponent<RectTransform>();
rect.SetParent(parent, false);
TextMeshProUGUI text = go.GetComponent<TextMeshProUGUI>();
if (TMP_Settings.defaultFontAsset != null)
{
text.font = TMP_Settings.defaultFontAsset;
}
text.text = string.Empty;
text.fontSize = fontSize;
text.fontStyle = style;
text.alignment = alignment;
text.color = new Color(0.96f, 0.94f, 0.88f, 1f);
text.raycastTarget = false;
return text;
}
}
}

View File

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

View File

@ -0,0 +1,268 @@
using System.Collections;
using System.Collections.Generic;
using Definition.Enum;
using UnityEngine;
using UnityEngine.UI;
using UnityGameFramework.Runtime;
namespace UI
{
public class MainOverlayForm : UGuiForm
{
[SerializeField] private Image _cueImage;
[SerializeField] private List<Sprite> _cueBindings = new List<Sprite>();
[SerializeField] private float _showDuration = 0.2f;
[SerializeField] private float _hideDuration = 0.2f;
private static int _dispatchCount;
private Coroutine _transitionCoroutine;
private bool _isClosingByCue;
private Vector3 _baseScale = Vector3.one;
private bool _baseScaleInitialized;
public static void DispatchCue(EmphasisType emphasis)
{
if (GameEntry.UI == null)
{
return;
}
UGuiForm existingForm = GameEntry.UI.GetUIForm(UIFormId.MainOverlayForm);
if (emphasis == EmphasisType.EntityCueClose)
{
if (existingForm is MainOverlayForm overlay)
{
overlay.PlayHideAndClose();
}
return;
}
if (emphasis != EmphasisType.EntityCue)
{
return;
}
if (existingForm is MainOverlayForm overlayForm)
{
overlayForm.ShowNextCue();
return;
}
int? formSerialId = GameEntry.UI.OpenUIForm(UIFormId.MainOverlayForm);
if (!formSerialId.HasValue)
{
Log.Warning("MainOverlayForm open failed. Check UIForm.txt config and prefab asset.");
}
}
protected override void OnOpen(object userData)
{
base.OnOpen(userData);
EnsureBaseScaleInitialized();
ShowNextCue();
}
protected override void OnClose(bool isShutdown, object userData)
{
StopTransition();
HideCueImage(clearSprite: true);
base.OnClose(isShutdown, userData);
}
private void ShowNextCue()
{
if (_cueImage == null)
{
Log.Warning("MainOverlayForm cue image is missing.");
return;
}
if (_cueBindings == null || _cueBindings.Count == 0)
{
HideCueImage(clearSprite: false);
Log.Warning("MainOverlayForm cue bindings are empty.");
return;
}
int index = _dispatchCount % _cueBindings.Count;
_dispatchCount++;
Sprite sprite = _cueBindings[index];
if (sprite == null)
{
HideCueImage(clearSprite: false);
return;
}
EnsureBaseScaleInitialized();
StopTransition();
_transitionCoroutine = StartCoroutine(PlayShowRoutine(sprite));
}
private void PlayHideAndClose()
{
if (_isClosingByCue)
{
return;
}
if (_cueImage == null)
{
if (GameEntry.UI != null && UIForm != null)
{
GameEntry.UI.CloseUIForm(UIForm);
}
return;
}
StopTransition();
_transitionCoroutine = StartCoroutine(PlayHideAndCloseRoutine());
}
private IEnumerator PlayShowRoutine(Sprite sprite)
{
_cueImage.sprite = sprite;
_cueImage.SetNativeSize();
_cueImage.gameObject.SetActive(true);
SetCueVisual(0f, 0f);
yield return PlayLerp(0f, 1f, 0f, 0.6f, _showDuration);
_transitionCoroutine = null;
}
private IEnumerator PlayHideAndCloseRoutine()
{
_isClosingByCue = true;
float fromAlpha = _cueImage.color.a;
float fromScale = GetCurrentScale01();
yield return PlayLerp(fromAlpha, 0f, fromScale, 0f, _hideDuration);
HideCueImage(clearSprite: true);
_isClosingByCue = false;
_transitionCoroutine = null;
if (GameEntry.UI != null && UIForm != null)
{
GameEntry.UI.CloseUIForm(UIForm);
}
}
private IEnumerator PlayLerp(float fromAlpha, float toAlpha, float fromScale, float toScale, float duration)
{
if (_cueImage == null)
{
yield break;
}
float safeDuration = Mathf.Max(0f, duration);
if (safeDuration <= 0f)
{
SetCueVisual(toAlpha, toScale);
yield break;
}
float elapsed = 0f;
while (elapsed < safeDuration)
{
elapsed += Time.unscaledDeltaTime;
float t = Mathf.Clamp01(elapsed / safeDuration);
float alpha = Mathf.Lerp(fromAlpha, toAlpha, t);
float scale = Mathf.Lerp(fromScale, toScale, t);
SetCueVisual(alpha, scale);
yield return null;
}
SetCueVisual(toAlpha, toScale);
}
private void SetCueVisual(float alpha, float scale01)
{
if (_cueImage == null)
{
return;
}
Color color = _cueImage.color;
color.a = Mathf.Clamp01(alpha);
_cueImage.color = color;
RectTransform rectTransform = _cueImage.rectTransform;
if (rectTransform == null)
{
return;
}
float safeScale = Mathf.Clamp01(scale01);
rectTransform.localScale = _baseScale * safeScale;
}
private float GetCurrentScale01()
{
if (_cueImage == null)
{
return 1f;
}
EnsureBaseScaleInitialized();
RectTransform rectTransform = _cueImage.rectTransform;
if (rectTransform == null)
{
return 1f;
}
float ratioX = Mathf.Abs(_baseScale.x) > 0.0001f ? rectTransform.localScale.x / _baseScale.x : 1f;
float ratioY = Mathf.Abs(_baseScale.y) > 0.0001f ? rectTransform.localScale.y / _baseScale.y : 1f;
return Mathf.Clamp01((ratioX + ratioY) * 0.5f);
}
private void EnsureBaseScaleInitialized()
{
if (_baseScaleInitialized)
{
return;
}
if (_cueImage == null || _cueImage.rectTransform == null)
{
_baseScale = Vector3.one;
_baseScaleInitialized = true;
return;
}
_baseScale = _cueImage.rectTransform.localScale;
if (_baseScale == Vector3.zero)
{
_baseScale = Vector3.one;
}
_baseScaleInitialized = true;
}
private void StopTransition()
{
if (_transitionCoroutine != null)
{
StopCoroutine(_transitionCoroutine);
_transitionCoroutine = null;
}
_isClosingByCue = false;
}
private void HideCueImage(bool clearSprite)
{
if (_cueImage == null)
{
return;
}
_cueImage.gameObject.SetActive(false);
SetCueVisual(0f, 0f);
if (clearSprite)
{
_cueImage.sprite = null;
}
}
}
}

View File

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

View File

@ -0,0 +1,22 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 19a1150cd0fb4b648ad8232de4e38baa, type: 3}
m_Name: Combine1005
m_EditorClassIdentifier:
_enabled: 1
_triggerType: 0
_triggerId: 1005
_useDefaultConfig: 1
_combineConfig:
AutoStart: 1
Slots: []
Parts: []

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 352998f9edeef2348a887c5a2e7dc5d1
guid: 4439eec25b0603e4abc486862811413a
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000

View File

@ -0,0 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 8e4367863f5042e1b5b0b3b3b7f62f5e, type: 3}
m_Name: Dialog1004
m_EditorClassIdentifier:
_enabled: 1
_triggerType: 0
_triggerId: 1004
_dialogId: 1005

View File

@ -1,8 +1,8 @@
fileFormatVersion: 2
guid: 28c42f88cfe56e84e95c0d7545db8c1b
guid: 3f853a1029e766343b69579255e40bbc
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 0
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 8e4367863f5042e1b5b0b3b3b7f62f5e, type: 3}
m_Name: Dialog2001
m_EditorClassIdentifier:
_enabled: 1
_triggerType: 0
_triggerId: 2001
_dialogId: 2002

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 20a90da7b22b040428da0c9c38c892d0
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 8e4367863f5042e1b5b0b3b3b7f62f5e, type: 3}
m_Name: Dialog2002
m_EditorClassIdentifier:
_enabled: 1
_triggerType: 0
_triggerId: 2002
_dialogId: 2003

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 993a30dececf3c849b753dec84f5a6f0
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 8e4367863f5042e1b5b0b3b3b7f62f5e, type: 3}
m_Name: Dialog2003
m_EditorClassIdentifier:
_enabled: 1
_triggerType: 0
_triggerId: 2003
_dialogId: 2003

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 752da669fb380ba48a43fa85c5928d30
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 8e4367863f5042e1b5b0b3b3b7f62f5e, type: 3}
m_Name: Dialog3001
m_EditorClassIdentifier:
_enabled: 1
_triggerType: 0
_triggerId: 3001
_dialogId: 3002

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a57f0cef82d7b0c4891685fa9bf91caa
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 8e4367863f5042e1b5b0b3b3b7f62f5e, type: 3}
m_Name: Dialog3002
m_EditorClassIdentifier:
_enabled: 1
_triggerType: 0
_triggerId: 3002
_dialogId: 3003

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ae1fa1f6bf239ae4797a2817dcddfee5
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 8e4367863f5042e1b5b0b3b3b7f62f5e, type: 3}
m_Name: Dialog3003
m_EditorClassIdentifier:
_enabled: 1
_triggerType: 0
_triggerId: 3003
_dialogId: 3004

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 85827e4f006c7bd4eb4438c79643482a
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 8e4367863f5042e1b5b0b3b3b7f62f5e, type: 3}
m_Name: Dialog4001
m_EditorClassIdentifier:
_enabled: 1
_triggerType: 0
_triggerId: 4001
_dialogId: 4002

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9c1f6f86d307c934bb1b6156ecc22ce4
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 8e4367863f5042e1b5b0b3b3b7f62f5e, type: 3}
m_Name: Dialog4002
m_EditorClassIdentifier:
_enabled: 1
_triggerType: 0
_triggerId: 4002
_dialogId: 4003

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 864c5f9e92613564c8dbd1864266461a
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 8e4367863f5042e1b5b0b3b3b7f62f5e, type: 3}
m_Name: Dialog4003
m_EditorClassIdentifier:
_enabled: 1
_triggerType: 0
_triggerId: 4003
_dialogId: 4004

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6fb570be0c658844681401443b633d1f
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2b63fb6eb96e4d3193dd26b443f856c7, type: 3}
m_Name: EndChapter1006
m_EditorClassIdentifier:
_enabled: 1
_triggerType: 0
_triggerId: 1005
_chapterId: 2

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 70abcb8196a34ab4293f06f432f45446
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2b63fb6eb96e4d3193dd26b443f856c7, type: 3}
m_Name: EndChapter2004
m_EditorClassIdentifier:
_enabled: 1
_triggerType: 0
_triggerId: 2004
_chapterId: 3

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b212fd0c183de84469231d272cb97369
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2b63fb6eb96e4d3193dd26b443f856c7, type: 3}
m_Name: EndChapter3004
m_EditorClassIdentifier:
_enabled: 1
_triggerType: 0
_triggerId: 3004
_chapterId: 4

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 44f4727efe7606f4eb271f10461657ef
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2b63fb6eb96e4d3193dd26b443f856c7, type: 3}
m_Name: EndChapter4004
m_EditorClassIdentifier:
_enabled: 1
_triggerType: 0
_triggerId: 4004
_chapterId: 0

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 778a252e3434f3b4e868a99cd8313ac5
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 KiB

After

Width:  |  Height:  |  Size: 147 KiB

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 9a1a43803190cbf4a8694a784c3df76b
guid: fc3cff8aba4e30c4ea369cb0a0f84baa
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
@ -94,7 +94,7 @@ TextureImporter:
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
@ -107,7 +107,7 @@ TextureImporter:
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: WebGL
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 KiB

After

Width:  |  Height:  |  Size: 150 KiB

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: e16e4557ab120734db2ebf120e075ee6
guid: bd4cb343cc137574a9b204474df45a3c
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
@ -94,7 +94,7 @@ TextureImporter:
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
@ -107,7 +107,7 @@ TextureImporter:
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: WebGL
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

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