Merge branch 'main' into DO-54-렌주룰-거짓-금수-잡기

This commit is contained in:
Sehyeon 2025-03-26 16:44:56 +09:00
commit 8096935912
54 changed files with 8635 additions and 541 deletions

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: b8e3d5d18e544774eb144db419a327d2
guid: ded4103a87c2de84db5f4ce2675227ef
DefaultImporter:
externalObjects: {}
userData:

View File

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

View File

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

View File

@ -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: []

View File

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

Binary file not shown.

View File

@ -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:

Binary file not shown.

View File

@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: 576c895328ccd5f4a91364f7fa42bea8
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:

View File

@ -10,7 +10,6 @@ GameObject:
m_Component:
- component: {fileID: 2402232447803946232}
- component: {fileID: 3667565604047053759}
- component: {fileID: 841089304725320238}
m_Layer: 0
m_Name: Audio Manager
m_TagString: Untagged
@ -46,102 +45,16 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
mainBgm: {fileID: 8300000, guid: 1d9c7fb20aa822c48933d00b6bd6a757, type: 3}
gameBgm: {fileID: 8300000, guid: 576c895328ccd5f4a91364f7fa42bea8, type: 3}
clickSound: {fileID: 8300000, guid: cff2e6cf7f46a074d86955b3b6fd499a, type: 3}
closeSound: {fileID: 8300000, guid: e7c0f32158a3e5b46bc3b59035aba898, type: 3}
sfxVolume: 0
--- !u!82 &841089304725320238
AudioSource:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2946408323859178723}
m_Enabled: 1
serializedVersion: 4
OutputAudioMixerGroup: {fileID: 0}
m_audioClip: {fileID: 0}
m_PlayOnAwake: 1
m_Volume: 1
m_Pitch: 1
Loop: 0
Mute: 0
Spatialize: 0
SpatializePostEffects: 0
Priority: 128
DopplerLevel: 1
MinDistance: 1
MaxDistance: 500
Pan2D: 0
rolloffMode: 0
BypassEffects: 0
BypassListenerEffects: 0
BypassReverbZones: 0
rolloffCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
panLevelCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
spreadCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
reverbZoneMixCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
coinsAddSound: {fileID: 8300000, guid: 1ec44182fa76a4b3eb1459c0a6d9a8ab, type: 3}
coinsEmptySound: {fileID: 8300000, guid: 908a78cb991984977bea42916bed8684, type: 3}
coinsRemoveSound: {fileID: 8300000, guid: 585a9de0fb7ee4163af5c559ba5b2364, type: 3}
winSound: {fileID: 8300000, guid: 1613a4bf934e4d043b4e50b1b74c16b5, type: 3}
loseSound: {fileID: 8300000, guid: 2a467daa72a01214384d2fa5677f668a, type: 3}
stoneSound: {fileID: 8300000, guid: 829ae38dccffa8f4ebf5829b8963ad31, type: 3}
bgmAudioSource: {fileID: 0}
sfxVolume: 1
isPlayBGM: 0
isPlaySFX: 0

View File

@ -527,6 +527,7 @@ GameObject:
- component: {fileID: 8635639988778983738}
- component: {fileID: 4545556044007292713}
- component: {fileID: 6152865991947934791}
- component: {fileID: 3813612584874639807}
m_Layer: 5
m_Name: Draw Effect Panel
m_TagString: Untagged
@ -575,7 +576,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 +619,54 @@ 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
--- !u!61 &3813612584874639807
BoxCollider2D:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8383435151006156655}
m_Enabled: 1
m_Density: 1
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_ForceSendLayers:
serializedVersion: 2
m_Bits: 4294967295
m_ForceReceiveLayers:
serializedVersion: 2
m_Bits: 4294967295
m_ContactCaptureLayers:
serializedVersion: 2
m_Bits: 4294967295
m_CallbackLayers:
serializedVersion: 2
m_Bits: 4294967295
m_IsTrigger: 0
m_UsedByEffector: 0
m_UsedByComposite: 0
m_Offset: {x: 0, y: 0}
m_SpriteTilingProperty:
border: {x: 0, y: 0, z: 0, w: 0}
pivot: {x: 0, y: 0}
oldSize: {x: 0, y: 0}
newSize: {x: 0, y: 0}
adaptiveTilingThreshold: 0
drawMode: 0
adaptiveTiling: 0
m_AutoTiling: 0
serializedVersion: 2
m_Size: {x: 1, y: 1}
m_EdgeRadius: 0

View File

@ -450,6 +450,7 @@ GameObject:
- component: {fileID: 8635639988778983738}
- component: {fileID: 4545556044007292713}
- component: {fileID: 3229800624310508893}
- component: {fileID: 6984539173991577744}
m_Layer: 5
m_Name: Lose Effect Panel
m_TagString: Untagged
@ -499,7 +500,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 +543,52 @@ 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
--- !u!61 &6984539173991577744
BoxCollider2D:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8383435151006156655}
m_Enabled: 1
m_Density: 1
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_ForceSendLayers:
serializedVersion: 2
m_Bits: 4294967295
m_ForceReceiveLayers:
serializedVersion: 2
m_Bits: 4294967295
m_ContactCaptureLayers:
serializedVersion: 2
m_Bits: 4294967295
m_CallbackLayers:
serializedVersion: 2
m_Bits: 4294967295
m_IsTrigger: 0
m_UsedByEffector: 0
m_UsedByComposite: 0
m_Offset: {x: 0, y: 0}
m_SpriteTilingProperty:
border: {x: 0, y: 0, z: 0, w: 0}
pivot: {x: 0, y: 0}
oldSize: {x: 0, y: 0}
newSize: {x: 0, y: 0}
adaptiveTilingThreshold: 0
drawMode: 0
adaptiveTiling: 0
m_AutoTiling: 0
serializedVersion: 2
m_Size: {x: 1, y: 1}
m_EdgeRadius: 0

