Corona中文站

强大、易学的跨平台(iOS/Android)开发框架。QQ群1:74390406(满) 群2:221929599

导航

Corona精彩samples赏析之《Samurai Fruit》

这是corona实现的一个简化的切水果游戏,实现中有许多动画表现和物理引擎的使用技巧。完整代码可在corona官方网站上找到。

===============================================================

这里主要分析下main.lua。

===============================================================

require ("physics")
local ui = require("ui")

physics.start()
--physics.setDrawMode ( "hybrid" ) -- 消除注释后可以在hybrid模式下显示
physics.setGravity( 0, 9.8 * 2)

physics.start()

-- 划线声音的音频 (当用户用他们的手指划过屏幕时会听到的声音)
local slashSounds = {slash1 = audio.loadSound("slash1.wav"), slash2 = audio.loadSound("slash2.wav"), slash3 = audio.loadSound("slash3.wav")}
local slashSoundEnabled = true -- 启动时声音是默认开启的
local minTimeBetweenSlashes = 150 -- 每次滑动声音之间最小间隔时间
local minDistanceForSlashSound = 50 -- 发生声音播放所需要用户手指一帧内滑行过的最小像素数量

-- 切碎水果声的音频
local choppedSound = {chopped1 = audio.loadSound("chopped1.wav"), chopped2 = audio.loadSound("chopped2.wav")}

-- 炸弹的音频
local preExplosion = audio.loadSound("preExplosion.wav")
local explosion = audio.loadSound("explosion.wav")

-- 添加一个碰撞过滤器,以便水果之间不互相碰撞,他们只和捕捉平台碰撞
-- categoryBits表示该物体的碰撞标志,maskBits是可与之碰撞的屏蔽位(可碰多个不同categoryBits的物体)
local fruitProp = {density = 1.0, friction = 0.3, bounce = 0.2, filter = {categoryBits = 010, maskBits = 01}}
local catchPlatformProp = {density = 1.0, friction = 0.3, bounce = 0.2, filter = {categoryBits = 01, maskBits = 010}}

-- 喷汁的过滤器,保证喷汁对象不合其他水果或捕捉平台交互
local gushProp = {density = 1.0, friction = 0.3, bounce = 0.2, filter = {categoryBits = 0100, maskBits = 01000} }

-- 游戏内 所有有效水果的容器
local avalFruit = {}

-- 划线属性(当你手指划过屏幕时,要显示出的划线效果)
local maxPoints = 5
local lineThickness = 20
local lineFadeTime = 250
local endPoints = {}

-- 完整水果的物理属性
local minVelocityY = 850
local maxVelocityY = 1100

local minVelocityX = -200
local maxVelocityX = 200

local minAngularVelocity = 100
local maxAngularVelocity = 200

-- 切碎的水果的物理属性
local minAngularVelocityChopped = 100
local maxAngularVelocityChopped = 200

-- 果汁印记属性
local splashFadeTime = 2500
local splashFadeDelayTime = 5000
local splashInitAlpha = .5
local splashSlideDistance = 50 -- 果汁印记在背景上向下流淌的距离

-- 包含所有果汁印记图片的容器
local splashImgs = {}

-- 喷汁属性
local minGushRadius = 10
local maxGushRadius = 25
local numOfGushParticles = 15
local gushFadeTime = 500
local gushFadeDelay = 500

local minGushVelocityX = -350
local maxGushVelocityX = 350
local minGushVelocityY = -350
local maxGushVelocityY = 350

-- 计时器引用
local bombTimer
local fruitTimer

-- 游戏属性
local fruitShootingInterval = 1000
local bombShootingInterval = 5000

-- 持有水果和果汁印记对象的组
local splashGroup
local fruitGroup

function main()

 display.setStatusBar( display.HiddenStatusBar )

 setUpBackground()
 setUpCatchPlatform()
 initGroups()
 initFruitAndSplash()


 Runtime:addEventListener("touch", drawSlashLine)

 startGame()
end

function startGame()
 shootObject("fruit")

 bombTimer = timer.performWithDelay(bombShootingInterval, function(event) shootObject("bomb") end, 0)
 fruitTimer = timer.performWithDelay(fruitShootingInterval, function(event) shootObject("fruit") end, 0)
end

function initGroups()
  splashGroup = display.newGroup()
  fruitGroup = display.newGroup()
end

