Добро пожаловать во вторую часть учебного пособия « Как взорвать вещи с помощью Corona SDK ».  В этом уроке мы усовершенствуем наше демонстрационное приложение из первой части, позволяя пользователю размещать на экране изображение фактической бомбы с замедленным взрывом.  Мы также изменим эффект взрыва, чтобы ящики иногда взрывались, а не просто летали с экрана. 
В первой части этой серии статей мы познакомились с тем, как настроить динамическую среду с помощью простой в использовании библиотеки физики Corona. Среда включала в себя статические объекты, такие как пол, и программно сгенерированные динамические объекты, такие как ящики. Событие прикосновения пользователя будет генерировать взрывную силу, которая приведет к полету ящиков. Если вы еще не читали первую часть , я предлагаю вам сделать это, прежде чем продолжить. У читателя должно быть понимание основных физических понятий, описанных в части I, чтобы понять часть II.
напоминание
Давайте начнем с небольшого обновления о том, как мы настраиваем нашу физическую среду в части I. Мы включим наш программно сгенерированный набор ящиков, которые укладываются и лежат на полу:
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
 | 
 local physics = require(«physics») 
physics.start() 
physics.setScale( 40 ) 
display.setStatusBar( display.HiddenStatusBar ) 
— The final «true» parameter overrides Corona’s auto-scaling of large images 
local background = display.newImage( «bricks.png», 0, 0, true ) 
background.x = display.contentWidth / 2 
background.y = display.contentHeight / 2 
local floor = display.newImage( «floor.png», 0, 280, true ) 
physics.addBody( floor, «static», { friction=0.5 } ) 
  local crates = {} 
for i = 1, 5 do 
    for j = 1, 5 do 
        crates[i] = display.newImage( «crate.png», 140 + (i*50), 220 — (j*50) ) 
        physics.addBody( crates[i], { density=0.2, friction=0.1, bounce=0.5 } ) 
    end 
end 
 | 

Все действия вышеупомянутого кода полностью объяснены в Части I учебного пособия, поэтому проверьте его, если что-то кажется непонятным.
Установите бомбу!
Для нашего первого шага мы собираемся добавить небольшое графическое обновление в наш метод setBomb. Вместо события касания, немедленно генерирующего взрыв, мы собираемся поместить бомбу на экран как ее собственный физический объект:
| 
 1 
2 
3 
4 
5 
6 
7 
 | 
 local function setBomb ( event ) 
    if(event.phase == «began») then 
        local bomb = display.newImage( «bomb.png», event.x,event.y ) 
        physics.addBody( bomb, { density=0.2, friction=0.1, bounce=0.5 } ) 
    end 
end 
background:addEventListener(«touch»,setBomb) 
 | 

Как и раньше, мы добавляем прослушиватель событий в фоновый режим, чтобы отслеживать любые сенсорные события и запускать метод setBomb при его возникновении. Внутри метода мы изолируем любую деятельность от «начальной» фазы события. Если бы мы не изолировали эту фазу, это привело бы к многократному выполнению кода, поскольку события касания имеют много фаз.
Метод setBomb в его нынешнем виде делает очень мало. Он загружает симпатичную графику бомбы и добавляет ее на экран как динамический физический объект. Теперь мы интегрируем наш метод взрыва обратно в код как локальная функция с именем blast:
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
 | 
 local circle = «» 
local explosion = «» 
local function blast( event ) 
    circle = display.newCircle( bomb.x, bomb.y, 80 ) 
    explosion = display.newImage( «explosion.png», bomb.x, bomb.y ) 
    circle:setFillColor(0,0,0, 0) 
    physics.addBody( circle, «static», {isSensor = true} ) 
    circle.myName = «circle» 
    circle.collision = onLocalCollision 
    circle:addEventListener( «collision», circle ) 
 end 
blast() 
 | 
