๊ฒ์์ ๋ง๋ค๋ค๋ณด๋ฉด ์์ ํ ์ผ์ด ์์ฃผ ์๋ค
๋ง์ฝ ์ถ์ ์ ์ด๋ผ๋ฉด ์ด์ ๋ฒ์ ์์ ์ ์ฅํ ์ธ์ด๋ธ ๋ฐ์ดํฐ๋ ์ ๊ฒฝ ์ธ ํ์ ์์ง๋ง ์ด๋ฏธ ์ถ์ ํ๋ค๋ฉด?
๊ธฐ์กด ์ธ์ด๋ธ ๋ฐ์ดํฐ๋ก๋ ๋ก๋ฉ์ด ๊ฐ๋ฅํ๋๋ก ํด์ผ ํ ๊ฒ์ด๋ค.
๊ทธ๋์ ์ด์ ์ ๋ง๋ค๋ ์ฝ๋์์ ์ธ์ด๋ธ ํ์ผ ๋ง์ด๊ทธ๋ ์ด์
๋ถ๋ถ์ ๋ค๊ณ ์๋ค.
์ ์์ ๋ฑ ๋น๋ฉ ๋ก๊ทธ๋ผ์ดํฌ๋ผ์ ์ธ์ด๋ธ ํ์ผ์ ์ค์๋๊ฐ ๋ณด๋ค ๋จ์ด์ง๊ธฐ ๋๋ฌธ์ย
ํด๋น ์ธ์ด๋ธ๋ฅผ ํ๊ธฐํ ์ง ์ฌ์ฉํ ์ง ์ฌ๋ถ๋ ๋ค์ด๊ฐ๋ can_migration ํจ์๋ ์๋๊ฑธ ๋ณผ ์ ์๋ค.
@abstract
class_name Migration_Base
func migrate(data: Dictionary) -> Dictionary:
data.version = update_version()
return data
@abstract
func update_version() -> String
@abstract
func can_migration() -> bool
# class_name Migration_0_0_1
extends Migration_Base
# PlayerData.base_data.speed_increment ์ถ๊ฐ๋จ
func migrate(data: Dictionary) -> Dictionary:
# 0.0.1 -> 0.0.2
data = super.migrate(data)
data.player.base_data.speed_increment = 0.1
return data
func update_version() -> String:
return "0.0.2"
func can_migration() -> bool:
return true
์ง๊ธ ๋ง๋๋ ๊ฒ์์ ๋ฐฉ์นํ ๊ฒ์์ผ๋ก ์๋ฌด๋ฆฌ ๋ชปํด๋ 4์๊ฐ ์ด์์ ํ๋ ์ด ํ์์ ๋ณด์ฅํ ๊ฑด๋ฐ
์ด๊ฑธ ๋ฒ์ ์ฌ๋ผ๊ฐ๋ค๊ณ ์ฒ์๋ถํฐ ๋ค์ ํ๋ผ๊ณ ํ๋ฉด ์๋๋ค
์ผ๋จ ๋ง์ด๊ทธ๋ ์ด์
์์
์ ์ ์ธ์ด๋ธ ํ์ผ ๊ตฌ์กฐ๋ถํฐ ๋ณด์
class_name SaveLoadUtils
const SAVE_DIR_PATH := "user://save"
static func save_dict_file(dict: Dictionary, file_name: String) -> void:
if not DirAccess.dir_exists_absolute(SAVE_DIR_PATH):
var error := DirAccess.make_dir_absolute(SAVE_DIR_PATH)
if error != OK:
prints("make_dir_error", file_name, error)
return
var file_path := SAVE_DIR_PATH.path_join(file_name)
var file := FileAccess.open(file_path, FileAccess.WRITE)
if not file:
prints("file_error", file_name, FileAccess.get_open_error())
return
var bytes := var_to_bytes(dict)
file.store_buffer(bytes)
file.close()
static func load_from_file(file_name: String) -> Dictionary:
var file_path := SAVE_DIR_PATH.path_join(file_name)
if not FileAccess.file_exists(file_path):
prints("file_not_found", file_name)
return {}
var file := FileAccess.open(file_path, FileAccess.READ)
if not file:
prints("file_error", file_name, FileAccess.get_open_error())
return {}
var bytes := file.get_buffer(file.get_length())
file.close()
var data: Variant = bytes_to_var(bytes)
if typeof(data) != TYPE_DICTIONARY:
prints("invalid_save_data", file_name)
return {}
return data as Dictionary
์ง๊ธ ํ๋ก์ ํธ๋ ์ ์ฝ๋๋ก ์ธ์ด๋ธ ๋ก๋๋ฅผ ๋ด๋นํ๊ณ ์๋ค.
๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๋จ์ผ Dictionary๋ก ๋ณํ ํ var_to_bytes ํ๋ ๊ตฌ์กฐ์ด๋ค.
json ์ ์ฌ์ฉํ ๊น ํ์ง๋ง ์ด์งํผ ์ธ๋ถ์์ ์ด๊ณ ์์ ํ ํ์๋ ์๊ณ json์ด ๊ฐ์ง๋ ํน๋ณํ ์ด์ ๋ ์์ด์ ์ด๋ฐ ๊ตฌ์กฐ๋ฅผ ์ฌ์ฉํ๋ค.
์ธ์ด๋ธ ๊ฐ๋ฅํ ๋ชจ๋ ๋ฐ์ดํฐ๋ ์๋ 2๊ฐ์ง ํจ์๋ฅผ ๊ตฌํํ๊ณ ย GameManager ๋ถํฐ ์์ํด to_dict๋ฅผ ํธ์ถํด ๊ฐ๋ ๋ฐฉ์์ด๋ค
func to_dict() -> Dictionary:
static func deserialize(dict: Dictionary) -> T:
๊ทธ๋์ ๋ง๋ 0.0.1 => 0.0.2 ๋ก ๋ง์ด๊ทธ๋ ์ด์
ํ๋ ์ฝ๋์ด๋ค
extends Migration_Base
# GARBAGE_COLLECTION => BUBBLE_DESTROY ์ผ๋ก ๋ณ๊ฒฝ๋จ
# ์ผ๋ถ ์์น ๋ณ๊ฒฝํจ
func migrate(data: Dictionary) -> Dictionary:
# 0.0.1 -> 0.0.2
data = super.migrate(data)
# Enum.Upgrade_Type.CRITICAL_HITBOX = 1163
# Enum.Upgrade_Mod_Target.CORNER_GOLD = 1102
if data.upgrade.upgrades.has(1163) and data.upgrade.upgrades[1163].current_level > 0:
update_value(data, 1102, "critical_hitbox", Enum.Upgrade_Mod_Op.ADD, 400)
...
# Enum.Upgrade_Mod_Target.BUBBLE_GOLD_PER_SECOND = 3008
# Enum.Upgrade_Mod_Target.BUBBLE_DESTROY_GOLD = 3009
update_value(data, 3008, "default_value_placeholder", Enum.Upgrade_Mod_Op.ADD, 0.0)
update_value(data, 3009, "default_value_placeholder", Enum.Upgrade_Mod_Op.ADD, 0.0)
return data
func update_value(data: Dictionary, stat_id: int, id: String, op: Enum.Upgrade_Mod_Op, value: float) -> Dictionary:
var temp_states: Dictionary = data.upgrade.stat_states[stat_id]
for entry_dict in temp_states.get("applied_modifiers", []):
if entry_dict.source_id == id:
entry_dict.value = value
entry_dict.op = op
break
return data
func update_version() -> String:
return "0.0.2"
func can_migration() -> bool:
return true
์ ๋์ํ๋ ์์ ํฌ์ฑ์ด๊ณ ย
Upgrade_BubbleDestroy ์ฐธ์กฐ๊ฐ ์์ด ๋์ค์ ์ด๋ป๊ฒ ๋ ์ง ๋ชจ๋ฅด๋ ๋จ์ ์ด ์์๋ค
100% ํ๋ฅ ๋ก ๋์ค์ ํ๋๊ฐ ๋นผ๋จน์ด์ ์๋ํ ์์น๊ฐ ์๋ ์๋ชป๋ ๊ฐ์ด ๋ค์ด๊ฐ ํ๊ฒฝ์ด์๋ค.
์ด๋ฐ์์ผ๋ก ๊ตฌํ ํ ์ ๋ฐ์ ์๋๊ฒ ์
๊ทธ๋ ์ด๋์ ์ค์ ์
๊ทธ๋ ์ด๋ ๋ฐ์ดํฐ๋ฅผ ์ง์ ๋ณ์กฐํ๋ ๊ตฌ์กฐ๋ผ์
์ฌ์ฉ์๊ฐ ์ด๋ฏธ ๊ตฌ๋งคํ์์ง๋ง ์์ ๋ ์
๊ทธ๋ ์ด๋๋ฅผ ์ ์ฉํ๋ ค๋ฉด ์ด๊ฒ ์ต์ ์ด์๋ค.
func _on_purchased() -> void:
data.current_value += level_upgrade_amounts[current_level - 1]
GameManager.upgrade.add_stat_modifier(
self,
Enum.Upgrade_Mod_Target.ALL_GOLD,
Enum.Upgrade_Mod_Op.MUL,
level_upgrade_amounts[current_level - 1]
)
์ด๋ฐ์์ผ๋ก _on_purchased์์ ์์ ํ๋ ๋ฐฉ์์ด๋ค
๋ฐ๋ก ์ธ๋ถ ๋ฐ์ดํฐ๋ก ๋ฝ์์ผ๋ฉด ์ฝ๊ฒ ๊ฐ๋ฅํ๋ฐ modifier ๋ง๊ณ ๋ค๋ฅธ ๋์ ํ ๊ฒฝ์ฐ๋ ์์ด ํ๋์ฝ๋ฉ ํ๋ค
๊ทธ๋์ ๊ตฌ๋งค์ ๋์์ ์์ ํ์๋ค
๋จผ์ UpgradePurchaseEffectContext๋ฅผ ๋ง๋ค์๋ค
@abstract
class_name UpgradePurchaseEffectContext
extends RefCounted
@abstract
func add_stat_modifier(
source_upgrade: Upgrade_Base,
target: Enum.Upgrade_Mod_Target,
op: Enum.Upgrade_Mod_Op,
value: float
) -> void
๊ทธ๋ค์ ์
๊ทธ๋์ด๋ ๊ตฌ์
์ ์ฌ์ฉํ UpgradeDataPurchaseEffectContext ๋ฅผ ๋ง๋ค์๋ค ์์ฑ์์์ upgrade_data ๋ฐ์์ ์ฐ๋์์ด๋ค
class_name UpgradeDataPurchaseEffectContext
extends UpgradePurchaseEffectContext
var upgrade_data
func _init(p_upgrade_data) -> void:
upgrade_data = p_upgrade_data
func add_stat_modifier(
source_upgrade: Upgrade_Base,
target: Enum.Upgrade_Mod_Target,
op: Enum.Upgrade_Mod_Op,
value: float
) -> void:
upgrade_data.add_stat_modifier(source_upgrade, target, op, value)
๊ทธ๋ฆฌ๊ณ ์ด๊ฑด ์
๊ทธ๋ ์ด๋ ๋ณต๊ตฌ์ ์ฌ์ฉํ ๊ฑฐ
class_name RawPurchaseEffectContext
extends UpgradePurchaseEffectContext
var modifier_entries_by_target := {}
func add_stat_modifier(
source_upgrade: Upgrade_Base,
target: Enum.Upgrade_Mod_Target,
op: Enum.Upgrade_Mod_Op,
value: float
) -> void:
if value == 0.0:
return
if not modifier_entries_by_target.has(target):
modifier_entries_by_target[target] = []
var modifier_entries: Array = modifier_entries_by_target[target]
for entry_dict in modifier_entries:
if entry_dict["source_id"] == source_upgrade.id and entry_dict["op"] == op:
entry_dict["value"] = float(entry_dict["value"]) + value
return
modifier_entries.append({
"source_id": source_upgrade.id,
"source_name": source_upgrade.upgrade_name,
"op": op,
"value": value,
})
๊ทธ๋ค์์ ๊ตฌ๋งค์ ๋์์ ์์ ํ์
func _apply_purchase_effect(context: UpgradePurchaseEffectContext, level: int) -> void:
data.current_value += level_upgrade_amounts[level - 1]
context.add_stat_modifier(
self,
Enum.Upgrade_Mod_Target.ALL_GOLD,
Enum.Upgrade_Mod_Op.MUL,
level_upgrade_amounts[level - 1]
)
context๋ฅผ ๋ฐ์์ ๋์ํ๋๋ก ์์ ํ์
์ด๋ฌ๋ฉด ๋ค๋ฅธ context๋ฅผ ์ ๋ฌํด์ ๋ญ๊ฐ ์์ ๋์๋์ง ์ ์ ์๋ค
๋ฌด์กฐ๊ฑด ๊ตฌ์
์๋ง ๋์ํ๊ณ ๋ค์ ๋ก๋ฉํ๋ฉด ์๋๋๊ฑด ์ด๊ฑธ ๋ง๋ค์ด์ ๋๋ฆฌ๊ธฐ๋ก ํ์
func _on_purchased_side_effect() -> void:
pass
๊ทธ๋ผ ๋ก์ง์ ์ด๋ฐ์์ผ๋ก ์์ ํ ์ ์์
Upgrade_BubbleDestroy๋ class ์ด๋ฆ์ด ๋ณ๊ฒฝ๋ ๊ฒฝ์ฐ์ธ๋ฐ ์ด๊ฑด ์์ฃผ ์์ง ์์๊ฒ ๊ฐ์์ ๊ทธ๋ฅ ๋ฝ์์ ๋ฃ๋์์ผ๋ก ์ฒ๋ฆฌํจ
extends Migration_Base
func migrate(data: Dictionary) -> Dictionary:
# 0.0.1 -> 0.0.2
data = super.migrate(data)
# Enum.Upgrade_Type.ZERO_PIXEL = 1042
# Enum.Upgrade_Mod_Target.CORNER_GOLD = 1102
if data.upgrade.upgrades.has(1042):
mark_purchase_effect_dirty(data, 1042)
...
# Enum.Upgrade_Type.CRITICAL_HITBOX = 1163
# Enum.Upgrade_Mod_Target.CORNER_GOLD = 1102
if data.upgrade.upgrades.has(1163):
mark_purchase_effect_dirty(data, 1163)
# Enum.Upgrade_Mod_Target.BUBBLE_GOLD_PER_SECOND = 3008
# Enum.Upgrade_Mod_Target.BUBBLE_DESTROY_GOLD = 3009
update_value(data, 3008, "default_value_placeholder", Enum.Upgrade_Mod_Op.ADD, 0.0)
update_value(data, 3009, "default_value_placeholder", Enum.Upgrade_Mod_Op.ADD, 0.0)
return data
func update_version() -> String:
return "0.0.2"
func can_migration() -> bool:
return true
์ต์ข
์ ์ผ๋ก ๋ง์ด๊ทธ๋ ์ด์
์์ ์
๋ฐ์ดํธ ๋ ๊ฒ๋ค ๋ํฐ ํ๋๊ทธ๋ง ์ค์ ํ๋ฉดย
์์์ ๋ค์ ๋ถ๋ฌ์ค๋๋ก ํด์ ์์น ๋ฒ๊ฒฝ์๋ ๋์ ํ ์ ์๋๋ก ํ์๋ค
์ด๋ฌ๋ฉด ์์ ์ฌ์ฉ์ ์ค์ฌ์ ํด๋จผ์๋ฌ๋ฅผ ๊ฐ์์ํฌ ์ ์์๊ฒ์ด๋ค
ํน์ ๊ฐ ์๋ชป ์
๋ ฅํ๋๊ฒ ์๋๋ผ ๋นผ๋จน๋ ๊ฒฝ์ฐ๋ ๋ง์ด๊ทธ๋ ์ด์
์ฝ๋ ์์ฑ์ AI์ ์์ํ๋ ์์ผ๋ก ํด๊ฒฐํ๋ค.
git diff ํ์ธํ๊ณ ์์์ ์์ฑํด์ค
extends Migration_Base
# ๋ถ์ ๋ฒ์: f75ece8a24772e0de99864a34e3beb722e0c1706 -> ํ์ฌ HEAD/์์
์ํ
# ๋ณ๊ฒฝ ์์ฝ:
# - FEVER_CHARGE_DURATION(2201)์ FEVER_CHARGE_PER_SECOND(2201) ์๋ฏธ๋ก ์ ํ
func migrate(data: Dictionary) -> Dictionary:
# 0.0.2 -> 0.0.3
data = super.migrate(data)
# Enum.Upgrade_Type.FEVER_TIME = 1122
# Enum.Upgrade_Mod_Target.FEVER_CHARGE_PER_SECOND = 2201
# source_id: fever_time
if data.upgrade.upgrades.has(1122):
mark_purchase_effect_dirty(data, 1122)
...
๋๊ธ 2