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

[게임메이커 강좌-전략][GMS2] RTS 게임 만들기-5 미니맵 구성

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

 

 

GAME MAKER 강좌

 

KAYAN

 

 

 

 

 

 

이번에는 화면 상단에 미니맵을 구성하는 것에 대해 알아 볼 것입니다.

 

(▲게임 화면)

 

 

 

 

 

◈미니맵 구성(obj_ctrl_sys)

 

 

간단하게 적의 위치와 지금 어느 위치를 보고 있는지 확인할 수 있는 미니맵을 구성해봅시다.

 

(▲이번 강좌에서 만들게 될 왼쪽 상단의 미니맵)

 

먼저 게임을 제어하는 시스템 오브젝트(obj_ctrl_sys)로 이동해서, [Create 이벤트]필요한 변수들을 추가해줍니다.

 


   //● obj_ctrl_sys - Create 이벤트
   
   
   minimap_x=0;//미니맵 x위치
   minimap_y=0;//미니맵 y위치
   
   minimap_width=160;//미니맵 가로 사이즈
   minimap_height=120;//미니맵 세로 사이즈
   
   minimap_click=0;//미니맵 안에 마우스 커서가 있을 때=1, 밖에 있으면=0
   


 

이것은 미니맵의 크기를 미리 정해주는 것입니다.

 

다음은 [Draw GUI 이벤트]에서, 아래와 같이 추가해 미니맵의 위치와 모양을 표시해주도록 해요.

 


   //● obj_ctrl_sys - Draw GUI 이벤트
   
   
   //------------------- 맵 마우스 드래그 표시
   var vx,vy,vw,vh;
   vx=camera_get_view_x(view_camera[0]);
   vy=camera_get_view_y(view_camera[0]);
   vw=camera_get_view_width(view_camera[0]);
   vh=camera_get_view_height(view_camera[0]);
   
   
   var x1,y1,x2,y2,c;
   if pressed==1{
   draw_set_alpha(0.8);
   x1=mouse_x-vx;
   y1=mouse_y-vy;
   x2=mouse_start_x-vx;
   y2=mouse_start_y-vy;
   c=c_red;draw_rectangle_color(x1,y1,x2,y2,c,c,c,c,1);
   draw_set_alpha(1);
   }
   
   //------------------ ▼ 이어서 추가
   
   //------------------ 미니맵
   var map_w,map_h,map_x,map_y;
   //미니맵 크기
   map_w=minimap_width;
   map_h=minimap_height;
   
   //미니맵 위치
   map_x=minimap_x;
   map_y=minimap_y;
   
   
   //미니맵 배경
   var c;
   
   //미니맵 외곽선
   c=c_gray;draw_rectangle_color(map_x-2,map_y-2,map_x+map_w+2,map_y+map_h+2,c,c,c,c,0);
   //미니맵 바탕
   c=c_dkgray;draw_rectangle_color(map_x,map_y,map_x+map_w,map_y+map_h,c,c,c,c,0);
   


 

이것은 어두운 회색 바탕의 약간 밝은 회색의 외곽선을 가진 사각형 모양의 미니맵입니다.

 

다음은 화면에 보이는 카메라 뷰의 현재 위치와 크기를 미니맵에 표시해볼까요.

마찬가지로 [Draw GUI 이벤트]에서, 위의 내용 밑에 아래와 같이 추가합니다.

 


   //● obj_ctrl_sys - Draw GUI 이벤트
   
   
   //------------------ ▼ 이어서 추가
   
   var vx,vy,vw,vh;
   vx=camera_get_view_x(view_camera[0]);
   vy=camera_get_view_y(view_camera[0]);
   vw=camera_get_view_width(view_camera[0]);
   vh=camera_get_view_height(view_camera[0]);
   
   
   //카메라 뷰 현재 위치
   var _x,_y,_px,_py,_pw,_ph;
   _px=map_x+((vx*map_w)/room_width);
   _py=map_y+((vy*map_h)/room_height);
   _pw=(vw*map_w)/room_width;
   _ph=(vh*map_h)/room_height;
   
   //미니맵 위에 흰색 사각형으로 카메라 뷰 표시
   c=c_white;draw_rectangle_color(_px,_py,_px+_pw,_py+_ph,c,c,c,c,1);
   


 

흰색의 사각형모양으로 뷰의 크기와 위치를 확인할 수 있게 만들었습니다.

