본문 바로가기
Unity/프로젝트

팀프로젝트 - 판타지 마블 (Fantasy marble) 02 - 기능 리뷰 (주사위)

by mynote6590 2025. 4. 8.

설계

주사위를 생성할 때 네트워크 동기화 부분에서 처음에 고민을 많이 했다. 처음 생각한 방식에는 2가지가 있었다.

  • 방식 1 : 첫번째 방식은 방장이 주사위를 생성하고 방장이 아닌 플레이어에게 전달해주는 방식
  • 방식 2 : 플레이어 개개인이 주사위를 생성하는 방식

두 방식중 첫번 째 방식을 사용해 만들기로 결정했는데 그 이유는 한명만 주사위를 만들고 그걸 공유하는 방식이 효율적이라고 생각했기 때문이다. 그리고 또 고려해야 할 점이 있었는데 랜덤으로 주사위의 결과값을 받는것을 RPC로 동기화를 한다면 각 로컬에서 다른 결과 값이 나오기 때문에 주사위를 굴린 사람만 랜덤 함수를 실행시키고 그에 대한 결과값을 RPC 동기화 시켜주어야 했다.

 

 

코드 설명

1. 주사위 생성 ( TurnBasedManager )

방장만 생성후 RPC를 이용해 나머지 플레이어에게 전달, 주사위 생성할 때 고유 번호 지정

private void Start()
{
    // 방장이 생성, 보내기
    if (PhotonNetwork.IsMasterClient == true)
    {
        _redDice = PhotonNetwork.Instantiate("DiceRed",new Vector3(7,0,-7), Quaternion.identity,0, new object[] { 0 }).GetComponent<DiceManager>();
        _blueDice = PhotonNetwork.Instantiate("DiceBlue", new Vector3(8, 0, -8), Quaternion.identity, 0, new object[] { 1 }).GetComponent<DiceManager>();
        _redDiceViewID = _redDice.photonView.ViewID;
        _blueDiceViewID = _blueDice.photonView.ViewID;
        _redDice._dicePlayerMove += PlayerMove;
        _blueDice._dicePlayerMove += PlayerMove;
        photonView.RPC("DiceSatting", RpcTarget.Others, _redDiceViewID, _blueDiceViewID);
    }
}

[PunRPC]
public void DiceSatting(int _redDiceView, int _blueView)
{
    _redDice = PhotonView.Find(_redDiceView).GetComponent<DiceManager>();
    _blueDice = PhotonView.Find(_blueView).GetComponent<DiceManager>();
    _redDice._dicePlayerMove += PlayerMove;
    _blueDice._dicePlayerMove += PlayerMove;
}

 


2. 주사위 굴리기 ( TurnBasedManager )

Dice함수를 호출한 사람만 Random 돌리고 그에 대한 결과만 동기화

 public void Dice()
 {
     int _redDiceNum = Random.Range(1,7); 
     int _blueDiceNum = Random.Range(1,7);
     _redDice._photonView.RPC("DiceStart", RpcTarget.All, _redDiceNum); // 빨간 주사위 애니메이션 실행
     _blueDice._photonView.RPC("DiceStart", RpcTarget.All, _blueDiceNum); // 파란 주사위 애니메이션 실행
 }

 

DiceManager

주사위 굴리는 애니메이션 - 랜덤으로 rotation 돌리기


[PunRPC]
public void DiceStart(int _diceNum)
{
    this._diceNum = _diceNum;
    _isRolling = true;
    _rb.AddForce(0, 5, 0, ForceMode.Impulse);
    _diceCor = StartCoroutine(RollingDice());
}
IEnumerator RollingDice()
{
    while (_isRolling == true)
    {
        transform.rotation = Quaternion.Euler(UnityEngine.Random.Range(0, 360), UnityEngine.Random.Range(0, 360), UnityEngine.Random.Range(0, 360));
        yield return new WaitForSeconds(0.1f);
    }
}

 

DiceManager

주사위 결과 보여주기 - 땅에 닿았을 때 미리 정해진 결과값에 맞게 rotation변경

public void OnCollisionEnter(Collision collision)
{
    if (collision.gameObject.tag == "DiceGround" && _isRolling == true)
    {
        _isRolling = false;
        StopCoroutine(RollingDice());
        _rb.velocity = Vector3.zero;
        ChangeRotation(_diceNum);
        _dicePlayerMove?.Invoke(diceID, _diceNum);
    }
}
public void ChangeRotation(int num)
{
    if (num == 1) { transform.rotation = one; }
    else if (num == 2) { transform.rotation = two; }
    else if (num == 3) { transform.rotation = three; }
    else if (num == 4) { transform.rotation = four; }
    else if (num == 5) { transform.rotation = five; }
    else if (num == 6) { transform.rotation = six; }
}

 


3. 플레이어 이동  ( TurnBasedManager )

2개의 주사위 애니메이션이 끝나고 결과를 통해 플레이어를 이동시킵니다.

아래의 코드는 땅에 닿으면 발생되는 이벤트, 이벤트가 실행될 때 배열을 이용해 2개의 주사위 애니메이션이 끝났는지 체크 하는 함수 입니다

public void PlayerMove(int diceKey, int diceNum)
{
    diceResults[diceKey] = diceNum;

    if ((diceResults[0] != null && diceResults[1] != null))
    {
        int total = diceResults[0].Value + diceResults[1].Value;
        ServerIngamePlayer._players[TurnMgr.currentTurn].RpcMovePlayer(total);
        diceResults[0] = null;
        diceResults[1] = null;
    }
}

