๐Ÿ“˜TIL - ๋ฉ”์ธ๋ฉ”๋‰ด ์ด์–ดํ•˜๊ธฐยท๋ถˆ๋Ÿฌ์˜ค๊ธฐ & ์กฐ์ž‘ํ‚ค ์•ˆ๋‚ด UI

HappyCircleยท2026๋…„ 7์›” 9์ผ

Unreal ๊ฐœ๋ฐœ

๋ชฉ๋ก ๋ณด๊ธฐ
162/163

๋ชฉ์ฐจ

  1. ๋ฉ”์ธ๋ฉ”๋‰ด "์ด์–ดํ•˜๊ธฐ" โ€” ์ตœ๊ทผ ์Šฌ๋กฏ ์ž๋™ ์„ ํƒ๊ณผ ์„œ๋ฒ„ ๊ถŒ์œ„ ๋กœ๋“œ ํ๋ฆ„
  2. ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ํŒจ๋„ โ€” ๋ชจ๋‹ฅ๋ถˆ ์ €์žฅ ์Šฌ๋กฏ ์œ„์ ฏ ์žฌ์‚ฌ์šฉ์˜ ์ œ์•ฝ 3๊ฐ€์ง€
  3. ํ€˜์ŠคํŠธ ์ง„ํ–‰ ์ €์žฅ/๋ณต์› โ€” ๋กœ๋“œํ•  ๋•Œ ์™„๋ฃŒ ์•Œ๋ฆผ์ด ๋‹ค์‹œ ์šฐ๋ฅด๋ฅด ๋œจ๋Š” ๋ฌธ์ œ
  4. ์ €์žฅ ์‹œ์  "์ถ”์  ํ€˜์ŠคํŠธ" ํ‘œ์‹œ โ€” HUD์˜ ๋กœ์ปฌ ์ƒํƒœ๋Š” ์„ธ์ด๋ธŒ์— ์—†๋‹ค
  5. ๋ฉ”์ธ๋ฉ”๋‰ด์—์„œ ํŒจ๋„์„ ๋‹ซ์œผ๋ฉด ์ปค์„œ๊ฐ€ ์ฃฝ๋Š” ๋ฌธ์ œ โ€” ๋ฌด์กฐ๊ฑด GameOnly ๋ณต์›์˜ ํ•จ์ •
  6. ์กฐ์ž‘ํ‚ค ์•ˆ๋‚ด ์œ„์ ฏ โ€” ์ผ์‹œ์ •์ง€ ๋ฉ”๋‰ด์—์„œ "ํŒจ๋„ ๊ต์ฒด"๋กœ ์—ฌ๋‹ซ๊ธฐ
  7. ์†Œํ”„ํŠธ ๋ ˆํผ๋Ÿฐ์Šค๋งŒ ์žˆ๋Š” UI๋Š” ํŒจํ‚ค์ง•์—์„œ ํ†ต์งธ๋กœ ๋น ์ง„๋‹ค
  8. ์ผ์‹œ์ •์ง€ "๋ฉ”์ธ ๋ฉ”๋‰ด๋กœ"๊ฐ€ ์กฐ์šฉํžˆ ๋ฌด์‹œ๋œ ์ด์œ  โ€” ์„ธ์…˜ ์ƒํƒœ๋จธ์‹ ์˜ ์ „ํ™˜ ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ

1. ๋ฉ”์ธ๋ฉ”๋‰ด "์ด์–ดํ•˜๊ธฐ" โ€” ์ตœ๊ทผ ์Šฌ๋กฏ ์ž๋™ ์„ ํƒ๊ณผ ์„œ๋ฒ„ ๊ถŒ์œ„ ๋กœ๋“œ ํ๋ฆ„

๋ชฉํ‘œ

๋ฉ”์ธ๋ฉ”๋‰ด์—์„œ ํšŒ์ƒ‰์œผ๋กœ ์ฃฝ์–ด ์žˆ๋˜(SetIsEnabled(false) + TODO ์ฃผ์„) "์ด์–ดํ•˜๊ธฐ"ยท"๋ถˆ๋Ÿฌ์˜ค๊ธฐ" ๋ฒ„ํŠผ์„ ์‹ค์ œ๋กœ ๋™์ž‘ํ•˜๊ฒŒ ํ•œ๋‹ค. "์ด์–ดํ•˜๊ธฐ"๋Š” ์Šฌ๋กฏ ์„ ํƒ ์—†์ด ๊ฐ€์žฅ ์ตœ๊ทผ์— ์ €์žฅํ•œ ์Šฌ๋กฏ์„ ์ฆ‰์‹œ ๋กœ๋“œํ•ด์•ผ ํ•œ๋‹ค.

๊ตฌํ˜„ โ€” "๊ฐ€์žฅ ์ตœ๊ทผ"์˜ ํŒ์ •์„ ํƒ€์ž„์Šคํƒฌํ”„ ๋ฌธ์ž์—ด ๋น„๊ต๋กœ

์„ธ์ด๋ธŒ ๋ฉ”ํƒ€์— ์ด๋ฏธ ์žˆ๋˜ SaveTimestamp๊ฐ€ "%Y-%m-%d %H:%M" ํ˜•์‹์ด๋ผ, ํŒŒ์‹ฑ ์—†์ด ๋ฌธ์ž์—ด ๋น„๊ต(>)๋งŒ์œผ๋กœ ์‹œ๊ฐ„์ˆœ ์ •๋ ฌ์ด ์„ฑ๋ฆฝํ•œ๋‹ค(์ž๋ฆฟ์ˆ˜ ๊ณ ์ • + ํฐ ๋‹จ์œ„๋ถ€ํ„ฐ ๋‚˜์—ด๋˜๋Š” ํ˜•์‹์ด๋ผ ์‚ฌ์ „์ˆœ == ์‹œ๊ฐ„์ˆœ). GetMostRecentSaveSlotIndex()๋Š” ์กด์žฌํ•˜๋Š” ์Šฌ๋กฏ์„ ์ˆœํšŒํ•˜๋ฉฐ ์ด ๋น„๊ต ํ•˜๋‚˜๋กœ ์ตœ์‹  ์Šฌ๋กฏ์„ ๊ณ ๋ฅธ๋‹ค.

// SaveTimestamp๋Š” "%Y-%m-%d %H:%M" ํ˜•์‹์ด๋ผ ๋ฌธ์ž์—ด ๋น„๊ต๋งŒ์œผ๋กœ ์‹œ๊ฐ„์ˆœ ์ •๋ ฌ์ด ์„ฑ๋ฆฝํ•œ๋‹ค.
if (BestSlot == INDEX_NONE || Save->SaveTimestamp > BestTimestamp) { ... }

๋ฒ„ํŠผ โ†’ ๋กœ๋“œ๊นŒ์ง€์˜ ํ๋ฆ„์€ ์‹ ๊ทœ ๊ฒŒ์ž„๊ณผ ๋™์ผํ•œ ์„œ๋ฒ„ ๊ถŒ์œ„ ๊ฒฝ๋กœ๋กœ ํ†ต์ผํ–ˆ๋‹ค:

ContinueButton ํด๋ฆญ โ†’ PC->RequestContinueGame() โ†’ Server_RequestContinueGame (RPC)
โ†’ GameMode::HandleContinueGame (IsRequestorHost ๊ฒ€์‚ฌ)
โ†’ SaveSubsystem::LoadFromSlot() ์„ฑ๊ณต ์‹œ โ†’ GameState::TransitionTo(InGame)

๋ฒ„ํŠผ ํ™œ์„ฑํ™” ์กฐ๊ฑด๋„ HasSaveGame() ๊ฒฐ๊ณผ(bSaveDataExists)๋กœ ๋ฐ”๊ฟ”, ์„ธ์ด๋ธŒ๊ฐ€ ์—†์œผ๋ฉด ์ด์–ดํ•˜๊ธฐ/๋ถˆ๋Ÿฌ์˜ค๊ธฐ๊ฐ€ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋น„ํ™œ์„ฑํ™”๋œ๋‹ค.

๋ฐฐ์šด ์ 

