Merge branch 'main' into DO-35-리플레이씬에서-기보-데이터로-돌-놓고-없애기

# Conflicts:
#	Assets/Resources/Prefabs/Panels/Main Panel.prefab
#	Assets/Script/UI/PanelController/ReplayPanelItemsController.cs
메인브랜치의 MainPanel로 통합
show().base 삭제로 해결
This commit is contained in:
HaeinLEE 2025-03-21 14:12:31 +09:00
commit bd6a1cf6be
49 changed files with 22010 additions and 2992 deletions

View File

@ -214,102 +214,6 @@ MonoBehaviour:
m_EditorClassIdentifier:
panelManagerPrefab: {fileID: 3475740041361426276, guid: 085ca07ca90c92545b2594bd13412701, type: 3}
audioManagerPrefab: {fileID: 2946408323859178723, guid: e829818dce39a5d4383e061111bed871, type: 3}
--- !u!82 &271965292
AudioSource:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 271965288}
m_Enabled: 1
serializedVersion: 4
OutputAudioMixerGroup: {fileID: 0}
m_audioClip: {fileID: 0}
m_PlayOnAwake: 1
m_Volume: 1
m_Pitch: 1
Loop: 0
Mute: 0
Spatialize: 0
SpatializePostEffects: 0
Priority: 128
DopplerLevel: 1
MinDistance: 1
MaxDistance: 500
Pan2D: 0
rolloffMode: 0
BypassEffects: 0
BypassListenerEffects: 0
BypassReverbZones: 0
rolloffCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
panLevelCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
spreadCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
reverbZoneMixCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
--- !u!1 &333752980
GameObject:
m_ObjectHideFlags: 0
@ -772,7 +676,7 @@ Canvas:
m_OverridePixelPerfect: 0
m_SortingBucketNormalizedSize: 0
m_VertexColorAlwaysGammaSpace: 0
m_AdditionalShaderChannelsFlag: 0
m_AdditionalShaderChannelsFlag: 25
m_UpdateRectTransformForStandalone: 0
m_SortingLayerID: 0
m_SortingOrder: 0
@ -788,7 +692,8 @@ RectTransform:
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0, y: 0, z: 0}
m_ConstrainProportionsScale: 0
m_Children: []
m_Children:
- {fileID: 1545261106}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}

View File

@ -38,4 +38,9 @@ public class MainPanelButtonController : MonoBehaviour
{
GameManager.Instance.panelManager.OpenReplayPanel();
}
//public void OpenRankingPanelClick()
//{
//GameManager.Instance.panelManager.OnRankingPanelClick();
//}
}

File diff suppressed because it is too large Load Diff

19688
Assets/KSH/GameCopy_AI.unity Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: e200b684d5479a643aa06e6361c430c9
guid: e61de5ff6b71b2e45b0878ccd8c8033a
DefaultImporter:
externalObjects: {}
userData:

View File

@ -6020,6 +6020,50 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 9726acf6f82a3644ba31eda5ef496991, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1 &820078853
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 820078855}
- component: {fileID: 820078854}
m_Layer: 0
m_Name: OmokAI
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &820078854
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 820078853}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 576baa0fe98d40608bf48109ba5ed788, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!4 &820078855
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 820078853}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 540, y: 1760, 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!1001 &831992430
PrefabInstance:
m_ObjectHideFlags: 0
@ -8175,6 +8219,8 @@ GameObject:
serializedVersion: 6
m_Component:
- component: {fileID: 1118625358}
- component: {fileID: 1118625360}
- component: {fileID: 1118625359}
m_Layer: 0
m_Name: GameManager
m_TagString: Untagged
@ -8197,6 +8243,116 @@ Transform:
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1118625359
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1118625357}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 35a4c6d5d3a97b444b968e68ec8bb9f7, type: 3}
m_Name:
m_EditorClassIdentifier:
panelManagerPrefab: {fileID: 3475740041361426276, guid: 085ca07ca90c92545b2594bd13412701, type: 3}
audioManagerPrefab: {fileID: 2946408323859178723, guid: e829818dce39a5d4383e061111bed871, type: 3}
--- !u!82 &1118625360
AudioSource:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1118625357}
m_Enabled: 1
serializedVersion: 4
OutputAudioMixerGroup: {fileID: 0}
m_audioClip: {fileID: 0}
m_PlayOnAwake: 1
m_Volume: 1
m_Pitch: 1
Loop: 0
Mute: 0
Spatialize: 0
SpatializePostEffects: 0
Priority: 128
DopplerLevel: 1
MinDistance: 1
MaxDistance: 500
Pan2D: 0
rolloffMode: 0
BypassEffects: 0
BypassListenerEffects: 0
BypassReverbZones: 0
rolloffCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
panLevelCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
spreadCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
reverbZoneMixCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
--- !u!1001 &1121996003
PrefabInstance:
m_ObjectHideFlags: 0
@ -17421,7 +17577,7 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 7155909258766834886, guid: b1a31da84076a534cae7cb20a7913a93, type: 3}
propertyPath: m_Name
value: '[Canvas] Game UI'
value: Canvas
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
@ -17438,3 +17594,4 @@ SceneRoots:
- {fileID: 307507522}
- {fileID: 1632066875}
- {fileID: 1118625358}
- {fileID: 820078855}

View File

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

View File

@ -12,8 +12,6 @@ GameObject:
- component: {fileID: 2911450618660204948}
- component: {fileID: 2729261171078307673}
- component: {fileID: 4465141290128179683}
- component: {fileID: 2835505888941074797}
- component: {fileID: 5687316735128766768}
m_Layer: 5
m_Name: Viewport
m_TagString: Untagged
@ -37,7 +35,7 @@ RectTransform:
m_Father: {fileID: 1188344226235047902}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0, y: 1}
@ -92,46 +90,6 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
m_ShowMaskGraphic: 1
--- !u!114 &2835505888941074797
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1858212639388121281}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3}
m_Name:
m_EditorClassIdentifier:
m_HorizontalFit: 1
m_VerticalFit: 2
--- !u!114 &5687316735128766768
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1858212639388121281}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Padding:
m_Left: 0
m_Right: 0
m_Top: 0
m_Bottom: 0
m_ChildAlignment: 0
m_Spacing: 5
m_ChildForceExpandWidth: 1
m_ChildForceExpandHeight: 1
m_ChildControlWidth: 0
m_ChildControlHeight: 0
m_ChildScaleWidth: 0
m_ChildScaleHeight: 0
m_ReverseArrangement: 0
--- !u!1 &2602607401298266001
GameObject:
m_ObjectHideFlags: 0
@ -167,9 +125,9 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 40, y: -40}
m_SizeDelta: {x: 36, y: 40}
m_Pivot: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 80, y: -80}
m_SizeDelta: {x: 80, y: 80}
m_Pivot: {x: 0.40175802, y: 0.5}
--- !u!222 &6169341149558268219
CanvasRenderer:
m_ObjectHideFlags: 0
@ -198,7 +156,7 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 21300000, guid: 427761ad91f2d9d4e85af2f6a2894218, type: 3}
m_Sprite: {fileID: 21300000, guid: 2f19224efc36c9b49978cafd03ede1bb, type: 3}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
@ -252,7 +210,7 @@ MonoBehaviour:
m_OnClick:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 0}
- m_Target: {fileID: 1589736077307570888}
m_TargetAssemblyTypeName: LeaderBoardController, Assembly-CSharp
m_MethodName: OnBackButtonClicked
m_Mode: 1
@ -301,8 +259,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: 0, y: -212}
m_SizeDelta: {x: 1080, y: 1496}
m_AnchoredPosition: {x: 0, y: -112.84912}
m_SizeDelta: {x: 1080, y: 1694.3018}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &824343901473742242
CanvasRenderer:
@ -325,7 +283,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 0.392}
m_Color: {r: 1, g: 1, b: 1, a: 0}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
@ -361,14 +319,14 @@ MonoBehaviour:
m_Elasticity: 0.1
m_Inertia: 1
m_DecelerationRate: 0.135
m_ScrollSensitivity: 1
m_ScrollSensitivity: 100
m_Viewport: {fileID: 8740302936727227434}
m_HorizontalScrollbar: {fileID: 0}
m_VerticalScrollbar: {fileID: 3478506922544614059}
m_HorizontalScrollbarVisibility: 2
m_VerticalScrollbarVisibility: 2
m_HorizontalScrollbarSpacing: -3
m_VerticalScrollbarSpacing: -3
m_VerticalScrollbarSpacing: -15
m_OnValueChanged:
m_PersistentCalls:
m_Calls: []
@ -523,6 +481,7 @@ GameObject:
m_Component:
- component: {fileID: 227489081374553986}
- component: {fileID: 6416582684501908282}
- component: {fileID: 2816695216852066709}
m_Layer: 5
m_Name: Content
m_TagString: Untagged
@ -544,10 +503,10 @@ RectTransform:
m_Children: []
m_Father: {fileID: 8740302936727227434}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 1060, y: 1493.8}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 0, y: 0.000034332275}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0, y: 1}
--- !u!114 &6416582684501908282
MonoBehaviour:
@ -562,7 +521,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
m_Padding:
m_Left: 0
m_Left: 100
m_Right: 0
m_Top: 0
m_Bottom: 0
@ -570,11 +529,25 @@ MonoBehaviour:
m_Spacing: 50
m_ChildForceExpandWidth: 1
m_ChildForceExpandHeight: 1
m_ChildControlWidth: 1
m_ChildControlHeight: 1
m_ChildControlWidth: 0
m_ChildControlHeight: 0
m_ChildScaleWidth: 0
m_ChildScaleHeight: 0
m_ReverseArrangement: 0
--- !u!114 &2816695216852066709
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3568653418969612126}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3}
m_Name:
m_EditorClassIdentifier:
m_HorizontalFit: 1
m_VerticalFit: 2
--- !u!1 &4202334049855500581
GameObject:
m_ObjectHideFlags: 0
@ -633,14 +606,14 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 0, b: 0, a: 1}
m_Color: {r: 1, g: 0.9607843, b: 0.8980392, a: 0.32156864}
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: 10905, guid: 0000000000000000f000000000000000, type: 0}
m_Sprite: {fileID: 21300000, guid: c06da2c324d0fee43b8b1998cf9fcd09, type: 3}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
@ -659,9 +632,9 @@ GameObject:
serializedVersion: 6
m_Component:
- component: {fileID: 2084150948806744478}
- component: {fileID: 7465938882283913230}
- component: {fileID: 1080045383039686757}
- component: {fileID: 3478506922544614059}
- component: {fileID: 6301460630123320729}
- component: {fileID: 1849839432792571899}
m_Layer: 5
m_Name: Scrollbar Vertical
m_TagString: Untagged
@ -687,46 +660,8 @@ RectTransform:
m_AnchorMin: {x: 1, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 20, y: -17}
m_SizeDelta: {x: 20, y: 0}
m_Pivot: {x: 1, y: 1}
--- !u!222 &7465938882283913230
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5452094549188284008}
m_CullTransparentMesh: 1
--- !u!114 &1080045383039686757
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5452094549188284008}
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: 0, b: 0, 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: 0}
m_Type: 1
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 &3478506922544614059
MonoBehaviour:
m_ObjectHideFlags: 0
@ -746,12 +681,12 @@ MonoBehaviour:
m_SelectOnDown: {fileID: 0}
m_SelectOnLeft: {fileID: 0}
m_SelectOnRight: {fileID: 0}
m_Transition: 1
m_Transition: 0
m_Colors:
m_NormalColor: {r: 1, g: 0.55039775, b: 0, a: 1}
m_NormalColor: {r: 1, g: 0.28627452, b: 0, a: 1}
m_HighlightedColor: {r: 1, g: 0, b: 0, a: 1}
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_PressedColor: {r: 0.93710685, g: 0.33299702, b: 0.33299702, a: 1}
m_SelectedColor: {r: 0.8409424, g: 0.9685534, b: 0.57564956, a: 1}
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
m_ColorMultiplier: 1
m_FadeDuration: 0.1
@ -776,6 +711,44 @@ MonoBehaviour:
m_OnValueChanged:
m_PersistentCalls:
m_Calls: []
--- !u!222 &6301460630123320729
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5452094549188284008}
m_CullTransparentMesh: 1
--- !u!114 &1849839432792571899
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5452094549188284008}
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: 0, b: 0, a: 0}
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: 0}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &6747908716986530392
GameObject:
m_ObjectHideFlags: 0
@ -810,7 +783,7 @@ RectTransform:
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: -20, y: -20}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &7787380462476439032
GameObject:
@ -882,7 +855,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: -300}
m_AnchoredPosition: {x: 0, y: -115}
m_SizeDelta: {x: 0, y: 50}
m_Pivot: {x: 0.5, y: 1}
--- !u!222 &5432856254510042803
@ -913,7 +886,7 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: Ranking
m_text: "\uB7AD\uD0B9"
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 85a19688db53c77469fc4406b01045da, type: 2}
m_sharedMaterial: {fileID: -2477908578676791210, guid: 85a19688db53c77469fc4406b01045da, type: 2}

View File

