Unity로 node.js WebSocket 통신하기 (socket.io,Mysql 이용해서 붐버맨 만들기)-4

2022. 7. 30. 18:38유니티 unity

 

 

이번엔 실시간 데이터 확인하기 위해서 어떤 게임을 만들까 생각하다가

붐버맨을 선택했습니다 

 

이미지 소스는

https://www.youtube.com/watch?v=8agb6x5RpOI 에서 다운로드하였습니다.

 

. 사용한 ui는 무료 에셋입니다

https://assetstore.unity.com/packages/2d/gui/icons/simple-ui-icons-147101

 

로그인 회원가입은 오목 그대로 썼고

방목록은 크레이지 아케이드처럼 6개씩 보이도록 했습니다.

예시 : 크레이지 아케이드

 

리스트에 방정보들을 담아서 보이도록 설정했습니다.

 

LobyManager.cs

더보기

 

using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[Serializable]
public class SeatInfo
{
    public string seatname;
    public int characterIdx;

    public int Idx;
    //0 준비안함
    //1 준비함
    //2 방장
}

[Serializable]
public class RoomInfo
{
    public string name;
    public int currentP;
    public int maxP;
    public SeatInfo[] seatInfos = new SeatInfo[4];
    public bool isPlaying;
    public int mapIdx;
}

public class LobyManager : MonoBehaviour
{
    public static LobyManager inst;

    private void Awake()
    {
        inst = this;
    }


    public RoomInfo[] roomInfos; //전체 방목록
    public List<RoomInfo> roomInfos2; //입장 가능한 방목록

    public int currentRoomPage; // 현재 페이지

    private int MaxRoomPage() // 최대페이지
    {
        if (roomInfos2.Count == 0)
        {
            return 0;
        }

        var count = roomInfos2.Count / 6;
        if (count <= 0) return 0;
        if (roomInfos2.Count % 6 == 0)
        {
            return count - 1;
        }

        return count;
    }

    [Header("방")] public Button[] roomBtn;
    public TextMeshProUGUI[] roomName;
    public TextMeshProUGUI[] roomCnt;

    [Header("버튼")] public Button nextBtn; //방 다음 목록
    public Button beforeBtn; //방 이전 목록

    [Header("채팅")] public Transform lobyChatParent;
    [SerializeField] private TMP_InputField lobyChatField;
    public List<GameObject> textObs;
    [Header("방만들기")] [SerializeField] private TMP_InputField lobyCreateRoomField;
    [SerializeField] private Toggle[] lobyCreateRoomToggle;


    public void OnEndEditEventMethod()
        //로비창 채팅
    {
        if (GameManager.inst.playerKey.UIChat.ChatEnd.triggered)
            //엔터키 누르면
        {
            UpdateChat();
            lobyChatField.ActivateInputField();
            lobyChatField.Select();
        }
    }

    public void UpdateChat()
        //채팅을 입력시 이벤트
    {
        if (lobyChatField.text.Equals(""))
        {
            return;
        }
        //아무것도없다면 리턴

        GameObject ob = ObjectPooler.SpawnFromPool("LobyChat", Vector3.zero);
        ob.GetComponent<TextMeshProUGUI>().text = $"> {GameManager.inst.Id} : {lobyChatField.text}";
        textObs.Add(ob);
        SocketManager.inst.socket.Emit("LobyChat", GameManager.inst.Id, lobyChatField.text);
        //딴사람들에게도 채팅내용을 받아야하니 이벤트를 보냅니다.
        lobyChatField.text = "";
    }

    public void LobyTextReset()
        //로비 채팅 대화청소
    {
        foreach (var ob in textObs)
        {
            ob.gameObject.SetActive(false);
        }

        textObs.Clear();
    }

    public void RoomClear()
        //방 전부 청소
    {
        for (int i = 0; i < roomBtn.Length; i++)
        {
            roomBtn[i].interactable = false;
            roomName[i].text = "";
            roomCnt[i].text = "";
        }

        nextBtn.interactable = false;
        beforeBtn.interactable = false;
    }

    void RoomSet(int currentPage)
        //방 설정
    {
        if (roomInfos2.Count == 0)
        {
            RoomClear();
            return;
        }

        if (currentPage < 0 || currentPage > MaxRoomPage())
        {
            Debug.LogError("오류");
            currentPage = 0;
        }

        int startIdx = (6 * currentPage);

        if (startIdx >= roomInfos2.Count)
        {
            Debug.LogError("오류");
            startIdx = 0;
        }

        for (int i = 0; i < 6; i++)
        {
            if (startIdx + i >= roomInfos2.Count)
            {
                roomBtn[i].interactable = false;
                roomName[i].text = "";
                roomCnt[i].text = "";
            }
            else
            {
                roomBtn[i].interactable = true;
                roomName[i].text = roomInfos2[startIdx + i].name;
                roomCnt[i].text = $"{roomInfos2[startIdx + i].currentP}/{roomInfos2[startIdx + i].maxP}";
            }
        }
    }

