Creating-your-first-script

From FiveM Wikipedia

Создание первого скрипта на Lua

Начало работы со сценариями для FiveM может быть немного ошеломляющим, учитывая широкий спектр возможностей и слабо распространенную документацию. В этом кратком и простом руководстве мы постараемся показать вам, как начать работу с быстрым ресурсом в Lua.

Ресурсы

Ресурс — это, проще говоря, набор файлов, которые можно запускать, останавливать и перезапускать по отдельности. Ваша папка server-data (при условии, что вы уже установили сервер) должна уже иметь папку с несколькими ресурсами в них.resources

Если вы работаете на своих собственных ресурсах, вы, вероятно, захотите создать каталог - этот будет проигнорирован Git при обновлении корня сервера-данных. Там мы сделаем папку, так как мы создаем, ну, тип игры с помощью системы.resources/[local]resources/[local]/mymodemapmanager

Это означает, что к настоящему времени вам понадобится папка, подобная этой, предполагая систему разработки Windows: . С этого момента мы будем вызывать эту папку.C:\your\path\to\cfx-server-data\resources\[local]\mymodemymode

Файлы манифеста

Папка ресурсов (вы знаете, это вы сделали выше) будет нуждаться в манифесте, который будет обнаружен FiveM. Поскольку это тип игры, ему также понадобится некоторая дополнительная информация, чтобы рассказать о том, что это тип игры.mymodemapmanager

