레이블이 game maker sudio인 게시물을 표시합니다. 모든 게시물 표시
레이블이 game maker sudio인 게시물을 표시합니다. 모든 게시물 표시

2019년 10월 20일 일요일

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


0. 목차


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

1. 동작 화면

전체적인 화면을 먼저 살펴보도록 하겠습니다. 검은 우주 화면에 별들이 우에서 좌로 스크롤 됩니다. 별들은 속도가 모두 다르며 깜빡이게 될겁니다. 여기에서 별은 2가지 형태로 준비하였습니다. 별의 깜빡임은 sprite animation 입니다. 너무 빠르게 깜빡이면 어색하기 때문에 적당히 조절 합니다.


2. 별 이미지

3*3 크기로 각각 두장을 준비 하였으며 speed는 fps 2 로 설정 하였습니다.


3. 별 Object


코드는 step과 outside Room 이벤트만 있습니다. step에서는 속\speed = savespeed로 속도를 조절하는 부분만 있습니다. 꼭 필요한 코드는 아니지만 별의 속도를 조절하거나 멈추거나 하는 용도로 사용하기 위해서 넣은 코드 입니다. outside Room에서는 별이 스크롤이 완료되면 다시 처음부터 시작 시키기 위한 코드 입니다. x의 위치를 다시 제일 끝으로 위치 시킵니다.



4. 별의 생성

별을 생성시키는 방법은 room 생성 creation code에서 합니다. 아래와 같이 구현하며 대부분 random 값을 많이 이용합니다. 여기에서는 별을 30개 만들었습니다.


var i;
for(i=0;i<30;i++){
 var inst1 = instance_create_layer(irandom(global.screen_width),irandom(global.screen_height),"Instances_BG",oSpaceStar1);
 inst1.direction = 180;
 inst1.image_alpha = random(1);
 inst1.speed = random(1);
 inst1.savespeed = inst1.speed;
 inst1.image_speed = random(1);
 var inst2 = instance_create_layer(irandom(global.screen_width),irandom(global.screen_height),"Instances_BG",oSpaceStar2);
 inst2.direction = 180;
 inst2.image_alpha = random(1);
 inst2.speed = random(1);
 inst2.savespeed = inst2.speed;
 inst2.image_speed = random(1);
}

5. Room의 layer 설정

Layer를 제일 아래 두어야 하며 그것보다 아래 layer는 검은색으로 설정해 두어야 합니다. 이전에 만들었던 배경 스크롤 layer는 눈 표시 모양을 제거하도록 합니다.



6. 소스

모두 완성 되었습니다. 전체 소스는 아래 참고 바랍니다.
https://drive.google.com/open?id=1pANe5VTzgPQOge_qG_JFxphx7p6Cwj85


























2019년 10월 13일 일요일

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


0. 목차


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


1. Timelines 사용, Paths 사용

Boss object에서 timelines, paths를 사용하였습니다. 그렇기 때문에 따로 추가 설명은 필요없을것으로 보이나 여러 paths가 존재할때 세부적으로 조정이 필요한 경우 path layer를 추가하여 확인 수정이 가능한 부분이 있습니다.
단순하게 paths에서 편집을 할때는 정확한 위치 판단이 어려움이 있습니다. 그렇지만 room에 path layer를 아래와 같이 추가합니다.
참고로 게임중에는 path layer가 보이지 않습니다.

2. Player 입장 퇴장

Boss object 구현에 힌트가 있습니다. tilmeline 0 에 control을 off하고 player를 입장 시킵니다. 아래와 같이 구현합니다.
with(oPlayerSky){
 x = -20;
 speed = 3;
 direction = 0;
 controlOff = true;
}

controlOff 이 변수는 oPlayerSky의 control을 off시킵니다.

oPlayerSky step이벤트에 if( controlOff ) return; 조건을 추가 합니다.

그리고 timeline 60 에 player contorl을 on시킵니다. 코드는 아래와 같은 합니다.
with(oPlayerSky){
 speed = 0;
 direction = 0;
 controlOff = false;
}

마지막으로 퇴장은 player 가 boss를 만나서 시간이 흘러 도저히 깨지 못하는 경우가 필요한 경우 사용합니다.

