Статьи

Создать игру о файтинге в Corona: Геймплей

Конечный продукт
Что вы будете создавать

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

Откройте gamelevel.lua , файл, который мы создали в первом уроке , и добавьте следующее ниже строки local scene = storyboard.newScene() .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
local playerSpeedY = 0
local playerSpeedX = 0
local playerMoveSpeed = 7
local playerWidth = 60
local playerHeight = 48
local bulletWidth = 8
local bulletHeight = 19
local islandHeight = 81
local islandWidth = 100
local numberofEnemysToGenerate = 0
local numberOfEnemysGenerated = 0
local playerBullets = {} — Holds all the bullets the player fires
local enemyBullets = {} — Hold the bullets from «all» enemy planes
local islands = {} — Holds all the islands
local planeGrid = {} — Holds 0 or 1 (11 of them for making a grid system)
local enemyPlanes = {} — Holds all of the enemy planes
local livesImages = {} — Holds all of the «free life» images
local numberOfLives = 3
local freeLifes = {} — Holds all the ingame free lives
local playerIsInvincible = false
local gameOver = false
local numberOfTicks = 0 — A number that is incremented each frame of the game
local islandGroup — A group to hold all of the islands
local planeGroup — A group that holds all the planes, bullets, etc
local player
local planeSoundChannel — SoundChannel for the plane sound
local firePlayerBulletTimer
local generateIslandTimer
local fireEnemyBulletsTimer
local generateFreeLifeTimer
local rectUp — The «up» control on the DPAD
local rectDown — The «down» control on the DPAD
local rectLeft — The «left» control on the DPAD
local rectRight — The «right» control on the DPAD

Большинство из них говорят сами за себя, но я включил комментарии для разъяснения. С этого момента весь код должен быть вставлен над строкой return scene .

Начните с добавления функции createScene в main.lua . Функция createScene вызывается, когда вид сцены еще не существует. В эту функцию мы добавим экранные объекты игры.

1
2
3
4
function scene:createScene( event )
    local group = self.view
end
scene:addEventListener( «createScene», scene )
1
2
3
4
5
function setupBackground ()
    local background = display.newRect( 0, 0, display.contentWidth, display.contentHeight)
    background:setFillColor( 0,0,1)
    scene.view:insert(background)
end

В setupBackground мы создаем синий фон, используя метод newRect объекта newRect . Метод setFillColor принимает значения RGB в процентах. setupBackground функцию setupBackground в createScene как показано ниже.

1
2
3
4
function scene:createScene( event )
    local group = self.view
    setupBackground()
end

Функция setupGroups создает экземпляры групп islandGroup и planeGroup и вставляет их в представление сцены. GroupObject — это специальный тип экранного объекта, в который можно добавлять другие экранные объекты. Важно сначала добавить islandGroup в view чтобы убедиться, что острова находятся ниже плоскостей.

1
2
3
4
5
6
function setupGroups()
    islandGroup = display.newGroup()
    planeGroup = display.newGroup()
    scene.view:insert(islandGroup)
    scene.view:insert(planeGroup)
end

setupGroups функцию setupGroups в createScene как показано ниже.

1
2
3
4
5
function scene:createScene( event )
    local group = self.view
    setupBackground()
    setupGroups()
end

Функция setupDisplay рисует черный прямоугольник в нижней части экрана и вставляет изображения dpad и плоскости в view .

1
2
3
4
5
6
7
8
9
function setupDisplay ()
    local tempRect = display.newRect(0,display.contentHeight-70,display.contentWidth,124);
    tempRect:setFillColor(0,0,0);
    scene.view:insert(tempRect)
    local logo = display.newImage(«logo.png»,display.contentWidth-139,display.contentHeight-70);
    scene.view:insert(logo)
    local dpad = display.newImage(«dpad.png»,10,display.contentHeight — 70)
    scene.view:insert(dpad)
end

Опять же, вызовите эту функцию в createScene как показано ниже.

1
2
3
4
5
6
function scene:createScene( event )
    local group = self.view
    setupBackground()
    setupGroups()
    setupDisplay()
end

