본문 바로가기
GameMaker강좌[GM8]/3D기능강좌

[게임메이커강좌-3D 강좌]간단한 FPS게임 만들기-2-게임 구성

by 타락카얀 2013. 5. 31.
728x90

 

 

 

이전 강좌에서 시점을 설정해보았지요.
이번엔 점프 및 이동에 대해 구성해보도록 해요.
음. 이번 강좌는 좀 더 복잡한 강좌가 될듯하네요.

 

 

(▲강좌에서 만들게 될 FPS 게임)

 

이번 강좌에선 위와 같은 블럭과 그 블럭에 점프해 올라가는 것까지 해보겠습니다.

 

 

 

 


◈ 블럭의 기본 설정

   (obj_block,obj_wall_h,obj_wall_v,obj_bl32)

 


우선 올라갈 수 있는 블럭 오브젝트(obj_bl32)를 하나 만듭니다.

 

 

(▲올라갈 수 있는 블럭 오브젝트)

 

스프라이트와 parent를 지정해주고, Create 이벤트에 아래와 같이 추가합니다.

 

 
     ★ obj_bl32 - Create 이벤트

     z=0;
     ztop=32;//높이



이것은 블럭의 높이를 지정해줌으로써, 플레이어가 그 높이만큼 뛰었을 때 올라갈 수 있게 하기 위해서입니다.

그리고 그 높이가 되지 못하면 못올라가며, 더불어 이동도 못하게할 수 있습니다.

 

 

(▲블럭의 높이)


이 블럭은 플레이어가 통과해야되는 오브젝트이므로, Solid는 체크해제 해야합니다.
Draw 이벤트를 추가하여, 아래와 같이 표시할 모델을 추가합시다.

 

 
     ★ obj_b32 - Draw 이벤트

     draw_set_color(c_white);
     draw_set_alpha(1);
     texid=background_get_texture(back_floor);


     d3d_transform_add_translation(x,y,z);
     d3d_draw_block(0,0,0,32,32,32,texid,1,1);//블럭 기본 모델
     d3d_transform_set_identity();



그리고 이전에 만들었던 블럭들의 Create 이벤트에도 똑같이 추가합니다.

 

 
     ★ obj_block - Create 이벤트

     z=0;
     ztop=32;

     ★ obj_wall_h - Create 이벤트


     z=0;
     ztop=320;

     ★ obj_wall_v - Create 이벤트


     z=0;
     ztop=320;


 

이렇게 해야 블럭의 z축이나 높이를 다른 오브젝트 또는 카메라(플레이어)에서 참조를 할 수 있습니다.


 

 

 

 


◈ 이동(obj_cam)

 


다음은 카메라 오브젝트(obj_cam)로 이동해서 이동하는 것을 설정해봅시다.

Step 이벤트를 추가하고 아래와 같이 설정합니다.

 

 
     obj_cam - Step 이벤트

     spd=2;//이동 속도
     dir=cam_zang;//방향
     key_pressed=0;//키 입력정보
     if keyboard_check(ord("A")){dir=cam_zang+90;
     x+=lengthdir_x(spd,dir)*place_free(x+lengthdir_x(spd,dir),y);
     y+=lengthdir_y(spd,dir)*place_free(x,y+lengthdir_y(spd,dir));key_pressed=1;}

     if keyboard_check(ord("D")){dir=cam_zang-90;
     x+=lengthdir_x(spd,dir)*place_free(x+lengthdir_x(spd,dir),y);
     y+=lengthdir_y(spd,dir)*place_free(x,y+lengthdir_y(spd,dir));key_pressed=1;}

     if keyboard_check(ord("W")){dir=cam_zang;
     x+=lengthdir_x(spd,dir)*place_free(x+lengthdir_x(spd,dir),y);
     y+=lengthdir_y(spd,dir)*place_free(x,y+lengthdir_y(spd,dir));key_pressed=1;}

     if keyboard_check(ord("S")){dir=cam_zang+180;
     x+=lengthdir_x(spd,dir)*place_free(x+lengthdir_x(spd,dir),y);
     y+=lengthdir_y(spd,dir)*place_free(x,y+lengthdir_y(spd,dir));key_pressed=1;}



키이벤트에서 설정해도 되지만, 실행하는데 이벤트 우선 순위를 강제적으로 설정하기 위해선

