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

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

by 타락카얀 2023. 3. 14.
728x90

 

 

GAME MAKER 강좌

 

KAYAN

 

 

 

 

 

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

 

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

 

 

 

 

 

◈공격 오브젝트(obj_att)

 

 

공격설정을 하기전에 어떤 공격을 시킬 것인지 구상하도록 해요.

강좌에선 간단하게 동그란 포탄을 직선으로 발사하여 공격시키는 형태로 만들어 보겠습니다.

 

(▲ 공격 장면)

 

먼저 공격용 이미지를 스프라이트(spr_att1)로 구성합니다.

 

(▲공격 이미지)

 

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

마스크 설정이 필요하다면 Modify Mask버튼을 누르고 마스크 설정을 하도록 합니다.

 

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

 

(▲공격 오브젝트)

 

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

 


   //● obj_attack_bullet - Create 이벤트
   
   
   flag=0;//0=플레이어 1=적
   damage_point=0;//공격력
  

 

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

플레이어에서만 사용할 것이라면 빼도 됩니다.

 

이제 이 오브젝트가 적에게 데미지를 입혀야 겠지요.

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

 


   //● obj_attack_bullet - Step 이벤트
   
   
   var it;
   if flag==0{//적(obj_enemy)과의 충돌. 플레이어의 공격
   it=instance_place(x,y,obj_enemy);
   if instance_exists(it){
   if it.HP>damage_point{it.HP-=damage_point;}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>damage_point{it.HP-=damage_point;}else{it.HP=0;}
   instance_destroy();}
   }
   


 

이것은 공격 오브젝트의 flag변수가 0일 때, 즉, 플레이어가 공격했을 때, 적 오브젝트와 충돌을 했다면,

충돌한 적의 ID값을 얻어 해당 적의 체력을 감소시키고, 자신은 파기합니다.

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_attack_bullet - Draw 이벤트
   
   
   draw_sprite_ext(sprite_index,-1,x,y,1,1,direction,c_white,1);
  

 

목표 오브젝트를 빗맞추었거나, 없을 때는 계속 지나가겠지요.

따라서 플레이어의 이동시 시간제한을 둔것처럼 이 오브젝트에도 이동 시간 제한을 설정하도록 해요.

여기에선 [Alarm 0 이벤트]를 사용하도록 하겠습니다.

[Alarm 0 이벤트]를 추가하고, 아래와 같이 이벤트를 작성합니다.

 


   //● obj_attack_bullet - Alarm 0 이벤트
   
   
   instance_destroy();



 

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

 

(▲ 흰색 이펙트)

 

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

 

(▲ 흰색 이펙트 이미지)

 

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

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

 

(▲ 이펙트 오브젝트)

 

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

여기에선 여러 용도로 사용하니, 여러 수치들을 세부적으로 선언하도록 하겠습니다.

 


   //● obj_effect1 - 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_effect1 - Step 이벤트
   
   
   if alp>0{alp-=fade_spd;}else{alp=0;instance_destroy();}
   rot+=rot_spd;
   sca+=sca_spd;
   


 

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

 

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

 


   //● obj_effect1 - Draw 이벤트
   
   
   draw_sprite_ext(spr,img,x,y,sca,sca,rot,col,alp);
   


 

이 이펙트도 일정 시간이 지나면 파기하는 이벤트를 추가해볼까요?

여기에선 [Alarm 0 이벤트]를 사용하도록 하겠습니다.

[Alarm 0 이벤트]를 추가하고, 아래와 같이 작성합니다.

 


   //● obj_effect1 - Alarm 0 이벤트
   
   
   instance_destroy();



 

이제 이 이펙트를 공격 오브젝트(obj_att)의 [Destroy 이벤트]에서 생성하면 됩니다.

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

 


   //● obj_attack_bullet - Destroy 이벤트
   
   
   var it;
   it=instance_create_depth(x,y,depth,obj_effect1);
   it.spr=spr_effect1;
   it.img=0;



 

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

 

음. 이 이펙트가 그냥 날라가면 좀 밋밋해보이겠죠.

여기에 잔상효과를 넣어봅시다.

 

(▲ 잔상 효과)

 

이것은 단순히 공격 오브젝트가 이동할 때마다 이펙트를 하나씩 떨궈뜨리면 됩니다.

간단하죠?

