본문 바로가기
GameMaker강좌[GMS2]/네트워크강좌

[게임메이커 강좌-네트워크][GMS2] 멀티플레이어 게임 만들기-3-서버 게임 구성

by 타락카얀 2024. 12. 25.
728x90

 

 

GAME MAKER 강좌

 

KAYAN

 

 

 

 

 

 

 

◈ 플레이어 설정

 

이제 게임 구성을 해봅시다.

네트워크 게임을 만들기 전에 오프라인 형태로 플레이어 이동이나 게임 구성을 먼저 만들어 보시는 것을 권장합니다.

 

 

먼저 플레이어 설정입니다.

플레이어는 8방향으로 이동할 거에요.

8방향이면 8방향의 이미지를 준비하는 것이 좋겠지만, 강좌에서는 4방향 이미지만 준비하겠습니다.

(▲ 4 방향의 이동하는 이미지)

 

먼저 플레이어 오브젝트의 부모 오브젝트(obj_player_parent)의 [Create 이벤트]에 게임룸에서 식별할 ID를 설정합니다.

 
obj_player_parent - Create 이벤트
 
 
object_map_id = 0;
 
global.object_map_id+ = 1;
object_map_id = global.object_map_id;
 

 

게임 실행 오브젝트(obj_game_staget)의 [Room End 이벤트]를 추가하고, 이 ID값을 초기화 합니다.

 
obj_game_stage - Room End 이벤트
 
 
if ( global.select_server == 1 ){
global.object_map_id = 0;
}
 

 

그리고 플레이어 오브젝트(obj_player_1)에 이동 이미지를 지정합니다.

 

(▲ 플레이어 오브젝트)

 

[Create 이벤트]에 게임에 필요한 변수를 추가합니다.

 

 
obj_player_1 - Create 이벤트
 
 
event_inherited( ); //부모 오브젝트 이벤트 상속
 
user_parent = noone; //플레이어를 생성한 유저 오브젝트( 서버용 )
select = 0; //유저 선택
 
HP = 100;
HP_MAX = HP;
 
pspeed = 5; //이동 속도
 
mask_index = sprite_index;
image_speed = 0.2;
 
reload = 0; //공격 체크
 
alarm[5] = 1; //서버 동기화
 
move_x = x; //클라이언트용 좌표
move_y = y;
 

 

이동은 선택된 플레이어만 이동시키도록 합니다.

[Step 이벤트]에 방향키를 눌렀을 때 이동시키도록 합니다.

 
obj_player_1 - Step 이벤트
 
 
if ( select == 1 ){
// == == == == == == == == == == == == == Controller == == == == == == == == == == == == =
 
if keyboard_check( vk_left ){ direction = 180; }
if keyboard_check( vk_right ){ direction = 0; }
if keyboard_check( vk_up ){ direction = 90; }
if keyboard_check( vk_down ){ direction = 270; }
if keyboard_check( vk_up ) && keyboard_check( vk_left ){ direction = 90+45; }
if keyboard_check( vk_up ) && keyboard_check( vk_right ){ direction = 45; }
if keyboard_check( vk_down ) && keyboard_check( vk_left ){ direction = 270-45; }
if keyboard_check( vk_down ) && keyboard_check( vk_right ){ direction = 270+45; }
 
var _move;
_move = 0;
if ( keyboard_check( vk_left ) || keyboard_check( vk_right ) || keyboard_check( vk_up ) || keyboard_check( vk_down ) ){ _move = 1; }
 
 
var hspd, vspd;
hspd = abs( keyboard_check( vk_right )-keyboard_check( vk_left ) )*lengthdir_x( pspeed, direction );
x+ = hspd*place_free( x+round( hspd ), y );
 
 
vspd = abs( keyboard_check( vk_down )-keyboard_check( vk_up ) )*lengthdir_y( pspeed, direction );
y+ = vspd*place_free( x, y+round( vspd ) );
 
// == == == == == == == == == == == == == Controller == == == == == == == == == == == == =
}
 
 
//방향별 이미지 설정
var dir;
dir = round( direction/90 ) mod 4;
if ( dir == 0 ){ sprite_index = spr_player_01_right; }
if ( dir == 1 ){ sprite_index = spr_player_01_up; }
if ( dir == 2 ){ sprite_index = spr_player_01_left; }
if ( dir == 3 ){ sprite_index = spr_player_01_down; }
 
