2019년 9월 22일 일요일

GMS2 GameMaker Studio 2 로 슈팅 게임 만들기 (3) (Let's Make shooting game)


0. 목차


슈팅 게임의 기초 - 배경 스크롤
다중 배경 스크롤
주인공 object
ON Screen Jog
적 object
적 탄환
boss object
boss 탄환
Timelines 사용
Paths 사용
Player 입장 구현
Player 퇴장 구현
우주 배경 배경 스크롤



1. Jog image

On screen jog라는것은 모바일 환경에서 게임중 키 사용이 어렵기 때문에 가상으로 조이스틱을 그려서 키를 입력받은 방법을 의미합니다.
여기에서 이미지는 4장을 준비하였습니다. 크게 조이스틱용 패드, 버튼으로 구성되며, 버튼을 눌렀을때 이미지 한장을 더 추가해서 전체 4장이 됩니다.

전체 sprite 종류


방향 버튼 크기는 86*87 크기로 하였습니다.

방향키를 눌렀을때 색상 변경을 위한 sprite

 총알 발사를 위한 action 버튼 60*60 반지름은 30 입니다.

action 버튼이 눌렸을때 sprite


2. 키입력 받기

키입력은 player의 step에서 처리를 합니다.
원리는 다음과 같습니다. 마우스와 Jog 중심점(basex,basey)으로부터 jogspacer 만큼 떨어진 4개의 좌표(아래 이미지에서 빨간색)와 마우스를 클릭한 좌표까지 4개의 좌표와의 거리를 계산해서 거리가 가장 짧은 위치가 버튼이 눌렸다고 판단하는 방식입니다.


버튼이 한개짜리인 action 버튼은 좀 더 쉽습니다. 버튼 중심과 마우스클릭한 시점까지의 거리가 버튼 반지름안에 들어오는지만 보면 됩니다.
해당 코드는 아래와 같습니다.
var distjog = point_distance(newbasex,basey,mx,my);if( distjog < button_r ){    kAction=true;}

Player 의 step 이벤트
if (instance_exists(oJogAir)) {
    // Disable double-click (increases input accuracy)
    device_mouse_dbclick_enable(false);

 var spacer = 16;
 var jogspacer = 50; // 키패드의 반지름
 var rightspacer = 30;
 var button_r = 30; // 버튼의 반지름 
 var basex = spacer + jogspacer;
 var basey = global.screen_height - spacer - jogspacer;
 var newbasex = global.screen_width - spacer - rightspacer; // 첫번째 위치 

    for (var i = 0; i < 8; ++i) {
  var clicked = device_mouse_check_button(i, mb_left);
  var mx = device_mouse_x(i);
  var my = device_mouse_y(i);
  if(clicked && !kLeft && !kRight && !kUp && !kDown){
   var distjog = point_distance(basex,basey,mx,my);
   if(distjog<=jogspacer+10){
    var distjog_left = point_distance(basex-jogspacer,basey,mx,my);
    var distjog_right = point_distance(basex+jogspacer,basey,mx,my);
    var distjog_down = point_distance(basex,basey+jogspacer,mx,my);
    var distjog_up = point_distance(basex,basey-jogspacer,mx,my);
    var minv = min(distjog_left,distjog_right,distjog_down,distjog_up);
    if( abs ( minv - distjog_left ) < 1 ) kLeft = true;
    if( abs ( minv - distjog_right ) < 1 ) kRight = true;
    if( abs ( minv - distjog_down ) < 1 ) kDown = true;
    if( abs ( minv - distjog_up ) < 1 ) kUp = true;
   }
  }
        if( clicked && !kAction){
   var distjog = point_distance(newbasex,basey,mx,my);
   if( distjog < button_r ){
    kAction=true;
   }
  }
    }
}

global.kLeft = kLeft;
global.kRight = kRight;
global.kUp = kUp;
global.kDown = kDown;
global.kJump = kJump;
global.kJumpRelease = kJumpRelease;
global.kAction = kAction;
global.kChange = kChange;


2. Draw GUI

GMS에 있어서 일반적인 좌표는 room을 base로 하게됩니다. 그래서 room의 viewport가 변경되면 출력된 이미지도 같이 변경되는데 이러한 현상을 막고자 Draw GUI가 존재합니다. object에 존재하고 on screen jog를 표현하려면 항상 특정 좌표 위치에 고정되어 출력 할 수 있는 DrawGUI를 이용하였습니다.

JogObject를 intance layer에 올립니다. 위치는 아무곳이나 관계가 없습니다. draw시 재조정 할것이기 때문입니다.

그리고 Draw에 빈 내용으로 채웁니다. Draw시 아무것도 draw 되지 않도록 합니다.
DrawGUI에서 아래 코드가 수행되도록 합니다.
내용은 키가 눌렸을때 down이미지를 추가로 draw 되도록 합니다.

/// @description Insert description here
// You can write your code in this editor
//draw_self();

var spacer = 16;
var jogspacer = 50;
var rightspacer = 30;
var basex = spacer + jogspacer;
var basey = global.screen_height - spacer - jogspacer;
var newbasex = global.screen_width - spacer - rightspacer;

draw_sprite_ext(sJogPad,0,basex,basey,1,1,0,c_white,0.5);
if( global.kLeft ){
 skip_left = 0;
 skip_top = 0;
 draw_sprite_part_ext(sJogPad_down, 0, skip_left, skip_top, jogspacer*2/3, jogspacer*2, basex-sprite_xoffset + skip_left, basey-sprite_yoffset + skip_top, 1, 1, c_white, 0.5);
} else if( global.kRight ){
 skip_left = (jogspacer*2/3)*2;
 skip_top = 0;
 draw_sprite_part_ext(sJogPad_down, 0, skip_left, skip_top, jogspacer*2/3, jogspacer*2, basex-sprite_xoffset + skip_left, basey-sprite_yoffset + skip_top, 1, 1, c_white, 0.5);
} else if( global.kUp ){
 skip_left = 0;
 skip_top = 0;
 draw_sprite_part_ext(sJogPad_down, 0, skip_left, skip_top, jogspacer*2, jogspacer*2/3, basex-sprite_xoffset + skip_left, basey-sprite_yoffset + skip_top, 1, 1, c_white, 0.5);
} else if( global.kDown ){
 skip_left = 0;
 skip_top = (jogspacer*2/3)*2;
 draw_sprite_part_ext(sJogPad_down, 0, skip_left, skip_top, jogspacer*2, jogspacer*2/3, basex-sprite_xoffset + skip_left, basey-sprite_yoffset + skip_top, 1, 1, c_white, 0.5);
}

basex = newbasex;
draw_sprite_ext(sJogAction,0,basex,basey,1,1,0,c_white,0.5);
//draw_sprite_ext(sJogAction,0,basex-60,basey,1,1,0,c_white,0.5);
//draw_sprite_ext(sJogAction,0,basex-120,basey,1,1,0,c_white,0.5);

if( global.kAction ){
 draw_sprite_ext(sJogAction_down,0,basex,basey,1,1,0,c_white,0.5);
}

3. Draw Part

 down이미지를 추가로 draw 되도록 할때 고민해야 할 부분이 있습니다. 발사 버튼의 경우 이미지가 하나라서 문제가 없지만, 방향 jog 같은 경우 4방향이 있습니다. sprite를 한번에 출력하면 안되기 때문에 sprite의 일부를 출력하도록 해야 합니다. 위 함수에서 draw_sprite_part_ext 사용하는 부분의 코드입니다. 원본이미지의 1/3 정도의 이미지를 출력할 수 있게 코드를 넣어봤습니다. jogspacer*2/3 의미가 1/3 정도 되는 양입니다. 왜냐하면 jogspacer이것이 반지름이 되기 때문에 jogspacer*2 이 값이 전체적인 크기가 됩니다. 그리고 jogspacer 은 50이지만 이미지는 90정도 되기때문에 크기가 맞지는 않습니다. 이렇게 구현한 이유는 Jog 이미지가 너무 커질 경우 게임에 방해가 될 가능성이 있어서, 화면에 draw되는 영역 바깥 영역을 일부 터치시에도 동작을 위한 코드입니다.

4. 동작화면

버튼 눌렀을때 녹색이 들어오게 됩니다.


5. 전체 소스


https://drive.google.com/open?id=1Ni39P9ZRA-cQAeh8366ow__FBG5G4_QF






댓글 없음:

댓글 쓰기