0939120  

技術工具: Corona SDK
執行難度: 普通
操作時間: 30 到60分鐘
書接上文
在本系列文章的前編中,我們瞭解了平衡球小遊戲的基本概念並創建出基礎使用者介面。在後編中,我們將共同完成基礎物理效果創建、遊戲互動性編寫等工作,並最終製作出能夠給使用者帶來樂趣的應用成品。

 
步驟二十九: 添加物理效果
為遊戲中的各個圖形元素分配物理屬性,要注意靜態元素是不能移動的。另外還要檢查玩家操控的小球與陷阱洞的半徑,這些元素必須聲明使用圓形物理屬性而不能用一般的方形,這樣會提高物理碰撞效果、提高遊戲性。

-- Add Physics to GFX  

 

physics.addBody(left, 'static') 

physics.addBody(right, 'static') 

physics.addBody(top, 'static') 

physics.addBody(bottom, 'static')  

 

physics.addBody(b1, 'static') 

physics.addBody(b2, 'static') 

physics.addBody(b3, 'static') 

physics.addBody(b4, 'static')  

 

physics.addBody(h1, 'static', {radius = 15}) 

physics.addBody(h2, 'static', {radius = 15}) 

physics.addBody(h3, 'static', {radius = 15}) 

physics.addBody(h4, 'static', {radius = 15}) 

physics.addBody(h5, 'static', {radius = 15}) 

  

physics.addBody(player, {radius = 14}) 

physics.addBody(goal, 'static', {radius = 15}) 




 
步驟三十: 將陷阱小洞設置為感應器
由於作為陷阱的小洞本身不會產生物理碰撞效果,因此我們只要為其設置接觸感應器即可。


 

-- Set Holes as Sensors      

    h1.isSensor = true 

    h2.isSensor = true 

    h3.isSensor = true 

    h4.isSensor = true 

    h5.isSensor = true      

    --gameListeners('add') 

end 

 步驟三十一: 代碼審查
以下列出的是本教程所提到全部代碼綱要,大家可以從宏觀角度對作品進行核查,確定所有要素都已經包含在程式成品當中:

-- Teeter like Game 

 

-- Developed by Carlos Yanez 

 

  

 

-- Hide Status Bar 

 

  

 

display.setStatusBar(display.HiddenStatusBar) 

 

  

 

-- Physics 

 

  

 

local physics = require('physics') 

 

physics.start() 

 

physics.setGravity(0, 0) 

 

  

 

-- Graphics 

 

  

 

-- [Background] 

 

  

 

local bg = display.newImage('bg.png') 

 

  

 

-- [Title View] 

 

  

 

local titleBg 

 

local playBtn 

 

local creditsBtn 

 

local titleView 

 

  

 

-- [Credits] 

 

  

 

local creditsView 

 

  

 

-- [Player] 

 

  

 

local player 

 

  

 

-- [Bars Table] 

 

  

 

local bars = {} 

 

  

 

-- [Holes Table] 

 

  

 

local holes = {} 

 

  

 

-- [Goal] 

 

  

 

local goal 

 

  

 

-- Sounds 

 

  

 

local bell = audio.loadSound('bell.caf') 

 

local buzz = audio.loadSound('buzz.caf') 

 

  

 

-- Functions 

 

  

 

local Main = {} 

 

local startButtonListeners = {} 

 

local showCredits = {} 

 

local hideCredits = {} 

 

local showGameView = {} 

 

local gameListeners = {} 

 

local movePlayer = {} 

 

local onCollision = {} 

 

local alert = {} 

 

local dragPaddle = {} 

 

  

 

-- Main Function 

 

  

 

function Main() 

 

    titleBg = display.newImage('titleBg.png') 

 

    playBtn = display.newImage('playBtn.png', display.contentCenterX - 35.5, display.contentCenterY + 10) 

 

    creditsBtn = display.newImage('creditsBtn.png', display.contentCenterX - 50.5, display.contentCenterY + 65) 

 

    titleView = display.newGroup(titleBg, playBtn, creditsBtn) 

 

      

 

    startButtonListeners('add') 

 

end 

 

function startButtonListeners(action) 

 

    if(action == 'add') then 

 

        playBtn:addEventListener('tap', showGameView) 

 

        creditsBtn:addEventListener('tap', showCredits) 

 

    else 

 

        playBtn:removeEventListener('tap', showGameView) 

 

        creditsBtn:removeEventListener('tap', showCredits) 

 

    end 

 

end 

 

  

 