Объект круга здесь помогает нам рассчитать радиус взрыва. Мы добавляем прослушиватель событий столкновения, чтобы определить, какой из ящиков попадает в зону взрыва. Кроме того, мы также добавляем изображение взрыва на экран в том же положении, что и изображение бомбы, после того, как взрыв гаснет.
Время взрыва
Если вы попробуете код на этом этапе, вы заметите, что все происходит очень быстро и некоторые артефактные изображения остаются позади. Чтобы сделать его более напряженным, мы заменим вызов функции «blast ()» таймером, который задержит взрыв бомбы на 3 секунды:
| 
 1 
 | 
 timer.performWithDelay(3000, blast ) 
 | 
Просто как тот! Класс таймера имеет функцию executeWithDelay () с двумя параметрами: количество миллисекунд ожидания и метод вызова. Таким образом, в этом случае 3000 миллисекунд равны 3 целым секундам задержки.
Удаление артефактов
Поскольку после 3-секундной задержки бомба взорвется, нам нужно убрать этот объект с экрана, как только произойдет взрыв. Это можно сделать очень легко. Все объекты, присутствующие на экране, снабжены удобной функцией removeSelf (). Как только объект удаляется с экрана, физический движок становится достаточно умным, чтобы собирать мусор и удалять его из всех физических расчетов. Мы можем добавить следующую строку в конец функции взрыва:
| 
 1 
 | 
 bomb:removeSelf() 
 | 
В дополнение к удалению бомбы мы собираемся удалить наш объект с радиусом взрыва окружности, а также график взрыва, который мы добавили для эффекта. Поскольку нам нужно дать нашему кругу радиуса взрыва немного времени для создания столкновений с нашими ящиками, мы собираемся удалить его на 1/10 секунды после вызова функции взрыва. Это может быть достигнуто путем замены нашего вызова функции timest blast следующим кодом:
| 
 1 
2 
3 
4 
5 
6 
 | 
 local function removeStuff( event ) 
    circle:removeSelf() 
    explosion:removeSelf() 
 end 
 timer.performWithDelay(3000, blast ) 
 timer.performWithDelay(3100, removeStuff) 
 | 
Как вы можете видеть, мы вызываем функцию взрыва после 3-секундной задержки от прикосновения к экрану, как мы это делали раньше. Теперь мы добавили еще один отложенный вызов, который выполняется через 3,1 секунды после прикосновения к экрану, который очищает наши остаточные объекты с помощью функции removeSelf ().
Вся созданная нами функция теперь выглядит так:
| 
 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 
 | 
 local function setBomb ( event ) 
    if(event.phase == «began») then 
        local bomb = display.newImage( «bomb.png», event.x,event.y ) 
        physics.addBody( bomb, { density=0.2, friction=0.1, bounce=0.5 } ) 
                 local circle = «» 
        local explosion = «» 
        local function blast( event ) 
            media.playEventSound( explosionSound ) 
            circle = display.newCircle( bomb.x, bomb.y, 80 ) 
            explosion = display.newImage( «explosion.png», bomb.x, bomb.y ) 
            bomb:removeSelf() 
            circle:setFillColor(0,0,0, 0) 
            physics.addBody( circle, «static», {isSensor = true} ) 
            circle.myName = «circle» 
            circle.collision = onLocalCollision 
            circle:addEventListener( «collision», circle ) 
         end 
         local function removeStuff( event ) 
            circle:removeSelf() 
            explosion:removeSelf() 
         end 
         timer.performWithDelay(3000, blast ) 
         timer.performWithDelay(3100, removeStuff) 
    end 
end 
background:addEventListener(«touch»,setBomb) 
 | 
Создание порога разрушения
В первой части нашего руководства функция обнаружения столкновений выглядела следующим образом:
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
 | 
 local function onLocalCollision( self, event ) 
        if ( event.phase == «began» and self.myName == «circle» ) then 
            local forcex = event.other.x-self.x 
            local forcey = event.other.y-self.y 
            if(forcex < 0) then 
                forcex = 0-(80 + forcex)-12 
            else 
                forcex = 80 — forcex+12 
            end 
            event.other:applyForce( forcex, forcey, self.x, self.y ) 
        end 