@ -13,7 +13,7 @@ GameObject:
- component: {fileID: 3015309825845733259}
- component: {fileID: 202560210887412943}
m_Layer: 5
m_Name: Rank Button
m_Name: Leaderboard Button
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
@ -388,9 +388,9 @@ MonoBehaviour:
m_OnClick:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 0}
m_TargetAssemblyTypeName: PanelManager, Assembly-CSharp
m_MethodName: OpenSettingsPanel
- m_Target: {fileID: 7488082087611091670}
m_TargetAssemblyTypeName: MainPanelController, Assembly-CSharp
m_MethodName: OpenSettingButtonClick
m_Mode: 1
m_Arguments:
m_ObjectArgument: {fileID: 0}
@ -410,7 +410,6 @@ GameObject:
m_Component:
- component: {fileID: 2720476515686469179}
- component: {fileID: 7796730417217941349}
- component: {fileID: 5873276942444222861}
m_Layer: 5
m_Name: Buttons
m_TagString: Untagged
@ -468,18 +467,6 @@ MonoBehaviour:
m_ChildScaleWidth: 0
m_ChildScaleHeight: 0
m_ReverseArrangement: 0
--- !u!114 &5873276942444222861
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1637449823560643887}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 93bfed50167472f489a27f0518070ed5, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1 &2440131775796007731
GameObject:
m_ObjectHideFlags: 0
@ -869,9 +856,9 @@ MonoBehaviour:
m_OnClick:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 5873276942444222861}
m_TargetAssemblyTypeName: MainPanelButtonController, Assembly-CSharp
m_MethodName: OnShopPanelClick
- m_Target: {fileID: 7488082087611091670}
m_TargetAssemblyTypeName: MainPanelController, Assembly-CSharp
m_MethodName: OnShopButtonClick
m_Mode: 1
m_Arguments:
m_ObjectArgument: {fileID: 0}
@ -1199,7 +1186,7 @@ GameObject:
- component: {fileID: 5553341337845712589}
- component: {fileID: 837160915965981132}
m_Layer: 5
m_Name: Game Record Button
m_Name: Replay Button
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
@ -1307,9 +1294,9 @@ MonoBehaviour:
m_OnClick:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 5873276942444222861}
m_TargetAssemblyTypeName: MainPanelButtonController, Assembly-CSharp
m_MethodName: OpenReplayPanelClick
- m_Target: {fileID: 7488082087611091670}
m_TargetAssemblyTypeName: MainPanelController, Assembly-CSharp
m_MethodName: OpenReplayButtonClick
m_Mode: 1
m_Arguments:
m_ObjectArgument: {fileID: 0}
@ -1470,7 +1457,7 @@ GameObject:
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
m_IsActive: 0
--- !u!224 &405965270916774547
RectTransform:
m_ObjectHideFlags: 0
@ -1714,7 +1701,7 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: "\uBC15\uC77C\uD658"
m_text: "\uD638\uB7AD\uC774"
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 85a19688db53c77469fc4406b01045da, type: 2}
m_sharedMaterial: {fileID: -2477908578676791210, guid: 85a19688db53c77469fc4406b01045da, type: 2}
@ -1800,7 +1787,7 @@ GameObject:
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
m_IsActive: 1
--- !u!224 &8172929902404983356
RectTransform:
m_ObjectHideFlags: 0

View File

@ -1,590 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &154459105952450598
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4212628350298048884}
- component: {fileID: 7043576313081816255}
- component: {fileID: 1260880031756413447}
- component: {fileID: 4783908613280260710}
- component: {fileID: 6733810038267402749}
m_Layer: 5
m_Name: Ranking Panel
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &4212628350298048884
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 154459105952450598}
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: 3321716118030398287}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &7043576313081816255
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 154459105952450598}
m_CullTransparentMesh: 1
--- !u!114 &1260880031756413447
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 154459105952450598}
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: 0, g: 0, b: 0, a: 0.74509805}
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: 10907, guid: 0000000000000000f000000000000000, type: 0}
m_Type: 1
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!225 &4783908613280260710
CanvasGroup:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 154459105952450598}
m_Enabled: 1
m_Alpha: 1
m_Interactable: 1
m_BlocksRaycasts: 1
m_IgnoreParentGroups: 0
--- !u!114 &6733810038267402749
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 154459105952450598}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5a1ec5e0ab0df4a418f3c30f67d8a1f9, type: 3}
m_Name:
m_EditorClassIdentifier:
panelRectTransform: {fileID: 3321716118030398287}
scrollItemPrefab: {fileID: 7605012172595602668, guid: 7f59e317567e58f48bffc6339a8718de, type: 3}
content: {fileID: 8786040817862643070}
--- !u!1 &1197605782786364269
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 5027046689765894947}
- component: {fileID: 1347723697007709839}
- component: {fileID: 1107002753484909144}
- component: {fileID: 3225345236067575878}
m_Layer: 5
m_Name: Close Button
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &5027046689765894947
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1197605782786364269}
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: 3321716118030398287}
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: 750}
m_SizeDelta: {x: 80, y: 80}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &1347723697007709839
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1197605782786364269}
m_CullTransparentMesh: 1
--- !u!114 &1107002753484909144
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1197605782786364269}
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: 18ae4aac85f6d444b9ffb2ac80f27c45, 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 &3225345236067575878
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1197605782786364269}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Navigation:
m_Mode: 3
m_WrapAround: 0
m_SelectOnUp: {fileID: 0}
m_SelectOnDown: {fileID: 0}
m_SelectOnLeft: {fileID: 0}
m_SelectOnRight: {fileID: 0}
m_Transition: 1
m_Colors:
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
m_ColorMultiplier: 1
m_FadeDuration: 0.1
m_SpriteState:
m_HighlightedSprite: {fileID: 0}
m_PressedSprite: {fileID: 0}
m_SelectedSprite: {fileID: 0}
m_DisabledSprite: {fileID: 0}
m_AnimationTriggers:
m_NormalTrigger: Normal
m_HighlightedTrigger: Highlighted
m_PressedTrigger: Pressed
m_SelectedTrigger: Selected
m_DisabledTrigger: Disabled
m_Interactable: 1
m_TargetGraphic: {fileID: 1107002753484909144}
m_OnClick:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 6733810038267402749}
m_TargetAssemblyTypeName: ScrollPanelController, Assembly-CSharp
m_MethodName: OnClickCloseButton
m_Mode: 1
m_Arguments:
m_ObjectArgument: {fileID: 0}
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
m_IntArgument: 0
m_FloatArgument: 0
m_StringArgument:
m_BoolArgument: 0
m_CallState: 2
--- !u!1 &2986058160753478837
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 724496705792770137}
- component: {fileID: 8648493451823812328}
- component: {fileID: 7189945641146564510}
- component: {fileID: 2471937349441775619}
m_Layer: 5
m_Name: Viewport
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &724496705792770137
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2986058160753478837}
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: 1763863758802443458}
m_Father: {fileID: 6370669554926347559}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0.04}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: -17, y: 0}
m_Pivot: {x: 0, y: 1}
--- !u!222 &8648493451823812328
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2986058160753478837}
m_CullTransparentMesh: 1
--- !u!114 &7189945641146564510
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2986058160753478837}
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: 10917, guid: 0000000000000000f000000000000000, type: 0}
m_Type: 1
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 &2471937349441775619
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2986058160753478837}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3}
m_Name:
m_EditorClassIdentifier:
m_ShowMaskGraphic: 0
--- !u!1 &4040312265684137449
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6370669554926347559}
- component: {fileID: 5974489158367526773}
- component: {fileID: 3405436097124740100}
- component: {fileID: 8479784636977362532}
m_Layer: 5
m_Name: Scroll View
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &6370669554926347559
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4040312265684137449}
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: 724496705792770137}
m_Father: {fileID: 3321716118030398287}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 0.95600003}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &5974489158367526773
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4040312265684137449}
m_CullTransparentMesh: 1
--- !u!114 &3405436097124740100
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4040312265684137449}
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: 0}
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: 10907, guid: 0000000000000000f000000000000000, type: 0}
m_Type: 1
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 &8479784636977362532
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4040312265684137449}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1aa08ab6e0800fa44ae55d278d1423e3, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Content: {fileID: 1763863758802443458}
m_Horizontal: 0
m_Vertical: 1
m_MovementType: 1
m_Elasticity: 0.1
m_Inertia: 1
m_DecelerationRate: 0.135
m_ScrollSensitivity: 1
m_Viewport: {fileID: 724496705792770137}
m_HorizontalScrollbar: {fileID: 0}
m_VerticalScrollbar: {fileID: 0}
m_HorizontalScrollbarVisibility: 2
m_VerticalScrollbarVisibility: 2
m_HorizontalScrollbarSpacing: -3
m_VerticalScrollbarSpacing: -3
m_OnValueChanged:
m_PersistentCalls:
m_Calls: []
--- !u!1 &4310918944742421438
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 3321716118030398287}
- component: {fileID: 7726104613918846892}
- component: {fileID: 6090514240258565626}
m_Layer: 5
m_Name: Panel
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &3321716118030398287
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4310918944742421438}
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: 5027046689765894947}
- {fileID: 6370669554926347559}
m_Father: {fileID: 4212628350298048884}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 800, y: 1500}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &7726104613918846892
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4310918944742421438}
m_CullTransparentMesh: 1
--- !u!114 &6090514240258565626
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4310918944742421438}
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: e89f6694961e7574b98368e14934a8b1, type: 3}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &8786040817862643070
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1763863758802443458}
- component: {fileID: 5526954107552917871}
- component: {fileID: 189414464021159721}
m_Layer: 5
m_Name: Content
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1763863758802443458
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8786040817862643070}
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: 724496705792770137}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0}
m_AnchorMax: {x: 0.5, y: 1}
m_AnchoredPosition: {x: -392, y: -2.9951172}
m_SizeDelta: {x: 784, y: 0}
m_Pivot: {x: 0, y: 1}
--- !u!114 &5526954107552917871
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8786040817862643070}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Padding:
m_Left: 0
m_Right: 0
m_Top: 0
m_Bottom: 0
m_ChildAlignment: 4
m_Spacing: 30
m_ChildForceExpandWidth: 1
m_ChildForceExpandHeight: 1
m_ChildControlWidth: 0
m_ChildControlHeight: 0
m_ChildScaleWidth: 0
m_ChildScaleHeight: 0
m_ReverseArrangement: 0
--- !u!114 &189414464021159721
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8786040817862643070}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3}
m_Name:
m_EditorClassIdentifier:
m_HorizontalFit: 0
m_VerticalFit: 2

View File

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

View File

@ -315,7 +315,7 @@ MonoBehaviour:
m_OnClick:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 0}
- m_Target: {fileID: 1799015040358630920}
m_TargetAssemblyTypeName: ScrollPanelController, Assembly-CSharp
m_MethodName: OnClickCloseButton
m_Mode: 1

View File

@ -350,7 +350,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0}
m_AnchorMax: {x: 0.5, y: 1}
m_AnchoredPosition: {x: -392, y: -56.72364}
m_AnchoredPosition: {x: -392, y: -5.683716}
m_SizeDelta: {x: 784, y: 0}
m_Pivot: {x: 0, y: 1}
--- !u!114 &2389273839879143832

View File

@ -1166,7 +1166,7 @@ RectTransform:
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 1060, y: 170}
m_SizeDelta: {x: 900, y: 170}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &8547166501184847619
MonoBehaviour:
@ -1185,7 +1185,7 @@ MonoBehaviour:
m_Right: 0
m_Top: 30
m_Bottom: 35
m_ChildAlignment: 0
m_ChildAlignment: 3
m_Spacing: 0
m_ChildForceExpandWidth: 1
m_ChildForceExpandHeight: 1

View File

@ -392,7 +392,6 @@ GameObject:
serializedVersion: 6
m_Component:
- component: {fileID: 1017982171}
- component: {fileID: 1017982173}
- component: {fileID: 1017982172}
m_Layer: 0
m_Name: GameManager
@ -430,102 +429,6 @@ MonoBehaviour:
m_EditorClassIdentifier:
panelManagerPrefab: {fileID: 3475740041361426276, guid: 085ca07ca90c92545b2594bd13412701, type: 3}
audioManagerPrefab: {fileID: 2946408323859178723, guid: e829818dce39a5d4383e061111bed871, type: 3}
--- !u!82 &1017982173
AudioSource:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1017982169}
m_Enabled: 1
serializedVersion: 4
OutputAudioMixerGroup: {fileID: 0}
m_audioClip: {fileID: 0}
m_PlayOnAwake: 1
m_Volume: 1
m_Pitch: 1
Loop: 0
Mute: 0
Spatialize: 0
SpatializePostEffects: 0
Priority: 128
DopplerLevel: 1
MinDistance: 1
MaxDistance: 500
Pan2D: 0
rolloffMode: 0
BypassEffects: 0
BypassListenerEffects: 0
BypassReverbZones: 0
rolloffCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
panLevelCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
spreadCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
reverbZoneMixCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
--- !u!1 &1472647225
GameObject:
m_ObjectHideFlags: 0

View File

@ -5198,7 +5198,6 @@ GameObject:
m_Component:
- component: {fileID: 679766978}
- component: {fileID: 679766977}
- component: {fileID: 679766979}
m_Layer: 0
m_Name: GameManager
m_TagString: Untagged
@ -5235,102 +5234,6 @@ Transform:
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!82 &679766979
AudioSource:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 679766976}
m_Enabled: 1
serializedVersion: 4
OutputAudioMixerGroup: {fileID: 0}
m_audioClip: {fileID: 0}
m_PlayOnAwake: 1
m_Volume: 1
m_Pitch: 1
Loop: 0
Mute: 0
Spatialize: 0
SpatializePostEffects: 0
Priority: 128
DopplerLevel: 1
MinDistance: 1
MaxDistance: 500
Pan2D: 0
rolloffMode: 0
BypassEffects: 0
BypassListenerEffects: 0
BypassReverbZones: 0
rolloffCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
panLevelCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
spreadCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
reverbZoneMixCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
--- !u!1001 &681290285
PrefabInstance:
m_ObjectHideFlags: 0

View File