์ •๋ ฌ ๊ฐ€๋Šฅํ•œ ๋ฌธ์ž์—ด ํฌ๋งท์€ ๊ทธ ์ž์ฒด๋กœ ์ธ๋ฑ์Šค๋‹ค. FDateTime ์žฌํŒŒ์‹ฑ์ด๋‚˜ ๋ณ„๋„ ํ•„๋“œ ์ถ”๊ฐ€ ์—†์ด, ์ €์žฅ ํ˜•์‹์ด "๊ณ ์ • ์ž๋ฆฟ์ˆ˜ + ํฐ ๋‹จ์œ„ ์šฐ์„ "์ด๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฌธ์ž์—ด ๋น„๊ต๋กœ ์ตœ์‹  ํŒ์ •์ด ๋๋‚œ๋‹ค โ€” ๋‹จ, ์ด ์„ฑ์งˆ์€ ํฌ๋งท์— ์˜์กดํ•˜๋ฏ€๋กœ ํฌ๋งท์„ ๋ฐ”๊พธ๋Š” ์ˆœ๊ฐ„ ๊ฐ™์ด ๊นจ์ง„๋‹ค๋Š” ๊ฑธ ์ฃผ์„์œผ๋กœ ๋‚จ๊ฒจ๋‘ฌ์•ผ ํ•œ๋‹ค.
๋ฉ”๋‰ด ๊ธฐ๋Šฅ๋„ ๊ฒŒ์ž„ํ”Œ๋ ˆ์ด์™€ ๊ฐ™์€ ๊ถŒ์œ„ ๊ฒฝ๋กœ๋ฅผ ํƒœ์šด๋‹ค. "๋ฉ”๋‰ด ๋ฒ„ํŠผ์ธ๋ฐ ๋ญ" ํ•˜๊ณ  ํด๋ผ์ด์–ธํŠธ์—์„œ ์ง์ ‘ ๋กœ๋“œํ•˜์ง€ ์•Š๊ณ  Server RPC โ†’ GameMode ํ•ธ๋“ค๋Ÿฌ(ํ˜ธ์ŠคํŠธ ๊ฒ€์‚ฌ ํฌํ•จ) ๊ตฌ์กฐ๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๋ฉด, ์ดํ›„ ๋ฉ€ํ‹ฐํ”Œ๋ ˆ์ด ํ™•์žฅ ์‹œ ๋ฉ”๋‰ด ๊ณ„์—ด๋งŒ ๋”ฐ๋กœ ๊ณ ์น  ์ผ์ด ์—†๋‹ค.


2. ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ํŒจ๋„ โ€” ๋ชจ๋‹ฅ๋ถˆ ์ €์žฅ ์Šฌ๋กฏ ์œ„์ ฏ ์žฌ์‚ฌ์šฉ์˜ ์ œ์•ฝ 3๊ฐ€์ง€

๋ชฉํ‘œ

"๋ถˆ๋Ÿฌ์˜ค๊ธฐ" ํ™”๋ฉด(WBP_LoadGame + URetrieveLoadGameWidget)์˜ ์Šฌ๋กฏ ๋ฆฌ์ŠคํŠธ๋ฅผ, ๋ชจ๋‹ฅ๋ถˆ ์ €์žฅ ํƒญ์—์„œ ์ด๋ฏธ ์“ฐ๊ณ  ์žˆ๋Š” WBP_SaveSlotEntry๋ฅผ ์—์…‹ ์ˆ˜์ • ์—†์ด ๊ทธ๋Œ€๋กœ ์žฌ์‚ฌ์šฉํ•ด์„œ ๋งŒ๋“ ๋‹ค(๋””์ž์ธ ์ผ๊ด€์„ฑ + ์ค‘๋ณต ์œ„์ ฏ ๋ฐฉ์ง€).

์›์ธ โ€” ๊ณต์œ  WBP๋ฅผ "๊ทธ๋Œ€๋กœ" ์žฌ์‚ฌ์šฉํ•˜๋Š” ๊ฑธ ๋ง‰๋Š” ์ œ์•ฝ์ด ์„ธ ๊ฐ€์ง€ ์žˆ์—ˆ์Œ

  1. OwnerMenu ํƒ€์ž… ๊ณ ์ •: WBP_SaveSlotEntry์˜ ํด๋ฆญ ํ†ต์ง€ ๋Œ€์ƒ์ธ OwnerMenu ๋ณ€์ˆ˜๊ฐ€ WBP_BonfireMenu_C ์ „์šฉ ํƒ€์ž…์œผ๋กœ ์„ ์–ธ๋ผ ์žˆ์–ด, ๋‹ค๋ฅธ ๋ฉ”๋‰ด๊ฐ€ ์ž์‹ ์„ Owner๋กœ ๋“ฑ๋กํ•  ์ˆ˜ ์—†๋‹ค.
  2. SlotIndex ์ธ์Šคํ„ด์Šค ํŽธ์ง‘ ๋ถˆ๊ฐ€: ์—”ํŠธ๋ฆฌ์˜ ์Šฌ๋กฏ ๋ฒˆํ˜ธ ๋ณ€์ˆ˜๊ฐ€ Instance Editable์ด ์•„๋‹ˆ์–ด์„œ, ๋””์ž์ด๋„ˆ์—์„œ 5๊ฐœ๋ฅผ ๋ฐฐ์น˜ํ•ด๋„ ๊ฐ๊ฐ์— 0~4๋ฅผ ์ง€์ •ํ•  ๋ฐฉ๋ฒ•์ด ์—†๋‹ค.
  3. ์—”ํŠธ๋ฆฌ์— ์—†๋Š” ์œ„์ ฏ: ์ธ๋„ค์ผ ์ด๋ฏธ์ง€ยท์ถ”์  ํ€˜์ŠคํŠธ ํ…์ŠคํŠธ์ฒ˜๋Ÿผ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ํ™”๋ฉด์—๋งŒ ํ•„์š”ํ•œ ์š”์†Œ๊ฐ€ ์—”ํŠธ๋ฆฌ WBP์— ์—†๋‹ค.

ํ•ด๊ฒฐ โ€” ์ „๋ถ€ "ํ˜ธ์ŠคํŠธ ์œ„์ ฏ์˜ C++"์—์„œ ํก์ˆ˜ํ•˜๊ณ  ๊ณต์œ  WBP๋Š” ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š์Œ

  1. OwnerMenu ๊ฒฝ์œ ๋ฅผ ํฌ๊ธฐํ•˜๊ณ , ์—”ํŠธ๋ฆฌ ๋‚ด๋ถ€์˜ Button์„ GetWidgetFromName์œผ๋กœ ์ฐพ์•„ C++์—์„œ ์ง์ ‘ OnClicked/OnHovered๋ฅผ ๋ฐ”์ธ๋”ฉํ–ˆ๋‹ค (HandleSlot0Clicked~HandleSlot4Clicked).
  2. ์Šฌ๋กฏ ๋ฒˆํ˜ธ๋Š” ScrollBox ๋‚ด ๋ฐฐ์น˜ ์ˆœ์„œ(ChildIndex)๋ฅผ ์œ ์ผํ•œ ๊ทผ๊ฑฐ๋กœ ์‚ผ์•˜๋‹ค โ€” "0~4 ์ˆœ์„œ๋กœ ๋ฐฐ์น˜ํ•  ๊ฒƒ"์ด WBP ์ชฝ ๊ณ„์•ฝ์ด ๋œ๋‹ค๋Š” ๊ฑธ ์ฃผ์„์œผ๋กœ ๋ช…์‹œ.
  3. ์—†๋Š” ์œ„์ ฏ์€ ๋Ÿฐํƒ€์ž„์— WidgetTree->ConstructWidget์œผ๋กœ ๋™์  ์ฃผ์ž…ํ–ˆ๋‹ค(์ธ๋„ค์ผ์šฉ SizeBox+Image, Text_QuestName). ๋‹จ ์—ฌ๊ธฐ์—” ํ•จ์ •์ด ์žˆ์—ˆ๋‹ค โ€” ๋ถ€๋ชจ๊ฐ€ CanvasPanel์ธ ์ž๋ฆฌ(์šฐ์ธก ํ”„๋ฆฌ๋ทฐ์˜ ํ€˜์ŠคํŠธ ํ…์ŠคํŠธ)๋Š” ๋™์  ์ƒ์„ฑ ์‹œ ์Šฌ๋กฏ ์„ค์ • ์—†์ด (0,0)์— ๋ถ™์–ด ํ™”๋ฉด ์ขŒ์ƒ๋‹จ์— ๋– ๋ฒ„๋ฆฌ๋ฏ€๋กœ, VerticalBox์ฒ˜๋Ÿผ ์ž๋™ ๋ฐฐ์น˜๋˜๋Š” ๋ถ€๋ชจ ์•„๋ž˜์—์„œ๋งŒ ๋™์  ์ƒ์„ฑ ํด๋ฐฑ์„ ๋‘๊ณ , CanvasPanel ์ž๋ฆฌ๋Š” WBP์— ์‹ค์ œ ๋ฐฐ์น˜๋œ ์œ„์ ฏ๋งŒ ์‚ฌ์šฉํ•˜๋„๋ก ๊ฐˆ๋ž๋‹ค.

๋ถ€๊ฐ€๋กœ, ๊ตฌํ˜• ์„ธ์ด๋ธŒ์˜ ์ธ๋„ค์ผ PNG๊ฐ€ ์•ŒํŒŒ 0์œผ๋กœ ์ €์žฅ๋ผ ํˆฌ๋ช…ํ•˜๊ฒŒ ๋ณด์ด๋Š” ๋ฌธ์ œ๋Š” ๋””์ฝ”๋”ฉ ์งํ›„ ํ”ฝ์…€ ์•ŒํŒŒ๋ฅผ 255๋กœ ๊ฐ•์ œํ•˜๋Š” ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜(DecodeThumbnail)์œผ๋กœ ๋กœ๋“œ ์‹œ์ ์— ํก์ˆ˜ํ–ˆ๊ณ , ํด๋ฆญ ์„ ํƒ ํ•˜์ด๋ผ์ดํŠธ(๊ณจ๋“œ ๊ธ€๋กœ์šฐ)๋Š” ๋ชจ๋‹ฅ๋ถˆ ๋ฉ”๋‰ด์™€ ๋™์ผ ๊ทœ์น™์œผ๋กœ ๋งž์ถฐ BonfireMenuWidget๊ณผ RetrieveLoadGameWidget ์–‘์ชฝ์— ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์ ์šฉํ–ˆ๋‹ค.

