3ea9d13eabc236a14e81d2b628f1726d9d690c17

์›๋ฌธ:RPG Programming Pitfalls #1: Stat System



๋กคํ”Œ๋ ˆ์ž‰ ๊ฒŒ์ž„์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ณต์žกํ•œ ์‹œ์Šคํ…œ์ด๋ฉฐ, ๊ฒŒ์ž„ ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ๋˜๊ธฐ ์œ„ํ•œ ๊ธธ์„ ์ด์ œ ๋ง‰ ์‹œ์ž‘ํ•œ ์‚ฌ๋žŒ์—๊ฒŒ ์ผ๋ฐ˜์ ์œผ๋กœ ์ถ”์ฒœํ•  ๋งŒํ•œ ์žฅ๋ฅด๋Š” ์•„๋‹™๋‹ˆ๋‹ค. ์ฒ˜์Œ๋ถ€ํ„ฐ ํ”„๋กœ์ ํŠธ๋ฅผ ๋ฐฉํ•ดํ•˜๋Š” ๋ถˆ๊ฑด์ „ํ•œ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์„ ํƒํ•จ์œผ๋กœ์จ ์Šค์Šค๋กœ์˜ ๋ฐœ๋“ฑ์„ ์ฐ์„ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์€ ์–ผ๋งˆ๋“ ์ง€ ์žˆ์Šต๋‹ˆ๋‹ค. ์•ˆํƒ€๊น๊ฒŒ๋„ ๋งŽ์€ ์ธ๊ธฐ ํŠœํ† ๋ฆฌ์–ผ๋„ ์ด๋Ÿฌํ•œ ํ•จ์ •์— ํ•˜๋‚˜ ์ด์ƒ ๋น ์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

RPG ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํ•จ์ • ๋ฌธ์„œ ์‹œ๋ฆฌ์ฆˆ๋Š” ๋กคํ”Œ๋ ˆ์ž‰ ๊ฒŒ์ž„ ๊ฐœ๋ฐœ(๋ฐ ๋‹ค๋ฅธ ์žฅ๋ฅด์—์„œ๋„)์—์„œ ํ”ํžˆ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋ฅผ ์•ˆ์ •์ ์ด๊ณ  ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ฐฉ์‹์œผ๋กœ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋…ผ์˜ํ•˜๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•ฉ๋‹ˆ๋‹ค. ํ•ต์‹ฌ์€ ์ข‹์€ ์•„์ด๋””์–ด์ฒ˜๋Ÿผ ๋ณด์˜€๋˜ ๊ณผ๊ฑฐ์˜ ์ž์‹ ์„ ์ง€์˜ฅ์˜ ๊นŠ์€ ๊ตฌ๋ฉ์ด๋กœ ์ €์ฃผํ•˜๊ณ  ์‹ถ์ง€ ์•Š์€ ํ•ด๊ฒฐ์ฑ…์„ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด๋ฉฐ, ์ด๋Š” ์ œ๊ฐ€ 6๊ฐœ์˜ ๋‹ค๋ฅธ RPG๋ฅผ ์ž‘์—…ํ•˜๋ฉด์„œ ๊ฐ€์žฅ ์ตœ์ ์˜ ๋ฐฉ๋ฒ•์ด๋ผ๊ณ  ํŒ๋‹จํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด ๊ธ€์˜ ์ฝ”๋“œ๋Š” C#์œผ๋กœ ์ž‘์„ฑํ•˜๊ณ  ์—”์ง„๋ณ„ ๋‚ด์šฉ์€ Unity๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋งŒ ์ผ๋ฐ˜์ ์ธ ์ ‘๊ทผ ๋ฐฉ์‹์€ ๋Œ€๋ถ€๋ถ„์˜ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์™€ ์—”์ง„์— ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์ด ์‹œ๋ฆฌ์ฆˆ์˜ ์ฒซ ๋ฒˆ์งธ ๊ธ€์€ RPG์˜ ํ•ต์‹ฌ์„ ์ด๋ฃจ๋Š” ํ†ต๊ณ„์— ๋Œ€ํ•ด ๋‹ค๋ฃน๋‹ˆ๋‹ค. ์‚ฌ์‹ค ๋Œ€๋ถ€๋ถ„์˜ ๊ฒŒ์ž„์—์„œ ๋‹ค์–‘ํ•œ ๊ฐ’์„ ์ถ”์ ํ•˜๊ธฐ ์œ„ํ•ด ๋ช‡ ๊ฐ€์ง€ ์‹œ์Šคํ…œ์„ ์‚ฌ์šฉํ•˜์ง€๋งŒ, RPG์—์„œ๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์˜ ํ•ต์‹ฌ์ด์ž ๋งค์šฐ ๋ˆˆ์— ์ž˜ ๋„๋Š” ์š”์†Œ์ž…๋‹ˆ๋‹ค.