timeline 6000정도 시간은 적절히 조절 하면 됩니다. 아래 정도 코드를 넣으면 화면 밖으로 퇴장합니다.
with(oParPlayer){
 speed = 6;
 direction = 0;
 controlOff = true;
}
global.disable_check_collision = true;

퇴장 이후 처리를 어떻게 할까 궁금해 하시는 분들을 위해 추가 설명을 드리자면, 화면밖에 어떤 event object를 두어서 해당 object와 충돌이 이루어지면 메인 화면으로 간다든지 처리를 하면 됩니다.
이번에는 내용은 간단하나 시작 부분이라 화면 캡쳐가 어려워서 따로 이미지를 올리지 않도록 하겠습니다.

3. 충돌

충돌 처리에 대한 부분이 빠졌습니다. 방식은 어려가지가 있지만, 여기에서는 규칙을 세웠습니다. 총알쪽에서 destroy를 호출 한다. 폭파 이미지는 해당 object에서 구현한다, 개인적인 규칙입니다.
날파리가 와 총알이 만났으니, 우리가 가지고 있는 총알인 oAmmo 코드에 충돌 이벤트를 넣어줍니다.


코드는 global.gun_power는 우리가 발사한 총알의 세기입니다. 체력이 0이 되었을때 destory를 하게 구현을 하였고,

var gun_power = global.gun_power;
with(other){
 hp = hp - gun_power;
 hurt = 1;
 if (hp <= 0) {
                hp = 0;
  instance_destroy()
 }
}

instance_destroy()

fly object에서는 destroy할때 폭파이미지를 그려줍니다.

if( hp==0 ) instance_create_layer(x,y,"Instances_Exp",oExplotion)

instance_destroy()

전체적으로 어느정도 완성된것 같습니다. score라던가 주인공이 맞았을대 그런 부분들이 남아 있지만 그런 부분들은 숙제로 남깁니다.

4. 동작 화면




5. 소스


https://drive.google.com/open?id=1LohlwJIQoHVJw2t7zylnB9QTZOdp-p0A









2019년 10월 6일 일요일

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


0. 목차


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

1. BOSS 소개

boss object는 oBossSky로 이름 지었습니다. 대게의 반쪽을 형상화하여 제작되었습니다. 그리고 주인공이 가까이 있을때는 3발의 탄환을 발사하며 멀리있을 경우 다수의 탄환을 발사하도록 제작하였습니다.

oBossSky의 step 코드 입니다. state가 일정 시간이 흐르면 need_to_change_state flag가 true가 되면서 다음 state로 변환하게 하는 state machine구조를 가지고 있습니다.
if( need_to_change_state ){
 // change state
 // last state
 // change the state as the last state
 switch(state){
  case SM_WALK:
  case SM_WLKR:
  case SM_WLKL:
   state = choose(SM_WALK,SM_WALK,SM_FIRE);
  break;
  case SM_FIREWAIT:
  case SM_FIRE:
   state = SM_WALK;
  break;
 }
 
 // enter state for init code
 speed = 0
 direction = 0
 state_count = 0
 switch(state){
  case SM_WALK:
   state_count_max = 90;
   if( state_save!=SM_WALK && state_save != -1 ){
    state = state_save;
   }
   if( instance_exists(oPlayerSky) ){
    if( oPlayerSky.x > x ){
     facing=-1;
    }else{
     facing=1;
    }
   }
  break;
  case SM_JUMP:
   state_count_max = 120;
  break;
  case SM_STAY:
   vx=0;
   state_count_max = 30;
  break;
  case SM_FIRE:
   state_save = prev_state;
   state_count_max = 10;
   vx=0;
  break;
 }
 
 need_to_change_state = false
}



// action
state_count++;

if (state==SM_STAY) {
}

if (state==SM_FIRE) {
 if( state_count > state_count_max ){
  if( instance_exists(oPlayerSky) ){
   var obj;
   if(distance_to_object(oPlayerSky)<50){
    obj = instance_create_layer(x,y-10,"Instances_Ammo",oEBulletTriSp);
   }else{
    obj = instance_create_layer(x,y-10,"Instances_Ammo",oEBulletMulti);
   }
   var dir = 0;
   if( facing == 1 ) dir = 180;
   else dir = 0;
   var gp = gun_power;
   with(obj){
    gun_power = gp;
    speed     = 5;
    direction = dir;
   }
   state_count = 0;
   state=SM_FIREWAIT;
  }
 }
}

