2016년 10월 8일 토요일

[GMS]Match3 Gamemaker studio 따라하기 (candy crush) Part1~5


GMS(Game maker studio)로 candy crush 같은 게임 만들기

+발번역+개인적 의견
저도 영어를 잘 못알아 듣지만 대충 코딩만을 보고 추측(?)해서 정리해보았습니다.

Part1

아래 동영상을 보면서 따라 하면 됩니다. 주된 내용은 화면에 보석object 들을 instance_create를 이용해 동적으로 출력 합니다.



1.1 이미지 준비

시작하면 6개의 gems 이미지와 배경이 필요합니다. gems 크기는 60*60 gems 이미지를 구하기 힘들테니, 아무렇게나 색이 다른 6개의 이미지를 만들어 줍니다. 그리고 origin 은 X,Y는 30,30 중심점으로 맞춥니다. Collision checking은 Bounding Box Full Image 로 설정합니다.




background이미지는 bag0로 로드합니다. 640*640 인데 크게 중요하지는 않을듯합니다.
backgrounds::bag0:640*640 이미지
room0::backgrounds 설정

1.2 object 생성

Control object 생성 하고, Create events 추가하고 global 변수 추가합니다.
backgroud에서 보석들의 Xs,Ys 는 시작 지점입니다. 참고로 보석은 60*60이지만 backgroud에서 68*68 크기를 가지고 있습니다.

CreateObject->Control
sprite:NA
Control::Create
globalvar Xs,Ys;
Xs = 116;
Ys = 166;
scr0();

보석들을 생성할때 image_speed=0으로 해줍니다. 이렇게 되면 애니메이션이 되지 않습니다.
CreateObject->ogems
sprite:sp_gems
ogems::Create
image_index = irandom(5);
image_speed = 0;

1.3 스크립트 생성

scr0 스크립트는 화면에 보석들을 한번에 출력하기 위한 스크립트입니다.

Scripts
scr0::
for(i=0;i<7;i++){
    for(j=0;j<7;j++){
        instance_create(Xs+i*68,Ys+j*68,ogems);
    }
}

1.4 Room 생성

Rooms
room0->width=640, room0->height=640
AddObject->objects:Control 추가합니다.

1.5 실행화면

정상적으로 모두 작업이 완료되면 아래와 같은 상태가 됩니다.



Part2

아래쪽에 보석 object가 없을때 떨어지는 물리적인 처리에 대한 코드입니다.



기존대비 3개 변수를 추가합니다.
off 의 의미는 오브젝트가 off가 되면 더 이상 처리를 안하겠다는 의미입니다.
아래쪽에 보석이 있는지 보기위해서 +12를 하는데 이 값은 보석과 보석 틈은 8이며 한번 이동 거리가 4이기  때문에 8+4=12가 됩니다.
place_free는 solid object가 있는지 보기 위한 함수이므로 보석들을 solid true가 되도록 설정합니다. place_free는 현재 객체가 해당 좌표로 이동했을때 충돌 여부를 검사하는 함수 입니다.

ogems::Create
image_index = irandom(5);
image_speed = 0;
off=false;
stable=false;
onhold=false;


ogems::Step
if (off)
exit;
if (!onhold)
{
    if place_free(x,y+12) and (y<574)
    {
        y+=4;
        stable=false;
    }else{
        stable=true;
        off=true;
        alarm[0]=5;
    }
}

ogems::Alarm[0]
off=false;

ogems::Draw
draw_self();

ogems::Solid = true;






Part3

가로 혹은 세로 3개의 보석이 같아면 해당 보석을 제거하고 아래로 내리는 동작을 구현합니다.


Alarm[0]를 수정하는데, Alarm[0]의 이벤트 발생 시점은 보석이 사라진후 위에서부터 블럭이 떨어지기 시작해서 멈출때 발생합니다.
멈추게 되면 x+68,x+134 다음 다다음의 보석값을 읽어서 두개가 같은지 비교하게 됩니다.
stable은 보석이 움직이고 있는지 나타나는 flag값입니다.
여기에서는 match 1은 가로축 match 2는 세로축 검사의 일치를 의미하고 switch에서 값에 따라서 블럭을 제거하기 위해서 alarm[1]을 발생시킵니다.