์ด ๊ธ€์—์„œ๋Š” ๋‹ค์Œ ์ฃผ์ œ์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค:

1.์Šคํƒฏ์ด๋ž€ ๋ฌด์—‡์ด๋ฉฐ ์–ด๋–ป๊ฒŒ ๋ณ€์ˆ˜๋กœ ๊ตฌํ˜„๋˜๋Š”๊ฐ€?
2.์œ„ ์ ‘๊ทผ ๋ฐฉ์‹์˜ ๋ฌธ์ œ์ 
3.๋”•์…”๋„ˆ๋ฆฌ(Dictonary)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ณด๋‹ค ์œ ์—ฐํ•œ ๊ตฌํ˜„ ๋งŒ๋“ค๊ธฐ
4.์ถ”๊ฐ€์ ์ธ ํŽธ์˜์„ฑ ์ถ”๊ฐ€ํ•˜๊ธฐ
5.Unity ์—”์ง„์—์„œ ์†”๋ฃจ์…˜ ์‚ฌ์šฉ
์šฐ๋ฆฌ๊ฐ€ ์ผ๋ฐ˜์ ์œผ๋กœ ๋ณต์ œํ•˜๊ณ ์ž ํ•˜๋Š” ๊ฒƒ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:


3ea9d132fd9c28a8699fe8b115ef046ca12c574a935c

(์—ญ์ฃผ:๋˜์ „์•ค ๋“œ๋ž˜๊ณค ์บ๋ฆญํ„ฐ ์‹œํŠธ ์‹œํŠธ)


์บ๋ฆญํ„ฐ ์‹œํŠธ. (2023 ๋…„ 6 ์›” 30 ์ผ). ์œ„ํ‚ค๋ฐฑ๊ณผ์—์„œ. https://en.wikipedia.org/wiki/Character_sheet

์ง๊ด€์ ์ธ ์ ‘๊ทผ ๋ฐฉ์‹์€ ์บ๋ฆญํ„ฐ์˜ ๋‹ค์–‘ํ•œ ์†์„ฑ์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณ€์ˆ˜๋กœ ์„ธ๋ถ„ํ™”ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค:


1
2
3
4
5
6
public int Strength;
public int Dexterity;
public int Constitution;
public int HitPoints;
public int ProficiencyBonus;
public int Attack => Strength + ProficiencyBonus;
cs

์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•˜๋ฉด, ํ•œ๋™์•ˆ ๋Œ€๋ถ€๋ถ„์˜ ํŠœํ† ๋ฆฌ์–ผ์—์„œ ๋‚˜์˜ค๋Š”๋Œ€๋กœ ํ•œ๋™์•ˆ์€ ๋ชจ๋“  ๊ฒƒ์ด ๊ดœ์ฐฎ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ํ•„์š”ํ•œ ์†์„ฑ๊ณผ ๋Šฅ๋ ฅ์„ ๊น”๋”ํ•œ ๋ณ€์ˆ˜ ํ•„๋“œ์— ์ž…๋ ฅํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋˜๊ณ ,

๋•Œ๋กœ๋Š” ์œ„์˜ '๊ณต๊ฒฉ(Attack)' ๊ฐ™์ด ์กฐ๊ธˆ ๋ณต์žกํ•œ ์†์„ฑ๋„ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ HitPoint(์ฒด๋ ฅ)์˜ ๊ฒฝ์šฐ ํ˜„์žฌ์™€ ์ตœ๋Œ€์น˜๋ฅผ ๋ชจ๋‘ ์ถ”์ ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ๋‘ ๊ฐœ ์ด์ƒ์˜ ๋ณ€์ˆ˜๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋Š” ์‚ฌ์‹ค์„ ๊นจ๋‹ซ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.



1
2
public int MaxHitPoints;
public int CurrentHitPoints;
cs

