6 ноября 2016 г

Отправлено 7 нояб. 2016 г., 14:13 пользователем Dimitrijs Fedotovs   [ обновлено 9 нояб. 2016 г., 09:11 ]

Пройденный материал

  • Состояния спрайтов
    • конструкция состояния - defineState("state name")
    • включение состояния - activate("state name")
    • событие - onActivation(...)

Трещины на блоках

На одном из предыдущих занятиях мы узнали, что блоки могут быть сделаны из разных материалом: Материал блока. Так же нам может понадобиться, чтобы на блоке рисовались трещины. Это полезно, если мы хотим, чтобы блок разбивался не с первого удара, а со второго или третьего или четвертого.

Для того чтобы установить "уровень разбитости" блока существует метод setCrackLevel(цифра). Где цифра - это уровень разбитости от 0 до 3.

Вот таблица всех материалов блоков и "уровней разбитости":

  0 1 2 3
 CANDY 
 
 
 
 AMBER 
 
 
 
 METAL 
 
 
 
 BUBBLESTONE 
 
 
 
 GREENSTONE 
 
 
 
 CITRINE 
 
 
 
 BRICK 
 
 
 
 SAND 
 
 
 
 CONCRETE 
 
 
 

Пример:
register('*', BlockSprite::new)
.onInit(b -> b.setMaterial(BlockMaterial.SAND))
.onInit(b -> b.setCrackLevel(2));

Состояния спрайтов

В любой игре персонажи могут находится в разных состояниях, например в Арканоиде блоки могут быть целыми, а могут быть "полуразбитыми" если требуется несколько ударов, чтобы их разбить. Или мяч может быть обычным и отскакивать от всех блоков, а может быть "огненным" и пролетать сквозь все блоки.

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

Состояние - это набор обработчиков событий, которые работают только тогда, когда это состояние активно.

Мы можем написать свои обработчики (например onCollision) для каждого состояния по-разному, поэтому спрайт будет вести себя по-разному, в зависимости от того, в каких он находится состояниях. Так же мы сами можем переключать спрайт из состояния в состояние.

Состояние по умолчанию

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

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

register('+', BlockSprite::new)
// onInit для состояния по умолчанию
.onInit(b -> b.setMaterial(BlockMaterial.BRICK));

Включение состояния

Для того чтобы активировать состояние нужно вызвать метод activate("state name"):

register('+', BlockSprite::new)
// onInit для состояния по умолчанию
.onInit(b -> b.setMaterial(BlockMaterial.BRICK))
 // при столкновении перейти в состояние CRACK
.onCollision(b -> b.activate("CRACK"));

Определение состояний

В предыдущем примере мы включаем состояние CRACK сразу после того как у блока срабатывает событие столкновение (onCollision). Но наша программа пока не знает что это такое - состояние CRACK - нам нужно объяснить.

Чтобы определить новое состояние нужно воспользоваться конструкцией defineState("state name") - где, state name - любое название, которое мы даем определяемому состоянию.

register('+', BlockSprite::new)
// onInit для состояния по умолчанию
.onInit(b -> b.setMaterial(BlockMaterial.BRICK))
  // при столкновении перейти в состояние CRACK
.onCollision(b -> b.activate("CRACK"))

// определяем состояние CRACK
.defineState("CRACK");

Теперь все что будет написано ниже defineState("CRACK") будет срабатывать только тогда, когда состояние CRACK активно, т.е. после первого столкновения с мячом.

В состоянии CRACK мы хотим, чтобы на спрайте блока нарисовалась одна трещина и при втором столкновении блок должен исчезнуть:

register('+', BlockSprite::new)
// onInit для состояния по умолчанию
.onInit(b -> b.setMaterial(BlockMaterial.BRICK))
// при столкновении перейти в состояние CRACK
.onCollision(b -> b.activate("CRACK"))

// определяем состояние CRACK
.defineState("CRACK")
// все описанные ниже обработчики событий
// будут срабатывать только если
// состояние CRACK активно:
// в момент перехода в состояние CRACK
// установить трещины на блоке
.onActivation(b -> b.setCrackLevel(1))
// при втором столкновении - блок должен исчезнуть
.onCollision(ai::halt);

Событие onActivation

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

Еще более "крепкий" блок

Если мы хотим сделать еще более крепкий блок, нам нужно добавить еще одно состояние. Код блока может выглядеть вот так:

register('+', BlockSprite::new)
.onInit(b -> b.setMaterial(BlockMaterial.BRICK))
.onCollision(b -> b.activate("CRACK1"))

.defineState("CRACK1")
.onActivation(b -> b.setCrackLevel(1))
.onCollision(b -> b.activate("CRACK2"))

.defineState("CRACK2")
.onActivation(b -> b.setCrackLevel(2))
.onCollision(ai::halt);

Дополнительно

Сообщения

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

Для этого существуют событие. Спрайт в любое время может отправить сообщение всем другим спрайтам. Другие спрайты могут на это событие отреагировать должным образом.

Отправка сообщений

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

Получение сообщений

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

Обработчик - это уже знакомый код, как ai::halt, b -> b.setColor(...) и т.д. и т.п.

Пример

Проще показать это на примере. Ниже зарегистрированы два блока. В момент, когда исчезает (выбивается) кирпичный блок (BRICK), отправляется сообщение message("bang!").

Все песочные блоки (SAND) отлавливают это сообщение и так же исчезают: onMessage(ai::halt, "bang!").

register('+', BlockSprite::new)
.onInit(b -> b.setMaterial(BlockMaterial.BRICK))
.onCollision(ai::halt)
.onHalt(b -> message("bang!"));

register('*', BlockSprite::new)
.onInit(b -> b.setMaterial(BlockMaterial.SAND))
.onInit(b -> b.setCrackLevel(2))
.onCollision(ai::halt)
.onMessage(ai::halt, "bang!");

ВНИМАНИЕ! очень важно не ошибиться в тексте сообщения. Если в событии onMessage будет написан текст, отличающийся хоть одной буквой от отправленного сообщения при помощи message - событие не сработает.

Домашнее задание

  1. В игре Арканоид сделать специальный очень крепкий блок из материала METAL, который возможно выбить, попав в него 4 раза. У нас только 3 уровня трещин (1, 2 и 3), поэтому для четвертого раза последний уровень (3) пусть повторится.
  2. Разобраться с сообщениями (описаны выше)
  3. КРЕПКИЙ ОРЕШЕК:
    Когда металлический блок выбивается шариком, шарик должен стать огненным (setFlame(true)). Сделать это при помощи сообщений.
  4. ОЧЕНЬ КРЕПКИЙ ОРЕШЕК:
    Когда шарик становится огненным - он должен пролетать через и уничтожать все блоки, не отскакивая от них.
    подсказка: у шарика должно быть два дополнительных состояния "нормальный" - где он будет отскакивать от блоков и "огненный" - где не будет отскока.
  5. СОВСЕМ КРЕПКИЙ ОРЕШЕК (если со всеми заданиями справились):
    Огненный шарик пролетает сквозь все блоки, кроме металлических. От металлических просто отскакивает.
Comments