๋ฐฐ์šด ์ 

๊ณต์œ  WBP ์žฌ์‚ฌ์šฉ์˜ ์ง„์งœ ๋น„์šฉ์€ "์œ„์ ฏ์„ ๊ทธ๋ฆฌ๋Š” ๊ฒƒ"์ด ์•„๋‹ˆ๋ผ "๊ทธ ์œ„์ ฏ์˜ ์•”๋ฌต์  ๊ณ„์•ฝ"์— ์žˆ๋‹ค. OwnerMenu ํƒ€์ž…, ์ธ์Šคํ„ด์Šค ํŽธ์ง‘ ๋ถˆ๊ฐ€ ๋ณ€์ˆ˜, ์—†๋Š” ์„œ๋ธŒ์œ„์ ฏ โ€” ์…‹ ๋‹ค WBP๋ฅผ ๊ณ ์น˜๋ฉด ํ•œ ์ค„์ด์ง€๋งŒ, ๊ทธ๋Ÿฌ๋ฉด ์ด๋ฏธ ์“ฐ๋Š” ํ™”๋ฉด(๋ชจ๋‹ฅ๋ถˆ)์— ํšŒ๊ท€ ์œ„ํ—˜์ด ์ƒ๊ธด๋‹ค. ํ˜ธ์ŠคํŠธ ์ชฝ C++์—์„œ ํก์ˆ˜ํ•˜๋Š” ๋ฐฉ์‹์€ ์ฝ”๋“œ๊ฐ€ ๋Š˜์–ด๋‚˜๋Š” ๋Œ€์‹  ๊ณต์œ  ์—์…‹์˜ ๊ธฐ์กด ์‚ฌ์šฉ์ฒ˜๋ฅผ ์™„์ „ํžˆ ๋ณด์กดํ•œ๋‹ค.
๋™์  ์œ„์ ฏ ์ฃผ์ž…์€ ๋ถ€๋ชจ ํŒจ๋„ ํƒ€์ž…์„ ๋จผ์ € ๋ณธ๋‹ค. VerticalBox/HorizontalBox๋Š” AddChild๋งŒ์œผ๋กœ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ฐฐ์น˜๋˜์ง€๋งŒ CanvasPanel์€ ์Šฌ๋กฏ(์•ต์ปค/์˜คํ”„์…‹)์„ ์ง์ ‘ ์„ค์ •ํ•˜์ง€ ์•Š์œผ๋ฉด (0,0)์— ๋œฌ๋‹ค โ€” "ํด๋ฐฑ์œผ๋กœ ๋™์  ์ƒ์„ฑ"์ด ํ•ญ์ƒ ์•ˆ์ „ํ•œ ๊ฒŒ ์•„๋‹ˆ๋ผ, ๋ถ€๋ชจ๊ฐ€ ์ž๋™ ๋ฐฐ์น˜ํ˜•์ผ ๋•Œ๋งŒ ์•ˆ์ „ํ•˜๋‹ค.


3. ํ€˜์ŠคํŠธ ์ง„ํ–‰ ์ €์žฅ/๋ณต์› โ€” ๋กœ๋“œํ•  ๋•Œ ์™„๋ฃŒ ์•Œ๋ฆผ์ด ๋‹ค์‹œ ์šฐ๋ฅด๋ฅด ๋œจ๋Š” ๋ฌธ์ œ

๋ชฉํ‘œ

์„ธ์ด๋ธŒ์— ํ€˜์ŠคํŠธ ์ง„ํ–‰(์™„๋ฃŒ ์Šคํ… ๋ชฉ๋กยท์ถ”์  ์Šคํ…ยท๋Œ€ํ™” ๋ถ„๊ธฐ ์„ ํƒ ๊ธฐ๋ก)์„ ํฌํ•จ์‹œํ‚ค๊ณ , ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ์‹œ ๊ทธ๋Œ€๋กœ ๋ณต์›ํ•œ๋‹ค. ์ด๋•Œ ์ด๋ฏธ ์™„๋ฃŒํ–ˆ๋˜ ์Šคํ…๋“ค์˜ "ํ€˜์ŠคํŠธ ์™„๋ฃŒ!" ์•Œ๋ฆผ์ด ๋กœ๋“œ ์งํ›„ ์—ฐ์‡„๋กœ ์žฌ์ƒ๋˜๋ฉด ์•ˆ ๋œ๋‹ค.

์›์ธ โ€” ๋ณต์›์„ ๊ธฐ์กด CompleteStep ๊ฒฝ๋กœ๋กœ ํƒœ์šฐ๋ฉด ์•Œ๋ฆผ ์‹œ์Šคํ…œ์ด ์ „๋ถ€ "์ƒˆ ์™„๋ฃŒ"๋กœ ์ธ์‹ํ•จ

UQuestBranchComponent::CompleteStep์€ ์„ ํ–‰ ์กฐ๊ฑด ๊ฒ€์‚ฌ์™€ ์•Œ๋ฆผ ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ๊ฐ€ ๋ฌถ์—ฌ ์žˆ๋Š” ๊ฒŒ์ž„ํ”Œ๋ ˆ์ด์šฉ ์ง„์ž…์ ์ด๋‹ค. ์ €์žฅ๋œ ์Šคํ… N๊ฐœ๋ฅผ ์ด ๊ฒฝ๋กœ๋กœ ํ•˜๋‚˜์”ฉ ์žฌ์™„๋ฃŒ์‹œํ‚ค๋ฉด ์•Œ๋ฆผ ์„œ๋ธŒ์‹œ์Šคํ…œ๊ณผ ๋ณต์ œ ํด๋ผ์ด์–ธํŠธ์˜ OnRep_CompletedSteps๊ฐ€ ์ „๋ถ€ "๋ฐฉ๊ธˆ ์™„๋ฃŒ๋จ"์œผ๋กœ ํŒ์ •ํ•œ๋‹ค.

ํ•ด๊ฒฐ โ€” ์•Œ๋ฆผ ์—†๋Š” ์ „์šฉ ๋ณต์› API + ๋ฒ ์ด์Šค๋ผ์ธ ์žฌ์‹œ๋”ฉ + ๊ฐฑ์‹  ์‹ ํ˜ธ 1ํšŒ

๋ณต์› ์ „์šฉ API(MakeQuestSaveData/ApplyQuestSaveData)๋ฅผ ๋ถ„๋ฆฌํ•ด ์ƒํƒœ๋ฅผ ํ†ต์งธ๋กœ ๋ฎ์–ด์“ฐ๋˜, ์•Œ๋ฆผ์ด ๊ธฐ์ค€์œผ๋กœ ์‚ผ๋Š” ๋ฒ ์ด์Šค๋ผ์ธ๋“ค์„ ๋ณต์›๋œ ์ƒํƒœ๋กœ ์žฌ์‹œ๋”ฉํ–ˆ๋‹ค:

CompletedSteps = InCompletedSteps;
// OnRep_CompletedSteps๊ฐ€ ๋ณต์ œ ํด๋ผ์ด์–ธํŠธ์—์„œ ์ด ์Šคํ…๋“ค์„ "์ƒˆ๋กœ ์™„๋ฃŒ๋จ"์œผ๋กœ ์žฌ์•Œ๋ฆผํ•˜์ง€ ์•Š๋„๋ก ๊ธฐ์ค€์„ ์„ ๋งž์ถ˜๋‹ค.
LastSeenCompletedSteps = CompletedSteps;

ํ˜ธ์ถœ๋ถ€(SaveSubsystem)๋Š” ๋ณต์› ํ›„ QuestNotificationSubsystem->ResetBaseline()์œผ๋กœ ์•Œ๋ฆผ ์„œ๋ธŒ์‹œ์Šคํ…œ์˜ ๊ธฐ์ค€์„ ๋„ ๋งž์ถ˜ ๋’ค, UI(ํŠธ๋ž˜์ปค/ํ€˜์ŠคํŠธ๋กœ๊ทธ)์—๋Š” ๊ฐฑ์‹  ์‹ ํ˜ธ(Channel_Quest_StepChanged)๋ฅผ ๋”ฑ ํ•œ ๋ฒˆ๋งŒ ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธํ•œ๋‹ค. "๊ฐœ๋ณ„ ์ด๋ฒคํŠธ ์žฌ์ƒ"๊ณผ "์ตœ์ข… ์ƒํƒœ ํ†ต์ง€"๋ฅผ ๋ถ„๋ฆฌํ•œ ๊ฒƒ.

๋ฐฐ์šด ์ 

