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

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

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

 

 

GAME MAKER 강좌

 

KAYAN

 

 

 

 

 

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

 

 

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

 

(▲ 패스를 현재 위치에서 갱신)

 

이 방식은 패스를 오브젝트에서 단일 방식으로 공유하여 사용하지만, 패스를 실시간으로 갱신하는

유동적인 패스를 구성하기 때문에 패스를 사용하는 오브젝트를 여러 개를 동시에 이동시킬 수 있습니다.

 

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

그리드와 패스를 만들 오브젝트( 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;


 

[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_x=mx;
   move_y=my;
   move_on=1;
   }
   }


 

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

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

이것은 이동하면서도 현재 좌표를 기준으로 패스를 갱신할 것입니다.

 

(▲ 패스 포인트)

 

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

 


   // obj_player - Step 이벤트
   
   var dir,n;
   
   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,x,y,move_x,move_y,1);//패스 작성
   
   n=path_get_number(global.move_path);//패스 포인트 개수
   
   move_target_x=x;
   move_target_y=y;
   if n>=2{
   move_target_x=path_get_point_x(global.move_path,1);//가장 가까운 패스 포인트
   move_target_y=path_get_point_y(global.move_path,1);
   }
   
   }


 

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

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

 

현재 좌표에서 가장 가까운 패스 포인터까지의 거리가 이동 속도 보다 클 때

이동 시키도록 합니다.

 


   // obj_player - Step 이벤트


   var dir,n;
   
   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,x,y,move_x,move_y,1);
   
   n=path_get_number(global.move_path);
   
   move_target_x=x;
   move_target_y=y;
   if n>=2{
   move_target_x=path_get_point_x(global.move_path,1);
   move_target_y=path_get_point_y(global.move_path,1);
   }
   
   //------------ ▼ 추가
   
   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{
   x=move_target_x;//패스 포인터 위치로 갱신
   y=move_target_y;
   }
   
   //------------ ▲ 추가
   
   }


 

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

 


   // obj_player - Step 이벤트
   
   var dir,n;
   
   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,x,y,move_x,move_y,1);
   
   n=path_get_number(global.move_path);
   
   move_target_x=x;
   move_target_y=y;
   if n>=2{
   move_target_x=path_get_point_x(global.move_path,1);
   move_target_y=path_get_point_y(global.move_path,1);
   }
   
   
   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{
   x=move_target_x;
   y=move_target_y;
   }
   
   
   //------------ ▼ 추가
   
   if !(point_distance(x,y,move_x,move_y)>pspeed){
   
   x=move_x;//도착지점으로 갱신
   y=move_y;
   move_on=0;//이동 초기화
   path_clear_points(global.move_path);//패스 클리어
   
   }
   
   //------------ ▲ 추가
   
   }


 

완성되었네요.

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

 

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

 

 

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

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

예)


   //● obj_system - Draw 이벤트

   
  

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

 

(▲ 테스트)

 

 

 

pathfinding_example2.yyz
0.04MB

 

 

 

 

 

 

300x250

댓글