hit 판정 #1

Eunho Bae·2022년 6월 25일

목표

서버 쪽에서 mapId를 알면 맵 파일을 읽어와서 collision 판단을 할 수 있도록 구현하고 어떤 좌표를 기준으로 플레이어의 존재 유무를 판단하여 스킬 hit 판정을 해보자.

맵 정보를 서버에서도 가지도록 변경

Client::MapEditor

	private static void GenerateMap()
	{
		GenerateByPath("Assets/Resources/Map");
		GenerateByPath("../Common/MapData");
	}

	private static void GenerateByPath(string pathPrefix)
    {
		GameObject[] gameObjects = Resources.LoadAll<GameObject>("Prefabs/Map");
        
        ...
    }

유니티 에디터에서 Tools->GenerateMap을 클릭하면 생성되는 맵 파일 경로를 추가하였다. 서버 측에서 Common/MapData 폴더 내에 존재하는 맵 텍스트 파일을 읽어들일 것이다.

Server::Map

...

	public class Map
	{
		...

		public struct Vector2Int
        {
			public int x;
			public int y;

			public Vector2Int(int x, int y) { this.x = x; this.y = y; }

			public static Vector2Int up { get { return new Vector2Int(0, 1); } }
			public static Vector2Int down { get { return new Vector2Int(0, -1); } }
			public static Vector2Int left { get { return new Vector2Int(-1, 0); } }
			public static Vector2Int right { get { return new Vector2Int(1, 0); } }

			public static Vector2Int operator+(Vector2Int a, Vector2Int b)
            {
				return new Vector2Int(a.x + b.x, a.y + b.y);
            }
        }

		public void LoadMap(int mapId, string pathPrefix = "../../../../../Common/MapData")
		{
			string mapName = "Map_" + mapId.ToString("000");

			// Collision 관련 파일
			string text = File.ReadAllText($"{pathPrefix}/{mapName}.txt");
			StringReader reader = new StringReader(text);

			MinX = int.Parse(reader.ReadLine());
            
            ...
        }

Client에서 MapManager에 있는 코드를 통째로 Server에서 새로 만든 Map.cs에 붙여넣어주었고, 유니티 관련 코드는 삭제하였다. 2차원 좌표가 필요하기 때문에 Vector2Int 구조체를 추가로 정의해주었다.

Server::RoomManager

public GameRoom Add(int mapId)
	{
		GameRoom gameRoom = new GameRoom();
		gameRoom.Init(mapId);
        
        ...
    }
            

Server::GameRoom

	Map _map = new Map(); // 내가 소속 중인 맵

	public void Init(int mapId)
    {
		_map.LoadMap(mapId);
    }

Server::Program에서 RoomManager.Add(1)를 호출하여 게임 룸을 하나 만들고, 받아온 맵 id를 게임 룸 내부의 Init 함수의 인자로 전달하고, Map 객체의 LoadMap의 인자로 또 전달하여 "../../../../../Common/MapData"의 경로에 들어가서 맵 파일을 긁어오도록 구현했다.

플레이어 자료구조 변경

Server::GameRoom

		Dictionary<int, Player> _players = new Dictionary<int,Player>();

플레이어를 List로 처음에 담아두도록 구현했는데 이 방법이 아닌 playerId를 키로 이용하여 빠르게 찾을 수 있도록 딕셔너리로 바꿔주었다.

플레이어 좌표에 존재 여부 판정

Server::Map

		bool[,] _collision; // 벽 여부 관리
		Player[,] _players; // 플레이어 여부 관리. 추후 몬스터도 추가되면 Creature 부모 클래스로 바꿀 수 있음
        
        ...
        
         public bool CanGo(Vector2Int cellPos, bool checkObjects = true)
		{
			if (cellPos.x < MinX || cellPos.x > MaxX)
				return false;
			if (cellPos.y < MinY || cellPos.y > MaxY)
				return false;

			int x = cellPos.x - MinX;
			int y = MaxY - cellPos.y;
			return !_collision[y, x] && (!checkObjects || _players[y,x] == null);
		}
        
        ...
        
        public void LoadMap(int mapId, string pathPrefix = "../../../../../Common/MapData")
		{
			...
            
			_collision = new bool[yCount, xCount];
			_players = new Player[yCount, xCount];
            
            ...
        }

Map 클래스에서 기존에 벽 여부만 관리하고 갈 수 있는지 여부를 판단했으나 이번에는 플레이어 여부도 관리를 해주도록 해주었다. CanGo 매개변수에 checkObjects를 추가해서 false일 경우 플레이어 존재 여부를 체크하지 않도록 한다.

플레이어 좌표 갱신

Server::GameRoom

	public void HandleMove(Player player, C_Move movePacket)
        {
			if (player == null)
				return;

			lock(_lock)
            {
			  // TODO : 검증

				PositionInfo movePosInfo = movePacket.PosInfo; // 플레이어가 이동할 위치
				PlayerInfo info = player.Info; // 현재 플레이어 정보

				// 다른 좌표로 이동할 경우 이동 할 수 있는지 체크
				if(movePosInfo.PosX != info.PosInfo.PosX || movePosInfo.PosY != info.PosInfo.PosY)
                {
					if (_map.CanGo(new Vector2Int(movePosInfo.PosX, movePosInfo.PosY)) == false)
						return;	
                }

				info.PosInfo.State = movePosInfo.State;
				info.PosInfo.MoveDir = movePosInfo.MoveDir;
				_map.ApplyMove(player, new Vector2Int(movePosInfo.PosX, movePosInfo.PosY)); 

Server::Map

		public bool ApplyMove(Player player, Vector2Int dest)
        {
			PositionInfo posInfo = player.Info.PosInfo;

			if (CanGo(dest, true) == false)
				return false;

			return true;
        }

플레이어의 이동 패킷 좌표와 현재 좌표를 비교하여 이동하려고 하는 경우 갈 수 있는지 여부를 체크해서 만약 갈 수 있는 경우 ApplyMove 함수를 호출하여 현재 플레이어 위치를 null로 갱신하고 새로운 좌표에 플레이어 객체를 넣어주도록 한다.

profile
개인 공부 정리

0개의 댓글