์ƒํƒœ ๋ณต์›์€ ์ƒํƒœ ์ „์ด๊ฐ€ ์•„๋‹ˆ๋‹ค. ๊ฒŒ์ž„ํ”Œ๋ ˆ์ด ์ง„์ž…์ (CompleteStep)์€ "์ „์ด"๋ฅผ ์ „์ œ๋กœ ์•Œ๋ฆผยท๊ฒ€์ฆ์ด ๋ฌถ์—ฌ ์žˆ์–ด์„œ, ๋กœ๋“œ ๋ณต์›์— ์žฌ์‚ฌ์šฉํ•˜๋ฉด ๋ถ€์ž‘์šฉ(์•Œ๋ฆผ ์—ฐ์‡„)์ด ๋”ฐ๋ผ์˜จ๋‹ค. ๋ณต์› ์ „์šฉ API๋ฅผ ๋ถ„๋ฆฌํ•˜๊ณ , ๊ทธ API๊ฐ€ ์ƒ๋žตํ•œ ์ฑ…์ž„(์•Œ๋ฆผ ๋ฒ ์ด์Šค๋ผ์ธ, UI ๊ฐฑ์‹ )์„ ํ˜ธ์ถœ๋ถ€ ๊ณ„์•ฝ์œผ๋กœ ๋ฌธ์„œํ™”ํ•˜๋Š” ํŽธ์ด ๋ช…ํ™•ํ–ˆ๋‹ค.
"์•Œ๋ฆผ ์•ˆ ํ•จ"๋งŒ์œผ๋กœ๋Š” ๋ถ€์กฑํ•˜๊ณ , ์•Œ๋ฆผ ์‹œ์Šคํ…œ์˜ '๊ธฐ์ค€์„ '๊นŒ์ง€ ์˜ฎ๊ฒจ์•ผ ํ•œ๋‹ค. ๋ณต์› ์‹œ์ ์— ์กฐ์šฉํžˆ ๋„˜์–ด๊ฐ€๋„ ๋ฒ ์ด์Šค๋ผ์ธ์ด ์˜› ์ƒํƒœ๋ฉด ๋‹ค์Œ ์ •์ƒ ์™„๋ฃŒ ๋•Œ ๋ฐ€๋ฆฐ ์ฐจ์ด๊ฐ€ ํ•œ๊บผ๋ฒˆ์— ์•Œ๋ฆผ์œผ๋กœ ํ„ฐ์ง„๋‹ค โ€” ์ƒํƒœ๋ฅผ ๋ฎ์–ด์“ฐ๋Š” ์ชฝ์ด ๋น„๊ต ๊ธฐ์ค€(LastSeen, Baseline)๋„ ํ•จ๊ป˜ ๋ฎ์–ด์“ธ ์ฑ…์ž„์ด ์žˆ๋‹ค.


4. ์ €์žฅ ์‹œ์  "์ถ”์  ํ€˜์ŠคํŠธ" ํ‘œ์‹œ โ€” HUD์˜ ๋กœ์ปฌ ์ƒํƒœ๋Š” ์„ธ์ด๋ธŒ์— ์—†๋‹ค

๋ชฉํ‘œ

์ €์žฅ ์Šฌ๋กฏ ๋ฆฌ์ŠคํŠธ์™€ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ํ”„๋ฆฌ๋ทฐ์— "์ด ์„ธ์ด๋ธŒ๊ฐ€ ์–ด๋А ํ€˜์ŠคํŠธ๋ฅผ ์ง„ํ–‰ ์ค‘์ด์—ˆ๋Š”์ง€"(ํ€˜์ŠคํŠธ๋ช… + ์ฒซ ๋ฏธ์™„๋ฃŒ ๋ชฉํ‘œ ๋ฌธ๊ตฌ)๋ฅผ ๋ณด์—ฌ์ค€๋‹ค.

์›์ธ โ€” "์ง€๊ธˆ ์–ด๋–ค ํ€˜์ŠคํŠธ๋ฅผ ์ถ”์  ์ค‘์ธ๊ฐ€"๋Š” HUD ์œ„์ ฏ์˜ ๋กœ์ปฌ ์ƒํƒœ๋ผ ์„ธ์ด๋ธŒ ํŒŒ์ดํ”„๋ผ์ธ์—์„œ ์ ‘๊ทผ ๋ถˆ๊ฐ€

ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ๋ช…์‹œ์ ์œผ๋กœ ์ถ”์ ์„ ๋ฐ”๊ฟจ๋Š”์ง€๋Š” QuestTrackerViewModel์˜ ๋กœ์ปฌ ์ƒํƒœ๋กœ๋งŒ ์กด์žฌํ•˜๊ณ , ์„ธ์ด๋ธŒ๊ฐ€ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” GameState/์ปดํฌ๋„ŒํŠธ์—๋Š” ์—†๋‹ค. ๊ทธ๋ ‡๋‹ค๊ณ  ์„ธ์ด๋ธŒ๋ฅผ ์œ„ํ•ด HUD ์ƒํƒœ๋ฅผ GameState๋กœ ๋Œ์–ด์˜ฌ๋ฆฌ๋Š” ๊ฑด ๋ฐฐ๋ณด๋‹ค ๋ฐฐ๊ผฝ์ด ์ปค์ง„๋‹ค.

ํ•ด๊ฒฐ โ€” ์ €์žฅ ์‹œ์ ์— HUD ํŠธ๋ž˜์ปค์™€ ๋™์ผํ•œ "๊ธฐ๋ณธ ์„ ์ • ๋กœ์ง"์œผ๋กœ ์žฌ๊ณ„์‚ฐํ•ด์„œ ๋ฌธ์ž์—ด๋กœ ๋ฐ•์ œ

QuestTrackerViewModel::Recompute์˜ ๊ธฐ๋ณธ ์„ ์ • ๊ทœ์น™(DisplayOrder ์ •๋ ฌ โ†’ ์ž ๊ธˆ ํ•ด์ œ๋๊ณ  ๋ฏธ์™„๋ฃŒ์ธ ํ€˜์ŠคํŠธ ์ค‘ ๋ฉ”์ธ ์šฐ์„ , ์—†์œผ๋ฉด ์•„๋ฌด๊ฑฐ๋‚˜)์„ ์ €์žฅ ์‹œ์ ์— ๊ทธ๋Œ€๋กœ ๋‹ค์‹œ ๋Œ๋ ค์„œ, ๊ฒฐ๊ณผ๋ฅผ TrackedQuestName/TrackedQuestObjective ํ‘œ์‹œ์šฉ ๋ฌธ์ž์—ด๋กœ ์„ธ์ด๋ธŒ ๋ฉ”ํƒ€์— ์ €์žฅํ–ˆ๋‹ค. UI(๋ชจ๋‹ฅ๋ถˆ ์Šฌ๋กฏยท๋ถˆ๋Ÿฌ์˜ค๊ธฐ ๋ฆฌ์ŠคํŠธยทํ”„๋ฆฌ๋ทฐ)๋Š” ์ด ๋ฌธ์ž์—ด์„ ๊ทธ๋Œ€๋กœ ๋ฟŒ๋ฆฌ๊ธฐ๋งŒ ํ•œ๋‹ค.

๋ฐฐ์šด ์ 

์„ธ์ด๋ธŒ ๋ฉ”ํƒ€์—๋Š” "์žฌ๊ณ„์‚ฐ ๊ฐ€๋Šฅํ•œ ์ฐธ์กฐ"๋ณด๋‹ค "๋ฐ•์ œ๋œ ํ‘œ์‹œ ๋ฌธ์ž์—ด"์ด ๋งž์„ ๋•Œ๊ฐ€ ์žˆ๋‹ค. ์Šฌ๋กฏ ๋ฆฌ์ŠคํŠธ๋Š” ๋กœ๋“œ ์ „์— ํ‘œ์‹œ๋ผ์•ผ ํ•˜๋ฏ€๋กœ ์–ด์ฐจํ”ผ ์„ธ์ด๋ธŒ ํŒŒ์ผ ์•ˆ์—์„œ ์ž๊ธ‰์ž์กฑํ•ด์•ผ ํ•œ๋‹ค โ€” ํƒœ๊ทธ๋ฅผ ์ €์žฅํ•˜๊ณ  ํ‘œ์‹œ ์‹œ์ ์— DataTable์„ ๋‹ค์‹œ ์กฐํšŒํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค, ์ €์žฅ ์‹œ์ ์— ํ™•์ •๋œ ๋ฌธ์ž์—ด์„ ๋„ฃ๋Š” ์ชฝ์ด ๋กœ๋“œ ์ „ UI์— ์•ˆ์ „ํ•˜๋‹ค(ํ…Œ์ด๋ธ”์ด ๋ฐ”๋€Œ์–ด๋„ "๊ทธ๋•Œ ๊ทธ ์ƒํƒœ"๋ฅผ ๋ณด์—ฌ์ค€๋‹ค).
์›๋ณธ ์ƒํƒœ์— ์ ‘๊ทผํ•  ์ˆ˜ ์—†์œผ๋ฉด "๊ฐ™์€ ๋กœ์ง์œผ๋กœ ์žฌ๊ณ„์‚ฐ"์ด ์ฐจ์„ ์ด๊ณ , ๊ทธ ์‚ฌ์‹ค์„ ์ฃผ์„์œผ๋กœ ๋‚จ๊ธด๋‹ค. ๋ช…์‹œ์  ์ถ”์  ๋ณ€๊ฒฝ๊นŒ์ง€ ๋ฐ˜์˜ํ•˜์ง€ ๋ชปํ•˜๋Š” ๊ทผ์‚ฌ์น˜๋ผ๋Š” ํ•œ๊ณ„๋Š”, ์ฝ”๋“œ๋งŒ ๋ด์„œ๋Š” ์•ˆ ๋ณด์ด๋ฏ€๋กœ "HUD ๋กœ์ปฌ ์ƒํƒœ๋ผ ์„ธ์ด๋ธŒ์— ์—†์Œ, ๊ธฐ๋ณธ๊ฐ’ ๊ธฐ์ค€์œผ๋กœ ๊ณ„์‚ฐ"์„ ์ฃผ์„์œผ๋กœ ๋ฐ•์•„๋’€๋‹ค.