@ -0,0 +1,12 @@
// AI에서만 사용하는 상수 모음
public class AIConstants
{
// 방향 상수
public static readonly int[][] Directions = new int[][]
{
new int[] {1, 0}, // 수직
new int[] {0, 1}, // 수평
new int[] {1, 1}, // 대각선 ↘ ↖
new int[] {1, -1} // 대각선 ↙ ↗
};
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: de0993e48b9548668a73768a38c11b6d
timeCreated: 1742362879

View File

@ -0,0 +1,580 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public static class AIEvaluator
{
// 패턴 가중치 상수
public struct PatternScore
{
// AI 패턴 점수
public const float FIVE_IN_A_ROW = 100000f;
public const float OPEN_FOUR = 15000f;
public const float HALF_OPEN_FOUR = 5000f;
public const float CLOSED_FOUR = 500f;
public const float OPEN_THREE = 3000f;
public const float HALF_OPEN_THREE = 500f;
public const float CLOSED_THREE = 50f;
public const float OPEN_TWO = 100f;
public const float HALF_OPEN_TWO = 30f;
public const float CLOSED_TWO = 10f;
public const float OPEN_ONE = 10f;
public const float CLOSED_ONE = 1f;
// 복합 패턴 점수
public const float DOUBLE_THREE = 8000f;
public const float DOUBLE_FOUR = 12000f;
public const float FOUR_THREE = 10000f;
// 위치 가중치 기본값
public const float CENTER_WEIGHT = 1.2f;
public const float EDGE_WEIGHT = 0.8f;
}
private static readonly int[][] Directions = AIConstants.Directions;
// 보드 전체 상태 평가
public static float EvaluateBoard(Enums.PlayerType[,] board, Enums.PlayerType aiPlayer)
{
float score = 0;
int size = board.GetLength(0);
// 복합 패턴 감지를 위한 위치 저장 리스트
List<(int row, int col, int[] dir)> aiOpen3Positions = new List<(int, int, int[])>();
List<(int row, int col, int[] dir)> playerOpen3Positions = new List<(int, int, int[])>();
List<(int row, int col, int[] dir)> ai4Positions = new List<(int, int, int[])>();
List<(int row, int col, int[] dir)> player4Positions = new List<(int, int, int[])>();
// 1. 기본 패턴 평가
score += EvaluateBoardPatterns(board, aiPlayer, size, aiOpen3Positions, playerOpen3Positions,
ai4Positions, player4Positions);
// 2. 복합 패턴 평가
score += EvaluateComplexPatterns(aiOpen3Positions, playerOpen3Positions, ai4Positions, player4Positions, aiPlayer);
return score;
}
// 기본 패턴 (돌의 연속, 열린 끝 등) 평가
private static float EvaluateBoardPatterns(
Enums.PlayerType[,] board,
Enums.PlayerType aiPlayer,
int size,
List<(int row, int col, int[] dir)> aiOpen3Positions,
List<(int row, int col, int[] dir)> playerOpen3Positions,
List<(int row, int col, int[] dir)> ai4Positions,
List<(int row, int col, int[] dir)> player4Positions)
{
float score = 0;
Enums.PlayerType opponentPlayer = (aiPlayer == Enums.PlayerType.PlayerA) ?
Enums.PlayerType.PlayerB : Enums.PlayerType.PlayerA;
for (int row = 0; row < size; row++)
{
for (int col = 0; col < size; col++)
{
if (board[row, col] == Enums.PlayerType.None) continue;
Enums.PlayerType currentPlayer = board[row, col];
int playerScore = (currentPlayer == aiPlayer) ? 1 : -1; // AI는 양수, 플레이어는 음수
// 위치 가중치 계산
float positionWeight = CalculatePositionWeight(row, col, size);
foreach (var dir in Directions)
{
var (count, openEnds) = MiniMaxAIController.CountStones(board, row, col, dir, currentPlayer);
// 기본 패턴 평가 및 점수 계산
float patternScore = EvaluatePattern(count, openEnds);
// 깨진 패턴 평가
var (isBroken, brokenCount, brokenOpenEnds) =
DetectBrokenPattern(board, row, col, dir, currentPlayer);
if (isBroken) // 깨진 패턴이 있을 시 비교 후 더 높은 점수 할당
{
float brokenScore = EvaluateBrokenPattern(brokenCount, brokenOpenEnds);
patternScore = Math.Max(patternScore, brokenScore);
}
// 패턴 수집 (복합 패턴 감지용)
CollectPatterns(row, col, dir, count, openEnds, currentPlayer, aiPlayer,
aiOpen3Positions, playerOpen3Positions, ai4Positions, player4Positions);
// 위치 가중치 적용
patternScore *= positionWeight;
// 최종 점수 (플레이어는 음수)
score += playerScore * patternScore;
}
}
}
return score;
}
// 개별 패턴 평가 (돌 개수와 열린 끝 기준)
private static float EvaluatePattern(int count, int openEnds)
{
if (count >= 5)
{
return PatternScore.FIVE_IN_A_ROW;
}
else if (count == 4)
{
return (openEnds == 2) ? PatternScore.OPEN_FOUR :
(openEnds == 1) ? PatternScore.HALF_OPEN_FOUR :
PatternScore.CLOSED_FOUR;
}
else if (count == 3)
{
return (openEnds == 2) ? PatternScore.OPEN_THREE :
(openEnds == 1) ? PatternScore.HALF_OPEN_THREE :
PatternScore.CLOSED_THREE;
}
else if (count == 2)
{
return (openEnds == 2) ? PatternScore.OPEN_TWO :
(openEnds == 1) ? PatternScore.HALF_OPEN_TWO :
PatternScore.CLOSED_TWO;
}
else if (count == 1)
{
return (openEnds == 2) ? PatternScore.OPEN_ONE : PatternScore.CLOSED_ONE;
}
return 0;
}
// 복합 패턴 평가 (3-3, 4-4, 4-3 등)
private static float EvaluateComplexPatterns(
List<(int row, int col, int[] dir)> aiOpen3Positions,
List<(int row, int col, int[] dir)> playerOpen3Positions,
List<(int row, int col, int[] dir)> ai4Positions,
List<(int row, int col, int[] dir)> player4Positions,
Enums.PlayerType aiPlayer)
{
float score = 0;
// 삼삼(3-3) 감지
int aiThreeThree = DetectDoubleThree(aiOpen3Positions);
int playerThreeThree = DetectDoubleThree(playerOpen3Positions);
// 사사(4-4) 감지
int aiFourFour = DetectDoubleFour(ai4Positions);
int playerFourFour = DetectDoubleFour(player4Positions);
// 사삼(4-3) 감지
int aiFourThree = DetectFourThree(ai4Positions, aiOpen3Positions);
int playerFourThree = DetectFourThree(player4Positions, playerOpen3Positions);
// 복합 패턴 점수 합산
score += aiThreeThree * PatternScore.DOUBLE_THREE;
score -= playerThreeThree * PatternScore.DOUBLE_THREE;
score += aiFourFour * PatternScore.DOUBLE_FOUR;
score -= playerFourFour * PatternScore.DOUBLE_FOUR;
score += aiFourThree * PatternScore.FOUR_THREE;
score -= playerFourThree * PatternScore.FOUR_THREE;
return score;
}
// 위치 가중치 계산 함수
private static float CalculatePositionWeight(int row, int col, int size)
{
float boardCenterPos = (size - 1) / 2.0f;
// 현재 위치와 중앙과의 거리 계산 (0~1 사이 값)
float distance = Math.Max(Math.Abs(row - boardCenterPos), Math.Abs(col - boardCenterPos)) / boardCenterPos;
// 중앙(거리 0)은 1.2배, 가장자리(거리 1)는 0.8배
return PatternScore.CENTER_WEIGHT - ((PatternScore.CENTER_WEIGHT - PatternScore.EDGE_WEIGHT) * distance);
}
// 방향이 평행한지 확인하는 함수
private static bool AreParallelDirections(int[] dir1, int[] dir2) // Vector로 변경
{
return (dir1[0] == dir2[0] && dir1[1] == dir2[1]) ||
(dir1[0] == -dir2[0] && dir1[1] == -dir2[1]);
}
// 패턴 수집 함수 (복합 패턴 감지용)
private static void CollectPatterns(
int row, int col, int[] dir, int count, int openEnds,
Enums.PlayerType currentPlayer, Enums.PlayerType aiPlayer,
List<(int row, int col, int[] dir)> aiOpen3Positions,
List<(int row, int col, int[] dir)> playerOpen3Positions,
List<(int row, int col, int[] dir)> ai4Positions,
List<(int row, int col, int[] dir)> player4Positions)
{
// 열린 3 패턴 수집
if (count == 3 && openEnds == 2)
{
if (currentPlayer == aiPlayer)
aiOpen3Positions.Add((row, col, dir));
else
playerOpen3Positions.Add((row, col, dir));
}
// 4 패턴 수집
if (count == 4 && openEnds >= 1)
{
if (currentPlayer == aiPlayer)
ai4Positions.Add((row, col, dir));
else
player4Positions.Add((row, col, dir));
}
}
#region Complex Pattern (3-3, 4-4, 4-3)
// 삼삼(3-3) 감지 함수
private static int DetectDoubleThree(List<(int row, int col, int[] dir)> openThreePositions)
{
int doubleThreeCount = 0;
var checkedPairs = new HashSet<(int, int)>();
for (int i = 0; i < openThreePositions.Count; i++)
{
var (row1, col1, dir1) = openThreePositions[i];
for (int j = i + 1; j < openThreePositions.Count; j++)
{
var (row2, col2, dir2) = openThreePositions[j];
// 같은 돌에서 다른 방향으로 두 개의 열린 3이 형성된 경우
if (row1 == row2 && col1 == col2 && !AreParallelDirections(dir1, dir2))
{
if (!checkedPairs.Contains((row1, col1)))
{
doubleThreeCount++;
checkedPairs.Add((row1, col1));
}
}
}
}
return doubleThreeCount;
}
// 사사(4-4) 감지 함수
private static int DetectDoubleFour(List<(int row, int col, int[] dir)> fourPositions)
{
int doubleFourCount = 0;
var checkedPairs = new HashSet<(int, int)>();
for (int i = 0; i < fourPositions.Count; i++)
{
var (row1, col1, dir1) = fourPositions[i];
for (int j = i + 1; j < fourPositions.Count; j++)
{
var (row2, col2, dir2) = fourPositions[j];
if (row1 == row2 && col1 == col2 && !AreParallelDirections(dir1, dir2))
{
if (!checkedPairs.Contains((row1, col1)))
{
doubleFourCount++;
checkedPairs.Add((row1, col1));
}
}
}
}
return doubleFourCount;
}
// 사삼(4-3) 감지 함수
private static int DetectFourThree(
List<(int row, int col, int[] dir)> fourPositions,
List<(int row, int col, int[] dir)> openThreePositions)
{
int fourThreeCount = 0;
var checkedPairs = new HashSet<(int, int)>();
foreach (var (row1, col1, _) in fourPositions)
{
foreach (var (row2, col2, _) in openThreePositions)
{
// 같은 돌에서 4와 열린 3이 동시에 형성된 경우
if (row1 == row2 && col1 == col2)
{
if (!checkedPairs.Contains((row1, col1)))
{
fourThreeCount++;
checkedPairs.Add((row1, col1));
}
}
}
}
return fourThreeCount;
}
#endregion
#region Evaluate Move Position
// 이동 평가 함수
public static float EvaluateMove(Enums.PlayerType[,] board, int row, int col, Enums.PlayerType AIPlayer)
{
float score = 0;
Enums.PlayerType opponentPlayer = (AIPlayer == Enums.PlayerType.PlayerA) ?
Enums.PlayerType.PlayerB : Enums.PlayerType.PlayerA;
// 복합 패턴 감지를 위한 위치 저장 리스트
List<(int[] dir, int count, int openEnds)> aiPatterns = new List<(int[], int, int)>();
List<(int[] dir, int count, int openEnds)> opponentPatterns = new List<(int[], int, int)>();
// AI 관점에서 평가
board[row, col] = AIPlayer;
foreach (var dir in Directions)
{
// 평가를 위한 가상 보드이기에 캐시 데이터에 저장X
var (count, openEnds) = MiniMaxAIController.CountStones(board, row, col, dir, AIPlayer, false);
aiPatterns.Add((dir, count, openEnds));
if (count >= 4)
{
score += PatternScore.FIVE_IN_A_ROW / 10;
}
else if (count == 3)
{
score += (openEnds == 2) ? PatternScore.OPEN_THREE / 3 :
(openEnds == 1) ? PatternScore.HALF_OPEN_THREE / 5 :
PatternScore.CLOSED_THREE / 5;
}
else if (count == 2)
{
score += (openEnds == 2) ? PatternScore.OPEN_TWO / 2 :
(openEnds == 1) ? PatternScore.HALF_OPEN_TWO / 3 :
PatternScore.CLOSED_TWO / 5;
}
else if (count == 1)
{
score += (openEnds == 2) ? PatternScore.OPEN_ONE :
PatternScore.CLOSED_ONE;
}
// 깨진 패턴 평가
var (isBroken, brokenCount, brokenOpenEnds) = DetectBrokenPattern(board, row, col, dir, AIPlayer);
if (isBroken)
{
float brokenScore = EvaluateBrokenPattern(brokenCount, brokenOpenEnds);
score = Math.Max(score, brokenScore);
}
}
// AI 복합 패턴 점수 계산 (새로 추가)
score += EvaluateComplexMovePatterns(aiPatterns, true);
// 상대 관점에서 평가 (방어 가치)
board[row, col] = opponentPlayer;
foreach (var dir in Directions)
{
var (count, openEnds) = MiniMaxAIController.CountStones(board, row, col, dir, opponentPlayer, false);
opponentPatterns.Add((dir, count, openEnds));
// 상대 패턴 차단에 대한 가치 (약간 낮은 가중치)
if (count >= 4)
{
score += PatternScore.FIVE_IN_A_ROW / 12.5f;
}
else if (count == 3)
{
score += (openEnds == 2) ? PatternScore.OPEN_THREE / 3.75f :
(openEnds == 1) ? PatternScore.HALF_OPEN_THREE / 6.25f :
PatternScore.CLOSED_THREE / 6.25f;
}
else if (count == 2)
{
score += (openEnds == 2) ? PatternScore.OPEN_TWO / 2.5f :
(openEnds == 1) ? PatternScore.HALF_OPEN_TWO / 3.75f :
PatternScore.CLOSED_TWO / 5f;
}
else if (count == 1)
{
score += (openEnds == 2) ? PatternScore.OPEN_ONE / 1.25f :
PatternScore.CLOSED_ONE;
}
}
score += EvaluateComplexMovePatterns(opponentPatterns, false);
// 원래 상태로 복원
board[row, col] = Enums.PlayerType.None;
// 중앙에 가까울수록 추가 점수
int size = board.GetLength(0);
float centerDistance = Math.Max(
Math.Abs(row - (size - 1) / 2.0f),
Math.Abs(col - (size - 1) / 2.0f)
);
float centerBonus = 1.0f - (centerDistance / ((size - 1) / 2.0f)) * 0.3f; // 30% 가중치
return score * centerBonus;
}
// 복합 패턴 평가를 위한 새로운 함수
private static float EvaluateComplexMovePatterns(List<(int[] dir, int count, int openEnds)> patterns, bool isAI)
{
float score = 0;
// 열린 3 패턴 및 4 패턴 찾기
var openThrees = patterns.Where(p => p.count == 3 && p.openEnds == 2).ToList();
var fours = patterns.Where(p => p.count == 4 && p.openEnds >= 1).ToList();
// 3-3 패턴 감지
if (openThrees.Count >= 2)
{
for (int i = 0; i < openThrees.Count; i++)
{
for (int j = i + 1; j < openThrees.Count; j++)
{
if (!AreParallelDirections(openThrees[i].dir, openThrees[j].dir))
{
float threeThreeScore = PatternScore.DOUBLE_THREE / 4; // 복합 패턴 가중치
score += isAI ? threeThreeScore : threeThreeScore;
break;
}
}
}
}
// 4-4 패턴 감지
if (fours.Count >= 2)
{
for (int i = 0; i < fours.Count; i++)
{
for (int j = i + 1; j < fours.Count; j++)
{
if (!AreParallelDirections(fours[i].dir, fours[j].dir))
{
float fourFourScore = PatternScore.DOUBLE_FOUR / 4;
score += isAI ? fourFourScore : fourFourScore;
break;
}
}
}
}
// 4-3 패턴 감지
if (fours.Count > 0 && openThrees.Count > 0)
{
float fourThreeScore = PatternScore.FOUR_THREE / 4;
score += isAI ? fourThreeScore : fourThreeScore;
}
return score;
}
// 깨진 패턴 (3-빈칸-1) 감지
private static (bool isDetected, int count, int openEnds) DetectBrokenPattern(
Enums.PlayerType[,] board, int row, int col, int[] dir, Enums.PlayerType player)
{
int size = board.GetLength(0);
int totalStones = 1; // 현재 위치의 돌 포함
int gapCount = 0; // 빈칸 개수
int openEnds = 0; // 열린 끝 개수
// 정방향 탐색
int r = row, c = col;
for (int i = 1; i <= 5; i++)
{
r += dir[0];
c += dir[1];
if (r < 0 || r >= size || c < 0 || c >= size)
break;
if (board[r, c] == player)
{
totalStones++;
}
else if (board[r, c] == Enums.PlayerType.None)
{
if (gapCount < 1) // 최대 1개 빈칸만 허용
{
gapCount++;
}
else
{
openEnds++;
break;
}
}
else // 상대방 돌
{
break;
}
}
// 역방향 탐색
r = row; c = col;
for (int i = 1; i <= 5; i++)
{
r -= dir[0];
c -= dir[1];
if (r < 0 || r >= size || c < 0 || c >= size)
break;
if (board[r, c] == player)
{
totalStones++;
}
else if (board[r, c] == Enums.PlayerType.None)
{
if (gapCount < 1)
{
gapCount++;
}
else
{
openEnds++;
break;
}
}
else
{
break;
}
}
// 깨진 패턴 감지: 총 돌 개수 ≥ 4 그리고 빈칸이 1개
bool isDetected = (totalStones >= 4 && gapCount == 1);
return (isDetected, totalStones, openEnds);
}
// 깨진 패턴 점수 계산 함수
private static float EvaluateBrokenPattern(int count, int openEnds)
{
if (count >= 5) // 5개 이상 돌이 있으면 승리
{
return PatternScore.FIVE_IN_A_ROW;
}
else if (count == 4) // 깨진 4
{
return (openEnds == 2) ? PatternScore.OPEN_FOUR * 0.8f :
(openEnds == 1) ? PatternScore.HALF_OPEN_FOUR * 0.8f :
PatternScore.CLOSED_FOUR * 0.7f;
}
else if (count == 3) // 깨진 3
{
return (openEnds == 2) ? PatternScore.OPEN_THREE * 0.7f :
(openEnds == 1) ? PatternScore.HALF_OPEN_THREE * 0.7f :
PatternScore.CLOSED_THREE * 0.6f;
}
return 0;
}
#endregion
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 34f6533696ad41518da4bcc203309338
timeCreated: 1742357084

View File

@ -5,34 +5,33 @@ using UnityEngine;
public static class MiniMaxAIController
{
// To-Do List
// 탐색 시간 개선: 캐싱(_stoneInfoCache), 좋은 수부터 탐색(Move Ordering)
// AI 난이도 개선
private const int SEARCH_DEPTH = 3; // 탐색 깊이 제한 (3 = 빠른 응답, 4 = 좀 더 강한 AI 그러나 느린)
private const int WIN_COUNT = 5;
private static int[][] _directions = new int[][]
{
new int[] {1, 0}, // 수직
new int[] {0, 1}, // 수평
new int[] {1, 1}, // 대각선 ↘ ↖
new int[] {1, -1} // 대각선 ↙ ↗
};
private static int[][] _directions = AIConstants.Directions;
private static int _playerLevel = 1; // 급수 설정
private static float _mistakeMove;
private static Enums.PlayerType _AIPlayerType = Enums.PlayerType.PlayerB;
// 중복 계산을 방지하기 위한 캐싱 데이터. 위치(row, col) 와 방향(dirX, dirY) 중복 계산 방지
private static Dictionary<(int, int, int, int), (int count, int openEnds)> _stoneInfoCache
= new Dictionary<(int, int, int, int), (int count, int openEnds)>();
private static System.Random _random = new System.Random(); // 랜덤 실수용 Random 함수
// 중복 계산을 방지하기 위한 캐싱 데이터. 위치 기반 (그리드 기반 해시맵)
private static Dictionary<(int row, int col), Dictionary<(int dirX, int dirY), (int count, int openEnds)>>
_spatialStoneCache = new Dictionary<(int row, int col), Dictionary<(int dirX, int dirY), (int count, int openEnds)>>();
// AI Player Type 변경 (AI가 선수로 둘 수 있을지도 모르니..)
public static void SetAIPlayerType(Enums.PlayerType AIPlayerType)
{
_AIPlayerType = AIPlayerType;
}
// 급수 설정 -> 실수 넣을 때 계산
public static void SetLevel(int level)
{
_playerLevel = level;
// 레벨에 따른 실수율? 설정
_mistakeMove = GetMistakeProbability(_playerLevel);
}
@ -71,7 +70,7 @@ public static class MiniMaxAIController
foreach (var (row, col, _) in validMoves)
{
board[row, col] = _AIPlayerType;
float score = DoMinimax(board, SEARCH_DEPTH, false, -1000, 1000, row, col);
float score = DoMinimax(board, SEARCH_DEPTH, false, -1000000, 1000000, row, col);
board[row, col] = Enums.PlayerType.None;
if (score > bestScore)
@ -88,7 +87,7 @@ public static class MiniMaxAIController
}
// 랜덤 실수
if (secondBestMove != null && UnityEngine.Random.value < _mistakeMove) // UnityEngine.Random.value == 0~1 사이 반환
if (secondBestMove != null && _random.NextDouble() < _mistakeMove)
{
Debug.Log("AI Mistake");
return secondBestMove;
@ -100,9 +99,9 @@ public static class MiniMaxAIController
private static float DoMinimax(Enums.PlayerType[,] board, int depth, bool isMaximizing, float alpha, float beta,
int recentRow, int recentCol)
{
if (CheckGameWin(Enums.PlayerType.PlayerA, board, recentRow, recentCol)) return -100 + depth;
if (CheckGameWin(Enums.PlayerType.PlayerB, board, recentRow, recentCol)) return 100 - depth;
if (depth == 0) return EvaluateBoard(board);
if (CheckGameWin(Enums.PlayerType.PlayerA, board, recentRow, recentCol, true)) return -100 + depth;
if (CheckGameWin(Enums.PlayerType.PlayerB, board, recentRow, recentCol, true)) return 100 - depth;
if (depth == 0) return AIEvaluator.EvaluateBoard(board, _AIPlayerType);
float bestScore = isMaximizing ? float.MinValue : float.MaxValue;
List<(int row, int col, float score)> validMoves = GetValidMoves(board); // 현재 놓을 수 있는 자리 리스트
@ -111,13 +110,11 @@ public static class MiniMaxAIController
{
board[row, col] = isMaximizing ? Enums.PlayerType.PlayerB : Enums.PlayerType.PlayerA;
ClearCachePartial(row, col); // 부분 초기화
// ClearCache();
float minimaxScore = DoMinimax(board, depth - 1, !isMaximizing, alpha, beta, row, col);
board[row, col] = Enums.PlayerType.None;
ClearCachePartial(row, col);
// ClearCache();
if (isMaximizing)
{
@ -147,14 +144,18 @@ public static class MiniMaxAIController
{
if (board[row, col] == Enums.PlayerType.None && HasNearbyStones(board, row, col))
{
float score = EvaluateBoard(board);
// 보드 전체가 아닌 해당 돌에 대해서만 Score 계산
float score = AIEvaluator.EvaluateMove(board, row, col, _AIPlayerType);
validMoves.Add((row, col, score));
}
}
}
// score가 높은 순으로 정렬 -> 더 좋은 수 먼저 계산하도록 함
validMoves.Sort((a, b) => b.Item3.CompareTo(a.Item3));
return validMoves;
// 시간 단축을 위해 상위 10-15개만 고려. 일단 15개
return validMoves.Take(15).ToList();
}
private static bool HasNearbyStones(Enums.PlayerType[,] board, int row, int col, int distance = 3)
@ -176,14 +177,16 @@ public static class MiniMaxAIController
}
// 특정 방향으로 같은 돌 개수와 열린 끝 개수를 계산하는 함수
private static (int count, int openEnds) CountStones(
Enums.PlayerType[,] board, int row, int col, int[] direction, Enums.PlayerType player)
public static (int count, int openEnds) CountStones(
Enums.PlayerType[,] board, int row, int col, int[] direction, Enums.PlayerType player, bool isSaveInCache = true)
{
int dirX = direction[0], dirY = direction[1];
var key = (row, col, dirX, dirY);
var posKey = (row, col);
var dirKey = (dirX, dirY);
// 캐시에 존재하면 바로 반환 (탐색 시간 감소)
if (_stoneInfoCache.TryGetValue(key, out var cachedResult))
if (_spatialStoneCache.TryGetValue(posKey, out var dirCache) &&
dirCache.TryGetValue(dirKey, out var cachedResult))
{
return cachedResult;
}
@ -222,53 +225,57 @@ public static class MiniMaxAIController
}
var resultValue = (count, openEnds);
_stoneInfoCache[key] = resultValue; // 결과 저장
if(isSaveInCache) // 결과 저장
{
if (!_spatialStoneCache.TryGetValue(posKey, out dirCache))
{
dirCache = new Dictionary<(int, int), (int, int)>();
_spatialStoneCache[posKey] = dirCache;
}
dirCache[dirKey] = (count, openEnds);
}
return resultValue;
}
#region Cache Clear
// 캐시 초기화, 새로운 돌이 놓일 시 실행
private static void ClearCache()
{
_stoneInfoCache.Clear();
_spatialStoneCache.Clear();
}
// 캐시 부분 초기화 (현재 변경된 위치 N에서 반경 5칸만 초기화)
private static void ClearCachePartial(int centerRow, int centerCol, int radius = 5)
// 캐시 부분 초기화 (현재 변경된 위치 N에서 반경 4칸만 초기화)
private static void ClearCachePartial(int centerRow, int centerCol, int radius = 4)
{
// 캐시가 비어있으면 아무 작업도 하지 않음
if (_stoneInfoCache.Count == 0) return;
if (_spatialStoneCache.Count == 0) return;
// 제거할 키 목록
List<(int, int, int, int)> keysToRemove = new List<(int, int, int, int)>();
// 모든 캐시 항목을 검사
foreach (var key in _stoneInfoCache.Keys)
for (int r = centerRow - radius; r <= centerRow + radius; r++)
{
var (row, col, _, _) = key;
// 거리 계산
int distance = Math.Max(Math.Abs(row - centerRow), Math.Abs(col - centerCol));
// 지정된 반경 내에 있는 캐시 항목을 삭제 목록에 추가
if (distance <= radius)
for (int c = centerCol - radius; c <= centerCol + radius; c++)
{
keysToRemove.Add(key);
// 반경 내 위치 확인
if (Math.Max(Math.Abs(r - centerRow), Math.Abs(c - centerCol)) <= radius)
{
// 해당 위치의 캐시 항목 제거
_spatialStoneCache.Remove((r, c));
}
}
}
}
// 반경 내의 키 제거
foreach (var key in keysToRemove)
{
_stoneInfoCache.Remove(key);
}
}
#endregion
// 최근에 둔 돌 위치 기반으로 게임 승리를 판별하는 함수
public static bool CheckGameWin(Enums.PlayerType player, Enums.PlayerType[,] board, int row, int col)
// MinimaxAIController 밖의 cs파일은 호출 시 맨 마지막을 false로 지정해야 합니다.
public static bool CheckGameWin(Enums.PlayerType player, Enums.PlayerType[,] board,
int row, int col, bool isSavedCache)
{
foreach (var dir in _directions)
{
var (count, _) = CountStones(board, row, col, dir, player);
var (count, _) = CountStones(board, row, col, dir, player, isSavedCache);
// 자기 자신 포함하여 5개 이상일 시 true 반환
if (count + 1 >= WIN_COUNT)
@ -305,6 +312,78 @@ public static class MiniMaxAIController
return fiveInARowMoves;
}
/*
#region Evaluate Score
// 특정 위치의 Score를 평가하는 새로운 함수
private static float EvaluateMove(Enums.PlayerType[,] board, int row, int col)
{
float score = 0;
board[row, col] = _AIPlayerType;
foreach (var dir in _directions)
{
// CountStones를 사용하나 캐시에 저장X, 가상 계산이기 때문..
var (count, openEnds) = CountStones(board, row, col, dir, _AIPlayerType, false);
if (count >= 4)
{
score += 10000;
}
else if (count == 3)
{
score += (openEnds == 2) ? 1000 : (openEnds == 1) ? 100 : 10;
}
else if (count == 2)
{
score += (openEnds == 2) ? 50 : (openEnds == 1) ? 10 : 5;
}
else if (count == 1)
{
score += (openEnds == 2) ? 10 : (openEnds == 1) ? 5 : 1;
}
}
// 상대 돌로 바꿔서 평가
board[row, col] = Enums.PlayerType.PlayerB;
foreach (var dir in _directions)
{
// 캐시 저장X
var (count, openEnds) = CountStones(board, row, col, dir, Enums.PlayerType.PlayerB, false);
// 상대 패턴 차단에 대한 가치 (방어 점수)
if (count >= 4)
{
score += 8000;
}
else if (count == 3)
{
score += (openEnds == 2) ? 800 : (openEnds == 1) ? 80 : 8;
}
else if (count == 2)
{
score += (openEnds == 2) ? 40 : (openEnds == 1) ? 8 : 4;
}
else if (count == 1)
{
score += (openEnds == 2) ? 8 : (openEnds == 1) ? 4 : 1;
}
}
// 원래 상태로 복원
board[row, col] = Enums.PlayerType.None;
// 중앙에 가까울수록 추가 점수
int size = board.GetLength(0);
float centerDistance = Math.Max(
Math.Abs(row - (size - 1) / 2.0f),
Math.Abs(col - (size - 1) / 2.0f)
);
float centerBonus = 1.0f - (centerDistance / ((size - 1) / 2.0f)) * 0.3f; // 30% 가중치
return score * centerBonus;
}
// 현재 보드 평가 함수
private static float EvaluateBoard(Enums.PlayerType[,] board)
@ -312,6 +391,13 @@ public static class MiniMaxAIController
float score = 0;
int size = board.GetLength(0);
// 복합 패턴 감지를 위한 위치 저장 리스트
List<(int row, int col, int[] dir)> aiOpen3Positions = new List<(int, int, int[])>();
List<(int row, int col, int[] dir)> playerOpen3Positions = new List<(int, int, int[])>();
List<(int row, int col, int[] dir)> ai4Positions = new List<(int, int, int[])>();
List<(int row, int col, int[] dir)> player4Positions = new List<(int, int, int[])>();
for (int row = 0; row < size; row++)
{
for (int col = 0; col < size; col++)
@ -321,20 +407,190 @@ public static class MiniMaxAIController
Enums.PlayerType player = board[row, col];
int playerScore = (player == _AIPlayerType) ? 1 : -1; // AI는 양수, 플레이어는 음수
// 위치 가중치 계산. 중앙 중심으로 돌을 두도록 함
float positionWeight = CalculatePositionWeight(row, col, size);
foreach (var dir in _directions)
{
var (count, openEnds) = CountStones(board, row, col, dir, player);
// 점수 계산
if (count == 4)
score += playerScore * (openEnds == 2 ? 10000 : 1000);
float patternScore = 0;
if (count >= 5)
{
Debug.Log("over 5 counts. count amount: " + count);
patternScore = 100000;
}
else if (count == 4)
{
patternScore = (openEnds == 2) ? 15000 : (openEnds == 1) ? 5000 : 500;
// 4 패턴 위치 저장
if (openEnds >= 1)
{
if (player == _AIPlayerType)
ai4Positions.Add((row, col, dir));
else
player4Positions.Add((row, col, dir));
}
}
else if (count == 3)
score += playerScore * (openEnds == 2 ? 1000 : 100);
{
patternScore = (openEnds == 2) ? 3000 : (openEnds == 1) ? 500 : 50;
// 3 패턴 위치 저장
if (openEnds == 2)
{
if (player == _AIPlayerType)
aiOpen3Positions.Add((row, col, dir));
else
playerOpen3Positions.Add((row, col, dir));
}
}
else if (count == 2)
score += playerScore * (openEnds == 2 ? 100 : 10);
{
patternScore = (openEnds == 2) ? 100 : (openEnds == 1) ? 30 : 10;
}
else if (count == 1)
{
patternScore = (openEnds == 2) ? 10 : 1;
}
// 위치 가중치 적용
patternScore *= positionWeight;
// 최종 점수 적용 (플레이어는 음수)
score += playerScore * patternScore;
}
}
}
// 2. 복합 패턴 감지 및 점수 부여 (4,4 / 3,3 / 4,3)
int aiThreeThree = DetectDoubleThree(aiOpen3Positions);
int playerThreeThree = DetectDoubleThree(playerOpen3Positions);
int aiFourFour = DetectDoubleFour(ai4Positions);
int playerFourFour = DetectDoubleFour(player4Positions);
int aiFourThree = DetectFourThree(ai4Positions, aiOpen3Positions);
int playerFourThree = DetectFourThree(player4Positions, playerOpen3Positions);
// 복합 패턴 점수 추가
score += aiThreeThree * 8000;
score -= playerThreeThree * 8000;
score += aiFourFour * 12000;
score -= playerFourFour * 12000;
score += aiFourThree * 10000;
score -= playerFourThree * 10000;
return score;
}
// 위치 가중치 계산 함수
private static float CalculatePositionWeight(int row, int col, int size)
{
float boardCenterPos = (size - 1) / 2.0f;
// 현재 위치와 중앙과의 거리 계산 (0~1 사이 값)
float distance = Math.Max(Math.Abs(row - boardCenterPos), Math.Abs(col - boardCenterPos)) / boardCenterPos;
// 중앙(거리 0)은 1.2배, 가장자리(거리 1)는 0.8배
return 1.2f - (0.4f * distance);
}
// 삼삼(3-3) 감지 함수
private static int DetectDoubleThree(List<(int row, int col, int[] dir)> openThreePositions)
{
int doubleThreeCount = 0;
var checkedPairs = new HashSet<(int, int)>();
for (int i = 0; i < openThreePositions.Count; i++)
{
var (row1, col1, dir1) = openThreePositions[i];
for (int j = i + 1; j < openThreePositions.Count; j++)
{
var (row2, col2, dir2) = openThreePositions[j];
// 같은 돌에서 다른 방향으로 두 개의 열린 3이 형성된 경우
if (row1 == row2 && col1 == col2 && !AreParallelDirections(dir1, dir2))
{
if (!checkedPairs.Contains((row1, col1)))
{
doubleThreeCount++;
checkedPairs.Add((row1, col1));
}
}
}
}
return doubleThreeCount;
}
// 방향이 평행한지 확인하는 함수
private static bool AreParallelDirections(int[] dir1, int[] dir2)
{
return (dir1[0] == dir2[0] && dir1[1] == dir2[1]) ||
(dir1[0] == -dir2[0] && dir1[1] == -dir2[1]);
}
// 사사(4-4) 감지 함수
private static int DetectDoubleFour(List<(int row, int col, int[] dir)> fourPositions)
{
int doubleFourCount = 0;
var checkedPairs = new HashSet<(int, int)>();
for (int i = 0; i < fourPositions.Count; i++)
{
var (row1, col1, dir1) = fourPositions[i];
for (int j = i + 1; j < fourPositions.Count; j++)
{
var (row2, col2, dir2) = fourPositions[j];
if (row1 == row2 && col1 == col2 && !AreParallelDirections(dir1, dir2))
{
if (!checkedPairs.Contains((row1, col1)))
{
doubleFourCount++;
checkedPairs.Add((row1, col1));
}
}
}
}
return doubleFourCount;
}
// 사삼(4-3) 감지 함수
private static int DetectFourThree(List<(int row, int col, int[] dir)> fourPositions,
List<(int row, int col, int[] dir)> openThreePositions)
{
int fourThreeCount = 0;
var checkedPairs = new HashSet<(int, int)>();
foreach (var (row1, col1, _) in fourPositions)
{
foreach (var (row2, col2, _) in openThreePositions)
{
// 같은 돌에서 4와 열린 3이 동시에 형성된 경우
if (row1 == row2 && col1 == col2)
{
if (!checkedPairs.Contains((row1, col1)))
{
fourThreeCount++;
checkedPairs.Add((row1, col1));
}
}
}
}
return fourThreeCount;
}
#endregion
*/
}

View File

@ -0,0 +1,26 @@
using System;
using UnityEngine;
using System.Threading.Tasks;
public class OmokAI : MonoBehaviour
{
public static OmokAI Instance;
private void Awake()
{
Instance = this;
}
public async void StartBestMoveSearch(Enums.PlayerType[,] board, Action<(int, int)?> callback)
{
(int row, int col)? bestMove = await Task.Run(() => MiniMaxAIController.GetBestMove(board));
callback?.Invoke(bestMove);
}
// true: Win, false: Lose
public bool CheckGameWin(Enums.PlayerType player, Enums.PlayerType[,] board, int row, int col)
{
bool isWin = MiniMaxAIController.CheckGameWin(player, board, row, col, false);
return isWin;
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 576baa0fe98d40608bf48109ba5ed788
timeCreated: 1742286909

View File

@ -1,84 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UI;
public class TestGameManager : MonoBehaviour
{
[SerializeField] private TMP_InputField rowText;
[SerializeField] private TMP_InputField colText;
[SerializeField] private TMP_Text boardText;
private Enums.PlayerType[,] _board;
private void Start()
{
_board = new Enums.PlayerType[15, 15];
MiniMaxAIController.SetLevel(1); // 급수 설정 테스트
ResultBoard();
}
public void OnClickGoButton()
{
int row = int.Parse(rowText.text);
int col = int.Parse(colText.text);
if (_board[row, col] != Enums.PlayerType.None)
{
Debug.Log("중복 위치");
return;
}
_board[row, col] = Enums.PlayerType.PlayerA;
Debug.Log($"Player's row: {row} col: {col}");
// var isEnded = MiniMaxAIController.CheckGameWin(Enums.PlayerType.PlayerA, _board, row, col);
// Debug.Log("PlayerA is Win: " + isEnded);
// 인공지능 호출
var result = MiniMaxAIController.GetBestMove(_board);
if (result.HasValue)
{
Debug.Log($"AI's row: {result.Value.row} col: {result.Value.col}");
_board[result.Value.row, result.Value.col] = Enums.PlayerType.PlayerB;
// isEnded = MiniMaxAIController.CheckGameWin(Enums.PlayerType.PlayerB, _board, result.Value.row, result.Value.col);
// Debug.Log("PlayerB is Win: " + isEnded);
}
ResultBoard();
}
private void ResultBoard()
{
boardText.text = "";
// player 타입에 따라 입력받는 건 무조건 A로 해서 A, AI는 B로 나타내고 None은 _
for (int i = 0; i < 15; i++)
{
for (int j = 0; j < 15; j++)
{
if (_board[i, j] == Enums.PlayerType.PlayerA)
{
boardText.text += 'A';
}
else if (_board[i, j] == Enums.PlayerType.PlayerB)
{
boardText.text += 'B';
}
else if (_board[i, j] == Enums.PlayerType.None)
{
boardText.text += '_';
}
boardText.text += ' ';
}
boardText.text += '\n';
}
}
}

View File

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

View File

@ -1,5 +1,6 @@
using UnityEngine;
[RequireComponent(typeof(AudioSource))]
public class AudioManager : MonoBehaviour
{
[Header("Sound")]
@ -22,7 +23,7 @@ public class AudioManager : MonoBehaviour
// 배경음악이 설정되면 재생
audioSource.clip = mainBgm; // 음악 클립 설정
audioSource.loop = true; // 반복 재생
audioSource.volume = 0.4f; // 볼륨
audioSource.volume = 0.2f; // 볼륨
audioSource.Play(); // 음악 시작
}
}

View File

@ -4,4 +4,7 @@
public const string GameServerURL = "ws://localhost:3000";
public const int BoardSize = 15;
public const int ReplayMaxRecordSize = 10;
public const int WIN_COUNT = 5;
//무승부 확인을 위한 최소 착수 수
public const int MinCountForDrawCheck = 150;
}

View File

@ -12,10 +12,10 @@ public abstract class BasePlayerState
public void ProcessMove(GameLogic gameLogic, Enums.PlayerType playerType, int row, int col)
{
gameLogic.fioTimer.PauseTimer();
gameLogic.SetNewBoardValue(playerType, row, col);
gameLogic.CountStoneCounter();
if (gameLogic.CheckGameWin(playerType, row, col))
{
@ -24,10 +24,23 @@ public abstract class BasePlayerState
}
else
{
//TODO: 무승부 확인
if (gameLogic.TotalStoneCounter >= Constants.MinCountForDrawCheck)
{
if (gameLogic.CheckGameDraw())
{
GameManager.Instance.panelManager.OpenConfirmPanel($"Game Over: Draw",() =>{});
gameLogic.EndGame();
}
else
{
HandleNextTurn(gameLogic);
}
}
else
{
HandleNextTurn(gameLogic);
}
}
}
}
@ -85,7 +98,11 @@ public class AIState: BasePlayerState
public override void OnEnter(GameLogic gameLogic)
{
gameLogic.fioTimer.StartTimer();
//TODO: AI이식
OmokAI.Instance.StartBestMoveSearch(gameLogic.GetBoard(), (bestMove) =>
{
if(bestMove.HasValue)
HandleMove(gameLogic, bestMove.Value.Item1, bestMove.Value.Item2);
});
}
public override void OnExit(GameLogic gameLogic)
@ -132,16 +149,22 @@ public class GameLogic : MonoBehaviour
public StoneController stoneController;
public Enums.PlayerType currentTurn;
public Enums.GameType gameType;
//총 착수된 돌 카운터
public int _totalStoneCounter;
public int TotalStoneCounter{get{return _totalStoneCounter;}}
public BasePlayerState firstPlayerState;
public BasePlayerState secondPlayerState;
private BasePlayerState _currentPlayerState;
//타이머
public FioTimer fioTimer;
private const int WIN_COUNT = 5;
//선택된 좌표
public int selectedRow;
public int selectedCol;
//마지막 배치된 좌표
private int _lastRow;
private int _lastCol;
#region Renju Members
// 렌주룰 금수 검사기
@ -151,9 +174,6 @@ public class GameLogic : MonoBehaviour
private List<Vector2Int> _forbiddenMoves = new List<Vector2Int>();
#endregion
private int _lastRow;
private int _lastCol;
private static int[][] _directions = new int[][]
{
new int[] {1, 0}, // 수직
@ -168,6 +188,7 @@ public class GameLogic : MonoBehaviour
_board = new Enums.PlayerType[15, 15];
this.stoneController = stoneController;
this.gameType = gameType;
_totalStoneCounter = 0;
selectedRow = -1;
selectedCol = -1;
@ -205,12 +226,11 @@ public class GameLogic : MonoBehaviour
//TODO: 기보 매니저에게 플레이어 닉네임 넘겨주기, 프로필정보도 넘겨줘야 합니다.
ReplayManager.Instance.InitReplayData("PlayerA","nicknameB");
switch (gameType)
{
case Enums.GameType.SinglePlay:
firstPlayerState = new PlayerState(true);
secondPlayerState = new PlayerState(false);
secondPlayerState = new AIState();
SetState(firstPlayerState);
break;
case Enums.GameType.MultiPlay:
@ -221,6 +241,12 @@ public class GameLogic : MonoBehaviour
break;
}
}
//돌 카운터 증가 함수
public void CountStoneCounter()
{
_totalStoneCounter++;
}
//착수 버튼 클릭시 호출되는 함수
public void OnConfirm()
{
@ -288,6 +314,7 @@ public class GameLogic : MonoBehaviour
LastNSelectedSetting(row, col);
ReplayManager.Instance.RecordStonePlaced(Enums.StoneType.White, row, col);
break;
}
}
@ -319,7 +346,7 @@ public class GameLogic : MonoBehaviour
public void EndGame()
{
SetState(null);
//TODO: 게임 종료 후 행동 구현
}
//승리 확인 함수
@ -330,7 +357,7 @@ public class GameLogic : MonoBehaviour
var (count, _) = CountStones(_board, row, col, dir, player);
// 자기 자신 포함하여 5개 이상일 시 true 반환
if (count + 1 >= WIN_COUNT)
if (count + 1 >= Constants.WIN_COUNT)
return true;
}
@ -376,7 +403,56 @@ public class GameLogic : MonoBehaviour
return (count, openEnds);
}
#region Renju Rule Detector
public Enums.PlayerType[,] GetBoard()
{
return _board;
}
//무승부 확인
public bool CheckGameDraw()
{
if (CheckIsFull(_board)) return true; // 빈 칸이 없으면 무승부
bool playerAHasChance = CheckFiveChance(_board, Enums.PlayerType.PlayerA);
bool playerBHasChance = CheckFiveChance(_board, Enums.PlayerType.PlayerB);
return !(playerAHasChance || playerBHasChance); // 둘 다 기회가 없으면 무승부
}
//연속되는 5개가 만들어질 기회가 있는지 판단
private bool CheckFiveChance(Enums.PlayerType[,] board, Enums.PlayerType player)
{
var tempBoard = (Enums.PlayerType[,])board.Clone();
int size = board.GetLength(0);
for (int row = 0; row < size; row++)
{
for (int col = 0; col < size; col++)
{
if (tempBoard[row, col] != Enums.PlayerType.None) continue;
tempBoard[row, col] = player;
foreach (var dir in _directions)
{
var (count, _) = CountStones(tempBoard, row, col, dir, player);
// 자기 자신 포함하여 5개 이상일 시 true 반환
if (count + 1 >= Constants.WIN_COUNT) return true;
}
}
}
return false;
}
//보드가 꽉 찼는지 확인
private static bool CheckIsFull(Enums.PlayerType[,] board)
{
int size = board.GetLength(0);
for (int row = 0; row < size; row++)
{
for (int col = 0; col < size; col++)
{
if (board[row, col] == Enums.PlayerType.None) return false;
}
}
return true;
}
#region Renju Rule Detector
// 금수 위치 업데이트 및 표시
private void UpdateForbiddenMoves()
{

View File

@ -5,7 +5,6 @@ using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
[RequireComponent(typeof(AudioSource))]
public class GameManager : Singleton<GameManager>
{
private Enums.GameType _gameType;
@ -51,7 +50,6 @@ public class GameManager : Singleton<GameManager>
}
else
{
Debug.Log("착수 위치를 선택 해주세요");
//TODO: 착수할 위치를 선택하라는 동작
}
}

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.Serialization;
@ -83,4 +84,42 @@ public class MainPanelController : MonoBehaviour
{
GameManager.Instance.ChangeToGameScene(Enums.GameType.SinglePlay);
}
//상점 패널 생성
public void OnShopButtonClick()
{
List<ShopItem> shopItems = new List<ShopItem>(); //상점 데이터 리스트 생성
for (int i = 0; i < 5; i++)
{
if (i == 0) //광고 항목
{
ShopItem shopItem = new ShopItem
{
name = "광고) 코인500개 ",
price = 0
};
shopItems.Add(shopItem);
}
else
{
ShopItem shopItem = new ShopItem
{
name = i*1000+"개 ",
price = i * 1000
};
shopItems.Add(shopItem);
}
}
GameManager.Instance.panelManager.OpenShopPanel(shopItems);
}
public void OpenReplayButtonClick()
{
GameManager.Instance.panelManager.OpenReplayPanel();
}
public void OpenSettingButtonClick()
{
GameManager.Instance.panelManager.OpenSettingsPanel();
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.Networking;
@ -47,7 +48,6 @@ public class NetworkManager : Singleton<NetworkManager>
else
{
var result = www.downloadHandler.text;
success?.Invoke();
// 회원가입 성공 팝업 표시
GameManager.Instance.panelManager.OpenConfirmPanel("회원 가입이 완료 되었습니다.", () =>
@ -395,10 +395,10 @@ public class NetworkManager : Singleton<NetworkManager>
string url = Constants.ServerURL + "/coins/purchase"; // 서버 엔드포인트
PurchaseData purchaseData = new PurchaseData(amount, paymentId, paymentType);
string jsonString = JsonUtility.ToJson(purchaseData);
byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonString);
using (UnityWebRequest www = new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST))
{
byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonString);
www.uploadHandler = new UploadHandlerRaw(bodyRaw);
www.downloadHandler = new DownloadHandlerBuffer();
www.SetRequestHeader("Content-Type", "application/json");
@ -445,4 +445,36 @@ public class NetworkManager : Singleton<NetworkManager>
}
}
}
public void GetLeaderboardData(Action<List<ScoreInfo>> success, Action failure)
{
StartCoroutine(GetLeaderboardDataCoroutine(success, failure));
}
private IEnumerator GetLeaderboardDataCoroutine(Action<List<ScoreInfo>> success, Action failure)
{
string url = Constants.ServerURL + "/leaderboard/"; // 서버의 리더보드 데이터 URL
UnityWebRequest www = UnityWebRequest.Get(url); // GET 요청으로 데이터 받기
yield return www.SendWebRequest(); // 요청 전송 대기
// 요청이 실패했을 때
if (www.result == UnityWebRequest.Result.ConnectionError || www.result == UnityWebRequest.Result.ProtocolError)
{
Debug.LogError("Error: " + www.error);
failure?.Invoke();
}
else
{
// 성공적으로 데이터를 받아온 경우
string jsonResponse = www.downloadHandler.text; // 응답으로 받은 JSON 데이터
// JSON을 ScoreInfo 리스트로 파싱
ScoreListWrapper wrapper = JsonUtility.FromJson<ScoreListWrapper>(jsonResponse);
List<ScoreInfo> leaderboardItems = wrapper.leaderboardDatas;
// Show 메서드를 통해 데이터를 표시
success?.Invoke(leaderboardItems);
}
}
}