ogems::Alarm[0]
off=false;
if (x<389) and (y>165)
if (!position_empty(x+68,y)) and (!position_empty(x+134,y))
{
    var N1,N2;
    N1=instance_position(x+68,y,ogems);
    N2=instance_position(x+134,y,ogems);
    if (N1.stable==true and N2.stable==true)
    {
        if(N1.image_index==image_index) and (N2.image_index==image_index)
        {
           match=1;
        }
    }
}
if (y<439) and (x>115)
if (!position_empty(x,y+68)) and (!position_empty(x,y+134))
{
    var N3,N4;
    N3=instance_position(x,y+68,ogems);
    N4=instance_position(x,y+134,ogems);
    if (N3.stable==true and N4.stable==true)
    {
        if(N3.image_index==image_index) and (N4.image_index==image_index)
        {
           match=2;
        }
    }
}
switch(match)
{
    case 1:
    with(N1)
    alarm[1]=1;
    with(N2)
    alarm[1]=1;
    alarm[1]=1;
    match=0;
    break;
    case 2:
    with(N3)
    alarm[1]=1;
    with(N4)
    alarm[1]=1;
    alarm[1]=1;
    match=0;
    break;
}

ogems::Create
image_index = irandom(5);
image_speed = 0;
off=false;
stable=false;
onhold=false;
match=0;

ogems::Alarm[1]
instance_destroy();

Control::press <space>
game_restart();

Part4

이동할 보석을 두개 선택하는 코드입니다.



F1,F2 는 각각 마우스 버튼을 누를때의 id를 저장하게됩니다. F1은 첫음으로 선택하는 보석이고 F2는 두번째 보석인데 두번째 보석이 이웃하지 않으면 F2에 저장하는것이 아니라 F1에 다시 저장하게 됩니다. 이때 F1x,F1y에 x,y좌표가 저장되게 됩니다.


Control::Create
globalvar F1,F2,F1x,F1y,F2x,F2y;
F1=0;
F2=0;
F1x=0;
F1y=0;
F2x=0;
F2y=0;
Xs = 116;
Ys = 166;
scr0();

마우스를 누르면 scr1을 이용해서 F1에 저장하고 이미 저장된 상태라면 F2에 저장하면서 onhold를 true를 설정합니다. onhold가 설정되면 block이 아래로 떨어지는 동작을 하지 않습니다.
gems::mouse_Left_Pressed
if(F2=0) and (stable) and (y>165)
if(F1=0)
{
    scr1();
}else
{
    if(F1!=id)and((x=F1x+68 and y=F1y)or(x=F1x-68 and y=F1y)or(x=F1x and y=F1y+68)or(x=F1x and y=F1y-68))
    {
        F2=id;
        F2x=x;
        F2y=y;
        with(all)
        onhold=true;
    }else{scr1();}
}

F1에 x,y,id를 저장하는 역할을 합니다
script::scr1()
with(all)
{
    off=false;
    onhold=false;
    nomatch=false;
}
F1=id;
F1x=x;
F1y=y;
화면에 디버깅 정보를 출력합니다.
Control::Draw
draw_text(15,15,F1);
draw_text(15,30,F1x);
draw_text(15,45,F1y);
draw_text(15,60,F2);
draw_text(15,75,F2x);
draw_text(15,90,F2y);



Part5

선택 된 두개의 보석을 교환하는 코드 입니다



이전대비 nomatch변수가 추가 되었습니다.
ogems::Create
image_index = irandom(5);
image_speed = 0;
off=false;
stable=false;
onhold=false;
match=0;
nomatch=false;

