From 99c6379554880288cde488d2acabf8f612be8436 Mon Sep 17 00:00:00 2001 From: Lim0_C Date: Tue, 25 Mar 2025 15:24:06 +0900 Subject: [PATCH 1/9] =?UTF-8?q?DO-46=20[Fix]=20=EC=B0=A9=EC=88=98=20?= =?UTF-8?q?=EC=9C=A0=ED=9A=A8X=20=EC=95=A0=EB=8B=88=EB=A9=94=EC=9D=B4?= =?UTF-8?q?=EC=85=98=20=EB=B0=8F=20Ai=20=EC=9D=B4=EB=A6=84=20=EC=9E=90?= =?UTF-8?q?=EB=8F=99=20=EC=83=9D=EC=84=B1=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Prefabs/Panels/Main Panel.prefab | 6 ++--- Assets/Scenes/Game.unity | 2 +- Assets/Script/Common/Constants.cs | 2 -- Assets/Script/Game/GameLogic.cs | 24 +++++++++++++++---- Assets/Script/Game/GameManager.cs | 12 ++++++---- 5 files changed, 31 insertions(+), 15 deletions(-) diff --git a/Assets/Resources/Prefabs/Panels/Main Panel.prefab b/Assets/Resources/Prefabs/Panels/Main Panel.prefab index 834ee12..72c53db 100644 --- a/Assets/Resources/Prefabs/Panels/Main Panel.prefab +++ b/Assets/Resources/Prefabs/Panels/Main Panel.prefab @@ -1457,7 +1457,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 0 + m_IsActive: 1 --- !u!224 &405965270916774547 RectTransform: m_ObjectHideFlags: 0 @@ -1713,7 +1713,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: "\uD638\uB7AD\uC774" + m_text: tester m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: 85a19688db53c77469fc4406b01045da, type: 2} m_sharedMaterial: {fileID: -2477908578676791210, guid: 85a19688db53c77469fc4406b01045da, type: 2} @@ -1799,7 +1799,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!224 &8172929902404983356 RectTransform: m_ObjectHideFlags: 0 diff --git a/Assets/Scenes/Game.unity b/Assets/Scenes/Game.unity index 254abaa..466309f 100644 --- a/Assets/Scenes/Game.unity +++ b/Assets/Scenes/Game.unity @@ -328,7 +328,7 @@ Camera: m_Enabled: 1 serializedVersion: 2 m_ClearFlags: 1 - m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_BackGroundColor: {r: 0.5372549, g: 0.654902, b: 0.5803922, a: 1} m_projectionMatrixMode: 1 m_GateFitMode: 2 m_FOVAxisMode: 0 diff --git a/Assets/Script/Common/Constants.cs b/Assets/Script/Common/Constants.cs index c8d7fe7..8fae832 100644 --- a/Assets/Script/Common/Constants.cs +++ b/Assets/Script/Common/Constants.cs @@ -7,6 +7,4 @@ public const int WIN_COUNT = 5; //무승부 확인을 위한 최소 착수 수 public const int MinCountForDrawCheck = 150; - - public string[] AI_NAMIES = { "이세돌", "신사동호랭이","진짜인간임","종로3가짱돌","마스터김춘배","62세황순자","고준일 강사님"}; } \ No newline at end of file diff --git a/Assets/Script/Game/GameLogic.cs b/Assets/Script/Game/GameLogic.cs index 36e120c..74d6c80 100644 --- a/Assets/Script/Game/GameLogic.cs +++ b/Assets/Script/Game/GameLogic.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; +using Random = UnityEngine.Random; public abstract class BasePlayerState { @@ -233,11 +234,15 @@ public class GameLogic : MonoBehaviour // AI 난이도 설정(급수 설정) OmokAI.Instance.SetRating(UserManager.Instance.Rating); - //유저 이름 사진 초기화 - GameManager.Instance.InitPlayersName(UserManager.Instance.Nickname, "AIPlayer"); - GameManager.Instance.InitProfileImages(UserManager.Instance.imageIndex, 1); + //AI닉네임 랜덤생성 + var aiName = RandomAINickname(); + var imageIndex = UnityEngine.Random.Range(0, 2); - ReplayManager.Instance.InitReplayData(UserManager.Instance.Nickname,"PlayerAI", UserManager.Instance.imageIndex, 1); + //유저 이름 사진 초기화 + GameManager.Instance.InitPlayersName(UserManager.Instance.Nickname, aiName); + GameManager.Instance.InitProfileImages(UserManager.Instance.imageIndex, imageIndex); + + ReplayManager.Instance.InitReplayData(UserManager.Instance.Nickname,aiName, UserManager.Instance.imageIndex, imageIndex); SetState(firstPlayerState); break; @@ -250,6 +255,17 @@ public class GameLogic : MonoBehaviour break; } } + + //AI닉네임 랜덤 생성 + private string RandomAINickname() + { + string[] AI_NAMIES = { "이세돌", "신사동호랭이","진짜인간임","종로3가짱돌","마스터김춘배","62세황순자","고준일 강사님"}; + + var index = UnityEngine.Random.Range(0, AI_NAMIES.Length); + + return AI_NAMIES[index]; + } + //돌 카운터 증가 함수 public void CountStoneCounter() { diff --git a/Assets/Script/Game/GameManager.cs b/Assets/Script/Game/GameManager.cs index e37aca3..8b09d03 100644 --- a/Assets/Script/Game/GameManager.cs +++ b/Assets/Script/Game/GameManager.cs @@ -11,7 +11,7 @@ public class GameManager : Singleton private Enums.GameType _gameType; private GameLogic _gameLogic; private StoneController _stoneController; - private GameObject _omokBoardImage; + private GameObject _camera; private GameUIController _gameUIController; [SerializeField] private GameObject panelManagerPrefab; @@ -46,10 +46,12 @@ public class GameManager : Singleton } else { - if (_stoneController != null && _omokBoardImage != null) + if (_camera != null) { - _stoneController.GetComponent().DOShakePosition(0.5f, 0.5f); - _omokBoardImage.GetComponent().DOShakePosition(0.5f, 0.5f); + _camera.transform.DOShakePosition(0.5f, 0.5f).OnComplete(() => + { + _camera.transform.position = new Vector3(0,0,-10); + }); } } } @@ -73,7 +75,7 @@ public class GameManager : Singleton _stoneController = GameObject.FindObjectOfType(); _stoneController.InitStones(); var fioTimer = FindObjectOfType(); - _omokBoardImage = GameObject.FindObjectOfType().gameObject; + _camera = GameObject.FindObjectOfType().gameObject; _gameUIController = GameObject.FindObjectOfType(); _gameLogic = new GameLogic(_stoneController, _gameType, fioTimer); From 5801348bc76fd2a8d5dd7fb33ec9a9ef52faf600 Mon Sep 17 00:00:00 2001 From: Lim0_C Date: Tue, 25 Mar 2025 18:07:10 +0900 Subject: [PATCH 2/9] =?UTF-8?q?DO-46=20[Feat]=20=EA=B2=8C=EC=9E=84=20?= =?UTF-8?q?=EC=8A=B9=ED=8C=A8=20=EC=9D=B4=ED=8E=99=ED=8A=B8=20=ED=8C=A8?= =?UTF-8?q?=EB=84=90=20=EB=9D=84=EC=9A=B0=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/GameUIController.cs | 10 +--- Assets/LYC/GameLYC.unity.meta | 2 +- .../Prefabs/Panels/Main Panel.prefab | 6 +-- Assets/Script/Game/GameLogic.cs | 16 +++--- .../Script/UI/PanelController/PanelManager.cs | 50 +++++++++++++++++++ 5 files changed, 62 insertions(+), 22 deletions(-) diff --git a/Assets/GameUIController.cs b/Assets/GameUIController.cs index d5f89d4..b602f67 100644 --- a/Assets/GameUIController.cs +++ b/Assets/GameUIController.cs @@ -17,6 +17,7 @@ public class GameUIController : MonoBehaviour [SerializeField] private Image profileImageB; [SerializeField] private Sprite[] profileImageSprites; //0. 기본 드래곤 1. 기본 호랑이 2.아이보리 드래곤 3. 아이보리 호랑이 [SerializeField] private Sprite[] indicatorSprites; //0. active 1. inactive + private Sprite _originalSpriteA; private Sprite _originalSpriteB; @@ -45,14 +46,7 @@ public class GameUIController : MonoBehaviour { GameManager.Instance.panelManager.OpenSettingsPanel(); } - - public void InitUI() - { - if (UserManager.Instance == null) return; - retryButton.SetActive(false); - playerANameText.text = UserManager.Instance.Nickname; - } - + public void InitPlayersName(string playerNameA, string playerNameB) { playerANameText.text = playerNameA; diff --git a/Assets/LYC/GameLYC.unity.meta b/Assets/LYC/GameLYC.unity.meta index d9e199b..cea0f0a 100644 --- a/Assets/LYC/GameLYC.unity.meta +++ b/Assets/LYC/GameLYC.unity.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: b8e3d5d18e544774eb144db419a327d2 +guid: ded4103a87c2de84db5f4ce2675227ef DefaultImporter: externalObjects: {} userData: diff --git a/Assets/Resources/Prefabs/Panels/Main Panel.prefab b/Assets/Resources/Prefabs/Panels/Main Panel.prefab index 72c53db..ca0fdef 100644 --- a/Assets/Resources/Prefabs/Panels/Main Panel.prefab +++ b/Assets/Resources/Prefabs/Panels/Main Panel.prefab @@ -1457,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 @@ -1713,7 +1713,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: tester + m_text: "\uD654\uB791\uB098\uBE44" m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: 85a19688db53c77469fc4406b01045da, type: 2} m_sharedMaterial: {fileID: -2477908578676791210, guid: 85a19688db53c77469fc4406b01045da, type: 2} @@ -1799,7 +1799,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 diff --git a/Assets/Script/Game/GameLogic.cs b/Assets/Script/Game/GameLogic.cs index 74d6c80..3a1176d 100644 --- a/Assets/Script/Game/GameLogic.cs +++ b/Assets/Script/Game/GameLogic.cs @@ -21,11 +21,9 @@ public abstract class BasePlayerState if (gameLogic.CheckGameWin(playerType, row, col)) { - GameManager.Instance.panelManager.OpenConfirmPanel($"Game Over: {playerType} Win", () => - { - var gameResult = playerType == Enums.PlayerType.PlayerA? Enums.GameResult.Win:Enums.GameResult.Lose; - gameLogic.EndGame(gameResult); - }); + var gameResult = playerType == Enums.PlayerType.PlayerA? Enums.GameResult.Win:Enums.GameResult.Lose; + GameManager.Instance.panelManager.OpenEffectPanel(gameResult); + gameLogic.EndGame(gameResult); } else { @@ -33,10 +31,8 @@ public abstract class BasePlayerState { if (gameLogic.CheckGameDraw()) { - GameManager.Instance.panelManager.OpenConfirmPanel($"Game Over: Draw", () => - { - gameLogic.EndGame(Enums.GameResult.Draw); - }); + GameManager.Instance.panelManager.OpenEffectPanel(Enums.GameResult.Draw); + gameLogic.EndGame(Enums.GameResult.Draw); } else { @@ -369,7 +365,7 @@ public class GameLogic : MonoBehaviour SetState(null); ReplayManager.Instance.SaveReplayDataResult(result); //TODO: 게임 종료 후 행동 구현 - SceneManager.LoadScene("Main"); + // SceneManager.LoadScene("Main"); } //승리 확인 함수 diff --git a/Assets/Script/UI/PanelController/PanelManager.cs b/Assets/Script/UI/PanelController/PanelManager.cs index cdda752..0b036bc 100644 --- a/Assets/Script/UI/PanelController/PanelManager.cs +++ b/Assets/Script/UI/PanelController/PanelManager.cs @@ -16,6 +16,7 @@ public class PanelManager : MonoBehaviour private LoadingPanelController loadingPanelController; private Dictionary panelPrefabs = new Dictionary(); + private Dictionary effectPanelPrefabs = new Dictionary(); private void Awake() { @@ -27,6 +28,13 @@ public class PanelManager : MonoBehaviour { panelPrefabs[prefab.name] = prefab; } + + //게임결과 이펙트 패널 + GameObject[] effectPrefabs = Resources.LoadAll("Prefabs/Effects"); + foreach (GameObject effect in effectPrefabs) + { + effectPanelPrefabs[effect.name] = effect; + } Debug.Log($"총 {panelPrefabs.Count}개의 패널이 로드됨."); } @@ -52,6 +60,48 @@ public class PanelManager : MonoBehaviour return null; } + + #region 게임결과 이펙트 패널 관련 + + public GameObject GetEffectPanel(string panelName) + { + if (effectPanelPrefabs.TryGetValue(panelName, out GameObject prefab)) + { + return Instantiate(prefab, _canvas.transform); + } + else + { + Debug.LogError($"패널 '{panelName}'을 찾을 수 없습니다."); + } + + return null; + } + + public void OpenEffectPanel(Enums.GameResult gameResult) + { + switch (gameResult) + { + case Enums.GameResult.Win: + if (_canvas != null) + { + var winEffectPanelObject = GetEffectPanel("Win Effect Panel"); + } + break; + case Enums.GameResult.Lose: + if (_canvas != null) + { + var winEffectPanelObject = GetEffectPanel("Lose Effect Panel"); + } + break; + case Enums.GameResult.Draw: + if (_canvas != null) + { + var winEffectPanelObject = GetEffectPanel("Draw Effect Panel"); + } + break; + } + } + #endregion public void OpenMainPanel() { From b8c6c1bf740ca73bdd82af9912af447a8c7cf458 Mon Sep 17 00:00:00 2001 From: Jay <96156114+jaydev00a@users.noreply.github.com> Date: Wed, 26 Mar 2025 02:06:46 +0900 Subject: [PATCH 3/9] =?UTF-8?q?DO-58=20[Feat]=20=EB=A9=80=ED=8B=B0=20?= =?UTF-8?q?=ED=94=8C=EB=A0=88=EC=9D=B4=20=ED=85=9C=ED=94=8C=EB=A6=BF=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EB=B0=8F=20ParrelSync=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Plugins/ParrelSync.meta | 8 + .../Plugins/ParrelSync/ScriptableObjects.meta | 8 + .../ParrelSyncProjectSettings.asset | 15 ++ .../ParrelSyncProjectSettings.asset.meta | 8 + .../Utility/UnityMainThreadDispatcher.prefab | 46 ++++ .../UnityMainThreadDispatcher.prefab.meta | 7 + Assets/Scenes/Game.unity | 58 +++++ Assets/Script/Common/AudioManager.cs | 10 +- Assets/Script/Common/Constants.cs | 10 + .../Common/UnityMainThreadDispatcher.cs | 121 ++++++++++ .../Common/UnityMainThreadDispatcher.cs.meta | 3 + Assets/Script/Game/GameLogic.cs | 214 +++++++++++++++++- Assets/Script/Game/GameManager.cs | 3 + Assets/Script/Game/MultiplayManager.cs | 195 ++++++++++++++++ Assets/Script/Game/MultiplayManager.cs.meta | 3 + Assets/Script/Main/LoadingPanelController.cs | 3 +- Assets/Script/Main/MainPanelController.cs | 3 +- .../Script/UI/PanelController/PanelManager.cs | 23 +- Packages/manifest.json | 2 + Packages/packages-lock.json | 23 ++ 20 files changed, 752 insertions(+), 11 deletions(-) create mode 100644 Assets/Plugins/ParrelSync.meta create mode 100644 Assets/Plugins/ParrelSync/ScriptableObjects.meta create mode 100644 Assets/Plugins/ParrelSync/ScriptableObjects/ParrelSyncProjectSettings.asset create mode 100644 Assets/Plugins/ParrelSync/ScriptableObjects/ParrelSyncProjectSettings.asset.meta create mode 100644 Assets/Resources/Prefabs/Utility/UnityMainThreadDispatcher.prefab create mode 100644 Assets/Resources/Prefabs/Utility/UnityMainThreadDispatcher.prefab.meta create mode 100644 Assets/Script/Common/UnityMainThreadDispatcher.cs create mode 100644 Assets/Script/Common/UnityMainThreadDispatcher.cs.meta create mode 100644 Assets/Script/Game/MultiplayManager.cs create mode 100644 Assets/Script/Game/MultiplayManager.cs.meta diff --git a/Assets/Plugins/ParrelSync.meta b/Assets/Plugins/ParrelSync.meta new file mode 100644 index 0000000..e9be2ff --- /dev/null +++ b/Assets/Plugins/ParrelSync.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 49e291f62c68bcb4b8ae5c0b9a87f98d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/ParrelSync/ScriptableObjects.meta b/Assets/Plugins/ParrelSync/ScriptableObjects.meta new file mode 100644 index 0000000..2aac655 --- /dev/null +++ b/Assets/Plugins/ParrelSync/ScriptableObjects.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a7530dff3f8b9a74081055b376b00dc7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/ParrelSync/ScriptableObjects/ParrelSyncProjectSettings.asset b/Assets/Plugins/ParrelSync/ScriptableObjects/ParrelSyncProjectSettings.asset new file mode 100644 index 0000000..0ec926c --- /dev/null +++ b/Assets/Plugins/ParrelSync/ScriptableObjects/ParrelSyncProjectSettings.asset @@ -0,0 +1,15 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c0011418c9d75434988a06b6df93b283, type: 3} + m_Name: ParrelSyncProjectSettings + m_EditorClassIdentifier: + m_OptionalSymbolicLinkFolders: [] diff --git a/Assets/Plugins/ParrelSync/ScriptableObjects/ParrelSyncProjectSettings.asset.meta b/Assets/Plugins/ParrelSync/ScriptableObjects/ParrelSyncProjectSettings.asset.meta new file mode 100644 index 0000000..fde75d0 --- /dev/null +++ b/Assets/Plugins/ParrelSync/ScriptableObjects/ParrelSyncProjectSettings.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 87e010131d20c0f4f8c3a9fe6d9bcdfb +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/Prefabs/Utility/UnityMainThreadDispatcher.prefab b/Assets/Resources/Prefabs/Utility/UnityMainThreadDispatcher.prefab new file mode 100644 index 0000000..0eb94f6 --- /dev/null +++ b/Assets/Resources/Prefabs/Utility/UnityMainThreadDispatcher.prefab @@ -0,0 +1,46 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &6968746470133643514 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2544285477164602481} + - component: {fileID: 450855533396324322} + m_Layer: 0 + m_Name: UnityMainThreadDispatcher + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2544285477164602481 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6968746470133643514} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &450855533396324322 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6968746470133643514} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e7461fd0f3834d9283d0ea00daaaea3b, type: 3} + m_Name: + m_EditorClassIdentifier: diff --git a/Assets/Resources/Prefabs/Utility/UnityMainThreadDispatcher.prefab.meta b/Assets/Resources/Prefabs/Utility/UnityMainThreadDispatcher.prefab.meta new file mode 100644 index 0000000..ea589bc --- /dev/null +++ b/Assets/Resources/Prefabs/Utility/UnityMainThreadDispatcher.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 120a1b7daa97a4247ae154ca3321d3b8 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scenes/Game.unity b/Assets/Scenes/Game.unity index 254abaa..bd4adef 100644 --- a/Assets/Scenes/Game.unity +++ b/Assets/Scenes/Game.unity @@ -47216,6 +47216,63 @@ SpriteRenderer: m_WasSpriteAssigned: 0 m_MaskInteraction: 0 m_SpriteSortPoint: 0 +--- !u!1001 &8064471184320700382 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + serializedVersion: 3 + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 2544285477164602481, guid: 120a1b7daa97a4247ae154ca3321d3b8, type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2544285477164602481, guid: 120a1b7daa97a4247ae154ca3321d3b8, type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2544285477164602481, guid: 120a1b7daa97a4247ae154ca3321d3b8, type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2544285477164602481, guid: 120a1b7daa97a4247ae154ca3321d3b8, type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2544285477164602481, guid: 120a1b7daa97a4247ae154ca3321d3b8, type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2544285477164602481, guid: 120a1b7daa97a4247ae154ca3321d3b8, type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2544285477164602481, guid: 120a1b7daa97a4247ae154ca3321d3b8, type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2544285477164602481, guid: 120a1b7daa97a4247ae154ca3321d3b8, type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2544285477164602481, guid: 120a1b7daa97a4247ae154ca3321d3b8, type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2544285477164602481, guid: 120a1b7daa97a4247ae154ca3321d3b8, type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6968746470133643514, guid: 120a1b7daa97a4247ae154ca3321d3b8, type: 3} + propertyPath: m_Name + value: UnityMainThreadDispatcher + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 120a1b7daa97a4247ae154ca3321d3b8, type: 3} --- !u!4 &8068830904452262562 Transform: m_ObjectHideFlags: 0 @@ -54561,3 +54618,4 @@ SceneRoots: - {fileID: 2953084326979075905} - {fileID: 873369031} - {fileID: 1657305406} + - {fileID: 8064471184320700382} diff --git a/Assets/Script/Common/AudioManager.cs b/Assets/Script/Common/AudioManager.cs index a264c4d..f635733 100644 --- a/Assets/Script/Common/AudioManager.cs +++ b/Assets/Script/Common/AudioManager.cs @@ -46,11 +46,17 @@ public class AudioManager : MonoBehaviour public void PlayClickSound() { - audioSource.PlayOneShot(clickSound, sfxVolume); + if (audioSource != null) + { + audioSource.PlayOneShot(clickSound, sfxVolume); + } } public void PlayCloseSound() { - audioSource.PlayOneShot(closeSound, sfxVolume); + if (audioSource != null) + { + audioSource.PlayOneShot(closeSound, sfxVolume); + } } } diff --git a/Assets/Script/Common/Constants.cs b/Assets/Script/Common/Constants.cs index ae029e0..84d60dd 100644 --- a/Assets/Script/Common/Constants.cs +++ b/Assets/Script/Common/Constants.cs @@ -10,4 +10,14 @@ public const int RAING_POINTS = 10; public string[] AI_NAMIES = { "이세돌", "신사동호랭이","진짜인간임","종로3가짱돌","마스터김춘배","62세황순자","고준일 강사님"}; + + public enum MultiplayManagerState + { + CreateRoom, // 방 생성 + JoinRoom, // 생성된 방에 참여 + StartGame, // 생성한 방에 다른 유저가 참여해서 게임 시작 + SwitchAI, // 15초 후 매칭 실패 시 AI 플레이로 전환 알림 + ExitRoom, // 자신이 방을 빠져 나왔을 때 + EndGame // 상대방이 접속을 끊거나 방을 나갔을 때 + }; } \ No newline at end of file diff --git a/Assets/Script/Common/UnityMainThreadDispatcher.cs b/Assets/Script/Common/UnityMainThreadDispatcher.cs new file mode 100644 index 0000000..9e9e932 --- /dev/null +++ b/Assets/Script/Common/UnityMainThreadDispatcher.cs @@ -0,0 +1,121 @@ +/* +Copyright 2015 Pim de Witte All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +using UnityEngine; +using System.Collections; +using System.Collections.Generic; +using System; +using System.Threading.Tasks; + +namespace PimDeWitte.UnityMainThreadDispatcher { + /// Author: Pim de Witte (pimdewitte.com) and contributors, https://github.com/PimDeWitte/UnityMainThreadDispatcher + /// + /// A thread-safe class which holds a queue with actions to execute on the next Update() method. It can be used to make calls to the main thread for + /// things such as UI Manipulation in Unity. It was developed for use in combination with the Firebase Unity plugin, which uses separate threads for event handling + /// + public class UnityMainThreadDispatcher : MonoBehaviour { + + private static readonly Queue _executionQueue = new Queue(); + + public void Update() { + lock(_executionQueue) { + while (_executionQueue.Count > 0) { + _executionQueue.Dequeue().Invoke(); + } + } + } + + /// + /// Locks the queue and adds the IEnumerator to the queue + /// + /// IEnumerator function that will be executed from the main thread. + public void Enqueue(IEnumerator action) { + lock (_executionQueue) { + _executionQueue.Enqueue (() => { + StartCoroutine (action); + }); + } + } + + /// + /// Locks the queue and adds the Action to the queue + /// + /// function that will be executed from the main thread. + public void Enqueue(Action action) + { + Enqueue(ActionWrapper(action)); + } + + /// + /// Locks the queue and adds the Action to the queue, returning a Task which is completed when the action completes + /// + /// function that will be executed from the main thread. + /// A Task that can be awaited until the action completes + public Task EnqueueAsync(Action action) + { + var tcs = new TaskCompletionSource(); + + void WrappedAction() { + try + { + action(); + tcs.TrySetResult(true); + } catch (Exception ex) + { + tcs.TrySetException(ex); + } + } + + Enqueue(ActionWrapper(WrappedAction)); + return tcs.Task; + } + + + IEnumerator ActionWrapper(Action a) + { + a(); + yield return null; + } + + + private static UnityMainThreadDispatcher _instance = null; + + public static bool Exists() { + return _instance != null; + } + + public static UnityMainThreadDispatcher Instance() { + if (!Exists ()) { + throw new Exception ("UnityMainThreadDispatcher could not find the UnityMainThreadDispatcher object. Please ensure you have added the MainThreadExecutor Prefab to your scene."); + } + return _instance; + } + + + void Awake() { + if (_instance == null) { + _instance = this; + DontDestroyOnLoad(this.gameObject); + } + } + + void OnDestroy() { + _instance = null; + } + + + } +} \ No newline at end of file diff --git a/Assets/Script/Common/UnityMainThreadDispatcher.cs.meta b/Assets/Script/Common/UnityMainThreadDispatcher.cs.meta new file mode 100644 index 0000000..9c2b213 --- /dev/null +++ b/Assets/Script/Common/UnityMainThreadDispatcher.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e7461fd0f3834d9283d0ea00daaaea3b +timeCreated: 1742916255 \ No newline at end of file diff --git a/Assets/Script/Game/GameLogic.cs b/Assets/Script/Game/GameLogic.cs index 36e120c..2d59f55 100644 --- a/Assets/Script/Game/GameLogic.cs +++ b/Assets/Script/Game/GameLogic.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; +using PimDeWitte.UnityMainThreadDispatcher; public abstract class BasePlayerState { @@ -54,10 +55,32 @@ public class PlayerState : BasePlayerState { private Enums.PlayerType _playerType; private bool _isFirstPlayer; + + private MultiplayManager _multiplayManager; + private string _roomId; + private bool _isMultiplay; + public PlayerState(bool isFirstPlayer) { _isFirstPlayer = isFirstPlayer; _playerType = isFirstPlayer ? Enums.PlayerType.PlayerA : Enums.PlayerType.PlayerB; + _isMultiplay = false; + } + + public PlayerState(bool isFirstPlayer, MultiplayManager multiplayManager, JoinRoomData data) + : this(isFirstPlayer) + { + _multiplayManager = multiplayManager; + _roomId = data.roomId; + _isMultiplay = true; + } + + public PlayerState(bool isFirstPlayer, MultiplayManager multiplayManager, string roomId) + : this(isFirstPlayer) + { + _multiplayManager = multiplayManager; + _roomId = roomId; + _isMultiplay = true; } public override void OnEnter(GameLogic gameLogic) @@ -88,6 +111,11 @@ public class PlayerState : BasePlayerState public override void HandleMove(GameLogic gameLogic, int row, int col) { gameLogic.SetStoneSelectedState(row, col); + if (_isMultiplay) + { + Debug.Log("row: " + row + "col: " + col); + _multiplayManager.SendPlayerMove(_roomId, new Vector2Int(row, col)); + } } public override void HandleNextTurn(GameLogic gameLogic) @@ -132,24 +160,64 @@ public class AIState: BasePlayerState } public class MultiPlayerState: BasePlayerState { + private Enums.PlayerType _playerType; + private bool _isFirstPlayer; + + private MultiplayManager _multiplayManager; + + public MultiPlayerState(bool isFirstPlayer, MultiplayManager multiplayManager) + { + _isFirstPlayer = isFirstPlayer; + _playerType = isFirstPlayer ? Enums.PlayerType.PlayerA : Enums.PlayerType.PlayerB; + _multiplayManager = multiplayManager; + } + public override void OnEnter(GameLogic gameLogic) { gameLogic.fioTimer.StartTimer(); + //TODO: 첫번째 플레이어면 렌주 룰 확인 + #region Renju Turn Set + // 턴이 변경될 때마다 금수 위치 업데이트 + gameLogic.UpdateForbiddenMoves(); + #endregion + + // gameLogic.currentTurn = _playerType; + // gameLogic.stoneController.OnStoneClickedDelegate = (row, col) => + // { + // HandleMove(gameLogic, row, col); + // }; + _multiplayManager.OnOpponentMove = moveData => + { + var row = moveData.position.x; + var col = moveData.position.y; + UnityThread.executeInUpdate(() => + { + HandleMove(gameLogic, row, col); + }); + }; } public override void OnExit(GameLogic gameLogic) { gameLogic.fioTimer.InitTimer(); + _multiplayManager.OnOpponentMove = null; } public override void HandleMove(GameLogic gameLogic, int row, int col) { - + ProcessMove(gameLogic, _playerType, row, col); } public override void HandleNextTurn(GameLogic gameLogic) { - + if (_isFirstPlayer) + { + gameLogic.SetState(gameLogic.secondPlayerState); + } + else + { + gameLogic.SetState(gameLogic.firstPlayerState); + } } } @@ -166,6 +234,7 @@ public class GameLogic : MonoBehaviour public BasePlayerState firstPlayerState; public BasePlayerState secondPlayerState; private BasePlayerState _currentPlayerState; + //타이머 public FioTimer fioTimer; @@ -176,6 +245,9 @@ public class GameLogic : MonoBehaviour private int _lastRow; private int _lastCol; + private MultiplayManager _multiplayManager; + private string _roomId; + #region Renju Members // 렌주룰 금수 검사기 private RenjuForbiddenMoveDetector _forbiddenDetector; @@ -242,14 +314,148 @@ public class GameLogic : MonoBehaviour SetState(firstPlayerState); break; case Enums.GameType.MultiPlay: - //TODO: 멀티 구현 필요 - ReplayManager.Instance.InitReplayData("PlayerA","nicknameB"); + // 메인 스레드에서 실행 - UI 업데이트는 메인 스레드에서 실행 필요 + UnityMainThreadDispatcher.Instance().Enqueue(() => + { + GameManager.Instance.panelManager.OpenLoadingPanel(true, true); + }); + _multiplayManager = new MultiplayManager((state, data) => + { + switch (state) + { + case Constants.MultiplayManagerState.CreateRoom: + Debug.Log("## Create Room"); + _roomId = data as string; + break; + case Constants.MultiplayManagerState.JoinRoom: + Debug.Log("## Join Room"); + var joinRoomData = data as JoinRoomData; + if (joinRoomData == null) + { + Debug.Log("Join Room 응답값이 null 입니다"); + return; + } + + // TODO: 선공, 후공 처리 + if (joinRoomData.isBlack) + { + } + + firstPlayerState = new MultiPlayerState(true, _multiplayManager); + secondPlayerState = new PlayerState(false, _multiplayManager, joinRoomData); + + // 메인 스레드에서 실행 - UI 업데이트는 메인 스레드에서 실행 필요 + UnityMainThreadDispatcher.Instance().Enqueue(() => + { + GameManager.Instance.InitPlayersName(UserManager.Instance.Nickname, joinRoomData.opponentNickname); + GameManager.Instance.InitProfileImages(UserManager.Instance.imageIndex, joinRoomData.opponentImageIndex); + + // 리플레이 데이터 업데이트 + ReplayManager.Instance.InitReplayData(UserManager.Instance.Nickname, joinRoomData.opponentNickname, UserManager.Instance.imageIndex, joinRoomData.opponentImageIndex); + + // 로딩 패널 열려있으면 닫기 + GameManager.Instance.panelManager.CloseLoadingPanel(); + + // 게임 시작 + SetState(firstPlayerState); + }); + break; + case Constants.MultiplayManagerState.SwitchAI: + Debug.Log("## Switching to AI Mode"); + SwitchToSinglePlayer(); + break; + case Constants.MultiplayManagerState.StartGame: + Debug.Log("## Start Game"); + var startGameData = data as StartGameData; + if (startGameData == null) + { + Debug.Log("Start Game 응답값이 null 입니다"); + return; + } + + // TODO: 선공, 후공 처리 + if (startGameData.isBlack) + { + } + firstPlayerState = new PlayerState(true, _multiplayManager, _roomId); + secondPlayerState = new MultiPlayerState(false, _multiplayManager); + + // 메인 스레드에서 실행 - UI 업데이트는 메인 스레드에서 실행 필요 + UnityMainThreadDispatcher.Instance().Enqueue(() => + { + GameManager.Instance.InitPlayersName(UserManager.Instance.Nickname, startGameData.opponentNickname); + GameManager.Instance.InitProfileImages(UserManager.Instance.imageIndex, startGameData.opponentImageIndex); + + // 리플레이 데이터 업데이트 + ReplayManager.Instance.InitReplayData(UserManager.Instance.Nickname, startGameData.opponentNickname, UserManager.Instance.imageIndex, startGameData.opponentImageIndex); + + // 로딩 패널 열려있으면 닫기 + GameManager.Instance.panelManager.CloseLoadingPanel(); + + // 게임 시작 + SetState(firstPlayerState); + }); + break; + case Constants.MultiplayManagerState.ExitRoom: + Debug.Log("## Exit Room"); + // TODO: Exit Room 처리 + break; + case Constants.MultiplayManagerState.EndGame: + Debug.Log("## End Game"); + // TODO: End Room 처리 + break; + } + ReplayManager.Instance.InitReplayData(UserManager.Instance.Nickname,"nicknameB"); + + }); + _multiplayManager.RegisterPlayer(UserManager.Instance.Nickname, UserManager.Instance.Rating, UserManager.Instance.imageIndex); break; case Enums.GameType.Replay: //TODO: 리플레이 구현 break; } } + + public void SwitchToSinglePlayer() + { + _multiplayManager?.Dispose(); + + // 기존 멀티플레이 상태 초기화 + _multiplayManager = null; + _roomId = null; + + // 싱글 플레이 상태로 변경 + firstPlayerState = new PlayerState(true); + secondPlayerState = new AIState(); + // AI 난이도 설정(급수 설정) + OmokAI.Instance.SetRating(UserManager.Instance.Rating); + + // 메인 스레드에서 실행 - UI 업데이트는 메인 스레드에서 실행 필요 + UnityMainThreadDispatcher.Instance().Enqueue(() => + { + // 스레드 확인 로그: 추후 디버깅 시 필요할 수 있을 것 같아 남겨둡니다 + // Debug.Log($"[UnityMainThreadDispatcher] 실행 스레드: {System.Threading.Thread.CurrentThread.ManagedThreadId}"); + // UI 업데이트 + GameManager.Instance.InitPlayersName(UserManager.Instance.Nickname, "AIPlayer"); + GameManager.Instance.InitProfileImages(UserManager.Instance.imageIndex, 1); + + // 리플레이 데이터 업데이트 + ReplayManager.Instance.InitReplayData(UserManager.Instance.Nickname, "PlayerAI", UserManager.Instance.imageIndex, 1); + + // 로딩 패널 열려있으면 닫기 + GameManager.Instance.panelManager.CloseLoadingPanel(); + + // 첫 번째 플레이어(유저)부터 시작 + SetState(firstPlayerState); + }); + } + + public void Dispose() + { + _multiplayManager?.LeaveRoom(_roomId); + _multiplayManager?.Dispose(); + } + //돌 카운터 증가 함수 public void CountStoneCounter() { diff --git a/Assets/Script/Game/GameManager.cs b/Assets/Script/Game/GameManager.cs index e37aca3..677308b 100644 --- a/Assets/Script/Game/GameManager.cs +++ b/Assets/Script/Game/GameManager.cs @@ -54,6 +54,7 @@ public class GameManager : Singleton } } + // 멀티 플레이를 위한 코드 public void ChangeToGameScene(Enums.GameType gameType) { _gameType = gameType; @@ -63,6 +64,8 @@ public class GameManager : Singleton public void ChangeToMainScene() { _gameType = Enums.GameType.None; + // TODO: 추후 혹시 모를 존재하는 socket 통신 종료 필요 - _gameLogic?.Dispose에서 LeaveRoom 호출하긴 하는데 서버에서 이미 해당 방을 삭제했을 경우 동작 확인 필요 + // _gameLogic?.Dispose(); SceneManager.LoadScene("Main"); } diff --git a/Assets/Script/Game/MultiplayManager.cs b/Assets/Script/Game/MultiplayManager.cs new file mode 100644 index 0000000..4c81e88 --- /dev/null +++ b/Assets/Script/Game/MultiplayManager.cs @@ -0,0 +1,195 @@ +using System; +using System.Threading.Tasks; +using Newtonsoft.Json; +using SocketIOClient; +using UnityEngine; + +public class CreateRoomData +{ + [JsonProperty("roomId")] + public string roomId { get; set; } +} + +public class JoinRoomData +{ + [JsonProperty("roomId")] + public string roomId { get; set; } + [JsonProperty("opponentRating")] + public int opponentRating { get; set; } + [JsonProperty("opponentNickname")] + public string opponentNickname { get; set; } + [JsonProperty("opponentImageIndex")] + public int opponentImageIndex { get; set; } + [JsonProperty("isBlack")] + public Boolean isBlack { get; set; } +} + +public class StartGameData +{ + [JsonProperty("opponentId")] + public string opponentId { get; set; } + [JsonProperty("opponentRating")] + public int opponentRating { get; set; } + [JsonProperty("opponentNickname")] + public string opponentNickname { get; set; } + [JsonProperty("opponentImageIndex")] + public int opponentImageIndex { get; set; } + [JsonProperty("isBlack")] + public Boolean isBlack { get; set; } +} + + +public class PositionData +{ + [JsonProperty("x")] + public int x { get; set; } + + [JsonProperty("y")] + public int y { get; set; } +} + +public class MoveData +{ + [JsonProperty("position")] + public PositionData position { get; set; } +} + + +public class MessageData +{ + [JsonProperty("message")] + public string message { get; set; } +} + +public class MultiplayManager : IDisposable +{ + private SocketIOUnity _socket; + private event Action _onMultiplayStateChanged; + public Action OnOpponentMove; + + public MultiplayManager(Action onMultiplayStateChanged) + { + _onMultiplayStateChanged = onMultiplayStateChanged; + try + { + var serverUrl = new Uri(Constants.GameServerURL); + + _socket = new SocketIOUnity(serverUrl, new SocketIOOptions + { + Transport = SocketIOClient.Transport.TransportProtocol.WebSocket + }); + + _socket.On("createRoom", CreateRoom); + _socket.On("joinRoom", JoinRoom); + _socket.On("startGame", StartGame); + _socket.On("switchAI", SwitchAI); + _socket.On("exitRoom", ExitRoom); + _socket.On("endGame", EndGame); + _socket.On("doOpponent", DoOpponent); + + _socket.Connect(); + } + catch (Exception e) + { + Debug.LogError("MultiplayManager 생성 중 오류 발생: " + e.Message); + } + } + + public async void RegisterPlayer(string nickname, int rating, int imageIndex) + { + // 연결될 때까지 대기 + while (!_socket.Connected) + { + Debug.Log("소켓 연결 대기 중..."); + await Task.Delay(100); // 0.1초 대기 후 다시 확인 + } + _socket.Emit("registerPlayer", new { nickname, rating, imageIndex }); + } + + private void CreateRoom(SocketIOResponse response) + { + var data = response.GetValue(); + _onMultiplayStateChanged?.Invoke(Constants.MultiplayManagerState.CreateRoom, data.roomId); + } + + private void JoinRoom(SocketIOResponse response) + { + var data = response.GetValue(); + Debug.Log($"룸에 참여: 룸 ID - {data.roomId}, 상대방 등급 - {data.opponentRating}, 상대방 이름 - {data.opponentNickname}, 흑/백 여부 - {data.isBlack}, 상대방 이미지 인덱스 - {data.opponentImageIndex}"); + + // 필요한 데이터 사용 + _onMultiplayStateChanged?.Invoke(Constants.MultiplayManagerState.JoinRoom, data); + } + + private void SwitchAI(SocketIOResponse response) + { + var data = response.GetValue(); + Debug.Log("switchAI: " + data.message); + _onMultiplayStateChanged?.Invoke(Constants.MultiplayManagerState.SwitchAI, data.message); + } + + private void StartGame(SocketIOResponse response) + { + var data = response.GetValue(); + Debug.Log($"게임 시작: 상대방 ID - {data.opponentId}, 상대방 등급 - {data.opponentRating}, 상대방 이름 - {data.opponentNickname}, 흑/백 여부 - {data.isBlack}, 상대방 이미지 인덱스 - {data.opponentImageIndex}"); + + // 필요한 데이터 사용 + _onMultiplayStateChanged?.Invoke(Constants.MultiplayManagerState.StartGame, data); + } + + + // 서버로 부터 상대방의 마커 정보를 받기 위한 메서드 + private void DoOpponent(SocketIOResponse response) + { + var data = response.GetValue(); + + if (data != null && data.position != null) + { + Vector2Int opponentPosition = new Vector2Int(data.position.x, data.position.y); + Debug.Log($"상대방의 위치: {opponentPosition}"); + OnOpponentMove?.Invoke(new MoveData { position = data.position }); + } + else + { + Debug.LogError("DoOpponent: 데이터가 올바르지 않습니다."); + } + } + + + // 플레이어의 마커 위치를 서버로 전달하기 위한 메서드 + public void SendPlayerMove(string roomId, Vector2Int position) + { + Debug.Log($"내 위치: {position}"); + _socket.Emit("doPlayer", new + { + roomId, + position = new { x = position.x, y = position.y } + }); + } + + + private void ExitRoom(SocketIOResponse response) + { + _onMultiplayStateChanged?.Invoke(Constants.MultiplayManagerState.ExitRoom, null); + } + + private void EndGame(SocketIOResponse response) + { + _onMultiplayStateChanged?.Invoke(Constants.MultiplayManagerState.EndGame, null); + } + + public void LeaveRoom(string roomId) + { + _socket.Emit("leaveRoom", new { roomId }); + } + + public void Dispose() + { + if (_socket != null) + { + _socket.Disconnect(); + _socket.Dispose(); + _socket = null; + } + } +} diff --git a/Assets/Script/Game/MultiplayManager.cs.meta b/Assets/Script/Game/MultiplayManager.cs.meta new file mode 100644 index 0000000..b167786 --- /dev/null +++ b/Assets/Script/Game/MultiplayManager.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 18c2446fa3d545d289abeca18341ef81 +timeCreated: 1742865818 \ No newline at end of file diff --git a/Assets/Script/Main/LoadingPanelController.cs b/Assets/Script/Main/LoadingPanelController.cs index 1388551..48def94 100644 --- a/Assets/Script/Main/LoadingPanelController.cs +++ b/Assets/Script/Main/LoadingPanelController.cs @@ -120,6 +120,7 @@ public class LoadingPanelController : MonoBehaviour { cancellationTokenSource.Cancel(); } - gameObject.SetActive(false); + + if (gameObject.activeSelf) gameObject.SetActive(false); } } diff --git a/Assets/Script/Main/MainPanelController.cs b/Assets/Script/Main/MainPanelController.cs index c352ce7..f4b22ad 100644 --- a/Assets/Script/Main/MainPanelController.cs +++ b/Assets/Script/Main/MainPanelController.cs @@ -85,8 +85,7 @@ public class MainPanelController : MonoBehaviour //코인 차감 후 게임 씬 로드 GameManager.Instance.panelManager.RemoveCoinsPanelUI((() => { - GameManager.Instance.ChangeToGameScene(Enums.GameType.SinglePlay); - //Todo: 게임 타입에 따라 다른 Scene 호출 + GameManager.Instance.ChangeToGameScene(Enums.GameType.MultiPlay); })); } diff --git a/Assets/Script/UI/PanelController/PanelManager.cs b/Assets/Script/UI/PanelController/PanelManager.cs index cdda752..f66194f 100644 --- a/Assets/Script/UI/PanelController/PanelManager.cs +++ b/Assets/Script/UI/PanelController/PanelManager.cs @@ -14,6 +14,7 @@ public class PanelManager : MonoBehaviour private Canvas _canvas; private CoinsPanelController _coinsPanel; private LoadingPanelController loadingPanelController; + private GameObject loadingPanelObject; private Dictionary panelPrefabs = new Dictionary(); @@ -71,11 +72,17 @@ public class PanelManager : MonoBehaviour public void OpenLoadingPanel(bool rotateImage = false, bool animatedText = false, bool flipImage = false) { + SetCanvas(); if (_canvas != null) { - var loadingPanelObject = GetPanel("Loading Panel"); + if (loadingPanelObject != null && loadingPanelObject.activeSelf) + { + // 기존 로딩 패널이 활성화되어 있으면 먼저 닫기 + CloseLoadingPanel(); + } + + loadingPanelObject = GetPanel("Loading Panel"); - // 로딩 화면이 생성된 후, 원하는 애니메이션 활성화 loadingPanelController = loadingPanelObject.GetComponent(); if (loadingPanelController != null) @@ -84,6 +91,18 @@ public class PanelManager : MonoBehaviour } } } + + public void CloseLoadingPanel() + { + if (loadingPanelObject != null && loadingPanelObject.activeSelf && loadingPanelController != null) + { + loadingPanelController.StopLoading(); + } + else + { + Debug.Log("로딩 패널이 이미 닫혔거나 비활성화 상태입니다."); + } + } public void OpenSigninPanel() { diff --git a/Packages/manifest.json b/Packages/manifest.json index 03e5328..93555b6 100644 --- a/Packages/manifest.json +++ b/Packages/manifest.json @@ -1,5 +1,6 @@ { "dependencies": { + "com.itisnajim.socketiounity": "https://github.com/itisnajim/SocketIOUnity.git", "com.unity.collab-proxy": "2.7.1", "com.unity.feature.2d": "2.0.1", "com.unity.ide.rider": "3.0.34", @@ -10,6 +11,7 @@ "com.unity.timeline": "1.7.6", "com.unity.ugui": "1.0.0", "com.unity.visualscripting": "1.9.4", + "com.veriorpies.parrelsync": "https://github.com/VeriorPies/ParrelSync.git?path=/ParrelSync", "com.unity.modules.ai": "1.0.0", "com.unity.modules.androidjni": "1.0.0", "com.unity.modules.animation": "1.0.0", diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json index dd254ab..dd05142 100644 --- a/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -1,5 +1,14 @@ { "dependencies": { + "com.itisnajim.socketiounity": { + "version": "https://github.com/itisnajim/SocketIOUnity.git", + "depth": 0, + "source": "git", + "dependencies": { + "com.unity.nuget.newtonsoft-json": "3.0.2" + }, + "hash": "c9e06b15391449ad42fd9b0f39fea48054751bcd" + }, "com.unity.2d.animation": { "version": "9.1.3", "depth": 1, @@ -168,6 +177,13 @@ "dependencies": {}, "url": "https://packages.unity.com" }, + "com.unity.nuget.newtonsoft-json": { + "version": "3.2.1", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, "com.unity.recorder": { "version": "4.0.3", "depth": 0, @@ -228,6 +244,13 @@ }, "url": "https://packages.unity.com" }, + "com.veriorpies.parrelsync": { + "version": "https://github.com/VeriorPies/ParrelSync.git?path=/ParrelSync", + "depth": 0, + "source": "git", + "dependencies": {}, + "hash": "610157ad762084380380148ba8ce14e266a6da97" + }, "com.unity.modules.ai": { "version": "1.0.0", "depth": 0, From 189d07aa93f9aa6e95bdddf7d36810bf45338ec0 Mon Sep 17 00:00:00 2001 From: Lim0_C Date: Wed, 26 Mar 2025 09:36:44 +0900 Subject: [PATCH 4/9] =?UTF-8?q?DO-46=20[Fix]=20=EC=9D=B4=ED=8E=99=ED=8A=B8?= =?UTF-8?q?=20=ED=8C=A8=EB=84=90=20=EB=B0=B0=EA=B2=BD=20=EC=96=B4=EB=91=A1?= =?UTF-8?q?=EA=B2=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Resources/Prefabs/Effects/Draw Effect Panel.prefab | 5 ++--- Assets/Resources/Prefabs/Effects/Lose Effect Panel.prefab | 5 ++--- Assets/Resources/Prefabs/Effects/Win Effect Panel.prefab | 8 ++++---- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/Assets/Resources/Prefabs/Effects/Draw Effect Panel.prefab b/Assets/Resources/Prefabs/Effects/Draw Effect Panel.prefab index 19e269f..c7dccab 100644 --- a/Assets/Resources/Prefabs/Effects/Draw Effect Panel.prefab +++ b/Assets/Resources/Prefabs/Effects/Draw Effect Panel.prefab @@ -575,7 +575,7 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} - m_Color: {r: 1, g: 1, b: 1, a: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0.5882353} m_RaycastTarget: 1 m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} m_Maskable: 1 @@ -618,10 +618,9 @@ MonoBehaviour: m_EditorClassIdentifier: bannerObj: {fileID: 7291411618834705046} bannerText: {fileID: 5108301403921453943} + interval: 0.1 dragonOpenEyes: {fileID: 3881260292094838299} dragonCloseEyes: {fileID: 6707373289257723271} tigerOpenEyes: {fileID: 1508819185250841244} tigerCloseEyes: {fileID: 399504369641388738} - fullText: "\uBB34\uC2B9\uBD80 \uC785\uB2C8\uB2E4" - interval: 0.1 flipDuration: 0.3 diff --git a/Assets/Resources/Prefabs/Effects/Lose Effect Panel.prefab b/Assets/Resources/Prefabs/Effects/Lose Effect Panel.prefab index 7d9ce05..82b84f5 100644 --- a/Assets/Resources/Prefabs/Effects/Lose Effect Panel.prefab +++ b/Assets/Resources/Prefabs/Effects/Lose Effect Panel.prefab @@ -499,7 +499,7 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} - m_Color: {r: 1, g: 1, b: 1, a: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0.5882353} m_RaycastTarget: 1 m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} m_Maskable: 1 @@ -542,8 +542,7 @@ MonoBehaviour: m_EditorClassIdentifier: bannerObj: {fileID: 7291411618834705046} bannerText: {fileID: 5108301403921453943} + interval: 0.1 characterOpenEyes: {fileID: 1632927645355555415} characterCloseEyes: {fileID: 3881260292094838299} depressedEffect: {fileID: 155037671892554820} - fullText: "\uD328\uBC30\uD588\uC2B5\uB2C8\uB2E4" - interval: 0.1 diff --git a/Assets/Resources/Prefabs/Effects/Win Effect Panel.prefab b/Assets/Resources/Prefabs/Effects/Win Effect Panel.prefab index f0cc93a..c8e9768 100644 --- a/Assets/Resources/Prefabs/Effects/Win Effect Panel.prefab +++ b/Assets/Resources/Prefabs/Effects/Win Effect Panel.prefab @@ -837,7 +837,7 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} - m_Color: {r: 1, g: 1, b: 1, a: 0} + m_Color: {r: 0, g: 0, b: 0, a: 0.5882353} m_RaycastTarget: 1 m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} m_Maskable: 1 @@ -866,6 +866,9 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 9b132148519758c42824252ec9a2d3a4, type: 3} m_Name: m_EditorClassIdentifier: + bannerObj: {fileID: 0} + bannerText: {fileID: 5108301403921453943} + interval: 0.1 haloEffectImg: {fileID: 376994097320605198} characterImg: {fileID: 3881260292094838299} shineEffectImg: @@ -875,9 +878,6 @@ MonoBehaviour: circleEffectImg: - {fileID: 1661063685800461951} - {fileID: 5812078995592861983} - bannerText: {fileID: 5108301403921453943} - fullText: "\uC2B9\uB9AC\uD588\uC2B5\uB2C8\uB2E4!" - interval: 0.1 --- !u!225 &4545556044007292713 CanvasGroup: m_ObjectHideFlags: 0 From 95eebb41c474fa77b1473373a02e3f9dda6a4155 Mon Sep 17 00:00:00 2001 From: Jay <96156114+jaydev00a@users.noreply.github.com> Date: Wed, 26 Mar 2025 10:21:19 +0900 Subject: [PATCH 5/9] =?UTF-8?q?DO-58=20[Fix]=20=EC=98=A4=EB=94=94=EC=98=A4?= =?UTF-8?q?=EB=A7=A4=EB=8B=88=EC=A0=80=20=EB=B3=91=ED=95=A9=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20=EC=A3=BC=EC=84=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Script/Common/AudioManager.cs | 8 ++++---- Assets/Script/Game/GameLogic.cs | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Assets/Script/Common/AudioManager.cs b/Assets/Script/Common/AudioManager.cs index 899fc5d..fcb7ff3 100644 --- a/Assets/Script/Common/AudioManager.cs +++ b/Assets/Script/Common/AudioManager.cs @@ -76,18 +76,18 @@ public class AudioManager : Singleton // 클릭 사운드(SFX) 재생 public void PlayClickSound() { - if (audioSource != null) + if (sfxAudioSource != null) { - audioSource.PlayOneShot(clickSound, sfxVolume); + sfxAudioSource.PlayOneShot(clickSound, sfxVolume); } } // 닫기 사운드(SFX) 재생 public void PlayCloseSound() { - if (audioSource != null) + if (sfxAudioSource != null) { - audioSource.PlayOneShot(closeSound, sfxVolume); + sfxAudioSource.PlayOneShot(closeSound, sfxVolume); } } diff --git a/Assets/Script/Game/GameLogic.cs b/Assets/Script/Game/GameLogic.cs index 2d59f55..1c70c95 100644 --- a/Assets/Script/Game/GameLogic.cs +++ b/Assets/Script/Game/GameLogic.cs @@ -299,6 +299,7 @@ public class GameLogic : MonoBehaviour switch (gameType) { + // TODO: 현재 싱글 플레이로 바로 넘어가지 않기 때문에 미사용 중 case Enums.GameType.SinglePlay: firstPlayerState = new PlayerState(true); secondPlayerState = new AIState(); @@ -330,6 +331,8 @@ public class GameLogic : MonoBehaviour case Constants.MultiplayManagerState.JoinRoom: Debug.Log("## Join Room"); var joinRoomData = data as JoinRoomData; + + // TODO: 응답값 없을 때 서버에서 다시 받아오기 or AI 플레이로 넘기는 처리 필요 if (joinRoomData == null) { Debug.Log("Join Room 응답값이 null 입니다"); @@ -367,6 +370,8 @@ public class GameLogic : MonoBehaviour case Constants.MultiplayManagerState.StartGame: Debug.Log("## Start Game"); var startGameData = data as StartGameData; + + // TODO: 응답값 없을 때 서버에서 다시 받아오기 or AI 플레이로 넘기는 처리 필요 if (startGameData == null) { Debug.Log("Start Game 응답값이 null 입니다"); From 3e216d3242737b134003686a6874d8b040b7203a Mon Sep 17 00:00:00 2001 From: Lim0_C Date: Wed, 26 Mar 2025 10:44:07 +0900 Subject: [PATCH 6/9] =?UTF-8?q?DO-46=20[Fix]=20=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?=EB=B3=91=ED=95=A9=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Script/Game/GameLogic.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Assets/Script/Game/GameLogic.cs b/Assets/Script/Game/GameLogic.cs index db30d8d..9624153 100644 --- a/Assets/Script/Game/GameLogic.cs +++ b/Assets/Script/Game/GameLogic.cs @@ -452,12 +452,15 @@ public class GameLogic : MonoBehaviour { // 스레드 확인 로그: 추후 디버깅 시 필요할 수 있을 것 같아 남겨둡니다 // Debug.Log($"[UnityMainThreadDispatcher] 실행 스레드: {System.Threading.Thread.CurrentThread.ManagedThreadId}"); - // UI 업데이트 - GameManager.Instance.InitPlayersName(UserManager.Instance.Nickname, "AIPlayer"); - GameManager.Instance.InitProfileImages(UserManager.Instance.imageIndex, 1); - - // 리플레이 데이터 업데이트 - ReplayManager.Instance.InitReplayData(UserManager.Instance.Nickname, "PlayerAI", UserManager.Instance.imageIndex, 1); + //AI닉네임 랜덤생성 + var aiName = RandomAINickname(); + var imageIndex = UnityEngine.Random.Range(0, 2); + + //유저 이름 사진 초기화 + GameManager.Instance.InitPlayersName(UserManager.Instance.Nickname, aiName); + GameManager.Instance.InitProfileImages(UserManager.Instance.imageIndex, imageIndex); + // 리플레이 데이터 업데이트 + ReplayManager.Instance.InitReplayData(UserManager.Instance.Nickname,aiName, UserManager.Instance.imageIndex, imageIndex); // 로딩 패널 열려있으면 닫기 GameManager.Instance.panelManager.CloseLoadingPanel(); @@ -576,7 +579,6 @@ public class GameLogic : MonoBehaviour SetState(null); ReplayManager.Instance.SaveReplayDataResult(result); //TODO: 게임 종료 후 행동 구현 - SceneManager.LoadScene("Main"); } //승리 확인 함수 From 30ed02abee872a344e76793608c5dabf2ce61ba3 Mon Sep 17 00:00:00 2001 From: Lim0_C Date: Wed, 26 Mar 2025 10:45:11 +0900 Subject: [PATCH 7/9] =?UTF-8?q?DO-46=20[fix]=20=ED=98=84=EC=9E=AC=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=20=EC=95=88=EB=90=98=EB=8A=94=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A3=BC=EC=84=9D=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Script/Game/GameLogic.cs | 36 ++++++++++++++++----------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Assets/Script/Game/GameLogic.cs b/Assets/Script/Game/GameLogic.cs index 9624153..971fcd7 100644 --- a/Assets/Script/Game/GameLogic.cs +++ b/Assets/Script/Game/GameLogic.cs @@ -297,24 +297,24 @@ public class GameLogic : MonoBehaviour switch (gameType) { // TODO: 현재 싱글 플레이로 바로 넘어가지 않기 때문에 미사용 중 - case Enums.GameType.SinglePlay: - firstPlayerState = new PlayerState(true); - secondPlayerState = new AIState(); - // AI 난이도 설정(급수 설정) - OmokAI.Instance.SetRating(UserManager.Instance.Rating); - - //AI닉네임 랜덤생성 - var aiName = RandomAINickname(); - var imageIndex = UnityEngine.Random.Range(0, 2); - - //유저 이름 사진 초기화 - GameManager.Instance.InitPlayersName(UserManager.Instance.Nickname, aiName); - GameManager.Instance.InitProfileImages(UserManager.Instance.imageIndex, imageIndex); - - ReplayManager.Instance.InitReplayData(UserManager.Instance.Nickname,aiName, UserManager.Instance.imageIndex, imageIndex); - - SetState(firstPlayerState); - break; + // case Enums.GameType.SinglePlay: + // firstPlayerState = new PlayerState(true); + // secondPlayerState = new AIState(); + // // AI 난이도 설정(급수 설정) + // OmokAI.Instance.SetRating(UserManager.Instance.Rating); + // + // //AI닉네임 랜덤생성 + // var aiName = RandomAINickname(); + // var imageIndex = UnityEngine.Random.Range(0, 2); + // + // //유저 이름 사진 초기화 + // GameManager.Instance.InitPlayersName(UserManager.Instance.Nickname, aiName); + // GameManager.Instance.InitProfileImages(UserManager.Instance.imageIndex, imageIndex); + // + // ReplayManager.Instance.InitReplayData(UserManager.Instance.Nickname,aiName, UserManager.Instance.imageIndex, imageIndex); + // + // SetState(firstPlayerState); + // break; case Enums.GameType.MultiPlay: // 메인 스레드에서 실행 - UI 업데이트는 메인 스레드에서 실행 필요 UnityMainThreadDispatcher.Instance().Enqueue(() => From 1f10415f9cc45d5e6cc2bf7e658693c4be6f8497 Mon Sep 17 00:00:00 2001 From: Lim0_C Date: Wed, 26 Mar 2025 10:52:51 +0900 Subject: [PATCH 8/9] =?UTF-8?q?DO-46=20[fix]=20AI=EB=8B=89=EB=84=A4?= =?UTF-8?q?=EC=9E=84=20=EB=9E=9C=EB=8D=A4=20=EC=83=9D=EC=84=B1=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=9C=84=EC=B9=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Script/Game/GameLogic.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Assets/Script/Game/GameLogic.cs b/Assets/Script/Game/GameLogic.cs index 971fcd7..8303a77 100644 --- a/Assets/Script/Game/GameLogic.cs +++ b/Assets/Script/Game/GameLogic.cs @@ -446,16 +446,17 @@ public class GameLogic : MonoBehaviour secondPlayerState = new AIState(); // AI 난이도 설정(급수 설정) OmokAI.Instance.SetRating(UserManager.Instance.Rating); + + //AI닉네임 랜덤생성 + var aiName = RandomAINickname(); + var imageIndex = UnityEngine.Random.Range(0, 2); // 메인 스레드에서 실행 - UI 업데이트는 메인 스레드에서 실행 필요 UnityMainThreadDispatcher.Instance().Enqueue(() => { // 스레드 확인 로그: 추후 디버깅 시 필요할 수 있을 것 같아 남겨둡니다 // Debug.Log($"[UnityMainThreadDispatcher] 실행 스레드: {System.Threading.Thread.CurrentThread.ManagedThreadId}"); - //AI닉네임 랜덤생성 - var aiName = RandomAINickname(); - var imageIndex = UnityEngine.Random.Range(0, 2); - + //유저 이름 사진 초기화 GameManager.Instance.InitPlayersName(UserManager.Instance.Nickname, aiName); GameManager.Instance.InitProfileImages(UserManager.Instance.imageIndex, imageIndex); From 4dca270bc8fd711f68633257301f49f3a592c508 Mon Sep 17 00:00:00 2001 From: Lim0_C Date: Wed, 26 Mar 2025 11:15:56 +0900 Subject: [PATCH 9/9] =?UTF-8?q?[HOTFIX]=20SwitchToSinglePlayer=20=EB=B2=84?= =?UTF-8?q?=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Script/Game/GameLogic.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Assets/Script/Game/GameLogic.cs b/Assets/Script/Game/GameLogic.cs index 8303a77..4a710d8 100644 --- a/Assets/Script/Game/GameLogic.cs +++ b/Assets/Script/Game/GameLogic.cs @@ -446,10 +446,6 @@ public class GameLogic : MonoBehaviour secondPlayerState = new AIState(); // AI 난이도 설정(급수 설정) OmokAI.Instance.SetRating(UserManager.Instance.Rating); - - //AI닉네임 랜덤생성 - var aiName = RandomAINickname(); - var imageIndex = UnityEngine.Random.Range(0, 2); // 메인 스레드에서 실행 - UI 업데이트는 메인 스레드에서 실행 필요 UnityMainThreadDispatcher.Instance().Enqueue(() => @@ -457,6 +453,10 @@ public class GameLogic : MonoBehaviour // 스레드 확인 로그: 추후 디버깅 시 필요할 수 있을 것 같아 남겨둡니다 // Debug.Log($"[UnityMainThreadDispatcher] 실행 스레드: {System.Threading.Thread.CurrentThread.ManagedThreadId}"); + //AI닉네임 랜덤생성 + var aiName = RandomAINickname(); + var imageIndex = UnityEngine.Random.Range(0, 2); + //유저 이름 사진 초기화 GameManager.Instance.InitPlayersName(UserManager.Instance.Nickname, aiName); GameManager.Instance.InitProfileImages(UserManager.Instance.imageIndex, imageIndex);