본문 바로가기
GameMaker강좌[GM8]/전략게임강좌

[게임메이커강좌-전략]RTS 게임 만들기 - 3 유닛 공격

by 타락카얀 2013. 7. 1.
728x90



GAME MAKER 강좌

 

 

KAYAN

 

 

 

 

 

이번에는 플레이어 유닛의 공격에 대해 설정해 보도록 하겠습니다.

 

 

(▲플레이어 유닛의 공격 장면)

 

 

 

 

 

 

 



◈공격 오브젝트(obj_att)

 
공격설정을 하기전에 어떤 공격을 시킬 것인지 구상하도록 해요.
강좌에선 간단하게 동그란 포탄을 직선으로 발사하여 공격시키는 형태로 만들어 보겠습니다.
먼저 공격용 이미지를 스프라이트(spr_att1)로 구성합니다.

 

 

(▲공격 이미지)


중심점을 가운데로 맞추세요.
마스크 설정이 필요하다면 Modify Mask버튼을 누르고 마스크 설정을 하도록 합니다.

그리고 공격으로 사용할 오브젝트(obj_att)를 하나 만들고, 스프라이트를 지정합니다.

 

 

(▲공격 오브젝트)


Create 이벤트를 추가하고, 필요한 변수를 선언합니다.

 

     ★ obj_att - Create 이벤트

     flag=0;//0=플레이어 1=적
     att=0;//공격력


이 오브젝트는 플레이어와 적 모두 사용할 수 있어요. flag 변수 값으로 두 진영을 구분하게 됩니다.
플레이어에서만 사용할 것이라면 빼도 됩니다.

이제 이 오브젝트가 적에게 데미지를 입혀야 겠지요.
Step 이벤트를 추가하고, 아래와 같이 이벤트를 작성합니다.

 

     ★ obj_att - Step 이벤트

     if flag=0{//적(obj_enemy)과의 충돌. 플레이어의 공격
     it=instance_place(x,y,obj_enemy);
     if instance_exists(it){
     if it.hp>att{it.hp-=att;}else{it.hp=0;}
     instance_destroy();}
     }

     if flag=1{//플레이어(obj_player)와 충돌. 적의 공격
     it=instance_place(x,y,obj_player);
     if instance_exists(it){
     if it.hp>att{it.hp-=att;}else{it.hp=0;}
     instance_destroy();}
     }


이것은 공격 오브젝트의 flag변수가 0일 때, 즉, 플레이어가 공격했을 때, 적 오브젝트와 충돌을 했다면,
적의 체력을 감소시키고, 자신은 파기합니다.
flag가 1일때는 반대로 적이 공격하는 것이므로, 플레이어와 충돌하면 그 상대 오브젝트의 체력을 감소시키고,
자신은 파기합니다.

참고instance_place(x,y,오브젝트)는 x,y위치에서 충돌하는 상대 인스턴스 id를 반환하는 함수입니다.
사용예)
it=instance_place(x,y,obj_player);
if instance_exists(it){with(it){instance_destroy();}}
이것은 obj_player와 충돌하면 충돌 상대를 파기시킵니다.

Draw 이벤트를 추가하고 이 공격 오브젝트의 이미지를 표시하도록 합니다.

 

     ★ obj_att - Draw 이벤트



     draw_sprite_ext(sprite_index,0,x,y,1,1,direction,c_white,1);



목표 오브젝트를 빗맞추었거나, 없을 때는 계속 지나가겠지요.
따라서 플레이어의 이동시 시간제한을 둔것처럼 이 오브젝트에도 이동 시간 제한을 설정하도록 해요.
여기에선 Alarm 0 이벤트를 사용하도록 하겠습니다.
Alarm 0 이벤트를 추가하고, 아래와 같이 이벤트를 작성합니다.

 

     ★ obj_att - Alarm 0 이벤트

     instance_destroy();


이 공격 오브젝트가 파기될때 아래와 같이 간단한 이펙트를 발생시켜 볼까요.

 

 

(▲ 흰색 이펙트)


먼저 이펙트로 사용할 이미지를 스프라이트(spr_effect1)로 구성합니다.

 

 

(▲ 흰색 이펙트 이미지)


중심점을 가운데로 맞춰주세요.

이 이펙트를 표시할 오브젝트(spr_ef1)를 하나 만듭니다.

 

 

