음, 사실 이 글을 보실 분들은 제가 특별히 소스 설명할 필요가 없을 텐데 쓸데없는 짓하고 있는 듯한 기분이 들지만, 포스팅을 늘리기 위해서라도 계속 합니다. :)

구동 동영상 및 전반적인 설명은 part 1.을 참고하시고,
소스 코드 다운 로드와 코드의 초기화 및 해제 설명은 part 2.를 참고하세요.

part 3.에서는 핵심 처리인 마우스 이벤트와 타이머의 핸들러를 살펴보도록 하겠습니다.

/*--------------------------------------
|   MouseMoveBG
|   백그라운드에서 마우스가 움직일 때 이벤트
---------------------------------------*/
function MouseMoveBG()
{
    var nPosX = 0, nPosY = 0;
    nPosX = event.x;
    nPosY = event.y;

       m_nPrevPosX = m_nPosX;
       m_nPrevPosY = m_nPosY;
    m_nPosX = nPosX;
    m_nPosY = nPosY;
    //m_divBG.innerHTML = "Cur X : " + nPosX + ", Cur Y : " + nPosY + "<BR>"
    //m_divBG.innerHTML = m_divBG.innerHTML + "X : " + m_nPosX + ", Y : " + m_nPosY + " / " + m_nTimeouts;

       m_nTimeouts = 0;    // 타이머 타임아웃 카운트 초기화

       // 캐릭터 보여줌
       ShowGhost();
}   // function MouseMoveBG()

초기화 코드에서 m_divBG의 MouseMove 이벤트를 MouseMoveBG()에 매핑했었습니다.
MouseMove 이벤트에서 제일 중요한 것은? 바로 커서의 현재 위치겠지요.
이벤트 핸들링은 event라는 키워드로 처리할 수 있고 event에서 얻을 수 있는 프로퍼티는 레퍼런스를 참조해야 합니다.

코드에서는 event.xevent.y로 현재 커서의 x, y 위치를 얻고 클래스 멤버 변수에 저장을 해둡니다.
사실 캐릭터 이동의 가속도도 적용하려고 커서의 이전 위치도 저장하긴 하는데 시간 관계상-귀찮아서- 사용되진 않습니다. ^^;

백그라운드에 마우스가 움직였다는 것은 어떤 상황이든 캐릭터가 보여야 하고 사용자 조작이 있었으므로 캐릭터가 사라지기까지의 시간을 초기화 해야 한다는 것을 말합니다.

캐릭터를 보여주는 코드는 메인 프로시저에 들어갈 수도 있겠지만 여기에서는 캐릭터가 보이는 조건이 MouseMove밖에 없으므로 해당 프로시저에서 처리했습니다.
만약 조건이 둘 이상이라면 별도의 멤버 변수에 캐릭터를 보여줘야 한다는 플래그를 설정하고 메인 프로시저에서 해당 플래그가 설정되어 있으면 캐릭터를 보이도록 처리하는 것이 더 효율적일 것입니다.

코드 설명하다 보니 엉성한 점이 하나둘씩 나타나는군요 흐흐...

/*--------------------------------------
|   ShowGhost
|   현재 커서 위치에 따라 적당한 고스트를 보여줌
---------------------------------------*/
function ShowGhost()
{
    //m_divBG.innerHTML = "Xpos : " + m_nPosX + " ImgX : " + m_imgGhost[0].style.left + " ImgY : " + m_imgGhost[0].style.top;
       var nPosX = 0;
       nPosX = parseInt(m_imgGhost[0].style.left);
if (m_nPosX < nPosX)
{
     if (m_imgGhost[0].style.visibility != "visible")
     {
         m_imgGhost[0].style.visibility = "visible";
         m_imgGhost[1].style.visibility = "hidden";
           }
}
else
{
     if (m_imgGhost[1].style.visibility != "visible")
     {
         m_imgGhost[0].style.visibility = "hidden";
         m_imgGhost[1].style.visibility = "visible";
           }
}
}   // function ShowGhost()

커서의 현재 위치를 기준으로 캐릭터 위치가 커서 위치보다 작으면 오른쪽을 봐야 하고, 그렇지 않으면 왼쪽을 봐야합니다.

여기서 주의할 점은 어떤 엘리먼트(객체)의 프로퍼티를 읽는 것은 빠르게 처리되지만 화면에 보여지는 상태가 변경되는 프로퍼티를 설정하거나 메소드를 호출하는 경우는 생각보다 많은 시간이 소모된다는 점인데요,
if (m_imgGhost[0].style.visibility != "visible")
와 같이 확인하는 코드를 넣어 상태를 변경할 필요가 없으면 최대한 변경하지 말아야 합니다.
만약 위의 코드 없이 왼쪽/오른쪽만 판단하여 마우스 이벤트가 발생할 때마다 이미지를 보이고 숨기면 엄청나게 버벅거리는 걸 느낄 수 있습니다.

이 점은 눈에 보이는 모든 요소에 해당하는 것으로 항상 화면 업데이트가 필요한 코드는 한곳에서 관리하는 것이 좋습니다. 그런 점에서 이 예제 코드는 썩 좋지가 않죠? (하하...)

멤버 변수 m_nTimeouts는 아무런 마우스 움직임이 없을 때 캐릭터가 사라질 때까지의 카운팅을 위한 것으로 자세한 것은 메인 프로시저 설명에서...

