지난 포스팅에서는 퀘스트를 수락하고 진행과정을 추적하며 클리어하는 시스템을 구축해봤다.
이제 퀘스트를 DB에 저장해서 플레이어가 로그인할 때, 퀘스트를 불러와보자.
Quest DB 모델
[Table("Quest")]
public class QuestDb
{
public int QuestDbId { get; set; }
public int TemplateId { get; set; }
public bool IsFinish { get; set; }
public bool IsCleard{ get; set; }
public int QuestType { get; set; }
public ICollection<QuestGoalDb> Goals { get; set; }
[ForeignKey("Player")]
public int PlayerDbId { get; set; }
public PlayerDb Player { get; set; }
}
[Table("QuestGoal")]
public class QuestGoalDb
{
public int QuestGoalDbId { get; set; }
[ForeignKey("Quest")]
public int OnwerQuestDbId { get; set; }
public QuestDb Quest { get; set; }
public int TemplateId { get; set; }
public int Count { get; set; }
}
퀘스트의 DB 모델은 위와 같다. 퀘스트의 id를 저장하고 각 퀘스트마다 진행사항들을 QuestGoal이라는 테이블로 관리한다.
DB에서 Quest 불러오기 및 전송
DB에서 Quest를 불러오는 방법은 다음과 같다.
List<QuestDb> quests = db.Quests
.Where(q => q.PlayerDbId == playerInfo.PlayerDbId)
.Include(q => q.Goals)
.ToList();
foreach (QuestDb questDB in quests)
{
Quest quest = Quest.MakeQuest(questDB.TemplateId);
if (quest == null) return;
switch (quest.QuestType)
{
case QuestType.Battle:
BattleQuest battleQuest = (BattleQuest)quest;
foreach (var goal in questDB.Goals)
{
battleQuest.Update(new BattleQuestGoals() { enemyId = goal.TemplateId, count = goal.Count });
}
battleQuest.IsFinish = questDB.IsFinish;
break;
case QuestType.Collection:
CollectionQuest collectionQuest = (CollectionQuest)quest;
foreach (var goal in questDB.Goals)
{
collectionQuest.Update(new CollectionQuestGoals() { collectionId = goal.TemplateId, count = goal.Count });
}
collectionQuest.IsFinish = questDB.IsFinish;
break;
case QuestType.Enter:
EnterQuest enterQuest = (EnterQuest)quest;
foreach (var goal in questDB.Goals)
enterQuest.Update(goal.TemplateId);
quest.IsFinish = questDB.IsFinish;
break;
}
if(questDB.IsCleard == false)
MyPlayer.QuestInven.AddQuest(quest);
else
MyPlayer.QuestInven.FinishQuest(quest);
}
각 퀘스트 Type에 맞춰서 진행사항을 덮어써주고 플레이어의 QuestInven 메모리에 올려준다.
그리고 모든 퀘스트를 클라이언트에게 전송이 필요하다.
S_AllQuestList allQuestList = new S_AllQuestList();
List<Quest> allQuest = player.QuestInven.GetAllQuest();
{
foreach (var quest in allQuest)
{
QuestInfo questInfo = new QuestInfo
{
QuestId = quest.TemplateId,
QuestType = quest.QuestType,
IsFinish = quest.IsFinish,
IsCleared = false
};
if (quest.QuestType == QuestType.Battle || quest.QuestType == QuestType.Collection)
{
Dictionary<int, int> countDict = quest.QuestType == QuestType.Battle
? ((BattleQuest)quest).countDict
: ((CollectionQuest)quest).countDict;
foreach (var key in countDict.Keys)
{
questInfo.QuestGoal.Add(new QuestGoal { Id = key, Count = countDict[key] });
}
}
allQuestList.QuestList.Add(questInfo);
}
player.Session.Send(allQuestList);
}
allQuestList = new S_AllQuestList();
allQuest = player.QuestInven.GetAllFinishQuest();
{
foreach (var quest in allQuest)
{
QuestInfo questInfo = new QuestInfo
{
QuestId = quest.TemplateId,
QuestType = quest.QuestType,
IsFinish = quest.IsFinish,
IsCleared = true
};
if (quest.QuestType == QuestType.Battle || quest.QuestType == QuestType.Collection)
{
Dictionary<int, int> countDict = quest.QuestType == QuestType.Battle
? ((BattleQuest)quest).countDict
: ((CollectionQuest)quest).countDict;
foreach (var key in countDict.Keys)
{
questInfo.QuestGoal.Add(new QuestGoal { Id = key, Count = countDict[key] });
}
}
allQuestList.QuestList.Add(questInfo);
}
player.Session.Send(allQuestList);
}
두번에 나눠서 보내는 이유는 퀘스트를 이미 클리어한 퀘스트와 아닌 퀘스트를 분리해야하기 때문이다.
이렇게 해서 이제 DB에 저장만 하면 플레이어는 언제든지 DB에서 꺼내와 퀘스트를 갱신할 수 있다.
퀘스트를 수락 했을 때 DB 추가
퀘스트를 수락했을 때 DB 추가는 생각보다 쉬운 편이다. 그도 그럴것이 DB의 데이터를 수정하는 것이 아니라 그냥 add만 해주면 되기 때문이다.
public static void AddQuestNoti(Player player, Quest quest)
{
if (quest == null || player == null)
return;
var questGoals = new List<QuestGoalDb>();
switch (quest.QuestType)
{
case QuestType.Battle:
BattleQuest battleQuest = (BattleQuest)quest;
foreach (var key in battleQuest.countDict.Keys)
questGoals.Add(new QuestGoalDb { TemplateId = key, Count = battleQuest.countDict[key] });
break;
case QuestType.Collection:
CollectionQuest collectionQuest = (CollectionQuest)quest;
foreach (var key in collectionQuest.countDict.Keys)
questGoals.Add(new QuestGoalDb { TemplateId = key, Count = collectionQuest.countDict[key] });
break;
case QuestType.Enter:
EnterQuest enterQuest = (EnterQuest)quest;
questGoals.Add(new QuestGoalDb { TemplateId = enterQuest.cur, Count = 1 });
break;
}
QuestDb questDb = new QuestDb()
{
TemplateId = quest.TemplateId,
QuestType = (int)quest.QuestType,
IsFinish = quest.IsFinish,
PlayerDbId = player.PlayerDbId,
IsCleard = false,
Goals = questGoals
};
foreach (var goal in questGoals)
{
goal.Quest = questDb;
}
Instance.Push(() => {
using (AppDbContext db = new AppDbContext())
{
db.Quests.Add(questDb);
bool success = db.SaveChangesEx();
if (!success)
{
}
}
});
}
퀘스트를 클리어했을 경우 DB 변경
이미 DB에 저장된 퀘스트를 clear 상태로 변경해주어야 한다.
변경해 주면서 진행사항 역시 업데이트 해줘야한다.
public static void ClearQuestNoti(Player player, Quest quest)
{
if (quest == null || player == null)
return;
var questGoals = new List<QuestGoalDb>();
switch (quest.QuestType)
{
case QuestType.Battle:
BattleQuest battleQuest = (BattleQuest)quest;
foreach (var key in battleQuest.countDict.Keys)
questGoals.Add(new QuestGoalDb { TemplateId = key, Count = battleQuest.countDict[key] });
break;
case QuestType.Collection:
CollectionQuest collectionQuest = (CollectionQuest)quest;
foreach (var key in collectionQuest.countDict.Keys)
questGoals.Add(new QuestGoalDb { TemplateId = key, Count = collectionQuest.countDict[key] });
break;
case QuestType.Enter:
EnterQuest enterQuest = (EnterQuest)quest;
questGoals.Add(new QuestGoalDb { TemplateId = enterQuest.cur, Count = 1 });
break;
}
Instance.Push(() => {
using (AppDbContext db = new AppDbContext())
{
QuestDb qdb = db.Quests
.Include(q => q.Goals)
.Where(q => q.TemplateId == quest.TemplateId && !q.IsCleard && q.PlayerDbId == player.PlayerDbId)
.FirstOrDefault();
if (qdb != null)
{
qdb.IsCleard = true;
qdb.IsFinish = quest.IsFinish;
db.QuestGoals.RemoveRange(qdb.Goals);
foreach (var goal in questGoals)
{
goal.OnwerQuestDbId = qdb.QuestDbId;
db.QuestGoals.Add(goal);
}
db.Entry(qdb).State = EntityState.Modified;
bool success = db.SaveChangesEx();
if (!success)
{
}
}
}
});
}
퀘스트 진행사항 업데이트 DB
퀘스트 진행 사항을 DB에서 업데이트 하는 방식은 여러가지가 있을 수 있는데 필자는 플레이어가 로그아웃 or 연결이 끊어졌을 때, 퀘스트중 변동사항이 있는 퀘스트를 전부 DB에서 업데이트하는 형식으로 진행했다.
가령 퀘스트에서 진행사항이 변경 됐다면 밑의 HashSet에 담아두고 메모리에 가지고 있다가 로그아웃 할때 하나씩 꺼내서 업데이트 하는 것이다.
public HashSet<Quest> UpdateQuestSet = new HashSet<Quest>();
public void QuestSave()
{
List<Quest> allQuest = UpdateQuestSet.ToList();
foreach (var quest in allQuest)
{
DbTransaction.UpdateQuest(this, quest);
}
}
public static void UpdateQuest(Player player, Quest quest)
{
if (quest == null || player == null)
return;
var questGoals = new List<QuestGoalDb>();
switch (quest.QuestType)
{
case QuestType.Battle:
BattleQuest battleQuest = (BattleQuest)quest;
foreach (var key in battleQuest.countDict.Keys)
questGoals.Add(new QuestGoalDb { TemplateId = key, Count = battleQuest.countDict[key] });
break;
case QuestType.Collection:
CollectionQuest collectionQuest = (CollectionQuest)quest;
foreach (var key in collectionQuest.countDict.Keys)
questGoals.Add(new QuestGoalDb { TemplateId = key, Count = collectionQuest.countDict[key] });
break;
case QuestType.Enter:
EnterQuest enterQuest = (EnterQuest)quest;
questGoals.Add(new QuestGoalDb { TemplateId = enterQuest.cur, Count = 1 });
break;
}
Instance.Push(() => {
using (AppDbContext db = new AppDbContext())
{
QuestDb qdb = db.Quests
.Include(q => q.Goals)
.Where(q => q.TemplateId == quest.TemplateId && !q.IsCleard && q.PlayerDbId == player.PlayerDbId)
.FirstOrDefault();
if (qdb != null)
{
qdb.IsFinish = quest.IsFinish;
db.QuestGoals.RemoveRange(qdb.Goals);
foreach (var goal in questGoals)
{
goal.OnwerQuestDbId = qdb.QuestDbId;
}
db.QuestGoals.AddRange(questGoals);
db.Entry(qdb).State = EntityState.Modified;
bool success = db.SaveChangesEx();
if (!success)
{
}
}
}
});
}
이렇게 하면 모든 퀘스트를 순회할 필요 없이 변동된 퀘스트만 업데이트해서 속도를 향상시킬 수 있다.
'Unity > 온라인 RPG' 카테고리의 다른 글
[Unity 3D] 3D RPG 게임 테스트 플레이 영상 (0) | 2024.08.21 |
---|---|
[Unity 3D] 유저간 채팅 시스템 구현하기 (0) | 2024.08.14 |
[Unity 3D] 퀘스트 시스템 구현하기 (1) | 2024.08.06 |
[Unity 3D] 각종 사운드 추가 (0) | 2024.07.29 |
[Unity 3D] 보스 드래곤의 패턴 만들기 (0) | 2024.07.29 |