5. ๋ฉ”์ธ๋ฉ”๋‰ด์—์„œ ํŒจ๋„์„ ๋‹ซ์œผ๋ฉด ์ปค์„œ๊ฐ€ ์ฃฝ๋Š” ๋ฌธ์ œ โ€” ๋ฌด์กฐ๊ฑด GameOnly ๋ณต์›์˜ ํ•จ์ •

๋ชฉํ‘œ

๋ฉ”์ธ๋ฉ”๋‰ด์—์„œ "๋ถˆ๋Ÿฌ์˜ค๊ธฐ"๋‚˜ "์„ค์ •" ํŒจ๋„์„ ์—ด์—ˆ๋‹ค ๋‹ซ์•„๋„ ๋ฉ”๋‰ด๊ฐ€ ๊ณ„์† ์กฐ์ž‘ ๊ฐ€๋Šฅํ•ด์•ผ ํ•œ๋‹ค.

์›์ธ โ€” ํŒจ๋„ ๋‹ซ๊ธฐ ๊ณตํ†ต ๊ฒฝ๋กœ๊ฐ€ ์ž…๋ ฅ ๋ชจ๋“œ๋ฅผ ๋ฌด์กฐ๊ฑด GameOnly๋กœ ๋˜๋Œ๋ฆฌ๊ณ  ์žˆ์—ˆ์Œ

RemoveActivePanelImmediately()๊ฐ€ ํŒจ๋„์„ ๋‹ซ์€ ๋’ค FInputModeGameOnly + ์ปค์„œ ์ˆจ๊น€์„ ์„ธ์…˜ ์ƒํƒœ์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ๊ฐ•์ œํ–ˆ๋‹ค. ์ธ๊ฒŒ์ž„์—์„œ ์ธ๋ฒคํ† ๋ฆฌ๋ฅผ ๋‹ซ์„ ๋•Œ๋Š” ์˜ฌ๋ฐ”๋ฅธ ๋™์ž‘์ด์ง€๋งŒ, ๋ฉ”์ธ๋ฉ”๋‰ด(UIOnly + ์ปค์„œ)์—์„œ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ/์„ค์ •์„ ๋‹ซ์œผ๋ฉด ์ปค์„œ์™€ UI ์ž…๋ ฅ์ด ์ฃฝ์–ด ๋ฉ”๋‰ด ์ „์ฒด๊ฐ€ ํด๋ฆญ ๋ถˆ๊ฐ€ ์ƒํƒœ๊ฐ€ ๋๋‹ค โ€” ์ด ๊ณตํ†ต ๊ฒฝ๋กœ๋ฅผ ์“ฐ๋Š” ํŒจ๋„์ด ๋ฉ”์ธ๋ฉ”๋‰ด์—์„œ๋„ ์—ด๋ฆฌ๊ธฐ ์‹œ์ž‘ํ•˜๋ฉด์„œ ์ž ๋ณตํ•ด ์žˆ๋˜ ์ „์ œ๊ฐ€ ๊นจ์ง„ ๊ฒƒ.

ํ•ด๊ฒฐ โ€” ๋‹ซ์€ ๋’ค์˜ ์ž…๋ ฅ ๋ชจ๋“œ๋ฅผ "ํ˜„์žฌ ์„ธ์…˜ ์ƒํƒœ"๋กœ ๊ฒฐ์ •

if (SessionState == MainMenu || SessionState == Result)
{
    UpdateInputMode(SessionState);   // UIOnly + ์ปค์„œ ์œ ์ง€
}
else
{
    SetInputMode(FInputModeGameOnly()); // ์ธ๊ฒŒ์ž„๋งŒ ๊ธฐ์กด๋Œ€๋กœ
    bShowMouseCursor = false;
}

๋ฐฐ์šด ์ 

"๋‹ซ์€ ๋’ค ์ƒํƒœ"๋Š” ์ƒ์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ผ ๋ฌธ๋งฅ์ด๋‹ค. ํŒจ๋„ ๋‹ซ๊ธฐ ๊ฐ™์€ ๊ณตํ†ต ๊ฒฝ๋กœ์— ํ•˜๋“œ์ฝ”๋”ฉ๋œ ๋ณต์› ๊ฐ’(GameOnly)์€ ๊ทธ ๊ฒฝ๋กœ๊ฐ€ ์ฒ˜์Œ ์“ฐ์ด๋˜ ๋ฌธ๋งฅ(์ธ๊ฒŒ์ž„)์˜ ์ „์ œ์ผ ๋ฟ์ด๋‹ค โ€” ๊ฐ™์€ ํŒจ๋„ ์‹œ์Šคํ…œ์„ ์ƒˆ ๋ฌธ๋งฅ(๋ฉ”์ธ๋ฉ”๋‰ด)์—์„œ ์žฌ์‚ฌ์šฉํ•˜๋Š” ์ˆœ๊ฐ„, ๋ณต์› ๋กœ์ง์€ "์›๋ž˜ ์ƒํƒœ๋กœ"๊ฐ€ ์•„๋‹ˆ๋ผ "์ง€๊ธˆ ์žˆ์–ด์•ผ ํ•  ์ƒํƒœ๋กœ"๋ฅผ ๋ฌผ์–ด์•ผ ํ•œ๋‹ค. ์„ธ์…˜ ์ƒํƒœ๋จธ์‹ ์ด ์ด๋ฏธ ์žˆ์œผ๋‹ˆ ๊ทธ๊ฑธ ๋‹จ์ผ ์ง„์‹ค๋กœ ์“ฐ๋ฉด ๋์ด์—ˆ๋‹ค.


6. ์กฐ์ž‘ํ‚ค ์•ˆ๋‚ด ์œ„์ ฏ โ€” ์ผ์‹œ์ •์ง€ ๋ฉ”๋‰ด์—์„œ "ํŒจ๋„ ๊ต์ฒด"๋กœ ์—ฌ๋‹ซ๊ธฐ

๋ชฉํ‘œ

์ผ์‹œ์ •์ง€ ๋ฉ”๋‰ด์˜ "์กฐ์ž‘ํ‚ค ์•ˆ๋‚ด" ๋ฒ„ํŠผ์œผ๋กœ ํ‚ค๋ณด๋“œ/๋งˆ์šฐ์Šค ์กฐ์ž‘๋ฒ• ํ™”๋ฉด(WBP_ControlsGuide)์„ ์—ด๊ณ , ESC๋กœ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋‹ซํžˆ๊ฒŒ ํ•œ๋‹ค.

๊ตฌํ˜„ โ€” ์ „์šฉ C++ ํด๋ž˜์Šค ์—†์ด ๊ธฐ์กด ํŒจ๋„ ์ธํ”„๋ผ์— ๊ทธ๋Œ€๋กœ ์–น๊ธฐ

  • ์œ„์ ฏ ์ž์ฒด๋Š” ์ˆœ์ˆ˜ WBP(๋ถ€๋ชจ: RetrieveGamePanelWidget)๋กœ, ํŒŒ์น˜๋จผํŠธ ๋ฐ•์Šค ํ…์Šค์ฒ˜ + ๋งˆ์šฐ์Šค ์ขŒ/์ค‘/์šฐ ์ž…๋ ฅ ์•„์ด์ฝ˜ + ํ‚ค ํ–‰(KeyRows) ๊ตฌ์„ฑ. ํ‘œ์‹œ ์ „์šฉ์ด๋ผ C++ ๋ฒ ์ด์Šค๋ฅผ ์ƒˆ๋กœ ๋งŒ๋“ค ์ด์œ ๊ฐ€ ์—†์—ˆ๋‹ค.
  • ์—ฌ๋Š” ์ชฝ์€ OpenControlsGuide() ํ•˜๋‚˜ โ€” OpenExclusivePanel(PanelClass, EKeys::Escape)์„ ๊ทธ๋Œ€๋กœ ์žฌ์‚ฌ์šฉํ•œ๋‹ค. ๋ฐฐํƒ€ ํŒจ๋„ ๋ชจ๋ธ์ด๋ผ ์ผ์‹œ์ •์ง€ ํŒจ๋„์ด ์ž๋™์œผ๋กœ ์กฐ์ž‘ํ‚ค ์•ˆ๋‚ด๋กœ "๊ต์ฒด"๋˜๊ณ , ESC(ToggleKey)๋กœ ๋‹ซํžŒ๋‹ค. ์Šคํƒ/๋„ค๋น„๊ฒŒ์ด์…˜ ์ฝ”๋“œ๋ฅผ ์ƒˆ๋กœ ๋งŒ๋“ค์ง€ ์•Š์•˜๋‹ค.
  • ์ผ์‹œ์ •์ง€ ๋ฉ”๋‰ด ์ชฝ์€ C++ ๋ฒ ์ด์Šค(URetrievePauseMenuWidget)๊ฐ€ CommandsButton์„ BindWidgetOptional๋กœ ์ง‘์–ด PC->OpenControlsGuide()์— ๋ฐฐ์„ ํ•œ๋‹ค โ€” WBP๋Š” ์ •ํ•ด์ง„ ์ด๋ฆ„์˜ ๋ฒ„ํŠผ๋งŒ ๋ฐฐ์น˜ํ•˜๋ฉด ๋˜๊ณ  ๊ทธ๋ž˜ํ”„ ๋ฐฐ์„ ์ด ํ•„์š” ์—†๋‹ค.
  • ํด๋ž˜์Šค ์ฐธ์กฐ๋Š” TSoftClassPtr + ์ƒ์„ฑ์ž ๊ธฐ๋ณธ ๊ฒฝ๋กœ๋กœ ๋‘๋˜, BP ์˜ค๋ฒ„๋ผ์ด๋“œ๊ฐ€ None์œผ๋กœ ๋‚จ๋Š” ์‚ฌ๊ณ ์— ๋Œ€๋น„ํ•ด LoadClass ํด๋ฐฑ์„ ๋„ฃ์—ˆ๋‹ค(7๋ฒˆ ํ•ญ๋ชฉ๊ณผ ์„ธํŠธ).