View File

@ -788,6 +788,7 @@ GameObject:
- component: {fileID: 8635639988778983738}
- component: {fileID: 3127148509640414758}
- component: {fileID: 4545556044007292713}
- component: {fileID: 7229651074095795597}
m_Layer: 5
m_Name: Win Effect Panel
m_TagString: Untagged
@ -837,7 +838,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 +867,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 +879,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
@ -890,6 +891,51 @@ CanvasGroup:
m_Interactable: 1
m_BlocksRaycasts: 1
m_IgnoreParentGroups: 0
--- !u!61 &7229651074095795597
BoxCollider2D:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8383435151006156655}
m_Enabled: 1
m_Density: 1
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_ForceSendLayers:
serializedVersion: 2
m_Bits: 4294967295
m_ForceReceiveLayers:
serializedVersion: 2
m_Bits: 4294967295
m_ContactCaptureLayers:
serializedVersion: 2
m_Bits: 4294967295
m_CallbackLayers:
serializedVersion: 2
m_Bits: 4294967295
m_IsTrigger: 0
m_UsedByEffector: 0
m_UsedByComposite: 0
m_Offset: {x: 0, y: 0}
m_SpriteTilingProperty:
border: {x: 0, y: 0, z: 0, w: 0}
pivot: {x: 0, y: 0}
oldSize: {x: 0, y: 0}
newSize: {x: 0, y: 0}
adaptiveTilingThreshold: 0
drawMode: 0
adaptiveTiling: 0
m_AutoTiling: 0
serializedVersion: 2
m_Size: {x: 1, y: 1}
m_EdgeRadius: 0
--- !u!1 &8851077685063213123
GameObject:
m_ObjectHideFlags: 0

View File

@ -10,7 +10,6 @@ GameObject:
m_Component:
- component: {fileID: 1769668775421633028}
- component: {fileID: 4222531876603999234}
- component: {fileID: 5499754916380040505}
m_Layer: 5
m_Name: Coins Panel
m_TagString: Untagged
@ -53,106 +52,6 @@ MonoBehaviour:
m_EditorClassIdentifier:
coinsRemoveImageObject: {fileID: 4198953742281733827}
coinsCountText: {fileID: 2892066450466788586}
coinsRemoveAudioClip: {fileID: 8300000, guid: 585a9de0fb7ee4163af5c559ba5b2364, type: 3}
coinsAddAudioClip: {fileID: 8300000, guid: 1ec44182fa76a4b3eb1459c0a6d9a8ab, type: 3}
coinsEmptyAudioClip: {fileID: 8300000, guid: 908a78cb991984977bea42916bed8684, type: 3}
ShopPanel: {fileID: 8190964574954487140, guid: eb257b0a685b2254f860f294ce8cba54, type: 3}
--- !u!82 &5499754916380040505
AudioSource:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 646100354038727038}
m_Enabled: 1
serializedVersion: 4
OutputAudioMixerGroup: {fileID: 0}
m_audioClip: {fileID: 0}
m_PlayOnAwake: 1
m_Volume: 1
m_Pitch: 1
Loop: 0
Mute: 0
Spatialize: 0
SpatializePostEffects: 0
Priority: 128
DopplerLevel: 1
MinDistance: 1
MaxDistance: 500
Pan2D: 0
rolloffMode: 0
BypassEffects: 0
BypassListenerEffects: 0
BypassReverbZones: 0
rolloffCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
panLevelCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
spreadCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
reverbZoneMixCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
--- !u!1 &4198953742281733827
GameObject:
m_ObjectHideFlags: 0

View File

@ -1713,7 +1713,7 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: "\uD638\uB7AD\uC774"
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}

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -13,7 +13,6 @@ GameObject:
- component: {fileID: 5762671619601460934}
- component: {fileID: 6789969287113785900}
- component: {fileID: 413977444317235173}
- component: {fileID: 6428684691423417346}
m_Layer: 5
m_Name: Switch
m_TagString: Untagged
@ -148,103 +147,6 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
handleImage: {fileID: 6756679498073114696}
clickSound: {fileID: 8300000, guid: cff2e6cf7f46a074d86955b3b6fd499a, type: 3}
--- !u!82 &6428684691423417346
AudioSource:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 212417647848747046}
m_Enabled: 1
serializedVersion: 4
OutputAudioMixerGroup: {fileID: 0}
m_audioClip: {fileID: 0}
m_PlayOnAwake: 1
m_Volume: 1
m_Pitch: 1
Loop: 0
Mute: 0
Spatialize: 0
SpatializePostEffects: 0
Priority: 128
DopplerLevel: 1
MinDistance: 1
MaxDistance: 500
Pan2D: 0
rolloffMode: 0
BypassEffects: 0
BypassListenerEffects: 0
BypassReverbZones: 0
rolloffCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
panLevelCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
spreadCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
reverbZoneMixCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
--- !u!1 &1754413034098038561
GameObject:
m_ObjectHideFlags: 0
@ -859,7 +761,6 @@ GameObject:
- component: {fileID: 2622934673103949083}
- component: {fileID: 3464089222698319368}
- component: {fileID: 8434701791193156984}
- component: {fileID: 9145795730378986889}
m_Layer: 5
m_Name: Switch
m_TagString: Untagged
@ -994,103 +895,6 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
handleImage: {fileID: 1822478720763898751}
clickSound: {fileID: 8300000, guid: cff2e6cf7f46a074d86955b3b6fd499a, type: 3}
--- !u!82 &9145795730378986889
AudioSource:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5387157431517620447}
m_Enabled: 1
serializedVersion: 4
OutputAudioMixerGroup: {fileID: 0}
m_audioClip: {fileID: 0}
m_PlayOnAwake: 1
m_Volume: 1
m_Pitch: 1
Loop: 0
Mute: 0
Spatialize: 0
SpatializePostEffects: 0
Priority: 128
DopplerLevel: 1
MinDistance: 1
MaxDistance: 500
Pan2D: 0
rolloffMode: 0
BypassEffects: 0
BypassListenerEffects: 0
BypassReverbZones: 0
rolloffCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
panLevelCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
spreadCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
reverbZoneMixCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
--- !u!1 &7654744771681034987
GameObject:
m_ObjectHideFlags: 0