View File

@ -23,9 +23,9 @@ public class ScoreCellController : MonoBehaviour
winText.text = item.win.ToString();
loseText.text = item.lose.ToString();
if (profileImage != null && item.profileImageIndex != null)
if (profileImage != null)
{
profileImage.sprite = profileSprites[item.profileImageIndex]; // 프로필 이미지 (Sprite 할당)
profileImage.sprite = profileSprites[item.imageIndex]; // 프로필 이미지 (Sprite 할당)
}
}
}

View File

@ -2,9 +2,10 @@ using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
[Serializable]
public class ScoreListWrapper
{
public List<ScoreInfo> scoreInfos; // 여러 개의 ScoreInfo를 담을 리스트
public List<ScoreInfo> leaderboardDatas; // 여러 개의 ScoreInfo를 담을 리스트
}

View File

@ -29,7 +29,7 @@ public struct ScoreInfo
public int win;
public int lose;
public int totalGames;
public int profileImageIndex;
public int imageIndex;
}
[Serializable]
@ -43,7 +43,12 @@ public class SigninPanelController : MonoBehaviour
[SerializeField] private TMP_InputField emailInputField;
[SerializeField] private TMP_InputField passwordInputField;
[SerializeField] private MainPanelManager mainPanel;
private MainPanelManager mainPanel;
private void Awake()
{
if (mainPanel == null) mainPanel = FindObjectOfType<MainPanelManager>();
}
public void OnClickSigninButton()
{
@ -60,13 +65,13 @@ public class SigninPanelController : MonoBehaviour
NetworkManager.Instance.Signin(signinData, (signinResult) =>
{
Destroy(gameObject);
// 유저 정보 저장
UserManager.Instance.SetUserInfo(signinResult);
// 메인 패널 정보 갱신
mainPanel.UpdateMainPanelUI(GameManager.Instance.panelManager.OpenMainPanel);
Destroy(gameObject);
}, result =>
{
if (result == 0)

View File

@ -1,9 +1,20 @@
public class ForbiddenDetectorBase
{
/// <summary>
/// 흑색 돌
/// </summary>
private protected Enums.PlayerType Black = Enums.PlayerType.PlayerA;
/// <summary>
/// 빈칸
/// </summary>
private protected Enums.PlayerType Space = Enums.PlayerType.None;
// 8방향을 나타내는 델타 배열 (가로, 세로, 대각선 방향)
/// <summary>
/// 흰색 돌, 렌주룰 내에선 벽으로도 활용
/// </summary>
private protected Enums.PlayerType White = Enums.PlayerType.PlayerB;
/// <summary>
/// 8방향을 나타내는 델타 배열 (가로, 세로, 대각선 방향)
/// </summary>
private protected readonly int[,] Directions = new int[8, 2]
{
{ 1, 0 }, // 오른쪽
@ -16,11 +27,13 @@
{ 1, -1 } // 오른쪽 위
};
// 방향 쌍을 정의 (반대 방향끼리 쌍을 이룸)
// 0-4: 가로 방향 쌍 (동-서)
// 1-5: 대각선 방향 쌍 (남동-북서)
// 2-6: 세로 방향 쌍 (남-북)
// 3-7: 대각선 방향 쌍 (남서-북동)
/// <summary>
/// 방향 쌍을 정의 (반대 방향끼리 쌍을 이룸)
/// 0-4: 가로 방향 쌍 (동-서)
/// 1-5: 대각선 방향 쌍 (남동-북서)
/// 2-6: 세로 방향 쌍 (남-북)
/// 3-7: 대각선 방향 쌍 (남서-북동)
/// </summary>
private protected readonly int[,] DirectionPairs = { { 0, 4 }, { 1, 5 }, { 2, 6 }, { 3, 7 } };
// 15*15 보드 사이즈

View File

@ -30,12 +30,9 @@ public class RenjuDoubleFourDetector: ForbiddenDetectorBase
/// </summary>
private bool CheckDoubleFour(Enums.PlayerType[,] board, int row, int col)
{
// 각각 두개의 라인에서 쌍사를 형성하는 경우
if (FindDoubleLineFour(board, row, col)) return true;
// true : 일직선으로 쌍사가 만들어지는 특수 패턴
// false : 모든 경우에도 쌍사가 만들어지지 않음
return FindSingleLineDoubleFour(board, row, col);
return FindDoubleLineFour(board, row, col) || // 각각 두개의 라인에서 쌍사를 형성하는 경우
FindSingleLineDoubleFour(board, row, col); // 일직선으로 쌍사가 만들어지는 특수 패턴
}
private bool FindDoubleLineFour(Enums.PlayerType[,] board, int row, int col)
@ -136,7 +133,7 @@ public class RenjuDoubleFourDetector: ForbiddenDetectorBase
}
else
{
linePattern[centerIndex + i] = Space; // 범위 밖은 빈칸으로 처리
linePattern[centerIndex + i] = White; // 범위 밖은 백돌로 처리
}
}
@ -152,7 +149,7 @@ public class RenjuDoubleFourDetector: ForbiddenDetectorBase
}
else
{
linePattern[centerIndex - i] = Space; // 범위 밖은 빈칸으로 처리
linePattern[centerIndex - i] = White; // 범위 밖은 백돌로 처리
}
}
@ -181,7 +178,7 @@ public class RenjuDoubleFourDetector: ForbiddenDetectorBase
}
}
// 정확히 4개의 돌이 있고, 1개의 빈칸이 있으면 4로 판정
// 4개의 돌이 있고, 1개의 빈칸이 있으면 4로 판정
// (현재 위치는 흑으로 이미 설정되어 있음)
if (stoneCount == 4)
{

View File

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

View File

@ -1,8 +1,25 @@
using System.Collections.Generic;
using UnityEngine;
public class RenjuDoubleThreeDetector: ForbiddenDetectorBase
/// <summary>
/// 렌주 쌍삼(3-3) 금수 판정을 위한 개선된 클래스
/// 렌주 국제 규칙 9.3에 따라 쌍삼의 예외 상황까지 정확히 판별
/// </summary>
public class RenjuDoubleThreeDetector : ForbiddenDetectorBase
{
// 열린 3 패턴 정보를 저장하는 구조체
private struct OpenThreeInfo
{
public int direction; // 방향 인덱스
public List<Vector2Int> emptyPositions; // 빈 좌표들 (4를 만들 수 있는 위치)
public OpenThreeInfo(int dir)
{
direction = dir;
emptyPositions = new List<Vector2Int>();
}
}
/// <summary>
/// 쌍삼(3-3) 여부를 검사합니다.
/// </summary>
@ -15,12 +32,635 @@ public class RenjuDoubleThreeDetector: ForbiddenDetectorBase
// 임시로 돌 배치
board[row, col] = Black;
// 쌍삼 검사
// bool isThreeThree = CheckThreeThree(board, row, col);
// 쌍삼 기본 검사 (열린 3이 2개 이상인지)
List<OpenThreeInfo> openThrees = FindAllOpenThrees(board, row, col);
// 원래 상태로 되돌림
board[row, col] = Space;
// 열린 3이 2개 미만이면 쌍삼이 아님
if (openThrees.Count < 2)
return false;
// 렌주 규칙 9.3에 따른 예외 케이스 확인
return !CheckDoubleThreeExceptions(board, row, col, openThrees);
}
/// <summary>
/// 모든 방향에서 열린 3을 찾아 반환합니다.
/// </summary>
private List<OpenThreeInfo> FindAllOpenThrees(Enums.PlayerType[,] board, int row, int col)
{
List<OpenThreeInfo> openThrees = new List<OpenThreeInfo>();
// 4개의 방향 쌍에 대해 검사 (대각선 및 직선 포함)
for (int i = 0; i < 4; i++)
{
int dir1 = DirectionPairs[i, 0];
int dir2 = DirectionPairs[i, 1];
// 열린 3 정보 획득
OpenThreeInfo threeInfo = new OpenThreeInfo(i);
if (FindOpenThreeInDirection(board, row, col, dir1, dir2, ref threeInfo))
{
openThrees.Add(threeInfo);
}
}
return openThrees;
}
/// <summary>
/// 특정 방향에서 열린 3을 찾고 관련 정보를 채웁니다.
/// </summary>
private bool FindOpenThreeInDirection(Enums.PlayerType[,] board, int row, int col, int dir1, int dir2, ref OpenThreeInfo threeInfo)
{
// 라인 패턴 추출
Enums.PlayerType[] linePattern = ExtractLinePattern(board, row, col, dir1, dir2);
int centerIndex = 5; // 중앙 인덱스 (현재 위치)
// 연속된 열린 3 또는 한 칸 떨어진 열린 3 확인
if (FindConsecutiveOpenThree(linePattern, centerIndex, ref threeInfo, row, col, dir1, dir2) ||
FindGappedOpenThree(linePattern, centerIndex, ref threeInfo, row, col, dir1, dir2))
{
// 열린 3이 발견됨
return true;
}
return false;
}
/// <summary>
/// 라인 패턴을 추출합니다.
/// </summary>
private Enums.PlayerType[] ExtractLinePattern(Enums.PlayerType[,] board, int row, int col, int dir1, int dir2)
{
Enums.PlayerType[] linePattern = new Enums.PlayerType[11];
int centerIndex = 5; // 중앙 인덱스 (현재 위치)
// 현재 위치 설정
linePattern[centerIndex] = Black;
// dir1 방향으로 패턴 채우기
for (int i = 1; i <= 5; i++)
{
int newRow = row + Directions[dir1, 0] * i;
int newCol = col + Directions[dir1, 1] * i;
if (IsInBounds(newRow, newCol))
{
linePattern[centerIndex + i] = board[newRow, newCol];
}
else
{
linePattern[centerIndex + i] = White; // 범위 밖은 벽으로 처리하여 일관성 유지
}
}
// dir2 방향으로 패턴 채우기
for (int i = 1; i <= 5; i++)
{
int newRow = row + Directions[dir2, 0] * i;
int newCol = col + Directions[dir2, 1] * i;
if (IsInBounds(newRow, newCol))
{
linePattern[centerIndex - i] = board[newRow, newCol];
}
else
{
linePattern[centerIndex - i] = White; // 범위 밖은 벽으로 처리하여 일관성 유지
}
}
return linePattern;
}
/// <summary>
/// 연속된 열린 3 패턴을 찾고 관련 정보를 채웁니다.
/// </summary>
private bool FindConsecutiveOpenThree(Enums.PlayerType[] linePattern, int centerIndex, ref OpenThreeInfo threeInfo, int row, int col, int dir1, int dir2)
{
// 연속된 3개의 돌 패턴 (●●●)
for (int start = centerIndex - 2; start <= centerIndex; start++)
{
// 범위 체크
if (start < 0 || start + 2 >= linePattern.Length)
{
continue;
}
// 3개의 연속된 돌 확인
bool isConsecutiveThree = true;
for (int i = 0; i < 3; i++)
{
if (linePattern[start + i] != Black)
{
isConsecutiveThree = false;
break;
}
}
if (isConsecutiveThree)
{
// 양쪽이 모두 열려있는지 확인
bool isLeftOpen = (start - 1 >= 0) && (linePattern[start - 1] == Space);
bool isRightOpen = (start + 3 < linePattern.Length) && (linePattern[start + 3] == Space);
// 양쪽이 모두 열려있으면 열린 3
if (isLeftOpen && isRightOpen)
{
// 추가 검증: 더 확장해서 열려있는지 확인
bool isExtendedLeftOpen = IsExtendedOpen(linePattern, start - 1, -1);
bool isExtendedRightOpen = IsExtendedOpen(linePattern, start + 3, 1);
if (isExtendedLeftOpen && isExtendedRightOpen)
{
// 4를 만들 수 있는 위치 저장
if (isLeftOpen)
{
int leftRow = row + Directions[dir2, 0] * (centerIndex - (start - 1));
int leftCol = col + Directions[dir2, 1] * (centerIndex - (start - 1));
if (IsInBounds(leftRow, leftCol))
{
threeInfo.emptyPositions.Add(new Vector2Int(leftCol, leftRow));
}
}
if (isRightOpen)
{
int rightRow = row + Directions[dir1, 0] * ((start + 3) - centerIndex);
int rightCol = col + Directions[dir1, 1] * ((start + 3) - centerIndex);
if (IsInBounds(rightRow, rightCol))
{
threeInfo.emptyPositions.Add(new Vector2Int(rightCol, rightRow));
}
}
return true;
}
}
}
}
return false;
}
/// <summary>
/// 한 칸 떨어진 열린 3 패턴을 찾고 관련 정보를 채웁니다.
/// </summary>
private bool FindGappedOpenThree(Enums.PlayerType[] linePattern, int centerIndex, ref OpenThreeInfo threeInfo, int row, int col, int dir1, int dir2)
{
// 한 칸 떨어진 패턴 확인 (●●○● 또는 ●○●●)
for (int start = Mathf.Max(0, centerIndex - 3); start <= Mathf.Min(linePattern.Length - 4, centerIndex); start++)
{
// 패턴 내에 돌과 빈칸 개수 확인
int stoneCount = 0;
int gapCount = 0;
int gapPosition = -1;
for (int i = 0; i < 4; i++)
{
if (linePattern[start + i] == Black)
{
stoneCount++;
}
else if (linePattern[start + i] == Space)
{
gapCount++;
gapPosition = start + i;
}
else
{
// 상대 돌이나 벽이 있으면 패턴이 깨짐
stoneCount = 0;
break;
}
}
// 3개의 돌과 1개의 빈칸으로 구성된 패턴
if (stoneCount == 3 && gapCount == 1)
{
// 양쪽이 모두 열려있는지 확인
bool isLeftOpen = (start - 1 >= 0) && (linePattern[start - 1] == Space);
bool isRightOpen = (start + 4 < linePattern.Length) && (linePattern[start + 4] == Space);
// 한쪽이라도 열려있으면 잠재적 열린 3
if (isLeftOpen || isRightOpen)
{
// 빈칸에 돌을 놓았을 때 열린 4가 되는지 확인
if (CheckIfCreatesOpenFour(linePattern, gapPosition))
{
// 4를 만들 수 있는 위치 저장 (빈칸 위치)
int gapRow = row;
int gapCol = col;
// 빈칸의 보드 좌표 계산
int offset = gapPosition - centerIndex;
if (offset > 0)
{
gapRow += Directions[dir1, 0] * offset;
gapCol += Directions[dir1, 1] * offset;
}
else if (offset < 0)
{
gapRow += Directions[dir2, 0] * (-offset);
gapCol += Directions[dir2, 1] * (-offset);
}
if (IsInBounds(gapRow, gapCol))
{
threeInfo.emptyPositions.Add(new Vector2Int(gapCol, gapRow));
}
// 장목이 되는지 확인 (장목이 되면 열린 3이 아님)
if (CheckIfCreatesOverline(linePattern, gapPosition))
{
return false;
}
return true;
}
}
}
}
return false;
}
/// <summary>
/// 특정 방향으로 추가로 열려있는지 확인합니다.
/// </summary>
private bool IsExtendedOpen(Enums.PlayerType[] linePattern, int startPos, int direction)
{
// 한 칸 더 확장해서 확인
int nextPos = startPos + direction;
if (nextPos >= 0 && nextPos < linePattern.Length)
{
// 다음 칸이 상대 돌이나 벽이면 확장 불가
if (linePattern[nextPos] == White)
{
return false;
}
}
else
{
// 범위를 벗어나면 확장 불가
return false;
}
return true;
}
/// <summary>
/// 빈칸에 돌을 놓았을 때 열린 4가 되는지 확인합니다.
/// </summary>
private bool CheckIfCreatesOpenFour(Enums.PlayerType[] linePattern, int position)
{
// 시뮬레이션: 빈칸에 돌을 놓아봄
Enums.PlayerType[] testPattern = new Enums.PlayerType[linePattern.Length];
System.Array.Copy(linePattern, testPattern, linePattern.Length);
testPattern[position] = Black;
// 놓은 위치를 포함해 연속된 4가 있는지 확인
for (int start = Mathf.Max(0, position - 3); start <= position; start++)
{
// 범위 체크
if (start + 3 >= testPattern.Length)
{
continue;
}
// 4개의 연속된 돌 확인
bool isConsecutiveFour = true;
for (int i = 0; i < 4; i++)
{
if (testPattern[start + i] != Black)
{
isConsecutiveFour = false;
break;
}
}
if (isConsecutiveFour)
{
// 양쪽이 모두 열려있는지 확인 (열린 4)
bool isLeftOpen = (start - 1 >= 0) && (testPattern[start - 1] == Space);
bool isRightOpen = (start + 4 < testPattern.Length) && (testPattern[start + 4] == Space);
if (isLeftOpen || isRightOpen)
{
return true; // 열린 4나 반열린 4가 됨
}
}
}
return false;
}
/// <summary>
/// 빈칸에 돌을 놓았을 때 장목(6목 이상)이 되는지 확인합니다.
/// </summary>
private bool CheckIfCreatesOverline(Enums.PlayerType[] linePattern, int position)
{
// 시뮬레이션: 빈칸에 돌을 놓아봄
Enums.PlayerType[] testPattern = new Enums.PlayerType[linePattern.Length];
System.Array.Copy(linePattern, testPattern, linePattern.Length);
testPattern[position] = Black;
// 놓은 위치 주변의 최대 연속 돌 수 계산
int maxLength = 1; // 놓은 돌 포함
// 오른쪽 방향 연속 돌 세기
for (int i = position + 1; i < testPattern.Length && testPattern[i] == Black; i++)
{
maxLength++;
}
// 왼쪽 방향 연속 돌 세기
for (int i = position - 1; i >= 0 && testPattern[i] == Black; i--)
{
maxLength++;
}
// 6목 이상이면 장목
return maxLength >= 6;
}
/// <summary>
/// 렌주 규칙 9.3에 따른 쌍삼 예외 케이스를 확인합니다.
/// </summary>
private bool CheckDoubleThreeExceptions(Enums.PlayerType[,] board, int row, int col, List<OpenThreeInfo> openThrees)
{
// 예외 케이스 1: 하나의 삼만 열린 사가 될 수 있는 경우 (9.3 a항)
bool canFormOpenFourWithoutDoubleFour = CheckExceptionCanFormOneFour(board, row, col, openThrees);
// 예외 케이스 2: 9.3 b항 (복잡한 연쇄 체크)
bool isMeetExceptionB = CheckExceptionB(board, row, col, openThrees);
// 어느 하나라도 예외 조건을 만족하면 쌍삼이 아님
return canFormOpenFourWithoutDoubleFour || isMeetExceptionB;
}
/// <summary>
/// 예외 케이스 1: 하나의 삼만 열린 사가 될 수 있고 쌍사가 형성되지 않는 경우 (9.3 a항)
/// </summary>
private bool CheckExceptionCanFormOneFour(Enums.PlayerType[,] board, int row, int col, List<OpenThreeInfo> openThrees)
{
int canFormFourCount = 0;
// 각 열린 3에 대해, 4를 만들 수 있는지 확인
foreach (var threeInfo in openThrees)
{
foreach (var emptyPos in threeInfo.emptyPositions)
{
// 빈 위치에 돌을 놓았을 때 열린 4가 되는지 확인
board[emptyPos.y, emptyPos.x] = Black;
// 쌍사가 형성되는지 확인
bool formsDoubleFour = CheckDoubleFour(board, emptyPos.y, emptyPos.x);
// 원래 상태로 복원
board[emptyPos.y, emptyPos.x] = Space;
// 쌍사 없이 4를 만들 수 있으면 카운트 증가
if (!formsDoubleFour)
{
canFormFourCount++;
// 디버깅
// Debug.Log($"Can form four at ({emptyPos.x}, {emptyPos.y}) without double four");
}
}
}
// 하나의 삼만 쌍사 없이 4로 만들 수 있는 경우
return canFormFourCount == 1;
}
/// <summary>
/// 예외 케이스 2: 9.3 b항의 복잡한 연쇄 체크
/// </summary>
private bool CheckExceptionB(Enums.PlayerType[,] board, int row, int col, List<OpenThreeInfo> openThrees)
{
// 이 부분은 매우 복잡한 렌주 규칙 9.3 b항을 구현해야 합니다.
// 기본적인 구현만 제공하며, 필요에 따라 확장 가능합니다.
// 각 열린 3에 대해, 4를 만들 때 다른 쌍삼이 형성되는지 확인
foreach (var threeInfo in openThrees)
{
bool canFormFourWithoutChainDoubleThree = false;
foreach (var emptyPos in threeInfo.emptyPositions)
{
// 빈 위치에 돌을 놓았을 때
board[emptyPos.y, emptyPos.x] = Black;
// 다른 쌍삼이 형성되는지 확인 (연쇄 체크)
bool formsOtherDoubleThree = false;
// 다른 모든 빈 위치에 대해 쌍삼 체크
for (int r = 0; r < BoardSize; r++)
{
for (int c = 0; c < BoardSize; c++)
{
if (board[r, c] == Space)
{
// 임시로 돌 배치하여 쌍삼 체크
board[r, c] = Black;
bool isDoubleThree = CheckSimpleDoubleThree(board, r, c);
board[r, c] = Space;
if (isDoubleThree)
{
formsOtherDoubleThree = true;
break;
}
}
}
if (formsOtherDoubleThree) break;
}
// 원래 상태로 복원
board[emptyPos.y, emptyPos.x] = Space;
// 연쇄 쌍삼이 형성되지 않으면 예외 조건 만족
if (!formsOtherDoubleThree)
{
canFormFourWithoutChainDoubleThree = true;
break;
}
}
// 하나의 삼이라도 연쇄 쌍삼 없이 4를 만들 수 있으면 예외 조건 만족
if (canFormFourWithoutChainDoubleThree)
{
return true;
}
}
return false;
}
/// <summary>
/// 단순 쌍삼 체크 (연쇄 검사용, 재귀 호출 방지)
/// </summary>
private bool CheckSimpleDoubleThree(Enums.PlayerType[,] board, int row, int col)
{
int openThreeCount = 0;
// 4개의 방향 쌍에 대해 검사
for (int i = 0; i < 4; i++)
{
int dir1 = DirectionPairs[i, 0];
int dir2 = DirectionPairs[i, 1];
// 간단한 열린 3 체크
if (CheckSimpleOpenThree(board, row, col, dir1, dir2))
{
openThreeCount++;
if (openThreeCount >= 2)
{
return true;
}
}
}
return false;
}
/// <summary>
/// 단순 열린 3 체크 (연쇄 검사용)
/// </summary>
private bool CheckSimpleOpenThree(Enums.PlayerType[,] board, int row, int col, int dir1, int dir2)
{
Enums.PlayerType[] linePattern = ExtractLinePattern(board, row, col, dir1, dir2);
int centerIndex = 5;
// 연속된 열린 3 패턴 체크
for (int start = centerIndex - 2; start <= centerIndex; start++)
{
if (start < 0 || start + 2 >= linePattern.Length)
{
continue;
}
bool isConsecutiveThree = true;
for (int i = 0; i < 3; i++)
{
if (linePattern[start + i] != Black)
{
isConsecutiveThree = false;
break;
}
}
if (isConsecutiveThree)
{
bool isLeftOpen = (start - 1 >= 0) && (linePattern[start - 1] == Space);
bool isRightOpen = (start + 3 < linePattern.Length) && (linePattern[start + 3] == Space);
if (isLeftOpen && isRightOpen)
{
return true;
}
}
}
// 한 칸 떨어진 열린 3 패턴 체크 (간단 구현)
for (int start = centerIndex - 3; start <= centerIndex; start++)
{
if (start < 0 || start + 3 >= linePattern.Length)
{
continue;
}
int stoneCount = 0;
int gapCount = 0;
for (int i = 0; i < 4; i++)
{
if (linePattern[start + i] == Black)
{
stoneCount++;
}
else if (linePattern[start + i] == Space)
{
gapCount++;
}
else
{
stoneCount = 0;
break;
}
}
if (stoneCount == 3 && gapCount == 1)
{
return true;
}
}
return false;
}
/// <summary>
/// 쌍사 여부를 확인합니다 (예외 처리용)
/// </summary>
private bool CheckDoubleFour(Enums.PlayerType[,] board, int row, int col)
{
int fourCount = 0;
// 4개의 방향 쌍에 대해 검사
for (int i = 0; i < 4; i++)
{
int dir1 = DirectionPairs[i, 0];
int dir2 = DirectionPairs[i, 1];
if (CheckFourInDirection(board, row, col, dir1, dir2))
{
fourCount++;
if (fourCount >= 2)
{
return true;
}
}
}
return false;
}
/// <summary>
/// 특정 방향에서 4가 형성되는지 확인합니다.
/// </summary>
private bool CheckFourInDirection(Enums.PlayerType[,] board, int row, int col, int dir1, int dir2)
{
Enums.PlayerType[] linePattern = ExtractLinePattern(board, row, col, dir1, dir2);
int centerIndex = 5;
// 윈도우 슬라이딩으로 연속된 4를 검사
for (int start = 0; start <= 7; start++)
{
// 현재 위치가 이 윈도우에 포함되는지 확인
bool currentPositionInWindow = (start <= centerIndex && centerIndex < start + 4);
if (!currentPositionInWindow) continue;
// 윈도우 내의 돌 개수 세기
int stoneCount = 0;
for (int i = 0; i < 4; i++)
{
if (linePattern[start + i] == Black)
{
stoneCount++;
}
}
// 4개의 돌이 있으면 4로 판정
if (stoneCount == 4)
{
return true;
}
}
return false;
}
}