Создайте файл с именем (это всегда Lua, даже если вы будете писать сценарии на C#/JS позже) в своей папке. В него поместите следующий текст с помощью любимого текстового редактора:fxmanifest.luamymode

fx_version 'cerulean'
game 'gta5'

author 'An awesome dude'
description 'An awesome, but short, description'
version '1.0.0'

resource_type 'gametype' { name = 'My awesome game type!' }

client_script 'mymode_client.lua'

Любой новый ресурс, который вы создадите, вероятно, захочет новейших игровых функций. Для этого и предназначено. Вы можете прочитать об этом в другом месте на этом сайте документации, если вы когда-нибудь почувствуете необходимость узнать больше. Чтобы указать, предназначен ли этот ресурс для gta5, rdr3 или , следует использовать переменную.fx_versioncommongame

С другой стороны, он говорит, что это, по сути, тип игры, и что он называется «Мой потрясающий тип игры!». Если вы просто создаете «автономный» дополнительный ресурс, вы, вероятно, не хотите включать строку.resource_typemapmanagerresource_type

Наконец, он указывает среде выполнения сценариев, что клиент должен загрузить сценарий с именем . Если бы это был JS-скрипт, он бы сказал, или если бы это был C#, это, вероятно, было бы , но на данный момент мы учим Lua, поэтому просто забудьте об этом.client_scriptmymode_client.luamymode_client.jsMyModeClient.net.dll

Наконец, мы должны сделать файл, вызываемый в папке ресурсов.mymode_client.luamymode

Чтобы узнать больше о файлах манифеста ресурсов, ознакомьтесь со ссылкой на манифест ресурса.

Написание кода

В этот файл поместим следующее содержимое:

local spawnPos = vector3(686.245, 577.950, 130.461)

AddEventHandler('onClientGameTypeStart', function()
    exports.spawnmanager:setAutoSpawnCallback(function()
        exports.spawnmanager:spawnPlayer({
            x = spawnPos.x,
            y = spawnPos.y,
            z = spawnPos.z,
            model = 'a_m_m_skater_01'
        }, function()
            TriggerEvent('chat:addMessage', {
                args = { 'Welcome to the party!~' }
            })
        end)
    end)

    exports.spawnmanager:setAutoSpawn(true)
    exports.spawnmanager:forceRespawn()
end)

Это сложно, особенно если вы не привыкли к концепции первоклассных функций. Вы также можете написать его по-другому, используя глобальные / локальные функции - но это просто немного странно.

Давайте пройдемся по этому шагу, с аннотированной версией.

-- define a local variable called `spawnPos` with a coordinate somewhere on the map
-- Lua in FiveM (through CfxLua) supports first-class vectors, which in this case can be accessed using .x, .y and .z.
local spawnPos = vector3(686.245, 577.950, 130.461)

-- add an event handler for the (local) event called 'onClientGameTypeStart'.
-- it takes no arguments in this case (in Lua you can omit arguments), since our resource is a game type and you can only run one at once,
-- that means this will basically run when we start ourselves on the client. nice!
AddEventHandler('onClientGameTypeStart', function()
    -- set an automatic spawn callback for the spawn manager.
    -- normally, this works using hardcoded spawn points, but since this is a scripting tutorial, we'll do it this way.
    --
    -- the spawn manager will call this when the player is dead, or when forceRespawn is called.
    exports.spawnmanager:setAutoSpawnCallback(function()
        -- spawnmanager has said we should spawn, let's spawn!
        exports.spawnmanager:spawnPlayer({
            -- this argument is basically a table containing the spawn location...
            x = spawnPos.x,
            y = spawnPos.y,
            z = spawnPos.z,
            -- ... and the model to spawn as.
            model = 'a_m_m_skater_01'
        }, function()
            -- a callback to be called once the player is spawned in and the game is visible
            -- in this case, we just send a message to the local chat box.
            TriggerEvent('chat:addMessage', {
                args = { 'Welcome to the party!~' }
            })
        end)
    end)

    -- enable auto-spawn
    exports.spawnmanager:setAutoSpawn(true)

    -- and force respawn when the game type starts
    exports.spawnmanager:forceRespawn()
end)

Краткое упоминание о разнице между клиентскими и серверными скриптами: большая часть того, что вы будете делать в FiveM, будет делаться с помощью клиентских скриптов, так как в текущих версиях в серверных скриптах нет взаимодействия с игровым функционалом. Серверные сценарии должны использоваться для выполнения сценариев действий между клиентами (с использованием событий клиент-сервер) и для обеспечения «источника доверия» для различных действий, таких как хранение/загрузка данных в постоянной базе данных.

Поскольку порождение игрока в значительной степени является полностью игровым взаимодействием, это происходит на стороне клиента. Каждый игрок, к которому он присоединится, будет иметь локальный экземпляр каждого клиентского сценария, запущенного на его ПК, без общих переменных или контекста между ними.

Запуск этого

Вы, вероятно, надеетесь, что сможете запустить этот небольшой пример - ну, надеюсь, у вас уже есть работающий экземпляр FXServer - если нет, следуйте руководству для этого.

После запуска FXServer выполните команду в консоли. Это перечитает каждый файл для каждого установленного вами ресурса, так как вы, вероятно, только что запустили сервер, это на самом деле не нужно, но если у вас уже запущен сервер, это просто хорошая идея™.refreshfxmanifest.lua

Наконец, выполните в консоли и подключитесь к серверу с помощью удобной кнопки клиента FiveM в режиме разработчика (или просто войдите на вкладку прямого подключения, или, если вы использовали порт по умолчанию, нажмите эту полезную ссылку на ПК, на котором у вас установлен FiveM).start mymodelocalhostlocalhost

Как только игра загрузится, вы должны увидеть себя где-то нерестящимся - надеюсь, на большой сцене!

Держите игру запущенной (и, возможно, установите ее в безрамочный или оконный режим в параметрах игры) и Alt-Tab обратно в ваш редактор кода - у нас есть еще много работы!

Перезапуск ресурсов

Глупо закрывать игру и сервер и перезапускать их для итерации на вашем ресурсе. Конечно, вы также можете перезапустить свой ресурс.

Давайте попробуем несколько разных точек появления.

Замените строку (первую) следующей:spawnPosmymode/mymode_client.lua

local spawnPos = vector3(-275.522, 6635.835, 7.425)

Затем в консоли сервера выполните волшебную команду . Вы должны (снова) увидеть «Добро пожаловать на вечеринку!», упомянутый в вашем чате, и оказаться на пирсе, а не на сцене.restart mymode

Расширяя это

Вы, вероятно, захотите сделать больше. Для этого вам придется научиться называть аборигенов, что не имеет ничего общего с коренными народами и на самом деле является меткой R* для «игровых функций скрипта». Есть много тонкостей, связанных с правильным вызовом аборигенов - для полной справки см. специальный раздел для этого - но мы начнем с простого пока.

Глупым способом «этот троп снова» мы сделаем команду, которая породит автомобиль. Локально. Потому что никто не заботится о сервере, когда они начинают.

В нижней части экрана добавьте следующий код:mymode_client.lua

RegisterCommand('car', function(source, args)
    -- TODO: make a vehicle! fun!
    TriggerEvent('chat:addMessage', {
        args = { 'I wish I could spawn this ' .. (args[1] or 'adder') .. ' but my owner was too lazy. :(' }
    })
end, false)

Начиная уже, мы видим вызов функции. Мы не определили эту функцию. Что ж, мы (как и команда FiveM) это сделали, но не тогда, когда вели вас, читатель, через это удивительно написанное чудо гида. Это означает, что он должен прийти откуда-то еще!

И, угадайте, это на самом деле REGISTER_COMMAND! Щелкните эту ссылку, и вы перейдете к документации для этого нативного носителя. Выглядит это примерно так:

// 0x5fa79b0f
// RegisterCommand
void REGISTER_COMMAND(char* commandName, func handler, BOOL restricted);

Мы будем в основном заботиться об имени во второй строке (как используется в коде Lua выше) и аргументах.RegisterCommand

Как видите, первым аргументом является имя команды. Второй аргумент — это функция, являющаяся обработчиком команды, а третий аргумент — логическая, указывающая, должна ли она быть ограниченной командой.

Сама функция получает аргумент, который является , который действительно имеет значение только в том случае, если вы работаете на сервере (это будет идентификатор клиента игрока, который ввел команду, действительно полезная вещь, которую нужно иметь), и массив, который в основном является тем, что вы вводите после команды, например, чтобы в конечном итоге быть или быть .sourceargs/car zentornoargs{ 'zentorno' }/car zentorno unused{ 'zentorno', 'unused' }

Поскольку мы уже знаем, как распечатать сообщение в окне чата, мы просто притворимся, что порождаем транспортное средство, печатая имя транспортного средства на консоли.

Давайте перезапустим ресурс и посмотрим, что получится. Выполнить , затем в поле чата клиента (по умолчанию) введите . Вы увидите, что окно чата жалуется, что вы были слишком ленивы, чтобы реализовать это. Мы покажем им, что вы абсолютно не ленивы, и на самом деле реализуем это сейчас.restart mymodeT/car zentorno

Реализация автомобильного спауна

Это много шаблонного кода, и мы захотим сделать это правильно, так как многие люди будут копировать этот пример, поэтому он может показаться немного ошеломляющим.

По сути, мы сделаем следующее:

  1. Проверьте, действительна ли переданная модель. Невесело пытаться породить «картофель», когда нет транспортного средства с таким названием.
  2. Загрузите модель. Вам нужно будет явно управлять каждой моделью, которую вы используете, это правила, первоначально определенные R*.
  3. Дождитесь загрузки модели. Да, игра будет продолжать работать асинхронно.
  4. Выясните, где находится игрок после загрузки.
  5. Создайте транспортное средство! Потрясающе, наконец-то вы становитесь креативными.
  6. Посадите игрока в транспортное средство.
  7. Убирайтесь, так как мы аккуратные люди и 🚮 все.

Поехали!

Замените бит, который вы только что вставили, на это, и не волнуйтесь, мы объясним это, прежде чем вы сможете сказать «ленивый» дважды:

RegisterCommand('car', function(source, args)
    -- account for the argument not being passed
    local vehicleName = args[1] or 'adder'

    -- check if the vehicle actually exists
    if not IsModelInCdimage(vehicleName) or not IsModelAVehicle(vehicleName) then
        TriggerEvent('chat:addMessage', {
            args = { 'It might have been a good thing that you tried to spawn a ' .. vehicleName .. '. Who even wants their spawning to actually ^*succeed?' }
        })

        return
    end

    -- load the model
    RequestModel(vehicleName)

    -- wait for the model to load
    while not HasModelLoaded(vehicleName) do
        Wait(500) -- often you'll also see Citizen.Wait
    end

    -- get the player's position
    local playerPed = PlayerPedId() -- get the local player ped
    local pos = GetEntityCoords(playerPed) -- get the position of the local player ped

    -- create the vehicle
    local vehicle = CreateVehicle(vehicleName, pos.x, pos.y, pos.z, GetEntityHeading(playerPed), true, false)

    -- set the player ped into the vehicle's driver seat
    SetPedIntoVehicle(playerPed, vehicle, -1)

    -- give the vehicle back to the game (this'll make the game decide when to despawn the vehicle)
    SetEntityAsNoLongerNeeded(vehicle)

    -- release the model
    SetModelAsNoLongerNeeded(vehicleName)

    -- tell the player
    TriggerEvent('chat:addMessage', {
		args = { 'Woohoo! Enjoy your new ^*' .. vehicleName .. '!' }
	})
end, false)

Для этого используется МНОГО аборигенов. Мы свяжем некоторые из них и объясним сложные части.

Шаг 1: Проверка

Мы начали с проверки названия автомобиля. Если он (то есть не существует), мы по умолчанию будем использовать . В любом случае, он хранится в переменной.niladder

Затем мы проверяем, находится ли автомобиль в образе компакт-диска, используя IS_MODEL_IN_CDIMAGE. Это в основном означает «зарегистрировано ли это в игре». Мы также проверяем, является ли это транспортным средством, использующим IS_MODEL_A_VEHICLE. Если какая-либо проверка не удалась, мы сообщаем об этом игроку и возвращаемся от команды.

Шаг 2: Загрузка модели

Теперь мы называем REQUEST_MODEL для загрузки фактической модели транспортного средства. Этот натив принимает аргумент, но в Lua вы также можете просто передать строку, и она будет преобразована в хэш. Вы часто увидите, что люди используют (GET_HASH_KEY), но если натив указан как принимающий, вам на самом деле это не нужно.HashGetHashKeyHash

Шаг 3: Ожидание загрузки модели

Мы зацикливаем вызовы HAS_MODEL_LOADED, чтобы проверить, удалась ли загрузка. Поскольку это цикл, и мы совместно многозадачны, вам придется дать игре время для запуска - в противном случае она даже не закончит загрузку, и игра, к сожалению, зависнет. Вот для чего предназначен вызов - он ждет заданное количество миллисекунд, а затем возвращается обратно в скрипт.Wait

Как только модель будет загружена, мы продолжим.

Шаг 4: Получение позиции игрока

Физические воплощения игроков идентифицируются по их , что является сокращением от «пешеход». Это термин GTA, и он обычно означает «все, что живет и имеет ноги». Мы используем PLAYER_PED_ID, чтобы получить локальный (в основном, тот, кто выполняет эту команду) пед игрока.ped

После того, как у нас есть пед и мы сохраняем его в переменной, мы получаем позицию педа игрока, используя GET_ENTITY_COORDS. Поскольку пед является сущностью (то же самое касается транспортных средств и некоторых других вещей), этот натив используется для получения их позиции. Этот натив, опять же, возвращает , аналогично тому, как он был определен ранее.Vector3spawnPos

Шаг 5: Создание транспортного средства

Мы используем CREATE_VEHICLE, чтобы, ну, создать транспортное средство. Тем временем мы пробрались в вызов, чтобы получить направление игрока, используя GET_ENTITY_HEADING, что заставляет автомобиль появляться лицом в том же направлении, что и игрок.

Это соглашение в нативных носителях создания сущностей о создании транспортного средства с сетевым объектом (), но не о том, чтобы сделать его объектом миссии (). Обычно вы хотите первое, или никто другой не увидит транспортное средство - и вы не захотите второе, так как вы не пишете полный сценарий миссии R*.true, falsetruefalse

Шаг 6: Установка игрока в транспортное средство

Поскольку теперь у нас есть пед и транспортное средство, мы можем использовать SET_PED_INTO_VEHICLE для размещения педали в транспортном средстве. Как говорится в документации, это водительское сиденье транспортного средства.-1

Шаг 7: Очистка

Игре нравится, когда вы убираете за собой, и поскольку мы больше ничего не делаем с транспортным средством или моделью в этом скрипте, мы позволим игре управлять этим. Это то, для чего мы используем SET_ENTITY_AS_NO_LONGER_NEEDED и SET_MODEL_AS_NO_LONGER_NEEDED.

Наконец, мы говорим игроку, чтобы он наслаждался своим новым транспортным средством.

Запуск этого

В вашей серверной консоли (да, вы можете разделить вещи точкой с запятой) и попробуйте в игровом клиенте (который к настоящему времени должен быть действительно надоест возрождаться). Теперь у вас будет свой собственный Rocket Voltic!refresh; restart mymode/car voltic2

Серверные скрипты

Вероятно, вы также захотите написать сценарии, которые взаимодействуют с сервером. Этот раздел еще предстоит написать. :-(


Введение в написание сценариев