(▲ 이펙트 오브젝트)


Create 이벤트를 추가하고, 필요한 변수들을 선언합니다.
여기에선 여러 용도로 사용하니, 여러 수치들을 세부적으로 선언하도록 하겠습니다. 헤헤~

 

     ★ obj_ef1 - Create 이벤트

     spr=sprite_index;//표시할 스프라이트 이미지
     img=0;//서브 이미지. -1일경우 애니메이션 재생
     fade_spd=0.05;//페이드 아웃 값
     sca=1;//확대/축소치
     sca_spd=0;//확대/축소 속도
     rot=0;//회전
     rot_spd=0;//회전 속도
     col=c_white;//혼합색
     alp=1;//투명도


후우~ 이 정도면 여러 용도로 충분하겠군요. 헤헤~
스프라이트 지정도 있고, 페이드 아웃치도 따로 준비, 확대나 축소 할수있는 기능도, 그리고, 회전도 가능하게 했네요.

이제 Step이벤트를 추가하고, 아래와 같이 작성합니다.

 

     ★ obj_ef1 - Step 이벤트

     if alp>0{alp-=fade_spd;}else{alp=0;instance_destroy();}
     rot+=rot_spd;
     sca+=sca_spd;


투명도가 0이 될때까지 점점 투명해지다 0이하가 되면 이펙트는 파기하도록 합니다.

Draw 이벤트를 추가하고 이펙트를 표시하도록 합니다.

 

     ★ obj_ef1 - Draw 이벤트

     draw_sprite_ext(spr,img,x,y,sca,sca,rot,col,alp);


이 이펙트도 일정 시간이 지나면 파기하는 이벤트를 추가해볼까요?
여기에선 Alarm 0 이벤트를 사용하도록 하겠습니다.
Alarm 0 이벤트를 추가하고, 아래와 같이 작성합니다.

 

     ★ obj_ef1 - Alarm 0 이벤트

     instance_destroy();


이제 이 이펙트를 공격 오브젝트(obj_att)Destroy 이벤트에서 생성하면 됩니다.
공격 오브젝트(obj_att)로 이동해서, Destroy 이벤트를 추가한 다음 아래와 같이 이펙트를 생성합니다.

 

     ★ obj_att - Destroy 이벤트

     it=instance_create(x,y,obj_ef1);
     it.spr=spr_effect1;
     it.img=0;


이펙트를 생성 후 spr변수에 이전에 만든 이펙트 이미지를 할당하면 됩니다.

음. 이 이펙트가 그냥 날라가면 좀 밋밋해보이겠죠.
여기에 잔상효과를 넣어봅시다.

 

 

 

(▲잔상 효과)


이것은 단순히 공격 오브젝트가 이동할 때마다 이펙트를 하나씩 떨궈뜨리면 됩니다.
간단하죠. 헤헤~
Step이벤트에 아래와 같이 추가합니다.

 

     ★ obj_att - Step 이벤트

     it=instance_create(x,y,obj_ef1);
     it.spr=spr_att1;//공격 이미지와 같은 스프라이트를 지정
     it.alp=0.5;


이렇게 하면 이동할 때마다 이펙트를 하나씩 떨어뜨리며 이동하고, 이펙트는 점점 투명해진 후 소멸하게 됩니다.

와우! 공격 오브젝트가 하나 완성되었네요! 굳~!
이제 플레이어 유닛의 공격 설정에 대해 알아봅시다.




◈플레이어 유닛의 공격(obj_pl_unit1)

 
플레이어 유닛 오브젝트(obj_pl_unit1)로 이동해서 먼저 공격에 필요한 변수들을 만들어 줍니다.
Create 이벤트에 아래와 같이 추가해주세요.

 

     ★ obj_pl_unit1 - Create 이벤트

     flag=0;
     select=0;

     hp=10;
     hp_max=hp;

     pspeed=2;

     move_on=0;
     move_x=0;
     move_y=0;

     target=noone;//공격 목표물

     //---------------------▼추가
     range_min=96;//공격시 근접 거리
     range_max=320;//공격시 이동 체크 범위
     range_att=128;//공격 범위

     att=1;//공격력
     att_sw=0;//공격 스위치
     att_delay=30;//공격 딜레이
     //---------------------▲추가