이때 boss의 탄환은 SM_FIRE 에서 구현을 하였으며, 주인공과 거리가 50 이상이냐 이하 이냐에 따라서 다른 총알을 생성 하도록 구현하였습니다.

   if(distance_to_object(oPlayerSky)<50){
    obj = instance_create_layer(x,y-10,"Instances_Ammo",oEBulletTriSp);
   }else{
    obj = instance_create_layer(x,y-10,"Instances_Ammo",oEBulletMulti);
   }

2. Bullet object

oEBulletMulti, oEBulletTriSp 두개 모두 구현 방식은 동일합니다.
핵심은 alarm[0] 이벤트이며 코드는 아래와 같습니다. -4 ~ 4 까지 9개의 탄환이 발사 됩니다. 그리고 자기 자신은 소멸합니다.

var i;
for(i=-4;i<=4;i++){
 var obj = instance_create_layer(x,y,"Instances_Ammo",oEBullet);
 var spd = speed;
 var dir = direction;
 //var gp = gun_power;
 with(obj){
  //gun_power = gp;
  speed     = spd;
  direction = dir+10*i;
  image_angle = dir+10*i-90;
 }
}

instance_destroy()

3. Boss 출동

object를 모두 만들었으면, 해당 object를 실행시키는 곳을 고민해야합니다.

그것은 바로 timelines에 넣으면 됩니다.
boss니 미지막에 넣으면 될것입니다. moment 2200을 마지막으로 두었기 때문에 그곳에 boss를 생성하는 코드를 넣습니다.

var inst = instance_create_layer(0,0,"Instances_Boss",oBossSky);
with(inst){
 path_start(pSky_B1_enter, 1, path_action_stop, true);
 pathinfo[0]=pSky_B1_move1;
 pathinfo[1]=pSky_B1_move1;
 pathinfo[2]=pSky_B1_move2;
}

이때 단순히 boss obeject를 넣는것이 아니라 boss도 어떤 움직임을 따라서 움직여야 합니다. 이것을 위해서 두개의 path를 만들었고 입장을 위해서도 한개의 path를 만들었습니다.
입장용 입니다. pSky_B1_enter


위 두개의 path는 random으로 선택되어집니다.

선택되는 코드는 oBossSky 의 path ended 이벤트에서 아래와 같이 구현하였습니다.

len = array_length_1d(pathinfo);

path_start(pathinfo[irandom(len-1)], 1, path_action_stop, false);

주의할점음 두개의 path가 입장점으로 돌아오도록 제작하여야 합니다. 그래야 랜덤으로 돌았을때 반복적으로 호출해도 동작이 어색하지 않게 됩니다.

4. 전체 코드


https://drive.google.com/open?id=1uJsjbFqMaDRo-uf7goB21VfuR23DDSFh



2019년 9월 29일 일요일

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


0. 목차


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

1. 적 object

적 이미지를 준비합니다. 이름은 Fly로 지었고 이미지의 출처는 Free이미지였는데 정확한 URL은 잘모르겠지만, 직접 그린 이미지는 아닙니다. 날파리 처럼 날개가 파닥 거리게 하였습니다.

object 이름은 oEnemyFlySky로 이름 지었습니다. 해당 object는 정해놓은 길을 따라서 파닥거리며 날라오게 할것입니다. 정해놓은길을 사용하기 때문에 해당 object의 x,y좌표는 제어할 필요가 없습니다. 그래서 step에서 좌표 제어하는 부분들을 모두 필요없습니다.

2. Timelines

Timelines이라는것은 일정시간 후에 어떤 동작이 일어나도록 미리 동작을 정해 놓는것을 의미합니다.

2.1 TimeLine의 호출

Room의 Tileline시작은 oSkyTimelineMaker라는 object를 만들어 두었습니다. object는 내용은 비어있고 , room 에서 더블클릭하여 instance Creation code에서 timeline을 설정 하도록 만들었습니다.

2.2 TimeLine 호출 내용

