본문 바로가기
GameMaker강좌[GMS2]/기타장르강좌

[게임메이커 강좌-기타][GMS2]그리드 기반의 길찾기-4 응용편3

by 타락카얀 2023. 1. 13.
728x90

 

 

GAME MAKER 강좌

 

KAYAN

 

 

 

 

 

◈ 그리드 기반의 길찾기(Path finding) 응용편3

 

 

이번 강좌에서는 실시간으로 패스를 갱신하면서 이동하는 것을 구성해봅시다.

패스는 초기에 설정하고, 이동이 완료되거나 이동 위치를 변경하기 전까지는 유지되는 형태입니다.

 

(▲ 패스 형태 유지)

 

이전 강좌에서는 이동하면서 패스를 갱신하는 방식이라 현재 위치에서 도착지점까지 경로가

약간 변경될 수 있었으나 이 방식은 패스를 처음 생성한대로 이동합니다.

 

먼저 룸 시작시 그리드와 패스를 만들어야 합니다.

그리드와 패스를 만들 오브젝트( obj_system )를 하나 추가하고 [Room Start 이벤트]를 추가합니다.

 


   // obj_system - Room Start 이벤트
   
   global.grid=32;
   global.move_grid=mp_grid_create(0,0,ceil(room_width/global.grid),ceil(room_height/global.grid),global.grid,global.grid);
   
   global.move_path=path_add( );


 

그리드는 룸의 크기 만큼 셀을 만듭니다.

그러면 룸 내의 그리드 크기만큼 좌표를 사용할 수 있게 됩니다.

 

강좌에서는 그리드의 크기를 32X32 정도의 크기로 설정했습니다만, 크기가 크면 좀 더

간결하게 이동할 수 있고, 작으면 정교하게 패스를 만들 수 있습니다.

다만, 이동하는 오브젝트의 이미지 크기를 고려해 그리드의 크기를 결정하는 것이 좋습니다.

 

그리고 룸 종료시 그리드와 패스를 파기하도록 합니다.

 


   // obj_system - Room End 이벤트
   
   mp_grid_destroy(global.move_grid);
   
   path_delete(global.move_path);


 

플레이어 오브젝트( obj_player )를 하나 만듭니다.

플레이어의 이미지는 그리드 셀 중심에 위치하도록 중심점을 맞춰주세요.

 

(▲ 플레이어의 이미지는 그리드 셀 중심에 위치하도록 설정)

 

오브젝트에 [Create 이벤트]를 추가하고 이동에 관련된 변수를 선언해줍니다.

이벤트를 구성하다 필요한 변수가 있다면 하나씩 추가해주면 됩니다.

 


   // obj_player - Create 이벤트
   
   move_on=0;//이동 스위치
   
   pspeed=2;//이동 속도
   
   move_x=0;//패스 도착지점
   move_y=0;
   
   move_target_x=0;//가장 가까운 패스 포인트
   move_target_y=0;
   
   move_xstart=0;//패스 시작지점
   move_ystart=0;
   
   move_path_ind=0;//이동할 패스 포인트


 

[Step 이벤트]를 추가하고, 여기서 패스 이동을 구성하겠습니다.

먼저, 마우스 좌표를 만들어 봅시다.

마우스 좌표는 그리드 셀 중심에 위치하도록 설정합니다.

그래야 패스를 구성할 때, 정확한 위치가 구성되기 때문이죠.

 


   // obj_player - Step 이벤트
   
   var mx,my;
   mx=(floor(mouse_x/global.grid)*global.grid)+floor(global.grid/2);
   my=(floor(mouse_y/global.grid)*global.grid)+floor(global.grid/2);


 

마우스를 클릭했을 때 이동하도록 구성합니다.

장애물이 추가된 셀은 패스를 생성할 수 없기 때문에, 마우스를 클릭할 때 장애물을 미리 체크하도록 합니다.

 

그리고 마우스를 클릭하면 현재 마우스 좌표를 변수에 저장하도록 합니다.

 


   // obj_player - Step 이벤트
   
   var _break, mbp_L;
   mbp_L=mouse_check_button_pressed(mb_left);
  

   _break=0;
  

   if position_meeting(mx,my,obj_block){ _break=1; }
  
  
   if mbp_L && _break==0{ move_on=0; }
  
   if move_on==0{
   if mbp_L && _break==0{
   move_xstart=x;//패스 시작지점
   move_ystart=y;
  
   move_x=mx;//패스 도착지점
   move_y=my;
  
   move_on=1;//패스 구성 스위치
   move_path_ind=1;//이동할 패스 포인트 초기화(0번은 현재 위치이기 때문에 제외)
   }
   }


 

마우스를 클릭했을 때, 패스 구성에 필요한 현재 위치, 그리고 도착 위치를 저장하는 거에요.