//이동 제한
var w, h;
w = ( sprite_width*0.5 );
h = ( sprite_height*0.5 );
x = max( w, min( x, room_width-w ) );
y = max( h, min( y, room_height-h ) );
 

 

그리고 방향에 따른 이미지를 정해줍니다.

마지막으로 룸 내에서만 이동하도록 이동 제한 설정합니다.

 

일단, 원하는 모습으로 이동하는지 확인해봐야겠죠.

게임 룸(Room_game_1)에 플레이어를 대충 임시 배치하고, 잘 작동하는지 확인해봅시다.

(▲ 플레이어 이동)

 

아직은 네트워크 기능을 넣지 않은 상태입니다.

먼저 서버용으로 만들어보고 원하는 형태가 나오면, 그 다음 클라이언트에 적용할 예정입니다.

 

 

 

 

 

◈ 플레이어의 공격

 

 

 

이동을 만들었으니, 총알도 발사하도록 만들어 봅시다.

먼저, 총알 오브젝트(obj_bullet)를 하나 만들고, 총알 이미지를 지정합니다.

 

(▲ 총알 오브젝트)

 

[Create 이벤트]를 추가하고, 이동 속도를 지정합니다.

총알 오브젝트는 생성한 즉시 이동하고, 지정시간후에 파기하도록 할 거에요.

 
obj_bullet - Create 이벤트
 
 
user_id = -1;
 
speed = 10;
alarm[0] = 50;
 

 

파기 시간은 간단하게 알람 이벤트를 이용합시다.

 

[Alarm 0 이벤트]를 추가하고, 파기하도록 합니다.

 
obj_bullet - Alarm 0 이벤트
 
 
instance_destroy( );
 

 

강좌에서는 이동 속도 10에 50알람(스텝) 후에 파기했으니, 10x50으로 약500 픽셀 거리를 이동하게 됩니다.

 

[End Step 이벤트]를 추가하고, 이미지의 방향을 이동 방향으로 동기화합니다.

 
obj_bullet - End Step 이벤트
 
 
image_angle = direction;
 

 

이제 이 오브젝트를 플레이어 오브젝트에서 생성하면 됩니다.

강좌에서는 Z 키를 눌렀을 때, 총알을 발사하는 것으로 하겠습니다.

 
obj_player_1 - Step 이벤트
 
 
if ( select == 1 ){
// == == == == == == == == == == == == == Controller == == == == == == == == == == == == =
 
if keyboard_check( vk_left ){ direction = 180; }
if keyboard_check( vk_right ){ direction = 0; }
if keyboard_check( vk_up ){ direction = 90; }
if keyboard_check( vk_down ){ direction = 270; }
if keyboard_check( vk_up ) && keyboard_check( vk_left ){ direction = 90+45; }
if keyboard_check( vk_up ) && keyboard_check( vk_right ){ direction = 45; }
if keyboard_check( vk_down ) && keyboard_check( vk_left ){ direction = 270-45; }
if keyboard_check( vk_down ) && keyboard_check( vk_right ){ direction = 270+45; }
 
var _move;
_move = 0;
if ( keyboard_check( vk_left ) || keyboard_check( vk_right ) || keyboard_check( vk_up ) || keyboard_check( vk_down ) ){ _move = 1; }
 
 
var hspd, vspd;
hspd = abs( keyboard_check( vk_right )-keyboard_check( vk_left ) )*lengthdir_x( pspeed, direction );
x+ = hspd*place_free( x+round( hspd ), y );
 
 
vspd = abs( keyboard_check( vk_down )-keyboard_check( vk_up ) )*lengthdir_y( pspeed, direction );
y+ = vspd*place_free( x, y+round( vspd ) );
 
 
//---------- ▼ 총알 발사
var inst;
if reload == 0{
if keyboard_check_pressed( ord( "Z" ) ){
 
inst = instance_create_depth( x, y, depth, obj_bullet );
inst.direction = direction;
inst.user_id = user_id; //누가 발사했는지 유저 ID를 설정
 
reload = 1; //재장전 체크
alarm[1] = 10; //재장전 초기화 시간
}}
//---------- ▲ 총알 발사
 
// == == == == == == == == == == == == == Controller == == == == == == == == == == == == =
}
 
 
//방향별 이미지 설정
var dir;
dir = round( direction/90 ) mod 4;
if ( dir == 0 ){ sprite_index = spr_player_01_right; }
if ( dir == 1 ){ sprite_index = spr_player_01_up; }
if ( dir == 2 ){ sprite_index = spr_player_01_left; }
if ( dir == 3 ){ sprite_index = spr_player_01_down; }
 