    public void RoomReset()
    {
        roomInfos2.Clear();

        for (int i = 0; i < roomInfos.Length; i++)
        {
            if (roomInfos[i].currentP >= roomInfos[i].maxP)
            {
                continue;
            }

            if (roomInfos[i].isPlaying)
            {
                continue;
            }

            roomInfos2.Add(roomInfos[i]);
        }

        if (currentRoomPage > MaxRoomPage() || currentRoomPage < 0)
        {
            currentRoomPage = 0;
        }

        if (currentRoomPage >= 1)
        {
            beforeBtn.interactable = true;
        }
        else
        {
            beforeBtn.interactable = false;
        }

        if (MaxRoomPage() > currentRoomPage)
        {
            nextBtn.interactable = true;
        }
        else
        {
            nextBtn.interactable = false;
        }

        RoomSet(currentRoomPage);
    }

    public void JoinBtn(int idx)
        //방 클릭
    {
        if ((currentRoomPage * 6) + idx >= roomInfos2.Count)
        {
            Debug.Log("오류");
            return;
        }

        GameManager.inst.loadingOb.SetActive(true);
        SocketManager.inst.socket.Emit("JoinRoomCheck", roomName[idx].text, GameManager.inst.Id);
    }

    public void MoveRoomBtn(int idx)
        //방목록버튼 클릭
    {
        if (idx == 0)
        {
            currentRoomPage--;
            RoomReset();
        }
        else
        {
            currentRoomPage++;
            RoomReset();
        }
    }

    // Start is called before the first frame update
    void Start()
    {
        SocketManager.inst.socket.OnUnityThread("LobyChatGet", data =>
            //로비 채팅
        {
            GameObject ob = ObjectPooler.SpawnFromPool("LobyChat", Vector3.zero);
            ob.GetComponent<TextMeshProUGUI>().text =
                $"{data.GetValue(0).GetString()} : {data.GetValue(1).GetString()}";
            textObs.Add(ob);
        });


        SocketManager.inst.socket.OnUnityThread("CreateRoom", data =>
            //방 생성
        {
            GameManager.inst.room = lobyCreateRoomField.text;
            RoomManager.inst.HostStartFunc();
        });
        SocketManager.inst.socket.OnUnityThread("RoomReset", data =>
            //방정보들 리셋
        {
            string s = data.GetValue(0).ToString();
            roomInfos = s != "" ? JsonConvert.DeserializeObject<RoomInfo[]>(s) : Array.Empty<RoomInfo>();
            RoomReset();
        });
        SocketManager.inst.socket.OnUnityThread("Join", data =>
            //참가
        {
            GameManager.inst.loadingOb.SetActive(false);
            GameManager.inst.lobyOb.SetActive(false);
            GameManager.inst.roomOb.SetActive(true);
            GameManager.inst.CreateRoomOb.SetActive(false);
            GameManager.inst.room = data.GetValue(0).GetString();
            string s = data.GetValue(1).ToString();
            RoomManager.inst.myRoomInfo = JsonConvert.DeserializeObject<RoomInfo>(s);
            RoomManager.inst.SlotReset(RoomManager.inst.myRoomInfo);
            GameManager.inst.characterIdx = 0;
            GameManager.inst.isPlaying = false;
            RoomManager.inst.ArrowSet(0);
            BoomberManager.inst.gameWait = 0;
        });
    }

    public void CreateFunc1()
        //방만들기 버튼
    {
        GameManager.inst.CreateRoomOb.SetActive(true);
        lobyCreateRoomToggle[0].isOn = true;
        lobyCreateRoomField.text = $"{GameManager.inst.Id}의 방";
        lobyCreateRoomField.Select();
    }

    public void CreatRoomBtn()
    //방생성버튼
    {
        if (lobyCreateRoomField.text == "")
        {
            return;
        }

        GameManager.inst.loadingOb.SetActive(true);
        SocketManager.inst.socket.Emit("CreateRoomCheck", lobyCreateRoomField.text, GameManager.inst.Id);
    }
}

 

 

