2015년 7월 10일 금요일

corona sdk 로 슈팅 게임 만들기(주인공 이동)


* 해야할 것 *

배경스크롤
주인공 이동
무기발사
적 이동
스테이지
점수 처리
시작처리
종료처리
적유닛 생성
이미지 사용할 것:http://m484games.ucoz.com/index/shoot_39_em_up_gfx/0-38
참고 소스:http://www.tandgapps.co.uk/resources/tutorial-space-shooter-in-240-lines/

주인공 이동

주인공 이미지를 로딩하고 움직이는 코드를 만들겠습니다.
기존대비 변경한 코드는 아래와 같습니다.
setup함수에서 player를 load하는데 비행기 이미지를 미리 만들어 두고 터치를 누르면 moveShip이 호출됩니다. 여기에서는 가로로만 이동되게 하였는데 중요한 부분은 transition 함수 입니다. 이동속도는 = 거리/시간 으로 여기에서는 일정한 속도로 움직이게 하기위해서 time이 속도가 일정하려면 시간이 변경되게 됩니다. 시간은=거리/상수(속도)값으로 0.2가 상수값으로 테스트로 적절한 값을 산출 합니다.
참고로 해당 내용은 참고 소스 링크의 부분과 다른 내용입니다.


local playerGroup = display.newGroup()

local ship

local function moveShip( event )
  local phase = event.phase
  print(event.x,event.y,phase)
  if( event.x > ship.x ) then
    transition.cancel( ship )
    transition.to( ship, {time = (event.x-ship.x)/0.2, x = event.x} )  -- t = d/k
  end
  if( event.x < ship.x ) then
    transition.cancel( ship )
    transition.to( ship, {time = -(event.x-ship.x)/0.2, x = event.x} )  -- t = d/k
  end
end

local function loadPlayer()
  ship = display.newImageRect("my.png", 20, 20) 
  ship.x = _W*0.5; ship.y = _H+30; ship.name = "ship"
  physics.addBody( ship, { isSensor = true } )
  Runtime:addEventListener("touch",moveShip)
  playerGroup:insert(ship) 
  transition.to(ship, {time = 600, y = _H-_H*0.1})
end

local function setup()
  loadBGImg()
  loadPlayer()
end

무기발사

휴대폰에서 조작이 쉽게하려면 총알 발사는 자동으로 이루어지는게 좋을겁니다. (드래곤 플라이트 처럼)
여기에서는 무기 발사에 대해 만들어 보겠습니다.
새로운 weapon 그룹을 만들고 bullet 이미지를 로드해서 timer를 등록하여 일정 주기마다 발사하도록 하면 됩니다 해당 내용은 loadBullet 함수로 되어 있습니다.
하지만 총알의 이동은 game loop에서 해주도록 합니다. moveWeapon 함수에서 모든 weapon들의 움직여 줍니다.



local weaponGroup = display.newGroup()

local function setup()
  loadBGImg()
  loadPlayer()
  loadBullet()
end

local function loadBullet()
  local fireTimer
  local function fireNow()
    local bullet = display.newImageRect( "bullet.png", 10, 15 )
    bullet.x = ship.x-5; bullet.y = ship.y-5; bullet.name = "laser"
    physics.addBody( bullet, { isSensor = true } )
    weaponGroup:insert( bullet )

    local bullet = display.newImageRect( "bullet.png", 10, 15 )
    bullet.x = ship.x +5; bullet.y = ship.y-5; bullet.name = "laser"
    physics.addBody( bullet, { isSensor = true } ) 
    weaponGroup:insert( bullet )
  end
  fireTimer = timer.performWithDelay(850, fireNow, 0)
end

local function moveWeapon()
 --Move our lasers up each frame!
  for i = weaponGroup.numChildren,1,-1 do
    local weapon = weaponGroup[i]
    if weapon ~= nil and weapon.y ~= nil then
      weapon:translate(0, -10);
    end
  end 
end

local function gameLoop(event)
  checkNSpawnEnemy()
  moveStarField()
  moveEnemy()
  moveWeapon()
end


충돌처리

충돌은 onCollison에서 처리하게 됩니다. object 이름에 따라서 어떻게 할건지 결정합니다. 문제는 ship 주인공이 사라지게되면 그걸 기반으로 하는 좌표들에서 문제가 발생하므로 해당 처리가 필요합니다.