//이동 제한
var w, h;
w = ( sprite_width*0.5 );
h = ( sprite_height*0.5 );
x = max( w, min( x, room_width-w ) );
y = max( h, min( y, room_height-h ) );
 

 

총알은 키를 눌렀을 때 발사하고, 다음 재발사하기 전에 약간의 지연시간을 주도록 합니다.

 

[Alarm 1 이벤트]를 추가하고, 재장전하도록 변수를 초기화합니다.

 
obj_player_1 - Alarm 1 이벤트
 
 
reload = 0;
 

 

잘 작동하는지 확인해봅시다.

 

 

 

 

 

 

 

◈ 플레이어 게임룸에 자동 생성

 

이번에는 게임 룸으로 입장했을 때, 플레이어를 지정한 위치에 자동으로 생성하도록 만들어 봅시다.

먼저 플레이어 생성 위치용 오브젝트(obj_start_point)에 이미지를 지정하고, 플레이어가 시작했으면 하는 위치에 배치하도록 합니다.

(▲ 플레이어 생성 위치용 오브젝트)

 

유저가 게임룸에 들어갔을 때, 바로 플레이어를 생성하지 않고, 서버와 클라이언트가 통신이 이루어지도록

몇 스텝 후에 생성하도록 합니다.

강좌에서는 알람 이벤트를 이용하겠습니다.

게임 실행용 오브젝트(obj_game_stage)의 [Create 이벤트]에 실행하고자 하는 [알람 이벤트]를 활성화 합니다.

 
obj_game_stage - Create 이벤트
 
 
alarm[0] = 10;
 

 

게임에 관한 대부분의 설정(플레이어의 생성 및 제어, 피격판정, 등)은 서버에서 관리하는 것이 좋습니다.

서버에서 먼저 상황을 처리하고, 클라이언트에게 메시지를 보내 서버와 동일하게 수행하라고 요청하는 방식입니다.

 

먼저, 게임룸에 배치된 플레이어 생성 위치용 오브젝트의 위치를 배열에 저장하여 랜덤으로 뒤섞습니다.

그러면 플레이어를 생성할 때, 랜덤 위치에 생성되겠죠.

 
obj_game_stage - Alarm 0 이벤트
 
 
alarm[0] = 1;
 
 
//--------- 서버
if global.select_server == 1{
if instance_exists( obj_start_point ){
var inst, i, _x, _y, aa, _depth, _obj;
 
//--------- 플레이어 생성 위치
var pos_xy = [];
var place_n = instance_number( obj_start_point );
 
for( i = 0; i<place_n; i++; ){
aa = instance_find( obj_start_point, i ); //시작 위치 얻기
pos_xy[i] = $"{ aa.x}, { aa.y}";
}
if array_length( pos_xy )>0{ randomize( ); array_shuffle_ext( pos_xy ); }//랜덤 섞기
//--------- 플레이어 생성 위치
 
}
}
 

 