timeline_index = tlSkyLvl1;   timeline 이름을 설정 합니다.
timeline_position = 0;         timeline 의 시작 위치를 알립니다.
timeline_running = true;      timeline 이 시작됨을 알립니다.

tlSkyLvl1의 내용을 살펴보면 아래와 같습니다.

대부분 같은 내용입니다. Moment는 시간을 의미합니다. 각각 60,200,400... step이 되었을때 어떤 동작을 설정 할 수 있습니다. 물론 해당 시간도 조정이 가능합니다.
내용을 살펴보면 해당 Moment에 path를 random으로 선택합니다. path는 다음에 설명 하도록 하겠습니다.
그리고 instance를 Instances_Enemies레이어에 생성 하게 됩니다. 이때 path를 설정하고 alarm[0]에 일정 시간 후에 path를 동작하도록 코드를 만들어 두었습니다. 
var i;
var pathinfo_l = choose(pSky_P1,pSky_P1F,pSky_P2,pSky_P2F);
for(i=0;i<3;i++){
var inst = instance_create_layer(0,0,"Instances_Enemies",oEnemyFlySky);
with(inst){
alarm[0]=i*60; pathinfo = pathinfo_l;
}
}

oEnemyFlySky Alarm 0 event에 아래 코드를 추가 하였습니다. Create 코드에는 visible = false로 되어있으며 아래와 같이 시작하는 시점에  visible이 true가 되도록 설정 하였습니다.
path_start(pathinfo, 1.5, path_action_stop, true);
visible = true;

이렇게 하는 이유는 특정 이벤트 후에 여러개의 object를 생성할때 붙어서 적들이 생성되면 모양새가 좋지 않습니다.

3. Paths

pSky_P1,pSky_P1F,pSky_P2,pSky_P2F 정의 된것은 path입니다.
path는 점들을 찍어서 길을 설정할때 만들고 object를 길을 따라 움직이게 만드는것이 가능합니다. 그것이  위에서 설명한 path_start 함수 입니다.
그리고 path를 만드는것은 paths에서 처리합니다.

4. 적 탄환

Fly 적이 그냥 날아가면 재미가 없습니다. 날라가면서 총알도 발사해야 합니다. 이것은 enemy step 이벤트에서 일정시간에 fire를 구현하면 됩니다. 대략 아래와 같은 코드로 구현가능합니다.

  var obj = instance_create_layer(x,y,"Instances_Ammo",oEBullet);
  var dir = 0;
  if( instance_exists(oPlayerSky)){
   dir = point_direction(x, y, oPlayerSky.x, oPlayerSky.y);
   var gp = gun_power;
   with(obj){
    gun_power = gp;
    speed     = 3;
    direction = dir;
   }
  }
  state_count = 0;
  vx = 0;
  state=SM_FIREWAIT;

위 코드는 주인공 위치로 총알을 발사하게 하는 코드입니다.

5. 완성된 모습


6. 전체 소스

https://drive.google.com/open?id=1-QVdcSBTHKJETbTkv5GyZ3w49-9yZBa3









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






2019년 9월 15일 일요일

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



0. 목차


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


1. 주인공 object

1.1 비행기 및 총알 그리기

옆에서 보는 비행기를 3가지 준비를 합니다. 가만히 있을때 위쪽으로 올라갈때와 내려갈때 각각 다른 이미지를 사용하면 보기에 좋습니다. 또한 뒤쪽의 엔진의 불꽃의 경우 따로 준비를 하였습니다. 불꽃의 경우 이미지 모든 이미지에 추가하게 되면 전체적인 이미지 갯수가 증가하기 때문입니다.
그럼, 그린 그림을 감상 하시죠




위로 올라갈때


아래로 내려올때


엔진 불꽃

주인공 총알

오늘의 작업 전체 이미지




1.2 Player 움직임을 위한 step 함수 구현

단순히 x , y 좌표값을 더 하는 것만으로도 쉽게 구현이 가능하지만, 여기에서는 vx(가로축의 속도) , vy (세로축의 속도) 두가지 값으로 접근을 합니다. vx가 증가해서 vxMax 까지 다다르게되고,  vy는 vyMax까지 다다르게 됩니다. 즉 이렇게 구현하는것은 속도가 한번에 급격하게 오르는것을 방지하기 위한 목적입니다. (자연스러운 움직임을 만들 수 있습니다.) 예를 들어 x축으로 +3 만큼 증가하고 있다고 쳤을때 다음 step에서 갑작이 -3으로 변경된다면 화면이 부자연 스러움이 생깁니다. 따라서 vx , vy를 두고 실제 x, y 좌표값이 결정되게 됩니다.