local function onCollision(event)
  if event.phase == "began" then
    local obj1 = event.object1; 
    local obj2 = event.object2; 

    if obj1.name == "laser" and obj2.name == "enemy" or obj1.name == "enemy" and obj2.name == "laser" then
      display.remove( obj1 ); obj1 = nil
      display.remove( obj2 ); obj2 = nil
      --score = score + 100 --Yay points!

    elseif obj1.name == "ship" and obj2.name == "enemy" or obj2.name == "ship" and obj1.name == "enemy" then
      display.remove( obj1 ); obj1 = nil
      display.remove( obj2 ); obj2 = nil
      --gameOver()--Weve died so call gameover...

    elseif obj1.name == "enemy" and obj2.name == "blocker" or obj2.name == "enemy" and obj1.name == "blocker"then
      if obj1.name == "enemy" then 
        display.remove(obj1); obj1 = nil
      elseif obj2.name == "enemy" then 
        display.remove( obj2 ); obj2 = nil
      end
    elseif obj1.name == "laser" and obj2.name == "blocker" or obj2.name == "laser" and obj1.name == "blocker"then
      if obj1.name == "laser" then 
        display.remove(obj1); obj1 = nil
      elseif obj2.name == "laser" then 
        display.remove( obj2 ); obj2 = nil
      end
    end
  end
end

Runtime:addEventListener( "collision", onCollision )


화면을 벗어난 객체 삭제

좌표로 벗어난 객체를 삭제하는 방식이 있겠지만 예제에서는 blocker라는 객체를 만들어 두고 충돌을 이용하여 구현 하였습니다. 위쪽과 아래쪽에 두어서 위쪽은 총알을 아래쪽은 적 객체가 충돌하는지 검사하게 됩니다.


local function loadBlocker()
  local laserBlock = display.newRect(0,-80,_W,2)
  laserBlock.name = "blocker" 
  physics.addBody( laserBlock, { isSensor = true } )
  bgGroup:insert(laserBlock)
 
  local shipBlock = display.newRect(0,_H+30,_W,2)
  shipBlock.name = "blocker"
  physics.addBody( shipBlock, { isSensor = true } )
  bgGroup:insert(shipBlock) 
end


이번에 작성한 전체 소스입니다.



display.setStatusBar( display.HiddenStatusBar ) --Hide status bar from the beginning
local physics = require ("physics") --Require physics
physics.start(); physics.setGravity( 0, 0 ) --Start physics

-----------------------------------------------
--*** Set up our variables and group ***
-----------------------------------------------
local bgGroup = display.newGroup()
local enemyGroup = display.newGroup()
local weaponGroup = display.newGroup()
local playerGroup = display.newGroup()

local _W = display.contentWidth
local _H = display.contentHeight

local stars1, stars2 --Background moving stars
local spawnInt = 0 --Gameloop spawn control
local spawnIntMax = 30 --Gameloop max spawn
local spawned = 0 --Keep track of enemies
local spawnedMax = 10 --Max allowed per level
local enemySpeed = 7 --How fast the enemies are
local wave = 0
local ship
local gameIsActive = true
local fireTimer

local function loadBGImg()
  stars1 = display.newImageRect("star1_480_800.png", 480, 800)
  stars1.x = _W*0.5; stars1.y = _H*0.5
  bgGroup:insert(stars1)
  stars2 = display.newImageRect("star2_480_800.png", 480, 800)
  stars2.x = _W*0.5; stars2.y = _H*0.5-800
  bgGroup:insert(stars2)
end

local function moveShip( event )
  local phase = event.phase
  if gameIsActive == false then return end
  print(event.x,event.y,phase)
  if( event.x > ship.x ) then
    transition.cancel( ship )
    transition.to( ship, {time = (event.x-ship.x)/0.2, x = event.x} )  -- t = d/k
  end
  if( event.x < ship.x ) then
    transition.cancel( ship )
    transition.to( ship, {time = -(event.x-ship.x)/0.2, x = event.x} )  -- t = d/k
  end
end

local function loadPlayer()
  ship = display.newImageRect("my.png", 20, 20) 
  ship.x = _W*0.5; ship.y = _H+30; ship.name = "ship"
  physics.addBody( ship, { isSensor = true } )
  Runtime:addEventListener("touch",moveShip)
  playerGroup:insert(ship) 
  transition.to(ship, {time = 600, y = _H-_H*0.1})
end

local function loadBullet()
  local function fireNow()
    local bullet = display.newImageRect( "bullet.png", 10, 15 )
    bullet.x = ship.x-5; bullet.y = ship.y-5; bullet.name = "laser"
    physics.addBody( bullet, { isSensor = true } )
    weaponGroup:insert( bullet )

    local bullet = display.newImageRect( "bullet.png", 10, 15 )
    bullet.x = ship.x +5; bullet.y = ship.y-5; bullet.name = "laser"
    physics.addBody( bullet, { isSensor = true } ) 
    weaponGroup:insert( bullet )
  end
  fireTimer = timer.performWithDelay(850, fireNow, 0)