키이벤트의 코드 역시 알고 있어야 합니다. 키보드 입력에 관한 코드는 아래와 같은 것이 있습니다.

     keyboard_check(key) : 키를 계속 입력중일때
     keyboard_check_direct(key) : 키를 계속 입력중일 때(이것은 위에것과 같으나,

                                      게임내에서 입력한 것만 반응)
     keyboard_check_pressed(key) : 키를 눌렀을 때
     keyboard_check_released(key) : 키를 눌렀다 떼었을 때

키코드의 문자 자판은 ord(chr)를 이용하여, ord("A")식으로 사용합니다.
그외의 자판은 vk_코드로 사용하게 됩니다(예 : 왼쪽 방향키 vk_left)






◈ 중력과 점프(obj_cam)

 


다음은 중력과 점프설정에 대해 알아봅시다.

카메라 오브젝트의 Create 이벤트에 아래와 같은 변수를 추가합니다.

 

 
     ★ obj_cam - Create 이벤트



     ztop=32; //높이

     zspeed=0; //중력 가속도
     jump=0; //점프 스위치



그리고 Step 이벤트로 이동해서 아래와 같은 코드를 키입력 설정있는 곳 아래 쪽에 추가합니다.

[참고] &&and, ||or와 같습니다.

 

 
     ★ obj_cam - Step 이벤트

     zspeed-=0.3;//중력
     if zspeed<-8{zspeed=-8;}//떨어지는 속도 제한

     if keyboard_check_pressed(vk_space){if jump=0{zspeed=6;jump=1;}}//점프

     //------------블럭 체크---------------
     zmin=0;

     n=instance_number(obj_block);
     for(k=0;k<n;k+=1;){it=instance_find(obj_block,k);
     aa=instance_place(x,y,it);

     //모든 블럭을 검색하여 플레이어와 충돌하는 블럭(2D상으로)을 체크함

     if instance_exists(aa){
     if z>=aa.z+aa.ztop{if zmin<=aa.z+aa.ztop{zmin=aa.z+aa.ztop;}}//z좌표의 최소값

     i=1;repeat(ztop){if aa.z+aa.ztop>z+i && aa.z<z+i{
     x=xprevious;y=yprevious;
     if key_pressed=1{
     x+=lengthdir_x(spd,dir)*!(place_meeting(x+lengthdir_x(spd,dir),y,aa));
     y+=lengthdir_y(spd,dir)*!(place_meeting(x,y+lengthdir_y(spd,dir),aa));
     }//키를 눌렀을 때 이동 경로에 블럭과 충돌한다면 이동 가능한쪽으로 움직임
     break;}i+=1;}//블럭과의 충돌

     i=0;repeat(ceil(abs(zspeed))+1){
     if zspeed>0{if (aa.z>z+ztop+i-1 && aa.z<=z+ztop+i){z=aa.z-ztop;zspeed=-0.3;break;}}
     if z+i-1>=aa.z+aa.ztop && z+i>=aa.z+aa.ztop-2{

     if zmin<=aa.z+aa.ztop{zmin=aa.z+aa.ztop;break;}}
     if zspeed<0{i-=1;}else{i+=1;}}//z값을 재설정함
     }
     }
     //-----------블럭 체크----------------

     z+=zspeed;//중력
     if z<=zmin{z=zmin;zspeed=0;jump=0;}



점프에 관련된 중요한 부분이에요.
jump변수가 0이면 점프를 하게 되지요.
하지만 블럭이 위에 있으면 멈춰야하고, 점프 후 아래쪽에 아무것도 없으면 계속 떨어져야 합니다.
어디까지 떨어져야 하는 가를 기준으로 하는 것이 zmin이라는 변수입니다.
만약 점프 후 플레이어의 z좌표가 블럭 위, 그러니까 블럭의 z+ztop보다 크다면 zmin은 그 높이값으로 설정되어
플레이어의 z좌표 최소값은 zmin이 됩니다.

블럭을 룸에 배치하고 점프 후 올라가는지 확인해봅시다.

 

 

(▲블럭 위에 올라 탐!)


내친 김에 총알도 발사하는 시스템을 만들어 볼까요.

 

 

 

 

 


◈ 총알 발사(obj_bullet)

 


총알로 사용할 오브젝트(obj_bullet)를 하나 만들어요. 그리고 Sprite에 마스크 용으로 사용할 스프라이트 이미지를

지정해주도록 해요.

 

 

(▲총알 오브젝트)