Функция setupPlayer просто добавляет изображение плеера на экран. Объект Display имеет два свойства только для contentWidth и contentHeight , представляющие исходную ширину и высоту содержимого в пикселях. Эти значения по умолчанию соответствуют ширине и высоте экрана, но могут иметь и другие значения, если вы используете масштабирование содержимого в config.lua . Мы используем эти свойства, чтобы выровнять игрока на сцене.

1
2
3
4
5
function setupPlayer()
    player = display.newImage(«player.png»,(display.contentWidth/2)-(playerWidth/2),(display.contentHeight — 70)-playerHeight)
    player.name = «Player»
    scene.view:insert(player)
end

setupPlayer функцию createScene в createScene .

1
2
3
4
5
6
7
function scene:createScene( event )
    local group = self.view
    setupBackground()
    setupGroups()
    setupDisplay()
    setupPlayer()
end

Функция setupLivesImages устанавливает шесть изображений жизни и setupLivesImages их в верхнем левом углу экрана. Затем мы вставляем эти изображения в таблицу livesImages , чтобы мы могли ссылаться на них позже. Наконец, мы следим за тем, чтобы были видны только первые три изображения.

01
02
03
04
05
06
07
08
09
10
function setupLivesImages()
for i = 1, 6 do
      local tempLifeImage = display.newImage(«life.png», 40* i — 20, 10)
      table.insert(livesImages,tempLifeImage)
      scene.view:insert(tempLifeImage)
      if( i > 3) then
           tempLifeImage.isVisible = false;
      end
end
end

Функция setupLivesImages также вызывается в функции createScene .

1
2
3
4
5
6
7
8
function scene:createScene( event )
        local group = self.view
         setupBackground()
         setupGroups()
         setupDisplay()
         setupPlayer()
         setupLivesImages()
end

Функция setupDPad устанавливает четыре прямоугольника rectUp , rectDown , rectLeft и rectRight . Мы осторожно размещаем их поверх изображения dpad, настраиваем их так, чтобы они не были видны, и isHitTestable свойство isHitTestable установлено в значение true .

Если вы установите экранные объекты невидимыми, вы изначально не сможете с ними взаимодействовать. Однако, установив для свойства isHitTestable значение true , это поведение переопределяется.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function setupDPad()
    rectUp = display.newRect( 34, display.contentHeight-70, 23, 23)
    rectUp:setFillColor(1,0,0)
    rectUp.id =»up»
    rectUp.isVisible = false;
    rectUp.isHitTestable = true;
    scene.view:insert(rectUp)
 
    rectDown = display.newRect( 34,display.contentHeight-23, 23,23)
    rectDown:setFillColor(1,0,0)
    rectDown.id =»down»
    rectDown.isVisible = false;
    rectDown.isHitTestable = true;
    scene.view:insert(rectDown)
 
    rectLeft = display.newRect( 10,display.contentHeight-47,23, 23)
    rectLeft:setFillColor(1,0,0)
    rectLeft.id =»left»
    rectLeft.isVisible = false;
    rectLeft.isHitTestable = true;
    scene.view:insert(rectLeft)
 
    rectRight= display.newRect( 58,display.contentHeight-47, 23,23)
    rectRight:setFillColor(1,0,0)
    rectRight.id =»right»
    rectRight.isVisible = false;
    rectRight.isHitTestable = true;
    scene.view:insert(rectRight)
end

Вы уже догадались. Эта функция также вызывается в createScene .

1
2
3
4
5
6
7
8
9
function scene:createScene( event )
    local group = self.view
    setupBackground()
    setupGroups()
    setupDisplay()
    setupPlayer()
    setupLivesImages()
    setupDPad()
end

Функция resetPlaneGrid сбрасывает таблицу planeGrid и вставляет одиннадцать нулей. Таблица planeGrid имитирует одиннадцать точек поперек оси X, в которых можно расположить вражеский самолет. Это будет иметь больше смысла, как только мы начнем создавать вражеские самолеты.

1
2
3
4
5
6
function resetPlaneGrid()
planeGrid = {}
     for i=1, 11 do
         table.insert(planeGrid,0)
     end
end

Вызовите эту функцию в createScene .

01
02
03
04
05
06
07
08
09
10
function scene:createScene( event )
    local group = self.view
    setupBackground()
    setupGroups()
    setupDisplay()
    setupPlayer()
    setupLivesImages()
    setupDPad()
    resetPlaneGrid()
end

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