View File

@ -1347,7 +1347,19 @@ MonoBehaviour:
m_Calls: []
m_OnDeselect:
m_PersistentCalls:
m_Calls: []
m_Calls:
- m_Target: {fileID: 5818338764896528680}
m_TargetAssemblyTypeName: SignupPanelController, Assembly-CSharp
m_MethodName: OnChangeEndPassword
m_Mode: 0
m_Arguments:
m_ObjectArgument: {fileID: 0}
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
m_IntArgument: 0
m_FloatArgument: 0
m_StringArgument:
m_BoolArgument: 0
m_CallState: 2
m_OnTextSelection:
m_PersistentCalls:
m_Calls: []
@ -2141,7 +2153,19 @@ MonoBehaviour:
m_Calls: []
m_OnValueChanged:
m_PersistentCalls:
m_Calls: []
m_Calls:
- m_Target: {fileID: 5818338764896528680}
m_TargetAssemblyTypeName: SignupPanelController, Assembly-CSharp
m_MethodName: OnChangeNickname
m_Mode: 0
m_Arguments:
m_ObjectArgument: {fileID: 0}
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
m_IntArgument: 0
m_FloatArgument: 0
m_StringArgument:
m_BoolArgument: 0
m_CallState: 2
m_OnTouchScreenKeyboardStatusChanged:
m_PersistentCalls:
m_Calls: []
@ -2791,10 +2815,34 @@ MonoBehaviour:
m_Calls: []
m_OnSelect:
m_PersistentCalls:
m_Calls: []
m_Calls:
- m_Target: {fileID: 5818338764896528680}
m_TargetAssemblyTypeName: SignupPanelController, Assembly-CSharp
m_MethodName: OnSelectEmail
m_Mode: 1
m_Arguments:
m_ObjectArgument: {fileID: 0}
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
m_IntArgument: 0
m_FloatArgument: 0
m_StringArgument:
m_BoolArgument: 0
m_CallState: 2
m_OnDeselect:
m_PersistentCalls:
m_Calls: []
m_Calls:
- m_Target: {fileID: 5818338764896528680}
m_TargetAssemblyTypeName: SignupPanelController, Assembly-CSharp
m_MethodName: OnChangeEndEmail
m_Mode: 0
m_Arguments:
m_ObjectArgument: {fileID: 0}
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
m_IntArgument: 0
m_FloatArgument: 0
m_StringArgument:
m_BoolArgument: 0
m_CallState: 2
m_OnTextSelection:
m_PersistentCalls:
m_Calls: []

View File

@ -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:

View File

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

Binary file not shown.

View File

@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: 1613a4bf934e4d043b4e50b1b74c16b5
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:

View File

@ -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}

View File