function showCredits:tap(e) 

 

    playBtn.isVisible = false 

 

    creditsBtn.isVisible = false 

 

    creditsView = display.newImage('credits.png', 0, display.contentHeight+40) 

 

    transition.to(creditsView, {time = 300, y = display.contentHeight-20, onComplete = function() creditsView:addEventListener('tap', hideCredits) end}) 

 

end 

 

  

 

function hideCredits:tap(e) 

 

    playBtn.isVisible = true 

 

    creditsBtn.isVisible = true 

 

    transition.to(creditsView, {time = 300, y = display.contentHeight+creditsView.height, onComplete = function() creditsView:removeEventListener('tap', hideCredits) display.remove(creditsView) creditsView = nil end}) 

 

end 

 

  

 

function showGameView:tap(e) 

 

    transition.to(titleView, {time = 300, x = -titleView.height, onComplete = function() startButtonListeners('rmv') display.remove(titleView) titleView = nil end}) 

 

      

 

    -- [Add GFX] 

 

      

 

    -- Goal 

 

      

 

    goal = display.newImage('goal.png') 

 

    goal.x = 439 

 

    goal.y = 31 

 

    goal.name = 'g' 

 

      

 

    -- Walls 

 

      

 

    local left = display.newLine(-1, 0, -1, display.contentHeight) 

 

    local right = display.newLine(display.contentWidth+1, 0, display.contentWidth+1, display.contentHeight) 

 

    local top = display.newLine(0, -3, display.contentWidth, -3) 

 

    local bottom = display.newLine(0, display.contentHeight, display.contentWidth, display.contentHeight) 

 

      

 

    -- Bars 

 

      

 

    local b1 = display.newImage('bar.png', 92, 67) 

 

    local b2 = display.newImage('bar.png', 192, -2) 

 

    local b3 = display.newImage('bar.png', 287, 67) 

 

    local b4 = display.newImage('bar.png', 387, -2) 

 

      

 

    -- Holes 

 

      

 

    local h1 = display.newImage('hole.png', 62, 76) 

 

    local h2 = display.newImage('hole.png', 124, 284) 

 

    local h3 = display.newImage('hole.png', 223, 224) 

 

    local h4 = display.newImage('hole.png', 356, 114) 

 

    local h5 = display.newImage('hole.png', 380, 256) 

 

      

 

    h1.name = 'h' 

 

    h2.name = 'h' 

 

    h3.name = 'h' 

 

    h4.name = 'h' 

 

    h5.name = 'h' 

 

      

 

    -- Player 

 

      

 

    player = display.newImage('player.png') 

 

    player.x = 49 

 

    player.y = 288 

 

    player:setReferencePoint(display.CenterReferencePoint) 

 

      

 

    -- Add Physics to GFX 

 

      

 

    physics.addBody(left, 'static') 

 

    physics.addBody(right, 'static') 

 

    physics.addBody(top, 'static') 

 

    physics.addBody(bottom, 'static') 

 

      

 

    physics.addBody(b1, 'static') 

 

    physics.addBody(b2, 'static') 

 

    physics.addBody(b3, 'static') 

 

    physics.addBody(b4, 'static') 

 

      

 

    physics.addBody(h1, 'static', {radius = 15}) 

 

    physics.addBody(h2, 'static', {radius = 15}) 

 

    physics.addBody(h3, 'static', {radius = 15}) 

 

    physics.addBody(h4, 'static', {radius = 15}) 

 

    physics.addBody(h5, 'static', {radius = 15}) 

 

      

 

    physics.addBody(player, {radius = 14}) 

 

    physics.addBody(goal, 'static', {radius = 15}) 

 

      

 

    -- Set Holes as Sensors 

 

      

 

    h1.isSensor = true 

 

    h2.isSensor = true 

 

    h3.isSensor = true 

 

    h4.isSensor = true 

 

    h5.isSensor = true 

 

      

 

    gameListeners('add') 

 

end 




 

步驟三十二: 遊戲監聽器
下列代碼的作用是為應用程式添加重力加速及物理碰撞監聽器。代碼還能通過遞交參數來移除這些效果。

 
 

