레이블이 corona인 게시물을 표시합니다. 모든 게시물 표시
레이블이 corona인 게시물을 표시합니다. 모든 게시물 표시

2015년 12월 29일 화요일

Full game code using corona sdk, Million Tile Engine - (4) final


This is full source for corona SDK.

You can meet the code that is including the code of AdMob, level selector, sound, and so on.

Source Code
https://drive.google.com/file/d/0B9vAKDzHthQIaWZUX1RkMFlQQnc/view?usp=sharing

Google Play Store
https://play.google.com/store/apps/details?id=don.arts.game.hitnrun

The license of this code that I made is  Public Domain CC0 (https://creativecommons.org/about/cc0), but sounds and some images have other license.
If you want to use, you will need to check the license.

[Images]
www.gameart2d.com
->Creative Common Zero (CC0) a.k.a Public Domain license - See more at: http://www.gameart2d.com/license.html#sthash.QWaLRQAq.dpuf
http://adamatomic.com/bomberplanet/assets.html
->IT IS PUBLIC DOMAIN NOW HAVE FUN
http://www.geocities.jp/kurororo4/looseleaf/
http://opengameart.org/content/explosions-0
->http://creativecommons.org/licenses/by/3.0/
http://opengameart.org/content/simple-duotone-tileset
->http://creativecommons.org/publicdomain/zero/1.0/

[Sounds]
http://soundbible.com/1068-Dentist-Drill.html ->Attribution 3.0
http://soundbible.com/1032-Zombie-Rising.html ->Attribution 3.0
http://soundbible.com/119-Button-Push-Sound.html ->Attribution 3.0
http://soundbible.com/1251-Beep.html ->Attribution 3.0
http://soundbible.com/1133-Beep-Ping.html ->Attribution 3.0
http://soundbible.com/1586-Big-Door-Closed.html ->Attribution 3.0
http://freesound.org/people/Wagna/sounds/325805/ ->This work is licensed under the Creative Commons 0 License.
http://freesound.org/people/DuffyBro/sounds/319107/ ->This work is licensed under the Creative Commons 0 License.





This game is consist of 45 levels and it is made of TILED(http://www.mapeditor.org/).
Tile Layer 1 is backgroud layer, tile Layer 2 is Block layer, tile layer 3 is item layer.
If you want to modify level, you have to know about it.

Thanks






corona sdk에서 취소키 처리


corona sdk에서 취소키를 누르면 곧장 런처로 빠져나와서 여간 사용이 불편합니다.
아래 링크를 참고해서 back 키 핸들러를 구현을 하면 됩니다.

https://docs.coronalabs.com/api/event/key/keyName.html


Example

-- Called when a key event has been received
local function onKeyEvent( event )
    -- Print which key was pressed down/up
    local message = "Key '" .. event.keyName .. "' was pressed " .. event.phase
    print( message )

    -- If the "back" key was pressed on Android or Windows Phone, prevent it from backing out of the app
    if ( event.keyName == "back" ) then
        local platformName = system.getInfo( "platformName" )
        if ( platformName == "Android" ) or ( platformName == "WinPhone" ) then
            return true
        end
    end

    -- IMPORTANT! Return false to indicate that this app is NOT overriding the received key
    -- This lets the operating system execute its default handling of the key
    return false
end

-- Add the key event listener
Runtime:addEventListener( "key", onKeyEvent )

여기서 주의해야 할점은  return 값입니다. 처리를 했다면 return true를 넘겨줘서 시스템의 취소키 처리를 안하도록 해야합니다.
아래처럼 구현하였습니다. isPause는 cbBtnPause가 지속적으로 호출되는것을 막기위해서 사용되었습니다.



local function cbBtnPause( event )
 playSound("click")
 removeAllListener()
 isPause = true
 loadShadowPopup()
 pauseGroup = display.newGroup()
 panelPause = display.newImage(pauseGroup,"resource/panelpause.png",centerX,centerY)
 btnResume = display.newImage(pauseGroup,"resource/btnresume.png",centerX,centerY-100+10)
 btnResume:addEventListener("touch", resumeProcess)

 btnRestart = display.newImage(pauseGroup,"resource/btnrestart.png",centerX,centerY+10)
    btnRestart.destination = "autoloadlevelplay"
 btnRestart:addEventListener("touch", buttonHit) 

 btnExit = display.newImage(pauseGroup,"resource/btnexit.png",centerX,centerY+100+10)
    btnExit.destination = "menu"
 btnExit:addEventListener("touch", buttonHit) 

 sceneGroup:insert(pauseGroup)
end


local function onKeyEvent( event )
 local phase = event.phase
 local keyName = event.keyName

 if( isPause == true ) then
  return true
 end
 if( keyName == "back") then
  cbBtnPause()
  return true
 end
 return false
end


 Runtime:addEventListener( "key", onKeyEvent )

corona sdk 런처에서 표시되는 아이콘 변경하기(android)

코로나 sdk에서 표시되는 아이콘을 변경하기 위해서는 아래 링크를 참고하면 됩니다.

https://docs.coronalabs.com/guide/distribution/buildSettings/index.html#TOC


Android

For Android, icon files will be copied into the .apk during the build, if they are available in your project folder. These icons should adhere to the standard Android icon names and sizes listed below. For detailed information on creating icons, please refer to the Android Icon Design Guidelines.
FileSize (w×h)
Icon-xxxhdpi.png192 × 192
Icon-xxhdpi.png144 × 144
Icon-xhdpi.png96 × 96
Icon-hdpi.png72 × 72
Icon-mdpi.png48 × 48
Icon-ldpi.png36 × 36

6개의 이름이 다른 파일이 서로 다른 크기의 icon이 있어야 하며 경로는 프로젝트 바로 아래 존재하여야 합니다.

한개 넣고 빌드해봤는데 안되네요. 설마 두개 넣고는 되겠지 했는데 역시나 안됩니다.
모두 있어야합니다.


2015년 12월 20일 일요일

corona sdk, Million Tile Engine 로 만드는 게임(3)


이제 게임의 대문을 만들어 보겠습니다.
첫페이지에 게임제목과 Play버튼을 넣고 레벨 선택하고 게임을 할 수 있도록 하면 됩니다.
그럴려면 장면 전환이 많이 일어나는데 corona sdk에서는 story board를 이용하여 제작 할 수 있습니다. 하지만 예전에는 storyboard가 기본으로 corona sdk에 포함되어 있었지만 현재는 빠져있습니다. 아래 링크에서 해당 내용이 나옵니다.
https://forums.coronalabs.com/topic/59565-20152731-module-storyboard-not-found-vs-latest-stable-cfstreamfault/
그리고 storyboard는 open souce로 여전히 사용할 수 있습니다.
아래 링크에서 받으면 됩니다.
https://github.com/coronalabs/framework-storyboard-legacy

구성 되는 화면은 아래와 같습니다.
menu.lua에서 만들어집니다.

Play와 credits 버튼을 만들었습니다. Play버튼을 누르면 아래와 같이 level select 화면으로 넘어갑니다. 아래화면은 levelselect1.lua, levelselect2.lua, levelselect3.lua 에서 이루어 집니다.



위에서는 테스트한다고 많이 나와있습니다. 적당한 level을 선택하면 지금까지 구현한 play화면으로 들어갑니다. 이것은 levelplay.lua에서 이루어 지는데 levelselect?.lua에서 levelplay.lua가 곧장 호출되는 방식이 아니라 autoloadlevelplay.lua에 의해 loading화면이 보여집니다. 안그러면 속도가 떨어져서 levelselcet 화면이 잠시동안 반응없는 상태가 됩니다.


아래는 credits버튼을 눌렀을때 입니다. credits.lua에서 구현됩니다.

 아래는 pause버튼을 눌렀을때 입니다. 이건 levelplay.lua에서 메뉴를 띄우게 됩니다.


storyboard는 기본 템플릿이 있습니다. 아래는 menu.lua입니다. buttonHit를 제외한 함수들은 모두 존재해야 합니다. 그리고 createScene에는 화면을 구성하는것들을 넣어주고 enterScene에는 timer나 callback함수들을 넣어주며, 마지막으로 중요한것은 모든 이미지는 scene내에서는 self.view의 그룹으로 되어있어야 scene가 전환될때 모두 삭제됩니다.
scene전환은 storyboard.gotoScene()함수를 이용하며 뒤쪽 전환 효과도 마음대로 선택 할 수있습니다.
storyboard를 이용하면 소스 관리도 효율적으로 할 수 있으며 화면 전환도 편하게 할 수있습니다.


local storyboard = require( "storyboard.storyboard" )
local scene = storyboard.newScene()

-- local forward references should go here --

local function buttonHit(event)
 storyboard.gotoScene (  event.target.destination, {effect = "slideDown"} )
 return true
end

-- Called when the scene's view does not exist:
function scene:createScene( event )
 local group = self.view
 print("menu createScene")
 -- CREATE display objects and add them to 'group' here.
 -- Example use-case: Restore 'group' from previously saved state.

 bg = display.newImage('resource/titlebg.png', centerX, centerY)
 titleBg = display.newImage('resource/titletext.png', centerX, centerY-100)
 playBtn = display.newImage('resource/btnplay.png', centerX, centerY +100)
 creditsBtn = display.newImage('resource/btncredits.png', centerX, centerY +240)

 playBtn.destination = "levelselect" .. tostring(levelLastPlay)
 creditsBtn.destination = "credits"
 
 playBtn:addEventListener('tap', buttonHit)
 creditsBtn:addEventListener('tap', buttonHit)
 
 group:insert(bg)
 group:insert(titleBg)
 group:insert(playBtn)
 group:insert(creditsBtn)

end


-- Called immediately after scene has moved onscreen:
function scene:enterScene( event )
 local group = self.view

 -- INSERT code here (e.g. start timers, load audio, start listeners, etc.)

end


-- Called when scene is about to move offscreen:
function scene:exitScene( event )
 local group = self.view

 -- INSERT code here (e.g. stop timers, remove listeners, unload sounds, etc.)
 -- Remove listeners attached to the Runtime, timers, transitions, audio tracks

end


-- Called prior to the removal of scene's "view" (display group)
function scene:destroyScene( event )
 local group = self.view

 -- INSERT code here (e.g. remove listeners, widgets, save state, etc.)
 -- Remove listeners attached to the Runtime, timers, transitions, audio tracks

end



---------------------------------------------------------------------------------
-- END OF YOUR IMPLEMENTATION
---------------------------------------------------------------------------------

-- "createScene" event is dispatched if scene's view does not exist
scene:addEventListener( "createScene", scene )

-- "enterScene" event is dispatched whenever scene transition has finished
scene:addEventListener( "enterScene", scene )

-- "exitScene" event is dispatched before next scene's transition begins
scene:addEventListener( "exitScene", scene )

-- "destroyScene" event is dispatched before view is unloaded, which can be
-- automatically unloaded in low memory situations, or explicitly via a call to
-- storyboard.purgeScene() or storyboard.removeScene().
scene:addEventListener( "destroyScene", scene )


---------------------------------------------------------------------------------

return scene

전체 소스는 아래 링크롤 통해서 확인하면 됩니다.

https://drive.google.com/file/d/0B9vAKDzHthQIbjk0dFB4T1F4cFE/view?usp=sharing

2015년 12월 12일 토요일

corona sdk, Million Tile Engine 로 만드는 게임(2)


지난번에 이어 주인공을 생성하고 이동하도록 하겠습니다.

일단 dpad를 만들고 move 터치 이벤트 리스너를 등록합니다.

Dpad:addEventListener("touch", move)

그리고 move 함수는 누른 위치에 따라 player.thismovement 변수에 각도값이 들어갑니다. 여기에서는 0,90,180,270이 들어갑니다.
해당소스는 mte sample코드에서 가져왔습니다.

local function move( event )
 print("move")
 if event.phase == "began" then
  display.getCurrentStage():setFocus(event.target, event.id)
  event.target.isFocus = true
 end
 if event.phase == "began" or event.phase == "moved" then
  local dirX = event.x - event.target.x
  local dirY = event.y - event.target.y 
  local angle = math.deg(math.atan(dirY/dirX))
  if dirX < 0 then
   angle = 90 + (90 - (angle * -1))
  end
  angle = angle + 90
  angle = math.round(angle / 90) * 90
  if angle == 360 then
   angle = 0
  end
  if angle % 90 ~= 0 then
   player.thismovement = nil
  else
   player.thismovement = tostring(angle)
  end
 elseif event.phase == "ended" or event.phase == "cancelled" then
  player.thismovement = nil
  display.getCurrentStage():setFocus( event.target, nil )
  event.target.isFocus = false
 end
 return true
end

주인공의 이동은 gameloop에서 이루어집니다. 실제 이동은 mte.moveSpriteTo  함수를 이용하게 되고 이동 가능한지 확인하는 함수는 obstacle 함수를 이용합니다.


local gameLoop = function(event)

...
 eObject = player
 if not eObject.isMoving then

  --MOVE eObject CHARACTER
  if eObject.thismovement then
   local result
   local xTile, yTile
   xTile, yTile = eObject.locX + atlas[eObject.thismovement][1], eObject.locY + atlas[eObject.thismovement][2]
   result = obstacle( eObject.level, xTile, yTile )
   if( result=="stop" ) then
    eObject:pause()
   end

   if not result then
    if eObject.sequence ~= eObject.thismovement then
     eObject:setSequence( eObject.thismovement )
    end
    eObject:play()
    mte.moveSpriteTo( { sprite = eObject, locX = xTile, locY = yTile, time = eObject.moveTime, transition = easing.linear } )
   end
  else
   eObject:pause()
  end
 end
...

아래는 실행 화면입니다.



전체 소스입니다.


--------------------------------------------------------------------------------
display.setStatusBar(display.HiddenStatusBar)
--------------------------------------------------------------------------------
-- TODO
-- [o] Move enemy with AI
-- [o] Player
-- [o] DPAD
-- [o] Camera
-- [x] Move enemy with AI adv
-- [x] Collision
-- [x] dig
-- [x] score
-- [x] level
-- [x] main screen
--------------------------------------------------------------------------------
-- Localize
--------------------------------------------------------------------------------
local mte = require('mte').createMTE()        --Load and instantiate MTE.
local physics = require ("physics")
local LD = require("lib.LD_LoaderG2")
system.activate( "multitouch" )

local myLevel = {}
local math_ceil = math.ceil
local math_abs = math.abs
local math_random = math.random
local table_insert = table.insert
local map
local player = {}
local dir = 0
local drill = false
local gravity = 1
local speed = 60
local printMemUsageCount = 0
local usePhysics = false
local TileSize = 32
local enemyProperty = {}
enemyProperty["red"] = {}
enemyProperty["red"].enemyMoveTime = 300
enemyProperty["red"].chanceChangeDir = 0 -- 움직이면서 방향전환할 확률 0 전환하지 않음 

enemyProperty["green"] = {}
enemyProperty["green"].enemyMoveTime = 300
enemyProperty["green"].chanceChangeDir = 50 -- 움직이면서 방향전환할 확률 0 전환하지 않음 

local enemyTable = {}
local enemySize = 0;
local atlas = {}
atlas["0"] = {0, -1}
atlas["90"] = {1, 0}
atlas["180"] = {0, 1}
atlas["270"] = {-1, 0}
atlas["360"] = {0, -1}
math.randomseed( os.time() )

--------------------------------------------------------------------------------
-- Functions
--------------------------------------------------------------------------------
local distanceBetween = function(x1, y1, x2, y2) return (((x2 - x1) ^ 2) + ((y2 - y1) ^ 2)) ^ 0.5 end
local getPointsAlongLine = function(x1, y1, x2, y2, d) local points = {} local diffX = x2 - x1 local diffY = y2 - y1 local distBetween = math_ceil(distanceBetween(x1, y1, x2, y2) / d) local x, y = x1, y1 local addX, addY = diffX / distBetween, diffY / distBetween for i = 1, distBetween do table_insert(points, {x, y}) x, y = x + addX, y + addY end return points end
local clamp = function(v, l, h) return (v < l and l) or (v > h and h) or v end

--------------------------------------------------------------------------------
-- Physics
--------------------------------------------------------------------------------
if usePhysics then
 physics.start()
 physics.setGravity(0, 9.8)
 mte.enableBox2DPhysics()
end

--------------------------------------------------------------------------------
-- Load Map
--------------------------------------------------------------------------------

mte.loadMap("resource/hitnrun.tmx")            --Load a map.
myLevel = LD:loadLevel("LoadRes")

--------------------------------------------------------------------------------
-- Physics
--------------------------------------------------------------------------------
--physics.setDrawMode( "hybrid" )


--------------------------------------------------------------------------------
-- SETUP D-PAD
--------------------------------------------------------------------------------
local controlGroup = display.newGroup()
local Dpad = display.newImageRect(controlGroup, "resource/ui_dpad.png", 256, 256)
Dpad.x = 120
Dpad.y = display.viewableContentHeight - 120
Dpad:toFront()

--------------------------------------------------------------------------------
-- Engine dependency code
--------------------------------------------------------------------------------
------------------------------------
local function spawnObject(pname,px,py,passetName,physicsTrue)
------------------------------------
 local function mteSpriteSetup(object,pname,px,py)
  local setup = {
   kind = "sprite", 
   layer = mte.getSpriteLayer(1), 
   levelPosX = px, 
   levelPosY = py,
   name = pname,
   level = 1
   }
  mte.addSprite(object, setup)
 end

 local objProps = 
 {
  name  = pname, 
  x  = px,
  y  = py,
  xScale  =  1,
  yScale  =  1,
  assetName = passetName,
 }
 -- For LD
 -- createObject param nil need not remove object.
 -- That is changed.
 local object = myLevel:createObject(nil, objProps).view 
 -- For Map Engine
 mteSpriteSetup(object,pname,px,py);
 if usePhysics then
  if( physicsTrue ~= nil ) then
   if( physicsTrue == true ) then
    physics.addBody(object, "dynamic", { density=10.0, friction=0.1, bounce=0.1 })
    object.isFixedRotation = true
   end
  end
 end
 return object
end

local function removeObject(displayObject)
 -- body
 mte.removeSprite(displayObject, true)
 -- For Dust map.layer["Object Layer 1"]:remove(displayObject)
end

local function getTileInfo( screenX, screenY, layer )
 local loc = mte.convert("levelPosToLoc", screenX, screenY, layer)
 print("Tile Loc",loc.x, loc.y)
 local tile = mte.getTileObj(loc.x, loc.y, layer)
 -- For Dust 
 --map.layer["Tile Layer 1"].tileByPixels(screenX, screenY)
 return tile
end

local function engineLoop(track)
 -- For MTE
 --mte.setCamera({sprite = track, scale = 1})
 mte.update() --Required to process the camera and display the map.
 mte.debug()  --Displays onscreen position/fps/memory text.

 -- For Dust 
 --map.updateView()
end

local function getTileId(tile)
 local id = tile.index
 return id
end

local function getTileXY(tile)
 local x = tile.locX
 local y = tile.locY
 return x , y
end

local function removeTile( tileX, tileY )
 mte.updateTile({locX = tileX, locY = tileY, layer = 1, tile = 0})
 -- For Dust
 --map.layer["Tile Layer 1"].lockTileErased(tile.tileX, tile.tileY)
end

local function setCamera(track)
 mte.constrainCamera({})
 mte.setCamera({sprite = track, scale = 1})
 mte.setCameraFocus(track)
 -- For Dust map.setCameraFocus(track,true,true)
end

local function setLocalCamera()
 local mapObj = mte.setCamera({locX = 15, locY = 15, scale = 1}) 
end

--DETECT OBSTACLES ------------------------------------------------------------
local obstacle = function(level, locX, locY)
 local detect = mte.getTileProperties({level = level, locX = locX, locY = locY})
 for i = 1, #detect, 1 do
  if detect[i].properties then
   if detect[i].properties.solid and i == 2 then
    detect = "stop"
    return detect
   end
  end
 end
end
--------------------------------------------------------------------------------
-- Engine dependency code END
--------------------------------------------------------------------------------





------------------------------------
local monitorMem = function()
------------------------------------
 collectgarbage()
 print( "MemUsage: " .. collectgarbage("count") )
 local textMem = system.getInfo( "textureMemoryUsed" ) / 1000000
 print( "TexMem: " .. textMem )
end


local function move( event )
 print("move")
 if event.phase == "began" then
  display.getCurrentStage():setFocus(event.target, event.id)
  event.target.isFocus = true
 end
 if event.phase == "began" or event.phase == "moved" then
  local dirX = event.x - event.target.x
  local dirY = event.y - event.target.y 
  local angle = math.deg(math.atan(dirY/dirX))
  if dirX < 0 then
   angle = 90 + (90 - (angle * -1))
  end
  angle = angle + 90
  angle = math.round(angle / 90) * 90
  if angle == 360 then
   angle = 0
  end
  if angle % 90 ~= 0 then
   player.thismovement = nil
  else
   player.thismovement = tostring(angle)
  end
 elseif event.phase == "ended" or event.phase == "cancelled" then
  player.thismovement = nil
  display.getCurrentStage():setFocus( event.target, nil )
  event.target.isFocus = false
 end
 return true
end

------------------------------------
------------------------------------
------------------------------------
------------------------------------
local gameLoop = function(event)
------------------------------------
------------------------------------
------------------------------------
------------------------------------
 local eObject
 local inputDir
 
 for i=1, enemySize-1 do
  eObject = enemyTable[i]
  inputDir = tonumber(eObject.thismovement)%360
  if not eObject.isMoving then
   if math_random(100) < enemyProperty[eObject.enemyType].chanceChangeDir then
    if math_random(4)<=2 then
     eObject.thismovement = tostring((tonumber(eObject.thismovement)+90+(math_random(2)-1)*180)%360)
    end
   end

   --MOVE eObject CHARACTER
   if eObject.thismovement then
    local result
    local xTile, yTile
    for i=1,4 do
     xTile, yTile = eObject.locX + atlas[eObject.thismovement][1], eObject.locY + atlas[eObject.thismovement][2]
     result = obstacle( eObject.level, xTile, yTile )
     if( result ~= "stop" ) then
      break
     end
     eObject.thismovement = tostring(math_random(4)*90)
     if( inputDir%180 == tonumber(eObject.thismovement)%180 ) then
      eObject.thismovement = tostring(math_random(4)*90)
      if( inputDir%180 == tonumber(eObject.thismovement)%180 ) then
       eObject.thismovement = tostring(math_random(4)*90)
       if( inputDir%180 == tonumber(eObject.thismovement)%180 ) then
        eObject.thismovement = tostring(math_random(4)*90)
       end
      end
     end
    end
    if not result then
     if eObject.sequence ~= eObject.thismovement then
      eObject:setSequence( eObject.thismovement )
     end
     eObject:play()
     mte.moveSpriteTo( { sprite = eObject, locX = xTile, locY = yTile, time = enemyProperty[eObject.enemyType].enemyMoveTime, transition = easing.linear } )
    end
   else
    eObject:pause()
   end
  end
 end

 eObject = player
 if not eObject.isMoving then

  --MOVE eObject CHARACTER
  if eObject.thismovement then
   local result
   local xTile, yTile
   xTile, yTile = eObject.locX + atlas[eObject.thismovement][1], eObject.locY + atlas[eObject.thismovement][2]
   result = obstacle( eObject.level, xTile, yTile )
   if( result=="stop" ) then
    eObject:pause()
   end

   if not result then
    if eObject.sequence ~= eObject.thismovement then
     eObject:setSequence( eObject.thismovement )
    end
    eObject:play()
    mte.moveSpriteTo( { sprite = eObject, locX = xTile, locY = yTile, time = eObject.moveTime, transition = easing.linear } )
   end
  else
   eObject:pause()
  end
 end

 engineLoop(player)

 printMemUsageCount = printMemUsageCount + 1;
 if( printMemUsageCount > 100 ) then
  printMemUsageCount = 0
  --monitorMem()
 end
end


local function spawnEnemyNPlayerObject()
 enemyTable = {}
 enemySize = 1;
 local objects = mte.getObject({})
 for key,value in pairs(objects) do
  --print(value.name, value.type, value.x, value.y, value.properties)
  if( value.type=="red" )then
   local enemy = spawnObject("red",value.x+TileSize/2,value.y+TileSize/2,"enemy_32_1_red.png")
   enemy:setSequence("move")
   enemy:play()
   enemy.enemyType = "red"
   enemy.thismovement = tostring(math_random(4)*90)
   enemyTable[enemySize] = enemy;
   enemySize=enemySize+1
  end
  if( value.type=="green" )then
   local enemy = spawnObject("green",value.x+TileSize/2,value.y+TileSize/2,"enemy_32_1.png")
   enemy:setSequence("move")
   enemy:play()
   enemy.enemyType = "green"
   enemy.thismovement = tostring(math_random(4)*90)
   enemyTable[enemySize] = enemy;
   enemySize=enemySize+1
  end
  if( value.type=="player" )then
   player = spawnObject("player",value.x+TileSize/2,value.y+TileSize/2,"player32_1.png")
   player.moveTime = 300
   player:setSequence("180")
   player:play()
  end
 end
end


------------------------------------
local function spawnPlayerObject()
------------------------------------

end
--------------------------------------------------------------------------------

spawnEnemyNPlayerObject()

setCamera(player)

Runtime:addEventListener("enterFrame", gameLoop)

Dpad:addEventListener("touch", move)


소스 링크
https://drive.google.com/file/d/0B9vAKDzHthQIeFlpMmt4aDhZOWc/view?usp=sharing

2015년 12월 5일 토요일

tile의 Extrude


TexturePacker Extrude 기능은 타일사용시 유용한 기능입니다.
corona sdk에서 타일을 이용할때 꼭 필요한 기능입니다.
만약 사용하지 않는다면 움직임시 약간의 잔상 문제가 발생합니다.

MTE 0v990 Tilesets, Map Structure, Getting Started word.pdf 문서에 나오는 내용입니다.

Creating Tilesets
1. Find or create the images you will use for your tiles.

You can separate pre-existing tilesets into individual images using programs like Gimp.
A. Load a tileset into Gimp.
B. Click and drag from the vertical ruler to place guides between each column of tiles.
C. Click and drag from the horizontal ruler to place guides between each row of tiles.
D. Go to Filters -> Web -> Slice…
E. Choose the desired output folder, image type, and name prefix. Do not add spacing.
F. Click OK.

2. Open Texture Packer or your preferred alternative and add your tile images.
3. Under layout, disable Auto Alias and Trim.
4. Change Border Padding and Shape Padding to 0.
5. Change Extrude to 1.
6. Export your tileset.
7. Open Tiled and load a map or create a map; set the tile size to the original size of the
tiles before extrusion. A 32x32 tile with an extrusion of 1 is 34x34 pixels, however the
tile size in Tiled should be set to 32x32.
8. Go to Map -> New Tileset… and browse to your new tileset.
9. Set Tile Width and Tile Height to the original size of your tiles before extrusion.
10. Set Margin to 1 and Spacing to 2.
11. Set Drawing Offset X and Y to 0.
You are now free to create your map. The Tileset window in Tiled will display the
extruded tiles correctly, and Corona will display the tiles with minimal edge artifacts even
when scaling is used. Tilesets without any extrusion will work (Margin and Spacing
should be set to 0), but they will often show flickering edge boundaries in Corona,
particularly if MTE's blockScale is set to anything other than their native size.
The latest daily builds of Corona SDK add nearest-neighbor OpenGL rendering,
which may alleviate the need for extruded tilesets if a retro 16-bit graphics style is
desired.

해당기능이 어떤 기능인지 정확히 알아야 합니다.
일단 texturepacker 를 실행시켜 차이를 알아보도록 하겠습니다.



Extrude 를 이용하면 tile 사이에 마지막 이미지 한줄이 복사가 됩니다.
이렇게 사용하기 위해서는 32*32 낱개의 이미지가 필요하게 됩니다. 이미 존재하는 32*32 타일 이미지를 이용하려면 분해를 해서 일일이 넣어줘야 하는데 여간 귀찮은 일이 아닙니다.

그래서 python을 이용해서 만들어 보았습니다.


python 2.7을 설치합니다.
잘안되면 아래와 같이 설치합니다.

os가 64bit를 사용하더라도 32bit를 설치하도록 합니다.
버전은 2.7.3, 높은 버전을 설치하면 안됩니다.


https://www.python.org/download/releases/2.7.3/
http://www.pythonware.com/products/pil/#pil117

python으로 extrude 1이 되도록 스크립트를 만들었습니다.
tiled에서 로딩할때는 10. Set Margin to 1 and Spacing to 2. 설정으로 로딩하면 됩니다.


import Image

savefilename = "tiles32_new.png" """ 저장할 파일 이름 """
loadfilename = "tiles32_raw.png" """ 로딩할 파일 이름 """
tilesize = 32 """ 타일크기 """


image = Image.open(loadfilename)
win = ImageTk.Tkinter.Tk()

xtilecount = image.size[0]/tilesize
ytilecount = image.size[1]/tilesize

print "tilecount:",xtilecount,ytilecount
newsize=( int(xtilecount*(tilesize+2)) , int(ytilecount*(tilesize+2)) ) 
print newsize
newimg=Image.new("RGBA",newsize) 
 
for j in range(0,ytilecount):
 for i in range(0,xtilecount):
  box = (i*tilesize,j*tilesize,i*tilesize+tilesize,j*tilesize+tilesize)
  print box
  cutting = image.crop(box)

  newimg.paste(cutting,(i*(tilesize+2),j*(tilesize+2)))
  newimg.paste(cutting,(i*(tilesize+2)+2,j*(tilesize+2)))
  newimg.paste(cutting,(i*(tilesize+2),j*(tilesize+2)+1))
  newimg.paste(cutting,(i*(tilesize+2),j*(tilesize+2)+2))
  newimg.paste(cutting,(i*(tilesize+2)+2,j*(tilesize+2)+2))
  newimg.paste(cutting,(i*(tilesize+2)+1,j*(tilesize+2)))
  newimg.paste(cutting,(i*(tilesize+2)+1,j*(tilesize+2)+2))
  newimg.paste(cutting,(i*(tilesize+2),j*(tilesize+2)+1))
  newimg.paste(cutting,(i*(tilesize+2)+2,j*(tilesize+2)+1))
  newimg.paste(cutting,(i*(tilesize+2)+1,j*(tilesize+2)+1))

newimg.save(savefilename,"PNG")


처리 하기전 tile


처리 후 tile


타일 출처
http://adamatomic.com/bomberplanet/assets.html