@ -1,56 +1,167 @@
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.Timeline;
[RequireComponent(typeof(AudioSource))]
public class AudioManager : MonoBehaviour
public class AudioManager : Singleton<AudioManager>
{
[Header("BGM")]
[SerializeField] private AudioClip mainBgm;
[SerializeField] private AudioClip gameBgm;
[Header("SFX")]
[SerializeField] private AudioClip clickSound;
[SerializeField] private AudioClip closeSound;
[SerializeField] private AudioClip coinsAddSound;
[SerializeField] private AudioClip coinsEmptySound;
[SerializeField] private AudioClip coinsRemoveSound;
[SerializeField] private AudioClip winSound;
[SerializeField] private AudioClip loseSound;
[SerializeField] private AudioClip stoneSound;
private AudioSource audioSource;
[HideInInspector] public AudioSource bgmAudioSource; // BGM을 위한 AudioSource
private AudioSource sfxAudioSource; // SFX를 위한 AudioSource
[HideInInspector] public float sfxVolume;
public float sfxVolume = 1.0f; // SFX 볼륨 (기본값 1)
[HideInInspector]public bool isPlayBGM;
[HideInInspector]public bool isPlaySFX;
private void Awake()
{
base.Awake(); // 부모 클래스의 Awake 호출
// BGM과 SFX를 위한 별도의 AudioSource 생성
bgmAudioSource = gameObject.AddComponent<AudioSource>();
sfxAudioSource = gameObject.AddComponent<AudioSource>();
}
// 시작 시 BGM을 자동으로 재생
private void Start()
{
PlayMainBGM();
sfxVolume = 1.0f; //테스트 코드
isPlayBGM = UserManager.IsPlayBGM;
isPlaySFX = UserManager.IsPlaySFX;
PlayBGM();
}
// 배경음악 시작
// 메인 BGM을 재생하는 함수
public void PlayMainBGM()
{
// AudioSource 컴포넌트 가져오기
audioSource = GetComponent<AudioSource>();
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 재생
}
}
// 배경음악 멈추기
public void StopMainBGM()
public void PlayGameBGM()
{
if (audioSource != null)
if (bgmAudioSource != null && gameBgm != null && !bgmAudioSource.isPlaying)
{
audioSource.Stop(); // 배경음악 멈추기
bgmAudioSource.clip = gameBgm;
bgmAudioSource.loop = true; // BGM을 반복 재생
bgmAudioSource.volume = 0.1f; // BGM 볼륨 설정
bgmAudioSource.Play(); // 게임 BGM 재생
}
}
public void PlayBGM()
{
if (isPlayBGM)
{
Scene currentScene = SceneManager.GetActiveScene();
if (currentScene.name == "Main")
{
StopBGM();
PlayMainBGM();
}
else if (currentScene.name == "Game")
{
StopBGM();
PlayGameBGM();
}
}
}
public void StopBGM()
{
if (bgmAudioSource != null && bgmAudioSource.isPlaying)
{
bgmAudioSource.Stop(); // 게임용 BGM을 멈춤
}
}
// 씬이 로드될 때마다 호출되는 OnSceneLoaded 메서드
protected override void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
PlayBGM();
}
// 클릭 사운드(SFX) 재생
public void PlayClickSound()
{
audioSource.PlayOneShot(clickSound, sfxVolume);
if (isPlaySFX && sfxAudioSource != null)
{
sfxAudioSource.PlayOneShot(clickSound, sfxVolume);
}
}
// 닫기 사운드(SFX) 재생
public void PlayCloseSound()
{
audioSource.PlayOneShot(closeSound, sfxVolume);
if (isPlaySFX && sfxAudioSource != null)
{
sfxAudioSource.PlayOneShot(closeSound, sfxVolume);
}
}
}
public void PlayCoinsAddSound()
{
if (isPlaySFX && sfxAudioSource!=null)
{
sfxAudioSource.PlayOneShot(coinsAddSound, sfxVolume);
}
}
public void PlayCoinsEmptySound()
{
if (isPlaySFX && sfxAudioSource!=null)
{
sfxAudioSource.PlayOneShot(coinsEmptySound, sfxVolume);
}
}
public void PlayCoinsRemoveSound()
{
if (isPlaySFX && sfxAudioSource!=null)
{
sfxAudioSource.PlayOneShot(coinsRemoveSound, sfxVolume);
}
}
public void PlayLoseSound()
{
if (isPlaySFX && sfxAudioSource!=null)
{
sfxAudioSource.PlayOneShot(loseSound, sfxVolume);
}
}
public void PlayWinSound()
{
if (isPlaySFX && sfxAudioSource!=null)
{
sfxAudioSource.PlayOneShot(winSound, sfxVolume);
}
}
public void PlayStoneSound()
{
if (isPlaySFX && sfxAudioSource!=null)
{
sfxAudioSource.PlayOneShot(stoneSound, sfxVolume);
}
}
}

View File

@ -6,16 +6,11 @@ using UnityEngine.EventSystems;
using UnityEngine.Serialization;
using UnityEngine.UI;
[RequireComponent(typeof(AudioSource))]
public class CoinsPanelController : MonoBehaviour
{
[SerializeField] private GameObject coinsRemoveImageObject;
[SerializeField] private TMP_Text coinsCountText;
[SerializeField] private AudioClip coinsRemoveAudioClip;
[SerializeField] private AudioClip coinsAddAudioClip;
[SerializeField] private AudioClip coinsEmptyAudioClip;
private Color _coinsColor;
private AudioSource _audioSource;
private int _coinsCount;
@ -110,19 +105,17 @@ public class CoinsPanelController : MonoBehaviour
{
ChangeTextAnimation(true, ()=>
{
// TODO : 코인 수량 업데이트
_coinsCount += 500;
action?.Invoke();
});
// 효과음 재생
// TODO : if (UserInformation.IsPlaySFX)
_audioSource.PlayOneShot(coinsAddAudioClip);
AudioManager.Instance.PlayCoinsAddSound();
});
sequence.AppendInterval(0.5f);
}
sequence.OnComplete(() =>
{
_coinsCount += coinsCount; //추가된 코인 적용
_canvasGroup.blocksRaycasts = true; //구매 후 클릭 활성화
});
}
@ -130,8 +123,7 @@ public class CoinsPanelController : MonoBehaviour
public void EmptyCoins()
{
// 효과음 재생
// TODO: if (UserInformation.IsPlaySFX)
_audioSource.PlayOneShot(coinsEmptyAudioClip);
AudioManager.Instance.PlayCoinsEmptySound();
GetComponent<RectTransform>().DOPunchPosition(new Vector3(20f, 0, 0), 1f, 7);
}
@ -151,8 +143,7 @@ public class CoinsPanelController : MonoBehaviour
}
// 효과음 재생
// TODO: if (UserInformation.IsPlaySFX)
_audioSource.PlayOneShot(coinsRemoveAudioClip);
AudioManager.Instance.PlayCoinsRemoveSound();
// 코인 사라지는 연출
coinsRemoveImageObject.SetActive(true);

View File

@ -7,6 +7,15 @@
public const int WIN_COUNT = 5;
//무승부 확인을 위한 최소 착수 수
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 // 상대방이 접속을 끊거나 방을 나갔을 때
};
}

View File

