2026-05-01 04:07:22 +09:00
|
|
|
using System.Collections;
|
2026-05-01 05:47:12 +09:00
|
|
|
using System.Collections.Generic;
|
2026-05-01 04:07:22 +09:00
|
|
|
using TMPro;
|
2026-05-01 04:03:39 +09:00
|
|
|
using UnityEngine;
|
2026-05-01 05:47:12 +09:00
|
|
|
using Photon.Pun;
|
|
|
|
|
using UnityEngine.Networking;
|
|
|
|
|
using Hashtable = ExitGames.Client.Photon.Hashtable;
|
2026-05-01 04:03:39 +09:00
|
|
|
|
2026-05-01 05:47:12 +09:00
|
|
|
public class GameManager : MonoBehaviourPunCallbacks
|
2026-05-01 04:03:39 +09:00
|
|
|
{
|
2026-05-01 04:07:22 +09:00
|
|
|
public TextMeshProUGUI text;
|
|
|
|
|
public GameObject readyPanel;
|
2026-05-01 05:47:12 +09:00
|
|
|
|
|
|
|
|
public static Dictionary<int, Sprite> AvatarCache = new Dictionary<int, Sprite>();
|
|
|
|
|
|
|
|
|
|
private void Start()
|
|
|
|
|
{
|
|
|
|
|
// 씬 시작 시, 모두의 아바타를 다운로드하는 코루틴 시작
|
|
|
|
|
StartCoroutine(DownloadAllAvatarsAndReady());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private IEnumerator DownloadAllAvatarsAndReady()
|
|
|
|
|
{
|
|
|
|
|
AvatarCache.Clear(); // 게임 시작 전 딕셔너리 초기화
|
|
|
|
|
|
|
|
|
|
// 방에 있는 모든 플레이어를 한 명씩 확인
|
|
|
|
|
foreach (var player in PhotonNetwork.PlayerList)
|
|
|
|
|
{
|
|
|
|
|
// 그 플레이어의 정보에 "AvatarUrl"이 있는지 확인
|
|
|
|
|
if (player.CustomProperties.TryGetValue("AvatarUrl", out object urlObject))
|
|
|
|
|
{
|
|
|
|
|
string url = (string)urlObject;
|
|
|
|
|
|
|
|
|
|
// 해당 URL에서 이미지를 다운로드 (비동기 대기)
|
|
|
|
|
yield return StartCoroutine(DownloadImage(player.ActorNumber, url));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- 다운로드가 모두 끝났음! ---
|
|
|
|
|
|
|
|
|
|
// 서버에 "나 이미지 다운로드 다 했고 준비 끝났어!" 라고 알림
|
|
|
|
|
Hashtable props = new Hashtable() { { "IsLoaded", true } };
|
|
|
|
|
PhotonNetwork.LocalPlayer.SetCustomProperties(props);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private IEnumerator DownloadImage(int actorNumber, string url)
|
|
|
|
|
{
|
|
|
|
|
// 빈 URL이면 무시
|
|
|
|
|
if (string.IsNullOrEmpty(url)) yield break;
|
|
|
|
|
|
|
|
|
|
using UnityWebRequest request = UnityWebRequestTexture.GetTexture(url);
|
|
|
|
|
yield return request.SendWebRequest(); // 다운로드 끝날 때까지 대기
|
|
|
|
|
|
|
|
|
|
if (request.result != UnityWebRequest.Result.Success)
|
|
|
|
|
{
|
|
|
|
|
Debug.LogError($"[다운로드 실패] 유저 번호: {actorNumber}, 에러: {request.error}");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// 성공적으로 받아왔다면 Texture를 추출
|
|
|
|
|
Texture2D texture = DownloadHandlerTexture.GetContent(request);
|
|
|
|
|
|
|
|
|
|
// UI(Image 컴포넌트)에 넣기 편하게 Sprite 형식으로 변환
|
|
|
|
|
Sprite avatarSprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height),
|
|
|
|
|
new Vector2(0.5f, 0.5f));
|
|
|
|
|
|
|
|
|
|
// 딕셔너리에 저장! (나중에 꺼내 쓸 목적)
|
|
|
|
|
AvatarCache[actorNumber] = avatarSprite;
|
|
|
|
|
|
|
|
|
|
Debug.Log($"[다운로드 성공] 유저 번호 {actorNumber}의 아바타 저장 완료!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 누군가의 상태가 바뀔 때마다 실행됨
|
|
|
|
|
public override void OnPlayerPropertiesUpdate(Photon.Realtime.Player targetPlayer, Hashtable changedProps)
|
|
|
|
|
{
|
|
|
|
|
// 내가 방장일 때만 모든 사람이 로딩을 마쳤는지 검사합니다.
|
|
|
|
|
if (!PhotonNetwork.IsMasterClient || !changedProps.ContainsKey("IsLoaded")) return;
|
|
|
|
|
if (CheckAllPlayersLoaded())
|
|
|
|
|
{
|
|
|
|
|
// 모두가 로딩을 마쳤다면, 방장이 모든 클라이언트에게 신호를 보냅니다!
|
|
|
|
|
photonView.RPC("RpcStartCountdown", RpcTarget.All);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool CheckAllPlayersLoaded()
|
|
|
|
|
{
|
|
|
|
|
// 방에 있는 모든 플레이어의 "IsLoaded"가 true인지 확인
|
|
|
|
|
foreach (var player in PhotonNetwork.PlayerList)
|
|
|
|
|
{
|
|
|
|
|
if (player.CustomProperties.TryGetValue("IsLoaded", out object isLoaded))
|
|
|
|
|
{
|
|
|
|
|
if (!(bool)isLoaded) return false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return false; // 아직 로딩 안 된 사람이 있음
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true; // 전원 로딩 완료!
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RPC: 방장이 호출하지만, 이 방에 있는 모든 사람의 컴퓨터에서 실행되는 마법의 함수!
|
|
|
|
|
[PunRPC]
|
|
|
|
|
private void RpcStartCountdown()
|
2026-05-01 04:03:39 +09:00
|
|
|
{
|
2026-05-01 05:47:12 +09:00
|
|
|
// 방장의 신호를 받고 다 같이 동시에 코루틴을 시작합니다.
|
2026-05-01 04:07:22 +09:00
|
|
|
StartCoroutine(GameSet());
|
2026-05-01 04:03:39 +09:00
|
|
|
}
|
|
|
|
|
|
2026-05-01 04:07:22 +09:00
|
|
|
private IEnumerator GameSet()
|
2026-05-01 04:03:39 +09:00
|
|
|
{
|
2026-05-01 05:47:12 +09:00
|
|
|
// 1. 카운트다운
|
2026-05-01 04:07:22 +09:00
|
|
|
text.text = "3";
|
|
|
|
|
yield return new WaitForSeconds(1f);
|
|
|
|
|
text.text = "2";
|
|
|
|
|
yield return new WaitForSeconds(1f);
|
|
|
|
|
text.text = "1";
|
|
|
|
|
yield return new WaitForSeconds(1f);
|
|
|
|
|
text.text = "GO!";
|
2026-05-01 05:47:12 +09:00
|
|
|
|
2026-05-01 04:07:22 +09:00
|
|
|
readyPanel.SetActive(false);
|
2026-05-01 05:47:12 +09:00
|
|
|
|
|
|
|
|
// --- 게임 시작 ---
|
|
|
|
|
Gazuaa.isGameActive = true; // 스페이스바 입력 활성화!
|
|
|
|
|
|
|
|
|
|
// 2. 15초 동안 게임 진행 (대기)
|
|
|
|
|
yield return new WaitForSeconds(15f);
|
|
|
|
|
|
|
|
|
|
// --- 게임 종료 ---
|
|
|
|
|
Gazuaa.isGameActive = false; // 스페이스바 입력 차단!
|
|
|
|
|
|
|
|
|
|
readyPanel.SetActive(true);
|
|
|
|
|
text.text = "STOP!";
|
|
|
|
|
|
|
|
|
|
yield return new WaitForSeconds(2f); // 결과 발표 전 약간의 뜸 들이기
|
|
|
|
|
|
|
|
|
|
// 3. 방장만 대표로 가챠를 돌려서 결과를 뽑습니다.
|
|
|
|
|
if (PhotonNetwork.IsMasterClient)
|
|
|
|
|
{
|
|
|
|
|
DetermineWinner();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DetermineWinner()
|
|
|
|
|
{
|
|
|
|
|
Dictionary<int, int> finalCounts = new Dictionary<int, int>();
|
|
|
|
|
int totalAvatars = 0;
|
|
|
|
|
|
|
|
|
|
// 방에 있는 모든 사람의 최종 아바타 개수를 수집합니다.
|
|
|
|
|
foreach (var player in PhotonNetwork.PlayerList)
|
|
|
|
|
{
|
|
|
|
|
if (player.CustomProperties.TryGetValue("AvatarCount", out object countObj))
|
|
|
|
|
{
|
|
|
|
|
int count = (int)countObj;
|
|
|
|
|
finalCounts[player.ActorNumber] = count;
|
|
|
|
|
totalAvatars += count;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 아무도 스페이스바를 누르지 않았다면?
|
|
|
|
|
if (totalAvatars == 0)
|
|
|
|
|
{
|
|
|
|
|
photonView.RPC("RpcShowWinner", RpcTarget.All, -1);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 가챠 뽑기 로직 (1 ~ 총 아바타 개수)
|
|
|
|
|
int randomPick = Random.Range(1, totalAvatars + 1);
|
|
|
|
|
int currentSum = 0;
|
|
|
|
|
int winnerActorNumber = -1;
|
|
|
|
|
|
|
|
|
|
foreach (var kvp in finalCounts)
|
|
|
|
|
{
|
|
|
|
|
currentSum += kvp.Value;
|
|
|
|
|
if (randomPick <= currentSum)
|
|
|
|
|
{
|
|
|
|
|
winnerActorNumber = kvp.Key; // 당첨자 식별 번호!
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 모두에게 "이 사람이 당첨됐다!" 라고 쏩니다.
|
|
|
|
|
photonView.RPC("RpcShowWinner", RpcTarget.All, winnerActorNumber);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[PunRPC]
|
|
|
|
|
private void RpcShowWinner(int winnerActorNumber)
|
|
|
|
|
{
|
|
|
|
|
if (winnerActorNumber == -1)
|
|
|
|
|
{
|
|
|
|
|
text.text = "아무도 참여하지 않았습니다.";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 우승자 닉네임 찾기
|
|
|
|
|
string winnerName = "알 수 없음";
|
|
|
|
|
foreach (var p in PhotonNetwork.PlayerList)
|
|
|
|
|
{
|
|
|
|
|
if (p.ActorNumber == winnerActorNumber) winnerName = p.NickName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
text.text = $"우승: {winnerName}!";
|
|
|
|
|
|
|
|
|
|
// 화면에서 우승자의 아바타만 빼고 전부 어둡게/투명하게 만드는 연출 실행!
|
|
|
|
|
FindObjectOfType<Gazuaa>().HighlightWinner(winnerActorNumber);
|
2026-05-01 04:03:39 +09:00
|
|
|
}
|
2026-05-01 05:47:12 +09:00
|
|
|
}
|