서버 쪽에서 방정보들을 가지고 있다가 보내는 방식으로 했습니다.

 

server.js

더보기
 socket.on('JoinRoomCheck', (roomname, id) => {
    //방 참여

    if (roomname in Rooms && Rooms[roomname].currentP < Rooms[roomname].maxP) {

      socket.leave('Loby');
      socket.join(roomname)
      Users[socket.id].Room = roomname;

      for (var index = 0; index < 4; index++) {
        if (Rooms[roomname].seatInfos[index].seatname == "") {
          Rooms[roomname].seatInfos[index].seatname = id
          Rooms[roomname].seatInfos[index].characterIdx = 0
          Rooms[roomname].seatInfos[index].Idx = 0
          break;
        }

      }

      Rooms[roomname].currentP++;

      var roomcheck = {
        name: Rooms[roomname].name,
        currentP: Rooms[roomname].currentP,
        maxP: Rooms[roomname].maxP,
        seatInfos: Rooms[roomname].seatInfos,
        isPlaying: Rooms[roomname].isPlaying,
        mapIdx: Rooms[roomname].mapIdx

      }
      console.log(`${id} : 방참가 성공`)
      socket.emit('Join', roomname, roomcheck)
      socket.to(roomname).emit('SlotReset', roomcheck)
      RoomLobyInfoEmit()
    }
    else {
      console.log(`${id} : 방참가 실패`)
      socket.emit('Warnning', '방 참가 실패')
    }
  })


  socket.on('CreateRoomCheck', (roomName, nickname) => {
    //방 생성
    if (roomName in Rooms) {
      //방이 있는지 없는지 확인
      console.log(" 방이름 겹침!")
      socket.emit('Warnning', "방이름이 이미 있습니다")
      //방생성 실패
    }
    else {
      //방생성 성공
      socket.leave("Loby");
      socket.join(roomName);
      //들어갑니다.

      Users[socket.id].Room = roomName


      Rooms[roomName] = {
        name: roomName,
        currentP: 1,
        maxP: 4,
        seatInfos: [
          {
            seatname: nickname,
            characterIdx: 0,
            Idx: 2
          },
          {
            seatname: "",
            characterIdx: 0,
            Idx: 0
          },
          {
            seatname: "",
            characterIdx: 0,
            Idx: 0
          },
          {
            seatname: "",
            characterIdx: 0,
            Idx: 0
          },
        ],
        isPlaying: false,
        mapIdx: 0
      }
      //방 기본 설정

      console.log(`${roomName} : 방생성 성공`)

      socket.emit('CreateRoom')
      //성공했다고 이벤트를 보냅니다.
      RoomLobyInfoEmit()
      //방 목록을 전부 보내는 이벤트를 실행합니다.
    }

  })

방에 들어가서

슬룻을 강퇴 이동 닫기 열기 캐릭터 변경 맵 변경할 때마다

방정보 담아서 서버에 보내고 서버가 방안에 있는 클라이언트에게 정보를 보내는 방식으로 하시면 됩니다.

 

 

 

 

 

오브젝트풀링은 해당 유튜브 영상에 나와있는 함수 썼습니다.

https://www.youtube.com/watch?v=xiojw0lHzro 

 

 

맵은 타일 맵으로 깔아주고 타일이 벽, 파괴 가능한 벽과 이동 가능한 벽 스폰 지역으로 나눴습니다.

 

 

이동해야 하는 벽과 벽파괴시 아이템을 줘야 하는 부분 때문에 오브젝트로 만들어서 관리하는 게 편하다고 생각해서 타일 맵 확인하고 그 자리에 오브젝트를 생성시키도록 했습니다.

 

MapGenerator.cs

더보기
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Tilemaps;
using Random = UnityEngine.Random;

[Serializable]
public class MapInfo
{
    public int itemPowerCnt;
    public int itemBombCnt;
    public int itemSpeedCnt;
    public int playerMax;
}

public class MapGenerator : MonoBehaviour
{
    public Tilemap[] maps;
    public String[] tilePrefabs;
    public TileBase[] tiles;
    public TileBase respawnTile;
    public Vector3 offset;
    public List<Vector3> temp_pos;
    public List<int> itemList;
    public List<int> itemListTemp;
    public List<MapInfo> mapInfos;
    public List<GameObject> tileObs;
    public List<GameObject> brickObs;

    public GameObject groundTiles;
    public GameObject outWallTiles;

    public List<int> randomPos;

    public List<int> randomPosTemp;

