레슨 7 / 8·4개 토픽
실전: 텍스트 어드벤처 게임
프로젝트 개요
지금까지 배운 테이블, 메타테이블, 코루틴, 문자열 패턴을 모두 활용하여 텍스트 기반 어드벤처 게임을 만들어 봅니다. 플레이어가 방을 이동하고, 아이템을 수집하며, 간단한 퍼즐을 풀 수 있는 게임입니다.
게임 상태와 아이템 시스템
테이블로 게임 월드를 구성하고, 메타테이블로 아이템의 공통 동작을 정의합니다. 각 방(room)에는 설명, 연결된 방, 놓인 아이템 정보가 있습니다.
lua
-- 아이템 클래스 (메타테이블 활용)
local Item = {}
Item.__index = Item
function Item.new(name, description, usable)
return setmetatable({
name = name,
description = description,
usable = usable or false,
}, Item)
end
function Item:describe()
return self.name .. " — " .. self.description
end
function Item:__tostring()
return "[" .. self.name .. "]"
end
-- 아이템 생성
local key = Item.new("열쇠", "녹슨 황동 열쇠", true)
local note = Item.new("쪽지", "희미한 글씨가 적힌 쪽지", true)
local sword = Item.new("검", "빛나는 단검", true)
-- 방(room) 정의
local rooms = {
entrance = {
name = "입구",
description = "어두운 동굴 입구입니다. 북쪽으로 통로가 보입니다.",
exits = { north = "hallway" },
items = { note },
},
hallway = {
name = "복도",
description = "길고 좁은 복도입니다. 횃불이 벽에 걸려 있습니다.",
exits = { south = "entrance", east = "treasure", west = "armory" },
items = { key },
},
armory = {
name = "무기고",
description = "먼지 쌓인 무기고입니다.",
exits = { east = "hallway" },
items = { sword },
},
treasure = {
name = "보물방",
description = "잠긴 문이 있습니다. 열쇠가 필요합니다.",
exits = { west = "hallway" },
items = {},
locked = true,
},
}플레이어와 게임 루프 (코루틴)
코루틴으로 게임 흐름을 관리합니다. 각 턴마다 yield로 사용자 입력을 기다리고, resume으로 게임을 진행합니다.
lua
-- 플레이어 상태
local player = {
location = "entrance",
inventory = {},
}
-- 인벤토리 관리
function player:has_item(name)
for i, item in ipairs(self.inventory) do
if item.name == name then return i end
end
return nil
end
function player:take_item(item_name)
local room = rooms[self.location]
for i, item in ipairs(room.items) do
if item.name == item_name then
table.insert(self.inventory, item)
table.remove(room.items, i)
return "'" .. item.name .. "'을(를) 획득했습니다."
end
end
return "그런 아이템이 없습니다."
end
-- 게임 코루틴
local game_loop = coroutine.create(function()
print("=== 텍스트 어드벤처 ===")
print("명령어: 이동 [방향], 줍기 [아이템], 보기, 가방, 사용 [아이템]\n")
while true do
local room = rooms[player.location]
print("[" .. room.name .. "] " .. room.description)
if #room.items > 0 then
io.write("아이템: ")
for _, item in ipairs(room.items) do
io.write(tostring(item) .. " ")
end
print()
end
-- 입력 대기 (yield)
local input = coroutine.yield("입력 대기")
-- input 처리는 다음 섹션에서
end
end)명령어 파서 (패턴 매칭)
string.match와 패턴을 사용하여 플레이어의 자연어 입력을 파싱합니다. "이동 북쪽", "줍기 열쇠" 같은 명령을 인식합니다.
lua
-- 방향 매핑
local dir_map = {
["북"] = "north", ["남"] = "south",
["동"] = "east", ["서"] = "west",
["북쪽"] = "north", ["남쪽"] = "south",
["동쪽"] = "east", ["서쪽"] = "west",
}
-- 명령어 파싱 함수
local function parse_command(input)
-- 공백 정리
input = string.gsub(input, "^%s+", "")
input = string.gsub(input, "%s+$", "")
-- "이동 [방향]"
local dir = string.match(input, "^이동%s+(.+)$")
if dir then
local eng_dir = dir_map[dir]
if eng_dir then
local room = rooms[player.location]
if room.exits[eng_dir] then
local target = room.exits[eng_dir]
local target_room = rooms[target]
-- 잠긴 방 체크
if target_room.locked then
if player:has_item("열쇠") then
target_room.locked = false
print("열쇠로 문을 열었습니다!")
else
print("문이 잠겨 있습니다.")
return
end
end
player.location = target
else
print("그 방향으로 갈 수 없습니다.")
end
else
print("알 수 없는 방향입니다.")
end
return
end
-- "줍기 [아이템]"
local item_name = string.match(input, "^줍기%s+(.+)$")
if item_name then
print(player:take_item(item_name))
return
end
-- "가방" — 인벤토리 확인
if string.match(input, "^가방$") then
if #player.inventory == 0 then
print("가방이 비어 있습니다.")
else
print("=== 가방 ===")
for _, item in ipairs(player.inventory) do
print(" " .. item:describe())
end
end
return
end
print("알 수 없는 명령입니다.")
end
-- 게임 실행 예시 (실제로는 io.read()와 함께)
-- coroutine.resume(game_loop)
-- parse_command("줍기 쪽지")
-- coroutine.resume(game_loop, "이동 북쪽")- •메타테이블(
Item)로 아이템 공통 동작 정의 - •테이블로 게임 월드(방, 연결, 아이템) 구성
- •코루틴으로 턴 기반 게임 루프 구현 (yield/resume)
- •패턴 매칭으로 자연어 명령어 파싱
- •
pcall로 예상치 못한 에러 안전하게 처리
💡
이 프로젝트를 확장하려면 NPC 대화(코루틴), 전투 시스템(메타테이블 상속), 세이브/로드(파일 I/O) 기능을 추가해 보세요.