뷰의 위치와 크기를 미니맵 크기로 환산하자면 아래와 같은 비율식으로 구할 수 있습니다.

 

     값=(실제 변동하는 크기X환산할 최대치)/실제 최대 크기

 

예를 들면 미니맵의 넓이가 160일때 룸 넓이(room_width)를 기준으로 뷰의 x좌표를 미니맵 크기로 환산한다면

 

     값=(camera_get_view_x(view_camera[0])*160)/room_width

 

이렇게 됩니다. 간단하죠?

이제 이 식으로 유닛의 위치도 표시해보도록 해요.

[Draw GUI 이벤트]에 아래와 같이 추가하여, 유닛들의 위치를 표시하도록 합니다.

 


   //● obj_ctrl_sys - Draw GUI 이벤트
   
   
   //유닛 위치
   with(obj_player){
   if x>0 && y>0 && x<room_width && y<room_height{//룸 내에 있을 때만 표시함.
   _x=map_x+((x*map_w)/room_width);
   _y=map_y+((y*map_h)/room_height);
   draw_circle_color(_x,_y,2,c_yellow,c_yellow,0);//플레이어는 노란색
   }}
   
   with(obj_enemy){
   if x>0 && y>0 && x<room_width && y<room_height{
   _x=map_x+((x*map_w)/room_width);
   _y=map_y+((y*map_h)/room_height);
   draw_circle_color(_x,_y,2,c_red,c_red,0);//적은 붉은 색
   }}
   


 

이것은 유닛이 룸 내에 있을 때만 위치를 표시하게 됩니다.

룸 밖에 있을 때도 표시한다면 미니맵에서 벗어나 보기에 안 좋겠지요.

표시색은 되도록이면 눈에 잘보이는 색으로 표시하도록 해요.

 

 

 

 

 

 

◈미니맵에서 드래그해 뷰를 이동시키기(obj_ctrl_sys)

 

 

다음은 미니맵에서 드래그해 뷰를 이동 시켜 보도록 만들어 보아요.

 

(▲ 미니맵에서 드래그해 뷰를 이동시키기)

 

이 이벤트는 [사용자 정의 이벤트](User Defined)를 이용해보도록 하겠습니다.

[사용자 정의 이벤트](User Defined)는 이벤트에서 직접 실행시키지 않으면, 이벤트가 발생되지 않지만,

이벤트를 호출하게 되면 바로 실행시킬 수 있는 이벤트입니다.

 

이벤트 메뉴-> Other -> User Defined -> [User 0 이벤트]를 추가하고, 아래와 같이 작성합니다.

 


   //● obj_ctrl_sys - User Defined 0 이벤트
   
   
   var _w,_h,vx,vy,vw,vh,mx,my;
   vx=camera_get_view_x(view_camera[0]);//미니맵의 표시 기준점
   vy=camera_get_view_y(view_camera[0]);//미니맵의 표시 기준점
   vw=camera_get_view_width(view_camera[0]);
   vh=camera_get_view_height(view_camera[0]);
   mx=mouse_x;
   my=mouse_y;
   
   var map_x,map_y,map_w,map_h;
   map_x=vx+minimap_x;
   map_y=vy+minimap_y;
   map_w=minimap_width;
   map_h=minimap_height;
   
   //마우스 포인터가 미니맵에 있는지 체크
   var _x,_y;
   if pressed==0{//미니맵 밖에서 먼저 클릭하지 않았을 때 작동
   if mx>map_x && my>map_y && mx<map_x+map_w && my<map_y+map_h{
   minimap_click=1;//마우스 포인터가 미니맵 안에 있을 때
   }
   else{//마우스 포인터가 미니맵 밖에 있을 때
   if !(mouse_check_button(mb_left)){minimap_click=0;pressed=0;}
   }
   }
   
   
   //미니맵을 통해 뷰 이동
   if minimap_click==1{
   _x=floor(((mx-map_x)*room_width)/map_w);
   _y=floor(((my-map_y)*room_height)/map_h);
   
   if mouse_check_button(mb_left){
   vx=max(0,min(_x-(vw/2),room_width-vw));
   vy=max(0,min(_y-(vh/2),room_height-vh));
   camera_set_view_pos(view_camera[0],vx,vy);
   }
   }
   
   
   if (mx>map_x && my>map_y && mx<map_x+map_w && my<map_y+map_h){
   with(obj_mouse_pointer){x=vx+(vw/2);y=vy+(vh/2);}
   }
   


 

이것은 마우스 포인터가 미니맵 안에 있을 때, 그리고 유닛을 선택하려고 드래그 하지 않았을 때 발생하게 합니다.

