죽을때까지코딩하고지는쪽은개가되는걸로
CodeDOM 코드 생성을 이용하여 스킬의 레벨에 따라 스킬의 수치가 달라지는 시스템 제작 ( Gradual Variable System ) 본문
CodeDOM 코드 생성을 이용하여 스킬의 레벨에 따라 스킬의 수치가 달라지는 시스템 제작 ( Gradual Variable System )
큐유이 2023. 2. 8. 22:14제목을 마지막에 썼는데
왠지 이거 라노벨 제목같다
사진은 귀여운 로께.
Que는 로그라이트 게임이다.
당연히 로그라이트니까 성장요소가 필요하다...
이
>> 성장요소 <<
라는것에서........ 예상하지 못한 문제가 생겼다.
데이터 관리를 어디에서 (시트, DB, 에디터) 관리할지?
그 데이터를 어떻게 가져올지?
사실 여기까진 그닥 문제가 었었다
관리야 어디서든 하면 되고 가져오는 건 어떻게든 가져오면 되는 거다
구 큐유이 ( 약 2년전 ) 의경우...
이런 식으로
하나의 클래스 안에 게임의 데이터들을 다 저장해서
엄청나게 큰 하나의 데이터 클래스를 관리했었다.
당연히 스킬 수치도 다 여기에 레벨별로 있었다.
그리고 이걸 스크립터블 오브젝트에 저장했다
이론상 문제는 없는 구조긴 한데....
문제가 몇 가지 있었다
아무래도 다양한 데이터를 다 갖고 있으니... 당연히 코드가 이런 모양으로 길어졌고
이유는 모르겠으나??????
스크립터블 오브젝트의 데이터가 다 날아가는 경우가 너무 많았다...
( 지금 생각해 보면 계속해서 코드를 수정하다 보니 직렬화된 데이터를 읽어오지 못하는 문제가 있던 게 아닌가? 싶긴 한데 정확한 이유는 모르겠다. )
그렇게 구 큐유이를 처참하게 말아먹고
한동안 스크립터블오브젝트와 손절을 하게 된다.. 사실 지금도 이때의 기억 때문에 스크립터블 오브젝트는 별로 좋아하지 않는다.
그리고 시간이 흘러 2년 후
큐유이를 다시 개발하게 되었고
그때와 비교해서 많이 발전했으니까. 뭔가 해보기로 했다.
필요한 기능은 다음과 같았다.
1. 레벨에 따른 데이터의 변화를 관리하기
2. 레벨이 변화하면 그에 따른 데이터를 자동으로 적용하기
난 참 글을 잘 못쓴다...... 대충 뭐가 필요한 거냐면
100만 볼트 ( lv.1 )
200 대미지를 주고 30% 확률로 마비시킨다.
100만 볼트 ( lv.2 )
300 대미지를 주고 40% 확률로 마비시킨다.
여기서 lv.1 -> lv.2가 됨에 따라
200 -> 300, 30% -> 40%으로 변화하는 데이터를 저장하는걸
쉽고 간단하게 하는 게 필요했다.
물론 테이블을 여러 개 만들어서 할 수도 있겠지만 그 정도로 데이터의 규모가 크지도 않을 거고
테이블을 만드는 작업은 수고가 크다. ( 귀찮다는 뜻 )
그래서 CodeDOM을 이용한 코드 생성으로 자동으로 스크립터블 오브젝트 코드를 만들고, 이를 자동으로 불러오고, 이를 자동으로 적용까지 해주는 시스템을 만들었다.
이름을 뭘로 할까 고민했는데
레벨이 증가하면서 변하는 변수들인 거니까
Gradual Variable System으로 정했다.
솔직히 이름은 별로 마음에 안 드는데 이게 내 최선이다..
실제로 사용하는 모습이다.
레벨이 올라갈 때 변해야 하는 변수들을 GradualVar<>로 선언한다.
그리고 생성자든 어디에서든 @_System.Setup()을 호출해 준다.
꺼내올 때는 이런 식으로 GradualVar<>.Value 로 가져온다.
레벨은 이런식으로 System을 호출해서 올린다. LevelUp() 말고도 SetLevel(), ResetLevel() 등등 만들어 뒀다.
이게 끝이다.
나름 간단하다고 생각한다...
사실 원래는 일반 변수에 [GradualVariable] 같은 어트리뷰트를 붙이는 걸 생각했었는데
아무리 생각해도 이건 리플렉션의 향기가 너무 났다.
사실 이미 리플렉션의 향기는 이 시스템에 쫙 퍼져있긴 한데 그래도 런타임에는 안 쓰니까 눈감고 무시하기로 했다.
나름 깔끔하다고 생각한다. 어트리뷰트 방식을 썼으면 더 깔끔했겠지만.... 그리고 지금도 아쉽다고 생각하고 있긴 하지만.... 고작 그거 편하자고 런타임에 계속 리플렉션을 호출하는 건 좀 아닌 것 같다. 리플렉션이 느리기도 하고 박싱 언박싱도 하니까 가비지도 나올 거고.
원리는 대략 이렇다.
GradualVar <>는 별거 없다. 그냥 Func<T>를 감싸둔거다. Value 프로퍼티를 호출하면 갖고있는 Func<T>를 호출해서 돌려준다.
System들이 공통적으로 받는 부모 클래스다. 싱글톤 패턴을 적용했고 레벨을 여기서 관리해 준다. 얘도 별거 없다.
CodeDOM으로 생성한 데이터와 걔를 리스트로 담는 스크립터블 오브젝트. 얘도 정말 별거 없긴 한데
자동 생성이라는 점이 마음에 든다. 이걸 일일이 치고 있을 생각 하면.... 더보기
그리고
대망의
하이라이트
문제의 System이다. 얘가 스크립터블 오브젝트를 불러올 거고, 레벨별로 딕셔너리에 담아둔다.
진짜 하이라이트
Setup() 함수다.
사실 이거야말로 코드 자동 생성의 맛인 거 같다
getter를 여기에서 만든다.
그리고 private으로 되어있을 변수들을 위해 리플렉션까지 써준다.
이러면 이제 런타임에는 리플렉션을 쓸 일은 없다. 첫 생성에만 써주니까.
Dictionary<>에서 긁어오니까 느리지도 않을 거다.
모든 게 다 만능 CodeDOM 자동생성으로 돌아간다.
근데 왜 CodeDOM분량은 1도 없는가?
...
지금 봐도 아찔하다
내가 이걸 어떻게 짠 건지도 잘 모르겠다
짜면서도 으어어어어하면서 짰다.
수많은 new와 긴 코드를 보아 뭔가 엄청난 걸 하는 것 같지만
요 단 세줄을 위해서다........
ㅋ
ㅋ
ㅋ
ㅋ
ㅋ
ㅋ
ㅋ
ㅋ
ㅋ
ㅋ
이걸 만들고 나니 고민이 생겼다.
이게 맞나?
지금... 지금 그 테이블 만드는 거 딸깍딸깍하는 게 귀찮다고 이러고 있는 게 맞을까? 코드몇글자쓰기귀찮다고이러고있는게맞을까??
그래도 많이 왔으니까.
조금만 더 해보자..라고 생각하다가
이부분을자동생성하게하는부분에서.
사실 저 문제의 for문짜는 코드만 없었어도 그냥 했겠지만
갑자기 삶이 싫어졌다.
Roslyn에서는 분명 그냥 String으로 코드를 박아두고 일부분만 바꾸는 식이였는데. 여긴 그런 거 안되나?
어?
어?
왠지 있을 법했다. 이걸 왜 지금까지 찾아볼 생각을 안 하고 나는 꾸역꾸역 msdn 읽어가면서 짰을까.
그리고...
ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
있었다.
코드에 코드를 박아두는 게 좀 뭣하긴 한데 너무 편하고 읽기도 편하고
난 지금까지 뭐 한 걸까....
라는 생각이 들어서 문제의 for문은 그냥 두기로 했다.....
뭐.... 런타임에 코드생성하는 건 아니니까.
에디터에서만 생성하는 거니까.....
나는 왜 지금까지 몇 번이고 CodeDOM을 사용했으면서
꽤 많이 썼으면서 이걸 찾아볼 생각은 안 했을까..
그렇게 Gradual Variable System을 완성했다.
큰 산을 하나 넘었다... 아마?
이제 마법소녀는 스킬을 레벨업 할 수 있다.
의외의 소득도 있다. 이제 코드 생성을 더 적극적으로 이용해 내 마음대로 뭐든 할 수 있다.
C#에 매크로가 없는 건 정말 아쉽지만. 뭐 이런 식으로 코드 생성이 가능하다면 매크로는 당연히 필요 없다. ( 그래도 있었으면.. )
끝.