Create 이벤트를 추가하고, 아래와 같이 필요한 변수들을 선언합니다.

 

 
     ★ obj_bullet - Create 이벤트

     z=0;//z좌표
     ydirection=0;//상하 회전. 좌우 회전은 direction을 사용.
     pspeed=0;//총알의 속도

     alarm[0]=360;//자동으로 파기되는 시간

     delay=5;//눈에 보이는 시간



총알이 어떤 이유에든 룸 상에 생성된 뒤 오랜 시간동안 남아있다면 게임을 플레이하는데 지장이

생깁니다.
그래서 필요 없는 인스턴스들은 자동적으로 파기 시킬 필요가 있지요.
이러한 이벤트는 알람을 이용하면 간단하게 설정할 수 있습니다.
Create 이벤트에 설정했던 Alarm 이벤트를 추가하고, 아래와 같이 필요없어진 총알을 파기하도록

합니다.

 

 
     ★ obj_bullet - Alarm0 이벤트

     instance_destroy();



다음은 Draw 이벤트를 추가한 후, 아래와 같이 총알의 모양을 표시하도록 합니다.

 

 
     ★ obj_bullet - Draw 이벤트

     if delay>0{delay-=1;}//보이는 시간
     else{
     d3d_transform_add_rotation_y(ydirection);//상하 회전
     d3d_transform_add_rotation_z(direction);//좌우 회전
     d3d_transform_add_translation(x,y,z);
     draw_rectangle_color(-3,-0.1,1,0.1,c_red,c_yellow,c_yellow,c_red,0);//모양
     d3d_transform_set_identity();
     }



일정시간(delay가 0일때)이 되면 충알이 보이도록 하는 것입니다.
물론 이것이 필요 없다면 그냥 모양만 표시하시면 됩니다.

다음은 총알을 발사한후 이동 시켜야겠지요.
Step 이벤트를 추가한 후, 아래와 같이 설정합니다.

 

 
     ★ obj_bullet - Step 이벤트

     x+=lengthdir_x(lengthdir_x(pspeed,ydirection),direction);
     y+=lengthdir_y(lengthdir_x(pspeed,ydirection),direction);
     z+=-lengthdir_y(pspeed,ydirection);



이렇게 하면 상하 회전값과 좌우 회전값에 따라 이동하게 됩니다.

이제 게임 상에 필요 없을 상황을 고려해야합니다.
예를 들면 지면 아래에 총알이 있다던지 아니면 보이지 않을 만큼 총알이 높이 있다던가, 또는 총알이

룸밖으로 나갔던가하는 상황말입니다.

이러한 것을 설정해봅시다.
Step 이벤에 아래와 같이 추가합니다.

 

 
     ★ obj_bullet - Step 이벤트

     if z<=0{instance_destroy();}//총알이 지면 아래 있을 때는 총알을 파기함.
     if z>1000{instance_destroy();}//총알이 너무 높이 있을 때는 총알을 파기함.



그리고 룸 밖에 있을 때도 생각해야겠지요.
Outside Room이벤트를 추가하고, 아래와 같이 추가합니다.

 

 
    ★ obj_bullet - Outside Room 이벤트

     instance_destroy();//룸 밖으로 나가면 총알을 파기함.



이제 이 총알을 발사해야합니다.
카메라 오브젝트로 이동하여 마우스 이벤트인 Global Left Pressed 이벤트를 추가하고 아래와 같이

설정합니다.

 

 
     ★ obj_cam - Global Left Pressed 이벤트

     _x=x+lengthdir_x(lengthdir_x(3,cam_yang),cam_zang);
     _y=y+lengthdir_y(lengthdir_x(3,cam_yang),cam_zang);
     _z=z+eye-lengthdir_y(3,cam_yang);//총알을 카메라의 조금 앞에 생성하도록 함.

     it=instance_create(_x,_y,obj_bullet);
     it.z=_z;
     it.direction=cam_zang;//카메라의 좌우 회전값
     it.ydirection=cam_yang;//카메라의 상하 회전값
     it.pspeed=5;//이동 속도



총알을 카메라의 조금 앞에 생성하고, 총알의 방향을 카메라의 상하 회전값과 좌우 회전값에 따라

이동하도록 설정합니다.

이제 잘 되는지 테스트해봅시다.