[Step 이벤트]에 아래와 같이 추가합니다.

 


   //● obj_att - Step 이벤트
   
   
   var it;
   if flag==0{
   it=instance_place(x,y,obj_enemy);
   if instance_exists(it){
   if it.HP>damage_point{it.HP-=damage_point;}else{it.HP=0;}
   instance_destroy();}
   }
   
   if flag==1{
   it=instance_place(x,y,obj_player);
   if instance_exists(it){
   if it.HP>damage_point{it.HP-=damage_point;}else{it.HP=0;}
   instance_destroy();}
   }
   
   //--------------▼ 이어서 추가
   
   it=instance_create_depth(x,y,0,obj_effect1);
   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;//공격 범위
   
   damage_point=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;}
   
   var _dir;
   _dir=point_direction(x,y,target.x,target.y);
   
   //-------- 공격 시작
   
   if distance_to_object(target)<=range_att{
   
   if att_sw==0{direction=_dir;
   var it;
   it=instance_create_depth(x,y,0,obj_attack_bullet);//공격 오브젝트 발사!
   it.direction=point_direction(x,y,target.x,target.y);
   it.speed=5;it.damage_point=damage_point;it.flag=flag;
   it.alarm[0]=point_distance(x,y,target.x,target.y)/it.speed;//이동 시간 제한
   alarm[0]=att_delay;
   att_sw=1;}
   }
   
   //-------- 공격 종료
   
   }
   
   //이동 중이었을 때는 목표지점까지 마저 이동함.
   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,mbc_L,mbp_L,mbc_R;
   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);
   
   
   var aa,bb;
   if mbp_L==1{
   with(obj_player){select=0;}
   aa=collision_point(mx,my,obj_player,1,1);
   if !(instance_exists(aa)){
   mouse_start_x=mx;mouse_start_y=my;
   pressed=1;}
   else{with(aa){select=1;}}
   }
   
   //유닛 선택
   
   var _x1,_y1,_x2,_y2;
   if pressed==1 && mbc_L==0{
   
   _x1=mouse_start_x;_y1=mouse_start_y;
   _x2=mx;_y2=my;
   if mouse_start_x>mx{_x1=mx;_x2=mouse_start_x;}
   if mouse_start_y>my{_y1=my;_y2=mouse_start_y;}
   
   var _sel,_max,it;
   _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;//공격 범위
   
   damage_point=1;//공격력
   att_sw=0;//공격 스위치
   att_delay=30;//공격 딜레이

   


 

플레이어 유닛과 별다른 차이점이 없죠.

flag는 적 유닛이므로 1값을 넣고, 다른 수치는 입맛대로 변경하시면 됩니다.

강좌에선 플레이어 유닛의 수치들과 동일하게 적용하도록 하겠습니다.

 

유닛을 표시해야겠지요.

[Draw 이벤트]를 추가하고, 이미지를 표시하도록 합니다.

 


   //● obj_en_unit1 - Draw 이벤트
   
   
   draw_sprite_ext(sprite_index,-1,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 이벤트
   
   
   var it;
   repeat(8){
   it=instance_create_depth(x,y,0,obj_effect1);
   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_depth(x,y,0,obj_effect1);
   it.spr=spr_effect1;
   it.img=0;it.col=c_red;
   it.sca_spd=0.05;//붉은색 이펙트를 가운데 생성하고 점점 확대시킴.
   
   it=instance_create_depth(x,y,0,obj_effect1);
   it.spr=spr_effect1;
   it.img=0;//흰색 이펙트를 가운데 생성함
   


 

이것은 이전에 만든 이펙트 오브젝트를 사용해서, 이펙트 여러개를 먼저 랜덤방향으로 흩날리고,

또 다른 이펙트로 가운데 위치에 붉은색 이펙트를 생성한 다음 점점 확대시킴니다.

그리고 가운데에 흰색 이펙트를 생성하는 것입니다.

 

이왕 만든김에 플레이어 유닛(obj_pl_unit1)의 파기했을 때도 같이 만들어 보도록 합시다.

플레이어 유닛 오브젝트(obj_pl_unit1)로 이동해서 [Destroy 이벤트]에서 이펙트를 생성하도록 합시다.

 


   //● obj_pl_unit1 - Destroy 이벤트
   
   
   var it;
   repeat(8){
   it=instance_create_depth(x,y,0,obj_effect1);
   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_depth(x,y,0,obj_effect1);
   it.spr=spr_effect1;
   it.img=0;it.col=c_red;
   it.sca_spd=0.05;//붉은색 이펙트를 가운데 생성하고 점점 확대시킴.
   
   it=instance_create_depth(x,y,0,obj_effect1);
   it.spr=spr_effect1;
   it.img=0;//흰색 이펙트를 가운데 생성함
   


 

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

 

 

 

와우! 드디어 플레이어의 공격설정이 끝났어요. 굳~!

어찌보면 쉽다고 생각한다면 쉬울 수도 있고, 어렵다고 생객할 수도 있었던 강좌였죠? 헤헤~

여러분 생각, 취향대로 공격 설정이라던지, 이펙트들을 만드시면 됩니다.

 

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

 

(▲테스트 화면)

 

오우~ 맙소사! 적이 아무런 행동이 없군요.ㅠㅠ

이것은 마치 학살 수준이네요.

다음 강좌에선 적이 공격할 수 있는 이벤트에 대해 알아보도록 하겠습니다.

 

 

 

- 다음 편에서 계속 -

 

 

 

 

 

 

300x250

댓글