View File

@ -1,11 +1,14 @@
using System.Collections.Generic;
using System.Text;
using UnityEngine;
public class RenjuForbiddenMoveDetector
public class RenjuForbiddenMoveDetector : ForbiddenDetectorBase
{
// 렌주 룰 금수 감지기 생성
private RenjuRuleChecker _ruleChecker = new RenjuRuleChecker();
private RenjuOverlineDetector _overlineDetactor = new();
private RenjuDoubleFourDetector _doubleFourDetactor = new();
private RenjuDoubleThreeDetector _doubleThreeDetector = new();
/// <summary>
/// 렌주 룰로 금수 리스트를 반환하는 함수
@ -14,6 +17,120 @@ public class RenjuForbiddenMoveDetector
/// <returns>금수 좌표를 담은 리스트</returns>
public List<Vector2Int> RenjuForbiddenMove(Enums.PlayerType[,] board)
{
return _ruleChecker.GetForbiddenMoves(board);
var forbiddenCount = 0;
List<Vector2Int> forbiddenMoves = new();
List<Vector2Int> tempForbiddenMoves = new();
for (int row = 0; row < BoardSize; row++)
{
for (int col = 0; col < BoardSize; col++)
{
// ** 비어 있지 않으면 검사할 필요 없음 **
if (!IsEmptyPosition(board, row, col)) continue;
// 장목 검사
if (_overlineDetactor.IsOverline(board, row, col))
{
forbiddenCount++;
Debug.Log("장목 금수 좌표 X축 : " + row + ", Y축 : " + col);
forbiddenMoves.Add(new Vector2Int(row, col));
continue;
}
// 4-4 검사
if (_doubleFourDetactor.IsDoubleFour(board, row, col))
{
forbiddenCount++;
Debug.Log("사사 금수 좌표 X축 : " + row + ", Y축 : " + col);
forbiddenMoves.Add(new Vector2Int(row, col));
continue;
}
if(forbiddenCount > 0) continue;
// 3-3 검사
if (_doubleThreeDetector.IsDoubleThree(board, row, col))
{
tempForbiddenMoves.Add(new Vector2Int(row, col));
// if (!SimulateDoubleFour(tempBoard))
// {
// Debug.Log("삼삼 금수 좌표 X축 : " + row + ", Y축 : " + col);
// forbiddenMoves.Add(new Vector2Int(row, col));
// }
}
}
}
foreach (var pos in tempForbiddenMoves)
{
board[pos.x, pos.y] = Black;
if (!SimulateDoubleFour(board)&& !SimulateOverline(board))
{
Debug.Log("X: "+pos.x + "Y: "+ pos.y);
forbiddenMoves.Add(new Vector2Int(pos.x, pos.y));
}
}
Debug.Log(DebugBoard(board));
return forbiddenMoves;
}
private bool SimulateDoubleFour(Enums.PlayerType[,] board)
{
for (int row = 0; row < BoardSize; row++)
{
for (int col = 0; col < BoardSize; col++)
{
if (_doubleFourDetactor.IsDoubleFour(board, row, col))
return true;
}
}
return false;
}
private bool SimulateOverline(Enums.PlayerType[,] board)
{
for (int row = 0; row < BoardSize; row++)
{
for (int col = 0; col < BoardSize; col++)
{
if (_overlineDetactor.IsOverline(board, row, col))
{
return true;
}
}
}
return false;
}
/// <summary>
/// 보드 상태를 시각적으로 출력하는 디버깅 함수
/// </summary>
/// <param name="board">현재 보드 상태</param>
/// <returns>보드의 시각적 표현 문자열</returns>
private string DebugBoard(Enums.PlayerType[,] board)
{
StringBuilder sb = new StringBuilder();
for (int row = 0; row < BoardSize; row++)
{
for (int col = 0; col < BoardSize; col++)
{
sb.Append(board[row, col] switch
{
Enums.PlayerType.None => "□",
Enums.PlayerType.PlayerA => "●",
Enums.PlayerType.PlayerB => "○",
_ => "?"
});
}
sb.AppendLine(); // 줄바꿈 추가
}
return sb.ToString();
}
}