여기까지 해보시면 아마 블럭은 그냥 지나칠 것입니다.
아직 블럭 충돌에 대한 설정을 하지 않았으니까요. 헤헤~
일단 Script 폴더에 스크립트를 하나 생성합니다.
그리고 아래와 같이 작성합니다.

 

 
     ★ place_3d_inst - 스크립트

     //place_3d_inst(obj,zmin,zmax);
     var _obj,it,i,n,_z1,_z2,_zc,_on,_d1,_d2,_inst;
     _obj=argument0;
     _z1=argument1;
     _z2=argument2;
     n=instance_number(_obj);
     _on=0;

     _inst=noone;

     for(i=0;i<n;i+=1;){it=instance_find(_obj,i);
     if !(collision_point(x,y,it,1,1)=noone){
     _zmin=it.z;
     _zmax=it.z+it.ztop;
     _d1=abs(_z2-_z1);
     _d2=abs((_zmax)-_zmin);

     if _d1<=_d2{_zc=_z1+(_d1/2);
     if (_zmin<_z1 && _zmax>_z1) || (_zmin<_z2 && _zmax>_z2)
     || (_zmin<_zc && _zmax>_zc){_on=1;_inst=it;break;}
     }else{_zc=_z1+(_d2/2);
     if (_z1<_zmin && _z2>_zmin) || (_z1<_zmax && _z2>_zmax)
     || (_zmin<_zc && _zmax>_zc){_on=1;_inst=it;break;}
     }
     }}

     return _inst;//충돌에 일치하는 인스턴스를 반환함.



이 스크립트는 블럭체크를 위한 스크립트입니다.

     place_3d_inst(obj,zmin,zmax);
     obj : 충돌 대상
     zmin : 현 오브젝트 z좌표의 최소값
     zmax : 현 오브젝트 z좌표의 최대값

 

 

(▲충돌 체크)


충돌하는 블럭 중 높이별로 3등분하여 어느 한쪽이라도 걸린다면 그 블럭의 ID를 반환하는

스크립트입니다.

 

위의 스크립트는 x,y의 인스턴스의 현재 좌표를 기준으로 삼지만, 만약 일일이 스크립트 외부에서

좌표를 체크하게 하고 싶다면 아래처럼 좌표만 argument로 변경해서 사용하시면 됩니다.

 



     ★ place_3d_inst_ext - 스크립트


     //place_3d_inst_ext(obj,x,y,zmin,zmax);
     var _obj,it,i,n,_x,_y,_z1,_z2,_zc,_on,_d1,_d2,_inst;
     _obj=argument0;
     _x=argument1;
     _y=argument2;
     _z1=argument3;
     _z2=argument4;
     n=instance_number(_obj);
     _on=0;

     _inst=noone;

     if !(collision_point(_x,_y,_obj,1,1)=noone){
     for(i=0;i<n;i+=1;){it=instance_find(_obj,i);
     if !(collision_point(_x,_y,it,1,1)=noone){
     //---------------------------
     _zmin=it.z;
     _zmax=it.z+it.ztop;
     _d1=abs(_z2-_z1);
     _d2=abs((_zmax)-_zmin);

     if _d1<=_d2{_zc=_z1+(_d1/2);
     if (_zmin<_z1 && _zmax>_z1) || (_zmin<_z2 && _zmax>_z2)
     || (_zmin<_zc && _zmax>_zc){_on=1;_inst=it;break;}
     }else{_zc=_z1+(_d2/2);
     if (_z1<_zmin && _z2>_zmin) || (_z1<_zmax && _z2>_zmax)
     || (_zmin<_zc && _zmax>_zc){_on=1;_inst=it;break;}
     }
     //---------------------------
     }}
     }

     return _inst;


 

이렇게 하면 스크립트 외부에서 좌표를 인자로 입력해 체크할 수가 있지요.

여러분이 편하신 방법을 이용하시면 됩니다.

 

 


다음은 총알 오브젝트로 이동해서 이 스크립트를 이용하여 블럭과의 충돌했을 때의 이벤트를

작성합시다.
Step 이벤트에 아래와 같이 추가합니다.

 

 
     ★ obj_bullet - Step이벤트

     if place_meeting(x,y,obj_block){
     if !(place_3d_inst(obj_block,z-1,z+1)=noone){
     instance_destroy();
     }}



충돌 대상은 블럭이고, 충돌 체크할 최소값은 z값에서 조금 아래,
최대값은 z값에서 약간 높은 지점으로 체크하게 됩니다.
일치하는 블럭을 만나면 총알은 그대로 파기합니다.

다시 한번 테스트 해봅시다.