    public void StartFunc()
    //맵 실행
    {
        BoomberManager.inst.itemDictionary.Clear();
        BoomberManager.inst.IsDead = false;
        BoomberManager.inst.moveMent.direction = Vector2.zero;
        GameManager.inst.roomOb.SetActive(false);
        GameManager.inst.playOb.SetActive(true);
        foreach (var tile in tileObs)
        {
            tile.SetActive(true);
        }

        groundTiles.SetActive(true);
        outWallTiles.SetActive(true);
        for (int i = 0; i < NetworkManager.inst.players.Length; i++)
        {
            if (NetworkManager.inst.players[i] != null)
            {
                NetworkManager.inst.players[i].SetActive(true);
            }
        }

        BoomberManager.inst.IsStart = true;
        GameManager.inst.isPlaying = true;
    }

    public void EndFunc()
    //맵 끝
    {
        BoomberManager.inst.StopAllCoroutines();
        BoomberManager.inst.timeText.text = "GameOver";
        MapDestory();
        groundTiles.SetActive(false);
        outWallTiles.SetActive(false);
        for (int i = 0; i < NetworkManager.inst.players.Length; i++)
        {
            if (NetworkManager.inst.players[i] != null)
            {
                Destroy(NetworkManager.inst.players[i]);
            }

            NetworkManager.inst.players[i] = null;
        }

        BoomberManager.inst.IsStart = false;
        GameManager.inst.roomOb.SetActive(true);
        GameManager.inst.playOb.SetActive(false);
        GameManager.inst.isPlaying = false;
        BoomberManager.inst.gameWait = 0;
        if (GameManager.inst.RoomHost)
        {
            SocketManager.inst.socket.Emit("PlayEnd", GameManager.inst.room);
        }

        BoomberManager.inst.player = null;
    }

    public void CharacterRandomFunc()
    //캐릭터 스폰지역 설정
    {
        randomPos.Clear();
        randomPosTemp.Clear();

        for (int i = 0; i < temp_pos.Count; i++)
        {
            randomPosTemp.Add(i);
        }

        while (randomPosTemp.Count != 0)
        {
            int ran = Random.Range(0, randomPosTemp.Count);
            randomPos.Add(randomPosTemp[ran]);
            randomPosTemp.RemoveAt(ran);
        }
    }

    public void MapCreate()
    //맵 생성
    {
        temp_pos.Clear();
        itemList.Clear();
        int itemAmount = 0;
        foreach (Vector3Int pos in maps[BoomberManager.inst.mapIdxGo].cellBounds.allPositionsWithin)
            //타일맵들을 전부 확인합니다.
        {
            //offset 부분


            
            if (!maps[BoomberManager.inst.mapIdxGo].HasTile(pos)) continue;
            // 해당 좌표에 타일이 없으면 넘어갑니다.
            
            TileBase tile = maps[BoomberManager.inst.mapIdxGo].GetTile(pos);
            // 해당 좌표의 타일을 얻습니다.
            Vector3 createPos = maps[BoomberManager.inst.mapIdxGo].CellToWorld(pos) + offset;

            if (tile == respawnTile)
            {
                temp_pos.Add(createPos);
                continue;
            }

            int idx = 0;

            for (int i = 0; i < tiles.Length; i++)
            {
                if (tile == tiles[i])
                {
                    idx = i;
                    break;
                }
            }

            GameObject ob = ObjectPooler.SpawnFromPool(tilePrefabs[idx], createPos, Quaternion.identity);
            //타일 오브젝트 생성
            tileObs.Add(ob);
            ob.SetActive(false);
            if (ob.TryGetComponent(out Brick b))
            {
                b.Idx = itemAmount;
                itemAmount++;
                itemList.Add(0);
                brickObs.Add(ob);
            }
        }

        BoomberManager.inst.respawnPos = temp_pos.ToList();
    }

    private void MapDestory()
    {
        brickObs.Clear();
        foreach (var ob in tileObs)
        {
            ob.SetActive(false);
        }

        foreach (var item in GameObject.FindGameObjectsWithTag("Item"))
        {
            item.SetActive(false);
        }

        tileObs.Clear();
    }