그리고 목표지점에 도달하기 전까지는 좌표를 변경하지 못하도록 스위치 변수로 막아둡니다.

왜냐하면 이동 도중에 좌표가 바뀌면 이동이 원하는 형태가 나오지 않을 수 있기 때문이죠.

 

다음은 이동이 시작되면 그리드에 장애물을 마킹하고 패스를 만듭니다.

패스는 현재 좌표를 시발점으로 변수에 저장한 마우스 좌표까지 패스를 만들도록 합니다.

 


   // obj_player - Step 이벤트
   
   if move_on==1{

  
   mp_grid_clear_all(global.move_grid);//그리드 클리어
   mp_grid_add_instances(global.move_grid,obj_block,0);//장애물 마킹
  
   mp_grid_path(global.move_grid,global.move_path,move_xstart,move_ystart,move_x,move_y,1);
  
  
   }


 

강좌에서는 패스의 대각선을 허용하여 대각선으로도 이동하도록 하겠습니다.

 

패스를 만든 후, 패스 포인트가 2개 이상일 경우 이동할 패스 포인트 위치를 변수에 저장합니다.

 

(▲ 패스 포인트)

 

참고로 패스 포인트 0번은 현재 위치입니다.

 


   // obj_player - Step 이벤트
   
   if move_on==1{

  
   mp_grid_clear_all(global.move_grid);
   mp_grid_add_instances(global.move_grid,obj_block,0);
  
   mp_grid_path(global.move_grid,global.move_path,move_xstart,move_ystart,move_x,move_y,1);
  
  
   //------------ ▼ 추가

   move_target_x=x;
   move_target_y=y;
  
   if path_get_number(global.move_path)>=2{//이동할 패스 포인트 위치를 변수에 저장
   move_target_x=path_get_point_x(global.move_path,move_path_ind);
   move_target_y=path_get_point_y(global.move_path,move_path_ind);
   }
  
   if point_distance(x,y,move_target_x,move_target_y)>pspeed{
   dir=point_direction(x,y,move_target_x,move_target_y);
  
   x+=lengthdir_x(pspeed,dir);//이동
   y+=lengthdir_y(pspeed,dir);
  
   }
   else{move_path_ind+=1;}//다음 패스 포인터 위치로 갱신

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


 

현재 좌표에서 이동할 패스 포인터까지의 거리가 이동 속도 보다 클 때 이동 시키도록 합니다.

그리고 이동할 패스 포인터 위치에 다달았으면 다음 패스 포인터 위치로 갱신합니다.

 

 

마지막으로 도착 지점에 다다랐을 때, 이동을 멈추고 변수들을 초기화하도록 합니다.

 


   // obj_player - Step 이벤트
   
  
if move_on==1{

  
   mp_grid_clear_all(global.move_grid);
   mp_grid_add_instances(global.move_grid,obj_block,0);
  
   mp_grid_path(global.move_grid,global.move_path,move_xstart,move_ystart,move_x,move_y,1);
  
  
   move_target_x=x;
   move_target_y=y;
  
   if path_get_number(global.move_path)>=2{
   move_target_x=path_get_point_x(global.move_path,move_path_ind);
   move_target_y=path_get_point_y(global.move_path,move_path_ind);
   }
  
   if point_distance(x,y,move_target_x,move_target_y)>pspeed{
   dir=point_direction(x,y,move_target_x,move_target_y);
  
   x+=lengthdir_x(pspeed,dir);
   y+=lengthdir_y(pspeed,dir);
  
   }
   else{move_path_ind+=1;}
  
  
   //------------ ▼ 추가

  
if !(point_distance(x,y,move_x,move_y)>pspeed){//이동 종료

   x=move_x;
   y=move_y;
   move_on=0;
   move_path_ind=1;
   path_clear_points(global.move_path);
   }

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


 

완성되었네요.

패스로 이동하는 오브젝트를 룸에 배치시 그리드 셀 중심에 위치하도록 배치하세요.

 

배치가 끝났다면 잘 되는지 테스트 해봅시다.

 

※ 패스가 잘 구성되는지 확인하고 싶을 때는 [Draw 이벤트] 에서 draw_path 함수를 사용하면

사용중인 패스를 간단하게 표시할 수 있습니다.

예)


   //● obj_system - Draw 이벤트

   
  

   draw_path(global.move_path,0,0,1);

 

(▲ 테스트)

 

이 방식은 패스를 오브젝트에서 단일 방식으로 공유하여 사용하며, 이동할 때, 고정된 패스를 구성합니다.

하지만 패스를 구성하기 전, 모든 장애물이 고정적이라면 오브젝트 여러 개를 동시에 이동시킬 수도 있습니다.

 

 

pathfinding_example3.yyz
0.04MB

 

 

 

 

 

 

300x250

댓글