airAccel    = 0.7;airFric     = 0.3;vxMax       = 3;vyMax       = 3;
// Velocityvx = 0;vy = 0;
Create에는 위와 같은 변수들이 초기화 됩니다. airAccel은 가속도를 의미합니다. step마다 더 해주는 가속도를 의미합니다. airFric는 키를 안누를때 정지시키기 위한 마찰력을 의미합니다.
게임은 자동으로 스크롤 되고 있기때문에 x 속도는 0이라고 봐야 합니다.

if(kRight){ vx = Approach(vx, vxMax, airAccel);}if(kLeft){ vx = Approach(vx, -vxMax, airAccel);}if(kUp){ vy = Approach(vy, -vyMax, airAccel);}if(kDown){ vy = Approach(vy, vyMax, airAccel);}
// Frictionif (!kRight && !kLeft) {    vx = Approach(vx, 0, airFric);if (!kUp && !kDown) {    vy = Approach(vy, 0, airFric);
위 내용은 step에서 처리하는 내용입니다. k가 붙은 kRight, kLeft 등의 변수는 키를 누르면 true가 되는 변수 입니다. 다른 분이 작성한 Approach 라는 script가 있는데 이것은 현재 속도와 max값을 넣어둘때 이번에 들어가는 step(증감 하는값)을 고려해서 최종 속도를 계산하는 함수 입니다.
// Friction 부분의 코드는 마찰력을 고려하여 손을 떼었을때 움직임이 0이 되도록 처리하는 함수 입니다.