    public void ItemSetting()
    //벽에게 아이템을 설정해줍니다. 파괴하면 아이템을 줌
    {
        itemListTemp.Clear();
        int powerCnt = mapInfos[BoomberManager.inst.mapIdxGo].itemPowerCnt;
        int bombCnt = mapInfos[BoomberManager.inst.mapIdxGo].itemBombCnt;
        int speedCnt = mapInfos[BoomberManager.inst.mapIdxGo].itemSpeedCnt;

        itemListTemp = itemList.ToList();
        for (int i = 0; i < powerCnt; i++)
        {
            if (itemListTemp.Count == 0)
            {
                return;
            }

            int ran = Random.Range(0, itemListTemp.Count);
            itemList[ran] = 1;
            itemListTemp.RemoveAt(ran);
        }

        for (int i = 0; i < bombCnt; i++)
        {
            if (itemListTemp.Count == 0)
            {
                return;
            }

            int ran = Random.Range(0, itemListTemp.Count);
            itemList[ran] = 2;
            itemListTemp.RemoveAt(ran);
        }

        for (int i = 0; i < speedCnt; i++)
        {
            if (itemListTemp.Count == 0)
            {
                return;
            }

            int ran = Random.Range(0, itemListTemp.Count);
            itemList[ran] = 3;
            itemListTemp.RemoveAt(ran);
        }


        BoomberManager.inst.itemIdx = itemList.ToList();
    }
}

 

 

 

캐릭터 애니메이션은 블렌드 트리로 설정해서 왼쪽을 누르면 왼쪽을 보게 오른쪽을 누르면 오른쪽을 보게 합니다.

.

 

캐릭터 이동 부분은 Input system을 사용합니다.

 

 

 

 

MoveMentController.cs

더보기
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.InputSystem;

public class MoveMentController : MonoBehaviour
{
    public new Rigidbody2D rigidbody;
    public Vector2 direction = Vector2.down;
    public Animator ani;
    private static readonly int Horizontal = Animator.StringToHash("horizontal");
    private static readonly int Vertical = Animator.StringToHash("vertical");
    private static readonly int IsMoving = Animator.StringToHash("IsMoving");

    [SerializeField] private GameObject moveOb;
    [SerializeField] private float moveTime;
    
    private void OnMovement(InputValue value)
    //키보드 방향키 누르면 발동
    {
        if (BoomberManager.inst.IsStart == false || BoomberManager.inst.IsDead || BoomberManager.inst.player == null)
            //시작전이면 발동 X
        {
            return;
        }

        Vector2 v = value.Get<Vector2>();
        //현재 키보드 방향 구함
        
        if (v == Vector2.up)
            //위쪽
        {
            direction = Vector2.up;
            ani.SetFloat(Horizontal, 0);
            ani.SetFloat(Vertical, 1);
            ani.SetBool(IsMoving, true);
            NetworkManager.inst.myData.horizontal = 0;
            NetworkManager.inst.myData.vertical = 1;
            NetworkManager.inst.myData.isMoving = true;
        }
        else if (v == Vector2.down)
            //아래
        {
            ani.SetFloat(Horizontal, 0);
            ani.SetFloat(Vertical, -1);
            direction = Vector2.down;
            ani.SetBool(IsMoving, true);
            NetworkManager.inst.myData.horizontal = 0;
            NetworkManager.inst.myData.vertical = -1;
            NetworkManager.inst.myData.isMoving = true;
        }
        else if (v == Vector2.left)
            //왼쪽
        {
            ani.SetFloat(Horizontal, -1);
            ani.SetFloat(Vertical, 0);
            ani.SetBool(IsMoving, true);
            direction = Vector2.left;
            NetworkManager.inst.myData.horizontal = -1;
            NetworkManager.inst.myData.vertical = 0;
            NetworkManager.inst.myData.isMoving = true;
        }
        else if (v == Vector2.right)
            //오른쪽
        {
            ani.SetFloat(Horizontal, 1);
            ani.SetFloat(Vertical, 0);
            ani.SetBool(IsMoving, true);
            direction = Vector2.right;
            NetworkManager.inst.myData.horizontal = 1;
            NetworkManager.inst.myData.vertical = 0;
            NetworkManager.inst.myData.isMoving = true;
        }
        else if (v == Vector2.zero)
            //움직임 키를 뗌
        {
            ani.SetBool(IsMoving, false);
            direction = Vector2.zero;
            NetworkManager.inst.myData.isMoving = false;
            string s = JsonConvert.SerializeObject(NetworkManager.inst.myData);
            SocketManager.inst.socket.Emit("Playmove", GameManager.inst.room, BoomberManager.inst.playerIdx, s);
        }
    }

    private void Update()
    {
        if (BoomberManager.inst.IsStart == false || BoomberManager.inst.IsDead || BoomberManager.inst.player == null)
        {
            return;
        }

        if (direction != Vector2.zero)
            //벽을 이동시키는 함수
        {
            BrickMoveCheck();
        }
    }