@ -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
/// <summary>
/// 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
/// </summary>
public class UnityMainThreadDispatcher : MonoBehaviour {
private static readonly Queue<Action> _executionQueue = new Queue<Action>();
public void Update() {
lock(_executionQueue) {
while (_executionQueue.Count > 0) {
_executionQueue.Dequeue().Invoke();
}
}
}
/// <summary>
/// Locks the queue and adds the IEnumerator to the queue
/// </summary>
/// <param name="action">IEnumerator function that will be executed from the main thread.</param>
public void Enqueue(IEnumerator action) {
lock (_executionQueue) {
_executionQueue.Enqueue (() => {
StartCoroutine (action);
});
}
}
/// <summary>
/// Locks the queue and adds the Action to the queue
/// </summary>
/// <param name="action">function that will be executed from the main thread.</param>
public void Enqueue(Action action)
{
Enqueue(ActionWrapper(action));
}
/// <summary>
/// Locks the queue and adds the Action to the queue, returning a Task which is completed when the action completes
/// </summary>
/// <param name="action">function that will be executed from the main thread.</param>
/// <returns>A Task that can be awaited until the action completes</returns>
public Task EnqueueAsync(Action action)
{
var tcs = new TaskCompletionSource<bool>();
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;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e7461fd0f3834d9283d0ea00daaaea3b
timeCreated: 1742916255

View File

@ -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
{
@ -10,6 +12,10 @@ public abstract class BasePlayerState
public abstract void OnExit(GameLogic gameLogic);
public abstract void HandleMove(GameLogic gameLogic, int row, int col);
public abstract void HandleNextTurn(GameLogic gameLogic);
protected string _roomId;
protected bool _isMultiplay;
protected MultiplayManager _multiplayManager;
public void ProcessMove(GameLogic gameLogic, Enums.PlayerType playerType, int row, int col)
{
@ -18,13 +24,16 @@ public abstract class BasePlayerState
gameLogic.SetNewBoardValue(playerType, row, col);
gameLogic.CountStoneCounter();
if (_isMultiplay)
{
_multiplayManager.SendPlayerMove(_roomId, new Vector2Int(row, col));
}
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 +41,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 +61,21 @@ public class PlayerState : BasePlayerState
{
private Enums.PlayerType _playerType;
private bool _isFirstPlayer;
public PlayerState(bool isFirstPlayer)
{
_isFirstPlayer = isFirstPlayer;
_playerType = isFirstPlayer ? Enums.PlayerType.PlayerA : Enums.PlayerType.PlayerB;
_isMultiplay = false;
}
public PlayerState(bool isFirstPlayer, MultiplayManager multiplayManager, string roomId)
: this(isFirstPlayer)
{
_isFirstPlayer = isFirstPlayer;
_multiplayManager = multiplayManager;
_roomId = roomId;
_isMultiplay = true;
}
public override void OnEnter(GameLogic gameLogic)
@ -132,24 +150,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 +224,7 @@ public class GameLogic : MonoBehaviour
public BasePlayerState firstPlayerState;
public BasePlayerState secondPlayerState;
private BasePlayerState _currentPlayerState;
//타이머
public FioTimer fioTimer;
@ -176,6 +235,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 +289,203 @@ 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;
}
// 선공, 후공 처리
bool isFirstPlayer = joinRoomData.isBlack;
if (isFirstPlayer)
{
Debug.Log("해당 플레이어가 선공 입니다");
firstPlayerState = new PlayerState(true, _multiplayManager, joinRoomData.roomId);
secondPlayerState = new MultiPlayerState(false, _multiplayManager);
}
else
{
Debug.Log("해당 플레이어가 후공 입니다");
firstPlayerState = new MultiPlayerState(true, _multiplayManager);
secondPlayerState = new PlayerState(false, _multiplayManager, joinRoomData.roomId);
}
// 메인 스레드에서 실행 - 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;
}
// 선공, 후공 처리
isFirstPlayer = startGameData.isBlack;
if (isFirstPlayer)
{
Debug.Log("해당 플레이어가 선공 입니다");
firstPlayerState = new PlayerState(true, _multiplayManager, _roomId);
secondPlayerState = new MultiPlayerState(false, _multiplayManager);
}
else
{
Debug.Log("해당 플레이어가 후공 입니다");
firstPlayerState = new MultiPlayerState(true, _multiplayManager);
secondPlayerState = new PlayerState(false, _multiplayManager, _roomId);
}
// 메인 스레드에서 실행 - 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 +589,6 @@ public class GameLogic : MonoBehaviour
SetState(null);
ReplayManager.Instance.SaveReplayDataResult(result);
//TODO: 게임 종료 후 행동 구현
SceneManager.LoadScene("Main");
}
//승리 확인 함수

View File

@ -11,7 +11,7 @@ public class GameManager : Singleton<GameManager>
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<GameManager>
}
else
{
if (_stoneController != null && _omokBoardImage != null)
if (_camera != null)
{
_stoneController.GetComponent<Transform>().DOShakePosition(0.5f, 0.5f);
_omokBoardImage.GetComponent<Transform>().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<GameManager>
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<GameManager>
_stoneController = GameObject.FindObjectOfType<StoneController>();
_stoneController.InitStones();
var fioTimer = FindObjectOfType<FioTimer>();
_omokBoardImage = GameObject.FindObjectOfType<SpriteRenderer>().gameObject;
_camera = GameObject.FindObjectOfType<Camera>().gameObject;
_gameUIController = GameObject.FindObjectOfType<GameUIController>();
_gameLogic = new GameLogic(_stoneController, _gameType, fioTimer);

View File

@ -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<Constants.MultiplayManagerState, object> _onMultiplayStateChanged;
public Action<MoveData> OnOpponentMove;
public MultiplayManager(Action<Constants.MultiplayManagerState, object> 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<CreateRoomData>();
_onMultiplayStateChanged?.Invoke(Constants.MultiplayManagerState.CreateRoom, data.roomId);
}
private void JoinRoom(SocketIOResponse response)
{
var data = response.GetValue<JoinRoomData>();
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<MessageData>();
Debug.Log("switchAI: " + data.message);
_onMultiplayStateChanged?.Invoke(Constants.MultiplayManagerState.SwitchAI, data.message);
}
private void StartGame(SocketIOResponse response)
{
var data = response.GetValue<StartGameData>();
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<MoveData>();
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;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 18c2446fa3d545d289abeca18341ef81
timeCreated: 1742865818

View File

@ -120,6 +120,7 @@ public class LoadingPanelController : MonoBehaviour
{
cancellationTokenSource.Cancel();
}
gameObject.SetActive(false);
if (gameObject.activeSelf) gameObject.SetActive(false);
}
}

View File

@ -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);
}));
}

