RU

Counter и Onnewloc

mkir #1281 02.08.2015 12:25 27 comments 13742 views

Вот уже неоднократно читал везде, что нельзя вообще ничего делать в counter, мол, только в каких-то уникальных и редких случаях. И зная, в общем-то, что собой представляют и counter и onnewloc, до сих пор не совсем понимаю, почему с ними нужно быть осторожнее и что нужно сделать, например, чтобы “перегрузить” counter. В моем текущем проекте (см.Нутбайский наемник) counter перегружен, но все до сих пор вроде бы работает нормально, как и задумывалось, но в данный момент все равно пытаюсь его “разгружать”.

По теме - хотелось бы услышать все за и против.

Edited at 02.08.2015 12:25 (10 years ago)

Только не давно начал им пользоваться… Как-то сказать по теме не чего.

mkir:

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

Честно говоря, я не могу придумать ни одного “уникального и редкого случая”, где понадобился бы COUNTER. Он же привязан к реальному времени, а это именно то, чего надо всячески избегать в текстовой (сиречь пошаговой) игре.

Локация COUNTER, она же “локация-счётчик”, по умолчанию вызывается плеером раз в полсекунды. Частоту вызова можно настроить.

Её предназначение - добавить в игру фишки, основанные на “реальном времени”.

Можно: Геймплейные фишки реального времени.

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

Пример использования COUNTER по назначению: Взорви зомби.

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

Также, помимо геймплейных фишек “в реальном времени”, в COUNTER можно делать вещи, относящиеся к оформлению, не влияющие на прохождение игры.

Можно: Анимационные эффекты.

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

Можно: Фоновая музыка.

Именно с помощью COUNTER делается “зацикливание” музыки в QSP-плеере. Трек заканчивается, и при очередном выполнении COUNTER он запускается заново, либо начинается следующий трек из плейлиста. Без использования COUNTER, музыка проиграется только один раз.

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

Нельзя: выносить проверки и прочий “обычный” код.

Проверки “у игрока закончилось здоровье”, “игрок смертельно устал”, “монстр убит”, “нужно получить уровень за опыт”, и любые другие, которые автору лень прописывать в нужных местах. Автор помещает их в COUNTER, и тем самым сразу создаёт ошибки в игре.

Например. При ходе в бою, у игрока и монстра последовательно отнимается здоровье. Бьёт монстр - игрок теряет здоровье, бьёт игрок - монстр теряет здоровье. При этом в COUNTER стоит проверка:

IF здоровье_игрока <= 0:
    GT 'смерть'
END

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

Но COUNTER не выполнит проверку “сразу”! Он вызывается раз в полсекунды.

Поэтому, у игрока есть ещё полсекунды, чтобы нанести “мёртвым” персонажем урон врагу, и даже убить его. А если это был решающий бой, то и пройти игру. А потом умереть! Или не умереть, если автор на следующих локациях выключил COUNTER…

Список возможных игровых ошибок одним лишь этим примером не ограничивается. Любой код, вынесенный “наобум” в COUNTER, любое использование COUNTER не по назначению, неминуемо ведёт к ошибкам. Поэтому делать так запрещается.

“А куда в таком случае выносить?” Все проверки лучше выносить в отдельную локацию, и выполнять её сразу после изменения соответствующей переменной. Вызов локации делается оператором GS.

Примеры правильной проверки:

здоровье_игрока = здоровье_игрока - сила_удара_врага
! Проверяем, не убили ли нашего игрока.
GS 'проверка_здоровья'
опыт = опыт + 200
! Проверяем, не нужно ли повысить уровень.
GS 'уровни'

то есть только gs? даже onnewloc не подходит?

Aleks Versus Moderator 03.08.2015 11:44 (10 years ago)

mkir,
назначение onnewloc - выполнять некий отрезок кода только при переходе на новую локацию. Допустим ты делаешь увеличение опыта из действия:

act 'получить 100 опыта':
    exp+=100
end

Как в таком случае изменится уровень героя, если ты забил его изменение в onnewloc?

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