그리고 뷰의 위치를 미니맵에서 실제 크기로 환산하여 재배치하게 됩니다.

이때 미니맵 안에 있을 때 필드의 상황에 영향을 주지 않도록 minimap_click변수의 값을 1로 만들 놓습니다.

 

강좌에서 미니맵이 왼쪽 상단에 있죠.

뷰의 가장자리는 뷰를 움직이는 마우스 포인터 오브젝트(obj_mouse_pointer)를 따라 뷰를 이동시킵니다.

미니맵의 위치가 가장자리에 위치한다면, 뷰를 움직이는 마우스 포인터 오브젝트(obj_mouse_pointer)에

의해 의도치 않게 뷰가 이동할 수 있죠.

 

(▲ 미니맵에서 마우스 포인터 위치에 따라 의도치않게 뷰가 이동할 수 있음)

 

이것을 막기 위해 아래와 같이 추가하여 미니맵 안에 있을 때는 마우스 포인터 오브젝트를 뷰 가운데로 정렬하도록 합니다.

 


   //● obj_ctrl_sys - User Defined 0 이벤트
   
   
   var _w,_h,vx,vy,vw,vh,mx,my;
   vx=camera_get_view_x(view_camera[0]);//미니맵의 표시 기준점
   vy=camera_get_view_y(view_camera[0]);//미니맵의 표시 기준점
   vw=camera_get_view_width(view_camera[0]);
   vh=camera_get_view_height(view_camera[0]);
   mx=mouse_x;
   my=mouse_y;
   
   var map_x,map_y,map_w,map_h;
   map_x=vx+minimap_x;
   map_y=vy+minimap_y;
   map_w=minimap_width;
   map_h=minimap_height;
   
   //마우스 포인터가 미니맵에 있는지 체크
   var _x,_y;
   if pressed==0{//미니맵 밖에서 먼저 클릭하지 않았을 때 작동
   if mx>map_x && my>map_y && mx<map_x+map_w && my<map_y+map_h{
   minimap_click=1;//마우스 포인터가 미니맵 안에 있을 때
   }
   else{//마우스 포인터가 미니맵 밖에 있을 때
   if !(mouse_check_button(mb_left)){minimap_click=0;pressed=0;}
   }
   }
   
   
   //미니맵을 통해 뷰 이동
   if minimap_click==1{
   _x=floor(((mx-map_x)*room_width)/map_w);
   _y=floor(((my-map_y)*room_height)/map_h);
   
   if mouse_check_button(mb_left){
   vx=max(0,min(_x-(vw/2),room_width-vw));
   vy=max(0,min(_y-(vh/2),room_height-vh));
   camera_set_view_pos(view_camera[0],vx,vy);
   }
   }
   
   
   if (mx>map_x && my>map_y && mx<map_x+map_w && my<map_y+map_h){
   with(obj_mouse_pointer){x=vx+(vw/2);y=vy+(vh/2);}
   }
   

   //------------------------ ▼ 이어서 추가
   
   if (mx>map_x && my>map_y && mx<map_x+map_w && my<map_y+map_h){
   with(obj_mouse_pointer){x=vx+(vw/2);y=vy+(vh/2);}
   }
  

   //END


 

