반응형
블로그 이미지
개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
솔웅

최근에 받은 트랙백

글 보관함


Posted on . Written by



Sticky Projectiles


끈적한 포탄들을 코로나로 구현하는 방법은 아주 간단합니다. 이 장에서는 아래와 같은 내용들을 다룰 겁니다.



1. 포탄이 벽에 붙을 수 있는 충분한 속도가 됐는지 여부를 체크하기 위한 directional velocity 를 어떻게 detect 하는지에 대한 방법
2. weld joint 를 사용해서 포탄을 다른 객체에 붙이는 방법


포탄을 만들고 launching하는 것은 큰 주제 입니다. 그래서 그에 대해서는 깊게 들어가지 않겠습니다. 이 게임을 만들 때 생각해야 할 것은 위 두 경우를 다룰 collision handler 함수 입니다.


우리가 할 첫번째 일은 포탄의 방향과 속도를 감지하는 것입니다. 우선 피타고라스 이론(Pythagorean theorem)을 사용해서 속도를 계산해야 합니다. 그리고 나서 그 값으로 다음의 둘 중 하나를 구현할 수 있습니다.  만약 속도가 충분하다면 포탄과 벽에 weld joint 를 생성할 겁니다. 속도가 충분하지 않으면 아무 joint 도 만들지 않을 겁니다. 그러면 포탄은 땅에 떨어지겠죠.


Detect Directional Velocity


우선 포탄의 vx vy linear velocities 를 가져옵니다. 그리고나서 그 값을 공식에 대입시키죠. 그러면 우리가 원하는 속도를 얻을 수 있습니다.


local vx,vy = self:getLinearVelocity()
local dirVel = math.sqrt( (vx*vx)+(vy*vy) )


Make Joint — or Don’t!

포탄의 속도에 따라 위에서 언급한 둘 중 한가지를 적용하게 될 겁니다. 속도가 여러분이 원하는 만큼 충분하다면 포탄이 다른곳에 붙을 수 있도록 만듭니다.


if ( dirVel > 330 ) then
   self:setLinearVelocity( 0,0 )
   timer.performWithDelay( 10, resolveColl, 1 )
end


Box2D 에서는 어떤 action들은 같은 time step 에 한꺼번에 실행되지 않는 것들이 있습니다. 왜냐하면 Box2D 엔진이 내부적으로 계산을 하고 있는 상황이기 때문이죠. 우리는 10 밀리세컨드의 timer 가 작동된 후에 joint 를 생성할 겁니다.


function resolveColl()
   local weldJoint = physics.newJoint( "weld", self, event.other, self.x, self.y )
end


여기까지 하시면 sticky 포탄 시나리오의 아주 기본적인 사항들은 완료 된 겁니다. 예제에 있는 여러 setting 들을 바꿔가면서 이것저것 많이 시도해 보세요.

StickyProjectiles.zip


참고로 전체 소스는 아래와 같습니다.


local physics = require("physics") ; physics.start() ; physics.setGravity( 0.0,9.8 ) ; physics.setDrawMode( "normal" )
physics.setPositionIterations( 16 ) ; physics.setVelocityIterations( 6 )
display.setStatusBar( display.HiddenStatusBar )

--set up some references and other variables
local ox, oy = math.abs(display.screenOriginX), math.abs(display.screenOriginY)
local cw, ch = display.contentWidth, display.contentHeight
local stage = display.getCurrentStage()

--set up terrain and background
local back = display.newImageRect( "sky.jpg", 1024, 768 ) ; back.x = cw/2 ; back.y = ch/2
local wallL = display.newRect( -ox, -oy, 40, ch+oy+oy )
physics.addBody(wallL, "static", { bounce=0.6, friction=1.0 } )
local wallR = display.newRect( cw-40+ox, -oy, 40, ch+oy+oy )
physics.addBody(wallR, "static", { bounce=0.6, friction=1.0 } )
local wallB = display.newRect( -ox, ch-40+oy, cw+ox+ox, 40 )
physics.addBody(wallB, "static", { bounce=0.6, friction=1.0 } )
local wallT = display.newRect( -ox, -oy, cw+ox+ox, 40 )
physics.addBody(wallT, "static", { bounce=0.6, friction=1.0 } )
local text = display.newText( "Tap screen to launch projectiles", 0, 0, "Times", 44 ) ; text:setTextColor(0,0,0,160) ; text.y = 140 ; text.x = cw/2

--set up boolean for projectile firing
local projFiring = false

--set up projectile sheet
local proj
local options = { width=40, height=40, numFrames=2, sheetContentWidth=80, sheetContentHeight=40 }
local projSheet = graphics.newImageSheet( "projspike.png", options )
local seq = { name = "main", frames = {1,2} }

--function to create new projectiles
local function newProj()

    proj = display.newSprite( projSheet, seq ) ; proj.x = 150 ; proj.y = 600
    physics.addBody( proj, "dynamic", { density=15.0, friction=0.8, bounce=0.3, radius=16 } )
    proj.gravityScale = 0
    projFiring = false
    proj.isBodyActive = false
end

--collision handler
local function projCollide( self,event )

    if ( event.phase == "began" ) then

        --get world coordinates of projectile for joint reference   
        self:removeEventListener( "collision", self ) ; self.collision = nil
       
        --delay function to resolve collision
        local function resolveColl( timerRef )
            if ( timerRef.source.action == "makeJoint" ) then
                local weldJoint = physics.newJoint( "weld", self, event.other, self.x, self.y )
            end
            newProj()
        end

        --check if velocity of projectile is sufficient to "stick"
        local vx,vy = self:getLinearVelocity()
        local dirVel = math.sqrt( (vx*vx)+(vy*vy) )

        if ( dirVel > 330 ) then  --if sufficient, stop velocity and trigger joint creation
            self:setLinearVelocity( 0,0 )
            local t = timer.performWithDelay( 10, resolveColl, 1 ) ; t.action = "makeJoint"
        else  --if not sufficient, "break" projectile and create new
            self:setFrame(2)
            local t = timer.performWithDelay( 10, resolveColl, 1 ) ; t.action = "none"
        end

    end
end

--screen touch handler
local function touchAction(event)

    if ( event.phase == "began" and projFiring == false ) then
       
        projFiring = true
        proj.isBodyActive = true
        local px,py = event.x-proj.x, event.y-proj.y

        proj:applyLinearImpulse( px/2, py/2, proj.x, proj.y )
        proj:applyTorque( 1200 )
        proj.gravityScale = 1.0
        proj.collision = projCollide ; proj:addEventListener( "collision", proj )
    end
end
stage:addEventListener( "touch", touchAction )

newProj()



반응형

Comment