๋ฐฐ์šด ์ 

"ํ™”๋ฉด ํ•˜๋‚˜ ์ถ”๊ฐ€"๊ฐ€ ์š”๊ตฌํ•˜๋Š” ์ฝ”๋“œ๋Š” ํ™”๋ฉด ์ˆ˜์— ๋น„๋ก€ํ•˜์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค. ๋ฐฐํƒ€ ํŒจ๋„(OpenExclusivePanel) + ESC ToggleKey + BindWidgetOptional ๋ฒ„ํŠผ ๋ฐฐ์„ ์ด๋ผ๋Š” ๊ธฐ์กด ๊ทœ์•ฝ์— ์–น์œผ๋‹ˆ, ์ƒˆ ํ™”๋ฉด์˜ ๋น„์šฉ์ด "WBP 1๊ฐœ + Open ํ•จ์ˆ˜ 1๊ฐœ"๋กœ ๋๋‚ฌ๋‹ค. ๋ฐ˜๋Œ€๋กœ ๋งํ•˜๋ฉด, ํŒจ๋„ ์ธํ”„๋ผ๋ฅผ ์ฒ˜์Œ ๋งŒ๋“ค ๋•Œ ๊ต์ฒด/๋‹ซ๊ธฐ/์ž…๋ ฅ ๊ทœ์•ฝ์„ ์ผ๋ฐ˜ํ™”ํ•ด๋‘” ๊ฒƒ์ด ์—ฌ๊ธฐ์„œ ํšŒ์ˆ˜๋๋‹ค.
ํ‘œ์‹œ ์ „์šฉ ์œ„์ ฏ์— C++ ๋ฒ ์ด์Šค๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๋Š” ๊ฒƒ๋„ ์„ค๊ณ„ ๊ฒฐ์ •์ด๋‹ค. ๋ฐ”์ธ๋”ฉํ•  ์ƒํƒœ๊ฐ€ ์—†๋Š” ์ •์  ์•ˆ๋‚ด ํ™”๋ฉด์€ WBP๋งŒ์œผ๋กœ ์ถฉ๋ถ„ํ•˜๊ณ , C++ ํด๋ž˜์Šค๋ฅผ ๋Š˜๋ฆฌ์ง€ ์•Š์œผ๋ฉด ๋ฆฌ์ปดํŒŒ์ผ ์—†์ด ๋””์ž์ด๋„ˆ๊ฐ€ ๋‚ด์šฉ์„ ๊ณ„์† ๋‹ค๋“ฌ์„ ์ˆ˜ ์žˆ๋‹ค.


7. ์†Œํ”„ํŠธ ๋ ˆํผ๋Ÿฐ์Šค๋งŒ ์žˆ๋Š” UI๋Š” ํŒจํ‚ค์ง•์—์„œ ํ†ต์งธ๋กœ ๋น ์ง„๋‹ค

๋ชฉํ‘œ

์—๋””ํ„ฐ์—์„  ์ž˜ ์—ด๋ฆฌ๋˜ ์„ค์ •์ฐฝยท์ผ์‹œ์ •์ง€ ๋ฉ”๋‰ดยท์กฐ์ž‘ํ‚ค ์•ˆ๋‚ด๊ฐ€ Shipping ํŒจํ‚ค์ง€์—์„œ "๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ๋„ ์กฐ์šฉํžˆ ์•„๋ฌด ์ผ๋„ ์•ˆ ์ผ์–ด๋‚˜๋Š”" ๋ฌธ์ œ๋ฅผ ๋ง‰๋Š”๋‹ค.

์›์ธ โ€” TSoftClassPtr/LoadClass ๊ฒฝ๋กœ ๋ฌธ์ž์—ด์€ ์ฟ ์ปค๊ฐ€ ๋”ฐ๋ผ๊ฐ€์ง€ ๋ชปํ•จ

์ด UI๋“ค์€ ์ „๋ถ€ TSoftClassPtr(๋˜๋Š” ๊ฒฝ๋กœ ๋ฌธ์ž์—ด LoadClass)๋กœ๋งŒ ์ฐธ์กฐ๋œ๋‹ค โ€” ํ•˜๋“œ ๋ ˆํผ๋Ÿฐ์Šค ์ฒด์ธ์ด ์—†์œผ๋‹ˆ ์ฟ ์ปค๊ฐ€ ํ•ด๋‹น ํด๋”์— ๋„๋‹ฌํ•˜์ง€ ๋ชปํ•˜๊ณ , ํŒจํ‚ค์ง€์—์„œ LoadSynchronous()๊ฐ€ null์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์—๋Ÿฌ ํŒ์—…๋„ ์—†์ด ํ•จ์ˆ˜๊ฐ€ ์กฐ๊ธฐ returnํ•˜๋ฏ€๋กœ ์ฆ์ƒ์ด "๋ฌด๋ฐ˜์‘"๋ฟ์ด๋ผ๋Š” ๊ฒŒ ์•…์งˆ์ด๋‹ค. ์ด๋ฏธ ์กฐ์ค€์  ํ…์Šค์ฒ˜, ์•„์ดํ…œ ํš๋“ ํ† ์ŠคํŠธ์—์„œ ๋‘ ๋ฒˆ ๊ฒช์€ ๊ฒƒ๊ณผ ๋™์ผ ํŒจํ„ด์˜ ์„ธ ๋ฒˆ์งธ ์žฌ๋ฐœ์ด์—ˆ๋‹ค.

ํ•ด๊ฒฐ โ€” always-cook ๋“ฑ๋ก + ์•Œ๋ ค์ง„ ๊ฒฝ๋กœ ํด๋ฐฑ์˜ ์ด์ค‘ ๋ฐฉ์–ด

; DefaultGame.ini โ€” ์†Œํ”„ํŠธ ์ฐธ์กฐ๋งŒ ์žˆ๋Š” UI ํด๋”๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ฟ ํ‚น์— ํฌํ•จ
+DirectoriesToAlwaysCook=(Path="/Game/Retrieve/UI/Interaction")
+DirectoriesToAlwaysCook=(Path="/Game/Retrieve/UI/Settings")
+DirectoriesToAlwaysCook=(Path="/Game/Retrieve/UI/Menu")

์ฝ”๋“œ ์ชฝ์—๋„ PauseMenuClass/ControlsGuideClass๊ฐ€ ๋น„์–ด ์žˆ์œผ๋ฉด(BP์— None ์˜ค๋ฒ„๋ผ์ด๋“œ๊ฐ€ ๋‚จ์€ ๊ฒฝ์šฐ ๋“ฑ) ์•Œ๋ ค์ง„ ๊ฒฝ๋กœ๋กœ LoadClass ํด๋ฐฑ์„ ํƒœ์›Œ, "ํ”„๋กœํผํ‹ฐ ์„ค์ • ์‹ค์ˆ˜"์™€ "์ฟ ํ‚น ๋ˆ„๋ฝ"์„ ๊ฐ๊ฐ ๋‹ค๋ฅธ ์ธต์—์„œ ๋ฐฉ์–ดํ•œ๋‹ค.

๋ฐฐ์šด ์ 