    private void BrickMoveCheck()
    {
        RaycastHit2D check = Physics2D.Raycast(BoomberManager.inst.player.transform.position, direction, 1f,
            BoomberManager.inst.brickMask);
        //바라보는쪽에 이동 가능한 벽이 있다면
        if (!check)
        {
            moveOb = null;
            moveTime = 0;
        }
        else
        {
            //0.2초동안 벽을 향해서 이동한다면 이동
            if (check.transform.CompareTag("Brick_Move"))
            {
                if (moveOb != check.transform.gameObject)
                    //혹시 같은 벽이 아니라면 버그
                {
                    moveTime = 0;
                    moveOb = check.transform.gameObject;
                }
                else
                {
                    moveTime += Time.deltaTime;
                    if (moveTime >= 0.2f)
                    {
                        moveTime = 0;
                        if (!Physics2D.OverlapCircle(check.transform.position + (Vector3)direction, 0.2f,
                                BoomberManager.inst.moveCheckMask))
                        {
                            if (moveOb.TryGetComponent(out Brick b))
                            {
                                if (b.isMoving == false)
                                {
                                    b.Move(direction);
                                    var position = b.transform.position;
                                    SocketManager.inst.socket.Emit("BrickMove", GameManager.inst.room, position.x,
                                        position.y, direction.x, direction.y, b.Idx);
                                }
                            }
                        }
                    }
                }
            }
        }
    }


    private void FixedUpdate()
    //움직이는 부분
    {
        if (BoomberManager.inst.IsStart == false)
        {
            return;
        }

        for (int i = 0; i < NetworkManager.inst.players.Length; i++)
            //현재 방에있는 플레이어들의 정보를 가져옵니다.
        {
            if (BoomberManager.inst.playerIdx == i)
            {
                continue;
            }

            if (NetworkManager.inst.players[i] != null)
                //해당 플레이어가 존재한다면 실행합니다.
            {
                Vector3 pos = new Vector3(NetworkManager.inst.playerDatas[i].pos_x,
                    NetworkManager.inst.playerDatas[i].pos_y);
                float horizontal = NetworkManager.inst.playerDatas[i].horizontal;
                float vertical = NetworkManager.inst.playerDatas[i].vertical;
                bool move = NetworkManager.inst.playerDatas[i].isMoving;
                if (NetworkManager.inst.players[i].TryGetComponent(out CharacterInfo info))
                {
                    info.ani.SetFloat(Horizontal, horizontal);
                    info.ani.SetFloat(Vertical, vertical);
                    info.ani.SetBool(IsMoving, move);
                }

                NetworkManager.inst.players[i].transform.position = pos;

                // NetworkManager.inst.players[i].transform.position =
                //     Vector3.Lerp(NetworkManager.inst.players[i].transform.position, pos, Time.deltaTime * 20);
            }
        }

        if (direction == Vector2.zero)
        {
            return;
        }

        if (BoomberManager.inst.IsDead)
        {
            return;
        }

        Vector2 position = rigidbody.position;
        Vector2 translation = direction * (BoomberManager.inst.Speed * Time.fixedDeltaTime);
        rigidbody.MovePosition(position + translation);
        var position1 = rigidbody.position;
        NetworkManager.inst.myData.pos_x = position1.x;
        NetworkManager.inst.myData.pos_y = position1.y;
        string s = JsonConvert.SerializeObject(NetworkManager.inst.myData);
        SocketManager.inst.socket.Emit("Playmove", GameManager.inst.room, BoomberManager.inst.playerIdx, s);
        //본인의 정보를 같은 방에있는 클라이언트에게 보냅니다.
        
    }

    public void Start()
    {
        SocketManager.inst.socket.OnUnityThread("Playmove", data =>
        //유닛의 정보를 받습니다.
        {
            NetworkManager.inst.playerDatas[data.GetValue(0).GetInt32()] =
                JsonConvert.DeserializeObject<PlayerData>(data.GetValue(1).ToString());
        });
        SocketManager.inst.socket.OnUnityThread("BrickMove", data =>
        //벽을 이동시킵니다.
        {
            GameObject ob = BoomberManager.inst.mapGenerator.brickObs[data.GetValue(0).GetInt32()];
            if (ob.TryGetComponent(out Brick b))
            {
                b.transform.position = new Vector3(data.GetValue(1).GetSingle(), data.GetValue(2).GetSingle());
                b.Move(new Vector3(data.GetValue(3).GetSingle(), data.GetValue(4).GetSingle()));
            }
        });
    }
    
}

 

 

 

 

