Привет! Эй, я сегодня решил выпустить, пока время есть, статейку небольшую, в которой расскажу о создании небольшого текстового квеста соответственно. Придётся разделить стетейку на две, так как в этой я хочу поведать о самом фундаменте, а во второй же написать систему диалогов для игры,
для того чтобы игрок мог сам выбрать свой вариант ответа.
для того чтобы игрок мог сам выбрать свой вариант ответа.
P.S. Это перезалив статьи, которая должна была выйти в проекте "LOVE" или "Game dev", но так, увы, не вышла
В общем к делу господа.
Подготовка
Для начала я предлагаю тебе не садиться писать код, а воссоздать небольшую иерархию проекта. Для начала создай в любом месте папку проекта,
внутри которой подготовь такие папки как:
внутри которой подготовь такие папки как:
- lua
- Text
- ui
А внутри ui ещё одну папку fonts В которую скинь шрифт (любой), но если что, то вот, держи Pixel Font
Также в папку ui Кинем текстуру кнопки, которую лучше взять здесь
Идём далее. Создадим в корневой папке проекта наши любимые файлы main.lua и conf.lua, а в папке lua создадим файл ui.lua
Кстати, может кому это будет интересно ( Огромное спасибо человеку Андреич за этот фрагмент кода )
Для тех, кто хочет запускать через батник (.bat) свою игру
Создадим в корневой папке текстовый файл (с любым названием) !!Предварительно в настройках папок нужно снять галочку с "скрывать расширения для зарезервированных типов" или как-то так
И пишем туда вот этот кусочек отборного кода для 64-х разрядной системы:
@ECHO OFF
start "" "%PROGRAMFILES%\LOVE\love" .
Для 32-х (86-х) разрядной системы:
@ECHO OFF
start "" "%PROGRAMFILES(x86)%\LOVE\love" .
Далее меняем расширения с .txt на .bat
Профит
Также предлагаю создать в папке Text текстовый файл с любым названием. Из этого файла, в этой части статьи, мы будем подгружать имена персонажей с их репликами.
Теперь к коду перейдём.
Код
Итак, давай начнём пока что с конфига (conf.lua) и далее уже приступим писать разные крутые штуки. Можешь написать всё сам, а можешь взять конфигурацию с сайта или от сюда:
function love.conf(t)
t.identity = nil -- The name of the save directory (string)
t.version = "0.10.1" -- The LÖVE version this game was made for (string)
t.console = false -- Attach a console (boolean, Windows only)
t.accelerometerjoystick = true -- Enable the accelerometer on iOS and Android by exposing it as a Joystick (boolean)
t.externalstorage = false -- True to save files (and read from the save directory) in external storage on Android (boolean)
t.gammacorrect = false -- Enable gamma-correct rendering, when supported by the system (boolean)
t.window.title = "Quest" -- The window title (string)
t.window.icon = nil -- Filepath to an image to use as the window's icon (string)
t.window.width = 1280 -- The window width (number)
t.window.height = 920 -- The window height (number)
t.window.borderless = false -- Remove all border visuals from the window (boolean)
t.window.resizable = true -- Let the window be user-resizable (boolean)
t.window.minwidth = 1 -- Minimum window width if the window is resizable (number)
t.window.minheight = 1 -- Minimum window height if the window is resizable (number)
t.window.fullscreen = false -- Enable fullscreen (boolean)
t.window.fullscreentype = "desktop" -- Choose between "desktop" fullscreen or "exclusive" fullscreen mode (string)
t.window.vsync = false -- Enable vertical sync (boolean)
t.window.msaa = 0 -- The number of samples to use with multi-sampled antialiasing (number)
t.window.display = 1 -- Index of the monitor to show the window in (number)
t.window.highdpi = false -- Enable high-dpi mode for the window on a Retina display (boolean)
t.window.x = nil -- The x-coordinate of the window's position in the specified display (number)
t.window.y = nil -- The y-coordinate of the window's position in the specified display (number)
t.modules.audio = true -- Enable the audio module (boolean)
t.modules.event = true -- Enable the event module (boolean)
t.modules.graphics = true -- Enable the graphics module (boolean)
t.modules.image = true -- Enable the image module (boolean)
t.modules.joystick = true -- Enable the joystick module (boolean)
t.modules.keyboard = true -- Enable the keyboard module (boolean)
t.modules.math = true -- Enable the math module (boolean)
t.modules.mouse = true -- Enable the mouse module (boolean)
t.modules.physics = true -- Enable the physics module (boolean)
t.modules.sound = true -- Enable the sound module (boolean)
t.modules.system = true -- Enable the system module (boolean)
t.modules.timer = true -- Enable the timer module (boolean), Disabling it will result 0 delta time in love.update
t.modules.touch = false -- Enable the touch module (boolean)
t.modules.video = true -- Enable the video module (boolean)
t.modules.window = true -- Enable the window module (boolean)
t.modules.thread = true -- Enable the thread module (boolean)
end
Правки я внёс лишь в название файла, в отключение вертикальной синхронизации и всё на этом.
Далее нам могут понадобиться кнопки и тексты. Я недавно писал для своей игры такую штуку, но в интернете есть и более крутые примеры.
Я лишь писал в целях развития самого себя и для того, чтобы вспомнить как это вообще делается :)
Я лишь писал в целях развития самого себя и для того, чтобы вспомнить как это вообще делается :)
Воть:
font = {};
button = {};
function Button(name, img, nameFont, pathFont, sizeFont, x, y, scaleX, scaleY, r, g, b)
font[nameFont] = love.graphics.newFont(pathFont, sizeFont);
font[nameFont]:setFilter("nearest","nearest");
buttonImg:setFilter("nearest","nearest");
local text = love.graphics.newText(font[nameFont], name);
love.graphics.setColor(r, g, b);
button[name] = love.graphics.draw(img, x, y,0,scaleX,scaleY,0,0);
love.graphics.setColor(0,0,0);
love.graphics.draw(text, x + img:getWidth() - text:getWidth() / 4 + scaleX * 12 - string.len(name) * 2, y + img:getHeight(), 0, 1, 1, 0, 0);
end
function Text(title, nameFont, pathFont, sizeFont, x, y, r, g, b, a)
font[nameFont] = love.graphics.newFont(pathFont, sizeFont);
font[nameFont]:setFilter("nearest","nearest");
local text = love.graphics.newText(font[nameFont], title);
love.graphics.setColor(r, g, b, a);
love.graphics.draw(text, x, y, 0, 1, 1, -1, 0,0,0);
end
Этот код из файла ui.lua. Немного подробнее о нём. Здесь мы можем наблюдать пока две функции (на момент написания статьи). Это Button и Text.
Думаю итак понятно что, за что отвечает. В Button мы подгружаем стандартную текстурку кнопки, а также создаём текст по центру кнопки. Всё это указывается в параметрах кнопки, то есть при вызове функции, поэтому сейчас мы переходим в main.lua и пишем основу там.
Думаю итак понятно что, за что отвечает. В Button мы подгружаем стандартную текстурку кнопки, а также создаём текст по центру кнопки. Всё это указывается в параметрах кнопки, то есть при вызове функции, поэтому сейчас мы переходим в main.lua и пишем основу там.
Для начала объявим начальные переменные:
dofile("lua/ui.lua");
dofile("lua/logic.lua");
resolutionX = 0;
resolutionY = 0;
widthWindow = 0;
heightWindow = 0;
fontPath = "ui/fonts/3572.ttf";
Здесь мы подключаем два наших файла, с которыми мы будем работать в дальнейшем. Также здесь есть значения разрешения экрана, путь к шрифту и размеры диалогового окна, с которым мы поработаем в следующей статье, но в этой мы его отрисуем.
Далее первые три функции:
function love.load()
resolutionX, resolutionY = love.window.getMode();
findFile("Text/words.txt");
readFile();
end
function love.resize(width, height)
resolutionX = width;
resolutionY = height;
end
function love.update(frame)
widthWindow = resolutionX / 2;
heightWindow = resolutionY / 3;
end
В love.load() мы передаём начальные размеры игрового окна в переменные. В love.resize() мы передаём новые размеры окна, если они были изменены, тем самым все элементы, которые будут на экране, смогут подстраиваться. В love.update() мы задаём размеры диалогового окна. Именно там, потому что размеры окна пользователь может изменить.
Далее остальное:
unction love.draw()
--BACKGROUND
love.graphics.setBackgroundColor(0,0,0);
-- WINDOW DIALOG
------------------------------------------
love.graphics.setColor(55,100,115); -- COLOR WINDOW BACKGROUND
love.graphics.rectangle("fill", (resolutionX / 2) - widthWindow / 2 , resolutionY - heightWindow / 2, widthWindow, heightWindow / 2);
love.graphics.setColor(255,255,255);
love.graphics.rectangle("line", (resolutionX / 2) - widthWindow / 2 , resolutionY - heightWindow / 2, widthWindow, heightWindow / 2);
-------------------------------------------
--MAIN TEXT
Text(name[id] .. ": " .. string.sub(phrases[id], count),"Main",fontPath,20,resolutionX / 2, resolutionY / 2, 255,255,255,255);
end
function love.keyreleased(button)
if button == "escape" then love.event.quit(); end
if button == "space" and id < table.getn(phrases) then id = id + 1; readFile(); end
end
В love.draw() Задаю фон, рисую диалоговое окно, далее вывожу текст, подгружаемый из файла,
который в свою очередь подгружается из файла logic.lua
В love.keyreleased() я реализовал выход по нажатию на Escape и смену текста по нажатию на Space.
И последний файл logic.lua
который в свою очередь подгружается из файла logic.lua
В love.keyreleased() я реализовал выход по нажатию на Escape и смену текста по нажатию на Space.
И последний файл logic.lua
phrases = {};
id = 1;
file = nil;
path = "";
iterator = 0;
name = {};
function findFile(pathFile)
path = pathFile
file = love.filesystem.newFile(path);
end
function readFile()
iterator = love.filesystem.lines(path);
count = 0;
for text in iterator do
table.insert(phrases, text);
count = string.find(phrases[id]," ");
name[id] = string.sub(phrases[id],1, count - 1);
end
end
В нём я храню реплики героев, файл, путь к файлу и имена героев. Далее в функции findFile я нахожу файл. Саму функцию вызываю в love.load (смотрим выше)
В функции readFile я пихаю по таблицам текст и имена героев. НО.
Как же сделал вот так:
С помощью таких функций как string.find,string.sub. В самом же текстовом файле, у меня содержится следующая информация:
Хочу заметить что имена "Pit" и "Mary" хранятся в отдельной таблице. В функции readFile я нахожу индекс самого первого пробела и до него - 1 беру имя, где - 1 - это вычитания единицы от индекса, чтобы не было пробела между именем и двоеточия ":". Основной же текст грузится после этого индекса. Тем самым мы и получаем
- "Pit: Hello!"
- "Mary: Hi!"
Нет, серьёзно, вот же:
Хочу напомнить что переход на другой диалог осуществляется с помощью нажатия на пробел. Да и, на момент написания статьи, у меня получилась небольшая рекурсия.
В общем после нажатия на пробел при переходе с последней реплики, игра не вылетает, хотя должна, так как элементы таблицы пусты, но вместо этого она крутит диалог по новой
В общем после нажатия на пробел при переходе с последней реплики, игра не вылетает, хотя должна, так как элементы таблицы пусты, но вместо этого она крутит диалог по новой
Первый акт закончен, господа, расходимся на перерыв в месяц, снова.