Aleks Versus:

Как в таком случае изменится уровень героя, если ты забил его изменение в onnewloc?

gt $curloc :)

В любом случае, при данном действии надо обновлять описание. А тут уж, кому как. Либо сразу после exp+=100 дописывать функцию проверки, либо обновить локу, а проверку делать в onnewloc :)

Другой вопрос, что onnewloc выполняется толькопосле просчета всей локации. Так, что если у вас привязаны события к определенному статам, то выполнять расчет лучше до локации. Вот тут бы пригодился $onbeforeloc :)

Aleks Versus Moderator 03.08.2015 12:11 (10 years ago)

mkir,
эм… я не в курсе, как устроено всё в твоей игре, но могу с закрытыми глазами сказать, что не везде применяется та же механика, которую используешь ты.

Для повышения уровня, конечно, не критично, если ты сделаешь проверку в onnewloc (хотя я хоть убей не понимаю, что мешает написать лишнюю локацию (которая будет пересчитывать опыт) и вызывать её, когда опыт увеличивается), но вот проверку здоровья пилить в onnewloc уже нельзя. Потому что проверка здоровья будет выполняться не в тот же самый момент, когда изменится здоровье, что чревато теми самыми глюками, какие описал выше Некс.

Помню, когда я только познакомился с платформой QSP, counter был объективной реальностью. Не было ни onnewloc, ни возможности писать свои функции и процедуры, поэтому все лепили в counter что только могли. Именно тогда и висел в справке совет, как разгрузить эту локацию, ибо объёмы помещаемого в неё кода были гигантскими (а в справке 5.6.4 до сих пор намекается, что counter берёт на себя все задачи обработчиков от строки ввода до перехода между локациями), но с тех пор появилась куча локаций-обработчиков, плюс возможность писать собственные обработчики.

mkir:

то есть только gs? даже onnewloc не подходит?

Увы. Специфика ONNEWLOC в том, что она выполняется после исполнения кода локации и до “передачи хода” игроку. Если в коде локации есть переменные, зависящие от ONNEWLOC, то их значения будут не актуальны при исполнении “выполнить при посещении”.

Пример.

! В ONNEWLOC
golod += time*golod/10
! В Loc1
golod = 50
time = 0
act "go to Loc2": time += 10 & gt 'Loc2'
! В Loc2
*nl golod
if golod = 100: gt 'end'

Автор ожидает, что при таких условиях golod станет равен 100 и игра закончится. Но нет! При заходе в локацию Loc2 переменная time = 10, а golod = 50(!). Код локации исполнится и вы увидите надпись 50. После этого выполнится ONNEWLOC и, следуя коду, golod станет равен 100. Ход перейдет игроку. Мы имеем ожидаемые значения переменных, но неожидаемое поведение плеера :)

Aleks Versus Moderator 03.08.2015 12:23 (10 years ago)

Babai,
описание нужно обновлять только если статы выводятся в описании. Если же они выводятся в доп описании? Или в инвентаре? Обновляется инвентарь или доп описание соответственно. Не суть. Переходы подобные goto $curloc сами по себе таят много опасностей, ими тоже следует пользоваться с умом. Я тысячи раз зацикливал свои поделки такими переходами по невнимательности. Во всяком случае, если достаточно опыта и сноровки, чтобы грамотно использовать goto $curloc, должно достать опыта написать маленькую локу, которая будет и изменять здоровье героя и проверять, не помер ли он:

!#health
! вызов например gs 'health',-100
HP=HP+args[0]
if HP<1:
   gt 'локация_смерти'
end

и что-нибудь такое же для изменения опыта и уровня.

mkir,
да, только GS.

Отбласть применения ONNEWLOC ещё хитрее, чем у COUNTER.

Почему ONNEWLOC бесполезен для проверок? Потому что сработает только при следующем переходе на локацию.

Нельзя: выносить проверки в ONNEWLOC

Вот, допустим:

ACT 'Подёргать ручку двери':
    IF RAND(1, 3) = 3:
        'Раздался щелчок и громкое шипение. Дверь была заминирована! Но взрывной механизм не сработал. Вам сопутствует удача.'
        XGT 'Тайная комната'
    ELSE
        'Раздался взрыв. Дверь была заминирована! Это последнее, о чём вы успели подумать перед смертью...'
        здоровье_игрока = 0
    END
END

Локация ONNEWLOC:

IF (здоровье_игрока <= 0) AND (умер = 0):
  умер = 1
  XGT 'смерть'
END

В таком примере, игрок может сколько угодно раз пробовать открыть дверь и взрываться, на локацию “Смерть” он не попадёт. Рано или поздно он сможет открыть дверь успешно, без взрыва… Только потом при переходе в “тайную комнату” он внезапно умрёт, потому что сработает проверка в ONNEWLOC.

Для чего же используется ONNEWLOC? Он нужен только тогда, когда для вас имеет значение сам факт перехода c одной локации на другую. Во всех остальных случаях, его использовать не стоит.

Можно: использовать для подсчёта ходов

Это чисто геймплейная фишка. Вы можете в игре сделать подсчёт ходов. Например, вам нужно обезвредить бомбу, таймер тикает, каждый переход по локации равняется 15 секундам (одному ходу). Успел игрок, уложился в нужное количество ходов - задание выполнено. Не успел - взорвался.

Можно: использовать для течения игрового времени

Аналогично предыдущему. С каждым переходом на локацию движется игровое время. ONNEWLOC уже используется не для однократного задания, а для “фоновых процессов”. Можно, например, вести учёт времени, дней, считать усталость и голод, и т.д.

Можно: использовать для учёта прошлых локаций

Иногда требуется “запомнить” предыдущую локацию. Делается это как раз с помощью ONNEWLOC.

Пример: игрок может в любой момент игры поговорить с божеством, для этого диалога автор создал отдельную локацию. Завершив диалог, игрок возвращается туда, где был. Но как узнать, где он был до этого?

Варианта два: либо заранее сохранять текущую локацию в отдельную переменную, либо вести учёт переходов в ONNEWLOC.

Код см. здесь: Возврат на предыдущую локацию.

Итого, использование локаций COUNTER и ONNEWLOC должно быть осмысленным.

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

Спасибо. Все понимаю и постараюсь переделать проект соответственно полученным знаниям). Но одно НО. Все равно не понимаю. В вашем приведенном примере кода достаточно поставить под “здоровье_игрока=0” gt $curloc и все нормально заработает, игрока моментально перебросит в “смерть”. я даже проверил - это действительно так. неужели и gt $curloc’ом пользоваться возбраняется? Вот не знаю, я все время делаю это “возвращение локации” при любых изменениях переменных, чтобы они сразу вступали в силу.

Aleks Versus Moderator 03.08.2015 14:18 (10 years ago)

mkir,
в простых примерах всегда всё просто.

mkir,
хороший вопрос.

Да, с помощью “костыля” GT $CURLOC можно “принудительно” вызвать ONNEWLOC. И тогда пример выше “заработает”.

Spoiler

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

В ONNEWLOC и COUNTER авторы “запихивают” проверки в основном затем, чтобы избавить себя от вызова локации с проверками. Чтобы писать код, а проверки происходили как бы “сами собой”, без принудительного вызова.

Если же мы делаем принудительный вызов через

GT $CURLOC

то чем это лучше обычного вызова локации по GS?

GS 'проверки'

Кода писать столько же. А в последнем случае он будет даже понятнее.

Тогда в чём смысл выносить проверки именно в ONNEWLOC? Почему не использовать отдельную локацию для проверок? В чём практический смысл использования ONNEWLOC не по назначению?

Не вижу на это никаких объективных причин.

Нужна служебная локация, $ONGOTO :) Срабатывающая в начале перехода и до кода “выполнить при посещении”.

Это сильно упростит жизнь всяческим проверкам, вычислениям статов и т.д. :)

Log in or Register to post comments.