키를 누르면 폭탄이 나오게 하고 폭탄 범위 안에 다른 폭탄이 있다면 같이 터지게 해야 합니다.

 

BoomController.cs

더보기
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.InputSystem;

public class BoomController : MonoBehaviour
{
    

    public void Start()
    {
        SocketManager.inst.socket.OnUnityThread("Bomb", data =>
        {
            GameObject bomb = ObjectPooler.SpawnFromPool(BoomberManager.inst.bombPrefab, new Vector3(data.GetValue(3).GetSingle(),data.GetValue(4).GetSingle()), quaternion.identity);
            if (bomb.TryGetComponent(out Boom component))
            {
                component.BoomFunc(data.GetValue(0).GetSingle(),data.GetValue(1).GetInt32(),data.GetValue(2).GetInt32());
            }
        });
    }

    private void OnBomb(InputValue value)
    //스페이스를 누르면 발동
    {
        if (BoomberManager.inst.IsStart == false||BoomberManager.inst.IsDead)
            //시작을 안했거나 죽으면 발동 X
        {
            return;
        }
        
        if (!BoomberManager.inst.playChat.isFocused&&BoomberManager.inst.bombsRemaining >0)
            //채팅중에는 폭탄이 안나오고 설치할 수 있는 폭탄 개수 체크
        {
            Vector2 position = BoomberManager.inst.player.transform.position;
            //현재 위치를 저장합니다.
            position.x = Mathf.Round(position.x);
            position.y = Mathf.Round(position.y);
            //타일안에 정확하게 들어가야 하기 때문에 반올림합니다.

            if (Physics2D.OverlapCircle(position, 0.2f, BoomberManager.inst.explodeMask))
                //그 타일에 폭탄이 있다면 설치 X
            {
                return;
            }
       
            
            BoomberManager.inst.bombsRemaining--;
            GameObject bomb = ObjectPooler.SpawnFromPool(BoomberManager.inst.bombPrefab, position, Quaternion.identity);
            SocketManager.inst.socket.Emit("Bomb",GameManager.inst.room,BoomberManager.inst.bombFuseTime,BoomberManager.inst.Power,BoomberManager.inst.playerIdx,position.x,position.y);
            if (bomb.TryGetComponent(out Boom component))
            {
                component.BoomFunc(BoomberManager.inst.bombFuseTime,BoomberManager.inst.Power,BoomberManager.inst.playerIdx);
            }
            
        }
    }
    
}

 

Boom.cs

더보기
using System.Collections;
using Unity.Mathematics;
using UnityEngine;

public class Boom : MonoBehaviour
{
    private int bombPower;
    public new BoxCollider2D collider;
    public int playerIdx;
    Collider2D[] b = new Collider2D[2];
    public bool isDead = false;

    public void BoomFunc(float fuseTime, int _power, int idx)
    {
        
        isDead = false;
        bombPower = _power;
        playerIdx = idx;
        StartCoroutine(PlaceBomb(fuseTime));
    }

    private IEnumerator PlaceBomb(float fuseTime)
    //시간이 지나면 터짐
    {
        yield return YieldInstructionCache.WaitForSeconds(fuseTime);
        DestroyFunc();
    }

    private void DestroyFunc()
    {
        Vector2 pos = transform.position;

        GameObject explosion =
            ObjectPooler.SpawnFromPool(BoomberManager.inst.explosionPreFabs[0], pos, quaternion.identity);


        Explode(pos, Vector2.up, bombPower);
        //위로 터짐
        Explode(pos, Vector2.down, bombPower);
        //아래로 터짐
        Explode(pos, Vector2.left, bombPower);
        //왼쪽
        Explode(pos, Vector2.right, bombPower);
        //오른쪽
        Dead();
    }

    private void Dead()
    {
        if (isDead)
        {
            return;
        }

        if (BoomberManager.inst.playerIdx == playerIdx)
            //자기 폭탄이라면 개수가 다시 채워지게
        {
            BoomberManager.inst.bombsRemaining++;
        }

        isDead = true;
        gameObject.SetActive(false);
        collider.isTrigger = true;
    }

    private void OnTriggerExit2D(Collider2D other)
    {
        if (other.CompareTag("Player"))
        {
            if (Physics2D.OverlapCircleNonAlloc(transform.position, 0.1f, b, BoomberManager.inst.playerMask) == 0)
                //유닛이 빠져나가면 폭탄 위를 못 지나가게 설정
            {
                collider.isTrigger = false;
            }
        }
    }