๊ทธ๋ฆฌ๊ณ  ๊ฒŒ์ž„์— ๋งˆ๋‚˜ ํฌ์ธํŠธ๋‚˜ ์Šคํƒœ๋ฏธ๋‚˜ ํฌ์ธํŠธ ๋˜๋Š” ์ด์™€ ์œ ์‚ฌํ•œ ๊ฒƒ์ด ํฌํ•จ๋˜์–ด ์žˆ๋‹ค๋ฉด, ๊ทธ๊ฒƒ๋“ค๋„ ์ด๊ฒƒ๊ณผ ๋˜‘๊ฐ™์ด ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
๊ทธ๋ž˜๋„ ๊ฝค ๊ด€๋ฆฌํ•˜๊ธฐ ์‰ฝ์ฃ ? ๊ทธ๋Ÿฐ ๋‹ค์Œ ์ฃผ๋ฌธ ๊ตฌํ˜„์„ ์‹œ์ž‘ํ•˜๊ณ  ๋””์ž์ธ ๋ฌธ์„œ์—์„œ ์ด๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค:


34aed468f5dc3f8650bbd58b368671656e

์ทจ์•ฝ

์ฃผ๋ฌธ์ด ์ง€์†๋˜๋Š” ๋™์•ˆ ๋Œ€์ƒ ์บ๋ฆญํ„ฐ์˜ ํž˜ ์†์„ฑ์„ 2๋งŒํผ ๋‚ฎ์ถฅ๋‹ˆ๋‹ค.


์ฃผ๋ฌธ์„ ํ›‘์–ด๋ณด๋ฉด ๋ฏผ์ฒฉ(Dexterity)๊ณผ ๊ฐ•๊ฑดํ•จ(Constitutin)์—๋„ ๋น„์Šทํ•œ ์ฃผ๋ฌธ์ด ์žˆ๊ณ  ๋‹ค๋ฅธ ์†์„ฑ์—๋„ ๋น„์Šทํ•œ ์ฃผ๋ฌธ์ด ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด์ œ ๋ชจ๋“  ์†์„ฑ์— ๋Œ€ํ•ด ๋™์ผํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค! ์•„๋‹ˆ๋ฉด ๋” ๋‚˜์€ ํ•ด๊ฒฐ์ฑ…์ด ์žˆ์„๊นŒ์š”?

์Šคํƒฏ ํด๋ž˜์Šค
๊ฐ ์†์„ฑ์— ๋Œ€ํ•ด ์ตœ์†Œ ๋‘ ๊ฐœ(์ตœ์†Œ๊ฐ’๋„ ์ถ”์ ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ๊ทธ ์ด์ƒ)์˜ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์‹  ์บ๋ฆญํ„ฐ์˜ ์Šคํƒฏ์˜ ์ง€์†์ ์ด๊ณ  ํ˜„์žฌ ์ƒํƒœ๋ฅผ ์ถ”์ ํ•˜๋Š” ์ปค์Šคํ…€ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
public class Stat
{
// Fields
private string _name;
private int _currentValue;
private int _maxValue;
private int _minvalue;
// Public Getters
public string Name => _name;
public int CurrentValue => _currentValue;
public int MaxValue => _maxValue;
public int MinValue => _minvalue;
// Events
private event System.Action<int> _onValueChanged;
public event System.Action<int> OnValueChanged
{
add
{
_onValueChanged += value;
}
remove
{
_onValueChanged -= value;
}
}
// Constructors
public Stat(string name, int value, int minValue, int maxValue)
{
_name = name;
_minvalue = minValue;
_maxValue = maxValue;
// Set the value through the SetValue method so it's guaranteed to be between min, max
SetValue(value);
}
/// <summary>
/// The most commonly used constructor that defaults the value to the max value
/// </summary>
/// <param name="name">Name of the stat</param>
/// <param name="value">Current and max value</param>
public Stat(string name, int value) : this(name, value, 0, value) { }
// Methods
/// <summary>
/// Adjust the current value, used often when you don't really need to know the stat's boundaries (e.g. taking damage)
/// </summary>
/// <param name="amount"></param>
public void Adjust(int amount)
{
SetValue(_currentValue + amount);
}
/// <summary>
/// Sets the current value and clamps it between min, max
/// </summary>
/// <param name="newValue">The value to set</param>
public void SetValue(int newValue)
{
// If nothing has changed, don't do anything
if (newValue == _currentValue)
return;
_currentValue = Mathf.Clamp(newValue, _minvalue, _maxValue);
_onValueChanged?.Invoke(_currentValue);
}
/// <summary>
/// Resets the value to its maximum. Often used in effects like healing or resetting a character to their default state.
/// </summary>
public void ResetToMax()
{
SetValue(_maxValue);
}
/// <summary>
/// Resets the value to the minimum value (usually 0). Useful for any stat that is gained temporarily.
/// </summary>
public void ResetToMin()
{
SetValue(_minvalue);
}
}
cs