일단 목표물을 따로 지정해주지 않았을 때, 유닛이 알아서 가까운 목표물을 인지하여,
목표물로 정하게 만들어 봅시다.

Step이벤트에 아래와 같이, 이전 강좌에서 작성 했던 이동 코드 위에 이벤트를 추가하여 작성합니다.

 

     ★ obj_pl_unit1 - Step 이벤트

     //---------------------▼추가
     var aa;

     if !(instance_exists(target)){target=noone;//목표물이 없는 경우 목표물을 초기화함

     if instance_exists(obj_enemy){aa=instance_nearest(x,y,obj_enemy);
     if instance_exists(aa) && move_on=0{//가까운 적을 검색해 이동 범위 안에 있을 때 목표물로 설정
     if distance_to_object(aa)<=range_max{target=aa;}
     }}

     }
     //---------------------▲추가

     //----------------------이동 코드 위에 추가함
     if move_on=1{if point_distance(x,y,move_x,move_y)>pspeed{
     mp_potential_step(move_x,move_y,pspeed,0);
     }else{move_on=0;}}

     if hp<=0{hp=0;instance_destroy();}


이것은 일단 목표물(target)이 없는 경우 목표물을 초기화 하고, 룸 상에 존재하는 가장 가까운 적을 검색해서
이동할 수 있는 범위 안에 드는지 체크해요.
그리고 어떠한 행동을 하지 않은 상태에서, 적이 이동 범위 안에 든다면 그 적 인스턴스를 목표물로 설정하는 거에요.

이제 목표물이 설정되었다면 유닛이 공격을 하든, 이동을 하든 어떠한 행동을 해야겠지요.
역시 마찬가지로 Step 이벤트에 아래와 같이 추가합니다.
이것은 목표물이 생겼을 때 상황으로 else라는 기능을 사용할 거에요.

 

     ★ obj_pl_unit1 - Step 이벤트

     var aa;

     if !(instance_exists(target)){target=noone;

     if instance_exists(obj_enemy){aa=instance_nearest(x,y,obj_enemy);
     if instance_exists(aa) && move_on=0{
     if distance_to_object(aa)<=range_max{target=aa;}
     }}

     }

     //---------------------▼추가
     else{
     if !(distance_to_object(target)<range_min){
     mp_potential_step(target.x,target.y,pspeed,0);
     if move_on=1{move_on=2;}//게임 유저가 직접 이동시켰을 때는 일단 그 이동은 중지. 공격 이동 우선함
     }else{move_on=0;}

     _dir=point_direction(x,y,target.x,target.y);
     //------------------------공격
     if distance_to_object(target)<=range_att{

     if att_sw=0{direction=_dir;
     it=instance_create(x,y,obj_att);//공격 오브젝트 발사!
     it.direction=point_direction(x,y,target.x,target.y);
     it.speed=5;it.att=att;it.flag=flag;
     it.alarm[0]=point_distance(x,y,target.x,target.y)/it.speed;//이동 시간 제한
     alarm[0]=att_delay;att_sw=1;}
     }
     //------------------------공격 종료

     if !(distance_to_object(target)<=range_max){target=noone;}//목표물이 이동거리보다 멀때는 target을 초기화

     }

     if !(instance_exists(target)){if move_on=2{move_on=1;}}//이동 중이었을 때는 목표지점까지 마저 이동함.
     //---------------------▲추가

     //----------------------이동 코드
     if move_on=1{if point_distance(x,y,move_x,move_y)>pspeed{
     mp_potential_step(move_x,move_y,pspeed,0);
     }else{move_on=0;}}

     if hp<=0{hp=0;instance_destroy();}


이것은 공격 목표물이 정해졌을 때, 근접할 수 있는 거리(range_min)보다 멀면 mp_potential_step으로 이동하게 됩니다.
또한 이동중일때는 멈추도록 move_on을 다른 값으로 설정하지요.
만약 근접거리에 당도했다면 완전히 멈추게 됩니다.

그리고 공격할 수 있는 범위(range_att)에 든다면, 목표물 방향으로 이전에 만든 공격 오브젝트를 발사하게 됩니다.
다시 발사할때는 약간의 지연시간을 두고 발사하게 되지요.
만약 목표물이 이동할 수 있는 거리보다 멀면 목표물(target)은 다시 초기화 하게 됩니다.