View File

@ -1,3 +1,11 @@
fileFormatVersion: 2
guid: 8618553c3e244abdb040fb7378dd4b65
timeCreated: 1741939566
fileFormatVersion: 2
guid: 4440d621b56f2ce459d819497911892b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,51 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 렌주 규칙의 모든 금수 규칙(3-3, 4-4, 장목)을 검사하는 통합 클래스
/// </summary>
public class RenjuRuleChecker: ForbiddenDetectorBase
{
private RenjuOverlineDetector _overlineDetactor = new();
private RenjuDoubleFourDetector _doubleFourDetactor = new();
private RenjuDoubleThreeDetector _doubleThreeDetector = new();
public List<Vector2Int> GetForbiddenMoves(Enums.PlayerType[,] board)
{
List<Vector2Int> forbiddenMoves = new();
for (int row = 0; row < BoardSize; row++)
{
for (int col = 0; col < BoardSize; col++)
{
// ** 비어 있지 않으면 검사할 필요 없음 **
if (!IsEmptyPosition(board, row, col)) continue;
// 장목 검사
if (_overlineDetactor.IsOverline(board, row, col))
{
Debug.Log("장목 금수 좌표 X축 : " + row + ", Y축 : " + col);
forbiddenMoves.Add(new Vector2Int(row, col));
continue;
}
// 4-4 검사
if (_doubleFourDetactor.IsDoubleFour(board, row, col))
{
Debug.Log("사사 금수 좌표 X축 : " + row + ", Y축 : " + col);
forbiddenMoves.Add(new Vector2Int(row, col));
continue;
}
// 3-3 검사
if (_doubleThreeDetector.IsDoubleThree(board, row, col))
{
Debug.Log("삼삼 금수 좌표 X축 : " + row + ", Y축 : " + col);
forbiddenMoves.Add(new Vector2Int(row, col));
}
}
}
return forbiddenMoves;
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 083e8b9070ed407b8744d4cacd0d53dc
timeCreated: 1742256498

View File

@ -13,7 +13,7 @@ public class ReplayController : MonoBehaviour
[SerializeField] private GameObject[] userBProfileImages;
void Start()
{
InitReplayUI();
// InitReplayUI();
//TODO: 프로필 이미지 불러오기
}

View File

@ -25,34 +25,11 @@ public class LeaderBoardController : MonoBehaviour
if (isLeaderboardLoaded) return; // 이미 리더보드가 로드되었으면 중복 호출 방지
leaderboardPanel.SetActive(true);
StartCoroutine(GetLeaderboardData());
isLeaderboardLoaded = true;
}
private IEnumerator GetLeaderboardData()
NetworkManager.Instance.GetLeaderboardData((leaderboardItems) =>
{
string url = Constants.ServerURL + "/leaderboard"; // 서버의 리더보드 데이터 URL
UnityWebRequest www = UnityWebRequest.Get(url); // GET 요청으로 데이터 받기
yield return www.SendWebRequest(); // 요청 전송 대기
// 요청이 실패했을 때
if (www.result == UnityWebRequest.Result.ConnectionError || www.result == UnityWebRequest.Result.ProtocolError)
{
Debug.LogError("Error: " + www.error);
}
else
{
// 성공적으로 데이터를 받아온 경우
string jsonResponse = www.downloadHandler.text; // 응답으로 받은 JSON 데이터
// JSON을 ScoreInfo 리스트로 파싱
ScoreListWrapper wrapper = JsonUtility.FromJson<ScoreListWrapper>(jsonResponse);
List<ScoreInfo> leaderboardItems = wrapper.scoreInfos;
// Show 메서드를 통해 데이터를 표시
Show(leaderboardItems);
}
}, () => { });
isLeaderboardLoaded = true;
}
public void Show(List<ScoreInfo> leaderboardItems)
@ -106,7 +83,7 @@ public class LeaderBoardController : MonoBehaviour
if (!string.IsNullOrEmpty(savedData))
{
// 저장된 JSON 데이터를 파싱하여 리더보드 리스트로 변환
leaderboard = JsonUtility.FromJson<ScoreListWrapper>(savedData).scoreInfos;
leaderboard = JsonUtility.FromJson<ScoreListWrapper>(savedData).leaderboardDatas;
}
return leaderboard;