end 
 | 
Он просто нашел все ящики в радиусе взрыва и применил силу, чтобы взорвать их от эпицентра взрыва. Чтобы сделать вещи более интересными, мы собираемся добавить порог разрушения к ящикам, который заставит их взорваться, если сила, приложенная к ним, достаточно высока. Это можно сделать так:
| 
 1 
2 
3 
4 
5 
6 
7 
8 
 | 
 if(math.abs(forcex) > 60 or math.abs(forcey) > 60) then 
    local explosion = display.newImage( «explosion.png», event.other.x, event.other.y ) 
    event.other:removeSelf() 
    local function removeExplosion( event ) 
        explosion:removeSelf() 
    end 
    timer.performWithDelay( 100, removeExplosion) 
end 
 | 
Нам нужно наблюдать силу, которую мы применили к каждому из ящиков, чтобы определить, не превышает ли она 60. 60 в этом случае в произвольном числе на основе нашего расчета силы. Мы используем функцию math.abs, чтобы получить абсолютное значение силы. В нашем примере силы могут быть положительными или отрицательными в зависимости от направления приложенной силы. Мы не беспокоимся о направлении в этом случае, мы просто хотим знать, превышает ли сила порог 60. Не стесняйтесь играть с этим пороговым числом, чтобы изменить, насколько слабые или сильные ящики.
В первой части наша сила взрыва была рассчитана таким образом, чтобы она уменьшалась по мере удаления ящика от эпицентра взрыва. Поэтому ящики, которые находятся ближе к эпицентру, имеют большую вероятность быть уничтоженными, а остальные просто улетят с экрана. Как мы делали раньше с нашим классом таймера, мы отображаем графику взрыва в прежней позиции разрушенного ящика, а затем удаляем его с экрана 1/10 секунды позже. Окончательный метод обнаружения столкновений будет выглядеть так:
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
 | 
 local function onLocalCollision( self, event ) 
        if ( event.phase == «began» and self.myName == «circle» ) then 
            local forcex = event.other.x-self.x 
            local forcey = event.other.y-self.y 
            if(forcex < 0) then 
                forcex = 0-(80 + forcex)-12 
            else 
                forcex = 80 — forcex+12 
            end 
            event.other:applyForce( forcex, forcey, self.x, self.y ) 
            if(math.abs(forcex) > 60 or math.abs(forcey) > 60) then 
                local explosion = display.newImage( «explosion.png», event.other.x, event.other.y ) 
                event.other:removeSelf() 
                local function removeExplosion( event ) 
                    explosion:removeSelf() 
                end 
                timer.performWithDelay( 50, removeExplosion) 
            end 
        end 
end 
 | 


Звучать на!
В качестве последнего шага в нашем уроке мы собираемся воспроизвести звуковой эффект взрыва при взрыве нашей бомбы. Как и все остальное в Corona, сделать это на удивление просто. Мы начнем с включения медиа-библиотеки в начало нашего проекта и предварительной загрузки нашего звукового файла:
| 
 1 
2 
 | 
 local media = require(«media») 
local explosionSound = media.newEventSound( «explosion.mp3» ) 
 | 
Для воспроизведения звука мы добавим следующую строку в нашу функцию blast (), которая находится внутри нашей функции setBomb ():
| 
 1 
2 
3 
4 
 | 
 local function blast( event ) 
    media.playEventSound( explosionSound ) 
    … 
end 
 | 
Теперь каждый раз, когда вызывается функция blast (), она будет использовать функцию playEventSound из библиотеки мультимедиа для воспроизведения нашего звукового файла «explo.mp3». Это не может быть проще, если мы попробуем!
И там у нас это есть! Теперь у нас есть более полный пример того, как легко создавать взрывы на платформе Corona. Не стесняйтесь загружать почтовые индексы для части I и II учебника и поиграть!