묻고 답해요
145만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
포톤 공부에 대해서 질문드립니다
안녕하세요 루키스 선생님 선생님 강의를 들을 때마다 느끼지만, 여러 번 듣고, 미리 기본을 다지고 오면 얻어가는 것이 많다고 느꼈습니다.지금 서버를 공부하고 있는데, 꽤 난이도가 있고 너무 막연한 느낌이 들었습니다. 내가 실제 게임에 연동할 수 있을까? 실제로 사용가능한가? 등등.. (아직 연동까지는 듣지 못했고, 서버 파트만 들었습니다.) 그러다 포톤이라는 서버프로그램을 알게 되어서 포톤을 공부해고 인강을 들을까 생각이 드는데 선생님 생각은 어떠신지 궁금합니다
-
미해결[실전 게임 코드 리뷰] 유니티 클리커 게임
Manager의 Start() 메서드 관련 질문입니다.
GameScene.cs 에서Managers.UI.ShowPopupUI<UI_TitlePopup>();위의 Manager 클래스의 프로퍼티 UI 를 반환받는 과정에서 이미 Manager 클래스의 Init() 메서드를 호출하는 것으로 확인했습니다. 그럼에도 불구하고 Manager 클래스의 Start() 메서드에서 Init()을 또 호출하는 특별한 이유가 있나요?Start() 에서 Init()을 호출하는 시점의 s_instance 멤버 변수 null 체크를 해보니 이미 최상단에 말씀드린 과정에서 Manager가 생성되어 전부 null이 아니라고 로그가 찍힙니다. (사실상 Start()를 지워도 문제 없이 Manager 오브젝트는 생성됨)물론 다른 Manager들도 위의 과정에서 new 키워드로 전부 생성 되었고요.그렇다면 Start() 메서드에서 Init()을 호출하는 것은 사실상 불필요한 호출이고, 정말 만약에라도 Manager의 Start() 가 실행되기 전까지 s_instance 가 null 일 경우가 생길 수 없는 것 같아서요.제가 못 본 다른 이유가 있는 걸까요?
-
미해결[실전 게임 코드 리뷰] 유니티 클리커 게임
BaseScene 관련 질문
GameObject go = GameObject.Find("EventSystem"); if (go == null) Managers.Resource.Instantiate("UI/EventSystem").name = "@EventSystem";BaseScene.csInit 메서드 안의 46번째 줄입니다.EventSystem을 프리팹화 시켜서 하이어라키상에 없으면 생성하도록 만드셨는데, 혹시 이렇게 방어적으로 코드를 작성한 이유가 따로 있을까요?Scene 이동도 없어서 파괴될 일은 전혀 없을 것 같은데, 프리팹까지 빼서 null 체크 부분 코딩을 한 이유가 궁금합니다.
-
미해결[유니티 레벨 업!] 모듈식으로 개발하는 퀘스트&업적 시스템
QuestSystemSaveTest 수강 후 오류
안녕하세요 선생님강의를 열심히 따라 듣고있던 중 세이브 & 로드 구현하고 테스트하는 와중 에러메세지 때문에 몇일 고민하다가 도저히 안되겠어서 글 남깁니다어딘가 오타가있는건 아닌지 꼼꼼히 확인해봤는데 제눈엔 도저히 찾질 못하겠습니다 ㅜㅜ우선 에러메세지는 다음과 같습니다세이브 테스트 오브젝트에 스크립트도 잘 부착하였구요스크립터블 오브젝트도 강의와 똑같이 만들고 부착하였습니다어느 스크립트가 문제인지 몰라 Quest, QuestSystem, QuestSystemSaveTest세가지 스크립트 작성하여 등록하겠습니다Quest.csusing System.Collections; using System.Collections.Generic; using UnityEngine; using System.Linq; using System.Diagnostics; using Debug = UnityEngine.Debug; public enum QuestState { Inactive, Running, Complete, Cancel, WaitingForCompletion // 퀘스트 완료시 자동으로 완료되는게 아닌, 사용자가 퀘스트 완료를 눌러야 완료되는 상태 } [CreateAssetMenu(menuName = "Quest/Quest", fileName ="Quest_")] public class Quest : ScriptableObject { #region Event public delegate void TaskSuccessChangeHandler(Quest quest, Task task, int currentSuccess, int prevSuccess); public delegate void CompleteHandler(Quest quest); public delegate void CancelHandler(Quest quest); public delegate void NewTaskGroupHandler(Quest quest, TaskGroup currentTaskGroup, TaskGroup prevTaskGroup); #endregion [SerializeField] private Category category; [SerializeField] private Sprite icon; [Header("Text")] [SerializeField] private string codeName; [SerializeField] private string displayName; [SerializeField, TextArea] private string description; [Header("Task")] [SerializeField] private TaskGroup[] taskGroups; [Header("Reward")] [SerializeField] private Reward[] rewards; [Header("Condition")] [SerializeField] private Condition[] acceptionConditions; [SerializeField] private Condition[] cancelConditions; [Header("Option")] [SerializeField] private bool useAutoComplete; [SerializeField] private bool isCancelable; // 캔슬 불가능한 퀘스트를 위한 변수 [SerializeField] private bool isSaveable; private int currentTaskGroupIndex; #region 프로퍼티들 public Category Category => category; public Sprite Icon => icon; public string CodeName => codeName; public string DisplayName => displayName; public string Description => description; public QuestState State { get; private set; } public TaskGroup CurrentTaskGroup => taskGroups[currentTaskGroupIndex]; public IReadOnlyList<TaskGroup> TaskGroups => taskGroups; public IReadOnlyList<Reward> Rewards => rewards; public bool IsRegistered => State != QuestState.Inactive; public bool IsCompletable => State == QuestState.WaitingForCompletion; public bool IsComplete => State == QuestState.Complete; public bool IsCancel => State == QuestState.Cancel; public virtual bool IsCancelable => isCancelable && cancelConditions.All(x=>x.IsPass(this)); public bool IsAcceptable => acceptionConditions.All(x => x.IsPass(this)); public virtual bool IsSaveable => isSaveable; #endregion public event TaskSuccessChangeHandler onTaskSuccessChanged; public event CompleteHandler onCompleted; public event CancelHandler onCanceled; public event NewTaskGroupHandler onNewTaskGroup; // Awake 역할의 함수로 Quest가 System에 등록되면 실행될 함수 public void OnRegister() { // 퀘스트를 중복 등록했을경우 에러 발생 Debug.Assert(!IsRegistered, "This quest has already been registered."); foreach (var taskGroup in taskGroups) { taskGroup.Setup(this); foreach (var task in taskGroup.Tasks) task.onSuccessChanged += OnSuccessChanged; } State = QuestState.Running; CurrentTaskGroup.Start(); } public void ReceiveReport(string category, object target, int successCount) { Debug.Assert(IsRegistered, "This quest has already been registered."); Debug.Assert(!IsCancel, "this quest has been canceled"); if (IsComplete) return; CurrentTaskGroup.ReceiveReport(category, target, successCount); if (CurrentTaskGroup.IsAllTaskComplete) { if (currentTaskGroupIndex + 1 == taskGroups.Length) // 다음 taskGroup이 없다면 { State = QuestState.WaitingForCompletion; if (useAutoComplete) Complete(); } else { var prevTaskGroup = taskGroups[currentTaskGroupIndex++]; prevTaskGroup.End(); CurrentTaskGroup.Start(); onNewTaskGroup?.Invoke(this, CurrentTaskGroup, prevTaskGroup); } } else State = QuestState.Running; } public void Complete() { CheckIsRunning(); foreach (var taskGroup in taskGroups) taskGroup.Complete(); State = QuestState.Complete; foreach (var reward in rewards) { reward.Give(this); } onCompleted?.Invoke(this); onTaskSuccessChanged = null; onCompleted = null; onCanceled = null; onNewTaskGroup = null; } public virtual void Cancel() { CheckIsRunning(); Debug.Assert(IsCancelable, "this quest can't be canceled"); State = QuestState.Cancel; onCanceled?.Invoke(this); } public Quest Clone() { var clone = Instantiate(this); clone.taskGroups = taskGroups.Select(x => new TaskGroup(x)).ToArray(); return clone; } public QuestSaveData ToSaveData() { return new QuestSaveData { codeName = codeName, state = State, taskGroupIndex = currentTaskGroupIndex, taskSuccessCounts = CurrentTaskGroup.Tasks.Select(x => x.CurrentSuccess).ToArray() }; } public void LoadFrom(QuestSaveData saveData) { State = saveData.state; currentTaskGroupIndex = saveData.taskGroupIndex; for (int i = 0; i < currentTaskGroupIndex; i++) { var taskGroup = taskGroups[i]; taskGroup.Start(); taskGroup.Complete(); } for (int i = 0; i < saveData.taskSuccessCounts.Length; i++) { CurrentTaskGroup.Start(); CurrentTaskGroup.Tasks[i].CurrentSuccess = saveData.taskSuccessCounts[i]; } } private void OnSuccessChanged(Task task, int currentSuccess, int prevSuccess) => onTaskSuccessChanged?.Invoke(this, task, currentSuccess, prevSuccess); [Conditional("UNITY_EDITOR")] // 유니티 에디터로 시행할때만 작동하도록 함 private void CheckIsRunning() { Debug.Assert(IsRegistered, "This quest has already been registered."); Debug.Assert(!IsCancel, "this quest has been canceled"); Debug.Assert(!IsComplete, "this quest has already been completed"); } } QuestSystem.csusing System.Collections; using System.Collections.Generic; using UnityEngine; using System.Linq; using Newtonsoft.Json.Linq; public class QuestSystem : MonoBehaviour { #region Save Path private const string kSaveRootPath = "questSystem"; private const string kActiveQuestsSavePath = "activeQuests"; private const string kCompletedQuestsSavePath = "completedQuests"; private const string kActiveAchievementsSavePath = "activeAchievements"; private const string kCompletedAchievementsSavePath = "completedAchievements"; #endregion #region Events public delegate void QuestRegisteredHandler(Quest newQuest); public delegate void QuestCompletedHandler(Quest quest); public delegate void QuestCanceledHandler(Quest quest); #endregion private static QuestSystem instance; private static bool isApplicationQuitting; public static QuestSystem Instance { get { if(!isApplicationQuitting && instance == null) { instance = FindObjectOfType<QuestSystem>(); if(instance == null) { instance = new GameObject("Quest System").AddComponent<QuestSystem>(); DontDestroyOnLoad(instance.gameObject); } } return instance; } } private List<Quest> activeQuests = new List<Quest>(); private List<Quest> completedQuests = new List<Quest>(); private List<Quest> activeAchievements = new List<Quest>(); private List<Quest> completedAchievements = new List<Quest>(); private QuestDatabase questDatabase; private QuestDatabase achievementDatabase; public event QuestRegisteredHandler onQuestRegistered; public event QuestCompletedHandler onQuestCompleted; public event QuestCanceledHandler onQuestCanceled; public event QuestRegisteredHandler onAchievementRegistered; public event QuestCompletedHandler onAchievementCompleted; public IReadOnlyList<Quest> ActiveQuests => activeQuests; public IReadOnlyList<Quest> CompletedQuests => completedQuests; public IReadOnlyList<Quest> ActiveAchievements => activeAchievements; public IReadOnlyList<Quest> CompletedAchievements => completedAchievements; private void Awake() { questDatabase = Resources.Load<QuestDatabase>("QuestDatabase"); achievementDatabase = Resources.Load<QuestDatabase>("AchievementDatabase"); if(!Load()) // 저장된 데이터가 없다면 업적에 등록 { foreach (var achievement in achievementDatabase.Quests) Register(achievement); } } private void OnApplicationQuit() { isApplicationQuitting = true; Save(); } public Quest Register(Quest quest) { var newQuest = quest.Clone(); if(newQuest is Achievement) { newQuest.onCompleted += OnAchievementCompleted; activeAchievements.Add(newQuest); newQuest.OnRegister(); onAchievementRegistered?.Invoke(newQuest); } else { newQuest.onCompleted += OnQuestCompleted; newQuest.onCanceled += OnQuestCanceled; activeQuests.Add(newQuest); newQuest.OnRegister(); onQuestRegistered?.Invoke(newQuest); } return newQuest; } // 내부용 private void ReceiveReport(List<Quest> quests, string category, object target, int successCount) { foreach (var quest in quests.ToArray()) quest.ReceiveReport(category, target, successCount); } //외부용 public void ReceiveReport(string category, object target, int successCount) { ReceiveReport(activeQuests, category, target, successCount); ReceiveReport(activeAchievements, category, target, successCount); } public void ReceiveReport(Category category, TaskTarget target, int successCount) => ReceiveReport(category.CodeName, target.Value, successCount); // 수업자료 보고 추가한 메서드 public void CompleteWaitingQuests() { foreach (var quest in activeQuests.ToList()) { if (quest.IsCompletable) quest.Complete(); } } public bool ContainsInActiveQuests(Quest quest) => activeQuests.Any(x => x.CodeName == quest.CodeName); public bool ContainsInCompletedQuests(Quest quest) => completedQuests.Any(x => x.CodeName == quest.CodeName); public bool ContainsInActiveAchievements(Quest quest) => activeAchievements.Any(x => x.CodeName == quest.CodeName); public bool ContainsInCompletedAchievements(Quest quest) => completedAchievements.Any(x => x.CodeName == quest.CodeName); private void Save() { var root = new JObject(); root.Add(kActiveQuestsSavePath, CreateSaveDatas(activeQuests)); root.Add(kCompletedQuestsSavePath, CreateSaveDatas(completedQuests)); root.Add(kActiveAchievementsSavePath, CreateSaveDatas(activeAchievements)); root.Add(kCompletedAchievementsSavePath, CreateSaveDatas(completedAchievements)); PlayerPrefs.SetString(kSaveRootPath, root.ToString()); PlayerPrefs.Save(); } private bool Load() { if (PlayerPrefs.HasKey(kSaveRootPath)) { var root = JObject.Parse(PlayerPrefs.GetString(kSaveRootPath)); LoadSaveDatas(root[kActiveQuestsSavePath], questDatabase, LoadActiveQuest); LoadSaveDatas(root[kCompletedQuestsSavePath], questDatabase, LoadCompletedQuest); LoadSaveDatas(root[kActiveAchievementsSavePath], achievementDatabase, LoadActiveQuest); LoadSaveDatas(root[kCompletedAchievementsSavePath], achievementDatabase, LoadCompletedQuest); return true; } else return false; } private JArray CreateSaveDatas(IReadOnlyList<Quest> quests) { var saveDatas = new JArray(); foreach (var quest in quests) { if(quest.IsSaveable) saveDatas.Add(JObject.FromObject(quest.ToSaveData())); // SaveData를 Json형태로 변환하여 JsonArray에 넣어줌 } return saveDatas; } private void LoadSaveDatas(JToken datasToken, QuestDatabase database, System.Action<QuestSaveData, Quest> OnSuccess) { var datas = datasToken as JArray; foreach (var data in datas) { var saveData = data.ToObject<QuestSaveData>(); var quest = database.FindQuestBy(saveData.codeName); OnSuccess.Invoke(saveData, quest); } } private void LoadActiveQuest(QuestSaveData saveData, Quest quest) { var newQuest = Register(quest); newQuest.LoadFrom(saveData); } private void LoadCompletedQuest(QuestSaveData saveData, Quest quest) { var newQuest = quest.Clone(); newQuest.LoadFrom(saveData); if (newQuest is Achievement) completedAchievements.Add(newQuest); else completedQuests.Add(newQuest); } #region Callback private void OnQuestCompleted(Quest quest) { activeQuests.Remove(quest); completedQuests.Add(quest); onQuestCompleted?.Invoke(quest); } private void OnQuestCanceled(Quest quest) { activeQuests.Remove(quest); onQuestCanceled?.Invoke(quest); Destroy(quest, Time.deltaTime); } private void OnAchievementCompleted(Quest achievement) { activeAchievements.Remove(achievement); completedAchievements.Add(achievement); onAchievementCompleted?.Invoke(achievement); } #endregion } QuestSystemSaveTest.csusing System.Collections; using System.Collections.Generic; using UnityEngine; public class QuestSystemSaveTest : MonoBehaviour { [SerializeField] private Quest quest; [SerializeField] private Category category; [SerializeField] private TaskTarget target; private void Start() { var questSystem = QuestSystem.Instance; if(questSystem.ActiveQuests.Count == 0) { Debug.Log("Register"); var newQuest = questSystem.Register(quest); } else { questSystem.onQuestCompleted += (quest) => { Debug.Log("Complete"); PlayerPrefs.DeleteAll(); PlayerPrefs.Save(); }; } } private void Update() { if (Input.GetKeyDown(KeyCode.Space)) QuestSystem.Instance.ReceiveReport(category, target, 1); } } 이상입니다..
-
미해결유니티 머신러닝 에이전트 완전정복 (기초편)
경로 생성
안녕하세요 DDPG 부분까지 재밌게 수강했습니다.드론이 랜덤하게 생성되는 particle (goal)에 도달하는 것 말고 유니티 상에서 경로를 생성해 준 후 그 경로를 goal이라고 지정한 후에, 그 경로를 잘 따라가게끔 하는 것을 실습해보고자 합니다.혹시, 유니티에서 경로를 생성하게끔 해주는 툴을 알고 계신가요?강의 내용과는 거리가 있지만, 알고 계시다면 답변 부탁드립니다.
-
미해결[실전 게임 코드 리뷰] 유니티 클리커 게임
안녕하세요 Rookiss님, 블로그 포스팅 질문입니다.
안녕하세요 평소에 Rookiss 강의를 즐겨 듣는 유니티 개발자입니다.학습한 내용을 잊어 버리지 않기 위해서 저 나름대로 코드 리뷰를 하면서 블로그에 포스팅하고 싶습니다. 하지만 그 과정에서 강의에 포함되어 있는 프로젝트의 코드의 일부분이 발췌 될 수도 있어서 혹시 포스팅을 해도 되는지 궁금해서 질문 드립니다.
-
미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part3: 유니티 엔진
유니티 scene view 격자
어느 순간 부터 scene 에서 격자무늬가 안보이기 시작했습니다 ㅠㅠ그나마 마우스 휠로 겁나 축소를 해야 이렇게 격자가 보이는 수준인데 해결 방법을 도무지 찾을 수가 없네요ㅠㅠ
-
미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part3: 유니티 엔진
OnMouseClicked 관련 질문
안녕하세요,강의 수강 후 혼자 복습하면서 리뷰 하던 중에 궁금한 점이 있어서 문의 드립니다.InputManager에서 KeyAction 과 다르게 Mounse Action은 public Action의 Generic 타입이 사용된 이유가 궁금합니다.unity의 Input에 press와 click이 구분되어있지 않기 때문에, enum을 통해서 구분할 수 있도록 생성해 준게 맞을까요?그래서 이후에 PlayerController 에서도 함수의 입력에 마우스 상태를 구분지어서 input으로 넣어주게 되고, 반대로 KeyAction은 Input에 GetKey라는 함수가 있어서 iput 없이 delegate를 이용할 수 있다.. 라고 생각하는데, 뭔가 명확하게 이해가 되지는 않고 흐릿해서 질문 드립니다. 감사합니다.
-
미해결유니티 Addressable 을 이용한 패치 시스템 구현
reference count 관련 질문
안녕하세요 좋은 강의 제공해주셔서 감사합니다.reference count 관련해서 궁금한 점이 있습니다.강의를 듣고 에셋로드시에 reference count가 1증가인스턴스 생성시 1증가 라고 이해를 했지만 실제로 디버깅해봤을 때에는에셋로드랑 인스턴스 생성 두 경우 모두 레퍼런스 카운트가 2로 표시되었습니다.혹시 저 값으로 확인을 하는 게 맞는지 혹은 제가 코드를 잘못 작성했는지.... 설정을 잘못했는지...제가 이해를 잘못한 것은 아닌지 알고 싶습니다. -에셋 로드시 - 인스턴스 생성시
-
미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
공부 방법에 대해서 질문이 있습니다
안녕하십니까 게임 프로그래머 취업 가이드 영상을 보고 클라이언트쪽만 공부하다가 서버 공부를 시작하려고 하는 대학교 2학년생입니다.유니티와 언리얼강의 전체를 한번씩 보면서 어떤 느낌인지 파악하고 이해 안되는 강의를 계속 돌려보는 식으로 공부하려고 했습니다. 근데 제가 유니티 c#, 알고리즘, 엔진을 들을때는 괜찮았는데 게임서버 강의를 이제 1번 완강했는데 거의 부분적인 이해만 한것같고 전체의5%정도밖에 이해를 못했습니다.. 이게 다음파트로 넘어가는게 맞을까요? 아니면 이해갈때까지 서버강의를 계속 들어야할까요?조금이라도 이해가 더 될까봐 한빛아카데미 데이터 통신과 컴퓨터 네트워크 박기현 지음 이 이론책을 같이 보면서 공부중인데 이 두꺼운 이론책을 봐도 이 강의와의 접점을 못찾겠고 봐도 이해가 잘 안가는데 보는게 맞을까요? 아니면 시간낭비일까요..?강의 듣는 순서도 고민이 됩니다 유니티 강의보다 c++ 강의와 c++ 서버 강의를 듣고 유니티 강의로 넘어오는게 나을까요 아니면 지금 하는대로 유니티먼저듣고 c++강의로 넘어가는게 맞을까요?중구난방하게 글을 적은 느낌이라 죄송합니다 ㅠㅠ
-
미해결유니티 머신러닝 에이전트 완전정복 (기초편)
학습 관련
안녕하세요, 수업 따라가면서 코드 오류는 없었지만 내용에 질문이 생겨 글 남기게 되었습니다. 드론 agent 스크립트 중 Heuristic()에서 키보드 입력을 주었을 때 OnActionReceived()로 전달되어 드론을 움직이는 것으로 이해했는데, 학습 과정에서는 키보드 입력 없이 드론이 어떤 방법으로 스스로 움직이며 학습을 하는 것인지 궁금합니다. 감사합니다!
-
미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part3: 유니티 엔진
현재 방식에서 Addressable로 변경시 로드
안녕하세요. 항상 좋은 강의 감사합니다. 다름이 아니라 현재 resources에서 경로를 통한 로드로 코드를 사용하시는데 만약 에셋번들이나 어드레서블로 변경시 해당 방식을 전부 교채해야 되는건가요..?
-
미해결유니티(Unity)로 시작하는 게임개발: Part 3. 슈팅게임 개발
강의도중 질문이 있습니다
선생님께서는 struct구조로 생성자를 많이 쓰시던데,class로 생성자를 만드는것과struct로 생성자를 만드는 것이어떤 차이가 있는지 궁금합니다.성능상에 차이만 있는건지제가 알기로는 class로 생성자를 만들면 초기값을 넣을 수 있다고 알고 있습니다.차이는 그정도일까요??
-
미해결유니티 머신러닝 에이전트 완전정복 (기초편)
알고리즘 관련
안녕하세요, 알고리즘 관련해서 질문드리고 싶어 글 남깁니다. 해당 드론 예제를 DDPG가 아닌 PPO로 학습할 경우 대체로 성능이 낮아질까요? 내장된 알고리즘이 아닌 DDPG를 선택해 사용하신 이유가 궁금합니다.또한 드론과 goal 사이에 여러 장애물이 무작위로 배치될 경우에도 해당 알고리즘으로 학습이 무리없이 진행될지 질문드리고 싶습니다. 강화학습에 입문하면서 도움 많이 받고 있습니다. 감사합니다!
-
미해결유니티 머신러닝 에이전트 완전정복 (기초편)
API를 통한 학습 관련
안녕하세요, mlagents-learn을 이용해 학습하고 적용하는 것까지는 무리 없이 수강하였는데, API를 이용해 학습하는 과정에서 질문이 생겨 문의 드립니다. (현재 아나콘다에서 실습 진행하고 있습니다.) 학습 알고리즘으로 PPO가 아닌 DQN을 사용하고자 한다면 강의와 같이 코드를 작성하여 저장하고, 저장한 경로에서 해당 파이썬 파일을 실행하면 학습이 진행되는 것이 맞을까요? 기타 다른 작업이 필요하지는 않은지 궁금합니다.예를 들어, mlagents-learn을 사용해 학습을 할 때에 사용했던 yaml 파일 등을 따로 생성할 필요는 없는지 등에 대해 여쭙고싶습니다. 감사합니다!
-
미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
new List 동기화 와 관련하여..
public List<IPacket> PopAll(){ List<IPacket> list = new List<IPacket>(); lock(_lock) { ...어떤 처리.. }} 이 함수가 멀티 스레드로 실행 된다고 했을대 list 이 변수는 안에가 list 형태인데 linkedlist 같은것도 스레드 세이프 한건가요?여러개의 스레드가 linkedlist 를 생성만 한다고 했을때(위의 예시처럼 원소 추가나 조작 없이) 동시에 new 연산이 내부에서 실행 된다 여러개 스레드에서 동시에 힙에 new 연산이 실행될것 같은데(예를 들어 c++ vector<> 같은..) 멀티스레드로 new 연산시 같은 주소에 생성될 일이 발생할 수도 있지 않을까 해서요 질문을 요약하자면-지역변수에서 힙쪽의 new 를 생성하는게 있다면 그것은 스레드 세이프한가? 라는것이 질문입니다답변 부탁드려요!
-
미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part3: 유니티 엔진
질문1. base.Init() 컴파일 에러가 발생합니다
메시지: CS0205 추상 기본 멤버를 호출할 수 없습니다https://learn.microsoft.com/ko-kr/dotnet/csharp/misc/cs0205원인 추정: 2023. 01. 05 이후 abstract 메서드 본문을 호출하는 시도를 문법적으로 중단(abstract 는 메서드 껍데기만 동일하게 가져가는 것을 의도로 할 때만 사용 가능하게끔 하고, base 를 호출하며 사용할 때는 virtual 를 쓰도록 강제로 권장 안 좋은 대안: Start() 실행 순서 관리를 설정에서 직접 관리. '편집 -> 프로젝트 설정 -> 스크립트 실행 순서로 이동하여 스크립트의 실행 순서를 설정'대체 시도: UI_Base 의 Init() 를 abstract 대신 virtual 로 변경 질문 요지: virtual 로 변경해도 문제가 없을까요?
-
미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
패킷 노가다작업..
패킷 작업이 노가다 같은데 좀 더 편한 방법 이있나요?protobuf 는 사용법만 좀 보긴 했는데 좀 더 편한가요?
-
미해결[실전 게임 코드 리뷰] 유니티 클리커 게임
프로젝트 파일 중에서 데이터 파일 관련
데이터 파일은원본 데이터인 excel 파일을, Editor에서 ExceltoXml 파싱 툴을 써서 xml문서로 만든 후,그 xml문서를 , 그 xml 문서에 대응되는 cs파일을 만들어서 사용하는 걸로 알고 있는데excel xml cs 이렇게 하나의 데이터 테이블을 게임에 사용하려면 3개의 파일이 필요한 거 아닌가요?Data 폴더에,CharacterStatusInfoData가 원본 엑셀 파일 없이 xml, script만 있는 상태인데관련 내용이 없어서 그냥 UI에 표시되는 텍스트만 나오고, 수치에 관련된 테이블은 없더라구요. 이건 그럼 어떻게 돌아가는건가요? 이름만 있는 빈 파일이라면.. 추가로, RewardData은 반대로 xml이 없는 경우인데 ,전제로 깔고 갔던 excel xml cs 가 아닌건가요? ㅠㅠ
-
미해결C#과 유니티, 실전 게임으로 제대로 시작하기 (저자 직강)
collision 발생 조건
안녕하세요! 강의 잘 보고 있습니다다름이 아니라 2.18 적군 구현하기 3 - 플레이어 피격 실습하면서 Enemy gameObject와 Player gameObject를 충돌시키려고 할 때, 두 gameObject 모두 Kinematic일 경우 충돌 이벤트 자체가 발생하지 않더라구요 Rigidbody의 Body Type별로 충돌 이벤트가 다른식으로 발생하는거 같은데, Rigbody의 body type이 왜 둘다 Kinematic이면 충돌이벤트가 발생하지 않는지Collider와 Rigidbody는 어떤식으로 상호작용하는지답변이 가능할까요? 강의에서 다루는 내용에서 크게 벗어난다면 키워드만 알려주시면 검색해서 공부해보겠습니다..! 새해 복 많이 받으시고, 즐거운 한 해 되세요!