스크립트, 기획자들에게 어디까지 열어줘야 좋은가
이번 회사에서는 좋은 기획자들을 만나서 호의호식(?)하고 있다. JSON, 스크립트 뿐만 아니라 AI 사용에도 능해서 그렇다.
기존 개발자가 기획자들이 JSON으로 동작을 짤 수 있도록 토대를 잘 만들어놓은 것도 컸다.
아무튼, 이 상황을 살려서 이번 프로젝트에서는 기획자들이 더 잘 일할 수 있게 만들고 싶었다.
그래서 유사 스크립트 구조를 짜서 기획자들에게 배포했다. 이번 글은 그에 대한 회고이다.
로그라이크 덱빌딩 게임이고, 유물 작동 스크립트를 기획자가 짤 수 있게 만든 것이니 참고 바란다.
결론부터
결과적으로는 옳고, 어려운 기능이나 연출이 많이 들어가야 하는 부분만 개발자가 손대는 것이 바람직한 것 같다.
아래에서 설명하겠지만 스크립트 초보를 위해 Lua를 사용하지 않고 비교적 쉬운 문법을 사용했는데, 결과적으로는 러닝 커브가 있더라도 업계 표준을 쓰는 게 좋은 것 같다.
스크립트 포맷
일단 포맷부터 보자.
아래는 전기 카드를 뽑은 뒤, 1/3 확률로 전기 {0}장을 덱 맨 위에 임시로 놓는 효과다.
[
{
"Trigger": "start",
"Condition": "true",
"Effect": "setLocalVar(0, -1)"
},
{
"Trigger": "draw",
"Condition": "getElementCount('electric', true, 1) == 1 && random(1,3) == 1",
"Effect": "activateArtifact(1);addCard('top', 'electric', 'add', getLocalVar(0), 'none', 1, false); addLocalVar(0, -1)"
}
]
구조는 JSON이고, Trigger/Condition/Effect를 유사 코드처럼 서술할 수 있게 되어 있다.
해당 프로젝트에 특화되었기에 다들 기획자가 단번에 이해할 수 있다며 긍정적으로 평했다.
왜 JSON인가? 왜 Lua가 아니었나?
루아 스크립트 언어가 버젓이 있는데도 이렇게 만들어놓은 건, 루아 스크립트와 유사 스크립트중에서 기획자들이 고르게 한 결과다.
1. JSON은 괜찮은데 코드 문법에는 익숙하지 않은 기획자가 있어, 문법 에러에 대한 우려가 있었다.
2. 이게 더 관리하기 편하고 잘 읽힌다고 생각했다.
구조는 어떻게 짰나?
Trigger는 해당 이벤트의 발생 타이밍을 정하는 것이고, Condition은 발생 시 조건을 체크한다.
그리고 Effect에서는 Condition이 True일 때 내용을 수행한다.
Trigger는 당연히 인게임 내에서 자체 구현했기에 패스하고,
Condition과 Effect를 수행하기 위해서는 마치 Eval문을 수행하는 것처럼 만들어야 했기에 라이브러리를 찾아보았다.
결과로 가볍고 성능이 괜찮은 NCalc라는 라이브러리를 찾을 수 있었다.
https://github.com/ncalc/ncalc
GitHub - ncalc/ncalc: NCalc is a fast and lightweight expression evaluator library for .NET, designed for flexibility and high p
NCalc is a fast and lightweight expression evaluator library for .NET, designed for flexibility and high performance. It supports a wide range of mathematical and logical operations. - ncalc/ncalc
github.com
NCalc는 어떤 라이브러리인가?
.Net Calculation의 줄임말인 듯 하다. 본래는 string을 계산한 결과값을 object 타입으로 돌려주는 라이브러리다.
작게는 수학 계산을 위해 쓰이고, 나의 경우 간단한 함수를 호출하는 데에도 사용했다.
var expression = new Expression("Round(Pow([Pi], 2) + Pow([Pi2], 2) + [X], 2)");
expression.Parameters["Pi2"] = new Expression("Pi * [Pi]");
expression.Parameters["X"] = 10;
expression.DynamicParameters["Pi"] = _ => {
Console.WriteLine("I'm evaluating π!");
return 3.14;
};
Debug.Assert(117.07 == expression.Evaluate());
위와 같은 방식으로 변수를 등록하여 계산하는 것도 가능하고,
var expression = new Expression("SecretOperation(3, 6)");
expression.Functions["SecretOperation"] = (args) => {
return (int)args[0].Evaluate() + (int)args[1].Evaluate();
};
Debug.Assert(9 == expression.Evaluate());
위와 같은 방식으로 함수를 등록해서 계산하는 것도 가능하다.
기타
다만 실제로는 성능 최적화나 await를 수행해야 하는 케이스가 있어서, 완전히 NCalc를 사용한 건 아니고...
결과를 바로 제공해야 하는 Condition 구문이나, await를 쓰지 않고도 작동하는 getVar(), setVar(), addVar()등의 커스텀 함수들은 NCalc에 붙여서 요긴하게 사용했다.
그 외의 Effect는 작은 모듈들로 만들어 객체로 async Action() 함수를 구현했다. 자세히는 설명하지 않고 넘어가겠다.
프로젝트에서 잘 기능했는가?
지금이야 익숙해져서 만족스럽지만, 좀 아쉬웠던 몇 가지 부분을 적어보겠다.
개발자가 생산하지 않은 버그는 생각보다 고통스럽다
개발 초기에는 이 JSON 스크립트에 대한 검증기가 없었기에, 테스트 빌드에서 버그가 났을 때 개발 코드가 문제인지 데이터 코드가 문제인지 바로 파악하기가 힘들었다. 그리고 그게 꽤 스트레스였다. 어쨌든 버그가 발생하면 보통 개발자를 찾고 기획자를 찾지는 않으니까.
이건 금방 해결했다. 스크립트 문법 검증과 작동 검증을 하는 툴을 만들어서 배포했기 때문.
왜 검증기부터 만들지 않았냐는 의문이 들 수 있는데, 이 프로젝트는 현재까지 개발 기간이 6개월이므로 다른 개발로 바빴다 ^^;;
AI를 효율적으로 사용할 수 없었다
초기에는 어느 정도 스크립트를 만든 후에 LLM에게 학습시켜서, 원하는 기획을 입력하면 스크립트로 출력하게 할 생각에 모두 들떠 있었다. 그러나 결과물은 만든 적도 없던 커스텀 함수를 넣어서 내놓거나, 문법에 맞지 않는 스크립트를 내놓았다. 즉, 할루시네이션이 심했다.
만약 Lua 스크립트였다면 업계 표준이기에 학습 데이터가 많아 그래도 쓸만한 결과물을 내놓았을텐데 말이다.
포맷의 가독성이 생각보다 안 좋고 IDE를 활용하지 못했다
처음에는 읽기 쉬운 스크립트가 될 거라고 생각했다. 하지만 언제나 창의적인 생각을 내놓는 것이 기획자였기 때문에...
[
{
"Trigger": "enable",
"Condition": "true",
"Effect": "setVar('서류철',0)"
},
{
"Trigger": "draw",
"Condition": "true",
"Effect": "addVar('서류철',1)"
},{
"Trigger": "draw",
"Condition": "getVar('서류철')>=50",
"Effect": "activateArtifact(1);sortCard('top','stone','any','any',-1,'any',-1,false);sortCard('top','electric','any','any',-1,'any',-1,false);sortCard('top','water','any','any',-1,'any',-1,false);sortCard('top','fire','any','any',-1,'any',-1,false);setVar('서류철',0)"
}
]
위 스크립트는 카드를 총 50회 뽑고 난 뒤 덱을 불-물-전기-돌 순서로 정렬하는 스크립트인데, 가독성이 좋지 않다.
우선 sortCard라는 함수에 모든 조건을 때려넣게 되는 부분이 문제였다. Lua였다면 이름 기반 매개변수처럼 작성할 수도 있었을 텐데. 심지어는 검증기 사용 이전에 IDE가 기본 문법 체크를 해주는 경우도 있었을 테니 이래저래 아쉬웠다.
정리, 그리고 다음 프로젝트에서는?
만약 스크립트 형태가 아니라 개발자가 직접 구현하는 형태였다면 안정성은 보장되었겠지만 시간이 엄청나게 들었을 것이다.
지금 데이터에 있는 몬스터 스킬이나 유물 데이터만 해도 200개가 넘어가니까...
다만 스크립트에 능한 기획자가 있어야만 이 모든 일이 가능한 것이 어려운 부분이긴 하다. 인수인계도 어려울 거고.
마음 한 켠에서는 스크립트 툴을 블록코딩 형태로 주는 것도 나쁘지 않겠다 싶다. ^Q^...
기획자와 협의해서 다음에는 Lua 스크립트를 써 보자고 얘기했다. 그 때에는 또 글이 올라갈지도 모르겠군...
그래도 이번에 유사 스크립트를 사용하게 만들었기에 Lua도 별 고통 없이 도입할 수 있는 것일지도 모르겠다.
'개발 일지 > 게임' 카테고리의 다른 글
| Please One More Block 개발 회고 (0) | 2025.12.17 |
|---|---|
| Please One More Block 데이터 분석 보고서 (OOI 2025) (2) | 2025.12.14 |
| How We Die: 우리들이 죽는 방법 회고 (4) - 기타 (0) | 2024.10.10 |
| How We Die: 우리들이 죽는 방법 회고 (3) - 그래픽 (0) | 2024.10.10 |
| How We Die: 우리들이 죽는 방법 회고 (2) - 개발 (0) | 2024.10.10 |