endstep에서는 vx vy를 고려하여 최종 x, y 값을 계산해 냅니다. 
if (vy < 1 && vy > -1){ // Do nothing}else{ if( vy > 0 ){     repeat(abs(vy)) {         if (y<global.screen_height-10)             y += sign(vy);         else             break;     } }else{     repeat(abs(vy)) {         if (y>+10)             y += sign(vy);         else             break;     } }}
위 코드는 endstep코드인데 y축에 대한 정보입니다. 동일하게 x축에 대한 처리도 있지만 전체 코드에서 확인 부탁드립니다. 
"Do nothing" 코드는 속도가 1 미만인 경우 좌표를 더하거나 빼거나 하지 않고 이전 좌표를 유지하게 됩니다. repeat(abs(vy)) { 이 부분은 만약 최대 속도가 3이라면 +3, -3일때 모두abs(vy)는 3이 넘어옵니다. y += sign(vy); 여기에서 1씩 더 해주거나 빼주려고 abs(절대값)을 취한 것 입니다. 
if (y<global.screen_height-10) , if (y>+10) 여기 코드는 위 아래 못 움직이는 처리를 위해서 사용하는 조건 입니다.

1.3 Player에 엔진 불꽃 추가하기


아래 코드에서 draw_sprite_ext(sEngineFire, subimage_index, x-32, y, facing * xscale, yscale, 0, mapc, image_alpha); 이 부분이 불꽃을 그리게 됩니다.
다만 주의할점은 subimage_index 라는 다른 변수를 만들고 인덱스를 따로 관리해 주어야 합니다. 여기에서는 적당하게 subimage_index += 0.2; 더해서 적당한 속도를 구현하였습니다.

var mapc = c_white;
var xscale = 1;
var yscale = 1;
var facing = 1;

if( up ){
 sprite_index = sPlayerSkyShipUp;
}else if( down ){
 sprite_index = sPlayerSkyShipDown;
}else{
 sprite_index = sPlayerSkyShip;
}

subimage_index += 0.2;

draw_sprite_ext(sEngineFire, subimage_index, x-32, y, facing * xscale, yscale, 0, mapc, image_alpha);
draw_sprite_ext(sprite_index, -1, x, y, facing * xscale, yscale, 0, mapc, image_alpha);

1.3 주인공이 발사하는 총알 object

해당 object는 특이점이 없습니다. Outside Room 이벤트에서 instance_destroy();만 해줍니다. 안그러면 총알을 발사할때마다 object가 늘어가게 됩니다.

주인공의 총알을 발사는 아래 Player 쪽에서 구현을 합니다.
kAction키가 들어오면 oAmmo object를 만들고 속도와 방향을 정해서 발사를 하게 됩니다.

if (kAction) { // Fire Bullet var bdir = 0; if( facing == 1 ) bdir = 0; else bdir = 180; var ct = oAmmo; with (instance_create_layer(x, y,"Instances_Ammo", ct)) { y = y + 6; speed     = 8; direction = bdir; }}

1.4 전체 소스

GMS2 소스입니다.
https://drive.google.com/open?id=1WqS5lQEFM90j5YQQKL3JPpPq6PzOOHfB

1.5 실행 예제



여기에서 사용된 이미지는 자유롭게 사용하셔도 됩니다. (출처를 기록하시면 더 좋습니다.)













2019년 9월 8일 일요일

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


(GameMaker Studio 2) GMS2로 가로형 슈팅 게임을 제작 하도록 하겠습니다. 아래 목차로 진행 예정이며 몇개의 글로 이루어질 예정입니다.

0. 목차


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

1. 슈팅 게임의 기초 - 배경 스크롤

일명 뺑뺑이 스크롤이라고 불립니다. 기본적인 동작은 뺑뺑이로 사용할 배경의 제작이 필요합니다. 일반적인 이미지를 가지고 편집을 하려면 Aseprite SW의 tilemode를 이용하여 제작하면 됩니다.

제작은 518*320 크기의 room 크기의 게임을 제작 하도록 하겠습니다.
만약 이련경우 518*320크기의 배경을 제작해서 배경 스크롤을 하게된다면 메모리 소모가 크게 됩니다. 하늘 배경이라고 한다면 아래쪽은 단일 파란색이므로 좀 더 작은 크기로 제작이 가능합니다.
120*80 크기의 배경을 만들어 보겠습니다. 만드는 방법은 Aseprite 툴을 사용하지 않아도 됩니다. 아래와 같이 대충 구름을 만듭니다. 큰 구름을 만들고 주변에 작은 구름을 붙여서 자연스럽게 만들면 됩니다.



그리고 가로 스크롤이므로 tile 모드 X Axis를 켜놓고 구름을 만듭니다. 타일모드를 켜게되면 아래와 같은 형태가 나오는데, 타일 모드가 없는 에디터라면 수동으로 붙여넣기 해서 미리 확인해보면 되고, 어색한 부분이 있으면 수정해줍니다.



수정된 그림입니다.



타일 모드를off 했을때 원본 이미지 입니다.



GMS2에서 스크롤을 해보겠습니다.

sprite를 추가합니다.



room Layers의 Backgroud에 sBGSky를 추가합니다. 그리고 horizental Tile을 check해서 tile형태가 되도록 합니다.

아래쪽에는 약간 허전합니다 이부분은 layer를 아래쪽에 하나 더 추가하여 배경색만 파란색으로 추가합니다. 이때 추가할때 스포이드로 동일색이 나타나도록 맞춰줍니다.

그리고나서 room0에 Backgrounds layer를 하나 더 추가해서 아래쪽으로 이동시킵니다. 그러면 아래와 같이 흰배경이 나타나는데 이전에 저장된 색으로 배경을 설정합니다.

설정하고, Background layer의 Horizental Speed 에 -1을 적어주면 스크롤이 됩니다. 해당값은 한 step에 얼마나 이미지를 이동시킬지를 결정하는 값입니다.

좀더 빠르게 스크롤을 시키고 싶다면 해당 값을 증가시키면 됩니다.

2. 다중 배경 스크롤

여러장의 이미지를 넣어서 배경 스크롤 시키는 것입니다. 위에서는 한장만 넣었습니다만, 아래쪽에 산이미지를 넣어 보겠습니다. 이번에도 그림을 그립니다. 동일하게 80*120에 tile모드로 그려 봅니다.


sBGLand라는 이름으로 불러옵니다.


Layer를 하나더 추가시켜보면 위쪽에 있어서 땅같지가 않습니다. 따라서 Y Offset을 설정을 합니다. 대략 200 정도가 적당합니다. 그리고 Horizontal Tile 선택 합니다.


실행시켜보면 하늘을 빨리 움직이지만 땅은 움직이지 않는 어색한 사이가 되어 버립니다. 그래서 여기에서 땅은 하늘 보다 가까우니  Horizontal speed를 -2가 되도록 넣어줍니다. 스크롤 2개를 만들어봤습니다만, 더 많은 layer를 추가해서 스크롤 시킬 수 있으며 스크롤값은 소수점도 가능합니다.

이번 시간에는 스크롤 관련해서 알아보았고 다음에는 주인공 움직임 관련해서 구현하도록 하겠습니다.


2019년 9월 1일 일요일

GMS2 게임에서 맞았을때 효과, sprite draw를 특정색으로 변하게 하는 효과 (Drawing a sprite as one color , Hittng effect in GMS2)


게임에서 맞았을때 효과 ( sprite draw를 특정색으로 변하게 하는 효과)


어떤 효과인지 아래를 참고 바랍니다.
주인공이 적을 맞혔을때 몸이 흰색으로 변하는 효과입니다.


블랜더 모드

위 동영상 예제에서는 주인공의 경우 빨간색으로 변경되는 화면을 볼 수 있습니다. 해당처리는 draw시 블랜더 모드를 사용하고 있습니다만, 전체적으로 빨간색으로 변경하지는 못합니다.

var mapc = c_white;
if(hurtStart>0){
 mapc = c_red;
}

draw_sprite_ext(sprite_index, image_index, x, y, facing * xscale, yscale, 0, mapc, image_alpha);
draw_sprite_ext(guntype_sprite_index, 0, x, y+3+bo, facing * xscale, yscale, 0, mapc, image_alpha);




구현은 간단

draw전 gpu_set_fog 함수를 이용합니다. 함수의 설명은 아래와 같습니다. start, end를 2d게임에서 0을 전달함으로서 해당 sprite를 안개로 인식하게 합니다.

gpu_set_fog(enable, color, start, end)
   enable : 사용   color : 안개 색상   start : 카메라의 위치를 중심으로 안개를 적용할 시작위치(투명)   end :  카메라의 위치를 중심으로 안개를 적용할 끝위치(불투명)


구현 소스


if( hurt==1 ){
 gpu_set_fog(1,c_white,0,0);
 draw_sprite_ext(sprite_index, image_index, x, y, facing * 1, 1, image_angle, c_white, image_alpha);
 gpu_set_fog(0,0,0,0);
}else{
 draw_sprite_ext(sprite_index, image_index, x, y, facing * 1, 1, image_angle, c_white, image_alpha);
}













2019년 8월 25일 일요일

GMS2 적 움직임 만들기 state machine이용 2 : 소주제 BOSS 움직임 ( enemy movement with state machine 2 : BOSS )


이전 글에 관한 내용

이전 글에서는 간단하게 적 움직임을 state machine에 어떻게 넣는가에 대해서 알아 보았습니다.

https://swlock.blogspot.com/2019/08/gms2-state-machine-enemy-movement-with.html

이번에는 좀 더 복잡하게 움직이는 움직임에 대해서 알아볼 차례입니다.


BOSS

게임에서의 보스는 항상 어려운 상대이고 도전 욕구를 불러 일으키는 상대입니다. 그렇기 때문에 게임에 있어서 보스는 무작정 절대 깰수 없는 상태로 만들면 안됩니다. 보스 움직임을 만들때 주의해야할 점은 보스는 허점을 만들어야 하고, 패턴을 만들어 두면서 사용자가 그 허점과 패턴을 리트라이 하면서 찾아내는 즐거움을 주어야 합니다.
그리고 일반적으로 강한 공격은 준비 동작(모션) 시간을 주는것이 바람직 합니다.

여기에서 구현하는 BOSS

BOSS는 32*64 이미지로 Crab을 모티브로 만들었으며, 대게를 반으로 잘라서 먹은것과 개구리 눈 부위를 상상하며 Pixel을 찍어서 직접 만들었습니다.

공격전에는 한쪽 발을 들면서 큰 공격을 하며, 상대방이 멀리 있으면 많은 수의 포탄을 날리고 가까우면 3발의 포탄을 날립니다.
따라서 이 BOSS의 상대하는 방법은 몸쪽에 가까이 붙으면서 상대하면 좋은 파해법이 되도록 만들었습니다.

ACTION 코드 비교

이전글에서 만든 object와 비교해보겠습니다. 기존 object는 좌우로 움직으며 총알만 쏩니다. 또하나의 특징은 마지막에 플랫폼에서 떨어지지 않도록 구현하였습니다.
그 부분은 아래 코드의 dLeft, dRight 조건입니다. 해당 조건일때 방향을 바꾸도록 구현이 되있습니다.
step 코드 일부분
// action
state_count++;
if(state==SM_WLKL || state==SM_WLKR){
 // Left 
 if (state==SM_WLKL) {
     facing = -1;
     // Apply acceleration left
     if (vx > 0)
         vx = Approach(vx, 0, tempFric);   
     vx = Approach(vx, -vxMax, tempAccel);
  if( cLeft || dLeft) {
   vx = 0;
   state=SM_WLKR;
  }
 // Right
 } else if (state==SM_WLKR) {
     facing = 1;
     // Apply acceleration right
     if (vx < 0)
         vx = Approach(vx, 0, tempFric);   
     vx = Approach(vx, vxMax, tempAccel);
  if( cRight || dRight ) {
   vx = 0;
   state=SM_WLKL;
  }
 }
 
}

BOSS의 경우 좌우로 떨어질수 있도록 합니다. 다만 벽에 닿는 조건은 그대로 유지가 필요합니다. 아래 코드에서 살펴보면 if( cLeft ) need_to_change_state = true; if( cRight ) need_to_change_state = true; 와 같은 조건으로 벽이 있다면, state를 변하게 만듭니다.

step코드의 일부분
// action
state_count++;
if(state==SM_WLKL || state==SM_WLKR){
 // Left 
 if (state==SM_WLKL) {
     facing = 1;
     // Apply acceleration left
     if (vx > 0)
         vx = Approach(vx, 0, tempFric);   
     vx = Approach(vx, -vxMax, tempAccel);
  if( cLeft ) need_to_change_state = true;
 // Right
 } else if (state==SM_WLKR) {
     facing = -1;
     // Apply acceleration right
     if (vx < 0)
         vx = Approach(vx, 0, tempFric);   
     vx = Approach(vx, vxMax, tempAccel);
  if( cRight ) need_to_change_state = true;
 }
 
}

FIRE코드 비교

총알을 발사하는 코드를 비교해보겠습니다. FIRE State일때 총알을 만들고 state를 firewait 상태로 만듭니다. 이것은 총을 쏜다음 총쏜 후 대기 animation frame을 넣기 위한 코드입니다.

if (state==SM_FIRE) {
 if( state_count > state_count_max ){
  var obj = instance_create_layer(x,y,"Instances",oEBullet);
  var dir = 0;
  if( facing == -1 ) dir = 180;
  else dir = 0; 
  with(obj){
   speed     = 5;
   direction = dir;
  }
  state_count = 0;
  vx = 0;
  state=SM_FIREWAIT;
 }
}

BOSS의 fire는 oPlayer(주인공 객체인데 여기에서는 샘플로 임의의 위치에 배치 하였습니다.) 가 가까이 있다면 3발의 총알을 발사하는 instance를 생성하고 거리가 멀다면 여러발을 발사하는 instance를 생성합니다. 그 부분은 다음 코드 입니다. obj = instance_create_layer(x,y-10,"Instances",oEBulletMulti);

if (state==SM_FIRE) {
 if( state_count > state_count_max ){
  var obj;
  if(distance_to_object(oPlayer)<50){
   obj = instance_create_layer(x,y-10,"Instances",oEBulletTriSp);
  }else{
   obj = instance_create_layer(x,y-10,"Instances",oEBulletMulti);
  }
  var dir = 0;
  if( facing == 1 ) dir = 180;
  else dir = 0;
  var gp = gun_power;
  with(obj){
   gun_power = gp;
   speed     = 5;
   direction = dir;
  }
  state_count = 0;
  state=SM_FIREWAIT;
 }
}

실제 동작 예제






소스 코드


https://drive.google.com/open?id=1KGjIznlC4OFtfu1AA32T8-aHVgDysG42