아쉬운 점

랜덤으로 돌리는 rotation을 이용해 주사위 애니메이션을 만들엇는데 조금 부자연스러운 점이 있다.


전체 코드

 TurnBasedManager 

using System.Collections;
using System.Collections.Generic;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;

public class TurnBasedManager : MonoBehaviourPun
{
    private int?[] diceResults = new int?[2]; // 주사위 2개라고 가정
    DiceManager _redDice;
    DiceManager _blueDice;
    int _redDiceViewID;
    int _blueDiceViewID;


    private void Start()
    {
        // 방장이 생성, 보내기
        if (PhotonNetwork.IsMasterClient == true)
        {
            _redDice = PhotonNetwork.Instantiate("DiceRed",new Vector3(7,0,-7), Quaternion.identity,0, new object[] { 0 }).GetComponent<DiceManager>();
            _blueDice = PhotonNetwork.Instantiate("DiceBlue", new Vector3(8, 0, -8), Quaternion.identity, 0, new object[] { 1 }).GetComponent<DiceManager>();
            _redDiceViewID = _redDice.photonView.ViewID;
            _blueDiceViewID = _blueDice.photonView.ViewID;
            _redDice._dicePlayerMove += PlayerMove;
            _blueDice._dicePlayerMove += PlayerMove;
            photonView.RPC("DiceSatting", RpcTarget.Others, _redDiceViewID, _blueDiceViewID);
        }
    }

    [PunRPC]
    public void DiceSatting(int _redDiceView, int _blueView)
    {
        _redDice = PhotonView.Find(_redDiceView).GetComponent<DiceManager>();
        _blueDice = PhotonView.Find(_blueView).GetComponent<DiceManager>();
        _redDice._dicePlayerMove += PlayerMove;
        _blueDice._dicePlayerMove += PlayerMove;
    }

    public void Dice()
    {
        int _redDiceNum = Random.Range(1,7);
        int _blueDiceNum = Random.Range(1,7);
        _redDice._photonView.RPC("DiceStart", RpcTarget.All, _redDiceNum);
        _blueDice._photonView.RPC("DiceStart", RpcTarget.All, _blueDiceNum);
    }

    public void PlayerMove(int diceKey, int diceNum)
    {
        diceResults[diceKey] = diceNum;
    
        if ((diceResults[0] != null && diceResults[1] != null))
        {
            int total = diceResults[0].Value + diceResults[1].Value;
            ServerIngamePlayer._players[TurnMgr.currentTurn].RpcMovePlayer(total);
            diceResults[0] = null;
            diceResults[1] = null;
        }
    }
}

DiceManager

using System;
using System.Collections;
using Photon.Pun;
using UnityEngine;

public class DiceManager : MonoBehaviourPun, IPunInstantiateMagicCallback
{
    bool _isRolling;
    public event Action<int, int> _dicePlayerMove;
    public int diceID;
    Rigidbody _rb;
    Coroutine _diceCor;
    public PhotonView _photonView;
    public int _diceNum;
    Quaternion one = Quaternion.Euler(-90, 0, 0);
    Quaternion two = Quaternion.Euler(0, 0, 0);
    Quaternion three = Quaternion.Euler(0, 90, -90);
    Quaternion four = Quaternion.Euler(180, 0, -90);
    Quaternion five = Quaternion.Euler(180, 0, 0);
    Quaternion six = Quaternion.Euler(90, 0, 0);

    private void Start()
    {
        _isRolling = false;
        _photonView = GetComponent<PhotonView>();
        _rb = GetComponent<Rigidbody>();
    }
    [PunRPC]
    public void DiceStart(int _diceNum)
    {
        this._diceNum = 0;
        this._diceNum = _diceNum;
        _isRolling = true;
        _rb.AddForce(0, 5, 0, ForceMode.Impulse);
        _diceCor = StartCoroutine(RollingDice());
    }

    IEnumerator RollingDice()
    {
        while (_isRolling == true)
        {
            transform.rotation = Quaternion.Euler(UnityEngine.Random.Range(0, 360), UnityEngine.Random.Range(0, 360), UnityEngine.Random.Range(0, 360));
            yield return new WaitForSeconds(0.1f);
        }
    }
    public void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.tag == "DiceGround" && _isRolling == true)
        {
            _isRolling = false;
            StopCoroutine(RollingDice());
            _rb.velocity = Vector3.zero;
            ChangeRotation(_diceNum);
            _dicePlayerMove?.Invoke(diceID, _diceNum);
        }
    }

    public void ChangeRotation(int num)
    {
        if (num == 1) { transform.rotation = one; }
        else if (num == 2) { transform.rotation = two; }
        else if (num == 3) { transform.rotation = three; }
        else if (num == 4) { transform.rotation = four; }
        else if (num == 5) { transform.rotation = five; }
        else if (num == 6) { transform.rotation = six; }
    }

    public void OnPhotonInstantiate(PhotonMessageInfo info)
    {
        object[] data = info.photonView.InstantiationData;
        if (data != null && data.Length > 0)
        {
            diceID = (int)data[0];
        }
    }
}

'Unity > 프로젝트' 카테고리의 다른 글

개인 프로젝트 - 리스크 오브 레인 2 모작  (0) 2025.03.06