function setUpBackground()

 local background = display.newImage("bg.png", true)
 background.x = display.contentWidth / 2
 background.y = display.contentHeight / 2

end

-- Populates avalFruit with all the fruit images and thier widths and heights
function initFruitAndSplash()

 local watermelon = {}                   --西瓜
 watermelon.whole = "watermelonWhole.png"
 watermelon.top = "watermelonTop.png"
 watermelon.bottom = "watermelonBottom.png"
 watermelon.splash = "redSplash.png"
 table.insert(avalFruit, watermelon)

 local strawberry = {}                   --草莓
 strawberry.whole = "strawberryWhole.png"
 strawberry.top = "strawberryTop.png"
 strawberry.bottom = "strawberryBottom.png"
 strawberry.splash = "redSplash.png"
 table.insert(avalFruit, strawberry)

 -- 初始化划线图片
 table.insert(splashImgs, "splash1.png")
 table.insert(splashImgs, "splash2.png")
 table.insert(splashImgs, "splash3.png")
end


function getRandomFruit()

 local fruitProp = avalFruit[math.random(1, #avalFruit)] --简短的表达式,漂亮
 local fruit = display.newImage(fruitProp.whole)
 fruit.whole = fruitProp.whole
 fruit.top = fruitProp.top
 fruit.bottom = fruitProp.bottom
 fruit.splash = fruitProp.splash

 return fruit

end

function getBomb()

 local bomb = display.newImage( "bomb.png")
 return bomb
end

function shootObject(type)

 local object = type == "fruit" and getRandomFruit() or getBomb()        -- 单目运算,老练的写法

 fruitGroup:insert(object)                                               --插入的水果group里

 object.x = display.contentWidth / 2
 object.y = display.contentHeight  + object.height * 2                   --正好藏在屏幕底部

 fruitProp.radius = object.height / 2
 physics.addBody(object, "dynamic", fruitProp)

 if(type == "fruit") then
  object:addEventListener("touch", function(event) chopFruit(object) end)
 else
  local bombTouchFunction
  bombTouchFunction = function(event) explodeBomb(object, bombTouchFunction); end
  object:addEventListener("touch", bombTouchFunction)
 end

 -- 设置线速度
 local yVelocity = getRandomValue(minVelocityY, maxVelocityY) * -1 -- 需要乘以-1,水果才会向上发射
 local xVelocity = getRandomValue(minVelocityX, maxVelocityX)
 object:setLinearVelocity(xVelocity,  yVelocity)

 -- 设置角速度(水果旋转方向的速度)
 local minAngularVelocity = getRandomValue(minAngularVelocity, maxAngularVelocity)
 local direction = (math.random() < .5) and -1 or 1                  --随机正负号
 minAngularVelocity = minAngularVelocity * direction
 object.angularVelocity = minAngularVelocity

end


function explodeBomb(bomb, listener)

 bomb:removeEventListener("touch", listener)

    -- 炸弹在爆炸时 应该不再运动
 bomb.bodyType = "kinematic"
 bomb:setLinearVelocity(0,  0)
 bomb.angularVelocity = 0

    -- 晃动场景
 local stage = display.getCurrentStage()

 local moveRightFunction
 local moveLeftFunction
 local rightTrans
 local leftTrans
 local shakeTime = 50
 local shakeRange = {min = 1, max = 25}

  moveRightFunction = function(event) rightTrans = transition.to(stage, {x = math.random(shakeRange.min,shakeRange.max), y = math.random(shakeRange.min, shakeRange.max), time = shakeTime, onComplete=moveLeftFunction}); end
 moveLeftFunction = function(event) leftTrans = transition.to(stage, {x = math.random(shakeRange.min,shakeRange.max) * -1, y = math.random(shakeRange.min,shakeRange.max) * -1, time = shakeTime, onComplete=moveRightFunction});  end

 moveRightFunction()         --开始左右晃场景

 local linesGroup = display.newGroup()

 -- 生成一系列线条来模拟一个爆炸
  local drawLine = function(event)

  local line = display.newLine(bomb.x, bomb.y, display.contentWidth * 2, display.contentHeight * 2)
  line.rotation = math.random(1,360)
  line.width = math.random(15, 25)
  linesGroup:insert(line)
 end
 local lineTimer = timer.performWithDelay(100, drawLine, 0)

 --  pre explosion之后调用的函数
 local explode = function(event)

  audio.play(explosion)
  blankOutScreen(bomb, linesGroup);       --屏幕泛白
  timer.cancel(lineTimer)
  stage.x = 0
  stage.y = 0
  transition.cancel(leftTrans)
  transition.cancel(rightTrans)

 end

 -- 先播放preExplosion声音,随后以explosion结束
 audio.play(preExplosion, {onComplete = explode})

 timer.cancel(fruitTimer)
 timer.cancel(bombTimer)

end

function blankOutScreen(bomb, linesGroup)

 local gameOver = displayGameOver()
 gameOver.alpha = 0 -- 将会在爆炸后淡入到游戏结束画面

 -- 创建一个爆炸动画
 local circle = display.newCircle( bomb.x, bomb.y, 5 )
 local circleGrowthTime = 300
 local dissolveDuration = 1000

 local dissolve = function(event) transition.to(circle, {alpha = 0, time = dissolveDuration, delay = 0, onComplete=function(event) gameOver.alpha = 1 end}); gameOver.alpha = 1  end

 circle.alpha = 0
 transition.to(circle, {time=circleGrowthTime, alpha = 1, width = display.contentWidth * 3, height = display.contentWidth * 3, onComplete = dissolve})

 -- 震动设备
 system.vibrate()

 bomb:removeSelf()
 linesGroup:removeSelf()

end

function displayGameOver()

 -- 将返回一个group,一边我们可以设置整个菜单的alpha值
 local group = display.newGroup()

 -- 用一个透明的方块来暗化背景
 local back = display.newRect( 0,0, display.contentWidth, display.contentHeight )
 back:setFillColor(0,0,0, 255 * .1)
 group:insert(back)

 local gameOver = display.newImage( "gameover.png")
 gameOver.x = display.contentWidth / 2
 gameOver.y = display.contentHeight / 2
 group:insert(gameOver)

 local replayButton = ui.newButton{
  default = "replayButton.png",
  over = "replayButton.png",
  onRelease = function(event) group:removeSelf(); startGame() end
 }
 group:insert(replayButton)

 replayButton.x = display.contentWidth / 2
 replayButton.y = gameOver.y + gameOver.height / 2 + replayButton.height / 2


 return group
end

-- 在 'min' 和 'max' 之间返回一个随机值
function getRandomValue(min, max)
 return min + math.abs(((max - min) * math.random()))
end

function playRandomSlashSound()

 audio.play(slashSounds["slash" .. math.random(1, 3)])
end

function playRandomChoppedSound()

 audio.play(choppedSound["chopped" .. math.random(1, 2)])
end

function getRandomSplash()

  return display.newImage(splashImgs[math.random(1, #splashImgs)])
end

function chopFruit(fruit)

 playRandomChoppedSound()

 createFruitPiece(fruit, "top")
 createFruitPiece(fruit, "bottom")

 createSplash(fruit)
 createGush(fruit)

 fruit:removeSelf()
end

-- 创建一个喷汁效果,使得看上去果汁从水果里飞出;这个函数写的相当精髓,天女散花
function createGush(fruit)

 local i
 for  i = 0, numOfGushParticles do
  local gush = display.newCircle( fruit.x, fruit.y, math.random(minGushRadius, maxGushRadius) )
  gush:setFillColor(255, 0, 0, 255)

  gushProp.radius = gush.width / 2
  physics.addBody(gush, "dynamic", gushProp)

  local xVelocity = math.random(minGushVelocityX, maxGushVelocityX)
  local yVelocity = math.random(minGushVelocityY, maxGushVelocityY)

  gush:setLinearVelocity(xVelocity, yVelocity)

  transition.to(gush, {time = gushFadeTime, delay = gushFadeDelay, width = 0, height = 0, alpha = 0, onComplete = function(event) gush:removeSelf() end})
 end

end

-- 精髓函数,制造果汁留在墙壁的印记,让这些印记透明流淌下去,然后消失
function createSplash(fruit)

 local splash = getRandomSplash()
 splash.x = fruit.x
 splash.y = fruit.y
 splash.rotation = math.random(-90,90)
 splash.alpha = splashInitAlpha
 splashGroup:insert(splash)

 transition.to(splash, {time = splashFadeTime, alpha = 0,  y = splash.y + splashSlideDistance, delay = splashFadeDelayTime, onComplete = function(event) splash:removeSelf() end})

end

-- 把水果一切两半
-- 用一些三角函数来计算位置,数学计算
-- 被切开的水平的头部和底部 (http://en.wikipedia.org/wiki/Rotation_matrix#Rotations_in_two_dimensions)
function createFruitPiece(fruit, section)

 local fruitVelX, fruitVelY = fruit:getLinearVelocity()

 -- 计算被切开的碎片的位置
 local half = display.newImage(fruit[section])
 half.x = fruit.x - fruit.x -- Need to have the fruit's position relative to the origin in order to use the rotation matrix
 local yOffSet = section == "top" and -half.height / 2 or half.height / 2
 half.y = fruit.y + yOffSet - fruit.y

 local newPoint = {}
 newPoint.x = half.x * math.cos(fruit.rotation * (math.pi /  180)) - half.y * math.sin(fruit.rotation * (math.pi /  180))
 newPoint.y = half.x * math.sin(fruit.rotation * (math.pi /  180)) + half.y * math.cos(fruit.rotation * (math.pi /  180))

 half.x = newPoint.x + fruit.x -- Put the fruit back in its original position after applying the rotation matrix
 half.y = newPoint.y + fruit.y
 fruitGroup:insert(half)

 -- 设置旋转角度
 half.rotation = fruit.rotation
 fruitProp.radius = half.width / 2 -- We won't use a custom shape since the chopped up fruit doesn't interact with the player
 physics.addBody(half, "dynamic", fruitProp)

 -- 设置线速度
 local velocity  = math.sqrt(math.pow(fruitVelX, 2) + math.pow(fruitVelY, 2))
 local xDirection = section == "top" and -1 or 1
 local velocityX = math.cos((fruit.rotation + 90) * (math.pi /  180)) * velocity * xDirection
 local velocityY = math.sin((fruit.rotation + 90) * (math.pi /  180)) * velocity
 half:setLinearVelocity(velocityX,  velocityY)

 -- 计算它的角速度
  local minAngularVelocity = getRandomValue(minAngularVelocityChopped, maxAngularVelocityChopped)
 local direction = (math.random() < .5) and -1 or 1
 half.angularVelocity = minAngularVelocity * direction
end

-- 在游戏的底部创建一个平台来“捕获”水果,并删除之
function setUpCatchPlatform()

 local platform = display.newRect( 0, 0, display.contentWidth * 4, 50)
 platform.x =  (display.contentWidth / 2)
 platform.y = display.contentHeight + display.contentHeight
 physics.addBody(platform, "static", catchPlatformProp)

 platform.collision = onCatchPlatformCollision
 platform:addEventListener( "collision", platform )
end

function onCatchPlatformCollision(self, event)
 -- 删除碰撞到平台的水果
 event.other:removeSelf()
end

-- 当用户用手指划过屏幕时,绘制划动线条, 由深变淡由粗变细,精髓函数
function drawSlashLine(event)

 -- 播放一个划动声音
 if(endPoints ~= nil and endPoints[1] ~= nil) then
  local distance = math.sqrt(math.pow(event.x - endPoints[1].x, 2) + math.pow(event.y - endPoints[1].y, 2))
  if(distance > minDistanceForSlashSound and slashSoundEnabled == true) then
   playRandomSlashSound();
   slashSoundEnabled = false
   timer.performWithDelay(minTimeBetweenSlashes, function(event) slashSoundEnabled = true end) --控制发声间隔
  end
 end

 -- 插入一个新的点,到数组的前面
 table.insert(endPoints, 1, {x = event.x, y = event.y, line= nil})

 -- 删除超额后的点
 if(#endPoints > maxPoints) then
  table.remove(endPoints)
 end

 for i,v in ipairs(endPoints) do
  local line = display.newLine(v.x, v.y, event.x, event.y)
  line.width = lineThickness
  transition.to(line, {time = lineFadeTime, alpha = 0, width = 0, onComplete = function(event) line:removeSelf() end})
 end

 if(event.phase == "ended") then
  while(#endPoints > 0) do
   table.remove(endPoints)
  end
 end
end


main()

<< Corona SDK未公开的私密音频APIcorona编译iOS和android文件所需的图标和资源列表 >>

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

最近发表

Powered By Z-Blog 1.8 Walle Build 100427 Copyright 2011-2015 BuildApp.Net. All Rights Reserved.