TSoftClassPtr๋กœ UI๋ฅผ ์ฐธ์กฐํ•˜๋Š” ์ˆœ๊ฐ„, always-cook ๋“ฑ๋ก๊นŒ์ง€๊ฐ€ ํ•œ ์„ธํŠธ๋‹ค. ๊ฐ™์€ ์›์ธ์œผ๋กœ ์„ธ ๋ฒˆ์งธ ๋‹นํ–ˆ๋‹ค โ€” ์ด์ œ ๊ทœ์น™ํ™”ํ•œ๋‹ค: ์†Œํ”„ํŠธ ์ฐธ์กฐ/๊ฒฝ๋กœ ๋ฌธ์ž์—ด ๋กœ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ์ปค๋ฐ‹์—๋Š” ๋ฐ˜๋“œ์‹œ DirectoriesToAlwaysCook ํ™•์ธ์ด ๋”ฐ๋ผ๋ถ™์–ด์•ผ ํ•˜๊ณ , ์ฆ์ƒ์€ ํ•ญ์ƒ "ํŒจํ‚ค์ง€์—์„œ๋งŒ ์กฐ์šฉํ•œ ๋ฌด๋ฐ˜์‘"์œผ๋กœ ๋‚˜ํƒ€๋‚œ๋‹ค๋Š” ๊ฑธ ๊ธฐ์–ตํ•  ๊ฒƒ.


8. ์ผ์‹œ์ •์ง€ "๋ฉ”์ธ ๋ฉ”๋‰ด๋กœ"๊ฐ€ ์กฐ์šฉํžˆ ๋ฌด์‹œ๋œ ์ด์œ  โ€” ์„ธ์…˜ ์ƒํƒœ๋จธ์‹ ์˜ ์ „ํ™˜ ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ

๋ชฉํ‘œ

์ผ์‹œ์ •์ง€ ๋ฉ”๋‰ด์˜ "๋ฉ”์ธ ๋ฉ”๋‰ด๋กœ" ๋ฒ„ํŠผ์ด ์•„๋ฌด ๋ฐ˜์‘์ด ์—†๋Š” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•œ๋‹ค. WBP ๋ฒ„ํŠผ ๋ฐฐ์„  ๋ฌธ์ œ๋กœ ๋ณด์˜€์ง€๋งŒ ์‹ค์ œ ์›์ธ์€ ์ „ํ˜€ ๋‹ค๋ฅธ ๊ณณ์— ์žˆ์—ˆ๋‹ค.

์›์ธ โ€” ํด๋ฆญ โ†’ RPC โ†’ GameMode๊นŒ์ง€ ์ „๋ถ€ ์ •์ƒ์ธ๋ฐ, ์ƒํƒœ๋จธ์‹ ์ด ๋งˆ์ง€๋ง‰์— ๊ฑฐ๋ถ€

๋ฐฐ์„ ์„ ์˜์‹ฌํ•˜๊ณ  ์ถ”์ ํ–ˆ์ง€๋งŒ ํ๋ฆ„์€ ์™„๋ฒฝํ–ˆ๋‹ค: MainMenuButton ํด๋ฆญ โ†’ HandleMainMenuClicked โ†’ Server_RequestQuitToMenu โ†’ GameMode::HandleQuitToMenu โ†’ GameState::TransitionTo(MainMenu). ๋ฌธ์ œ๋Š” ๋งˆ์ง€๋ง‰ ํ•œ ์ค„ โ€” IsLegalTransition()์ด ์ „ํ™˜ ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ ๋ฐฉ์‹์ธ๋ฐ, InGame์—์„œ ํ—ˆ์šฉ๋œ ๋ชฉ์ ์ง€๊ฐ€ Result๋ฟ์ด์—ˆ๋‹ค. "๋ฉ”์ธ ๋ฉ”๋‰ด๋กœ ๋‚˜๊ฐ€๊ธฐ"๋Š” ์‚ฌ๋ง ํ™”๋ฉด(Result โ†’ MainMenu)๋งŒ ์ƒ์ •ํ•˜๊ณ  ์„ค๊ณ„๋๊ณ , ์ผ์‹œ์ •์ง€ ๋ฉ”๋‰ด๋Š” ๊ฒŒ์ž„์„ ์‹ค์ œ๋กœ ํฌ์ฆˆํ•˜์ง€ ์•Š์•„ ์„ธ์…˜ ์ƒํƒœ๊ฐ€ InGame์ธ ์ฑ„๋กœ ์š”์ฒญ๋˜๋ฏ€๋กœ ๋กœ๊ทธ ํ•œ ์ค„ ์—†์ด return false๋กœ ๋ฌด์‹œ๋๋‹ค.

ํ•ด๊ฒฐ โ€” ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ์— ํ•œ ์ค„ ์ถ”๊ฐ€

case ERetrieveSessionState::InGame:
    // MainMenu ํ—ˆ์šฉ: ์ผ์‹œ์ •์ง€ ๋ฉ”๋‰ด์˜ "๋ฉ”์ธ ๋ฉ”๋‰ด๋กœ"(Server_RequestQuitToMenu)๊ฐ€ InGame ์ƒํƒœ์—์„œ ํ˜ธ์ถœ๋œ๋‹ค.
    return To == ERetrieveSessionState::Result || To == ERetrieveSessionState::MainMenu;

์ „ํ™˜๋งŒ ํ—ˆ์šฉ๋˜๋ฉด ๊ธฐ์กด HandleSessionStateChanged ๊ฒฝ๋กœ๊ฐ€ ํŒจ๋„ ์ •๋ฆฌ โ†’ ์œ„์ ฏ ์Šค์™‘ โ†’ ์ž…๋ ฅ๋ชจ๋“œ โ†’ ๋ฉ”๋‰ด ์นด๋ฉ”๋ผ๊นŒ์ง€ ์•Œ์•„์„œ ์ฒ˜๋ฆฌํ•˜๋ฏ€๋กœ ์ˆ˜์ •์€ ์ด ํ•œ ์ค„์ด ์ „๋ถ€์˜€๋‹ค.

๋ฐฐ์šด ์ 

ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธํ˜• ์ƒํƒœ๋จธ์‹ ์€ "๊ฑฐ๋ถ€"๋ฅผ ๋กœ๊ทธ๋กœ ๋‚จ๊ฒจ์•ผ ํ•œ๋‹ค. TransitionTo๊ฐ€ ๋ถˆ๋ฒ• ์ „ํ™˜์„ ์กฐ์šฉํžˆ false๋กœ ์‚ผํ‚ค๋Š” ๋ฐ”๋žŒ์—, ์ฆ์ƒ(๋ฒ„ํŠผ ๋ฌด๋ฐ˜์‘)์—์„œ ์›์ธ(์ƒํƒœ๋จธ์‹  ๊ฑฐ๋ถ€)๊นŒ์ง€ ๊ฑฐ๋ฆฌ๊ฐ€ ๋„ˆ๋ฌด ๋ฉ€์—ˆ๋‹ค โ€” UI ๋ฐฐ์„ ๋ถ€ํ„ฐ RPC๊นŒ์ง€ ์ „ ๊ตฌ๊ฐ„์„ ๊ฑฐ์Šฌ๋Ÿฌ ์˜ฌ๋ผ๊ฐ€์•ผ ํ–ˆ๋‹ค. ๊ฑฐ๋ถ€ ์‹œ UE_LOG(Warning, "Illegal transition %s -> %s") ํ•œ ์ค„์ด ์žˆ์—ˆ๋‹ค๋ฉด ๋กœ๊ทธ ํ•œ ๋ฒˆ์— ๋๋‚ฌ์„ ๋ฌธ์ œ๋‹ค.
์ƒˆ UI ํ๋ฆ„์„ ์ถ”๊ฐ€ํ•  ๋•Œ๋Š” "๊ทธ ํ๋ฆ„์ด ๋ฐŸ๋Š” ์ƒํƒœ ์ „ํ™˜"์ด ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ์— ์žˆ๋Š”์ง€ ๋จผ์ € ํ™•์ธํ•œ๋‹ค. ์ผ์‹œ์ •์ง€ ๋ฉ”๋‰ด๋ผ๋Š” ์ƒˆ ์ง„์ž…์ ์ด ๊ธฐ์กด์— ์—†๋˜ ์ „ํ™˜(InGame โ†’ MainMenu)์„ ์š”๊ตฌํ•˜๊ฒŒ ๋๋Š”๋ฐ, ๋ฒ„ํŠผยทRPCยทํ•ธ๋“ค๋Ÿฌ๋Š” ๋‹ค ๋งŒ๋“ค๋ฉด์„œ ์ƒํƒœ๋จธ์‹  ๊ทœ์น™ ๊ฐฑ์‹ ๋งŒ ๋น ์กŒ๋‹ค โ€” ์ƒํƒœ๋จธ์‹ ์ด ์žˆ๋Š” ํ”„๋กœ์ ํŠธ์—์„œ ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ์ฒดํฌ๋ฆฌ์ŠคํŠธ์— "์ „ํ™˜ ๊ทœ์น™ ๊ฐฑ์‹ "์„ ํฌํ•จํ•ด์•ผ ํ•œ๋‹ค.


์˜ค๋Š˜์˜ ํ•ต์‹ฌ ์š”์•ฝ