function gameListeners(action) 

 

    if(action == 'add') then 

 

        Runtime:addEventListener('accelerometer', movePlayer) 

 

        player:addEventListener('collision', onCollision) 

 

        player:addEventListener('touch', dragPaddle) 

 

    else 

 

        Runtime:removeEventListener('accelerometer', movePlayer) 

 

        player:removeEventListener('collision', onCollision) 

 

        player:removeEventListener('touch', dragPaddle) 

 

    end 

 

  • end 
  •  

    步驟三十三: 移動小球
    以下函數用來捕捉物理加速值,並根據結果給小球的X及Y屬性賦值。

    function movePlayer:accelerometer(e) 

     

        player.x = player.x + (e.yGravity*-15) 

     

        player.y = player.y + (e.xGravity*-15) 

     

    end 

    步驟三十四: 物理碰撞
    當小球與其它物件發生碰撞時,其名稱會與觸碰物件相比照。根據物件類型的不同(陷阱小洞及目的地),遊戲會給出不同的提示資訊。

     
     

    function onCollision(e) 

     

        if(e.other.name == 'h') then 

     

            alert() 

     

        elseif(e.other.name == 'g') then 

     

            alert('win') 

     

        end 

     

    end 

       

     

    步驟三十五: 提示資訊
    提示資訊被觸發時,遊戲中的所有監聽器都會被移除,並在播放音效的同時顯示正確的文本內容。

     

    function alert(action) 

     

        local alert 

     

          

     

        gameListeners('rmv') 

     

          

     

        if(action == 'win') then 

     

            alert = display.newImage('complete.png') 

     

            alert.x = display.contentCenterX 

     

            alert.y = display.contentCenterY 

     

            transition.from(alert, {time = 300, xScale = 0.3, yScale = 0.3}) 

     

            audio.play(bell) 

     

        else 

     

            alert = display.newImage('gameOver.png') 

     

            alert.x = display.contentCenterX 

     

            alert.y = display.contentCenterY 

     

            transition.from(alert, {time = 300, xScale = 0.3, yScale = 0.3}) 

     

            audio.play(buzz) 

     

        end 

     

    end 

     
    步驟三十六: 模擬移動
    這一步純屬建議,大家可以將下列代表添加進來,藉以在類比環境下拖動小球,觀察移動方式是否與預期相符。

     

    function dragPaddle(e) 

     

        if(e.phase == 'began') then 

     

            lastY = e.y - player.y 

     

            lastX = e.x - player.x 

     

        elseif(e.phase == 'moved') then 

     

            player.y = e.y - lastY 

     

            player.x = e.x- lastX 

     

        end 

     

    end 

     
     步驟三十七: 調用Main函數
    為了在應用啟動時進行初始化,我們需要調用Main函數。上述代碼編寫完成之後,我們只需編輯以下內容即可實現初始化需求:

     
    Main() 步驟三十八: 載入介面
    0939121  

     
    當我們啟動指南針應用時,iOS系統會逐項載入基本資料,這時Default.png檔將作為背景圖樣顯示在主畫面當中。將這張圖片保存到我們的專案資源資料夾中,這樣它就會被自動添加到Corona的編譯器中。

     
    步驟三十九: 圖示

    0939120  
     
    現在大家的做圖功力就該派上用場了,快為自己的應用打造一款美觀又令人印象深刻的圖示吧。在非視網膜屏的iPhone設備上,圖示檔的尺寸應為57x57圖元,而視網膜屏則需要114x114圖元,另外我們還需要為iTunes軟體商店打造一個512x512的大版圖形。我建議大家先以512x512圖元為基準設計,然後再縮小成其它兩種尺寸。

     
    大家沒必要在圖示製作方面過分投入精力,製作圓角或者添加半透明特效完全是種花蛇添足——因為iTunes與iPhone會自動為你實現這些效果。

     
    步驟四十: 在模擬環境下進行測試
    0939123  

     
    是時候進行最終測試了。打開Corona模擬器,選擇我們的專案資料夾並點擊「打開」。如果一切都依照預期效果順利運行,那麼我們就可以著手做最後一項工作了。

     
    步驟四十一: 創建
    0939124  

     
    在Corona模擬器中,點選檔選項下的創建項並選擇目標設備平臺。在對話方塊中輸入專案資料並點擊創建按鈕。等上幾秒,我們的應用作品就大功告成啦!接下來大家可以在設備上進行實機測試,或者直接將應用發佈到軟體商店中。

    總結
    後期測試總是越多越好,當我們對自己的應用作品詳加打磨後,發行使用者版吧——這也許會成為輝煌成功的第一步!

    希望這篇指南文章能夠説明大家在移動開發的道路上越走越好,感謝朋友們的支援!


    原文連結:

    HTTP://mobile.tutsplus.com/tutorials/corona/corona-sdk-create-a-teeter-like-game-physics-and-interaction/
    創作者介紹
    創作者 shadow 的頭像
    shadow

    資訊園

    shadow 發表在 痞客邦 留言(0) 人氣()