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
'유니티 unity' 카테고리의 다른 글
Unity Unitask 사용법 (0) | 2022.08.10 |
---|---|
unity(유니티) vector 함수 전부 알기 (0) | 2022.08.03 |
유니티 Addressable (사용법,서버에서 받기) (0) | 2022.07.26 |
Unity로 node.js WebSocket 통신하기 (socket.io,Mysql 이용해서 오목게임 만들기)-3 (0) | 2022.07.25 |
Unity로 node.js WebSocket 통신하기 (socket.io를 이용해서 룸형식 채팅방 구현)-2 (4) | 2022.07.23 |