View File

@ -19,6 +19,7 @@ public class PanelManager : MonoBehaviour
private void Awake()
{
SetCanvas();
// Prefabs 폴더에서 모든 패널 프리팹 로드
GameObject[] prefabs = Resources.LoadAll<GameObject>("Prefabs/Panels");
@ -30,7 +31,7 @@ public class PanelManager : MonoBehaviour
Debug.Log($"총 {panelPrefabs.Count}개의 패널이 로드됨.");
}
void Start()
void SetCanvas()
{
if (_canvas == null)
{
@ -114,7 +115,6 @@ public class PanelManager : MonoBehaviour
if (_canvas != null)
{
var settingsPanelObject = GetPanel("Setting Panel");
settingsPanelObject.GetComponent<PanelController>().Show();
}
}

View File

@ -41,9 +41,4 @@ public class ReplayPanelItemsController : ScrollPanelController
replayCell.SetReplayRecord(replayRecord);
}
}
public virtual void Show()
{
base.Show();
}
}

View File

@ -49,7 +49,7 @@ TextureImporter:
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteBorder: {x: 23, y: 21, z: 23, w: 22}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
@ -126,7 +126,7 @@ TextureImporter:
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
internalID: 1537655665
vertices: []
indices:
edges: []

View File

@ -49,7 +49,7 @@ TextureImporter:
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteBorder: {x: 0, y: 19, z: 0, w: 20}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
@ -126,7 +126,7 @@ TextureImporter:
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
internalID: 1537655665
vertices: []
indices:
edges: []

