openlang

Покрасил одну полоску здоровья, покраснели все

На этот раз меня укусили общие ресурсы в интерфейсе Godot. У каждого юнита в моём маленьком прототипе есть сцена полоски здоровья, это ProgressBar с переопределённым StyleBoxFlat для заливки. Когда здоровье падает ниже 25, я хотел, чтобы краснела только эта полоска, и написал вот так.

func _on_health_changed(value: float) -> void:
	$Bar.value = value
	var fill := $Bar.get_theme_stylebox("fill") as StyleBoxFlat
	fill.bg_color = Color.RED if value < 25.0 else Color.GREEN

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

Не уверен я в исправлении. Вызвать duplicate() в _ready, включить resource_local_to_scene или вообще перестать писать в стайлбокс?

Разговор продолжается 10

Следуя доводам
  1. Ссылка на ответ

    Два из этих вариантов сохраняют изменение и дают каждой полоске собственный объект для изменения. Третий меняет саму операцию. Если у заливки всего два вида, то два стайлбокса, которые никогда не меняются и назначаются через add_theme_stylebox_override, означают, что ни одна полоска не владеет ничем изменяемым, и общий объект перестаёт быть проблемой.

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

  2. Ссылка на ответ

    Меньше всего кода требует одна галочка. Выберите StyleBoxFlat в инспекторе, раскройте раздел Resource и включите Local To Scene. Тогда каждый экземпляр сцены полоски при создании получает свою копию, а обработчик можно оставить ровно как есть. Я бы начал с этого и писал что-то ещё, только если этого окажется мало.

  3. Ссылка на ответ

    Следите за get_theme_stylebox("fill"). Уберите однажды переопределение, и эта строка вернёт стайлбокс из темы, а обработчик начнёт перекрашивать все ProgressBar с этой темой. duplicate() перед записью закрывает оба случая, и для StyleBoxFlat хватает поверхностной копии.

  4. Ссылка на ответ

    Прежде чем ставить галочку, стоит проверить, где на самом деле живёт стайлбокс, потому что Local To Scene действует на ресурс, который использует сцена. Это встроенный подресурс сцены полоски или файл .tres, на который указывает переопределение? Насколько я знаю, и то и другое должно копироваться на каждый экземпляр. А вот стайлбокс из Theme на каком-нибудь родительском узле - это другой объект, и галочка на собственном переопределении полоски до него не дотянется. Когда это ясно, галочка вполне безопасный первый шаг.

  5. Ссылка на ответ

    Встроенный подресурс, сохранён прямо в health_bar.tscn, никакой Theme выше нет. Включил Local To Scene, и всё работает, краснеет только полоска раненого юнита, остальные остаются зелёными. Немного досадно, что это галочка, а не поведение по умолчанию, но понимаю, большинство стайлбоксов как раз и задуманы общими, копировать их все было бы глупо.

  6. Ссылка на ответ

    Теперь мне любопытно, сколько вообще существует стайлбоксов. Выведите $Bar.get_theme_stylebox("fill").get_instance_id() для двух полосок в _ready. До галочки одно число, после два разных, если копия появляется там, где мы думаем. А потом попробуйте полоску, добавленную во время работы через instantiate(), а не расставленную в редакторе, просто чтобы посмотреть, получит ли и она свою копию.

  7. Ссылка на ответ

    Зачем вообще трогать стайлбокс? self_modulate у каждого узла свой. Поставьте красный одной полоске, и ничего общего не будет. Двести полосок - это двести значений цвета на двухстах узлах и ноль лишних ресурсов.

  8. Ссылка на ответ

    Пробовал это первым делом, так и дошёл до стайлбокса. self_modulate умножает всё, что рисует полоска, так что серый фон темнеет до бордового вместе с заливкой, и уже не разобрать, сколько осталось здоровья. Для короткой вспышки при попадании годится, для состояния, которое держится, нет.

  9. Ссылка на ответ

    Я тоже за заготовки, хотя по другой причине, чем число объектов. Правило, которое стоит сохранить, такое. Цвет каждой полоски зависит от здоровья её собственного юнита и больше ни от чего. Общий изменяемый стайлбокс его нарушает, позволяя здоровью одного юнита решать цвет чужой полоски. Копии правило восстанавливают при условии, что каждая копия действительно создана. Две заготовки восстанавливают его, ни от какой копии не завися, потому что обработчик выбирает одну по текущему значению, а сами заготовки никогда не меняются. Значение 25 я бы всё же проверил отдельно, там < и <= расходятся.

  10. Ссылка на ответ

    Вывел идентификаторы. До галочки у всех шести полосок был один и тот же, после стало шесть разных, и полоска, добавленная через instantiate(), тоже получила свой. Так что всё сработало ровно как описано.

    А потом я всё равно перешёл на два заготовленных стайлбокса, в основном из-за довода tally6. Обработчик сократился до такого.

    const FILL_OK := preload("res://ui/fill_ok.tres")
    const FILL_LOW := preload("res://ui/fill_low.tres")
    
    func _on_health_changed(value: float) -> void:
    	$Bar.value = value
    	var fill: StyleBox = FILL_LOW if value < 25.0 else FILL_OK
    	$Bar.add_theme_stylebox_override("fill", fill)

    Local To Scene снова выключен, в стайлбоксы теперь ничего не пишет. Считать ли ровно 25 низким, так и не решил.

Другие стороны того же вопроса

  1. Должна ли отклонённая плитка сдвигать палитру?

    Если просто создать корутину в Lua, ещё ничего не выполняется. Хост вызывает coroutine.resume, и только тогда генератор работ…

  2. Кто владеет буфером Vulkan после отправки?

    C++ RAII-обёртка над буфером Vulkan записывает работу с этим буфером, отправляет её и выходит из области видимости. Деструкто…

    Ответы 5cppc
  3. Статус «Сохранено» зачитывается дважды

    После одного сохранения скринридер дважды произнёс «Saved». За этим стоит состояние всего с одним переходом в saved, и тест р…