    private void Explode(Vector2 pos, Vector2 direction, int length)
    //폭파 함수
    {
        if (length <= 0)
            //길이가 끝나면 끝
        {
            return;
        }

        pos += direction;
        //위치를 방향을 더합니다.

        Collider2D hit = Physics2D.OverlapCircle(pos, 0.2f, BoomberManager.inst.boomMask);
        //위치에 뭐가있는지 확인합니다.

        if (hit != null)
        {
            if (hit.CompareTag("Wall"))
                //벽이라면 아무것도 안합니다.
            {
                return;
            }
            else if (hit.CompareTag("Brick") || hit.CompareTag("Brick_Move"))
                //파괴가능한 벽이거나 이동가능한 벽이라면
            {
                if (hit.transform.TryGetComponent(out Brick b))
                {
                    b.Func();
                    //파괴애니메이션을 보여주고 파괴시킴
                }

                return;
            }
            else if (hit.CompareTag("Item"))
                //아이템이라면 파괴
            {
                hit.gameObject.SetActive(false);
            }
            else if (hit.CompareTag("Bomb"))
                //같은 폭탄이라면
            {
                if (hit.transform.TryGetComponent(out Boom b))
                {
                    if (b.isDead == true)
                    {
                        return;
                    }

                    b.Dead();
                    //해당 폭탄을 즉시 삭제시킵니다.
                }

                GameObject explosionOb =
                    ObjectPooler.SpawnFromPool(BoomberManager.inst.explosionPreFabs[1], pos, quaternion.identity);
                //폭파 이펙트 생성
                
                explosionOb.transform.rotation =
                    Quaternion.AngleAxis(SetDirection(direction), Vector3.forward);
                //각도 설정
                
                int power = hit.GetComponent<Boom>().bombPower;
                //해당 폭탄의 파워를 가져옵니다.
                if (direction == Vector2.up)
                    //현재 방향이 위쪽이라면 위 왼쪽 오른쪽
                {
                    Explode(pos, Vector2.up, power);
                    Explode(pos, Vector2.left, power);
                    Explode(pos, Vector2.right, power);
                }
                else if (direction == Vector2.down)
                {
                    Explode(pos, Vector2.down, power);
                    Explode(pos, Vector2.left, power);
                    Explode(pos, Vector2.right, power);
                }
                else if (direction == Vector2.left)
                {
                    Explode(pos, Vector2.up, power);
                    Explode(pos, Vector2.down, power);
                    Explode(pos, Vector2.left, power);
                }
                else if (direction == Vector2.right)
                {
                    Explode(pos, Vector2.up, power);
                    Explode(pos, Vector2.down, power);
                    Explode(pos, Vector2.right, power);
                }

                return;
            }
        }

        GameObject explosionOb2 = null;

        explosionOb2 = ObjectPooler.SpawnFromPool(length > 1 ? BoomberManager.inst.explosionPreFabs[1] : BoomberManager.inst.explosionPreFabs[2], pos, Quaternion.identity);

        explosionOb2.transform.rotation = Quaternion.AngleAxis(SetDirection(direction), Vector3.forward);

        length--;

        if (length <= 0)
        {
            return;
        }

        Explode(pos, direction, length);
    }
    
    private float SetDirection(Vector2 direction)
        //폭파 이펙트 각도 설정
    {
        return Mathf.Atan2(direction.y, direction.x)*Mathf.Rad2Deg;
    }
}

 

폭파 이펙트 오브젝트에 플레이어가 닿으면 죽게 설정합니다.

using UnityEngine;

public class Explosion : MonoBehaviour
{
    private void OnTriggerEnter2D(Collider2D col)
    {
        if (BoomberManager.inst.IsStart && col.CompareTag("Player") && BoomberManager.inst.player == col.gameObject)
        {
            BoomberManager.inst.Dead();
        }
    }
}

 

이동 동기화를 보낼 때 데이터를 묶어서 보내는데

현재 위치와 애니메이션 변수만 보냈습니다

 

[Serializable]
public class PlayerData
{
    public float pos_x;
    //현재위치 x
    public float pos_y;
    //현재위치 y
    
    
    
    public float horizontal;
    public float vertical;
    public bool isMoving;
    public bool isDead;
    //애니메이션
}

 

socket.io를 이용해서 테스트 용도로 실시간으로 데이터를 받아서 플레이하는 게임을 만들어 봤습니다.

간단한 실시간 대전이라면 충분히 무리 없다고 생각이 듭니다.

 

 

깃허브주소

https://github.com/wolstar415/unity_boomberman