View File

@ -1,5 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using TMPro;
using UnityEngine;
using UnityEngine.Networking;
@ -22,7 +24,9 @@ public class SignupPanelController : MonoBehaviour
[SerializeField] private TMP_InputField confirmPasswordInputField;
[SerializeField] private Toggle[] imageSelectToggles;
private int _selectedImageIndex = 0;
private bool _emailValid = false;
private void Start()
{
SetToggleInit();
@ -43,15 +47,37 @@ public class SignupPanelController : MonoBehaviour
public void OnValueChanged(bool value, int index)
{
// 현재 토글을 끄려고 할 때 (value가 false)
if (!value && index == _selectedImageIndex)
{
// 현재 켜져 있는 토글을 다시 켜지 않게 하고
imageSelectToggles[index].onValueChanged.RemoveAllListeners();
// 다른 토글을 켬 (현재 선택된 인덱스가 아닌 다음 토글을 켬)
int nextIndex = (index + 1) % imageSelectToggles.Length;
_selectedImageIndex = nextIndex;
imageSelectToggles[nextIndex].isOn = true;
// 이벤트 리스너 다시 추가
int capturedIndex = index;
imageSelectToggles[index].onValueChanged.AddListener((bool val) => OnValueChanged(val, capturedIndex));
return;
}
// 새로운 토글을 선택했을 때
if (value)
{
int previousIndex = _selectedImageIndex;
_selectedImageIndex = index;
int previousIndex = (_selectedImageIndex == 0) ? 1 : 0;
imageSelectToggles[previousIndex].isOn = false;
// 선택된 토글이 변경되었을 때만 이전 토글을 끔
if (previousIndex != index)
{
imageSelectToggles[previousIndex].isOn = false;
}
}
}
public void OnClickConfirmButton()
{
var email = emailInputField.text;
@ -59,7 +85,7 @@ public class SignupPanelController : MonoBehaviour
var password = passwordInputField.text;
var confirmPassword = confirmPasswordInputField.text;
if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(nickname) ||
if (string.IsNullOrEmpty(email) || !_emailValid || string.IsNullOrEmpty(nickname) ||
string.IsNullOrEmpty(password) || string.IsNullOrEmpty(confirmPassword))
{
// 입력 내용 누락 팝업 표시
@ -104,4 +130,63 @@ public class SignupPanelController : MonoBehaviour
{
Destroy(gameObject);
}
// 이메일 입력을 완료하면 유효성 검사를 진행함
public void OnChangeEndEmail(string emailText)
{
const string emailPattern = @"^[^\s@]+@[^\s@]+\.[^\s@]+$";
// 입력이 끝났을 때 이메일 형식 검사
_emailValid = Regex.IsMatch(emailText, emailPattern);
// 이메일이 비어있지 않은 경우에만 색상 변경
if (!string.IsNullOrEmpty(emailText))
{
if (_emailValid) return;
emailInputField.textComponent.color = Color.red;
// 이메일 유효하지 않음
GameManager.Instance.panelManager.OpenConfirmPanel("올바른 이메일 주소를 입력하세요.", () => {});
Debug.Log("이메일 유효성 X");
}
}
// 이메일 인풋을 선택했을 때 이메일이 유효하지 않았으면 텍스트를 초기화함
public void OnSelectEmail()
{
if (!_emailValid)
{
emailInputField.textComponent.color = Color.black;
emailInputField.text = String.Empty;
}
}
// 닉네임 글자 6자 초과하면 마지막 글자를 잘라서 6자로 만듬
public void OnChangeNickname(string nicknameText)
{
const int maxNicknameLength = 6;
if (nicknameText.Length > maxNicknameLength)
{
// 글자수가 제한을 초과하면 처음 6글자만 남김
string limitedText = nicknameText.Substring(0, maxNicknameLength);
nicknameInputField.text = limitedText;
}
}
// 비밀번호 유효성 검사
public void OnChangeEndPassword(string passwordText)
{
// 비밀번호가 비어 있으면 검사하지 않음
if (string.IsNullOrEmpty(passwordText)) return;
// 비밀번호 글자수 제한 확인
if (passwordText.Length < 6 || passwordText.Length > 18)
{
// 비밀번호 글자 수 제한
GameManager.Instance.panelManager.OpenConfirmPanel("비밀번호는 6자 이상 18자 이하로 입력해주세요.", () => {});
Debug.Log("비밀번호 글자 수 제한");
}
}
}

View File

@ -87,14 +87,22 @@ public class UserManager : Singleton<UserManager>
public static bool IsPlaySFX
{
get { return PlayerPrefs.GetInt("IsPlaySFX", 1) == 1; }
set { PlayerPrefs.SetInt("IsPlaySFX", value ? 1 : 0); }
set
{
PlayerPrefs.SetInt("IsPlaySFX", value ? 1 : 0);
AudioManager.Instance.isPlaySFX = value;
}
}
// 배경음악 재생 여부
public static bool IsPlayBGM
{
get { return PlayerPrefs.GetInt("IsPlayBGM", 1) == 1; }
set { PlayerPrefs.SetInt("IsPlayBGM", value ? 1 : 0); }
set
{
PlayerPrefs.SetInt("IsPlayBGM", value ? 1 : 0);
AudioManager.Instance.isPlayBGM = value;
}
}
protected override void OnSceneLoaded(Scene scene, LoadSceneMode mode)

View File

@ -12,10 +12,11 @@ public class DrawEffectController : EffectController
[SerializeField] private float flipDuration = 0.3f;
protected override string fullText => "무승부 입니다";
protected override void ShowEffect()
public override void ShowEffect(OnEffectPanelEnded onEffectPanelEnd)
{
gameObject.SetActive(true);
cancellationTokenSource = new CancellationTokenSource();
onEffectPanelEnded = onEffectPanelEnd;
ShowPanel();
StartCoroutine(AnimateLoadingText());

View File

@ -3,9 +3,11 @@ using System.Threading;
using DG.Tweening;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UIElements;
[RequireComponent(typeof(CanvasGroup))]
public abstract class EffectController : MonoBehaviour
public abstract class EffectController : MonoBehaviour, IPointerClickHandler
{
[SerializeField] protected GameObject bannerObj;
[SerializeField] protected TextMeshProUGUI bannerText;
@ -14,14 +16,17 @@ public abstract class EffectController : MonoBehaviour
protected CancellationTokenSource cancellationTokenSource;
protected int currentLength = 0;
public delegate void OnEffectPanelEnded();
protected OnEffectPanelEnded onEffectPanelEnded;
protected virtual void Start()
{
ShowEffect();
}
// protected virtual void Start()
// {
// ShowEffect();
// }
// 효과를 실행하는 메서드 (자식이 구현해야 함)
protected abstract void ShowEffect();
public abstract void ShowEffect(OnEffectPanelEnded onEffectPanelEnded);
// 공통 UI 애니메이션 (패널 표시)
protected virtual void ShowPanel()
@ -59,7 +64,13 @@ public abstract class EffectController : MonoBehaviour
cancellationTokenSource.Dispose();
cancellationTokenSource = null;
}
onEffectPanelEnded?.Invoke();
gameObject.SetActive(false);
}
public void OnPointerClick(PointerEventData eventData)
{
HideEffect();
}
}

View File

@ -12,10 +12,11 @@ public class LoseEffectController : EffectController
protected override string fullText => "패배했습니다";
protected override void ShowEffect()
public override void ShowEffect(OnEffectPanelEnded onEffectPanelEnd)
{
gameObject.SetActive(true);
cancellationTokenSource = new CancellationTokenSource();
onEffectPanelEnded = onEffectPanelEnd;
ShowPanel();
StartCoroutine(AnimateLoadingText());

View File

@ -14,10 +14,11 @@ public class WinEffectController : EffectController
protected override string fullText => "승리했습니다!";
protected override void ShowEffect()
public override void ShowEffect(OnEffectPanelEnded onEffectPanelEnd)
{
gameObject.SetActive(true);
cancellationTokenSource = new CancellationTokenSource();
onEffectPanelEnded = onEffectPanelEnd;
ShowPanel();
StartCoroutine(AnimateLoadingText());

View File

@ -0,0 +1,15 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class RatingPointsController : MonoBehaviour
{
[SerializeField] Image[] minusImages;
[SerializeField] Image[] plusImage;
private Color32 _minusColor = new Color32(255, 0, 0, 255);
private Color32 _plusColor = new Color32(34, 87, 255, 255);
private Color32 _defaultColor = new Color32(176, 176, 176, 255);
}

View File

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

View File

@ -6,11 +6,9 @@ using UnityEngine.UI;
using DG.Tweening;
[RequireComponent(typeof(Image))]
[RequireComponent(typeof(AudioSource))]
public class SwitchController : MonoBehaviour
{
[SerializeField] private Image handleImage;
[SerializeField] private AudioClip clickSound;
//스위치에 상태 변경 시 호출할 콜백 함수
public delegate void OnSwitchChangedDelegate(bool isOn);
@ -56,8 +54,7 @@ public class SwitchController : MonoBehaviour
}
// 효과음 재생
if (clickSound != null)
_audioSource.PlayOneShot(clickSound);
AudioManager.Instance.PlayClickSound();
//이벤트 호출
OnSwitchChanged?.Invoke(isOn);

View File

@ -14,8 +14,10 @@ public class PanelManager : MonoBehaviour
private Canvas _canvas;
private CoinsPanelController _coinsPanel;
private LoadingPanelController loadingPanelController;
private GameObject loadingPanelObject;
private Dictionary<string, GameObject> panelPrefabs = new Dictionary<string, GameObject>();
private Dictionary<string, GameObject> effectPanelPrefabs = new Dictionary<string, GameObject>();
private void Awake()
{
@ -27,6 +29,13 @@ public class PanelManager : MonoBehaviour
{
panelPrefabs[prefab.name] = prefab;
}
//게임결과 이펙트 패널
GameObject[] effectPrefabs = Resources.LoadAll<GameObject>("Prefabs/Effects");
foreach (GameObject effect in effectPrefabs)
{
effectPanelPrefabs[effect.name] = effect;
}
Debug.Log($"총 {panelPrefabs.Count}개의 패널이 로드됨.");
}
@ -52,6 +61,56 @@ 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");
winEffectPanelObject.GetComponent<WinEffectController>().ShowEffect(OnEffectPanelEnded);
}
break;
case Enums.GameResult.Lose:
if (_canvas != null)
{
var loseEffectPanelObject = GetEffectPanel("Lose Effect Panel");
loseEffectPanelObject.GetComponent<LoseEffectController>().ShowEffect(OnEffectPanelEnded);
}
break;
case Enums.GameResult.Draw:
if (_canvas != null)
{
var drawEffectPanelObject = GetEffectPanel("Draw Effect Panel");
drawEffectPanelObject.GetComponent<DrawEffectController>().ShowEffect(OnEffectPanelEnded);
}
break;
}
}
private void OnEffectPanelEnded()
{
OpenRatingPanel();
}
#endregion
public void OpenMainPanel()
{
@ -71,11 +130,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<LoadingPanelController>();
if (loadingPanelController != null)
@ -84,6 +149,18 @@ public class PanelManager : MonoBehaviour
}
}
}
public void CloseLoadingPanel()
{
if (loadingPanelObject != null && loadingPanelObject.activeSelf && loadingPanelController != null)
{
loadingPanelController.StopLoading();
}
else
{
Debug.Log("로딩 패널이 이미 닫혔거나 비활성화 상태입니다.");
}
}
public void OpenSigninPanel()
{
@ -188,6 +265,16 @@ public class PanelManager : MonoBehaviour
GameManager.Instance.panelManager.OpenShopPanel(shopItems);
}
//승급 패널 생성
public void OpenRatingPanel()
{
if (_canvas != null)
{
var replayPanelObject = GetPanel("Rating Panel");
replayPanelObject.GetComponent<RatingPanelController>().Show();
}
}
//코인 패널 코인 갱신
public void UpdateCoinsPanelUI(int coinsChanged)
{
@ -203,7 +290,7 @@ public class PanelManager : MonoBehaviour
Debug.Log("코인 패널이 null 입니다.");
}
}
public void RemoveCoinsPanelUI(Action onComplete)
{
NetworkManager.Instance.DeductCoins((i) =>

View File

@ -0,0 +1,51 @@
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UI;
public class RatingPanelController : ConfirmPanelController
{
[SerializeField] private TMP_Text getPointsText;
[SerializeField] private TMP_Text scoreText;
[SerializeField] private GameObject threePointsIndicator;
[SerializeField] private GameObject fivePointsIndicator;
[SerializeField] private GameObject tenPointsIndicator;
private bool _isWin;
private int _requiredPoints;
private int _currentPoints;
private int _myRating;
/// <summary>
/// 텍스트 초기화, 승급포인트 계산
/// </summary>
/// <param name="isWin"></param>
public void InitRatingPanel(bool isWin)
{
_isWin = isWin;
_myRating= UserManager.Instance.Rating;
if (_myRating >= 10 && _myRating <= 18) {// 10~18급은 3점 필요
threePointsIndicator.gameObject.SetActive(true);
} else if (_myRating >= 5 && _myRating <= 9) {// 5~9급은 5점 필요
fivePointsIndicator.gameObject.SetActive(true);
} else if (_myRating >= 1 && _myRating <= 4) {// 1~4급은 10점 필요
tenPointsIndicator.gameObject.SetActive(true);
tenPointsIndicator.GetComponent<RatingPointsController>();
}
string win = _isWin ? "승리" : "패배";
string get = _isWin ? "얻었습니다." : "잃었습니다.";
getPointsText.text = $"게임에서 {win}했습니다.\n{Constants.RAING_POINTS} 승급 포인트를 {get}";
//TODO: network에 스코어 요청
}
void Start()
{
InitRatingPanel(false);
}
}

View File

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

View File

@ -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<SwitchController>().OnSwitchChanged += (OnSFXToggleValueChanged);
bgmSwitch.GetComponent<SwitchController>().OnSwitchChanged += (OnBGMToggleValueChanged);
// 스위치 컨트롤러 상태 변경 이벤트 연결
sfxSwitch.GetComponent<SwitchController>().OnSwitchChanged += OnSFXToggleValueChanged;
bgmSwitch.GetComponent<SwitchController>().OnSwitchChanged += OnBGMToggleValueChanged;
// 현재 저장된 설정 값을 UI에 반영
sfxSwitch.GetComponent<SwitchController>().SetSwitch(UserManager.IsPlaySFX);
bgmSwitch.GetComponent<SwitchController>().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)
// BGM을 끄는 경우
if (!value)
{
GameManager.Instance.audioManager.PlayMainBGM(); // BGM을 켜
AudioManager.Instance.StopBGM(); // BGM을 끄
}
// BGM을 켜는 경우
else
{
GameManager.Instance.audioManager.StopMainBGM(); // BGM을 끄기
// 이미 BGM이 재생 중인 경우 새로 시작하지 않도록 체크
if (!AudioManager.Instance.bgmAudioSource.isPlaying)
{
AudioManager.Instance.PlayBGM(); // BGM을 켜기
}
}
}
// X 버튼 클릭시 호출되는 함수
public void OnClickCloseButton()
{

View File

@ -0,0 +1,52 @@
using UnityEngine;
using UnityEngine.UI;
public class SingleInteractableButtonHandler : MonoBehaviour
{
[Tooltip("이 버튼이 한 번만 클릭되도록 제한할지 여부")]
[SerializeField] private bool enableOneTimeClick = true;
private Button _button;
private bool hasBeenClicked = false;
private void Awake()
{
// 버튼 컴포넌트 가져오기
_button = GetComponent<Button>();
if (_button != null && enableOneTimeClick)
{
// 기존 onClick 이벤트를 저장
Button.ButtonClickedEvent originalOnClick = _button.onClick;
_button.onClick = new Button.ButtonClickedEvent();
_button.onClick.AddListener(() =>
{
if (!hasBeenClicked)
{
hasBeenClicked = true;
for (int i = 0; i < originalOnClick.GetPersistentEventCount(); i++)
{
originalOnClick.Invoke();
}
// 버튼 비활성화
_button.interactable = false;
}
});
}
}
// 버튼 상태 리셋
public void ResetButton()
{
hasBeenClicked = false;
if (_button != null)
{
_button.interactable = true;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4330206548604932b038a7007dacf94b
timeCreated: 1742970986

View File

@ -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",

View File

@ -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,