나는 Conquest라고 불리는 Ruby에서 Text Adventure를 해왔으며, 어디에서 가는지 매우 행복합니다. 그러나 곧 게임을 저장할 수있는 기능을 추가하고 싶습니다. 나는 YAML로 이런 것들을 해왔지만 더 나은 방법이 있다고 상상할 수 있습니다.내 게임에 저장 시스템 추가하기
이 게임은 내가 만든 다른 게임보다 약간 복잡하므로 저장 시스템을 관리하는 매우 쉬운 방법이 필요합니다.
나는 내 게임 내 사용자 정의 클래스의 모든 인스턴스에 대해 YAML이 같은 일을 생각 :--- !ruby/object:Player
items: ...
그러나 나는 아마 대략가는 나쁜 방법입니다 생각. 내 파일 몇 개를 보여 드리며이 문제를 해결하는 가장 좋은 방법이라고 생각하는 것을 알고 싶습니다. 그리고 진행 중임을 기억하십시오. 그래서 퀘스트와 같은 것들이 제대로 작동하지는 않지만 요점을 얻으실 수 있습니다.
lib 디렉토리/player.rb :
class Player
attr_accessor :items
def initialize
@items = {}
@quests = QuestList.quests
end
def pickup(key, item)
@items[key.to_sym] = item
if key == "scroll"
@quests[:mordor].start
end
end
def inventory
@items.values.each { |item|
a_or_an = %w[a e i o u].include?(item.name[0]) \
? "an " : "a "
a_or_an = "" if item.name[-1] == "s"
puts "#{a_or_an}#{item.name.downcase}"
}
end
end
lib 디렉토리/item.rb가 :
class Item
attr_reader :name, :description, :hidden, :can_pickup
def initialize(name, description, options = {})
@name = name
@description = description
@options = options
@hidden = options[:hidden] || false
@can_pickup = options[:hidden] || true
add_info
end
def add_info
end
end
class Prop < Item
def add_info
@hidden = true
@can_pickup = false
end
end
# sends you to a new room, usally something that
# has more functionality than just a room
class Transporter < Prop
attr_accessor :goto
def add_info
@hidden = true
@can_pickup = false
@goto = options[:goto]
end
end
# SUBCLASSES BELOW: (only subclass when you have a good reason)
# can be eaten,
# use item.is_a? Food
class Food < Item
end
class Tree < Prop
def climb
if @options[:can_climb]
puts @description
else
puts "You start climbing the tree, but you don't get far before you fall down."
end
end
end
lib 디렉토리/quest_list.rb (이것은 아마도 절약 시스템에 의해 대체 될 것이다) :
module QuestList
QUESTS = {
# this need a better name ⬇️
main: Quest.new("The main mission", []),
mordor: Quest.new("Onward to Mordor", [])
}
def self.quests
QUESTS
end
end
lib/room_list.rb (이것은 아마도 절약 시스템으로 대체 될 것입니다) :
module RoomList
ROOMS = {
castle_main:
Room.new("Main room", "This is the main room of the castle. It needs a better description\nand name. Theres a hallway south.",
paths: { s: :hallway}
),
hallway:
Room.new("Hallway", "This castle has a long hallway. There is a door to the west and\na large room north.",
paths: { n: :castle_main, s: :castle, w: :dinning_hall }
),
dinning_hall:
Room.new("Dinning hall", "The dinning hall. There is a door to the east.",
paths: { e: :hallway }
),
castle:
Room.new("Castle", "You are in the castle. There's a long hallway to the north, and\nthe courtyard is to the south.",
paths: { n: :hallway, s: :courtyard }
),
courtyard:
Room.new("Castle courtyard", "You are at the castle courtyard. There's a nice fountain in the center.\nThe castle entrance is north. There is a forest south.",
paths: { n: :castle, s: :forest },
items: {
# this peach is useless, it'll confuse people
# a peach:
peach: Food.new("Peach", "A delicious peach")
}),
forest:
Room.new("Large forest", "This forest is very dense. There is a nice courtyard north.\nThe forest continues west and south.",
paths: { n: :courtyard, s: :forest_1, w: :forest__1 }
),
forest__1:
Room.new("Large forest", "This forest is very nice. You can go north, east and west into\nsome more forest.",
paths: { n: :forest__2, e: :forest, w: :sticks }
),
sticks:
Room.new("Large forest", "This forest is getting boring, but hey, who knows what you'll find here!\nYou can go east.",
paths: { e: :forest__1 },
items: {
sticks: Item.new("Sticks", "Just a couple of sticks. They like they are cedar wood.")
}),
forest__2:
Room.new("Large forest", "You are in a large forest. There looks like theres a grand building over\neast, but you can't quite get to it from here. You can go south.",
paths: { s: :forest__1 }
),
forest_1:
Room.new("Large forest", "There is a large, magnificent tree east. The forest continues\nnorth and south.",
paths: { n: :forest, e: :banyan_tree, s: :forest_2 }
),
banyan_tree:
# http://en.wikipedia.org/wiki/Banyan
Room.new("Large banyan tree", "There is a large banyan tree, with many twists and roots going up the tree.\nYou can go west.",
paths: { w: :forest_1 },
items: {
tree: Tree.new("Banyan", "You climb up the top of the tree, and see lots of trees and a\ncastle somewhere around north. It looks like there is a small\nvillage some where south east. You climb back down.", { #
can_climb: true
})}),
forest_2:
Room.new("Large forest", "Just some more forest. The forest continues north and south.",
paths: { n: :forest_1, s: :forest_3 }
),
forest_3:
Room.new("Large forest", "Dang, how many trees are in this forest? You can go north, south, and west.",
paths: { n: :forest_2, s: :forest_4, w: :more_trees }
),
more_trees:
Room.new("Large forest", "You can go east and west.",
paths: { e: :forest_3, w: :more_trees_1 }
),
more_trees_1:
Room.new("Large forest", "You can go east and south.",
paths: { e: :more_trees, s: :more_trees_2 }
),
more_trees_2:
Room.new("Large forest", "You can go north and south.",
paths: { n: :more_trees_1, s: :more_trees_3 }
),
more_trees_3:
Room.new("Large forest", "You can go north and east",
paths: { n: :more_trees_2, e: :path_to_village }
),
path_to_village:
Room.new("Large forest", "Its hard to see because of all these trees, but you think you see a small\nhut to the east. You can also go back west",
paths: { e: :village, w: :more_trees_3 }
),
village:
# add an item or 2 here
Room.new("Abandon village", "There are a bunch of huts here, some people must have lived here before.\nThere is some more forest down south. You can go back west into the forest.",
paths: { w: :path_to_village, s: :forest_by_village },
items: {
pickaxe: Item.new("Pickaxe", "Be careful, it looks sharp.")
}),
forest_by_village:
Room.new("Large forest", "Geez more forest. The village is north, and there is a valley east",
paths: { n: :village, e: :valley }
),
valley:
Room.new("Valley", "It's a beautiful valley, with some giganic mountains east, with some\nsnow of the tops. There is a forest to the west",
paths: { e: :mountains, w: :forest_by_village }
),
mountains:
Room.new("Mountains", "There are many tall mountains with snow on the tops. You can go back west.",
paths: { u: :mountain, w: :valley },
has_mountain: true
),
mountain:
Room.new("Tall mountain", "This mountain is very steep. You can continue climbing or go back down",
paths: { d: :mountains, u: :mountain_1 },
# the scroll and Randy should be moved to mountain_3 once it exists
items: {
scroll: Item.new("Scroll", "Its some kind of elvish... You can't read it.") },
people: {
# Randy will read elvish in the future
randy: Person.new("Randy", "He's just an elf",
race: "Elf",
talk: "I can read elvish. Go figure."
)}),
mountain_1:
Room.new("Tall mountain", "Climbing this mountain is very tiring. You can continue climbing\nor go back down",
paths: { d: :mountain }
),
forest_4:
Room.new("Large forest", "There is a lot of trees here. It's very shady in this area.\nThe forest continues north.",
paths: { n: :forest_3 }
)
}
def self.room_list
ROOMS
end
end
lib 디렉토리/delegate.rb :
class Delegate
attr_accessor :current_room
def initialize
@rooms = RoomList.room_list
@player = Player.new
@current_room = @rooms[:courtyard]
@help = 0
end
def parse(input)
directions = "up|down|north|east|south|west|u|d|n|e|s|w"
# input will always be converted to lower case before getting here
case input
when /^(?<direction>(#{directions}))$/
direction = $~[:direction]
walk(direction)
when /^(go|walk)((?<direction>#{directions}|to mordor))?$/
direction = $~[:direction]
if direction
walk(direction)
else
puts "#{input.capitalize} where?"
end
when /^(get|take|pickup|pick up)((?<item>[a-z ]+))?$/
item = $~[:item]
if item
pickup(item)
else
puts "Please supply an object to #{input}."
end
when /^look((?<item>[a-z]+))?$/
item = $~[:item]
item.nil? ? look : inspect(item)
when /^inspect((?<item>[a-z]+))?$/
item = $~[:item]
if item
inspect(item)
else
puts "Please supply an object to inspect."
end
when /^rub sticks(together)?$/
rub_sticks
when /^quests?$/
# this is probably going to be a for statement. You understand thos more than i do so have at it.
# this should loop through the list of quests in quests.yml and return the ones that are true
# correction: it should call .each, for statments are bad practice in ruby
when /^(i|inv|inventory)$/
inventory
when /^climb((?<tree_name>[a-z]+))?(tree)?$/
# this regex needs to be cleaned up, just the tree part really
# nvm, the whole regex sucks
= $~[:tree_name]
climb()
# doesn't have to be a tree...
when /^(help|h)$/
@smart_aleck ||= ["Why?","No.","Stop asking plz.","seriously, shut up.","...","...","...","Ok, seriously.","Do u not understand the meaning of \"be quiet\"?","ug"].to_enum
begin
puts @smart_aleck.next
rescue StopIteration
@smart_aleck.rewind
puts @smart_aleck.next
end
when /^(quit|exit)$/
quit
when /^\s?$/
else
= ["I don't speak jibberish.","Speak up. Ur not making any sense.","R u trying to confuse me? Cuz dats not gonna work","What the heck is that supposed to mean?"]
puts .sample
end
end
def walk(direction)
if direction != "to mordor"
if new_room = @rooms[@current_room[direction]]
@current_room = new_room.enter
else
puts "You can't go that way."
end
else
#TODO: add quest system. We should have a main quest and other side quests like going to mordor.
puts "One does not simply walk to Mordor... You need to find the eagles. They will take you to Mordor."
end
end
def pickup(item)
if _item = @current_room.items[item.to_sym]
if _item.can_pickup
_item = @current_room.remove_item(item)
@player.pickup(item, _item)
else
puts "You can't pick that up."
end
else
puts "That item isn't in here."
end
end
def inventory
@player.inventory
end
def look
@current_room.look
end
def inspect(item)
# this could be refactored
if the_item = @player.items[item.to_sym]
puts the_item.description
elsif the_item = @current_room.items[item.to_sym]
puts the_item.description
else
puts "This item is not here or your inventory."
end
end
def rub_sticks
if @player.items[:sticks]
# do something involving fire
puts "I need to implement this."
end
end
def climb(thing_name)
if = @current_room.items[:tree]
name = .name.downcase
if thing_name.nil? || thing_name == "tree" || thing_name == name
.climb
else
puts "You can't climb that."
end
# I don't like how this works :(
elsif @current_room.options[:has_mountain]
if ["up", "mountain", nil].include? thing_name
walk("u")
end
else
puts "You can't climb that."
end
end
def quit
exit
end
end
lib 디렉토리/quest.rb :
class Quest
attr_accessor :steps
def initialize(name, steps, options = {})
@name = name
# steps (the argument) should be a hash like this:
# [:found_ring, :melted_ring]
@steps = steps.inject({}) { |hash, step| hash[step] = false; hash }
# then @step will be this:
# { found_ring: false, melted_ring: false }
@started = false
@options = options
end
def start
@started = true
puts "#{'Quest started!'.cyan} - #{name}"
end
end
lib 디렉토리/room.rb는 :
class Room
attr_reader :items, :options, :people
def initialize(name, description, options = {})
@name = name
@description = description
@paths = options[:paths] || {}
@items = options[:items] || {}
@people = options[:people] || {}
@options = options
@visited = false
end
def [](direction)
@paths[direction.to_sym]
end
def enter
puts @name.cyan
unless @visited
puts @description
list_items
end
@visited = true # can't hurt to set it every time, right?
self
end
def remove_item(item)
@items.delete(item.to_sym)
end
def look
puts @name.cyan
puts @description
list_items
end
def list_items
visible_items = @items.values.select { |i| (!i.hidden) && i.can_pickup }
unless visible_items.empty?
puts "Items that are here:".magenta
visible_items.map do |item|
a_or_an = %w[a e i o u].include?(item.name[0]) \
? "an " : "a "
a_or_an = "" if item.name[-1] == "s"
puts "#{a_or_an}#{item.name.downcase}"
end
end
visible_people = @people.values.select { |i| (!i.hidden) && i.can_pickup }
unless visible_people.empty?
puts "People that are here:".magenta
visible_people.map do |people|
puts "#{people.name}"
end
end
end
end
그게 Delegate.current_room
해야 알 플레이어를 지키지 않는다면 플레이어의 재산이 아닙니다. 나는 그것을 고치려고하지 않았습니다.
그래서 YAML 직렬화로 이것을 저장하는 것에 대해 어떻게 생각합니까? (못생긴 !ruby/object:Class
물건)
나는 이것에 관해 더 좋은 길을 알고 정말로 싶다. 그러나 나는 아무것도 생각할 수 없다. 나는 ~/.conquest_save에 yaml이나 다른 저장 형식을 넣을 수 있다고 생각했다.
나는 당신의 모든 의견을 고맙게 생각하고있다. 전체 프로젝트는 Github here에 있습니다.
왜 db가 아니고 'player_gateway'입니까? – dax
그냥 파일이기 때문에 sqlite3 데이터베이스를 넣을 수 있습니다. 그것은 가장 우아하고 읽기 쉬운 방법이 될 것입니다. – Cereal
그 점에 대해 생각했지만, 내 선수 클래스처럼 루비 오브젝트를 저장할 수 있는지 또는 어떻게 저장할 수 있는지 알지 못했습니다. – Addison