1
2
3
4
function scene:enterScene( event )
    local group = self.view
end
scene:addEventListener( «enterScene», scene )

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

1
2
local previousScene = storyboard.getPrevious()
storyboard.removeScene(previousScene)

Когда вы входите в новую сцену, на предыдущую сцену, на которой вы находились, можно ссылаться, вызывая getPrevious объекта storyboard . Мы полностью удаляем его из раскадровки, вызывая removeScene для объекта storyboard .

Добавьте следующий код под кодом, который вы ввели на предыдущем шаге. Этот фрагмент кода добавляет слушателей касания к каждому из прямоугольников, вызывая movePlane при каждом касании. Давайте посмотрим на эту функцию movePlane на следующем шаге.

1
2
3
4
rectUp:addEventListener( «touch», movePlane)
rectDown:addEventListener( «touch», movePlane)
rectLeft:addEventListener( «touch», movePlane)
rectRight:addEventListener( «touch», movePlane)

Функция movePlane отвечает за настройку скорости самолетов. Мы проверяем, совпадает ли фаза события касания с началом, что означает, что игрок приземлился, но не поднял палец назад. Если это правда, мы устанавливаем скорость и направление, в соответствии с которым был затронут прямоугольник. Если фаза сенсорного события равна ended , то мы знаем, что игрок поднял палец, что означает, что мы установили скорость на 0 .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
function movePlane(event)
    if event.phase == «began» then
        if(event.target.id == «up») then
          playerSpeedY = -playerMoveSpeed
        end
        if(event.target.id == «down») then
          playerSpeedY = playerMoveSpeed
        end
        if(event.target.id == «left») then
          playerSpeedX = -playerMoveSpeed
        end
        if(event.target.id == «right») then
          playerSpeedX = playerMoveSpeed
        end
    elseif event.phase == «ended» then
        playerSpeedX = 0
        playerSpeedY = 0
   end
end

Давайте добавим немного звука в нашу игру. Добавьте следующий фрагмент кода в функцию enterScene . Он загружает и играет planesound.mp3 . Если установить для свойства loops значение -1 , звук будет зацикливаться вечно. Если вы хотите узнать больше об аудио в Corona, обязательно ознакомьтесь с документацией .

1
2
local planeSound = audio.loadStream(«planesound.mp3»)
planeSoundChannel = audio.play( planeSound, {loops=-1} )

Мы также добавляем обработчик событий во время выполнения с именем enterFrame который будет вызывать функция gameLoop . Частота, с которой enterFrame событие enterFrame зависит от значения числа кадров в секунду (FPS), установленного в config.lua . В нашем примере он будет вызываться 30 раз в секунду. Добавьте этот прослушиватель событий в функцию enterScene .

1
Runtime:addEventListener(«enterFrame», gameLoop)

В функции gameLoop мы обновляем позиции спрайтов и выполняем любую другую логику, которая должна присутствовать в каждом кадре. Если вы хотите больше узнать о игровых циклах, Майкл Джеймс Уильямс написал отличную статью, в которой объясняется, как работает общий игровой цикл . Добавьте следующий фрагмент кода.

1
2
3
function gameLoop()
    movePlayer()
end

Функция movePlayer управляет перемещением плоскости игрока. Мы перемещаем плоскость в соответствии со значениями playerSpeedX и playerSpeedY , которые будут playerSpeedY 7 или 0 , в зависимости от того, касается ли игрок касания DPad или нет. Вернитесь к функции movePlane если это неясно. Мы также делаем некоторую проверку границ, чтобы убедиться, что самолет не может двигаться за пределы экрана.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
function movePlayer()
    player.x = player.x + playerSpeedX
    player.y = player.y + playerSpeedY
    if(player.x < 0) then
        player.x = 0
   end
   if(player.x > display.contentWidth — playerWidth) then
       player.x = display.contentWidth — playerWidth
   end
   if(player.y < 0) then
       player.y = 0
  end
  if(player.y > display.contentHeight — 70- playerHeight) then
      player.y = display.contentHeight — 70 — playerHeight
  end
end

Если вы протестируете игру сейчас, вы сможете перемещаться по экрану с помощью DPad.

Это завершает второй урок этой серии. В следующей части этой серии мы продолжим игровой процесс. Спасибо за чтение и увидимся там.