/*--------------------------------------
|   MainProcedure
|   메인 프로시저
---------------------------------------*/
function MainProcedure()
{
    clearTimeout(m_tmrMain); // 타이머 제거
    // MouseMove로부터 Timeout된 회수 체크하여 캐릭터를 숨길지 여부 확인
    if (m_nTimeouts < m_nMaxTimeouts)
    {
        // 타임아웃되기 전까지 캐릭터를 마우스 포지션까지 이동
        //m_divBG.innerHTML = "Timeup : " + m_nTimeouts;
        var nImgX = 0, nImgY = 0;
        nImgX = parseInt(m_imgGhost[0].style.left);
        nImgY = parseInt(m_imgGhost[0].style.top);
       
        if (m_nPosX < (nImgX - m_nGhostSpeed))
        {
            // 왼쪽 보고 있는 경우
            m_imgGhost[0].style.left = (nImgX - m_nGhostSpeed);
            m_imgGhost[1].style.left = (nImgX - m_nGhostSpeed);
        }
        else if (m_nPosX > (nImgX + m_nGhostSpeed))
        {
            // 오른쪽 보고 있는 경우
            m_imgGhost[0].style.left = (nImgX + m_nGhostSpeed);
            m_imgGhost[1].style.left = (nImgX + m_nGhostSpeed);
        }
       
        // 어느쪽이든 Y 위치 조정
        if (m_nPosY < (nImgY - m_nGhostSpeed))
        {
            m_imgGhost[0].style.top = (nImgY - m_nGhostSpeed);
            m_imgGhost[1].style.top = (nImgY - m_nGhostSpeed);
        }
        else if (m_nPosY > (nImgY + m_nGhostSpeed))
        {
            m_imgGhost[0].style.top = (nImgY + m_nGhostSpeed);
            m_imgGhost[1].style.top = (nImgY + m_nGhostSpeed);
        }

        m_nTimeouts = m_nTimeouts + 1;
    }
    else
    {
        // 시간 초과되면 숨김
        for(i=0;i<2;i++)
        {
            m_imgGhost[i].style.visibility = "hidden";
        }
        //m_divBG.innerHTML = "Cleanup"
    }
    m_tmrMain = setTimeout(MainProcedure, m_nInterval);
}   // function MainProcedure()

별것도 아닌 게 괜히 라인 수만 많네요.
여기서 눈여겨 볼 부분은 프로시저를 시작하자 마자 타이머를 클리어하고 모든 처리가 완료된 후 타이머를 다시 세팅한다는 점입니다.

만약 이러한 조치가 없이 타이머가 무작정 작동한다면, 메인 프로시저 내부의 코드가 어떤 이유로 지연이 걸려서 타이머의 주기보다 더 오랜 시간이 소요되었을 때 메인 프로시저의 코드가 미처 완료되기 전에 다시 메인 프로시저가 실행되게 되고 이는 심각한 버그로 나타나게 될 것입니다.

따라서 메인 프로시저는 반드시 타이머에 의해서만 구동되고 메인 프로시저 내에서는 우선 타이머를 끄고 자신의 임무를 모두 수행한 후에 다시 타이머를 가동하여 그 다음 턴의 처리를 수행해야 합니다.

메인 프로시저는 캐릭터가 사라져야 하는지 여부를 항상 체크합니다.

m_nTimeouts가 체크 회수보다 작다면 캐릭터가 아직 사라질 시점이 아니므로 m_nTimeouts를 계속 증가시키고 동시에 현재 커서 위치 방향으로 캐릭터를 지정된 픽셀만큼 이동시킵니다.

만약 사용자가 마우스를 움직이지 않고 가만히 있으면 m_nTimeouts를 0으로 초기화하는 MouseMoveBG()가 호출되지 않으며 메인 프로시저 내에서 m_nTimeouts는 계속 증가합니다.
이렇게 증가하다가 m_nMaxTimeouts에 다다르면 즉, 캐릭터가 나타난 이후로 사용자의 입력이 (m_nMaxTimeouts * 메인 프로시저 인터벌 시간)만큼 지났다면 캐릭터를 숨깁니다.

이 코드들이 무한히 반복되면서 part 1.에서 보는 것과 같은 사용자 움직임에 반응하는 캐릭터의 애니메이션 효과를 줄 수 있는 것이지요.


처음으로 좀 제대로 된 예제를 만들어서 설명을 길게 했습니다.
사용자 반응 처리를 위한 가장 기본적인 사항과 애니메이션에 대한 처리 아이디어는 이 예제로 충분히 이해할 수 있을 것입니다.

다음번엔 좀 더 개짓의 취지에 맞는 XML 비동기 통신... AJAX 기법을 활용한 개짓을 만들어볼 예정입니다.
개짓은 비동기 통신을 위한 메소드를 제공하는데요, 이것을 적극 활용하여 외부 XML으로부터 데이터를 수신하고 파싱하여 개짓 내에서 활용하는 기법을 알아보는 것이죠.


여담입니다만, 개짓 컨테스트를 준비하고 있는 다른 분들은 다들 숨어서 혼자서 개발하시는지 블로그나 게시판을 찾을 수가 없더군요.
단편적인 소개를 하는 곳은 몇몇 있는데 커뮤니티화 된 곳은 못찼겠더라구요.
혹시 다들 영어는 기본이라 외국 포럼에서 활동하시는거라면 낭패, 절망. OTL

개짓 개발자분들 정보 공유좀 하자구요! =3=

신고
Posted by gongdo


티스토리 툴바