이제 이 이벤트를 [End Step 이벤트]에서 호출 시켜 봅시다.

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

   


   //● obj_ctrl_sys - End Step 이벤트
   
   
   
   //------------------------ ▼ 추가
   
   event_user(0); //◀미니맵 이벤트 호출
   
   //------------------------ ▲ 추가
   
   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 && minimap_click==0{ //◀유닛 선택은 미니맵 밖에 있을 때 발생하도록 변경
   
   //------------------------ ▲ 수정
   
   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,it;
   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;
   _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 && minimap_click==0{ //◀유닛 선택은 미니맵 밖에 있을 때 발생하도록 변경
   
   //------------------------ ▲ 수정
   
   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{
   move_on=1;
   move_x=mx;move_y=my;
   target=bb;
   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;}
   }
  
   //END

   


 

으아아~ 이렇게 보니 복잡해보이네요.

이 설정, 저 설정 넣다보면 느는건 코드양입니다.

전략 게임을 만들다보면 어쩔 수 없는 부분이죠.

 

이것은 event_user(이벤트 번호)로 사용자 정의 이벤트를 호출할 수 있으며, 호출하는 즉시 이벤트가 발생하게 됩니다.

그리고 미니맵에서 뷰를 드래그하는 것은 유닛 선택부분보다 먼저 발생하도록 처리합니다.

 

 

이제 잘되는지 테스트 해봅시다.

 

 

 

 

 

◈미니맵을 통한 유닛의 이동(obj_ctrl_sys)

 

이번에는 유닛을 선택한 후, 미니맵을 클릭해 이동시켜 보도록 해요.

 

(▲ 미니맵을 통한 유닛의 이동)

 

마찬가지로 [사용자 정의 이벤트](User Defined)를 이용하도록 합시다.

이벤트 메뉴-> Other -> User Defined -> [User 1 이벤트]를 추가하고, 아래와 같이 작성합니다.

 


   //● obj_ctrl_sys - User Defined 1 이벤트
   
   
   //--------------- 미니맵을 통해 룸 좌표를 제어 시작
   var _w,_h,vx,vy,vw,vh,mx,my;
   vx=camera_get_view_x(view_camera[0]);//미니맵의 표시 기준점
   vy=camera_get_view_y(view_camera[0]);//미니맵의 표시 기준점
   vw=camera_get_view_width(view_camera[0]);
   vh=camera_get_view_height(view_camera[0]);
   mx=mouse_x;
   my=mouse_y;
   
   var map_x,map_y,map_w,map_h;
   map_x=vx+minimap_x;
   map_y=vy+minimap_y;
   map_w=minimap_width;
   map_h=minimap_height;
   
   //유닛 이동
   if minimap_click==1{//미니맵 안에 있고
   if mouse_check_button_pressed(mb_right){//마우스 오른쪽 버튼을 눌렀을 때 발생
   var _x,_y;
   _x=((mx-map_x)*room_width)/map_w;
   _y=((my-map_y)*room_height)/map_h;
   
   with(obj_unit_position){check=1;
   x=_x+irandom_range(-8,8);
   y=_y+irandom_range(-8,8);}
   
   with(obj_player){if select==1{
   move_on=1;
   move_x=_x;move_y=_y;target=noone;
   alarm[5]=point_distance(x,y,_x,_y)/pspeed;
   }}
   
   }}
   //--------------- 미니맵을 통해 룸 좌표를 제어 끝
  



 

저번 강좌에서 이동 설정에 대해 해보았죠?

넵. 그것과 같은 것입니다.

조금 다르다면 목표물(target) 설정이 없다는 것뿐이죠.

미니맵에서 실제크기로 좌표를 환산하여, 유닛 포지션 오브젝트(obj_unit_position)의 위치를

지정해주고, 선택된 유닛들의 위치도 지정하여 이동 시키는 것입니다.

 

 

이제 이 이벤트를 [End Step 이벤트]에서 호출 시켜 봅시다.

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

 


   //● obj_ctrl_sys - End Step 이벤트
   
   
   
   //------------------------ 미니맵
   
   event_user(0);
   
   //------------------------ 미니맵
   
   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 && minimap_click==0{
   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,it;
   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;
   _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 && minimap_click==0{
   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{
   move_on=1;
   move_x=mx;move_y=my;
   target=bb;
   alarm[5]=point_distance(x,y,move_x,move_y)/pspeed;
   }
   }}
   }
   
   //----------------------- ▼ 삽입
   
   event_user(1); //◀미니맵 클릭 이벤트 호출
   
   //----------------------- ▲ 삽입
   
   //---------------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;}}
   
   //END



 

캬아~ 길다~

위의 위치에 호출 이벤트를 한줄 삽입하시면 됩니다.

 

이렇게 하면 [User Defined 1 이벤트]에서 구성했던 것을 실행하여 미니맵을 클릭했을 때

선택된 유닛이 해당 좌표로 이동해요.

 

 

와우~! 드디어 미니맵이 완성되었습니다.

테스트! 테스트를 해봅시다!

 

(▲ 테스트 화면)

 

후아~ 강좌는 여기까지입니다.

기초적인 부분만 설명했으나, 강좌가 많이 복잡했는데, 재미있으셨는지 모르겠네요.

이제 여러분이 이 강좌를 토대로 응용하셔서 게임을 제작하시면 됩니다.

어째든 재미있는 게임을 만들어 보도록 해요.

 

 

 

 

- 끝 -

 

 

 

 

● 예 제

strategy_rts_example01a_5.yyz
0.07MB

 

● 실행 버전

strategy_rts_example01a_5.zip
3.32MB

 

 

 

 

 

 

 

 

300x250

댓글