์œ„์—์„œ๋Š” ๊ฐ ์Šคํƒฏ์— ๋Œ€ํ•ด ํ˜„์žฌ, ์ตœ์†Œ, ์ตœ๋Œ€ ์„ธ ๊ฐ€์ง€ ๊ฐ’์„ ์ •์˜ํ–ˆ์Šต๋‹ˆ๋‹ค.

์Šคํƒฏ์˜ ํ˜„์žฌ ๊ฐ’์„ ์ˆ˜์ •ํ•ด์•ผ ํ•  ๋•Œ(์˜ˆ: ์บ๋ฆญํ„ฐ๊ฐ€ ํ”ผํ•ด๋ฅผ ์ž…์—ˆ์„ ๋•Œ) Adjust ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

Adjust() ๋‚ด๋ถ€์—์„œ ํ˜„์žฌ ๊ฐ’์— ์ˆ˜์ •ํ•  ๊ฐ’์„ ๋”ํ•œ SetValue() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ํ›„์ž๊ฐ€ ๊ฒฐ๊ณผ๋ฅผ ์ตœ์†Œ๊ฐ’๊ณผ ์ตœ๋Œ€๊ฐ’ ์‚ฌ์ด์— ๊ณ ์ •ํ•ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ SetValue() ๋ฉ”์„œ๋“œ๋Š” ํ˜„์žฌ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜์—ˆ์Œ์„ ๋ชจ๋“  ์‚ฌ๋žŒ์—๊ฒŒ ์•Œ๋ฆฌ๊ธฐ ์œ„ํ•ด OnValueChanged ์ด๋ฒคํŠธ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋ฐ๋ฏธ์ง€ ํ”Œ๋กœํ„ฐ๋ฅผ ํ‘œ์‹œํ•  ๋•Œ ๋งค์šฐ ํŽธ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

์œ„์˜ ์ฃผ๋ฌธ์ด ์ฝ”๋“œ์—์„œ ์–ด๋–ป๊ฒŒ ๋ณด์ด๋Š”์ง€๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:


public class StrengthDebuff { public int AdjustValue = -2; public void OnStartEffect(NewbieCharacterSheet target) { target.Strength.SetValue(AdjustValue); } public void OnEndEffect(NewbieCharacterSheet target) { target.Strength.ResetToMax(); } }

ํ›จ์”ฌ ๋‚˜์•„์กŒ์ฃ ? ํ•˜์ง€๋งŒ ์—ฌ์ „ํžˆ ๋ช‡ ๊ฐ€์ง€ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐ€๋Šฅํ•œ ๋ชจ๋“  ์Šคํƒฏ ๊ฐ์†Œ ์ฃผ๋ฌธ์„ ์ˆ˜์šฉํ•˜๋ ค๋ฉด ๊ฐ ์†์„ฑ์— ๋Œ€ํ•ด ๊ฑฐ์˜ ๋™์ผํ•œ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
์ฃผ๋ฌธ์ด ์ง€์†๋˜๋Š” ๋™์•ˆ ๋‹ค๋ฅธ ํšจ๊ณผ๊ฐ€ ํž˜ ์†์„ฑ์„ ๋ณ€๊ฒฝํ•˜๋ฉด ์ฃผ๋ฌธ์ด ์ข…๋ฃŒ๋˜๋ฉด ์†์„ฑ์ด ์›๋ž˜ ๊ฐ’์œผ๋กœ ๋Œ์•„๊ฐ€๋ฏ€๋กœ ๋ฌดํšจํ™”๋ฉ๋‹ˆ๋‹ค.
์ด ๊ฐ’์€ ์›๋ž˜์˜ ์ตœ๋Œ€ ํž˜์— ๊ณ ์ •๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ๋Œ€์ƒ์˜ ํž˜์„ ์ฆ๊ฐ€์‹œํ‚ค๋Š” ์ฃผ๋ฌธ์—๋Š” ๋‹ค๋ฅธ ํ•ด๊ฒฐ์ฑ…์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ฉด ๋‹ค๋ฅธ ๋งŽ์€ ๋ฌธ์ œ๋„ ํ•ด๊ฒฐ๋˜๋ฏ€๋กœ ์ง€๊ธˆ์€ ์ฒซ ๋ฒˆ์งธ ๋ฌธ์ œ์— ์ง‘์ค‘ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.


-

๊ธ€์ž์ˆ˜ ์ œํ•œ์— ๋‚˜์ค‘์— ์ถ”๊ฐ€ ๋ฒˆ์—ญํ•จ