화살 #1

Eunho Bae·2022년 6월 25일

화살 스킬 같은 경우 클라이언트에서 화살을 생성해서 날린다고 해도 서버에서 굳이 화살 좌표를 맞춰줘야 할지 선택을 해야하는데 와우 같은 게임의 경우 A플레이어가 몬스터를 대상으로 스킬을 쓴 후 바로 다른 지역으로 이동해서 사라지더라도 B플레이어가 뒤늦게 들어왔을 때 스킬만 덩그러니 발동되는 것을 볼 수 있다. 따라서 스킬도 서버에서 어느정도 관리를 해주는게 좋다.

Protocol.proto

message ObjectInfo {
  int32 objectId = 1;
  string name = 2;
  PositionInfo posInfo = 3;
}

message S_Spawn {
  repeated ObjectInfo objects = 1;
}

message S_EnterGame {
  ObjectInfo player = 1;
}

먼저 PlayerInfo를 ObjectInfo로 바꿔주고, playerId가 아닌 objectId로 바꿔주었다.

Server::GameObject

public class GameObject
    {
        public GameObjectType ObjectType { get; protected set; } = GameObjectType.None;
		public int Id
        {
			get { return Info.ObjectId; }
			set {  Info.ObjectId = value; }
        }

        public ObjectInfo Info { get; set; } = new ObjectInfo() { PosInfo = new PositionInfo() };
        public PositionInfo PosInfo { get; private set; } = new PositionInfo();

        public GameObject()
        {
            Info.PosInfo = PosInfo; // 따로 관리를 하다가 나중에 패킷을 보낼때 PositionInfo를 ObjectInfo의 PosInfo에 넣어주고 보냄.
        }

        public GameRoom Room { get; set; }

		public Vector2Int CellPos
		{
			get
			{
				return new Vector2Int(PosInfo.PosX, PosInfo.PosY);
			}

			set
			{
				PosInfo.PosX = value.x;
				PosInfo.PosY = value.y;
			}
		}

		public Vector2Int GetFrontCellPos()
        {
			return GetFrontCellPos(PosInfo.MoveDir);
        }

		public Vector2Int GetFrontCellPos(MoveDir dir)
		{
			Vector2Int cellPos = CellPos;

			switch (dir)
			{
				case MoveDir.Up:
					cellPos += Vector2Int.up;
					break;
				case MoveDir.Down:
					cellPos += Vector2Int.down;
					break;
				case MoveDir.Left:
					cellPos += Vector2Int.left;
					break;
				case MoveDir.Right:
					cellPos += Vector2Int.right;
					break;
			}

			return cellPos;
		}
	}

먼저 최상위 클래스인 GameObject를 만들고, 이를 상속하는 모든 오브젝트가 공유해서 사용할 수 있는 GetFrontCellPos, CellPos, Room, 그리고 패킷으로 보낼때 사용할 ObjectInfo, PositionInfo, 그리고 GameObjectType와 각 오브젝트마다 고유 id를 관리하는 Id를 정의해준다.

그리고 PositionInfo 위치정보는 따로 관리를 해줘서 GameObject 생성자에서 PosInfo에 넣어주도록 한다.

Server::Player

public class Player : GameObject
	{

		public ClientSession Session { get; set; }

        public Player()
        {
			ObjectType = GameObjectType.Player;
        }
	}

플레이어는 플레이어만 각자 세션을 가지고 있기 때문에 GameObject로 올려주지 않았다.

Server::Monster

    public class Monster : GameObject
    {
        public Monster()
        {
            ObjectType = GameObjectType.Monster;
        }
    }

Server::Projectile

   public class Projectile : GameObject
    {
        public Projectile()
        {
            ObjectType = GameObjectType.Projectile;
        }
    }

Server::Arrow

  public class Arrow : Projectile
    {
        public GameObject Owner { get; set; }

        public void Update()
        {

        }
    }

투사체는 여러 종류가 있을 수 있기 때문에 Projectile 상위 클래스를 하나 정의한 다음 Arrow가 상속하도록 정의했다.
Arrow는 자신이 어느 오브젝트 소속인지 알 수 있게 하여 추후 데미지 판정이나 아군이 쐈는지 등을 판별할때 Owner를 사용할 수 있다.

Server::ObjectManager

public class ObjectManager
	{
		public static ObjectManager Instance { get; } = new ObjectManager();

...

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

		// 맨 처음 비트는 부호비트
		// 그다음 7비트는 ObjectType
		// 나머지는 playerId
		int _counter = 0; // 게임오브젝트 개수

		public T Add<T>() where T : GameObject, new()
        {
			T gameObject = new T();

			lock(_lock)
            {
				gameObject.Id = GenerateId(gameObject.ObjectType);

				if(gameObject.ObjectType == GameObjectType.Player)
                {
					_players.Add(gameObject.Id, gameObject as Player);
                }
            }

			return gameObject;	
        }

		int GenerateId(GameObjectType type)
        {
			lock(_lock)
            {
				return ((int)type << 24) | (_counter++);
            }
        }

		public static GameObjectType GetObjectTypeById(int id)
        {
			int type = (id >> 24) & 0x7F; 
			return (GameObjectType)type;
        }

		public bool Remove(int objectId)
		{
			GameObjectType objectType = GetObjectTypeById(objectId);

			lock (_lock)
			{
				if(objectType == GameObjectType.Player)
					return _players.Remove(objectId);
			}

			return false;
		}

		public Player Find(int objectId)
		{
			GameObjectType objectType = GetObjectTypeById(objectId);

			lock (_lock)
			{
				if (objectType == GameObjectType.Player)
                {
					Player player = null;
					if (_players.TryGetValue(objectId, out player))
						return player;
				}
			}

			return null;
		}
	}
profile
개인 공부 정리

0개의 댓글