์ž‘์—…ํ•ต์‹ฌ ๋ฌธ์ œํ•ด๊ฒฐ ์›์น™
์ด์–ดํ•˜๊ธฐ"๊ฐ€์žฅ ์ตœ๊ทผ ์Šฌ๋กฏ" ํŒ์ •์ •๋ ฌ ๊ฐ€๋Šฅํ•œ ํƒ€์ž„์Šคํƒฌํ”„ ํฌ๋งท โ†’ ๋ฌธ์ž์—ด ๋น„๊ต, ๋กœ๋“œ๋Š” ์„œ๋ฒ„ ๊ถŒ์œ„ ๊ฒฝ๋กœ๋กœ ํ†ต์ผ
๋ถˆ๋Ÿฌ์˜ค๊ธฐ ํŒจ๋„๊ณต์œ  ์Šฌ๋กฏ ์œ„์ ฏ์˜ ์•”๋ฌต์  ๊ณ„์•ฝ 3๊ฐœ(OwnerMenu ํƒ€์ž…ยทSlotIndex ํŽธ์ง‘๋ถˆ๊ฐ€ยท์—†๋Š” ์œ„์ ฏ)์ „๋ถ€ ํ˜ธ์ŠคํŠธ C++์—์„œ ํก์ˆ˜(์ง์ ‘ ๋ฐ”์ธ๋”ฉยทChildIndexยท๋™์  ์ฃผ์ž…), ๊ณต์œ  WBP ๋ฌด์ˆ˜์ •
ํ€˜์ŠคํŠธ ์ €์žฅ/๋ณต์›๋กœ๋“œ ์‹œ ์™„๋ฃŒ ์•Œ๋ฆผ ์—ฐ์‡„ ์žฌ์ƒ๋ณต์› ์ „์šฉ API + ์•Œ๋ฆผ ๋ฒ ์ด์Šค๋ผ์ธ ์žฌ์‹œ๋”ฉ + ๊ฐฑ์‹  ์‹ ํ˜ธ 1ํšŒ
์ถ”์  ํ€˜์ŠคํŠธ ํ‘œ์‹œHUD ๋กœ์ปฌ ์ƒํƒœ๋Š” ์„ธ์ด๋ธŒ์— ์—†์Œ์ €์žฅ ์‹œ์ ์— ๋™์ผ ์„ ์ • ๋กœ์ง์œผ๋กœ ์žฌ๊ณ„์‚ฐํ•ด ํ‘œ์‹œ ๋ฌธ์ž์—ด๋กœ ๋ฐ•์ œ
๋ฉ”๋‰ด์—์„œ ํŒจ๋„ ๋‹ซ๊ธฐ๋ฌด์กฐ๊ฑด GameOnly ๋ณต์› โ†’ ๋ฉ”๋‰ด ์ปค์„œ ์‚ฌ๋ง๋‹ซ์€ ๋’ค ์ž…๋ ฅ ๋ชจ๋“œ๋ฅผ ์„ธ์…˜ ์ƒํƒœ๋กœ ๊ฒฐ์ •
์กฐ์ž‘ํ‚ค ์•ˆ๋‚ด์ƒˆ ํ™”๋ฉด ์ถ”๊ฐ€ ๋น„์šฉ๋ฐฐํƒ€ ํŒจ๋„ ์ธํ”„๋ผ ์žฌ์‚ฌ์šฉ โ€” WBP 1๊ฐœ + Open ํ•จ์ˆ˜ 1๊ฐœ๋กœ ์ข…๊ฒฐ
ํŒจํ‚ค์ง• ๋ฌด๋ฐ˜์‘์†Œํ”„ํŠธ ์ฐธ์กฐ๋งŒ ์žˆ๋Š” UI๊ฐ€ ์ฟ ํ‚น ๋ˆ„๋ฝalways-cook ๋“ฑ๋ก + LoadClass ํด๋ฐฑ ์ด์ค‘ ๋ฐฉ์–ด (๋™์ผ ์›์ธ 3๋ฒˆ์งธ ์žฌ๋ฐœ โ†’ ๊ทœ์น™ํ™”)
๋ฉ”์ธ ๋ฉ”๋‰ด๋กœ ๋ฒ„ํŠผ์ƒํƒœ๋จธ์‹ ์ด InGameโ†’MainMenu๋ฅผ ์กฐ์šฉํžˆ ๊ฑฐ๋ถ€ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ ํ•œ ์ค„ ์ถ”๊ฐ€, "๊ฑฐ๋ถ€๋Š” ๋กœ๊ทธ๋ฅผ ๋‚จ๊ฒจ์•ผ ํ•œ๋‹ค"๋Š” ๊ตํ›ˆ

๊ณตํ†ต ๊ตํ›ˆ: ์˜ค๋Š˜ ํ•˜๋ฃจ๋Š” "์ด๋ฏธ ์žˆ๋Š” ์‹œ์Šคํ…œ์— ์ƒˆ ์ง„์ž…์ ์„ ์–น์„ ๋•Œ ๊นจ์ง€๋Š” ์ „์ œ๋“ค"์˜ ํ•˜๋ฃจ์˜€๋‹ค.

  1. ๊ณตํ†ต ๊ฒฝ๋กœ์˜ ํ•˜๋“œ์ฝ”๋”ฉ์€ ์ฒซ ์‚ฌ์šฉ์ฒ˜์˜ ์ „์ œ๋‹ค. ํŒจ๋„ ๋‹ซ๊ธฐ์˜ GameOnly ๋ณต์›(์ธ๊ฒŒ์ž„ ์ „์ œ), ์ƒํƒœ๋จธ์‹ ์˜ ์ „ํ™˜ ๋ชฉ๋ก(์‚ฌ๋ง ํ™”๋ฉด ์ „์ œ), ์Šฌ๋กฏ ์—”ํŠธ๋ฆฌ์˜ OwnerMenu ํƒ€์ž…(๋ชจ๋‹ฅ๋ถˆ ์ „์ œ) โ€” ์ „๋ถ€ ์ฒ˜์Œ ๋งŒ๋“ค ๋• ์˜ณ์•˜์ง€๋งŒ, ๋ฉ”์ธ๋ฉ”๋‰ด/์ผ์‹œ์ •์ง€๋ผ๋Š” ์ƒˆ ๋ฌธ๋งฅ์ด ์ƒ๊ธฐ์ž ์กฐ์šฉํžˆ ๊นจ์กŒ๋‹ค. ์žฌ์‚ฌ์šฉ ์ „์— "์ด ๊ฐ’/๊ทœ์น™์€ ๋ฌด์—‡์„ ์ „์ œ๋กœ ํ•˜๋Š”๊ฐ€"๋ฅผ ๋ฌผ์–ด์•ผ ํ•œ๋‹ค.
  2. ์กฐ์šฉํ•œ ์‹คํŒจ๊ฐ€ ๋””๋ฒ„๊น… ๋น„์šฉ์˜ ๋Œ€๋ถ€๋ถ„์ด๋‹ค. ์ƒํƒœ๋จธ์‹ ์˜ ๋ฌด์–ธ ๊ฑฐ๋ถ€, ํŒจํ‚ค์ง€์—์„œ์˜ LoadSynchronous null, ๊ธธ์ด 0 ๋ชฝํƒ€์ฃผ โ€” ์˜ค๋Š˜๋„ "์—๋Ÿฌ ์—†์ด ์•„๋ฌด ์ผ๋„ ์•ˆ ์ผ์–ด๋‚˜๋Š”" ์ฆ์ƒ์ด ๊ฐ€์žฅ ์˜ค๋ž˜ ๊ฑธ๋ ธ๋‹ค. ๊ฑฐ๋ถ€/์‹คํŒจ ์ง€์ ์— ๋กœ๊ทธ ํ•œ ์ค„ ๋„ฃ๋Š” ๊ฒƒ์ด ์ตœ๊ณ ์˜ ํˆฌ์ž๋‹ค.
  3. ์ƒํƒœ๋ฅผ ๋ณต์›ํ•˜๋Š” ์ฝ”๋“œ์™€ ์ƒํƒœ๋ฅผ ์ „์ด์‹œํ‚ค๋Š” ์ฝ”๋“œ๋Š” ๋‹ค๋ฅธ API์—ฌ์•ผ ํ•œ๋‹ค. ํ€˜์ŠคํŠธ ๋ณต์›์„ CompleteStep์— ํƒœ์šฐ์ง€ ์•Š๊ณ  ์ „์šฉ API๋กœ ๋ถ„๋ฆฌํ•œ ๊ฒƒ์ฒ˜๋Ÿผ, "์ „์ด"์— ๋ฌถ์ธ ๋ถ€์ˆ˜ํšจ๊ณผ(์•Œ๋ฆผยท๊ฒ€์ฆ)๋ฅผ "๋ณต์›"์ด ๋ฌผ๋ ค๋ฐ›์œผ๋ฉด ์•ˆ ๋œ๋‹ค โ€” ๋Œ€์‹  ๋ณต์›์ด ์ƒ๋žตํ•œ ์ฑ…์ž„(๋ฒ ์ด์Šค๋ผ์ธ, UI ๊ฐฑ์‹ )์€ ํ˜ธ์ถœ๋ถ€ ๊ณ„์•ฝ์œผ๋กœ ๋ช…์‹œํ•œ๋‹ค.
profile
๊ฐœ๋ฐœํ•ฉ์‹œ๋‹ค!

0๊ฐœ์˜ ๋Œ“๊ธ€