end

local function moveWeapon()
 --Move our lasers up each frame!
  for i = weaponGroup.numChildren,1,-1 do
    local weapon = weaponGroup[i]
    if weapon ~= nil and weapon.y ~= nil then
      weapon:translate(0, -10);
    end
  end 
end

local function loadBlocker()
  local laserBlock = display.newRect(0,-80,_W,2)
  laserBlock.name = "blocker" 
  physics.addBody( laserBlock, { isSensor = true } )
  bgGroup:insert(laserBlock)
 
  local shipBlock = display.newRect(0,_H+30,_W,2)
  shipBlock.name = "blocker"
  physics.addBody( shipBlock, { isSensor = true } )
  bgGroup:insert(shipBlock) 
end

local function setup()
  loadBlocker()
  loadBGImg()
  loadPlayer()
  loadBullet()
end

local function moveStarField()
  --Move the starfields.
  stars1:translate(0,2)
  stars2:translate(0,2) 
  if stars1.y >= (_H*0.5)+800 then
    stars1.y = (_H*0.5)-800
  end
  if stars2.y >= (_H*0.5)+800 then
    stars2.y = (_H*0.5)-800
  end
end

local function spawnEnemy()
  local imageInt = math.random(1,3)
  local enemy = display.newImageRect("ship_"..imageInt.."_20_20.png",20,20)

  enemy.x = math.random( 20, _W-20 ); enemy.y = -30; enemy.rotation = 90
  enemy.name = "enemy"; physics.addBody( enemy, { isSensor = true } )
  enemyGroup:insert( enemy )

  if spawned == spawnedMax then 
    wave = wave + 1 --Increase the wave.
    if wave <= 18 then --Limit max speed/spawn
      enemySpeed = enemySpeed + 1 
      spawnIntMax = math.round(spawnIntMax * 0.9)
    end
    spawned = 0 --Reset so that the next wave starts from 0
  end
  spawnInt = 0
end

local function checkNSpawnEnemy()
  --Increase the int until it spawns an enemy..
  spawnInt = spawnInt + 1
  --change spawnIntMax if you want enemies to spawn
  --faster or slower.
  if spawnInt == spawnIntMax then 
    spawnEnemy()
    spawned = spawned + 1
  end
end

local function moveEnemy()
  --Move the enemies down each frame!
  local i
  for i = enemyGroup.numChildren,1,-1 do
    local enemy = enemyGroup[i]
    if enemy ~= nil and enemy.y ~= nil then
      enemy:translate( 0, enemySpeed)
    end
  end 
end

local function gameLoop(event)
  checkNSpawnEnemy()
  moveStarField()
  moveEnemy()
  moveWeapon()
end

local function gameOver()
  gameIsActive = false
  if( fireTimer ~= nil ) then 
    timer.cancel(fireTimer) 
    fireTimer = nil
  end
end

local function onCollision(event)
  if event.phase == "began" then
    local obj1 = event.object1; 
    local obj2 = event.object2; 

    if obj1.name == "laser" and obj2.name == "enemy" or obj1.name == "enemy" and obj2.name == "laser" then
      display.remove( obj1 ); obj1 = nil
      display.remove( obj2 ); obj2 = nil
      --score = score + 100 --Yay points!

    elseif obj1.name == "ship" and obj2.name == "enemy" or obj2.name == "ship" and obj1.name == "enemy" then
      display.remove( obj1 ); obj1 = nil
      display.remove( obj2 ); obj2 = nil
      gameOver()--Weve died so call gameover...

    elseif obj1.name == "enemy" and obj2.name == "blocker" or obj2.name == "enemy" and obj1.name == "blocker"then
      if obj1.name == "enemy" then 
        display.remove(obj1); obj1 = nil
      elseif obj2.name == "enemy" then 
        display.remove( obj2 ); obj2 = nil
      end
    elseif obj1.name == "laser" and obj2.name == "blocker" or obj2.name == "laser" and obj1.name == "blocker"then
      if obj1.name == "laser" then 
        display.remove(obj1); obj1 = nil
      elseif obj2.name == "laser" then 
        display.remove( obj2 ); obj2 = nil
      end
    end
  end
end


setup()
Runtime:addEventListener ("enterFrame", gameLoop)
Runtime:addEventListener( "collision", onCollision )

댓글 없음:

댓글 쓰기