어라... 총알이 파기 됬는지, 아니면 계속 남아있는지 확인이 안된다구요?
네. 뭔가 허전할 것입니다.
그래서 총알이 파기될때 이펙트하나를 발생하게 합시다.

 





◈ 총알의 스파크(obj_effect1)

 


일단 총알과 마찬가지로 오브젝트(obj_effect1)를 하나 만듭니다.

 

 

(▲총알 오브젝트)


Create 이벤트를 추가하고 필요한 변수들을 선언합니다.

 

 
     ★ obj_effect1 - Create 이벤트

     z=0;//z좌표
     ydirection=0;//상하 회전. 좌우 회전은 direction을 사용.
     pspeed=0;//이펙트의 이동속도

     alp=1;//투명도



다음은 Draw 이벤트를 추가하고, 이펙트의 모양을 설정합니다.
강좌에선 간단하게 선으로 표시하도록 하겠습니다.

 

 
     obj_effect1 - Draw 이벤트

     draw_set_alpha(alp);
     d3d_transform_add_rotation_y(ydirection);
     d3d_transform_add_rotation_z(direction);
     d3d_transform_add_translation(x,y,z);
     draw_line_color(-2,0,1,0,c_red,c_yellow);
     d3d_transform_set_identity();
     draw_set_alpha(1);



이렇게 하면 노란색과 빨간색으로 이루어진 선 모양이 됩니다.
Step 이벤트를 추가하고, 이동에 대한 이벤트를 작성합시다.

 

 
     obj_effect1 - Step 이벤트

     x+=lengthdir_x(lengthdir_x(pspeed,ydirection),direction);
     y+=lengthdir_y(lengthdir_x(pspeed,ydirection),direction);
     z+=-lengthdir_y(pspeed,ydirection);

     if alp>0{alp-=0.05;}else{instance_destroy();}
     if pspeed>0{pspeed-=0.1;}else{pspeed=0;}



이 이펙트는 이동중 속도가 점점 줄고, 투명도가 0이 되면 사라지게 됩니다.
음. 이런 설정이 아니더라도 여러분이 원하는 설정을 하시면 됩니다.

이제 이 이펙트를 총알이 파기될때 발생하도록 만들어봅시다.
총알 오브젝트(obj_bullet)으로 이동해서 이벤트 메뉴-> Other -> User Defined 0을 추가합니다.
User Defined 이벤트는 사용자 정의 이벤트로 오브젝트 내에 따로 호출했을 때만 발생되는

이벤트입니다.
오브젝트에서 가장 많이 사용되는 스크립트만 따로 작성할 때 유용한 이벤트이죠.
오출 할 때는 event_user(번호)로 호출하며, 호출시 바로 실행됩니다.

 

 
     ★ obj_bullet - User Defined 0 이벤트

     repeat(10){
     it=instance_create(x,y,obj_effect1);
     it.z=z;
     it.direction=direction+random_range(-20,20);
     it.ydirection=ydirection+180+random_range(-20,20);
     it.pspeed=2;
     }



이것은 총알 방향의 반대 방향으로 이펙트를 발생시키게 됩니다.

이제 이 이벤트를 총알이 파기되는 곳(instance_destroy가 있는 곳)에 배치하면 됩니다.

 

 
     ★ obj_bullet - Step 이벤트

     if place_meeting(x,y,obj_block){
     if !(place_3d_inst(obj_block,z-1,z+1)=noone){
     //---------------------
     event_user(0);◀이벤트 호출 삽입
     //---------------------
     instance_destroy();
     }}


     if z<=0{
     //---------------------
     event_user(0);◀이벤트 호출 삽입
     //---------------------
     instance_destroy();}



굳! 굳! 굳! 이제 어느정도 된 것 같네요.

블럭도 룸에 배치해보고, 여러가지로 제대로 되는지 테스트 해봅시다.

 

 

 

(▲테스트 화면)

 

이번엔 좀 어려운 강좌가 되었군요.
잘 따라해보셨는지 모르겠네요. 헤헤~
어찌됬든 게임메이커에서 3D는 2D게임의 응용이어서 2D를 잘 다룬다면 3D도 잘 하실것이라

생각됩니다.
재미있는 게임 만드세용~^O^

 

 

 

 

 

 

 

-------응용예제------

 

fpsX1-2.gmk
다운로드

 

fpsX1-2.exe
다운로드

 

 

300x250

댓글