move_on이 1이면 게임 유저가 직접 유닛을 이동시켰을 때입니다.
따라서 유닛이 이동 중인데 목표물이 없어졌다면, 이동 지점까지 마저 이동을 하는 것이죠.

다음은 재장전에 대해 설정해야합니다.
Alarm 0 이벤트를 추가하고, 다시 공격을 발사하도록 이벤트를 작성하도록 해요.

 

     ★ obj_pl_unit1 - Alarm 0 이벤트

     att_sw=0;


실제 게임을 플레이하는 사람이 직접 목표물을 정하도록 만들어 봅시다.
시스템 오브젝트(obj_ctrl_sys)로 이동해서 End Step 이벤트에 아래와 같이 추가합니다.

 

     ★ obj_ctrl_sys - End Step 이벤트

     var mx,my;
     mx=mouse_x;
     my=mouse_y;
     mbc_L=mouse_check_button(mb_left);
     mbp_L=mouse_check_button_pressed(mb_left);

     mbc_R=mouse_check_button(mb_right);

     if mbp_L=1{
     with(obj_player){select=0;}
     aa=collision_point(mx,my,obj_player,1,1);
     if !(instance_exists(aa)){mp_x=mx;mp_y=my;pressed=1;}
     else{with(aa){select=1;}}
     }
     //select
     var _x1,_y1,_x2,_y2;
     if pressed=1 && mbc_L=0{

     _x1=mp_x;_y1=mp_y;
     _x2=mx;_y2=my;
     if mp_x>mx{_x1=mx;_x2=mp_x;}
     if mp_y>my{_y1=my;_y2=mp_y;}

     var _sel,_max;
     _sel=0;_max=12;
     with(obj_player){if _sel<_max{if x>_x1 && y>_y1 && x<_x2 && y<_y2{
     select=1;
     it=instance_find(obj_unit_position,_sel);
     if instance_exists(it){it.target=id;}
     _sel+=1;}}}

     pressed=0;}



     if mbc_R=1{
     with(obj_player){if select=1{
     aa=collision_point(mx,my,obj_player,1,1);
     bb=collision_point(mx,my,obj_enemy,1,1);
     if !(instance_exists(bb)){
     if !(instance_exists(aa)){
     move_on=1;
     move_x=mx;move_y=my;target=noone;
     alarm[5]=point_distance(x,y,move_x,move_y)/pspeed;
     }
     }else{target=bb;

     //---------------------▼추가
     move_on=1;
     move_x=mx;move_y=my;
     alarm[5]=point_distance(x,y,move_x,move_y)/pspeed;
     //---------------------▲추가

     }
     }}
     }

     //---------------move pointer--------------
     with(obj_unit_position){
     if instance_exists(target){
     if check=1{
     with(target){if select=1{move_x=other.x;move_y=other.y;}}
     }}else{target=noone;}}


캬아~ 추가 코드가 몇줄 안되네요. 핫~ 핫~
이건 간단합니다. 이전 강좌에서 유닛 이동에 대해 해보셨죠.
그것과 같은 것입니다. 조금 다른 점이라면 공격시 행동을 정해주는 것이지요.
먼저 목표물을 게임 유저가 직접 정했다면, 유닛의 목표물을 지정해주지요.
그리고 이동을 체크(move_on=1)도 해주고, 이동 좌표를 지정해줍니다.
이것은 목표물이 없어졌을 때 마저 이동시키게 하기 위함입니다.


테스트 하기 전에 적에 대해 간단하게 설정해봅시다.
적에 대한 설정을 아무것도 안했기 때문에 이대로 테스트하면 오류가 생길 수 있습니다.


 

 

 

 


◈적 설정(obj_en_unit1)

 
적 유닛 오브젝트로 이동해서 플레이어 유닛의 Create 이벤트 내용처럼 필요한 변수들을 선언해줍니다.
Create 이벤트에 아래와 같이 작성해요.

 

     ★obj_en_unit1 - Create 이벤트

     flag=1;//적 유닛이므로 1
     select=0;

     hp=10;
     hp_max=hp;

     pspeed=2;

     move_on=0;
     move_x=0;
     move_y=0;

     target=noone;

     range_min=96;
     range_max=320;
     range_att=128;

     att=1;
     att_sw=0;
     att_delay=30;