!onhold 코드는 이전과 동일합니다.
else쪽은 코드가 굉장히 복잡합니다. if F1=id F2=id 내부의 코드는 블럭을 매 step마다 이동시키기 위한 코드입니다. 결국 F1 F2가 자리가 변경이 되고 나면(id F1일때는 F2로 가야하고, id F2일때는 F1자리로 이동합니다.) 모든 보석에 대해 scr2를 수행합니다.
scr2에 의해서 검사를 했는데도 match가 일어나지 않으면, nomatch=true가 됩니다. 그러면 다음 step부터는 원상태로 돌아가는 동작을 합니다.
모두 돌아간뒤에는 nomatch = false가 됩니다.
ogems::Step
if (off)
exit;
if (!onhold)
{
    if place_free(x,y+12) and (y<574)
    {
        y+=4;
        stable=false;
    }else{
        stable=true;
        off=true;
        alarm[0]=5;
    }
}else{
    if(!nomatch)
    {
        if(F1=id)
        {
            if((x<F2x)and(y=F2y))
            {x+=4}else
            if((x>F2x)and(y=F2y))
            {x-=4};
            if((x=F2x)and(y<F2y))
            {y+=4}else
            if((x=F2x)and(y>F2y))
            {y-=4};
            if((x==F2x)and(y==F2y))
            {
                with(all)
                scr2();
                exit;
            }
        }
        if(F2=id)
        {
            if((x<F1x)and(y=F1y))
            {x+=4}else
            if((x>F1x)and(y=F1y))
            {x-=4};
            if((x=F1x)and(y<F1y))
            {y+=4}else
            if((x=F1x)and(y>F1y))
            {y-=4};
            if((x==F1x)and(y==F1y))
            {
                with(all)
                scr2();
                exit;
            }
        }
    }else{
        if(F2=id)
        {
            if((x<F2x)and(y=F2y))
            {x+=4}else
            if((x>F2x)and(y=F2y))
            {x-=4};
            if((x=F2x)and(y<F2y))
            {y+=4}else
            if((x=F2x)and(y>F2y))
            {y-=4};
            if((x==F2x)and(y==F2y))
            {
                onhold=false;
                nomatch=false;
                F2=0;
                exit;
            }
        }
        if(F1=id)
        {
            if((x<F1x)and(y=F1y))
            {x+=4}else
            if((x>F1x)and(y=F1y))
            {x-=4};
            if((x=F1x)and(y<F1y))
            {y+=4}else
            if((x=F1x)and(y>F1y))
            {y-=4};
            if((x==F1x)and(y==F1y))
            {
                onhold=false;
                nomatch=false;
                F1=0;
                exit;
            }
        }
    }
}

scr2 스크립트는 alarm[0]코드와 비슷합니다. 즉, 가로세로 3개가 일치하는 블럭이 있는지 보게됩니다. match되었을때
script::scr2();
match=0;
if (x<389) and (y>165)
if (!position_empty(x+68,y)) and (!position_empty(x+134,y))
{
    var N1,N2;
    N1=instance_position(x+68,y,ogems);
    N2=instance_position(x+134,y,ogems);
    if (N1.stable==true and N2.stable==true)
    {
        if(N1.image_index==image_index) and (N2.image_index==image_index)
        {
           match=1;
        }
    }
}
if (y<439) and (x>115)
if (!position_empty(x,y+68)) and (!position_empty(x,y+134))
{
    var N3,N4;
    N3=instance_position(x,y+68,ogems);
    N4=instance_position(x,y+134,ogems);
    if (N3.stable==true and N4.stable==true)
    {
        if(N3.image_index==image_index) and (N4.image_index==image_index)
        {
           match=2;
        }
    }
}
switch(match)
{
    case 1:
    with(N1)
    alarm[1]=1;
    with(N2)
    alarm[1]=1;
    alarm[1]=1;
    with(F1)
    {
        x=F2x;
        y=F2y;
    }
    with(F2)
    {
        x=F1x;
        y=F1y;
    }
    F1=0;
    F2=0;
    with(all)
    {
        off=false;
        onhold=false;
        nomatch=false;
    }
    match=0;
    break;
    case 2:
    with(N3)
    alarm[1]=1;
    with(N4)
    alarm[1]=1;
    alarm[1]=1;
    with(F1)
    {
        x=F2x;
        y=F2y;
    }
    with(F2)
    {
        x=F1x;
        y=F1y;
    }
    F1=0;
    F2=0;
    with(all)
    {
        off=false;
        onhold=false;
        nomatch=false;
    }
    match=0;
    break;
    default:
    if(F1=id)
    {F2.nomatch=true;}
    if(F2=id)
    {F1.nomatch=true;}
}








댓글 없음:

댓글 쓰기