Merge pull request 'feature/UpdateAvatar' (#405) from feature/UpdateAvatar into develop

Reviewed-on: https://git.pthub.vn/Unity/perfect-world-unity/pulls/405
This commit is contained in:
cuongnv
2026-05-06 08:56:49 +00:00
22 changed files with 1392 additions and 45 deletions
+36 -39
View File
@@ -161,8 +161,8 @@ GameObject:
m_Component:
- component: {fileID: 2127540135059465312}
- component: {fileID: 2085804573552353186}
- component: {fileID: 5477947404616405472}
- component: {fileID: 2090693574295861813}
- component: {fileID: 2301294577913282359}
m_Layer: 5
m_Name: Avatar
m_TagString: Untagged
@@ -186,8 +186,8 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: -151.8, y: 7.8005}
m_SizeDelta: {x: 85.3021, y: 124.4}
m_AnchoredPosition: {x: -151.8, y: -19.49}
m_SizeDelta: {x: 128, y: 180}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &2085804573552353186
CanvasRenderer:
@@ -197,36 +197,6 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 955704001844978227}
m_CullTransparentMesh: 1
--- !u!114 &5477947404616405472
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 955704001844978227}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 21300000, guid: 88530e3bf1ec5ea4d93e2a0d01a5d525, type: 3}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!114 &2090693574295861813
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -267,10 +237,37 @@ MonoBehaviour:
m_SelectedTrigger: Selected
m_DisabledTrigger: Disabled
m_Interactable: 1
m_TargetGraphic: {fileID: 5477947404616405472}
m_TargetGraphic: {fileID: 2301294577913282359}
m_OnClick:
m_PersistentCalls:
m_Calls: []
--- !u!114 &2301294577913282359
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 955704001844978227}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Texture: {fileID: 8400000, guid: bba090692914cf841bc3173b85871a66, type: 2}
m_UVRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
--- !u!1 &974102734944724072
GameObject:
m_ObjectHideFlags: 0
@@ -1804,7 +1801,7 @@ RectTransform:
m_GameObject: {fileID: 4823752405346273106}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1.3, y: 1.3, z: 1.3}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 2127540135059465312}
@@ -1818,10 +1815,10 @@ RectTransform:
- {fileID: 8118804085881780215}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 275.1, y: -110.3}
m_SizeDelta: {x: 461, y: 133}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: -143, y: -20}
m_SizeDelta: {x: 128, y: 180}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &3549955056024652669
CanvasRenderer:
+75 -1
View File
@@ -30,7 +30,8 @@ RectTransform:
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Children:
- {fileID: 1510810868840459915}
m_Father: {fileID: 7601428160728630082}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
@@ -175,6 +176,7 @@ MonoBehaviour:
_nameText: {fileID: 8459104239633154731}
_statText: {fileID: 7225922753763360209}
healthImage: {fileID: 391766345810538963}
_avatarImage: {fileID: 5732841064457103199}
--- !u!1 &5230218528339883288
GameObject:
m_ObjectHideFlags: 0
@@ -601,6 +603,78 @@ MonoBehaviour:
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!1 &8106823484631380573
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1510810868840459915}
- component: {fileID: 8158437551720672858}
- component: {fileID: 5732841064457103199}
m_Layer: 5
m_Name: AvatarImage
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1510810868840459915
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8106823484631380573}
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: 8841982213385894347}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: -19}
m_SizeDelta: {x: 128, y: 180}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &8158437551720672858
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8106823484631380573}
m_CullTransparentMesh: 1
--- !u!114 &5732841064457103199
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8106823484631380573}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 0
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Texture: {fileID: 8400000, guid: 86b263f7c57f2b2408a59d5c3bafcfe1, type: 2}
m_UVRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
--- !u!1 &8340737021475261245
GameObject:
m_ObjectHideFlags: 0
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a295172ba5abc95449425dcf9c63a0ae
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,39 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!84 &8400000
RenderTexture:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Avatar_EslePlayer
m_ImageContentsHash:
serializedVersion: 2
Hash: 00000000000000000000000000000000
m_IsAlphaChannelOptional: 0
serializedVersion: 6
m_Width: 90
m_Height: 128
m_AntiAliasing: 1
m_MipCount: -1
m_DepthStencilFormat: 90
m_ColorFormat: 12
m_MipMap: 0
m_GenerateMips: 1
m_SRGB: 0
m_UseDynamicScale: 0
m_UseDynamicScaleExplicit: 0
m_BindMS: 0
m_EnableCompatibleFormat: 1
m_EnableRandomWrite: 0
m_TextureSettings:
serializedVersion: 2
m_FilterMode: 0
m_Aniso: 0
m_MipBias: 0
m_WrapU: 1
m_WrapV: 1
m_WrapW: 1
m_Dimension: 2
m_VolumeDepth: 1
m_ShadowSamplingMode: 2
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 86b263f7c57f2b2408a59d5c3bafcfe1
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 8400000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,39 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!84 &8400000
RenderTexture:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Avatar_HostPlayer
m_ImageContentsHash:
serializedVersion: 2
Hash: 00000000000000000000000000000000
m_IsAlphaChannelOptional: 0
serializedVersion: 6
m_Width: 90
m_Height: 128
m_AntiAliasing: 1
m_MipCount: -1
m_DepthStencilFormat: 90
m_ColorFormat: 12
m_MipMap: 0
m_GenerateMips: 1
m_SRGB: 0
m_UseDynamicScale: 0
m_UseDynamicScaleExplicit: 0
m_BindMS: 0
m_EnableCompatibleFormat: 1
m_EnableRandomWrite: 0
m_TextureSettings:
serializedVersion: 2
m_FilterMode: 0
m_Aniso: 0
m_MipBias: 0
m_WrapU: 1
m_WrapV: 1
m_WrapW: 1
m_Dimension: 2
m_VolumeDepth: 1
m_ShadowSamplingMode: 2
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bba090692914cf841bc3173b85871a66
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 8400000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,194 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &92356230978766869
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 3314397423840653037}
- component: {fileID: 4978808551651698537}
m_Layer: 0
m_Name: ElsePlayerPortraitCapture
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &3314397423840653037
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 92356230978766869}
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: 926741264994506648}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &4978808551651698537
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 92356230978766869}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: aa7ed0ae48461f645932270bfda7ec70, type: 3}
m_Name:
m_EditorClassIdentifier:
portraitCamera: {fileID: 3566761532475057225}
outputTexture: {fileID: 8400000, guid: 86b263f7c57f2b2408a59d5c3bafcfe1, type: 2}
portraitLayer: 9
stagePosition: {x: 9999, y: 0, z: 0}
cameraDistance: 2
faceUpLift: 0.04
lookSmoothSpeed: 20
headFaceAxis: 0
flipFaceAxis: 0
fieldOfView: 35
--- !u!1 &6925809458492533610
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 926741264994506648}
- component: {fileID: 3566761532475057225}
- component: {fileID: 3644885308902631293}
- component: {fileID: 5217538199177562225}
m_Layer: 0
m_Name: Camera
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &926741264994506648
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6925809458492533610}
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: 3314397423840653037}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!20 &3566761532475057225
Camera:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6925809458492533610}
m_Enabled: 1
serializedVersion: 2
m_ClearFlags: 4
m_BackGroundColor: {r: 1, g: 1, b: 1, a: 0}
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: 10
field of view: 60
orthographic: 1
orthographic size: 0.2
m_Depth: 0
m_CullingMask:
serializedVersion: 2
m_Bits: 511
m_RenderingPath: -1
m_TargetTexture: {fileID: 8400000, guid: 86b263f7c57f2b2408a59d5c3bafcfe1, type: 2}
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 &3644885308902631293
AudioListener:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6925809458492533610}
m_Enabled: 1
--- !u!114 &5217538199177562225
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6925809458492533610}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3}
m_Name:
m_EditorClassIdentifier:
m_RenderShadows: 0
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
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: ceb796bc537f4b24bbe022ed90af174a
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,193 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &5307011407514547582
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 969654748246802177}
- component: {fileID: 5984941419743339992}
- component: {fileID: 6319271102976364310}
- component: {fileID: 8188555422466428940}
m_Layer: 0
m_Name: Camera
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &969654748246802177
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5307011407514547582}
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: 2751797771262260024}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!20 &5984941419743339992
Camera:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5307011407514547582}
m_Enabled: 1
serializedVersion: 2
m_ClearFlags: 4
m_BackGroundColor: {r: 1, g: 1, b: 1, a: 0}
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: 10
field of view: 60
orthographic: 1
orthographic size: 0.2
m_Depth: 0
m_CullingMask:
serializedVersion: 2
m_Bits: 511
m_RenderingPath: -1
m_TargetTexture: {fileID: 8400000, guid: bba090692914cf841bc3173b85871a66, type: 2}
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 &6319271102976364310
AudioListener:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5307011407514547582}
m_Enabled: 1
--- !u!114 &8188555422466428940
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5307011407514547582}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3}
m_Name:
m_EditorClassIdentifier:
m_RenderShadows: 0
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 &7927669976924756812
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2751797771262260024}
- component: {fileID: 210989495011050937}
m_Layer: 0
m_Name: HostPlayerPortraitCapture
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &2751797771262260024
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7927669976924756812}
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: 969654748246802177}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &210989495011050937
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7927669976924756812}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 93accee0d329e0f45a7be8b74cf0cb99, type: 3}
m_Name:
m_EditorClassIdentifier:
portraitCamera: {fileID: 5984941419743339992}
outputTexture: {fileID: 8400000, guid: bba090692914cf841bc3173b85871a66, type: 2}
portraitLayer: 9
headFaceAxis: 0
flipFaceAxis: 0
cameraDistance: 1.2
faceUpLift: 0.04
lookSmoothSpeed: 20
fieldOfView: 35
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 633f0bb5015e02a4588799a30e68fb62
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+2 -2
View File
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fdca6f6915cfc055b350581a53fbc4d2e19ab522da9eb954295d71d45d8aed58
size 303299
oid sha256:2dfe17492882fb88b93875b027ade9cef638e3bdadaf3a7ccbbcf48dd7ec6b67
size 308617
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using BrewMonster.Scripts.Managers;
using CSNetwork.Protocols.RPCData;
@@ -13,6 +14,13 @@ namespace BrewMonster.Scripts
public List<GameObject> playerModels = new();
public List<int> playerModelIds = new();
/// <summary>
/// Fired (with the roleId) the first time a model becomes active after loading completes.
/// Subscribe from HostPlayerPortraitCapture so it can attach the portrait camera at the
/// right moment even when the async load finishes after AttachToPlayerModelPreview is called.
/// </summary>
public event Action<int> OnModelReady;
private int _loadVersion;
private bool _hasRequestedPlayerModelId;
@@ -104,20 +112,28 @@ namespace BrewMonster.Scripts
/// <summary>
/// Applies the visibility of player models based on the requested player model id.
/// Fires <see cref="OnModelReady"/> the first time a model transitions to active so that
/// portrait capture systems can attach at the correct moment after async loading.
/// </summary>
private void ApplyRequestedModelVisibility()
{
if(!_hasRequestedPlayerModelId)
if (!_hasRequestedPlayerModelId)
return;
int n = Mathf.Min(playerModels.Count, playerModelIds.Count);
for (int i = 0; i < n; i++)
{
GameObject go = playerModels[i];
if(go == null)
if (go == null)
continue;
go.SetActive(playerModelIds[i] == _requestedPlayerModelId);
bool shouldBeActive = playerModelIds[i] == _requestedPlayerModelId;
bool wasActive = go.activeSelf;
go.SetActive(shouldBeActive);
// Fire once when this model first becomes visible
if (shouldBeActive && !wasActive)
OnModelReady?.Invoke(playerModelIds[i]);
}
}
@@ -0,0 +1,221 @@
using BrewMonster.Scripts.Managers;
using UnityEngine;
namespace BrewMonster
{
/// <summary>
/// Renders an EC_ElsePlayer portrait into a RenderTexture for the target HUD (HUDNPC).
///
/// Instead of following the live player transform every frame (which causes jitter when
/// the target moves/jumps), this system clones the visual model into an isolated
/// "portrait stage" at a fixed world position. The portrait camera only ever looks at
/// the static clone, so the output is perfectly stable.
///
/// Public API (unchanged — CECUIManager needs no edits):
/// SetTarget(Transform playerRoot) — clone model, start rendering
/// ClearTarget() — destroy clone, stop rendering
/// OutputTexture — wire to RawImage.texture
/// </summary>
public class ElsePlayerPortraitCapture : MonoSingleton<ElsePlayerPortraitCapture>
{
[Header("Portrait Camera")]
[SerializeField] private Camera portraitCamera;
[Header("Render Output")]
[SerializeField] private RenderTexture outputTexture;
[Header("Portrait Stage")]
[Tooltip("Layer index for the Portrait layer (Project Settings > Tags and Layers).")]
[SerializeField] private int portraitLayer = 9;
[Tooltip("Fixed world position of the clone. Keep far from gameplay to avoid overlap.")]
[SerializeField] private Vector3 stagePosition = new Vector3(9999f, 0f, 0f);
[Header("Framing")]
[Tooltip("Distance the camera sits in front of the face (meters). ~0.51.0 for tight portrait.")]
[SerializeField] private float cameraDistance = 0.7f;
[Tooltip("Extra upward offset applied to the look-at focus point (moves portrait up toward eyes).")]
[SerializeField] private float faceUpLift = 0.04f;
[Tooltip("How fast the camera rotation interpolates toward the face each frame (higher = snappier).")]
[SerializeField][Range(1f, 50f)] private float lookSmoothSpeed = 20f;
[Header("Head Bone Face Direction")]
[Tooltip("Which local axis of Bip01 Head points OUT of the face (toward the camera).")]
[SerializeField] private FaceAxis headFaceAxis = FaceAxis.Forward;
[Tooltip("Flip the chosen face axis (e.g. if Forward gives back-of-head, enable this).")]
[SerializeField] private bool flipFaceAxis;
[SerializeField][Range(10f, 60f)] private float fieldOfView = 35f;
// ── runtime state ────────────────────────────────────────────────────────────
private GameObject _portraitClone;
private Transform _headBone;
public RenderTexture OutputTexture => outputTexture;
// ──────────────────────────────────────────────────────────────────────────────
// Lifecycle
// ──────────────────────────────────────────────────────────────────────────────
protected override void Initialize()
{
EnsureCamera();
EnsureRenderTexture();
}
protected override void OnDestroy()
{
base.OnDestroy();
ClearTarget();
if (outputTexture != null && outputTexture.IsCreated())
outputTexture.Release();
}
// ──────────────────────────────────────────────────────────────────────────────
// Public API
// ──────────────────────────────────────────────────────────────────────────────
/// <summary>
/// Clones the visual model of <paramref name="playerRoot"/> into the portrait stage
/// and starts rendering. Safe to call multiple times — old clone is destroyed first.
/// </summary>
public void SetTarget(Transform playerRoot)
{
ClearTarget();
if (playerRoot == null) return;
Transform modelRoot = PortraitCaptureUtils.FindVisualModelRoot(playerRoot);
if (modelRoot == null)
{
BMLogger.LogWarning("[ElsePlayerPortraitCapture] Visual model root not found — portrait skipped.");
return;
}
_portraitClone = Object.Instantiate(modelRoot.gameObject);
_portraitClone.GetComponentInChildren<Animator>().enabled = false;
_portraitClone.name = "[Portrait] ElsePlayer";
_portraitClone.transform.position = stagePosition;
_portraitClone.transform.rotation = Quaternion.identity;
// Strip physics / game-logic components; keep Animator for idle animation
PortraitCaptureUtils.CleanupCloneComponents(_portraitClone);
// Hide from main camera — portrait camera sees only this layer
PortraitCaptureUtils.SetLayerRecursive(_portraitClone, portraitLayer);
_headBone = PortraitCaptureUtils.FindHeadBone(_portraitClone.transform);
AttachCameraToHeadBone();
SetCameraEnabled(true);
}
/// <summary>
/// Destroys the portrait clone and disables the camera.
/// Call from OnTargetHUDClear / TryHideUINPC.
/// </summary>
public void ClearTarget()
{
if (_portraitClone != null)
{
Object.Destroy(_portraitClone);
_portraitClone = null;
}
DetachCamera();
_headBone = null;
}
private void LateUpdate()
{
if (portraitCamera == null || !portraitCamera.enabled) return;
if (_headBone == null) return;
Vector3 faceCenter = _headBone.position + Vector3.up * faceUpLift;
Vector3 toFace = faceCenter - portraitCamera.transform.position;
if (toFace.sqrMagnitude < 0.0001f) return;
Quaternion targetRot = Quaternion.LookRotation(toFace);
portraitCamera.transform.rotation = Quaternion.Slerp(
portraitCamera.transform.rotation,
targetRot,
Time.deltaTime * lookSmoothSpeed);
}
// ──────────────────────────────────────────────────────────────────────────────
// Internal
// ──────────────────────────────────────────────────────────────────────────────
private void AttachCameraToHeadBone()
{
if (portraitCamera == null) return;
Transform anchor = _headBone != null ? _headBone : _portraitClone.transform;
Vector3 faceDir = GetFaceDirectionWorld(anchor);
Vector3 headPos = anchor.position;
Vector3 faceCenter = headPos + Vector3.up * faceUpLift;
Vector3 camWorldPos = headPos + faceDir * cameraDistance;
portraitCamera.transform.SetParent(anchor, worldPositionStays: false);
portraitCamera.transform.position = camWorldPos;
portraitCamera.transform.LookAt(faceCenter);
}
private Vector3 GetFaceDirectionWorld(Transform bone)
{
Vector3 dir = headFaceAxis switch
{
FaceAxis.Forward => bone.forward,
FaceAxis.Back => -bone.forward,
FaceAxis.Up => bone.up,
FaceAxis.Down => -bone.up,
FaceAxis.Right => bone.right,
FaceAxis.Left => -bone.right,
_ => bone.forward
};
return flipFaceAxis ? -dir : dir;
}
private void DetachCamera()
{
if (portraitCamera != null)
{
portraitCamera.transform.SetParent(transform, worldPositionStays: false);
SetCameraEnabled(false);
}
}
private void SetCameraEnabled(bool enabled)
{
if (portraitCamera != null)
portraitCamera.enabled = enabled;
}
private void EnsureCamera()
{
if (portraitCamera != null)
{
portraitCamera.cullingMask = 1 << portraitLayer;
portraitCamera.fieldOfView = fieldOfView;
portraitCamera.enabled = false;
return;
}
portraitCamera = PortraitCaptureUtils.CreatePortraitCamera(
transform, "PortraitCamera_ElsePlayer", portraitLayer, fieldOfView);
}
private void EnsureRenderTexture()
{
if (outputTexture != null)
{
if (portraitCamera != null) portraitCamera.targetTexture = outputTexture;
return;
}
outputTexture = PortraitCaptureUtils.CreatePortraitRT("ElsePlayerPortraitRT", portraitCamera);
}
public enum FaceAxis { Forward, Back, Up, Down, Right, Left }
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: aa7ed0ae48461f645932270bfda7ec70
+14
View File
@@ -13,6 +13,7 @@ namespace BrewMonster
[SerializeField] private TextMeshProUGUI _nameText;
[SerializeField] private TextMeshProUGUI _statText;
[SerializeField] private Image healthImage;
[SerializeField] private RawImage _avatarImage;
private void OnEnable()
{
@@ -53,5 +54,18 @@ namespace BrewMonster
{
healthImage.fillAmount = health;
}
public void SetAvatar(Texture tex)
{
if (_avatarImage == null) return;
_avatarImage.texture = tex;
_avatarImage.gameObject.SetActive(tex != null);
}
public void ShowAvatar(bool show)
{
if (_avatarImage != null)
_avatarImage.gameObject.SetActive(show);
}
}
}
@@ -0,0 +1,353 @@
using BrewMonster.Scripts;
using BrewMonster.Scripts.Managers;
using UnityEngine;
namespace BrewMonster
{
/// <summary>
/// Renders the host player's portrait into a RenderTexture for the HUD.
///
/// Integrates with <see cref="PlayerModelPreview"/>:
/// • If the model is already loaded when AttachToPlayerModelPreview is called → attach immediately.
/// • If the model is still loading (async) → subscribe to PlayerModelPreview.OnModelReady and
/// attach automatically as soon as the model becomes active.
///
/// Camera is parented directly to the "Bip01 Head" bone so it rides every head
/// movement from idle animation without any world-space tracking code.
/// Every LateUpdate the camera rotation is Slerp-interpolated toward the face.
///
/// Public API:
/// AttachToPlayerModelPreview(int roleId) — primary; handles async loading
/// SetHostPlayer(Transform root) — fallback; any player root directly
/// Refresh(int roleId) — call after equipment reload
/// ClearPortrait() — detach camera, stop rendering
/// OutputTexture — wire to RawImage.texture on HUD
/// </summary>
public class HostPlayerPortraitCapture : MonoSingleton<HostPlayerPortraitCapture>
{
// ──────────────────────────────────────────────────────────────────────────────
// Inspector
// ──────────────────────────────────────────────────────────────────────────────
[Header("Portrait Camera")]
[SerializeField] private Camera portraitCamera;
[Header("Render Output")]
[SerializeField] private RenderTexture outputTexture;
[Header("Portrait Layer")]
[Tooltip("Layer index rendered ONLY by the portrait camera (Project Settings > Tags and Layers).")]
[SerializeField] private int portraitLayer = 29;
[Header("Head Bone Face Direction")]
[Tooltip("Which local axis of Bip01 Head points OUT of the face (toward the camera).\n" +
"In most Biped rigs exported from 3ds Max/Unity: Forward (+Z).\n" +
"Switch to Back (-Z) or Up (+Y) if portrait faces wrong way.")]
[SerializeField] private FaceAxis headFaceAxis = FaceAxis.Forward;
[Tooltip("Flip the chosen face axis (e.g. if Forward gives back-of-head, enable this).")]
[SerializeField] private bool flipFaceAxis;
[Header("Framing")]
[Tooltip("Distance the camera sits in front of the face (meters). ~0.51.0 for tight portrait.")]
[SerializeField] private float cameraDistance = 0.7f;
[Tooltip("Extra upward offset applied to the look-at focus point (moves portrait up toward eyes).")]
[SerializeField] private float faceUpLift = 0.04f;
[Tooltip("How fast the camera rotation interpolates toward the face each frame (higher = snappier).")]
[SerializeField][Range(1f, 50f)] private float lookSmoothSpeed = 20f;
[SerializeField][Range(10f, 60f)] private float fieldOfView = 40f;
// ──────────────────────────────────────────────────────────────────────────────
// Runtime state
// ──────────────────────────────────────────────────────────────────────────────
private Transform _modelTransform;
private Transform _headBone;
// Stored while waiting for async model load; -1 means no pending request
private int _pendingRoleId = -1;
public RenderTexture OutputTexture => outputTexture;
// ──────────────────────────────────────────────────────────────────────────────
// Lifecycle
// ──────────────────────────────────────────────────────────────────────────────
protected override void Initialize()
{
EnsureCamera();
EnsureRenderTexture();
}
protected override void OnDestroy()
{
base.OnDestroy();
UnsubscribeModelReady();
ClearPortrait();
if (outputTexture != null && outputTexture.IsCreated())
outputTexture.Release();
}
// ──────────────────────────────────────────────────────────────────────────────
// Public API
// ──────────────────────────────────────────────────────────────────────────────
/// <summary>
/// Attaches the portrait camera to the model matching <paramref name="roleId"/> inside
/// <see cref="PlayerModelPreview"/>.
///
/// Two paths:
/// 1. Model already loaded and active → attach immediately.
/// 2. Model still loading (async) → subscribe to <see cref="PlayerModelPreview.OnModelReady"/>
/// and attach automatically when the event fires for this roleId.
/// </summary>
public void AttachToPlayerModelPreview(int roleId)
{
UnsubscribeModelReady();
DetachCamera();
if (PlayerModelPreview.Instance == null)
{
BMLogger.LogWarning("[HostPlayerPortraitCapture] PlayerModelPreview.Instance is null.");
return;
}
// Path 1: model already in the list and active → attach now
if (TryAttachFromPreview(roleId))
return;
// Path 2: model not ready yet — wait for OnModelReady event
_pendingRoleId = roleId;
PlayerModelPreview.Instance.OnModelReady += OnPreviewModelReady;
BMLogger.Log($"[HostPlayerPortraitCapture] Model for roleId={roleId} not ready, waiting for load...");
}
/// <summary>Fallback: attach directly to any player hierarchy root.</summary>
public void SetHostPlayer(Transform hostPlayerRoot)
{
DetachCamera();
if (hostPlayerRoot == null) return;
_modelTransform = hostPlayerRoot;
SetupPortrait();
}
/// <summary>Re-attach after equipment reload rebuilds the PlayerModelPreview model.</summary>
public void Refresh(int roleId) => AttachToPlayerModelPreview(roleId);
/// <summary>Detach camera and stop rendering. Call on logout / scene unload.</summary>
public void ClearPortrait()
{
UnsubscribeModelReady();
DetachCamera();
_modelTransform = null;
_headBone = null;
_pendingRoleId = -1;
}
// ──────────────────────────────────────────────────────────────────────────────
// LateUpdate — smooth Slerp toward face every frame
// ──────────────────────────────────────────────────────────────────────────────
private void LateUpdate()
{
if (portraitCamera == null || !portraitCamera.enabled) return;
if (_headBone == null) return;
// Focus point: centre of face, nudged upward by faceUpLift
Vector3 faceCenter = _headBone.position + Vector3.up * faceUpLift;
// Direction from camera to face centre
Vector3 toFace = faceCenter - portraitCamera.transform.position;
if (toFace.sqrMagnitude < 0.0001f) return;
// Slerp rotation smoothly toward the look-at orientation
Quaternion targetRot = Quaternion.LookRotation(toFace);
portraitCamera.transform.rotation = Quaternion.Slerp(
portraitCamera.transform.rotation,
targetRot,
Time.deltaTime * lookSmoothSpeed);
}
// ──────────────────────────────────────────────────────────────────────────────
// PlayerModelPreview integration helpers
// ──────────────────────────────────────────────────────────────────────────────
/// <summary>
/// Called by <see cref="PlayerModelPreview.OnModelReady"/> when a model becomes active.
/// Attaches only if the roleId matches the pending request.
/// </summary>
private void OnPreviewModelReady(int readyRoleId)
{
if (readyRoleId != _pendingRoleId) return;
UnsubscribeModelReady();
TryAttachFromPreview(readyRoleId);
}
/// <summary>
/// Tries to find and attach to the model for <paramref name="roleId"/> in PlayerModelPreview.
/// Returns true if the model was found (active or not) and attachment succeeded.
/// </summary>
private bool TryAttachFromPreview(int roleId)
{
var preview = PlayerModelPreview.Instance;
if (preview == null) return false;
var models = preview.playerModels;
var ids = preview.playerModelIds;
int count = Mathf.Min(models.Count, ids.Count);
for (int i = 0; i < count; i++)
{
if (ids[i] != roleId || models[i] == null) continue;
// Ensure the model is active so the camera can render it
if (!models[i].activeSelf)
models[i].SetActive(true);
_modelTransform = models[i].transform;
SetupPortrait();
return true;
}
return false;
}
private void UnsubscribeModelReady()
{
if (PlayerModelPreview.Instance != null)
PlayerModelPreview.Instance.OnModelReady -= OnPreviewModelReady;
_pendingRoleId = -1;
}
// ──────────────────────────────────────────────────────────────────────────────
// Internal
// ──────────────────────────────────────────────────────────────────────────────
private void SetupPortrait()
{
if (_modelTransform == null) return;
// Move model to portrait layer so the dedicated camera can render it
PortraitCaptureUtils.SetLayerRecursive(_modelTransform.gameObject, portraitLayer);
// Find head bone — primary target for camera attachment
_headBone = PortraitCaptureUtils.FindChildByName(_modelTransform, "Bip01 Head")
?? PortraitCaptureUtils.FindChildByName(_modelTransform, "Bip001 Head")
?? PortraitCaptureUtils.FindHeadBone(_modelTransform);
if (_headBone == null)
BMLogger.LogWarning("[HostPlayerPortraitCapture] Head bone not found — portrait skipped.");
AttachCameraToHeadBone();
SetCameraEnabled(true);
}
/// <summary>
/// Parents the portrait camera to "Bip01 Head" and sets its initial position
/// directly in front of the face.
///
/// Coordinate math:
/// faceDir = local axis of headBone that points OUT of the face (Inspector: headFaceAxis)
/// camWorldPos = headBone.position + faceDir * cameraDistance
///
/// After SetParent, Unity converts this world position into a localPosition relative
/// to the head bone automatically. From that point on the camera rides the bone —
/// no per-frame world-space override is needed. Only LookAt is refreshed in LateUpdate.
/// </summary>
private void AttachCameraToHeadBone()
{
if (portraitCamera == null) return;
Transform anchor = _headBone ?? _modelTransform;
// Face direction in world space — derived from head bone's own local axes
Vector3 faceDir = GetFaceDirectionWorld(anchor);
Vector3 headPos = anchor.position;
Vector3 faceCenter = headPos + Vector3.up * faceUpLift;
// Camera sits cameraDistance units in FRONT of the face (outside, looking back in)
Vector3 camWorldPos = headPos + faceDir * cameraDistance;
// Parent to head bone first with worldPositionStays:false (resets local to zero),
// then assign world position — Unity stores the correct localPosition internally
portraitCamera.transform.SetParent(anchor, worldPositionStays: false);
portraitCamera.transform.position = camWorldPos;
// Initial look-at so first frame is already correct before LateUpdate fires
portraitCamera.transform.LookAt(faceCenter);
}
/// <summary>
/// Returns the world-space direction that points OUT OF the character's face,
/// derived from the head bone's local coordinate axes according to <see cref="headFaceAxis"/>.
///
/// Biped rig convention (3ds Max → Unity export):
/// In most Perfect World rigs, the head bone's +Z (forward) points toward the face.
/// If the portrait comes out upside-down or backwards, toggle <see cref="headFaceAxis"/>
/// and/or <see cref="flipFaceAxis"/> in the Inspector without changing code.
/// </summary>
private Vector3 GetFaceDirectionWorld(Transform bone)
{
Vector3 dir = headFaceAxis switch
{
FaceAxis.Forward => bone.forward,
FaceAxis.Back => -bone.forward,
FaceAxis.Up => bone.up,
FaceAxis.Down => -bone.up,
FaceAxis.Right => bone.right,
FaceAxis.Left => -bone.right,
_ => bone.forward
};
return flipFaceAxis ? -dir : dir;
}
private void DetachCamera()
{
if (portraitCamera != null)
{
portraitCamera.transform.SetParent(transform, worldPositionStays: false);
SetCameraEnabled(false);
}
}
private void SetCameraEnabled(bool enabled)
{
if (portraitCamera != null)
portraitCamera.enabled = enabled;
}
private void EnsureCamera()
{
if (portraitCamera != null)
{
portraitCamera.cullingMask = 1 << portraitLayer;
portraitCamera.fieldOfView = fieldOfView;
portraitCamera.enabled = false;
return;
}
portraitCamera = PortraitCaptureUtils.CreatePortraitCamera(
transform, "PortraitCamera_HostPlayer", portraitLayer, fieldOfView);
}
private void EnsureRenderTexture()
{
if (outputTexture != null)
{
if (portraitCamera != null) portraitCamera.targetTexture = outputTexture;
return;
}
outputTexture = PortraitCaptureUtils.CreatePortraitRT("HostPlayerPortraitRT", portraitCamera);
}
// ──────────────────────────────────────────────────────────────────────────────
// Helpers
// ──────────────────────────────────────────────────────────────────────────────
public enum FaceAxis { Forward, Back, Up, Down, Right, Left }
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 93accee0d329e0f45a7be8b74cf0cb99
@@ -0,0 +1,139 @@
using UnityEngine;
namespace BrewMonster
{
/// <summary>
/// Shared static helpers used by ElsePlayerPortraitCapture and HostPlayerPortraitCapture.
/// </summary>
public static class PortraitCaptureUtils
{
private static readonly string[] HeadBoneNames =
{
"Bip01 Head", "Bip001 Head", "head", "Head", "Bone_Head", "HEAD"
};
// ──────────────────────────────────────────────────────────────────────────────
// Model / bone search
// ──────────────────────────────────────────────────────────────────────────────
/// <summary>
/// Returns the root transform of the visual model inside <paramref name="playerRoot"/>.
/// Tries to find the first child that owns a SkinnedMeshRenderer; falls back to the
/// root itself if a SkinnedMeshRenderer is found anywhere in the hierarchy.
/// </summary>
public static Transform FindVisualModelRoot(Transform playerRoot)
{
if (playerRoot == null) return null;
// Walk immediate children first — model root is usually a direct child
for (int i = 0; i < playerRoot.childCount; i++)
{
var child = playerRoot.GetChild(i);
if (child.GetComponentInChildren<SkinnedMeshRenderer>() != null)
return child;
}
// Fallback: if the root itself contains a SMR, use it
if (playerRoot.GetComponentInChildren<SkinnedMeshRenderer>() != null)
return playerRoot;
return null;
}
/// <summary>
/// Searches <paramref name="modelRoot"/>'s hierarchy for a head bone by common name patterns.
/// </summary>
public static Transform FindHeadBone(Transform modelRoot)
{
if (modelRoot == null) return null;
foreach (var boneName in HeadBoneNames)
{
var bone = FindChildByName(modelRoot, boneName);
if (bone != null) return bone;
}
return null;
}
/// <summary>Recursive depth-first search for a child transform by exact name.</summary>
public static Transform FindChildByName(Transform root, string name)
{
if (root.name == name) return root;
for (int i = 0; i < root.childCount; i++)
{
var result = FindChildByName(root.GetChild(i), name);
if (result != null) return result;
}
return null;
}
// ──────────────────────────────────────────────────────────────────────────────
// Clone setup
// ──────────────────────────────────────────────────────────────────────────────
/// <summary>
/// Strips non-visual components from a cloned GameObject so it becomes a lightweight
/// render-only puppet. Keeps Animator so idle animations continue to play.
/// </summary>
public static void CleanupCloneComponents(GameObject go)
{
foreach (var col in go.GetComponentsInChildren<Collider>(true))
Object.Destroy(col);
foreach (var rb in go.GetComponentsInChildren<Rigidbody>(true))
Object.Destroy(rb);
foreach (var mb in go.GetComponentsInChildren<MonoBehaviour>(true))
{
if (mb is Animator) continue;
Object.Destroy(mb);
}
}
/// <summary>Recursively sets the layer of <paramref name="go"/> and all its children.</summary>
public static void SetLayerRecursive(GameObject go, int layer)
{
go.layer = layer;
foreach (Transform child in go.transform)
SetLayerRecursive(child.gameObject, layer);
}
// ──────────────────────────────────────────────────────────────────────────────
// Camera / RenderTexture factories
// ──────────────────────────────────────────────────────────────────────────────
/// <summary>
/// Creates a Camera child GameObject under <paramref name="parent"/> whose culling mask
/// covers only <paramref name="portraitLayer"/>. Camera starts disabled.
/// </summary>
public static Camera CreatePortraitCamera(Transform parent, string goName, int portraitLayer, float fov)
{
var camGO = new GameObject(goName);
camGO.transform.SetParent(parent, false);
var cam = camGO.AddComponent<Camera>();
cam.cullingMask = 1 << portraitLayer;
cam.clearFlags = CameraClearFlags.SolidColor;
cam.backgroundColor = Color.clear;
cam.fieldOfView = fov;
cam.nearClipPlane = 0.1f;
cam.farClipPlane = 200f;
cam.enabled = false;
return cam;
}
/// <summary>
/// Creates a 256×256 ARGB32 RenderTexture and wires it to <paramref name="cam"/> if provided.
/// </summary>
public static RenderTexture CreatePortraitRT(string rtName, Camera cam)
{
var rt = new RenderTexture(256, 256, 16, RenderTextureFormat.ARGB32)
{
name = rtName,
antiAliasing = 1
};
rt.Create();
if (cam != null) cam.targetTexture = rt;
return rt;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 5d2d46da24e62da45a4abea222294fd2
+23
View File
@@ -1,4 +1,5 @@
using BrewMonster;
using BrewMonster.Managers;
using BrewMonster.Network;
using BrewMonster.UI;
using System;
@@ -8,6 +9,7 @@ using BrewMonster.Scripts.Chat.EmotionData;
using BrewMonster.Scripts.Managers;
using BrewMonster.Scripts.Task.UI;
using BrewMonster.Scripts.UI;
using CSNetwork.GPDataType;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
@@ -172,10 +174,29 @@ public class CECUIManager : MonoSingleton<CECUIManager>
float fill = obj.MaxHealth > 0 ? (float)obj.CurrentHealth / (float)obj.MaxHealth : 1f;
npsUI.SetHealthImage(fill);
currentTargetNPCID = obj.IDNPC;
// Render ElsePlayer portrait avatar — mirrors C++ PlayerRenderPortrait / SetRenderCallback
if (GPDataTypeHelper.ISPLAYERID(obj.IDNPC))
{
var elsePlayer = EC_ManMessageMono.Instance?.GetECManPlayer?
.GetPlayer(obj.IDNPC) as EC_ElsePlayer;
if (elsePlayer != null)
{
ElsePlayerPortraitCapture.Instance?.SetTarget(elsePlayer.transform);
npsUI.SetAvatar(ElsePlayerPortraitCapture.Instance?.OutputTexture);
return;
}
}
// NPC thường hoặc không tìm thấy ElsePlayer — ẩn avatar
ElsePlayerPortraitCapture.Instance?.ClearTarget();
npsUI.ShowAvatar(false);
}
private void OnTargetHUDClear(CECHostPlayer.TargetHUDClearEvent _)
{
ElsePlayerPortraitCapture.Instance?.ClearTarget();
npsUI.ShowAvatar(false);
npsUI.gameObject.SetActive(false);
}
@@ -187,6 +208,8 @@ public class CECUIManager : MonoSingleton<CECUIManager>
private void TryHideUINPC(NPCDiedEvent obj)
{
if (obj.NPCID != currentTargetNPCID) return;
ElsePlayerPortraitCapture.Instance?.ClearTarget();
npsUI.ShowAvatar(false);
npsUI.gameObject.SetActive(false);
}
+1
View File
@@ -187,6 +187,7 @@ namespace BrewMonster.UI
if (PlayerModelPreview.Instance.playerModelIds != null && PlayerModelPreview.Instance.playerModelIds.Contains(roleId))
{
PlayerModelPreview.Instance.ShowPlayerModel(roleId);
HostPlayerPortraitCapture.Instance?.AttachToPlayerModelPreview(roleId);
_btnEnterGame.interactable = true;
_showModelReadyCoroutine = null;
yield break; // Model is ready, show it and stop this coroutine