플레이어 유닛과 별다른 차이점이 없죠.
flag은 적 유닛이므로 1값을 넣고, 다른 수치는 입맛대로 변경하시면 됩니다.
강좌에선 플레이어 유닛의 수치들과 동일하게 적용하도록 하겠습니다.

유닛을 표시해야겠지요.
Draw 이벤트를 추가하고, 이미지를 표시하도록 합니다.

 

     ★obj_en_unit1 - Draw 이벤트

     draw_sprite_ext(sprite_index,0,x,y,1,1,direction,c_white,1);


그리고 체력이 0일때의 이벤트도 만들어야 하겠죠.
Step 이벤트를 추가하고, 체력이 0일때의 이벤트를 작성하도록 해요.

 

     ★obj_en_unit1 - Step 이벤트

     if hp<=0{hp=0;instance_destroy();}


음. 적 유닛이 파기될때 그냥 없어지면 좀 허전할 거에요.
간단한 이펙트라도 발생시킵시다.
Destroy 이벤트를 추가하고, 간단한 이펙트를 생성하도록 합니다.

 

     ★obj_en_unit1 - Destroy 이벤트

     repeat(8){
     it=instance_create(x,y,obj_ef1);
     it.spr=spr_effect1;
     it.img=0;
     it.sca=random_range(0.05,0.6);
     it.direction=irandom(360);
     it.speed=random_range(2,5);
     it.friction=0.1;
     }//흰색 이펙트를 랜덤 방향으로 날림


     it=instance_create(x,y,obj_ef1);
     it.spr=spr_effect1;
     it.img=0;it.col=c_red;
     it.sca_spd=0.05;//붉은색 이펙트를 가운데 생성하고 점점 확대시킴.

     it=instance_create(x,y,obj_ef1);
     it.spr=spr_effect1;
     it.img=0;//흰색 이펙트를 가운데 생성함


이것은 이전에 만든 이펙트 오브젝트를 사용해서, 이펙트 여러개를 먼저 랜덤방향으로 흩날리고,
또 다른 이펙트로 가운데 위치에 붉은색 이펙트를 생성한 다음 점점 확대시킴니다.
그리고 가운데에 흰색 이펙트를 생성하는 것입니다.

이왕 만든김에 플레이어 유닛(obj_pl_unit1)의 파기했을 때도 같이 만들어 보도록 합시다.
플레이어 유닛 오브젝트(obj_pl_unit1)로 이동해서 Destroy 이벤트에서 이펙트를 생성하도록 합시다.

 

     ★ obj_pl_unit1 - Destroy 이벤트

     repeat(8){
     it=instance_create(x,y,obj_ef1);
     it.spr=spr_effect1;
     it.img=0;
     it.sca=random_range(0.05,0.6);
     it.direction=irandom(360);
     it.speed=random_range(2,5);
     it.friction=0.1;
     }//흰색 이펙트를 랜덤 방향으로 날림


     it=instance_create(x,y,obj_ef1);
     it.spr=spr_effect1;
     it.img=0;it.col=c_red;
     it.sca_spd=0.05;//붉은색 이펙트를 가운데 생성하고 점점 확대시킴.

     it=instance_create(x,y,obj_ef1);
     it.spr=spr_effect1;
     it.img=0;//흰색 이펙트를 가운데 생성함


강좌에선 적 유닛의 파기 이벤트와 동일하게 적용하겠습니다.



와우! 드디어 플레이어의 공격설정이 끝났어요. 굳~!
어찌보면 쉽다고 생각한다면 쉬울 수도 있고, 어렵다고 생객할 수도 있었던 강좌였죠? 헤헤~
여러분 생각, 취향대로 공격 설정이라던지, 이펙트들을 만드시면 됩니다.

어째든 제대로 작동되는지 테스트 해봅시다.

 

 

(▲테스트 화면)


오우~ 맙소사! 적이 아무런 행동이 없군요.ㅠㅠ
이것은 마치 학살 수준이네요.
다음 강좌에선 적이 공격할 수 있는 이벤트에 대해 알아보도록 하겠습니다.

 

 


- 다음 편에서 계속 -

 

 



 

 

----------- 예 제 -----------

 

strategy-RTSX-1-3.gmk
다운로드

 

strategy-RTSX-1-3.exe
다운로드

 

 

 

300x250

댓글