View File

@ -306,7 +306,11 @@ PrefabInstance:
m_Modifications:
- target: {fileID: 626368541760032086, guid: 65aac0e22fe25aa42b9cf7df776a854d, type: 3}
propertyPath: m_Name
value: '[Canvas] Game UI'
value: Canvas
objectReference: {fileID: 0}
- target: {fileID: 1250546304786973426, guid: 65aac0e22fe25aa42b9cf7df776a854d, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6113787613246818512, guid: 65aac0e22fe25aa42b9cf7df776a854d, type: 3}
propertyPath: m_Pivot.x
@ -21706,6 +21710,7 @@ GameObject:
m_Component:
- component: {fileID: 1260188604546925059}
- component: {fileID: 5677811662396631252}
- component: {fileID: 5677811662396631253}
m_Layer: 0
m_Name: GameManager
m_TagString: Untagged
@ -33884,10 +33889,104 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 35a4c6d5d3a97b444b968e68ec8bb9f7, type: 3}
m_Name:
m_EditorClassIdentifier:
signinPanel: {fileID: 0}
signupPanel: {fileID: 0}
canvas: {fileID: 0}
profileSprites: []
panelManagerPrefab: {fileID: 3475740041361426276, guid: 085ca07ca90c92545b2594bd13412701, type: 3}
audioManagerPrefab: {fileID: 2946408323859178723, guid: e829818dce39a5d4383e061111bed871, type: 3}
--- !u!82 &5677811662396631253
AudioSource:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3626119060319719757}
m_Enabled: 1
serializedVersion: 4
OutputAudioMixerGroup: {fileID: 0}
m_audioClip: {fileID: 0}
m_PlayOnAwake: 1
m_Volume: 1
m_Pitch: 1
Loop: 0
Mute: 0
Spatialize: 0
SpatializePostEffects: 0
Priority: 128
DopplerLevel: 1
MinDistance: 1
MaxDistance: 500
Pan2D: 0
rolloffMode: 0
BypassEffects: 0
BypassListenerEffects: 0
BypassReverbZones: 0
rolloffCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
panLevelCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
spreadCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
reverbZoneMixCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
--- !u!61 &5684907091474785187
BoxCollider2D:
m_ObjectHideFlags: 0