다음 랜덤으로 뒤섞인 위치에 플레이어를 생성하도록 합니다.

 
obj_game_stage - Alarm 0 이벤트
 
 
alarm[0] = 1;
 
//--------- 서버
if global.select_server == 1{
if instance_exists( obj_start_point ){
var inst, i, _x, _y, aa, _depth, _obj;
 
//--------- 플레이어 생성 위치
var pos_xy = [];
var place_n = instance_number( obj_start_point );
 
for( i = 0; i<place_n; i++; ){
aa = instance_find( obj_start_point, i );
pos_xy[i] = $"{ aa.x}, { aa.y}";
}
if array_length( pos_xy )>0{ randomize( ); array_shuffle_ext( pos_xy ); }
//--------- 플레이어 생성 위치
 
 
//--------- ▼ 플레이어 생성
var _pos;
 
i = -1;
with( obj_game_user ){
if ( room_sync == 0 ){ continue; }//룸 동기화가 안된 유저는 제외
 
//게임 유저의 플레이어가 룸에 존재하지 않을 때
if ( user_gameplay == noone ) && ( user_regen< = 0 ){
i+ = 1;
i = ( i mod place_n );
 
_pos = string_split( pos_xy[i], ", " ); //생성할 위치
_x = real( _pos[0] );
_y = real( _pos[1] );
 
_depth = -10;
 
_obj = obj_player_1;
inst = instance_create_depth( _x, _y, _depth, _obj ); //플레이어 생성
inst.user_id = user_id; //유저 ID
inst.user_name = user_name; //유저 이름
inst.user_parent = id; //플레이어에 연결된 게임 유저 오브젝트
inst.select = select; //서버에서 실행하는 것이라 서버 유저는 1, 클라이언트는 0
 
user_gameplay = inst; //게임 유저가 생성한 플레이어
 
user_regen = 1; //재생성 체크
}
 
//플레이어가 룸에 없으면 재생성
if !( instance_exists( user_gameplay ) ){
if user_regen == 1{
user_gameplay = noone;
alarm[1] = 60*3; //재생성 시간
user_regen = 5;
}}
 
 
}
//--------- ▲ 플레이어 생성
 
}
}
//--------- 서버
 

 

게임룸에 플레이어가 존재하지 않으면 플레이어를 생성하도록 합니다.

 

[End Step 이벤트]를 추가하고, 카메라가 플레이어를 따라다니도록 합니다.

 
obj_game_stage - End Step 이벤트
 
 
set_carmera_target( );
 

 

그리고 [Draw 이벤트]를 추가하고, 플레이어 캐릭터 위에 유저 이름을 표시하도록 합니다.

 
obj_game_stage - Draw 이벤트
 
 
depth = -10;
draw_set_font( font0 );
draw_set_halign( fa_center );
draw_set_valign( fa_bottom );
 
var t, c;
with( obj_player_parent ){
t = string( user_name );
 
var _x, _y, xs, ys, w, h;
w = string_width( t )+16;
h = string_height( t )+8;
 
var _x, _y;
_x = floor( x );
_y = floor( y );
c = c_black; draw_text_color( _x+1, _y-32-4, t, c, c, c, c, 0.8 );
c = c_black; draw_text_color( _x, _y-1-32-4, t, c, c, c, c, 0.8 );
c = c_black; draw_text_color( _x-1, _y-1-32-4, t, c, c, c, c, 0.8 );
c = c_black; draw_text_color( _x, _y+1-32-4, t, c, c, c, c, 0.8 );
c = c_white; draw_text_color( _x, _y-32-4, t, c, c, c, c, 0.8 );
}
 
 
draw_set_halign( fa_left );
draw_set_valign( fa_top );
 

 

여기까지!

이전 테스트시 게임룸에 임시 배치된 플레이어는 삭제합시다.

그리고 특정 위치에 플레이어가 잘 생성되는지 확인해봅시다.

(▲ 플레이어 이동)

 

 

 

 

 

 

 

 

 

300x250

댓글