Перейти к содержимому

ECS

Гуру
  • Публикации

    533
  • Зарегистрирован

  • Посещение

  • Победитель дней

    203

Сообщения, опубликованные пользователем ECS


  1. 42 минуты назад, WheatComp сказал:

    Выдает ошибку:

    Символ "s" поставил лишний, сорян. Я тебе сам принцип хотел показать, что нужно сначала пройтись по всем имеющимся танкам, а затем уже обращаться к их полям.

     

    Если нужен самодельный сериализатор для любого типа таблиц, то можно написать что-то вроде

    tank1 = {amount = 222, capacity = 333, nestedTable = {1, 2, 3}}
    tank2 = {amount = 452, capacity = 349, nestedTable = {4, 5, 6}}
    tankTable = {tank1, tank2}
    
    local function printTableContents(source, name, indentator)
      indentator = indentator or ""
      name = name or ""
    
      print(indentator .. name .. "{")
    
      for key, value in pairs(source) do
      	if type(value) == "table" then
        	printTableContents(value, tostring(key) .. " = ", (indentator or "") .. "  ")
        else
        	print(indentator .. "  " .. tostring(key) .. " = " .. tostring(value))
        end
      end
    
      print(indentator .. "}")
    end
    
    printTableContents(tankTable)

    В результате выведется

     

    image.png.0ced26fb63264a0cad788e7fd31c338b.png

    • Нравится 1
    • Спасибо 1

  2. 3 часа назад, WheatComp сказал:
    
    tank1 = {amount = 222, capacity = 333}
    tank2 = {amount = 452, capacity = 349}
    tankTable = {tank1, tank2}
    
    for key, value in pairs(tankTable) do
    print(value)
    print(tankTable[1][key])
    end

    Выводит nil nil.

    tank1 = {amount = 222, capacity = 333}
    tank2 = {amount = 452, capacity = 349}
    tankTable = {tank1, tank2}
    
    -- Сначала перебираем танки
    for tankIndex, tank in ipairs(tanksTable) do
      -- Затем все поля танка
      for key, value in pairs(tank) do
        print(key, value)
      end
    end

     


  3. 13 часа назад, WheatComp сказал:

    Можно поподробнее, где хранится этот буфер энергии? И где смотреть "алмазность", или "железность" робота?

    Хранится в роботе, получить объем буфера можно через computer.maxEnergy(), а смотреть "алмазность" можно либо через experience.level(), либо визуально, т.к. с накоплением опыта робот меняет свой цвет сначала на золотой, а затем на алмазный:

     

    image.png.aab2d72090afbe146291a625e4292a99.png

     

    13 часа назад, WheatComp сказал:

    А если просто выкидывать мусор? Прописать названия, если == то дропнуть.

    А выкидывание никто не отменял, его нужно грамотно комбинировать с упаковкой. Например, если робот накопал 9 стаков реда, их можно упаковать в стак блоков, освободив целых 8 слотов под другие ресурсы. Это в любом случае быстрее, чем перемещаться на базу, сбрасывать инвентарь, возвращаться на раскопки и продолжать работу

     

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

    • Нравится 1

  4. 22 часа назад, WheatComp сказал:

    Нужна ли эта строчка в send.lua? В этом файле ничего не принимается, вроде.

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

     

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

     

    • Метод modem.setWakeMessage() - имба. Когда робот выполнил все задачи, можно смело его выключать, оставляя где-то на глубине в границах привата. Когда необходимо продолжить работу, шлём сетевой пакет на включение - профит. И энергия сохранена, и нажимать кнопку Power не надо
    • Широковещательные пакеты через modem.broadcast() опасны на публичных серверах, и в 100% случаев станут причиной угона робота. В идеале, когда все машинки скрафчены, необходимо внести адреса их модемов в конфиг на хосте, а впоследствии общаться приватными пакетами через modem.send(). Либо захардкодить в роботах адрес хоста и доверять лишь ему одному, как в примере выше
    • Дополнительные батареи - мусор. Вместо них лучше установить апгрейд опыта, построить ферму кобблы и позволить роботу фармить экспу. Спустя 2-4 реальных часа у тебя раскачается "алмазный" робот с колоссальным буфером энергии
    • Апгрейд навигации для вычисления позиции/поворота - мусор. Вместо него куда дешевле и интереснее накодить софтверную триангуляцию, основанную на перемещениях робота и расстоянии, которое "пролетел" сетевой пакет. А поворот можно определить с помощью геоанализатора, ломания/установки блоков и вращения робота
    • Софтверное позиционирование - имба. Вычисляешь единожды координаты/поворот робота - и корректируешь их при вызове move/turn. Взамен появляется возможность автоматизировать перемещение в любую указанную точку. При отключении питания имеет смысл сохранять эти данные на диске, либо определять их автоматически при включении
    • Геоанализатор - легальный чит. Однако использовать его в штатном режиме, сканируя вертикальными "столбцами" - выстрел в ногу, т.к. точность результатов на расстоянии в 10+ блоков будет отвратная. Вместо этого стоит заюзать опциональные аргументы scan(), анализируя небольшие горизонтальные плоскости 8х8 блоков вокруг робота, постепенно прокапывая вниз. Точность при этом станет как советском станке
    • Экран/клавиатура/дисковод и полноценная ОС - штуки ситуативные, т.к. стоимость сборки улетает в космос, а выхлоп сомнительный. Мнение не навязываю, но я предпочитаю ставить больше апгрейдов инвентаря/крафтинг/редстоун-плату/etc., делая машинку универсальнее и контролируя её по сети
    • Апгрейд крафтинга очень недооценён. Каким бы большим ни был инвентарь, он гарантированно заполнится всякими андезитами/мусоритами. Эту проблему в соло решает крафтинг, упаковывающий ресурсы в стаки, особенно на серверах с модами типа экстраутилит, позволяющими "сжимать" кобблу
    • Жёсткий диск 1 тира - топ за свои деньги. Всегда приятно иметь возможность сохранять/загружать координаты робота или довольно объёмные "снимки" местности с геоанализатора
    • Нравится 5

  5. 16 часов назад, WheatComp сказал:

    Пытался проверить, будет ли 3-х секундная задержка (computer.pullSignal(3)), нет задержки

    Эта штука ждёт каких-либо сигналов в течение 3 секунд. Если сигнал получен - pullSignal вернёт информацию о нём досрочно. Если сигналов не было - pullSignal будет честно ждать 3 секунды, а затем вернет nil

     

    Вообще, как я понял, тебе нужен некий аналог os.sleep(3) из OpenOS, который реализован как раз на чистом pullSignal. Можно скопировать его из исходников

     

    16 часов назад, WheatComp сказал:

    upd: оказалось, pullSignal() относится к api. Есть ли "компонентная" альтернатива?

    На самом деле не относится, это на вики мешанина. Суперглобалки component/computer/robot/unicode/bit32, а также штатные библиотеки Lua доступны отовсюду. Все остальное - софтверные фичи OpenOS


  6. 2 часа назад, WheatComp сказал:

    Это  странно, почему filesystem.exists() смотрит в /, а не в /home, где обычно стартует терминал при включении игрового компа

    Библиотека filesystem абстрагируется от действий текущего юзера и рабочей директории, предоставляя более "сырой" доступ к файловой системе без высокоуровневых проверок. Для поддержки поиска по раб. директории следует обратиться к методу shell.resolve(path), который используется большинством штатных скриптов в /bin/. Посмотри на отправляющий код с пред. сообщения - там я как раз добавил эту фичу для удобства

     

    2 часа назад, WheatComp сказал:

    Почему-то arg[1] выводит send. Вчера, когда пытался разобраться с аргументами, читал, что в Lua-интерпретаторах обычно args[0] выводит имя файла. Попытался в игре - выводит nil

    Всё верно. В десктопном Lua ты получил бы что-то наподобие:

     

    image.png.ff333b6366cc5a5b1d28578fcb251fe2.png

     

    А в OpenComputers уже нет, т.к. глобальной переменной arg вовсе не существует:

     

    image.png.f907bec2eb8d147574a68ede7291a794.png

     

    Все входные аргументы мы получаем вручную через args = {...}, создавая при этом самую обычную переменную. Она не какая-то магическая, не системная. Её и назвать можно как угодно, хоть someWeirdDots = {...}, тут уж воля фантазии

     

    2 часа назад, WheatComp сказал:

    Робота удалось заставить шевелиться, но возникает некоторая путаница с командами. В ОpenOS же вперед - это forward(), а тут move(side). Придется изменить много кода и ошибки, чувствую, неизбежны

    Согласен, хотя это скорее дело привычки. Всегда можно скопировать таблицу направлений из исходников Sides API, и воспользоваться ей для движения робота в нужную сторону, если вводить непонятные цифры а-ля robot.move(1/2/3/4) дискомфортно

     

    2 часа назад, WheatComp сказал:

    К тому же на сайте игры не написано, каковы же остальные "низкоуровневые" команды. Описаны move, turn, swing, а back, up, down не описаны

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

     

    1762805977_-1.jpg.8ddb8aced4aee4a1183d000e5bcbdc01.jpg

     

    Например, back/up/down реализуются софтверно средствами OpenOS. При этом robot.back() из OpenOS эквивалентен компонентному вызову robot.move(2). Вообще крайне рекомендую чекнуть исходники по пред. ссылке - там много всего интересного, и на деле окажется, что работа с "чистым" роботом ничуть не сложнее, чем с библиотекой из OpenOS. Непривычно и неочевидно - да, есть такое

    • Нравится 1

  7. 11 час назад, WheatComp сказал:

    Попытался запустить и так и сяк, не получилось (прикрепил фото)

    Проверяй наличие файлов и корректность вводимых путей. Интереса ради загрузил майн, проверил - всё сработало, как и задумано:

     

    Управляющий скрипт /home/send.lua:

    Скрытый текст
    
    -- Подключаем библиотеки
    local component = require("component")
    local filesystem = require("filesystem")
    local shell = require("shell")
    local io = require("io")
    local event = require("event")
    
    -- Получаем и анализируем входные аргументы
    local args = {...}
    
    if #args < 1 then
      print("Usage: send <filename>")
      return
    end
    
    local path = shell.resolve(args[1])
    
    -- Проверяем, существует ли файл, содержимое которого
    -- мы будем отсылать по сети
    if not filesystem.exists(path) then
      print("Specified file doesn't exist")
      return
    
    -- Проверяем, не директория ли это
    elseif filesystem.isDirectory(path) then
      print("It's a directory, not file")
      return
    end
    
    -- Читаем содержимое файла
    local file = assert(io.open(args[1], "rb"))
    local data = file:read("*a")
    file:close()
    
    -- Получаем компонент модема, вставленный в хост, и
    -- открываем любой порт. Он обязан быть таким же, что и у
    -- всех подконтрольных роботов
    local modem = component.modem
    local port = 123
    modem.open(port)
    
    -- Отсылаем прочтённое содержимое всем роботам на выполнение
    -- Если файл будет слишком большим, чтобы уместиться
    -- в пакет модема - нужно будет писать отдельную
    -- систему для разбивки на пакеты. Но это уже выходит за
    -- рамки текущего примера
    modem.broadcast(port, "execute", data)
    
    -- Далее можно реализовать приёмку сообщений об ошибках,
    -- визуализировать их, логировать и т.п.

     

     

    Микропрограмма-ресивер для робота /home/eeprom.lua:

    Скрытый текст
    
    -- Получаем компонент модема, вставленный в робота, и
    -- открываем любой порт по вкусу
    local modem = component.proxy(component.list("modem")())
    local port = 123
    modem.open(port)
    
    
    -- Анализируем сигналы в бесконечном цикле
    while true do
      local data = {computer.pullSignal()}
    
      -- Если пришло сообщение по модему, и адрес модема отправителя 
      -- совпадает с адресом хоста, т.е. если мы можем доверять
      -- содержимому сообщения
      if data[1] == "modem_message" then
        
        -- Анализируем содержимое сообщения. В этом примере мы
        -- будем выполнять код, присланный хостом, и отсылать ему
        -- обратно информацию в случае неудачи
        if data[6] == "execute" and data[7] then
    
          -- Пытаемся загрузить присланную строку в виде Lua-кода
          local result, reason = load(data[7])
    
          -- Если код загружен - выполняем его
          if result then
            result, reason = xpcall(result, debug.traceback)
          end
    
          -- Если загрузка/выполнение не удались - отсылаем
          -- причину хосту. В принципе это тоже необязательный
          -- шаг, но при контроле большого кол-ва дронов/роботов
          -- будет разумно визуализировать все проблемы на хосте
          if not result and reason then
            modem.send(serverModemAddress, port, "executeFailed", tostring(reason))
          end
        end
      end
    end

     

     

    Исполняемый код, отсылаемый роботам по сети /home/robot.lua:

    Скрытый текст
    
    local robot = component.proxy(component.list("robot")())
    
    for i = 1, 4 do
      robot.turn(true)
    end

     

     

    Команда для отсылки:

    send robot.lua

    Результат:

     

    PIhndMK.gif

     

    11 час назад, WheatComp сказал:

    Кстати про require("robot"). Без него же робот не поднимется, т.к. у него нет OpenOS

    Движение робота реализуется самим роботом, а не OpenOS. Как правило, любая ОС в OpenComputers - это софтверная оболочка, функционирующая поверх аппаратной части, и предоставляющая набор библиотек, упрощающих разработку софта. Например, в случае с роботами OpenOS оборачивает компонент robot в библиотеку robot, предоставляющую набор шорткатов. То же самое и с глобальной функцией require - её попросту нет в "голых" роботах, управляемых из-под EEPROM

     

    Использовать ОС крайне удобно, когда речь идет об одном роботе, хранящем пользовательские скрипты и управляемом напрямую через экран/клавиатуру. Однако в случае с роем такое решение контр-продуктивно, т. к. любая ОС потребляет ресурсы и требует крафта более дорогостоящих компонентов - в частности, планок памяти и жёстких дисков. Это всё равно что ставить Debian на сеть устройств для капельного полива домашних растений - ну то есть да, это возможно, а зачем?

     

    Разумнее прибегнуть к концепции микропрограмм на EEPROM, которые относительно дёшевы в плане крафта, а профит в виде освободившихся слотов под компоненты существенный. Заодно можно получить интересный опыт по "низкоуровневой" разработке под опенкомпы:

    -- Робот с OpenOS
    local robot = require("robot")
    robot.moveUp()
    robot.turnRight()
    robot.swing()
    
    -- Робот с EEPROM
    local robot = component.proxy(component.list("robot")())
    robot.move(1)
    robot.turn(true)
    robot.swing(3)

     

    11 час назад, WheatComp сказал:

    а в начале файла-ресивера нет запроса библиотеки "computer". Сработает ли скрипт

    Таблицы component/computer/robot/unicode/bit32 являются суперглобальными и доступны сразу при запуске микропрограммы EEPROM, однако во время инициализации OpenOS они маскируются из соображений консистентности кода. Поэтому при работе из-под EEPROM вызов require не требуется, а из-под OpenOS уже требуется

     

    11 час назад, WheatComp сказал:

    Вот для них прямо необходима общая папка, поскольку внутри них будут всякие улучшения (верстаки, контроллеры инвентаря и емкости, а они же требуют OpenOS)

    Верстаки, контроллеры инвентаря и ёмкости - это аппаратные компоненты, функционирующие самостоятельно и не требующие OpenOS. Любая ОС - это просто средство доступа к компонентам, причём одно из многих:

    -- Робот с OpenOS
    local component = require("component")
    local sides = require("sides")
    
    local crafting = component.crafting
    local inventoryController = component.inventory_controller
    local tankController = component.tank_controller
    
    crafting.craft(10)
    inventoryController.dropIntoSlot(sides.front, 10)
    tankController.drain()
    
    -- Робот с EEPROM
    local crafting = component.proxy(component.list("crafting")())
    local inventoryController = component.proxy(component.list("inventory_controller")())
    local tankController = component.proxy(component.list("tank_controller")())
    
    crafting.craft(10)
    inventoryController.dropIntoSlot(3, 10)
    tankController.drain()

    Как видно, разница в коде несущественна, отличие лишь в способе получения доступа к ним и в наличии упрощающих библиотек типа sides

     

    10 часов назад, WheatComp сказал:

    В дополнение. По-моему, пропущены end-ы после return в скрипте для хоста?:

     

    image.png.182a9bf029c63b7f2aae0426628c041a.png

     

    Чтобы прояснить ситуацию: никто не призывает тебя использовать голый EEPROM вместо классического подхода с HDD/OpenOS. Разрабатывай так, как считаешь комфортным и оптимальным для себя - я лишь указал один из способов решения поставленной задачи, который использовал при выживании, и который сам считаю оптимальным

     

    Если тебе требуется весь богатый функционал, поставляемый OpenOS для роботов, ничто не мешает модифицировать скрипт-ресивер таким образом, чтобы он работал из-под OpenOS:

     

    Скрытый текст
    
    -- Подключаем библиотеки
    local component = require("component")
    local event = require("event")
    
    -- Получаем компонент модема, вставленный в робота, и
    -- открываем любой порт по вкусу
    local modem = component.modem
    local port = 123
    modem.open(port)
    
    -- Задаём адрес модема, вставленного в компьютер-хост
    -- Вообще это опциональная штука, необходимая для защиты от
    -- атак на роботов на публичных серверах. В локальной игре
    -- это не имеет особого смысла
    local serverModemAddress = "eafaff-fae-faefaefa-fafe"
    
    -- Ждем сообщений по модему в бесконечном цикле
    while true do
      local data = {event.pull("modem_message")}
    
      -- Если адрес модема отправителя совпадает с адресом хоста,
      -- т.е. если мы можем доверять содержимому сообщения
      if data[3] == serverModemAddress then
        
        -- Анализируем содержимое сообщения. В этом примере мы
        -- будем выполнять код, присланный хостом, и отсылать ему
        -- обратно информацию в случае неудачи
        if data[6] == "execute" and data[7] then
    
          -- Пытаемся загрузить присланную строку в виде Lua-кода
          local result, reason = load(data[7])
    
          -- Если код загружен - выполняем его
          if result then
            result, reason = xpcall(result, debug.traceback)
          end
    
          -- Если загрузка/выполнение не удались - отсылаем
          -- причину хосту. В принципе это тоже необязательный
          -- шаг, но при контроле большого кол-ва дронов/роботов
          -- будет разумно визуализировать все проблемы на хосте
          if not result and reason then
            modem.send(serverModemAddress, port, "executeFailed", tostring(reason))
          end
        end
      end
    end

     

     

    • Нравится 2

  8. @WheatComp Если по-читерски, то можно сдюпнуть жёсткий диск одного робота, вставить его в остальные и работать в одной общей папке

     

    Если по-честному, то имеет смысл дать возможность каждому роботу выполнять произвольный код, присылаемый компьютером-хостом по беспроводной сети. Это позволит избавиться от OpenOS, жёсткого диска и упростить сборку робота за счёт более дешёвых компонентов:

     

    1) Крафтим 20 чистых EEPROM и беспроводных модемов

    2) Прошиваем их скриптом-ресивером:

     

    Скрытый текст
    
    -- Получаем компонент модема, вставленный в робота, и
    -- открываем любой порт по вкусу
    local modem = component.proxy(component.list("modem")())
    local port = 123
    modem.open(port)
    
    -- Задаём адрес модема, вставленного в компьютер-хост
    -- Вообще это опциональная штука, необходимая для защиты от
    -- атак на роботов на публичных серверах. В локальной игре
    -- это не имеет особого смысла
    local serverModemAddress = "eafaff-fae-faefaefa-fafe"
    
    -- Анализируем сигналы в бесконечном цикле
    while true do
      local data = {computer.pullSignal()}
    
      -- Если пришло сообщение по модему, и адрес модема отправителя 
      -- совпадает с адресом хоста, т.е. если мы можем доверять
      -- содержимому сообщения
      if data[1] == "modem_message" and data[3] == serverModemAddress then
        
        -- Анализируем содержимое сообщения. В этом примере мы
        -- будем выполнять код, присланный хостом, и отсылать ему
        -- обратно информацию в случае неудачи
        if data[6] == "execute" and data[7] then
    
          -- Пытаемся загрузить присланную строку в виде Lua-кода
          local result, reason = load(data[7])
    
          -- Если код загружен - выполняем его
          if result then
            result, reason = xpcall(result, debug.traceback)
          end
    
          -- Если загрузка/выполнение не удались - отсылаем
          -- причину хосту. В принципе это тоже необязательный
          -- шаг, но при контроле большого кол-ва дронов/роботов
          -- будет разумно визуализировать все проблемы на хосте
          if not result and reason then
            modem.send(serverModemAddress, port, "executeFailed", tostring(reason))
          end
        end
      end
    end

     

     

    3) Вставляем прошитые EEPROM с модемами в роботов

    4) Пишем управляющий скрипт для компьютера-хоста. Предполагаю, что на нём будет использоваться OpenOS:

     

    Скрытый текст
    
    -- Подключаем библиотеки
    local component = require("component")
    local filesystem = require("filesystem")
    local shell = require("shell")
    local io = require("io")
    local event = require("event")
    
    -- Получаем и анализируем входные аргументы
    local args = {...}
    
    if #args < 1 then
      print("Usage: send <filename>")
      return
    end
    
    local path = shell.resolve(args[1])
    
    -- Проверяем, существует ли файл, содержимое которого
    -- мы будем отсылать по сети
    if not filesystem.exists(path) then
      print("Specified file doesn't exist")
      return
    
    -- Проверяем, не директория ли это
    elseif filesystem.isDirectory(path) then
      print("It's a directory, not file")
      return
    end
    
    -- Читаем содержимое файла
    local file = assert(io.open(args[1], "rb"))
    local data = file:read("*a")
    file:close()
    
    -- Получаем компонент модема, вставленный в хост, и
    -- открываем любой порт. Он обязан быть таким же, что и у
    -- всех подконтрольных роботов
    local modem = component.modem
    local port = 123
    modem.open(port)
    
    -- Отсылаем прочтённое содержимое всем роботам на выполнение
    -- Если файл будет слишком большим, чтобы уместиться
    -- в пакет модема - нужно будет писать отдельную
    -- систему для разбивки на пакеты. Но это уже выходит за
    -- рамки текущего примера
    modem.broadcast(port, "execute", data)
    
    -- Далее можно реализовать приёмку сообщений об ошибках,
    -- визуализировать их, логировать и т.п.

     

     

    5) Запускаем всех роботов и отсылаешь им любой файл с жёсткого диска на исполнение командой send robots/example.lua

     

    Единственный нюанс - работоспособность кода выше чекнуть не могу, т.к. майна под рукой нет, но общий концепт, думаю, понятен

    • Нравится 1
    • Одобряю 1
    • Спасибо 1

  9. 1 час назад, WheatComp сказал:

    В package.lua, что в папке lib папки жесткого диска робота, добавил конце строку "/D:/23/?.lua"

    Это не возымеет эффекта, т.к. библиотека package реализована средствами OpenOS, а не мода. То есть ей доступны те же данные, что и игровому жёсткому диску. А ему в свою очередь доступны только данные в рамках директории игрового сейва - это захардкожено из соображений серверной безопасности, и обойти не получится


    Вероятно, package задумывалась как полный аналог одноименной библиотеки в "реальной" версии Lua - отсюда и путаница

    • Нравится 2

  10. 8 минут назад, Bumer_32 сказал:

    а почему нельзя отключить только определённую кнопку

    Можно, button.disabled = false. Но оно меняет визуальный стиль ¯\_(ツ)_/¯

    По идее каждая кнопка имеет таблицу .colors, где можно кастомизировать все цвета. Так что можно заюзать что-то наподобие

    button.colors.disabled = button.colors.default

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

     


  11. Увы, нет, это не было предусмотрено "из коробки". Как я понял, речь идёт о неком аналоге IsHitTestVisible, когда система должна игнорировать touch-ивенты, но без смены визуального стиля элементов. Причём глобально, а не только для кнопок.

     

    На текущий момент это можно реализовать лишь с помощью перегрузки:

    window.isEventHandlingEnabled = true
    
    local originalWindowEventHandler = window.eventHandler
    
    window.eventHandler = function(workspace, window, ...)  
      if window.isEventHandlingEnabled then
        originalWindowEventHandler(workspace, window, ...)
      end
    end

     


  12. 15 часов назад, Oleshe сказал:

    Есть таблица которую оно может запринтить, но не может записать аля не хватает памяти

    Для print заданы ограничения сериализатора, которые отсутствуют в writeTable

     

    15 часов назад, Oleshe сказал:

    Объясняйся

    А какие именно - не скажу, сам думай. Не люблю приказной формат дискуссий)0

    • Нравится 2
    • Одобряю 1

  13. Хост маркета располагается на домашней OrangePI и пару недель не отвечает на пинги. Может быть, умерла загрузочная SD-карта, которая родилась с нокией le papan 15 лет назад и давно на ладан дышит. Или, что более вероятно, сработало реле в распред. щитке, и питание полностью отключилось (прощай, заморозка в холодосе). Или вообще роутер с алика самопроизвольно перепрошился и устал работать. Сложно сказать. Бекапы валяются на внешнем диске на пыльных антресолях, а сам я за тридевять земель от дома, вернусь ориентировочно к концу лета - примерно тогда и гляну, что там случилось

    • Спасибо 1
    • Ха-ха 1
    • В шоке 1
    • Грусть 1

  14. 19 часов назад, Oleshe сказал:

    gui.workspace(160,50)

    Workspace создается за пределами экрана, первые два параметра - это x и y. Подробнее в доке

      

    19 часов назад, Oleshe сказал:

    a = vector.new(10,10)

    a = vector.add(a,1,1,'square',10,10,0xFFFFFF)

    a = vector.add(a,1,1,'line',10,10,0xFFFFFF)

    Переменная "a" переопределена, из-за чего теряется объект, созданный в vector.new(). Т.е. каждая новая фигура добавляется не в родительский object, а в предыдущую фигуру

     

    Фиксим, радуемся:

     

    image.png.338abc2f440624c0bf1a3f0b73c3e734.png

     

    Это что касалось логики. Теперь по коду: осмелюсь прогнать его через Lua Beautifier, оформить ООП и дать человеческие названия переменным, т.к. "a", "what" или "toendobject" имеют сомнительную информативность. Использовать его или нет - дело, офк, твоё

     

    Приложение:

     

    Скрытый текст
    
    local vector = require("vectorImage")
    local gui = require("gui")
    
    -----------------------------------------------------
    
    local workspace = gui.workspace()
    
    -- Создаем фоновую панель для заливки цветом
    workspace:addChild(gui.panel(1, 1, workspace.width, workspace.height, 0x262626))
    
    -- Создаем холст, на котором будут располагаться фигуры
    local canvas = workspace:addChild(vector.newCanvas(3, 2, 20, 10))
    
    -- Добавляем фигуры на холст
    canvas:addShape(1, 1, "rectangle", 20, 10, 0x444444)
    canvas:addShape(3, 2, "line", 18, 9, 0xFF0000)
    canvas:addShape(10, 5, "ellipse", 6, 3, 0xFFFFFF)
    
    -----------------------------------------------------
    
    workspace:draw()
    workspace:start()

     

     

    Либа:

     

    Скрытый текст
    
    local gui = require("gui")
    local screen = require("screen")
    local fs = require("filesystem")
    local compress = require("compressor")
    
    -----------------------------------------------------
    
    local vectorImage = {}
    
    local function unpack(type)
      return compress.unpack(type, "/Temporary/lib")
    end
    
    local function pack(type)
      return compress.pack("/Temporary/lib", type)
    end
    
    function vectorImage.new(width, heigth)
      return {width = width, heigth = heigth}
    end
    
    function vectorImage.load(path)
      if fs.exists(path) then
        unpack(path)
        
        return fs.readTable("/Temporary/lib")
      else
        return nil, "File does not exists"
      end
    end
    
    function vectorImage.save(type, path)
      fs.writeTable(type, "/Temporary/lib")
      
      return pack(path)
    end
    
    local function canvasAddShape(canvas, x, y, type, ...)
      local args = {...}
      
      if type == "line" then
        table.insert(canvas.shapes, {
          type = type,
          x = x,
          y = y,
          x1 = args[1],
          y1 = args[2],
          color = args[3]
        })
      elseif type == "rectangle" then
        table.insert(canvas.shapes, {
          type = type,
          x = x,
          y = y,
          width = args[1],
          height = args[2],
          color = args[3]
        })
      elseif type == "ellipse" then
        table.insert(canvas.shapes, {
          type = type,
          x = x,
          y = y,
          radiusX = args[1],
          radiusY = args[2],
          color = args[3]
        })
      end
      
      return canvas
    end
    
    local function canvasDraw(canvas)
      local x, y = canvas.x, canvas.y
      local shapes = canvas.shapes
      local shape
    
      for i = 1, #shapes do
        shape = shapes[i]
    
        if shape.type == "line" then
          screen.drawLine(
            x + shape.x - 1,
            y + shape.y - 1,
            x + shape.x1 - 1,
            y + shape.y1 - 1,
            shape.color,
            0x0,
            " "
          )
        elseif shape.type == "rectangle" then
          screen.drawRectangle(
            x + shape.x - 1,
            y + shape.y - 1,
            shape.width,
            shape.height,
            shape.color,
            0xFFFFFF,
            " "
          )
        elseif shape.type == "ellipse" then
          screen.drawEllipse(
            x + shape.x - 1,
            y + shape.y - 1,
            shape.radiusX,
            shape.radiusY,
            shape.color,
            0xFFFFFF,
            " "
          )
        end
      end
    end
    
    function vectorImage.newCanvas(x, y, width, height)
      local canvas = gui.object(x, y, width, height)
      
      canvas.shapes = {}
    
      canvas.draw = canvasDraw
      canvas.addShape = canvasAddShape
    
      return canvas
    end
    
    return vectorImage

     

     

    • Нравится 2

  15. Исходники не полные, инфы о создании элементов фигур в what нет, поэтому чёрт его знает. На текущий момент в глаза бросается лишь использование глобальных экранных координат вместо локальных координат объекта в методе отрисовки. Все методы либы screen работают с экраном, а не с объектом toendobject, у которого есть свои собственные координаты. Поэтому для корректной отрисовки фигур нужно будет юзать конструкцию вида

    screen.drawRectangle(
      toendobject.x + what[i].x,
      toendobject.y + what[i].y,
      toendobject.x + what[i].x1,
      toendobject.y + what[i].y1,
      what[i].color,
      0xFFFFFF,
      'R'
    )

    Ещё мб ты не вызываешь метод workspace:draw() после добавления всех элементов в него, поэтому никаких новых данных на экране и не появляется

     

    И напоследок, вполне может быть, что x/y/x1/y1 не округлены, что выливается в ошибки экранной либы. Но опять же, без полных сырцов что-либо дельное сказать сложно


  16. 22 часа назад, Oleshe сказал:

    Я использовал цикл потому-что нужно было что-бы когда мы зажимали клавишу, мы ходили без остановки, только потом я догадался сделать isKeyDown, который можно встроить в eventHandler. Я использовал для начала, остатки это текст, но потом нужда в непрерывной ходьбе

    Дык его попросту нельзя использовать. Либа сама делает всю работу, обрабатывая ивенты по-своему и творя внутреннюю магию. Если используешь - получаешь "unexpected behaviour" со всеми вытекающими. Поэтому либо повторяй в точности всю "магию", либо присоединяй eventHandler как положено и не парься

     

    22 часа назад, Oleshe сказал:

    Что-бы цикл не стопорил все на 2 минуты по кастомному конфигу. Иногда мне нужно обработать огромный цикл, это занимает больше секунды и оно вылетает.

    Для твоей задачи это не требуется, т.к. удержание клавиш нажатыми в любом случае генерирует бесконечный поток событий key_down, позволяя персонажу двигаться ходить непрерывно. Авто-пуллинг событий впустую нагружает компьютер, обрабатывая их через либу. Иногда это действительно необходимо (пример с опросом реакторов), но чаще всего нет

     

    22 часа назад, Oleshe сказал:

    В чем отличие? Как и описывалось в коде список "игрок" описывает его, он не является контейнером, либо я не правильно понял ответ (или вопрос). Или имелось в виду что если используем контейнер. Из-за этого и пошла ошибка отрисовки, при перемещений контейнера на фоне пластины оно и закрашивало.

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

     

    22 часа назад, Oleshe сказал:

    Это было сделано потому-что мы просто ставили новый объект, другого способа поменять координату я не знаю.

    localX/localY

     

    22 часа назад, Oleshe сказал:

    Хотелось-бы что-бы каждый граф. элемент имел динамические координаты, кнопка, картинка, текст, ползунок и т.д., не только контейнеры

    Дык имеют же. Читай доку!!11


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

     

    1) Обработка событий встроена, workspace.start() сам по себе запускает бесконечный цикл пуллинга событий, и делать это вручную через while true нет смысла:

    wk.eventHandler = function(e1, e2, e3, e4, e5, e6)
      ...
    end
    
    wk:start(0.01)

    2) Непонятно, зачем конкретно в твоём случае используется таймаут поллинга в 0.01 сек. Эта фича используется в довольно редких ситуациях, когда нужно делать что-то с заданным интервалом. Например, опрашивать внешние компоненты типа реакторов или перемещать визуальные элементы в играх раз в N сек. Если тебе требуется лишь анализ нажатия клавиш, то:

    bg.eventHandler = function(e1, e2, e3, e4, e5, e6)
      if e1 == "key_down" then
        -- A
        if e3 == 30 then
          ...
            
          wk:draw()
        -- D
        else if e3 == 32 then
          ...
          
          wk:draw()
        end
      end
    end
    
    wk:start(0.01)

    3) Изменять экранные координаты объекта бесполезно, т.к. они рассчитываются автоматически и используются только во время вызова метода draw(). Чтобы изменить позицию элемента в родительском контейнере, используй localX/localY:

    -- Нельзя
    player.x = player.x - 2
    
    -- Можно
    player.localX = player.localX - 2

    4) Нет смысла удалять и пересоздавать объекты картинок при каждом ивенте. Если тебе нужно попросту заменить картинку в существующем виджете, то:

    local playerpic = wk:addChild(gui.image(50,25,images['Man Right']))
    
    bg.eventHandler = function(e1, e2, e3, e4, e5, e6)
      if e1 == "key_down" then
        -- A
        if e3 == 30 then
          player.facing = 'Left'
          playerpic.image = images['Man ' .. player.facing]
          
          wk:draw()
        end
      end
    end

    5) Можно вообще создать собственный виджет "player", определив ему метод отрисовки и не собирать его из GUI.image. Он и работать будет быстрее, и все поля будут храниться в одном месте, и любой кастом можно будет оформить за пару строк кода:

    local player = wk:addChild(gui.object(50, 25, 8, 4))
    player.facing = 'Left'
    
    player.draw = function(player)
      screen.drawImage(player.x, player.y, images['Man ' .. player.facing]
    end

     

    • Нравится 4

  18. 13 часа назад, Oleshe сказал:

    Почему они летают? у координата = 0 ставим стенку на 0-ой корде с высотой в 2. у = у + 2, ставим стенку на 2-й высоте, но, мне кажется, он ставит её не там

    Ничего не понял, кроме того, что они летают :p

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

    • Грусть 1
×
×
  • Создать...