Основная механика: вращение автоматически сгенерированных слоев с помощью Utils.interval

Другие игры и/или игровые механики из этой серии

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

  1. Выбор персонажа в штаб-квартире рейда [четверг, 23 июня]
  2. Бесконечный бегун [четверг, 30 июня]
  3. Guitar Hero [четверг, 7 июля]
  4. Карточный матч [четверг, 14 июля]
  5. Змея [четверг, 21 июля]
  6. Ловец падающего мяча [четверг, 28 июля]
  7. Аркадный джойстик [четверг, 4 августа]
  8. Раздвижные блоки [четверг, 11 августа]
  9. Вертикальный платформер [четверг, 18 августа]
  10. Понг [четверг, 25 августа]
  11. Буря [четверг, 1 сентября]

Ссылка на прототип Framer

Элементы игры

  • Шкала здоровья
  • Игровые кольца
  • Объект игрока
  • Пуля игрока
  • Враг
  • Управление игроком
  • Игровые циклы

Шкала здоровья

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

# Health gauge
damageMeter = new Layer
 width: Screen.width
 height: 50
health = new Layer
 parent: damageMeter
 width: Screen.width
 height: 50
 backgroundColor: "green"
 originX: 0

Игровые кольца

  • playerRing управляет вращением объекта игрока
  • bulletRing управляет вращением игрока (это отдельно от playerRing, поэтому после выстрела пуля может сохранять свою собственную траекторию и не будет вращаться при вращении объекта игрока)
  • вражеское кольцо управляет вращением вражеского объекта. Ему присваивается случайное значение в каждом игровом цикле.
# Three game rings
playerRing = new Layer
 width: 600
 height: 600
 x: Align.center
 y: Align.center
 borderRadius: "50%"
 backgroundColor: "transparent"
bulletRing = new Layer
 width: 600
 height: 600
 x: Align.center
 y: Align.center
 borderRadius: "50%"
 backgroundColor: "transparent"
enemyRing = new Layer
 width: 600
 height: 600
 x: Align.center
 y: Align.center
 borderRadius: "50%"
 backgroundColor: "transparent"
 rotation: Utils.randomNumber(0, 360)

Объект игрока

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

# Player object
plane = new Layer
 parent: playerRing
 width: 100
 height: 100
 x: Align.center
 y: Align.top(-50)
 borderRadius: 10
 backgroundColor: "white"
plane.states.add
 damaged:
  backgroundColor: "red"

Управление игроком

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

# Primary control
control = new SliderComponent
 x: Align.center
 y: Align.bottom(-100)
 width: Screen.width - 200
control.knobSize = 100
control.value = 0.5
control.on "change:value", ->
 playerRing.rotation = Utils.modulate(control.value, [0,1], [-180,180])

Игровой цикл: стрельба пулями

Каждую секунду:

  • Кольцо пули настроено так, чтобы соответствовать кольцу игрока.
  • Новая пуля создается с двумя анимациями: одна для перемещения к центру, а другая для создания эффекта взрыва. Инициализируется первая анимация, и по ее окончании сработает вторая анимация. После окончания второй анимации слой пули уничтожается в качестве очистки.
# Game loop: shoot bullets
Utils.interval 1, ->
 bulletRing.rotation = playerRing.rotation
 bullet = new Layer
  parent: bulletRing
  width: 25
  height: 25
  x: Align.center
  y: Align.top(50)
  backgroundColor: "yellow"
  borderRadius: "50%"
 shoot = new Animation
  layer: bullet
  properties: 
   y: Align.center
   opacity: .2
  time: 1
  curve: "linear"
 explode = new Animation
  layer: bullet
  properties: 
   scale: 2
   opacity: 0
  time: 0.5
 shoot.start()
 shoot.onAnimationEnd ->
  explode.start()
 explode.onAnimationEnd ->
  bullet.destroy()

Игровой цикл: возродить врага

Запуск с интервалом в 4 секунды:

  • кольцо врага случайно вращается
  • Создается новый враг, и ему назначается анимация, которая перемещает его от центра наружу к объекту игрока.
  • Когда вражеский слой движется к объекту игрока, игра наблюдает за ним, чтобы обнаружить столкновение либо между пулей игрока, либо между объектом игрока.
  • Если он сталкивается с пулей, он уничтожается, а интервал уменьшается (чтобы игра шла быстрее, тем самым увеличивая сложность)
  • Если он сталкивается с объектом игрока, кажется, что игрок получает урон, индикатор здоровья уменьшается, а пуля уничтожается.
# Game loop: respawn enemy object
time = 4
Utils.interval 4, ->
 enemyRing.rotation = Utils.randomNumber(-180,180)
 enemy = new Layer
  parent: enemyRing
  backgroundColor: "red"
  width: 25
  height: 25
  borderRadius: "50%"
  opacity: 0
  x: Align.center
  y: Align.center
 move = new Animation
  layer: enemy
  properties: 
   y: Align.top
   opacity: 1
  time: time
  curve: "linear"
 move.start()
 move.onAnimationEnd ->
  enemy.destroy()
 enemy.on "change:y", ->
  for bullet in bulletRing.children
   if (enemy.midX > bullet.x && enemy.midX < bullet.maxX) && (enemy.midY > bullet.y && enemy.midY < bullet.maxY) && (Utils.round(enemyRing.rotation, 0, 10) == Utils.round(bulletRing.rotation, 0, 10))
    if time > 1
     time -= 0.25
    else if time < 1
     time = 1
    bullet.destroy()
    enemy.destroy()
  if (enemy.midX > plane.x && enemy.midX < plane.maxX) && (enemy.midY > plane.y && enemy.midY < plane.maxY) && (Utils.round(enemyRing.rotation, 0, 40) == Utils.round(playerRing.rotation, 0, 40))
   if health.scaleX > 0.1
    health.scaleX -= 0.1
   else if Utils.round(health.scaleX, 1) == 0.1
    print "You died"
   plane.states.switchInstant "damaged"
   plane.states.switch "default"
   enemy.destroy()

Механика Framer/CoffeeScript выделена здесь

  • Слушатели событий
  • Состояния слоя
  • Поток управления
  • Петли
  • Глобальные переменные