Data & Config

Eunho Bae·2022년 6월 25일

Newtonsoft.Json

Server프로젝트 우클릭 후 Manage NuGet Packages 클릭. 그리고 Newtonsoft.Json 설치

분석

Protocol.proto

enum SkillType {
  SKILL_NONE = 0;
  SKILL_AUTO = 1;
  SKILL_PROJECTILE = 2;
}

StatData.json

{
  "stats": [
    {
      "level": "1",
      "maxHp": "200",
      "attack": "20",
      "totalExp": "0"
    },
    {
      "level": "2",
      "maxHp": "250",
      "attack": "25",
      "totalExp": "10"
    },
    {
      "level": "3",
      "maxHp": "300",
      "attack": "30",
      "totalExp": "20"
    }
  ]
} 

SkillData.json

{
    "skills": [
        {
            "id": "1",
            "name": "평타", 
            "cooldown": "0.2",
            "damage": "10",
            "skillType": "SkillAuto"
        },
        {
            "id": "2",
            "name": "화살 공격",
            "cooldown": "0.2",
            "damage": "5",
            "skillType": "SkillProjectile",
            "projectile": {
                "name": "화살",
                "speed": "20.0",
                "range": "10",
                "prefab": "Creature/Arrow"
            }
        }
    ]
} 

Server::Data.Contents

#region Stat
	[Serializable]
	public class Stat
	{
		public int level;
		public int maxHp;
		public int attack;
		public int totalExp;
	}

	[Serializable]
	public class StatData : ILoader<int, Stat>
	{
		public List<Stat> stats = new List<Stat>();

		public Dictionary<int, Stat> MakeDict()
		{
			Dictionary<int, Stat> dict = new Dictionary<int, Stat>();
			foreach (Stat stat in stats)
				dict.Add(stat.level, stat);
			return dict;
		}
	}
	#endregion

	#region Skill
	[Serializable]
	public class Skill
	{
		public int id;
		public string name;
		public float cooldown; // 스킬 몇번마다 쓸 수 있는지
		public int damage;
		public SkillType skillType;
		public ProjectileInfo projectile;
	}

	public class ProjectileInfo // SkillType이 SKILL_PROJECTILE일 때 
	{
		public string name;
		public float speed;
		public int range; // 투사체 최대 갈 수 있는 거리
		public string prefab; // 프리팹 경로
	}

	[Serializable]
	public class SkillData : ILoader<int, Skill>
	{
		public List<Skill> skills = new List<Skill>();

		public Dictionary<int, Skill> MakeDict()
		{
			Dictionary<int, Skill> dict = new Dictionary<int, Skill>();
			foreach (Skill skill in skills)
				dict.Add(skill.id, skill);
			return dict;
		}
	}
	#endregion

Server::ConfigManager

[Serializable]
	public class ServerConfig
	{
		public string dataPath;
		// 최대 동접 유저 수, 포트 번호 등등
	}

	public class ConfigManager
	{
		public static ServerConfig Config { get; private set; }

		public static void LoadConfig()
		{
			string text = File.ReadAllText("config.json");
			Config = Newtonsoft.Json.JsonConvert.DeserializeObject<ServerConfig>(text);
		}
	}

config.json

{
  "dataPath": "../../../../../Client/Assets/Resources/Data"
}

처음 서버를 실행하면 ConfigManager의 LoadConfig() 함수가 실행된다.
이 함수는 서버 실행파일가 위치한 폴더에 있는 config.json에서 string으로 가져오게 된다.

가져온 string을 DeserializeObject로 변환해주면 아래 사진처럼 문자열이 Config에 저장되는 것을 볼 수 있다.

Config에 저장된 경로에는 SkillData.json와 StatData.json이 저장되어 있다.
그리고 DataManager에서 LoadData() 함수를 실행시키는데, json 파일에 있는 데이터를 string으로 전부 가져와서 StatData, SkillData의 형식에 맞게 반환하면 StatData 클래스의 stats과 SkillData 클래스의 skills 리스트에 알아서 저장이 되고, 그 안에서 하나씩 빼온 후 Stat의 경우 레벨 마다 HP나 공격력 등이 어떤지 정의되어 있기 떄문에 딕셔너리에 각각 level과 스킬 id를 키로 해서 넣어준 후 반환하여 DataManager가 모든 스탯과 스킬 정보를 가지고 있게 만들었다.

Server::GameRoom

	public void HandleSkill(Player player, C_Skill skillPacket)
	{
		...


				Data.Skill skillData = null;
				if (DataManager.SkillDict.TryGetValue(skillPacket.Info.SkillId, out skillData) == false)
					return;

				switch (skillData.skillType)
				{
					case SkillType.SkillAuto:
						{
							Vector2Int skillPos = player.GetFrontCellPos(info.PosInfo.MoveDir);
							GameObject target = Map.Find(skillPos);
							if (target != null)
							{
								Console.WriteLine("Hit GameObject !");
							}
						}
						break;
					case SkillType.SkillProjectile:
						{
							Arrow arrow = ObjectManager.Instance.Add<Arrow>();
							if (arrow == null)
								return;

							arrow.Owner = player;
							arrow.Data = skillData; // 어떤 스킬로 인해 화살이 만들어졌나
							arrow.PosInfo.State = CreatureState.Moving;
							arrow.PosInfo.MoveDir = player.PosInfo.MoveDir;
							arrow.PosInfo.PosX = player.PosInfo.PosX;
							arrow.PosInfo.PosY = player.PosInfo.PosY;
							EnterGame(arrow);
						}
						break;
				}

스킬 패킷을 서버가 받으면 어떤 스킬인지 스킬 아이디를 가지고 SkillDict에 저장된 스킬데이터를 가져온 후 switch문으로 분기하여 스킬타입에 따라 다르게 처리하도록 로직을 구성한다.
추가로 스킬 패킷을 가져와서 arrow의 Data에 어떤 스킬로 인해 화살이 만들어졌는지 Projectile 클래스에 Data를 만들어서 알 수 있게 하였다. 이렇게 하면 스킬에 정의된 투사체 속도, 거리, 공격력 등을 알 수 있기 때문에 데미지 판정에도 활용할 수 있으며 x밀리초 마다 한번씩 발사되도록 로직을 짤 때 도움을 받을 수도 있다.

Server::Arrow

		long _nextMoveTick = 0;

		public override void Update()
		{
			if (Data == null || Data.projectile == null || Owner == null || Room == null)
				return;

			if (_nextMoveTick >= Environment.TickCount64)
				return;

			long tick = (long)(1000 / Data.projectile.speed);
			_nextMoveTick = Environment.TickCount64 + tick;

			...
		}
profile
개인 공부 정리

0개의 댓글