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/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/Game bgm.mp3 b/Assets/Resources/Game bgm.mp3 new file mode 100644 index 0000000..5789b5f Binary files /dev/null and b/Assets/Resources/Game bgm.mp3 differ diff --git a/Assets/Resources/Game bgm.mp3.meta b/Assets/Resources/Game bgm.mp3.meta new file mode 100644 index 0000000..114346b --- /dev/null +++ b/Assets/Resources/Game bgm.mp3.meta @@ -0,0 +1,23 @@ +fileFormatVersion: 2 +guid: 6d4eda23943dd0b4099b86b28fa0840c +AudioImporter: + externalObjects: {} + serializedVersion: 7 + defaultSettings: + serializedVersion: 2 + loadType: 0 + sampleRateSetting: 0 + sampleRateOverride: 44100 + compressionFormat: 1 + quality: 1 + conversionMode: 0 + preloadAudioData: 0 + platformSettingOverrides: {} + forceToMono: 0 + normalize: 1 + loadInBackground: 0 + ambisonic: 0 + 3D: 1 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/Prefabs/Audio Manager.prefab b/Assets/Resources/Prefabs/Audio Manager.prefab index f8be64d..1fa43b9 100644 --- a/Assets/Resources/Prefabs/Audio Manager.prefab +++ b/Assets/Resources/Prefabs/Audio Manager.prefab @@ -46,6 +46,7 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: mainBgm: {fileID: 8300000, guid: 1d9c7fb20aa822c48933d00b6bd6a757, type: 3} + gameBgm: {fileID: 8300000, guid: 6d4eda23943dd0b4099b86b28fa0840c, type: 3} clickSound: {fileID: 8300000, guid: cff2e6cf7f46a074d86955b3b6fd499a, type: 3} closeSound: {fileID: 8300000, guid: e7c0f32158a3e5b46bc3b59035aba898, type: 3} sfxVolume: 0 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 diff --git a/Assets/Resources/Prefabs/Panels/Main Panel.prefab b/Assets/Resources/Prefabs/Panels/Main Panel.prefab index 1ae27ad..ca0fdef 100644 --- a/Assets/Resources/Prefabs/Panels/Main Panel.prefab +++ b/Assets/Resources/Prefabs/Panels/Main Panel.prefab @@ -1104,7 +1104,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: "5\uAE09" + m_text: "18\uAE09" m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: 85a19688db53c77469fc4406b01045da, type: 2} m_sharedMaterial: {fileID: -2477908578676791210, guid: 85a19688db53c77469fc4406b01045da, type: 2} @@ -1713,7 +1713,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: "\uD14C\uC2A4\uD2B8\uC0BC\uBC88" + 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} 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..0f450eb 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 @@ -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..fcb7ff3 100644 --- a/Assets/Script/Common/AudioManager.cs +++ b/Assets/Script/Common/AudioManager.cs @@ -1,56 +1,116 @@ using UnityEngine; +using UnityEngine.SceneManagement; -[RequireComponent(typeof(AudioSource))] -public class AudioManager : MonoBehaviour +public class AudioManager : Singleton { [Header("BGM")] [SerializeField] private AudioClip mainBgm; + [SerializeField] private AudioClip gameBgm; [Header("SFX")] [SerializeField] private AudioClip clickSound; [SerializeField] private AudioClip closeSound; - private AudioSource audioSource; + private AudioSource bgmAudioSource; // BGM을 위한 AudioSource + private AudioSource sfxAudioSource; // SFX를 위한 AudioSource - [HideInInspector] public float sfxVolume; + [HideInInspector] public float sfxVolume = 1.0f; // SFX 볼륨 (기본값 1) + private void Awake() + { + base.Awake(); // 부모 클래스의 Awake 호출 + + // BGM과 SFX를 위한 별도의 AudioSource 생성 + bgmAudioSource = gameObject.AddComponent(); + sfxAudioSource = gameObject.AddComponent(); + } + + // 시작 시 BGM을 자동으로 재생 private void Start() { - PlayMainBGM(); - sfxVolume = 1.0f; //테스트 코드 + if (UserManager.IsPlayBGM) // UserManager의 BGM 설정값에 따라 BGM을 재생 + { + PlayMainBGM(); // 기본 BGM을 재생 + } } - - // 배경음악 시작 + + // 메인 BGM을 재생하는 함수 public void PlayMainBGM() { - // AudioSource 컴포넌트 가져오기 - audioSource = GetComponent(); - - if (audioSource != null && mainBgm != null) + if (bgmAudioSource != null && mainBgm != null && !bgmAudioSource.isPlaying) { - // 배경음악이 설정되면 재생 - audioSource.clip = mainBgm; // 음악 클립 설정 - audioSource.loop = true; // 반복 재생 - audioSource.volume = 0.1f; // 볼륨 - audioSource.Play(); // 음악 시작 + bgmAudioSource.clip = mainBgm; + bgmAudioSource.loop = true; // BGM을 반복 재생 + bgmAudioSource.volume = 0.1f; // BGM 볼륨 설정 + bgmAudioSource.Play(); // BGM 재생 } } - // 배경음악 멈추기 + // BGM을 멈추는 함수 public void StopMainBGM() { - if (audioSource != null) + if (bgmAudioSource != null && bgmAudioSource.isPlaying) { - audioSource.Stop(); // 배경음악 멈추기 + bgmAudioSource.Stop(); // BGM을 멈춤 + } + } + + public void PlayGameBGM() + { + if (bgmAudioSource != null && gameBgm != null && !bgmAudioSource.isPlaying) + { + bgmAudioSource.clip = gameBgm; + bgmAudioSource.loop = true; // BGM을 반복 재생 + bgmAudioSource.volume = 0.1f; // BGM 볼륨 설정 + bgmAudioSource.Play(); // 게임 BGM 재생 + } + } + + public void StopGameBGM() + { + if (bgmAudioSource != null && bgmAudioSource.isPlaying) + { + bgmAudioSource.Stop(); // 게임용 BGM을 멈춤 } } + // 클릭 사운드(SFX) 재생 public void PlayClickSound() { - audioSource.PlayOneShot(clickSound, sfxVolume); + if (sfxAudioSource != null) + { + sfxAudioSource.PlayOneShot(clickSound, sfxVolume); + } } + // 닫기 사운드(SFX) 재생 public void PlayCloseSound() { - audioSource.PlayOneShot(closeSound, sfxVolume); + if (sfxAudioSource != null) + { + sfxAudioSource.PlayOneShot(closeSound, sfxVolume); + } } -} + + // 씬이 로드될 때마다 호출되는 OnSceneLoaded 메서드 + protected override void OnSceneLoaded(Scene scene, LoadSceneMode mode) + { + if (scene.name == "Main") + { + StopGameBGM(); + + if (UserManager.IsPlayBGM) // BGM 설정값에 따라 메인 BGM을 재생 + { + PlayMainBGM(); + } + } + else if (scene.name == "Game") + { + StopMainBGM(); + + if (UserManager.IsPlayBGM) // BGM 설정값에 따라 게임 BGM을 재생 + { + PlayGameBGM(); + } + } + } +} \ No newline at end of file diff --git a/Assets/Script/Common/Constants.cs b/Assets/Script/Common/Constants.cs index ae029e0..0dda83a 100644 --- a/Assets/Script/Common/Constants.cs +++ b/Assets/Script/Common/Constants.cs @@ -9,5 +9,13 @@ public const int MinCountForDrawCheck = 150; 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..4a710d8 100644 --- a/Assets/Script/Game/GameLogic.cs +++ b/Assets/Script/Game/GameLogic.cs @@ -3,6 +3,8 @@ using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; +using PimDeWitte.UnityMainThreadDispatcher; +using Random = UnityEngine.Random; public abstract class BasePlayerState { @@ -20,11 +22,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 { @@ -32,10 +32,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 { @@ -54,10 +52,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 +108,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 +157,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 +231,7 @@ public class GameLogic : MonoBehaviour public BasePlayerState firstPlayerState; public BasePlayerState secondPlayerState; private BasePlayerState _currentPlayerState; + //타이머 public FioTimer fioTimer; @@ -176,6 +242,9 @@ public class GameLogic : MonoBehaviour private int _lastRow; private int _lastCol; + private MultiplayManager _multiplayManager; + private string _roomId; + #region Renju Members // 렌주룰 금수 검사기 private RenjuForbiddenMoveDetector _forbiddenDetector; @@ -227,29 +296,187 @@ public class GameLogic : MonoBehaviour switch (gameType) { - case Enums.GameType.SinglePlay: - firstPlayerState = new PlayerState(true); - secondPlayerState = new AIState(); - // AI 난이도 설정(급수 설정) - OmokAI.Instance.SetRating(UserManager.Instance.Rating); - - //유저 이름 사진 초기화 - 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); - - SetState(firstPlayerState); - break; + // 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.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; + + // TODO: 응답값 없을 때 서버에서 다시 받아오기 or AI 플레이로 넘기는 처리 필요 + 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; + + // TODO: 응답값 없을 때 서버에서 다시 받아오기 or AI 플레이로 넘기는 처리 필요 + 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; } } + + //AI닉네임 랜덤 생성 + private string RandomAINickname() + { + string[] AI_NAMIES = { "이세돌", "신사동호랭이","진짜인간임","종로3가짱돌","마스터김춘배","62세황순자","고준일 강사님"}; + + var index = UnityEngine.Random.Range(0, AI_NAMIES.Length); + + return AI_NAMIES[index]; + } + + + 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}"); + + //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(); + + // 첫 번째 플레이어(유저)부터 시작 + SetState(firstPlayerState); + }); + } + + public void Dispose() + { + _multiplayManager?.LeaveRoom(_roomId); + _multiplayManager?.Dispose(); + } + //돌 카운터 증가 함수 public void CountStoneCounter() { @@ -353,7 +580,6 @@ public class GameLogic : MonoBehaviour SetState(null); ReplayManager.Instance.SaveReplayDataResult(result); //TODO: 게임 종료 후 행동 구현 - SceneManager.LoadScene("Main"); } //승리 확인 함수 diff --git a/Assets/Script/Game/GameManager.cs b/Assets/Script/Game/GameManager.cs index e37aca3..6ebe1f1 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,14 +46,17 @@ 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); + }); } } } + // 멀티 플레이를 위한 코드 public void ChangeToGameScene(Enums.GameType gameType) { _gameType = gameType; @@ -63,6 +66,8 @@ public class GameManager : Singleton public void ChangeToMainScene() { _gameType = Enums.GameType.None; + // TODO: 추후 혹시 모를 존재하는 socket 통신 종료 필요 - _gameLogic?.Dispose에서 LeaveRoom 호출하긴 하는데 서버에서 이미 해당 방을 삭제했을 경우 동작 확인 필요 + // _gameLogic?.Dispose(); SceneManager.LoadScene("Main"); } @@ -73,7 +78,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); 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 303f74a..c872b5a 100644 --- a/Assets/Script/UI/PanelController/PanelManager.cs +++ b/Assets/Script/UI/PanelController/PanelManager.cs @@ -14,8 +14,10 @@ public class PanelManager : MonoBehaviour private Canvas _canvas; private CoinsPanelController _coinsPanel; private LoadingPanelController loadingPanelController; + private GameObject loadingPanelObject; private Dictionary panelPrefabs = new Dictionary(); + private Dictionary effectPanelPrefabs = new Dictionary(); private void Awake() { @@ -27,6 +29,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 +61,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() { @@ -71,11 +122,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 +141,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/Assets/Script/UI/PanelController/SettingPanelController.cs b/Assets/Script/UI/PanelController/SettingPanelController.cs index a758df1..ba9efb3 100644 --- a/Assets/Script/UI/PanelController/SettingPanelController.cs +++ b/Assets/Script/UI/PanelController/SettingPanelController.cs @@ -1,6 +1,5 @@ using System.Collections; using System.Collections.Generic; -using Unity.VisualScripting; using UnityEngine; using UnityEngine.UI; @@ -8,44 +7,45 @@ public class SettingsPanelController : PanelController { [SerializeField] private Button sfxSwitch; [SerializeField] private Button bgmSwitch; - + void Start() { - //스위치 컨트롤러 상태 변경 이벤트 연결 - sfxSwitch.GetComponent().OnSwitchChanged += (OnSFXToggleValueChanged); - bgmSwitch.GetComponent().OnSwitchChanged += (OnBGMToggleValueChanged); + // 스위치 컨트롤러 상태 변경 이벤트 연결 + sfxSwitch.GetComponent().OnSwitchChanged += OnSFXToggleValueChanged; + bgmSwitch.GetComponent().OnSwitchChanged += OnBGMToggleValueChanged; // 현재 저장된 설정 값을 UI에 반영 sfxSwitch.GetComponent().SetSwitch(UserManager.IsPlaySFX); bgmSwitch.GetComponent().SetSwitch(UserManager.IsPlayBGM); } - - // SFX On/Off시 호출되는 함수 + + // SFX On/Off 시 호출되는 함수 public void OnSFXToggleValueChanged(bool value) { - Debug.Log("SFX : "+ value); UserManager.IsPlaySFX = value; // UserManager에 값 저장 } - - - // BGM On/Off시 호출되는 함수 + + // BGM On/Off 시 호출되는 함수 public void OnBGMToggleValueChanged(bool value) { - Debug.Log("BGM : "+ value); UserManager.IsPlayBGM = value; // UserManager에 값 저장 - - // GameManager에서 BGM 상태를 반영 - if (value) - { - GameManager.Instance.audioManager.PlayMainBGM(); // BGM을 켜기 - } - else + + // BGM을 끄는 경우 + if (!value) { GameManager.Instance.audioManager.StopMainBGM(); // BGM을 끄기 } + // BGM을 켜는 경우 + else + { + // 이미 BGM이 재생 중인 경우 새로 시작하지 않도록 체크 + if (!GameManager.Instance.audioManager.GetComponent().isPlaying) + { + GameManager.Instance.audioManager.PlayMainBGM(); // BGM을 켜기 + } + } } - // X 버튼 클릭시 호출되는 함수 public void OnClickCloseButton() { 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,