액션게임만들기
게임기획 액션게임만들기 제작하기 레벨디자인 2
* 액션게임을만들기까지 1 2 3 기획프로그래밍레벨디자인 3
1. 액션게임기획의과정 기획단계 기획서플레이어게임의틀 4
1-1. 기획서작성 기획단계 어떤게임을만들고싶은지, 어떤놀이가필요한지, 어떤형태로진행할것인지, 이게임이재미있을지, 목표 기획서 5
1-2. 플레이어선정 기획단계 Why? 대상에따라아이디어방향이달라짐 남녀, 나이대, 생활환경, 직업등다양한변수고려 많은시간을투자해야함! Chap 5~7 대상 : 기획과프로그래밍을공부하고싶은사람 6
1-3. 큰틀짜기 기획단계 장르 대상플레이어에맞춰결정 가로스크롤점프액션 액션게임 규칙, 목표 진행방향 변화 캐릭터 액션 기획서로작성! 세계관 7
2. 사양서 기획단계 기획서는전체적인틀을잡아주는역할을한다면 사양서는프로그래머가무엇을만들어야하는지파악할수있는기초작업. 시퀀스 사양서 게임속요소 플레이어캐릭터 그외 ex) 방해캐릭터, 아이템등 8
2. 사양서 * 플레이어캐릭터사양서 기획단계 9
2. 사양서 * 플레이어캐릭터사양서 기획단계 10
* 액션게임을만들기까지 1 2 3 기획프로그래밍레벨디자인 11
* 기초준비 구현 - 프로그래밍 1. 새프로젝트만들기 2. 레이아웃설정맞추기 12
* 기초준비 구현 - 프로그래밍 3. Player 와 Floor 만들기 - Player 1 [GameObject] [Create Other] [Sphere] 2 Hierarchy에생긴 Sphere를 Player 로변경 3 Player를선택하고 Inspector에있는 [Tag] 를 Player로설정한다. 4 Player의 [Transform/Position] 을 (X:0, Y:2, Z:0) 으로설정한다. 5 Player의 [Transform/Rotation] 을 (X:0, Y:90, Z:0) 으로설정한다. 13
* 기초준비 구현 - 프로그래밍 3. Player 와 Floor 만들기 - Floor 1 [GameObject] [Create Other] [Cube] Cube 2 개를만들어각각 Floor0, Floor1 로이름지정 2 [Assets] [Create] [Material] 도 2 개만들어준다. 3 Floor0 에 Floor0Material 을드래그앤드롭한다. ( 프리팹 ) 4 Floor1 에 Floor1Material 을드래그앤드롭한다. ( 프리팹 ) 5 Floor1Material 을선택하여 Inspector 를표시한후색깔을빨간색으로설정한다. - Block 1 [GameObject] [Create Empty] 2 Hierarchy의 GameObject를 GameRoot 로변경 14
구현 - 프로그래밍 화면의발판이되는블록을만들고, 블록이왼쪽끝으로넘어가면 지워지는부분까지구현해보도록하자. 15
* 플레이어를따라가는카메라 구현 - 프로그래밍 앞으로계속나아가려면플레이어가있어야하고, 그플레이어를쫓아가는카메라가필요! 16
* 플레이어를따라가는카메라 구현 - 프로그래밍 - 오른쪽으로진행 17
* 플레이어를따라가는카메라 구현 - 프로그래밍 - 카메라 CameraControl.cs private GameObject player = null; private Vector3 position_offset = Vector3.zero; void Start() { // 멤버변수 player에 Player 오브젝트를할당. this.player = GameObject.FindGameObjectWithTag("Player"); // 카메라위치 (this.transform.position) 와. // 플레이어위치 (this.player.transform.position) 의차이를보관. this.position_offset = this.transform.position - this.player.transform.position; void LateUpdate() { // 카메라현재위치를 new_position에할당. Vector3 new_position = this.transform.position; // 플레이어의 X좌표에차이값을더해서 new_position의 X에대입. new_position.x = this.player.transform.position.x + this.position_offset.x; // 카메라위치를새로운위치 (new_position) 로갱신. this.transform.position = new_position; 18
* 블록만들기 구현 - 프로그래밍 전부를미리만드는것보다진행하면서만들어내는것이더효율적! 2 개의스크립트를구현해서블록을제때제때만들어보도록하자. 19
* 블록만들기 구현 - 프로그래밍 BlockCreator.cs public GameObject[] blockprefabs; // 블록을저장할배열. private int block_count = 0; // 생성한블록의개수. void Start() { void Update() { public void createblock(vector3 block_position) { // 만들어야할블록의종류 ( 흰색인가빨간색인가 ) 를구한다. int next_block_type = this.block_count % this.blockprefabs.length; // 블록을생성하고 go에보관한다. % : 나머지를구하는연산자. GameObject go = GameObject.Instantiate(this.blockPrefabs[next_block_type]) as GameObject; go.transform.position = block_position; // 블록의위치를이동. this.block_count++; // 블록의개수를증가. 20
구현 - 프로그래밍 * 맵 ( 스테이지 ) 만들기 MapCreator.cs public static float BLOCK_WIDTH = 1.0f; // 블록의폭. public static float BLOCK_HEIGHT = 0.2f; // 블록의높이. public static int BLOCK_NUM_IN_SCREEN = 24; // 화면내에들어가는블록의개수. // 블록에관한정보를모아서관리하는구조체. 구조체여러개의정보를하나로묶을때사용 private struct FloorBlock { public bool is_created; // 블록이만들어졌는가. public Vector3 position; // 블록의위치. ; private FloorBlock last_block; // 마지막에생성한블록. private PlayerControl player = null; // 씬상의 Player 를보관. private BlockCreator block_creator; // BlockCreator 를보관. 21
* 맵 ( 스테이지 ) 만들기 구현 - 프로그래밍 MapCreator.cs void Start() { this.player = GameObject.FindGameObjectWithTag("Player"). GetComponent<PlayerControl>(); this.last_block.is_created = false; this.block_creator = this.gameobject.getcomponent<blockcreator>(); private void create_floor_block() { Vector3 block_position; // 이제부터만들블록의위치. if(! this.last_block.is_created) { // last_block이생성되지않은경우. // 블록의위치를일단 Player와같게한다. block_position = this.player.transform.position; // 그러고나서블록의 X 위치를화면절반만큼왼쪽으로이동. block_position.x -= BLOCK_WIDTH * ((float)block_num_in_screen / 2.0f); // 블록의 Y위치는 0으로. block_position.y = 0.0f; else { // last_block이생성된경우. // 이번에만들블록의위치를직전에만든블록과같게. block_position = this.last_block.position; 22
* 맵 ( 스테이지 ) 만들기 구현 - 프로그래밍 MapCreator.cs void Update() { // 플레이어의 X위치를가져온다. float block_generate_x = this.player.transform.position.x; // 그리고대략반화면만큼오른쪽으로이동. // 이위치가블록을생성하는문턱값이된다. block_generate_x += BLOCK_WIDTH * ((float)block_num_in_screen + 1) / 2.0f; // 마지막에만든블록의위치가문턱값보다작으면. while(this.last_block.position.x < block_generate_x) { // 블록을만든다. this.create_floor_block(); Player의위치에서화면절반의앞 ( 오른쪽 ) 에문턱값을설정하고, 마지막에만든블록이그문턱값보다왼쪽에있을때는그문턱값을넘을때까지블록을계속만든다. 23
* 불필요한블록지우기 구현 - 프로그래밍 사용한블록을계속남겨두면프로그램처리도무거워지고, 게임이멈출수도있기때문에지워주어야한다. BlockControl.cs public MapCreator map_creator = null; // MapCreator를보관하는변수. void Start() { // MapCreator를가져와서멤버변수 map_creator에보관. map_creator = GameObject.Find("GameRoot").GetComponent<MapCreator>(); void Update() { if(this.map_creator.isdelete(this.gameobject)) { // 카메라에게나안보이냐고물어보고안보인다고대답하면 GameObject.Destroy(this.gameObject); // 자기자신을삭제. public bool isdelete(gameobject block_object) { bool ret = false; // 반환값. // Player로부터반화면만큼왼쪽에위치. // 이위치가사라지느냐마느냐를결정하는문턱값이됨. float left_limit = this.player.transform.position.x - BLOCK_WIDTH * ((float)block_num_in_screen / 2.0f); // 블록의위치가문턱값보다작으면 ( 왼쪽 ). if(block_object.transform.position.x < left_limit) { ret = true; // 반환값을 true( 사라져도좋다 ) 로. return(ret); // 판정결과를돌려줌. 24
* 점프 리지드바디로물리효과를먼저줄것 구현 - 프로그래밍 PlayerControl.cs // 점프 에필요한전역변수선언먼저. public static float ACCELERATION = 10.0f; // 가속도. public static float SPEED_MIN = 4.0f; // 속도의최솟값. public static float SPEED_MAX = 8.0f; // 속도의최댓값. public static float JUMP_HEIGHT_MAX = 3.0f; // 점프높이. public static float JUMP_KEY_RELEASE_REDUCE = 0.5f; // 점프후의감속도. public enum STEP { // Player의각종상태를나타내는자료형. NONE = -1, // 상태정보없음. RUN = 0, // 달린다. JUMP, // 점프. MISS, // 실패. NUM, // 상태가몇종류있는지보여준다 (=3). ; public STEP step = STEP.NONE; // Player의현재상태. public STEP next_step = STEP.NONE; // Player의다음상태. public float step_timer = 0.0f; // 경과시간. private bool is_landed = false; // 착지했는가. private bool is_colided = false; // 뭔가와충돌했는가. private bool is_key_released = false; // 버튼이떨어졌는가. 25
* 점프 리지드바디로물리효과를먼저줄것 구현 - 프로그래밍 PlayerControl.cs void Start() { this.next_step = STEP.RUN; private void check_landed() // 착지했는지조사 { this.is_landed = false; // 일단 false로설정. do { Vector3 s = this.transform.position; // Player의현재위치. Vector3 e = s + Vector3.down * 1.0f; // s부터아래로 1.0f로이동한위치. RaycastHit hit; if(! Physics.Linecast(s, e, out hit)) { // s부터 e 사이에아무것도없을때. break; // 아무것도하지않고 do~while 루프를빠져나감 ( 탈출구로 ). // s부터 e 사이에뭔가있을때아래의처리가실행. if(this.step = = STEP.JUMP) { // 현재, 점프상태라면. // 경과시간이 3.0f 미만이라면. if(this.step_timer < Time.deltaTime * 3.0f) { break; // 아무것도하지않고 do~while 루프를빠져나감 ( 탈출구로 ). // s부터 e 사이에뭔가있고 JUMP 직후가아닐때만아래가실행. this.is_landed = true; while(false); // 루프의탈출구. 26
* 점프 리지드바디로물리효과를먼저줄것 구현 - 프로그래밍 PlayerControl.cs void Update() { Vector3 velocity = this.rigidbody.velocity; // 속도를설정. this.check_landed(); // 착지상태인지체크. this.step_timer += Time.deltaTime; // 경과시간을진행한다. // 다음상태가정해져있지않으면상태의변화를조사한다. if(this.next_step = = STEP.NONE) { switch(this.step) { // Player의현재상태로분기. case STEP.RUN: // 달리는중일때. if(! this.is_landed) { // 달리는중이고착지하지않은경우아무것도하지않는다. else { If(Input.GetMouseButtonDown(0)) { // 달리는중이고착지했고왼쪽버튼이눌렸다면. // 다음상태를점프로변경. this.next_step = STEP.JUMP; break; case STEP.JUMP: // 점프중일때. if(this.is_landed) { // 점프중이고착지했다면다음상태를주행중으로변경. this.next_step = STEP.RUN; break; // ' 다음정보 ' 가 ' 상태정보없음 ' 이아닌동안 ( 상태가변할때만 ). while(this.next_step!= STEP.NONE) { this.step = this.next_step; // ' 현재상태 ' 를 ' 다음상태 ' 로갱신. this.next_step = STEP.NONE; // ' 다음상태 ' 를 ' 상태없음 ' 으로변경. // 계속 27
* 점프 리지드바디로물리효과를먼저줄것 구현 - 프로그래밍 PlayerControl.cs switch(this.step) { // 갱신된 ' 현재상태 ' 가. case STEP.JUMP: // ' 점프 ' 일때. // 점프할높이로점프속도를계산 ( 마법의주문임 ). velocity.y = Mathf.Sqrt( 2.0f * 9.8f * PlayerControl.JUMP_HEIGHT_MAX); // ' 버튼이떨어졌음을나타내는플래그 ' 를클리어한다. this.is_key_released = false; break; this.step_timer = 0.0f; // 상태가변했으므로경과시간을제로로리셋. // 상태별로매프레임갱신처리. switch(this.step) { case STEP.RUN: // 달리는중일때. // 속도를높인다. velocity.x += PlayerControl.ACCELERATION * Time.deltaTime; // 속도가최고속도제한을넘으면. if(mathf.abs(velocity.x) > PlayerControl.SPEED_MAX) { // 최고속도제한이하로유지한다. velocity.x *= PlayerControl.SPEED_MAX / Mathf.Abs(this.rigidbody.velocity.x); break; 28
* 점프 리지드바디로물리효과를먼저줄것 구현 - 프로그래밍 PlayerControl.cs case STEP.JUMP: // 점프중일때. do { // ' 버튼이떨어진순간 ' 이아니면. if(! Input.GetMouseButtonUp(0)) { break; // 아무것도하지않고루프를빠져나간다. // 이미감속된상태면 ( 두번이상감속하지않도록 ). if(this.is_key_released) { break; // 상하방향속도가 0 이하면 ( 하강중이라면 ). if(velocity.y <= 0.0f) { break; // 버튼이떨어져있고상승중이라면감속시작. // 점프의상승은여기서끝. velocity.y *= JUMP_KEY_RELEASE_REDUCE; this.is_key_released = true; while(false); break; // Rigidbody의속도를위에서구한속도로갱신. // ( 이행은상태에관계없이매번실행된다 ). this.rigidbody.velocity = velocity; 29
* 바닥에구멍내기 구현 - 프로그래밍 LevelControl.cs // 만들어야할블록에관한정보를모은구조체. public struct CreationInfo { public Block.TYPE block_type; // 블록의종류. public int max_count; // 블록의최대개수. public int height; // 블록을배치할높이. public int current_count; // 작성한블록의개수. ; public CreationInfo previous_block; // 이전에어떤블록을만들었는가. public CreationInfo current_block; // 지금어떤블록을만들어야하는가. public CreationInfo next_block; // 다음에어떤블록을만들어야하는가. public int block_count = 0; // 생성한블록의총수. public int level = 0; // 난이도. 30
* 바닥에구멍내기 구현 - 프로그래밍 MapCreator.cs // Block 클래스추가 public class Block { // 블록의종류를나타내는열거체. public enum TYPE { NONE = -1, // 없음. FLOOR = 0, // 마루. HOLE, // 구멍. NUM, // 블록이몇종류인지나타낸다 (=2). ; ; LevelControl.cs private void clear_next_block(ref CreationInfo block) { // 프로필노트에실제로기록하는처리를한다. // 전달받은블록 (block) 을초기화. block.block_type = Block.TYPE.FLOOR; block.max_count = 15; block.height = 0; block.current_count = 0; public void initialize() { this.block_count = 0; // 블록의총수를초기화. // 이전, 현재, 다음블록을각각. // clear_next_block() 에넘겨서초기화한다. this.clear_next_block(ref this.previous_block); this.clear_next_block(ref this.current_block); this.clear_next_block(ref this.next_block); 31
구현 - 프로그래밍 * 바닥에구멍내기 LevelControl.cs private void update_level(ref CreationInfo current, CreationInfo previous) { 대소문자구분주의 switch(previous.block_type) { case Block.TYPE.FLOOR: // 이번블록이바닥일경우. current.block_type = Block.TYPE.HOLE; // 다음번은구멍을만든다. current.max_count = 5; // 구멍은 5개만든다. current.height = previous.height; // 높이를이전과같게한다. break; case Block.TYPE.HOLE: // 이번블록이구멍일경우. current.block_type = Block.TYPE.FLOOR; // 다음은바닥만든다. current.max_count = 10; // 바닥은 10개만든다. break; public void update(){ // 이번에만든블록개수를증가. this.current_block.current_count++; // 이번에만든블록개수가 max_count 이상이면. if(this.current_block.current_count >= this.current_block.max_count) { this.previous_block = this.current_block; this.current_block = this.next_block; // 다음에만들블록의내용을초기화. this.clear_next_block(ref this.next_block); // 다음에만들블록을설정. this.update_level(ref this.next_block, this.current_block); this.block_count++; // 블록의총수를증가. 32
* 바닥에구멍내기 구현 - 프로그래밍 MapCreator.cs public static float BLOCK_WIDTH = 1.0f; public static float BLOCK_HEIGHT = 0.2f; public static int BLOCK_NUM_IN_SCREEN = 24; private LevelControl level_control = null; void Start() { this.player = GameObject.FindGameObjectWithTag( "Player").GetComponent<PlayerControl>(); this.last_block.is_created = false; this.block_creator = this.gameobject.getcomponent<blockcreator>(); this.level_control = new LevelControl(); this.level_control.initialize(); LevelControl 과 MapCreator 를연계시킴 기존코드에추가할것 33
* 바닥에구멍내기 구현 - 프로그래밍 MapCreator.cs private void create_floor_block(){ Vector3 block_position; if(! this.last_block.is_created) { block_position = this.player.transform.position; 실행화면 block_position.x -= BLOCK_WIDTH * ((float)block_num_in_screen / 2.0f); block_position.y = 0.0f; else { block_position = this.last_block.position; block_position.x += BLOCK_WIDTH; // 볼드체부분을추가. this.level_control.update(); // LevelControl을갱신. // level_control에저장된 current_block( 지금만들블록정보 ) 의. // height( 높이 ) 를씬상의좌표로변환. block_position.y = level_control.current_block.height * BLOCK_HEIGHT; // 지금만들블록에관한정보를변수 current에넣는다. LevelControl.CreationInfo current = this.level_control.current_block; // 지금만들블록이바닥이면 ( 지금만들블록이구멍이라면 ) if(current.block_type == Block.TYPE.FLOOR) { // block_position의위치에블록을실제로생성. this.block_creator.createblock(block_position); this.last_block.position = block_position; this.last_block.is_created = true; 34
* 구멍에떨어졌을때 PlayerControl.cs public static float NARAKU_HEIGHT = -5.0f; 구현 - 프로그래밍 구멍에떨어졌을때, 게임이바로끝나지않는다. 계속밑으로떨어지는화면반복. = 문턱값보다더낮은장소에 Player 위치존재. void Update() { Vector3 velocity = this.rigidbody.velocity; this.check_landed(); switch(this.step) { case STEP.RUN: case STEP.JUMP: // 현재위치가한계치보다아래면. if(this.transform.position.y < NARAKU_HEIGHT) { this.next_step = STEP.MISS; // ' 실패 ' 상태로한다. break; 35
* 구멍에떨어졌을때 PlayerControl.cs 구현 - 프로그래밍 구멍에떨어졌을때, 게임이바로끝나지않는다. 계속밑으로떨어지는화면반복. = 문턱값보다더낮은장소에 Player 위치존재. switch(this.step) { case STEP.JUMP: // 점프중일때. do {... velocity.y *= JUMP_KEY_RELEASE_REDUCE; this.is_key_released = true; while(false); break; case STEP.MISS: // 가속도 (ACCELERATION) 를빼서 Player의속도를느리게해간다. velocity.x -= PlayerControl.ACCELERATION * Time.deltaTime; if(velocity.x < 0.0f) { // Player의속도가마이너스면. velocity.x = 0.0f; // 0으로한다. break; 36
* 액션게임을만들기까지 1 2 3 기획프로그래밍레벨디자인 37
레벨디자인 그림 7-1 에서나온요소들을고려하여레벨디자인을해보자. 38
* 각요소별고려사항 레벨디자인 요소고려사항 ( 예시 ) 플레이어속도발판길이구멍크기발판높이 0~15 초 : 속도 7.0 15~30 초 : 속도 9.0 ( 갑자기빨라진다 ) 0~15 초 : 9~10 블록 15~30 초 : 8~9 블록 0~15 초 : 1~2 블록 15~30 초 : 3~5 블록 0~15 초 : 0 블록 15~30 초 : -4~+4 블록 ( 갑자기높이차이가생긴다 ) => 텍스트파일로정리해둘것 39
* 텍스트데이터게임에반영 레벨디자인 각요소를관리하는새로운클래스를만들어줌 LevelControl.cs // 중간에추가 public class LevelData { public struct Range { // 범위를표현하는구조체. public int min; // 범위의최솟값. public int max; // 범위의최댓값. ; public float end_time; // 종료시간. public float player_speed; // 플레이어의속도. public Range floor_count; // 발판블록수의범위. public Range hole_count; // 구멍의개수범위. public Range height_diff; // 발판의높이범위. public LevelData() { this.end_time = 15.0f; // 종료시간초기화. this.player_speed = 6.0f; // 플레이어의속도초기화. this.floor_count.min = 10; // 발판블록수의최솟값을초기화. this.floor_count.max = 10; // 발판블록수의최댓값을초기화. this.hole_count.min = 2; // 구멍개수의최솟값을초기화. this.hole_count.max = 6; // 구멍개수의최댓값을초기화. this.height_diff.min = 0; // 발판높이변화의최솟값을초기화. this.height_diff.max = 0; // 발판높이변화의최댓값을초기화. 40
* Level Data 를 List 로다루기 레벨디자인 LevelControl.cs // 스크립트시작부분에써준다. // xx 안에정의된이름을사용할거에요라고선언 using System.Collections.Generic; //List형멤버변수를추가, 각각의최댓값최솟값을넣어준다. public class LevelControl : MonoBehaviour { private List<LevelData> level_datas = new List<LevelData>(); public int HEIGHT_MAX = 20; public int HEIGHT_MIN = -4; 41
* text data 를그대로읽어들여해석하기 레벨디자인 42
* text data 를그대로읽어들여해석하기 레벨디자인 LevelControl.cs public void loadleveldata(textasset level_data_text){ // 텍스트데이터를문자열로가져온다. string level_texts = level_data_text.text; // 개행코드 '\' 마다분할해서문자열배열에넣는다. string[] lines = level_texts.split('\n'); // lines 내의각행에대해서차례로처리해가는루프. foreach(var line in lines) { if(line = ="") { // 행이빈줄이면. continue; // 아래처리는하지않고반복문의처음으로점프한다. ; Debug.Log(line); // 행의내용을디버그출력한다. string[] words = line.split(); // 행내의워드를배열에저장한다. int n = 0; // LevelData형변수를생성한다. // 현재처리하는행의데이터를넣어간다. LevelData level_data = new LevelData(); // words내의각워드에대해서순서대로처리해가는루프. foreach(var word in words) { if(word.startswith("#")) { // 워드의시작문자가 # 이면. break; // 루프탈출. if(word = = "") { // 워드가텅비었으면. continue; // 루프의시작으로점프한다. // n 값을 0, 1, 2,...7로변화시켜감으로써 8항목을처리한다. // 각워드를플롯값으로변환하고 level_data에저장한다. switch(n) { case 0: level_data.end_time = float.parse(word); break; case 1: level_data.player_speed = float.parse(word); break; case 2: level_data.floor_count.min = int.parse(word); break; case 3: level_data.floor_count.max = int.parse(word); break; case 4: level_data.hole_count.min = int.parse(word); break; case 5: level_data.hole_count.max = int.parse(word); break; case 6: level_data.height_diff.min = int.parse(word); break; case 7: level_data.height_diff.max = int.parse(word); break; n++; 43
* text data 를그대로읽어들여해석하기 레벨디자인 LevelControl.cs if(n >= 8) { // 8항목 ( 이상 ) 이제대로처리되었다면. // List 구조의 level_datas에 level_data를추가한다. this.level_datas.add(level_data); else { // 그렇지않다면 ( 오류의가능성이있다 ). if(n = = 0) { // 1워드도처리하지않은경우는주석이므로. // 문제없다. 아무것도하지않는다. else { // 그이외이면오류다. // 데이터개수가맞지않다는것을보여주는오류메시지를표시한다. Debug.LogError("[LevelData] Out of parameter.\n"); // level_datas에데이터가하나도없으면. if(this.level_datas.count == 0) { // 오류메시지를표시한다. Debug.LogError("[LevelData] Has no data.\n"); // level_datas에기본 LevelData를하나추가해둔다. this.level_datas.add(new LevelData()); 44
* loadleveldata() 메서드호출 레벨디자인 LevelControl.cs public TextAsset level_data_text = null; void Start() { this.player = GameObject.FindGameObjectWithTag("Player ).GetComponent<PlayerControl>(); this.last_block.is_created = false; this.block_creator = this.gameobject.getcomponent<blockcreator>(); this.level_control = new LevelControl(); this.level_control.initialize(); this.level_control.loadleveldata(this.level_data_text); // 이구문을추가한다. 45
* 발판과구멍에데이터반영 읽어온 text data 중발판과구멍에관한것을반영시킨다. 레벨디자인 Gameroot 스크립트 = 경과시간관리 1 [Assets] [Create] [C# Script] 로스크립트를만들고 GameRoot 로이름을변경한다. 2 Hierarchy 의 GameRoot 에 GameRoot 스크립트를드래그앤드롭한다. 46
* 발판과구멍에데이터반영 레벨디자인 GameRoot.cs public float step_timer = 0.0f; // 경과시간을유지한다. void Update() { this.step_timer += Time.deltaTime; // 경과시간을더해간다. public float getplaytime(){ float time; time = this.step_timer; return(time); // 호출한곳에경과시간을알려준다. GameRoot 스크립트의 getplaytime() 메서드를사용하기위해서 MapCreator 클래스를수정해준다. MapCreator.cs private GameRoot game_root = null; void Start() {... this.game_root = this.gameobject.getcomponent<gameroot>(); void create_floor_block() {... // this.level_control.update(); this.level_control.update(this.game_root.getplaytime());... 47
* level_data 에서읽어온데이터반영 레벨디자인 LevelControl.cs private void update_level( ref CreationInfo current, CreationInfo previous, float passage_time){ // 새인수 passage_time으로플레이경과시간을받는다. // 레벨 1~ 레벨 5를반복한다. float local_time = Mathf.Repeat(passage_time, this.level_datas[this.level_datas.count - 1].end_time); // 현재레벨을구한다. int i; for(i = 0; i < this.level_datas.count - 1; i++) { if(local_time <= this.level_datas[i].end_time) { break; this.level = i; current.block_type = Block.TYPE.FLOOR; current.max_count = 1; if(this.block_count >= 10) { // 현재레벨용레벨데이터를가져온다. LevelData level_data; level_data = this.level_datas[this.level]; switch(previous.block_type) { case Block.TYPE.FLOOR: // 이전블록이바닥인경우. current.block_type = Block.TYPE.HOLE; // 이번엔구멍을만든다. // 구멍크기의최솟값 ~ 최댓값사이의임의의값. current.max_count = Random.Range( level_data.hole_count.min, level_data.hole_count.max); current.height = previous.height; // 높이를이전과같이한다. break; case Block.TYPE.HOLE: // 이전블록이구멍인경우. current.block_type = Block.TYPE.FLOOR; // 이번엔바닥을만든다. // 바닥길이의최솟값 ~ 최댓값사이의임의의값. current.max_count = Random.Range( level_data.floor_count.min, level_data.floor_count.max); 48
* level_data 에서읽어온데이터반영 레벨디자인 LevelControl.cs // 바닥높이의최솟값과최댓값을구한다. int height_min = previous.height + level_data.height_diff.min; int height_max = previous.height + level_data.height_diff.max; height_min = Mathf.Clamp(height_min, HEIGHT_MIN, HEIGHT_MAX); height_max = Mathf.Clamp(height_max, HEIGHT_MIN, HEIGHT_MAX); // 바닥높이의최솟값 ~ 최댓값사이의임의의값. current.height = Random.Range(height_min, height_max); break; // 지난번 update_level() 과달라진점중하나는 // 플레이경과시간을인수 passage_time 으로받음 // 최솟값과최대값사이의값을강제로넣기위해사용, 인수는 3 개 49
* level_data 에서읽어온데이터반영 레벨디자인 LevelControl.cs update() 메서드는 update_level() 에경과시간을알려줘야한다. 경과시간을인수로받도록변경한다. // public void update() public void update(float passage_time){ this.current_block.current_count++; if(this.current_block.current_count >= this.current_block.max_count) { this.previous_block = this.current_block; this.current_block = this.next_block; this.clear_next_block(ref this.next_block); // this.update_level(ref this.next_block, this.current_block); this.update_level( ref this.next_block, this.current_block, passage_time); this.block_count++; 50
* 플레이어에도데이터반영 레벨디자인 < 수정해야할사항 > LevelControl 클래스에 getplayerspeed() 메서드추가 PlayerControl 클래스에멤버변수두개추가 PlayerControl 클래스의 Update() 메서드수정 MapCreator 클래스의 Start() 메서드에한행추가 51
* 플레이어에도데이터반영 레벨디자인 LevelControl 클래스에 getplayerspeed() 메서드추가 LevelControl.cs public float getplayerspeed(){ return(this.level_datas[this.level].player_speed); PlayerControl 클래스에멤버변수두개추가 PlayerControl.cs private float click_timer = -1.0f; // 버튼이눌린후의시간. private float CLICK_GRACE_TIME = 0.5f; // 점프하고싶은의사를받아들일시간. 52
* 플레이어에도데이터반영 레벨디자인 PlayerControl 클래스의 Update() 메서드수정 PlayerControl.cs void Update() {... this.step_timer += Time.deltaTime; if(input.getmousebuttondown(0)) { // 버튼이눌렸으면. this.click_timer = 0.0f; // 타이머를리셋. else { if(this.click_timer >= 0.0f) { // 그렇지않으면. this.click_timer += Time.deltaTime; // 경과시간을더한다. if(this.next_step = = STEP.NONE) { 53
* 플레이어에도데이터반영 레벨디자인 PlayerControl 클래스의 Update() 메서드수정 PlayerControl.cs if(this.next_step = = STEP.NONE) { switch(this.step) { case STEP.RUN: if(! this.is_landed) { else { if(input.getmousebuttondown(0)) { this.next_step = STEP.JUMP; break; if(this.is_landed) { // 착지했다면. this.click_timer = -1.0f; // 버튼이눌리지않은상태를나타내는 -1.0f로. this.next_step = STEP.JUMP; // 점프상태로한다. 54
* 플라스틱러너의시퀀스연결 레벨디자인 - Title 씬을만들고프로그램을준비. TitleScript.cs void Update() { if(input.getmousebuttondown(0)) { Application.LoadLevel("GameScene"); void OnGUI() { GUI.Label(new Rect(Screen.width / 2, Screen.height / 2, 128, 32), "PlasticRunner"); 55
* 플라스틱러너의시퀀스연결 레벨디자인 - 사용할씬을프로젝트에알려주어야한다. 1 [File] [Building Settings...] 를실행 2 [Building Settings] 창의 [Scenes In Build] 에 TitleScene을드래그앤드롭 3 마찬가지로 GameScene을드래그앤드롭 playercontrol.cs public bool isplayend() // 게임이끝났는지판정. { bool ret = false; switch(this.step) { case STEP.MISS: // MISS 상태라면. ret = true; // ' 죽었어요 '(true) 라고알려줌. break; return(ret); 56
* 플라스틱러너의시퀀스연결 레벨디자인 - 구멍에빠져서게임타이틀로돌아가게한다. GameRoot.cs public class GameRoot : MonoBehaviour { public float step_timer = 0.0f; private PlayerControl player = null; void Start(){ this.player = GameObject.FindGameObjectWithTag( "Player").GetComponent<PlayerControl>(); void Update() { this.step_timer += Time.deltaTime; if(this.player.isplayend()) { Application.LoadLevel("TitleScene"); public float getplaytime() 57
다음페이지에서보아요