[게임만들기] 1인 게임 개발 중간 점검, 데모 빌드 만들었습니다

기획기사 | 윤서호 기자 | 댓글: 11개 |



"저에게 시간과 예산이 조금만 더 있었더라면!"

아마 좀 오래된 만화를 보신 분들이라면 흔히 듣는 레퍼토리였을 것입니다. 각자 반응이 제각각 달랐겠지만, 누구나 그때는 그 대상이 자기가 될 수 있다는 걸 생각지도 않았죠. 그러다가 시간이 지나서 그런 상황이 종종 닥쳐오곤 합니다.

흘러가는 이야기로 말하고는 했지만, 어쨌든 게임 개발 기획을 진행하면서 저 말이 가슴에 가면 갈수록 와닿고 있습니다. 반은 제 취미(?)이자 욕심으로 시작했으니 저 앞에 '돈'은 빼더라도, 일단 시간이 많았으면 얼마나 좋을까 하는 생각이 자꾸 들기 때문이죠. 엔진이 발전하면서 컴파일에 드는 시간이나 코드 체킹에 드는 시간이 줄지만, 그만큼 써먹으면 좋은 기능이 자꾸 생기고 있으니 그거 욕심부리다가 또 다른 시간을 빼앗겨버리곤 하는 게 일상입니다.

그래서 중간 단계를 선보이려고 하지만, 대작병이라는 게 사실 아주 조그만 것에서도 자리잡고 있는 병이었습니다. 뭔가 조금 만들어두면 "아 이걸론 이불킥각이야 좀 더 해야지"라는 '좀 더' 증상이 스멀스멀 기어나오기 시작합니다. 그게 그냥 어느 한 영역에서만 자리잡으면 모르겠는데, 곳곳에 전이가 되면서 손을 쓸 수 없게 되죠. 그러다가 얼기설기 짜집기한 코드들이 제멋대로 튀기 시작하면 그야말로 불 난 집에 휘발유 끼얹는 격이고요.

자꾸 그러면 결국 아무 것도 보여줄 것이 없다는 생각이 들어서 아주 기초적인 플레이 메카니즘, 씬 구성만으로 데모를 만들었습니다. 원래대로라면 WebGL로 만들어서 WebGL 공유 기능을 활용해 데모 버전을 공유할 생각이었지만, 현 단계에서는 도저히 알 수 없는 오류로 그건 보류해야 할 것 같습니다.




▲ 당초 계획은 WebGL로도 빌드를 만들어보는 것이지만, 뭔가 알 수 없는 오류로 그건 보류했습니다

게임만들기 기사 모아보기
[게임만들기 ①] 1인 게임 개발에 한 번 도전해보았습니다 - 예고 및 기획 단계편
[게임만들기 ②] 1인 게임 개발에 한 번 도전해보았습니다 - 캐릭터 디자인편
[게임만들기 ③] 1인 게임 개발에 한 번 도전해보았습니다 - 중간 점검 및 기획 수정편
[게임만들기 ④] 1인 게임 개발에 한 번 도전해보았습니다 - 배경 및 스테이지 설계편
[게임만들기 ⑤] 1인 게임 개발에 한 번 도전해보았습니다 - 적 캐릭터 제작편
[게임만들기 ⑥] 1인 게임 개발에 한 번 도전해보았습니다 - 적 만들기 심화편
[게임만들기] 1인 게임 개발 도전, 지금도 하고 있나요?
[게임만들기] 1인 게임 개발의 또다른 난관, 엔진 업데이트에 대처하는 자세

※ 기사 말미에 소스 코드가 동봉되어있습니다



■ 당면 목표: 적을 피하고, 때리는 플랫포머의 기초 플레이 메카니즘 구현




결국 플랫포머의 기본 플레이 루틴을 살펴보면 달리고 점프하기, 적을 공격하고 피하거나 맞아서 사망하고 다시 트라이하기 등이 있을 겁니다. 즉 데모라고 하려면 이 정도는 갖춰야 하는 최소한의 마지노선인 셈이죠.

이미 앞에서 애니메이션을 어떻게 구축해나갔는지 설명한 만큼, 이건 간단하게 훑고 가는 정도로만 하겠습니다. 핵심은 1) 그림을 일일이 그리지 않고 그 안에 뼈대를 심는 스켈레탈 방식을 선택했고, 2) 키프레임 애니메이션으로 작업 3) 애니메이션은 각각의 경우를 고려해서 서있는 동작, 걷기, 일반 공격, 점프, 점프 공격, 점프 크러쉬, 사망 등으로 분류하고, 각각 어떻게 연결될지 고려해서 트랜지션을 설계하기 이 셋으로 볼 수 있습니다.



▲ 주인공에게 할당된 애니메이션 클립과 파라미터, 트랜지션 (클릭하면 확대됩니다)

사실 이 작업 중에서 가장 오래 걸리는 작업이 애니메이션을 만드는 작업이었습니다. 일일이 그리지 않았고, 각 프레임마다 작업한 건 아니긴 합니다. 그래도 나름 자연스럽게 만들기 위해서는 계속 재생하고 체크하면서 본을 옮기고, 키프레임을 새로 주는 작업을 반복해나가야 했죠. 더군다나 각 부분의 이름이나 구조가 기존 것과 동일해야만 예전에 작업했던 걸 불러올 수 있다는 걸 그때 알았기 때문에 완전히 새로 작업해나갔습니다.

이미 이동, 점프, 적을 공격하는 코드는 만들었지만 특수 공격에 대한 코드는 작업하진 않았습니다. 일단 특수 공격의 발동 조건을 어떻게 해야 할지, 모션을 어떻게 만들고 판정을 만들어야 할지 해결이 안 됐었기 때문이죠. 결국 어차피 복잡한 건 만들 수 없으니, 최대한 간단하게 만들었습니다. 점프 상태에서 마우스 우클릭을 하면 공격하게끔 말이죠. 다만 이 특수 공격엔 빠르게 낙하하는 기능을 추가해야 하는데, 그걸 어떤 식으로 구현해야 할지 고민 중입니다. 오브젝트에 가해지는 중력값을 잠깐 변화시켰다가 원상복귀시키면 어떨까 싶은데, 그게 어떻게 해야할지 난제인 상황이거든요.



▲ 일반공격, 점프공격뿐만 아니라 점프 후 우클릭을 하면 특수 공격을 하게끔 했습니다



▲ 애니메이터 세팅은 다음과 같습니다 (클릭하면 확대됩니다)


그리고 적에게 공격당하면 피해를 입는 TakeDamage()코드까지 넣었는데, 플레이어가 피격당하는 조
건을 어떻게 넣어야 할지가 관건입니다. 예제를 보고 넣었는데, 이상하게 안 잡히는 걸로 봐서는 뭔가가 안 돌아가는 거 같긴 해서 코드 커버리지를 보고 있는데도 안 잡히기 때문이죠. 아마 여기저기서 긁어모은 코드를 마개조한 여파인 것 같긴 합니다. 다만 지금은 어디서 오류나지 않고 정상 작동하는 것만 감지덕지한 상황이라서 이건 나중에 살펴봐야 할 것 같습니다.

또 하나, 카메라는 유니티 시네머신을 활용해서 채림을 따라가게끔 설정했습니다. 시네머신 패키지를 설치한 뒤에 2D 카메라를 만들면 메인 카메라와 연동이 됩니다. 여기서 카메라가 바라볼 대상(LookAt)과, 카메라가 따라다닐 대상(Follow)를 지정하면 그걸 따라다니는 식이죠. 예제에서는 주인공에게 Empty Object를 주고 그 창에다가 넣는 식으로 나왔는데, 제 프로젝트에선 그냥 평범하게 걷는 모션이 이상하게 좌우로 미세하게 떨리는 느낌이 들었습니다. 이 역시도 문제 원인을 파악하지 못했기 때문에 일단은 나중으로 보류해둔 상태입니다.



▲ 시네머신으로 2D 카메라를 만들고 주인공의 빈 오브젝트를 따라가게끔 설정하면 (클릭 시 확대)



▲ 보다 자연스럽게 주인공을 따라다니는 카메라를 연출할 수 있습니다



■ 기본보다 조금 더 발전된 적을 만들어보자 - 자폭형, 비행형 적




흔히 시중에서 구할 수 있는 2D 플랫포머 참고 자료, 참고 서적에서는 대체로 적과 접촉해야만 데미지를 입는 것만 구현해둔 것이 많습니다. 물론 2D 플랫포머를 구현하려면 그것 말고도 여러 가지 신경써야 하는 게 많다보니 각 분야에서 기본기만 언급하고 넘어갈 수밖에 없긴 합니다. 그래도 그것만으로는 너무 밋밋하다보니, 기본기를 바탕으로 자폭형 적과 비행형 적들을 한 번 만들어봤습니다.

코드를 개조하기 전에 우선 이 적이 어떤 행동 알고리즘을 갖고 있을까, 생각을 해봐야 했습니다. 그냥 단순히 자폭하기엔 밋밋하고, 비행형 적도 어떤 조건에서 플레이어에게 탄을 쏠 건지 정해야 했으니까요. 그래서 각 개체에 빈 오브젝트들을 넣고 BoxCollider2D에 IsTrigger를 설정해둔 뒤에 PlayerFinder 등으로 분류했습니다. 이름에 특별한 의미가 있는 건 아니고, 그냥 그게 플레이어를 체크하기 위한 건지, 아니면 그 사거리에 들어왔을 때 사격하게끔 하는 장치인지 등을 분류하기 위한 것입니다.

결론은 PlayerFinder로 지정한 범위 내로 플레이어가 다가오면 1차 반응(달리기 등등)을 보이게 설계하고, 그보다 더 가까운 범위로 들어오면 2차 반응(슬라이딩이나 사격 등)으로 넘어가게끔 작업했습니다. 뭔가 디테일하게 하고 싶었지만, 당장 쓸 줄 아는 게 Collider의 IsTrigger를 활용하는 것이라 이를 최대한 활용해보기로 했죠. 알고 있는 건 OnTriggerEnter2D(), OnTriggerExit2D(), OnTriggerStay2D() 이 수준인데 여기서 갑자기 뭘 더 배워서 쓰기엔 제 능력이 너무 부족했거든요.

즉 플레이어가 특정 범위 내로 들어올 땐 OnTriggerEnter2D, 그 밖으로 나갔을 땐 OnTriggerExit2D()를 활용해서 이를 표현한 것이죠. 그리고 각각의 범위를 BoxCollider2D로 지정해둔 셈입니다.



▲ 처음 시야에 들어오면 달리고, 더 가까워지면 슬라이딩하게 디자인, 소스 코드는 기사 하단 참고
(클릭 시 확대)


그렇게 하기로 결정한 뒤에 자폭형 적, 클라인봄의 애니메이션을 걷기, 점화, 달리기, 슬라이딩, 폭발 총 5가지로 설정해두고 작업을 했습니다. 걷기가 기본 상태고 플레이어를 발견했을 때(즉 PlayerFinder 안으로 들어왔을 때) 점화 애니메이션 후 달리기로 이어지게 설정했습니다. 그리고 슬라이딩 범위에 플레이어가 들어오면 슬라이딩을 하게끔 말이죠. 그리고 플레이어와 충돌하면 폭발하는 것으로 마무리를 지었습니다.

한 가지 유의점은, 플레이어와의 충돌 조건은 OnTriggerEnter2D가 아닌 OnCollisionEnter2D로 표현했습니다. Trigger로 주니까 플레이어 캐릭터가 갖고 있는 각종 IsTrigger가 걸린 Collider에도 반응하더군요. 예외를 두는 방법이 있을 것 같긴 한데 그게 정확히 뭔지 몰라서 과감하게 배제하고 Collision으로 넣었습니다.



▲ 자세히 보면 폭발 전까지는 플레이어를 통과하지 못하는 걸 통해 Collision이라는 걸 알 수 있습니다



▲ 폭발 판정은 빈 오브젝트에 따로 두고, 애니메이션 단계에서 특정 시간에만 활성화되도록 설정
(클릭 시 확대)


그리고 Collision 이후에 폭발 애니메이션 창에서 이펙트가 나오는 프레임에 폭발 데미지가 활성화되게끔 설정했습니다. 건드렸다고 해서 바로 폭발하지 않고, 플레이어가 어느 정도 피할 수 있는 여지를 주기 위해서죠. 이걸 좀 더 세분화해서 만들고 싶은데 어떻게 해야 할지, 아직 방법을 고민하는 중입니다. 그런데 고민만 하다가는 끝이 없을 것 같아서 일단 가능한 정도만 만들었습니다.

비행형 적인 뫼비우스마일은 웃기, 깜빡이기, 사격, 소멸 네 가지 상태로 구분하고 각각 애니메이션을 만들었습니다. 웃기가 기본 상태고 플레이어와 접촉하면 깜빡이기, 그보다 더 안으로 들어오면 총알 발사, 플레이어에게 공격받아서 체력이 0이 되면 소멸 이렇게 설정했죠.

기본 메카니즘은 클라인봄과 동일하지만, '사격'은 별도로 구현해야 합니다. 다행히 물체를 쏘는 건 슈팅 게임 만들기 예제에서 구할 수 있긴 하지만, 특정 범위 밖으로 나간 총알을 사라지게 만드는 코드를 구현하는 게 어려웠습니다. 그 범위를 또 다시 Trigger로 설정하자니, 각종 Trigger와 상호작용하면서 예상치 못할 일이 벌어질 것 같았거든요.



▲ 플레이어 탐지 및 공격 시작 메카니즘은 클라인봄과 동일하지만
(클릭 시 확대)



▲ 사격하기 위해서는 다른 코드가 필요합니다

사실 클라인봄도 OnTriggerEnter2D()를 활용해 보이지 않는 벽을 따라서 좌우 전환하게 만들었는데, 클라인봄 본체가 아니라 PlayerFinder에 붙은 Trigger가 벽과 반응해서 생각한 것보다 더 앞에서 그런 행동을 연달아서 했던 전례가 있었습니다. 그래서 Trigger는 붙이지 않고 총알은 Destroy(object, 숫자)를 활용해서 일정 시간 뒤에 사라지게끔 임시방편을 적용해보았습니다.



■ 타이틀, 게임플레이, 그리고 게임 오버 씬까지 뼈대 만들기




완벽하지는 않지만 어쨌든 플레이어와 적은 구축했으니, 남은 건 씬을 구성하는 일입니다. 게임을 하면 보통 타이틀 화면이 뜨고, 그 다음에 캐릭터 선택 화면이 뜨거나 혹은 플레이 화면으로 곧장 넘어가곤 하죠. 그러다가 클리어하면 다음 스테이지로 넘어가거나 엔딩이 나오고, 게임오버되면 게임오버 문구가 화면에 뜨거나 혹은 게임오버 창으로 넘어가기도 합니다. 게임 엔진에서는 이 각각의 단위를 씬이라고 표현하는데, 그걸 다 만들어둔 뒤에 적재적소에 불러오는 것이 이제 해야 할 일인 셈이죠.

우선 데모 단위에서 필요한 건 StartGame, Stage1_1, GameOver, Clear 이렇게 네 가지였습니다. 이름은 크게 의미가 없고, 제가 그냥 임의로 구분짓기 위해서 붙여둔 이름입니다. 굳이 말한다면 타이틀씬, 스테이지1의 첫 번째 단계, 게임오버, 클리어했을 때 씬 이렇게 만든 거죠.

이제 씬 단계에 넘어오면 UI를 만들어야 하는데, 유니티에는 여러 UI 기능이 있긴 하지만 일단은 가장 기초적인 버튼 기능과 캔버스를 활용했습니다. 이때 가장 먼저 고려할 건 해상도인데, 그걸 처음에 생각도 않고 무작정 만들었다가 버튼이 안 보여서 몇 번이고 다시 만들었습니다. 미리 말씀드리자면, 빌드를 추출할 때 화면 해상도 설정을 염두하지 않으면 엔진에서 구동할 때와는 전혀 다른 모습이 나오곤 하니 주의할 필요가 있습니다.



▲ 해상도 문제를 생각 않고 만들면 이렇게 UI 문제가 발생하니 조심조심

물론 해상도 옵션을 디테일하게 만드는 방법이 있긴 하지만, 해상도 관련 문제는 지금 게임 만들기를 처음하거나 혹은 배우고 있는 입장에선 생각하기 너무 어렵죠. 그래서 처음 시작하는 씬에 스크립트를 하나 만들고, 거기에

void Awake()
{
Screen.sleepTimeout = SleepTimeout.NeverSleep;
Screen.SetResolution(1024, 768, false);
}

이 코드를 짜두는 걸 추천합니다. 지금 제 게임은 1024*768 해상도이고, 윈도우 창모드로 뜨게끔 했기 때문에 저렇게 짰고, 해상도에 따라서 괄호 안의 내용은 변경하면 됩니다. 1920*1080 해상도에 전체화면을 원한다면 (1920, 1080, true) 이런 식이죠.

처음에 그렇게 안 하고 무작위로 캔버스를 만든 뒤에, 버튼을 만들고 버튼에다가 기능을 넣는 것만 적용했습니다. 그래서 해상도가 바뀌니까 버튼이 안 보이거나 원하는 위치에 안 나오는 등 오류가 발생했지만, 그건 수작업으로 포지션과 피벗, 혹은 높이와 너비를 바꾸면 임시방편이 되기 때문에 이 부분은 나중에 또 문제가 생기면 수정하는 걸로 하겠습니다. 그것보다는 버튼이 제대로 작동하는지 따지는 것부터 필요했죠.

하이어라키창에서 버튼을 만들게 되면 하단에 'OnClick'이라는 창이 있습니다. 그 버튼을 클릭했을 때 어떤 함수를 호출할 건지 선택하라는 것이죠. 그 OnClick에 들어갈 함수를 버튼에 주지 않고, 씬에 빈 오브젝트를 만든 뒤에 UIManager라는 이름을 붙여놓고 그곳에다가 줬습니다. 별도 오브젝트를 불러오고, 거기에 있는 함수를 불러오는 방식이기 때문이죠.



▲ 이처럼 코드를 만들어두고



▲ 빈 오브젝트에 스크립트를 넣은 뒤 버튼의 OnClick에 넣고 해당 함수를 불러오도록 하면 됩니다

씬 넘어가는 것을 관리하는 UIManager 오브젝트를 만들었으니, 여기에 어떤 스크립트를 또 넣어야 구동될지 생각해봐야 합니다. 일반적인 플랫포머 게임에서 어떻게 하는지 차근차근히 짚어봐야 하는 것이죠.

씬의 구성을 살펴보면 게임을 시작할 때 시작 버튼과 종료 버튼이 있고, 시작 버튼을 누르면 게임플레이 화면으로 넘어가야 합니다. 종료 버튼은 말 그대로 게임을 끝내고 바탕화면으로 돌아가죠. 게임오버되면 다시 시작하거나, 처음 화면으로 돌아갑니다. 클리어하면 다시 시작하고요.

씬을 넘어가는 코드는 스크립트 맨 위에 있는 using 란에 using UnityEngine.SceneManagement;을 추가해줍니다. 그리고 나서 특정 조건 함수 밑에 SceneManager.LoadScene("씬 이름");을 넣으면 그 조건에 따라서 씬이 넘어갑니다. 예를 들어서 플레이어의 박스콜라이더 Trigger와 충돌했을 때 씬을 불러오고 싶다면

void OnTriggerEnter2D(Collider2D collision)
{
if(collision.CompareTag("Player"))
{
SceneManager.LoadScene("Clear");
}

이와 같은 방식으로 짜면 됩니다. 이건 스테이지의 클리어 지점에 플레이어가 도달했을 때 클리어씬으로 넘어가는 코드의 일부죠. 버튼을 눌렀을 때, 그에 맞는 씬으로 가게끔 하려면 단순한 void 함수에다가 SceneManager.LoadScene코드를 활용하고 버튼의 OnClick에 해당 함수를 추가하면 작동합니다. 문제는 이 코드를 어떤 상황에서 불러와야 할지 생각하는 것이죠.



▲ 위의 예제 코드를 활용한 결과.gif

앱을 종료하는 기능을 추가하고 싶다면 Application.Quit();을 적절한 곳에 배치하면 됩니다. 만약에 UI상의 어떤 버튼을 누르면 나가게 만들고 싶다면 스크립트를 만들고, 단순 void 함수에 이 코드를 배치한 뒤에 앞서 언급한 버튼의 OnClick()기능을 활용하는 식이죠.

그 외에 특정 버튼을 눌렀을 때 게임 종료하는 코드는 void Update()내에서 Input.GetKeyDown(키 이름)을 활용하는 식으로 구축했습니다. 예제는 다음과 같습니다.

void Update()
{
if(Input.GetKeyDown(KeyCode.Escape))
{
Application.Quit();
}
}

이렇게 게임을 종료하는 코드까지 다 짜고, 이제 프로젝트를 빌드하고 구동하는 작업까지 진행해보겠습니다. 프로젝트를 빌드하기 위해서는 File->Build Settings에 들어가서 플랫폼과 빌드에 포함될 씬을 고르고 Build를 누르면 됩니다. 이 단계에서 어떤 연유에서인지 WebGL, 안드로이드 빌드업이 제대로 안 되서 일단 PC로 해보았습니다만, 그 전에 뭔가 더 체크해야 할 게 있었습니다. Player Settings에 들어가서 해상도 비율 설정을 하고 그런 것 등이죠. 그리고 빌드를 시작할 때 아이콘 설정을 안 해두면 그냥 유니티 아이콘이 뜨니까, 이를 바꿔주는 작업도 필요했고요.



▲ 작업이 일단락되면 빌드를 시작하면 됩니다

이와 같이 여러 고비를 겪긴 했지만, 어찌저찌해서 데모 버전까지 만들었습니다. 물론 이걸로 끝은 아닙니다. 전에 야심차게 말했던 2D 라이트 기능이 정확히 어떻게 작동하는지 이해하지 못해서 그걸 활용하지 못했고, 이펙트는 하나도 안 넣은 상태죠. 피격 때 일순 무적이 되는 판정도 구현하지 못했고, 오디오클립이 없어서 듣는 재미가 하나도 없는 상태입니다.

▲ 모든 작업이 끝난 뒤에 나온 데모 버전. 사운드는 하나도 삽입하지 않은 상태입니다



▲ 사내 테스트 중인 데모 버전






▲ 해본 사람들의 평가. 개발자들의 저 멘트를 직접 하게 되리라곤 생각도 못했습니다

그리고 무엇보다도 횡스크롤 플랫포머의 꽃이라고 할 수 있는 갖가지 기믹과 발판, 보스도 구현이 안 된 상태입니다. 더군다나 기껏 만들어두고 배포할 방법도 미처 생각해두지 않았으니, 아직은 갈 길이 한참 남았죠.



■ 부록 - 소스 코드 중간 공개

비록 미완성인 상태지만, 현재까지 만들어둔 소스 코드를 전부 공개하고자 합니다. 물론 아직 구현해야 할 걸 다 하지 못한 상태라 최종본은 아닙니다만, 처음 횡스크롤 플랫포머를 만들어보고자 하는 분들께 참고가 됐으면 하는 바람입니다. 저 스스로 코드를 마개조하고 때로는 직접 짜면서 공부하려고 주석을 달아둔 것도 있는데, 100% 정확한 것이 아닌 만큼 어디까지나 참고 정도로만 봐주시면 되겠습니다.

※ 주의: //, /* */ 안의 주석은 직접 공부하면서 적은 것과 참조한 코드에 수록된 주석을 그대로 넣었습니다. 일부 비문이 있을 수 있습니다. 글자 간격도 원문 그대로 수록했습니다.


■ 주인공(유채림)에게 넣어둔 스크립트




ChaerimController - 캐릭터 이동 및 조작을 위해 삽입한 스크립트

using System.Collections;
using System.Collections.Generic;
using UnityEngine.SceneManagement;
using UnityEngine;

public class ChaerimController : MonoBehaviour
{
[HideInInspector] public bool facingRight = true;
[HideInInspector] public bool jump = false;

public float moveForce = 365; // 이동할 때 쓰는 힘
public float maxSpeed = 5; // 최고 이속
public float jumpForce = 1000f; //점프할 때 가해지는 힘
public Transform groundCheck; // 그라운드체크
public int attackDamage = 20; //일반 공격 데미지
public int crushDamage = 50; // 점프크러시 데미지

private bool grounded = false; //그라운드체크가 닿기 전에는 false
private Animator anim; // 애니메이터 넣기 위한 코드
private Rigidbody2D rb2d; // 리지드바디2D

public Collider2D AttackRange; // 공격범위, 채림 앞에 AttackRange 박스 Collider2D
public Collider2D CrushDamage; // 점프크러시 범위. 채림 기준 45도 밑쪽에 박스 Collider2D
private float attackTimer = 0; // 공격 판정 타이밍
private float attacked = 2000f; // 공격 끝난 시점의 시간?

// Use this for initialization
void Awake()
{
anim = GetComponent(); // 애니메이터 불러 와
rb2d = GetComponent(); // 리지드바디2D 호출
AttackRange.enabled = false; // AttackRange는 처음엔 비활성화, Fire의 순간 true; attacked일 때 비활성화
}
// Update is called once per frame
void Update()
{
grounded = Physics2D.Linecast(transform.position, groundCheck.position, 1 << LayerMask.NameToLayer("Ground"));
// grounded의 정의

if (Input.GetButtonDown("Jump") && grounded) //점프하기 위한 조건
{
jump = true;
anim.SetTrigger("Jump");
}
}

void FixedUpdate()
{
float h = Input.GetAxis("Horizontal"); // 좌우 이동을 위한 인풋 설정
bool fire = Input.GetButton("Fire1"); // 일반 공격을 위한 인풋 설정
bool fire2 = Input.GetButton("Fire2"); // 점프크러시를 위한 인풋 설정

anim.SetFloat("Speed", Mathf.Abs(h));
// 좌우 이동 조건을 위한 설정, 스피드의 절대값이 몇 이상일 때 움직이도록

if (h * rb2d.velocity.x < maxSpeed)
rb2d.AddForce(Vector2.right * h * moveForce);
//좌우 버튼X리지드바디의 X축 내의 속도값 < 5면 리지드바디2D에 힘이 가해짐(Vector(1,0)*h*moveForce(365)

if (Mathf.Abs(rb2d.velocity.x) > maxSpeed)
rb2d.velocity = new Vector2(Mathf.Sign(rb2d.velocity.x) * maxSpeed, rb2d.velocity.y);
//채림의 x축 내의 속도 절대값이 최대 속도보다 빨라지면? 벨로시티값이 새로 생김
//Mathf.Sign은 부호 반환 = 양수나 0은 1, 음수면 -1로.
//즉 채림의 velocity값 = 새로운 벡터2(1 혹은 -1 곱하기 최대 속도, 그리고 y축에서의 속도값)

if (h > 0 && !facingRight)
Flip();
// 만약 h가 우측을 눌렀는데 facingRight(우측 보는 상태)가 아니다? 그러면 Flip 호출

else if (h < 0 && facingRight)
Flip();
//혹은 좌측으을 눌렀는데 facingRight(우측 보는 상태)다? 그러면 Flip 함수 호출

if (jump) //점프 상태일 때
{
anim.SetTrigger("Jump"); //애니메이션 파라미터 중 점프 트리거를 발동
rb2d.AddForce(new Vector2(0f, jumpForce)); //강체에 힘이 주어짐(벡터2( x축은 영향 없음, jumpforce가 가해짐)
jump = false; //그리고 점프 상태가 해제
}

anim.SetBool("Fire", fire); // 일반공격 애니메이터 트랜지션 조건 (Bool 값) 지정
anim.SetBool("Fire2", fire2); // 점프크러시 애니메이터 트랜지션 조건 (Bool 값) 지정
/*
* 점프크러쉬 할 때 추가해야 할 것 : 낙하 가속도, flip 동결(좌우로 움직이면 이상하니까)
* 1타, 2타는 AttackRange 활성화, 마지막 낙하해서 찍는 순간에만 CrushRange 활성화
*
*/

if (grounded) // 땅에 닿은 상태면
anim.SetTrigger("Grounded"); //애니메이터 조건 중 Grounded를 트리거

if (Input.GetButton("Fire1") && !fire) // 일반 버튼을 눌렀는데 공격하는 상태가 아니면?
{
fire = true; // 파이어가 트루,
attackTimer = attacked; //어택 타이머값은 2000f

AttackRange.enabled = true; //어택레인지 활성화
}

if (fire) //파이어 버튼 눌렀을 때
{
if (attackTimer > 0) // 어택타이머값이 0 이상이면
{
attackTimer = Time.deltaTime; //어택타이머값 = 지난 프레임이 완료되기까지 걸리는 시간
}

else //어택타이머값이 0 미만이면?
{
fire = false; //일반 공격은 x
AttackRange.enabled = false; // 어택레인지 비활성화
}
}
}

void Flip() // 이동방향이 바뀔 때 호출되는 함수
{
facingRight = !facingRight; //facingRight나
Vector3 theScale = transform.localScale; // vector3스케일 =부모 트랜스폼과 상대적인 트랜스폼 스케일
theScale.x *= -1; // 스케일.x는 그 마이너스값이나 플러스나 적용.
transform.localScale = theScale; //로컬스케일 x는 절대값은 동일, 음수, 양수 부호만 달라짐.
}

void GameOver() //게임오버 됐을 땐?
{
SceneManager.LoadScene("GameOver"); //게임 오버 씬으로 ㄱㄱ
}
}


ChaerimStats - 캐릭터 체력, 상태와 연관 있는 스크립트

public class ChaerimStats : MonoBehaviour
{
public int coinsCollected = 0; // 동전, 아직 만들진 않았음.
public int startingHealth = 100; // 시작할 때 체력
public int currentHealth; // 현재 체력

//원본에는 체력바가 있었으나 오류떠서 일단은 삭제

public Image damageImage; // 피격받을 때 화면에 뜨는 이미지, 아직 구현 못함
// public AudioClip deathClip; // 오디오클립, 나중에 넣어야 함.
public float flashSpeed = 5f; // 피격받을 때 화면이 깜빡이는 속도, 아직 구현 못함
public Color flashColour = new Color(1f, 0f, 0f, 0.1f); // 이것 역시도 미구현...원본에서 뭐였는지 까먹음.
public int damage; // 데미지

Animator anim; // 애니메이터
AudioSource playerAudio; // 오디오소스 컴퍼넌트, 아직 사용은 안 함
ChaerimController chaerimController; // 플레이어컨트롤러(여기선 채림컨트롤러) 스크립트에서 레퍼런스 따올 거임

public bool isDead; // 플레이어가 죽었나 안 죽었나 나타내는 bool값, 왜 퍼블릭으로 했는지는 기억이 안 남
public bool damaged; // 데미지 입었나 안 입었나 나타내는 bool값
public bool isImmune = false; // 무적 상태일 때 bool 값
public float immunityDuration = 3f; // 무적상태 지속 시간
private float immunityTime = 0f; // 무적상태 발동 시작하면서 가는 타이머. 처음엔 0부터 시작함.

void Awake()
{
anim = GetComponent();
playerAudio = GetComponent();
chaerimController = GetComponent();
//일단 애니메이터 등등등 다 세팅, 채림컨트롤러 스크립트도 활용되기 때문에 불러옴.

currentHealth = startingHealth;
//시작할 때 체력 = 현재 체력으로 세팅
}

void Update()
{
if(this.isImmune == true)
{
immunityTime = immunityTime + Time.deltaTime;
if(immunityTime >= immunityDuration)
{
this.isImmune = false;
this.damaged = false;
}

//피격받으면 일순 무적 상태가 되는 코드인데 이상하게 발동이 안 되고 있음. 수정 요망.
}
}

public void TakeDamage(int damage, bool playHitReaction)
{
if(this.isImmune ==false)
{
currentHealth -= damage;
//currentHealth = currentHealth - damage; 이런 의미임 현재 체력 = 현재 체력 - 데미지, 즉 현재 체력에서 데미지만큼 감소된다는 뜻.

if (playHitReaction == true)
{
PlayHitReaction();
}
//이것도 작동 안 함.
}


// Set the health bar's value to the current health.
/* if (healthSlider != null)
healthSlider.value = currentHealth;*/
// Play the hurt sound effect.
// if (playerAudio)
// playerAudio.Play();

// If the player has lost all it's health and the death flag hasn't been set yet...
if (currentHealth <= 0)
{
// ... it should die.
Death();
}

else if(playHitReaction == true)
{
PlayHitReaction();
}
}

void PlayHitReaction()
{
this.isImmune = true;
this.immunityTime = 0f;
this.gameObject.GetComponent().SetBool("Damage", damaged);
}


public void CollectCoin(int coinValue)
{
this.coinsCollected = this.coinsCollected + coinValue;
// 동전 모으면 어떤 효과 주기 위한 기본 단계, 아직 디테일 설계 중.
}

void Death()
{
// Set the death flag so this function won't be called again.
isDead = true;
// Tell the animator that the player is dead.
anim.SetTrigger("Die");

// Set the audiosource to play the death clip and play it (this will stop the hurt sound from playing).
/* if (playerAudio)
{
playerAudio.clip = deathClip;
playerAudio.Play();
}*/

// Turn off the movement and shooting scripts.
chaerimController.enabled = false;
}
}


ChaerimNormalAttack - 일반 공격 스크립트. AttackRange에 추가됨.




using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ChaerimNormalAttack : MonoBehaviour
{
public int damage = 30; //데미지는 30
public bool playHitReaction = false; //적의 피격 반응은 처음엔 거짓

void OnTriggerEnter2D(Collider2D collider) //자 이제 트리거 박스 안에 뭐가 들어갔습니다 짜라랏짜란
{
if (collider.tag == ("Enemy")) // 그 부딪힌 애 Tag가 Enemy라면?
{
EnemyStats stats = collider.gameObject.GetComponent(); //일단 EnemyStat에 있는 콜라이더 참고합니다
stats.TakeDamage(this.damage, this.playHitReaction);//그리고 거기에 있는 TakeDamage()함수에 데미지 int 값 넣어주고, 리액션은 아직 미구현
}
}
}


ChaerimJumpCrush - 특수 공격. CrushRange에 넣은 스크립트




using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ChaerimJumpCrush : MonoBehaviour
{
public int damage = 60; //이거 데미지는 60
public bool playHitReaction = false; // 역시나 반응은 아직 미구현이지만 어쨌든 false라고 세팅

void OnTriggerEnter2D(Collider2D collider) //일반 공격과 마찬가지로 그 범위 안에 뭐 들어왔나 trigger 세팅
{
if (collider.tag == ("Enemy"))//그게 적이면?
{
EnemyStats stats = collider.gameObject.GetComponent();//EnemyStats 스크립트 갖고 있는 애의 collider 참고하고
stats.TakeDamage(this.damage, this.playHitReaction);//리액션은 아직 미구현했지만 TakeDamage 함수에 데미지 수치= 60 전달 ㄱㄱ
}
}
}


■ 클라인봄(자폭형 적)에게 넣어둔 스크립트




KleinBombController - 적의 이동과 관련된 스크립트

using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using UnityEngine;

public class KleinBombController : MonoBehaviour
{
[HideInInspector] public bool facingRight = true;
public float MoveSpeed = 10; // 걷는 속도
public float RunningSpeed = 16; // 달리는 속도
public float SlidingSpeed = 20; //슬라이딩할 때 속도
public float BasicSpeed = 10; // 걷는 속도로 다시 돌아올 때

public Transform PlayerFinder; //플레이어 인식하기 위한 장치
public Transform SlidingRange; // 슬라이딩 시작하는 지점을 나타내기 위한 것

private Animator anim; // 애니메이터 넣기 위한 코드
private Rigidbody2D rb2d; // 리지드바디2D

public bool running = false; // 처음부터 달리지 않으므로 이 bool값은 false
public bool sliding = false; // 처음부터 슬라이딩하지 않으니 이하동문

public Transform target; //그래서 얘 타겟이 누구임...인데 이거 다른 코드에서 긁어서 써봤는데 지금은 안 쓰이는 듯?

// Use this for initialization
void Awake()
{
anim = GetComponent(); // 애니메이터 불러 와
rb2d = GetComponent(); // 리지드바디2D 호출
}

void Update()
{
//일단은 보류
}

void FixedUpdate()
{
rb2d.velocity = new Vector3(MoveSpeed, 0, 0);
//이 강체의 속도는 MoveSpeed만큼 갑니다. 날아다니진 않으니까 x값만 바뀜

anim.SetBool("Running", running);
anim.SetBool("Sliding", sliding);

//달리기, 슬라이딩의 조건은 매 프레임마다 불러옴. 그때마다 일단은 false.

if(running) // 달리기 때는?
{
MoveSpeed = RunningSpeed;
rb2d.velocity = new Vector3(MoveSpeed, 0, 0);
// 걷는 속도 = 달리는 속도. 즉 처음의 저 공식 MoveSpeed => RunningSpeed로 바뀌는 거임
}

else
{
MoveSpeed = BasicSpeed;
rb2d.velocity = new Vector3(MoveSpeed, 0, 0);
//달리지 않으면? 기초 속도로 다시 돌아옵니다.
}

if(sliding)
{
rb2d.velocity = new Vector3(MoveSpeed, 0, 0);
MoveSpeed = SlidingSpeed;
//걷다가 갑자기 슬라이딩으로 넘어갈 때
}

else if(running == true && sliding == true)
{
rb2d.velocity = new Vector3(MoveSpeed, 0, 0);
MoveSpeed = SlidingSpeed;
//뛰다가 슬라이딩으로 넘어갈 때
}

else
{
rb2d.velocity = new Vector3(MoveSpeed, 0, 0);
MoveSpeed = BasicSpeed;
//그 모든 상태가 아니면 그냥 기본으로 갑니다잉
}
}

void OnCollisionEnter2D(Collision2D collision) // 뭐랑 부딪혔을 때(트리거 아님, 유의)
{
if (collision.gameObject.tag == "Wall")//벽이랑 부딪혔다?
{
MoveSpeed *= -1;
SlidingSpeed *= -1;
BasicSpeed *= -1;
RunningSpeed *= -1;

Flip();
//방향 전환 갑니다. 그리고 저 값을 다 일일이 -1 주는 이유는, 그래야 이동 방향이 달라짐.
// 깔끔하게 정리할 수 있을 거 같긴한데 현 단계에선 무리.
}

if (collision.gameObject.tag == "Player")// 태그가 플레이어인 애(채림)과 박았다?
{
Explosion();//그럼 폭발이당
}
}

void OnTriggerEnter2D(Collider2D collision) //트리거 코드
{
if (collision.gameObject.tag == "Wall")
{
MoveSpeed *= -1;
SlidingSpeed *= -1;
BasicSpeed *= -1;
RunningSpeed *= -1;
Flip();
//콜리전 때와 마찬가지이긴 한데 이거 상당히 맹점이 있어서 뭔가 개선이 필요함. (플레이어 찾는 트리거까지도 반응을 해버려서...)
}
}

void Explosion()
{
anim.SetTrigger("Explosion");
Debug.Log("BOOM");
//폭발할 때 애니메이션 불러옴.
}

void Flip()
{
facingRight = !facingRight;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
//이동 방향이 바뀔 때, 이미지가 그에 맞춰서 뒤집히게끔하는 코드.
}
}


KleinBombPlayerFinder -플레이어를 탐지하는 코드




using System.Collections;
using System.Collections.Generic;

using UnityEngine;

public class KleinBombPlayerFinder : MonoBehaviour
{
// 플레이어를 찾았다! 어떻게 해야 할까?
private KleinBombController parent;
//일단 이 폭탄의 스크립트를 불러와봅시다
void Start()
{
parent = GetComponentInParent();
//클라인봄컨트롤러의 요소를 부모로 호출한다는데...
}

void OnTriggerEnter2D(Collider2D collision) //일단 이 박스 콜라이더에
{
if (collision.CompareTag("Player")) //플레이어 태그를 단 뭔가가 들어왔다
{
parent.target = collision.transform;
parent.running = true;//그럼 뛰어. KleinBombController의 if(running) 참조.
}
}

void OnTriggerExit2D(Collider2D collision)
{
if(collision.CompareTag("Player")) //범위 내에서 플레이어가 사라졌다?
{
parent.target = null;
parent.running = false; //그럼 달리기는 false, 그리고 KleinBombController의 if(running)의 else문 참조
}
}
}


KleinBombSliding - 플레이어가 사정거리에 들어왔을 때 슬라이딩하는 코드




using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class KleinBombSliding : MonoBehaviour
{
// 슬라이딩 조건. 기본 구조는 러닝과 똑같음.
private KleinBombController parent;

void Start()
{
parent = GetComponentInParent();
}

// Update is called once per frame
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Player"))
{
parent.target = collision.transform;
parent.sliding = true;
Debug.Log("슬라이딩 간닷");
}
}

void OnTriggerExit2D(Collider2D collision)
{
if (collision.CompareTag("Player"))
{
parent.target = null;
parent.sliding = false;
Debug.Log("슬라이딩 실패?");
}
}
//여기 이 코드는 KleinBombController의 void FixedUpdate()의 if(sliding)과 연계하는 코드임.
}


ExplosionDamage - 자폭할 때 코드




using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ExplosionDamage : MonoBehaviour
{
public int damage = 50; //데미지 주는 코드
public bool playHitReaction = false; //리액션은 여전히 안 먹힘....

void OnTriggerEnter2D(Collider2D collider) //폭발 범위에 뭐가 들어왔다
{
if(collider.tag == ("Player"))//근데 그게 플레이어 태그를 갖고 있는 녀석(채림)이다
{
ChaerimStats stats = collider.gameObject.GetComponent();//채림 스탯에서 일단 참고하고
stats.TakeDamage(this.damage, this.playHitReaction);//채림컨트롤러 스크립트에 있는 TakeDamage()의 데미지 값이 처음에 설정한 50으로 전달됨
Destroy(gameObject);//그리고 자폭이니까 이 클라인봄은 사라짐. ㅂㅇㅂㅇ
}
}
}


■ 뫼비우스마일(비행형 적)에게 넣어둔 스크립트




MoebiuSmileController - 적 이동 및 사격과 관련된 스크립트

using System.Collections;
using System.Collections.Generic;

using UnityEngine;

public class MoebiuSmileController : MonoBehaviour
{
public float MoveSpeed = 3;

public bool chasing = false; // 추적하게 만들고 싶었으나 아직은 미구현...어려움-_-a
public bool attacking = false; //처음부터 공격하는 게 아니므로 false
public bool firing = false; //처음부터 사격하지 않으니 false

public Transform PlayerFinder; //플레이어 찾는 박스 collider 넣기 위한 거
public Transform ShootingRange; // 사격 시작하는 범위 넣기 위한 collider 넣으려고

private Animator anim; // 애니메이터 넣기 위한 코드
private Rigidbody2D rb2d; // 리지드바디2D

public Transform target; // 타겟은 누구?

public float bulletSpeed = 20; //총알 속도
public GameObject bullet; //총알
public float shootingInterval; //사격 간격
public float bulletTimer; //총알이 발사되고 흘러가는 시간. 처음엔 0부터 시작해서 shootingInterval까지.

// Use this for initialization
void Awake()
{
anim = GetComponent(); // 애니메이터 불러 와
rb2d = GetComponent(); // 리지드바디2D 호출

}

void Update()
{

}

void FixedUpdate()
{
rb2d.velocity = new Vector3(MoveSpeed, 0, 0);
//transform.Translate(new Vector3(MoveSpeed, 0, 0) * Time.deltaTime);
// PlayerCheck();

anim.SetBool("Chasing", chasing);
anim.SetBool("Firing", firing);
// 이 중에 Chasing은 그냥 플레이어 탐지 정도로만 해둠. Firing은 구현

if (firing)
{
attacking = true;
Fire(attacking);
}
//뭔가 꼬였는데 최초에 설정된 코드가 attack과 fire가 명확히 구분하기 어려워서 임시방편으로 그랬던 걸로.
}

void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "Waypoint")
{
MoveSpeed *= -1;
}

if (collision.gameObject.tag == "Wall")
{
Debug.Log("이걸론 못 막지롱");
}

if (collision.gameObject.tag == "Player")
{
Debug.Log("형왔다");
}
}

public void Fire(bool attacking)
{
bulletTimer += Time.deltaTime;
if (bulletTimer >= shootingInterval)
{
Vector2 direction = target.transform.position - gameObject.transform.position;
direction.Normalize();
//방향은 타겟의 위치값 - 이 게임 오브젝트위 위치값 계산 후에 이걸 노멀라이즈한다는데...그것까지밖에 모름.
if (attacking)
{
GameObject bulletClone;
bulletClone = Instantiate(bullet, ShootingRange.transform.position, ShootingRange.transform.rotation) as GameObject;
bulletClone.GetComponent().velocity = direction * bulletSpeed;
bulletTimer = 0;
//공격하면 불릿의 클론들이 계속 나옴. 그리고 그 방향대로 나오게끔 설정함.
}
//여긴 좀 공부가 더 필요할 듯...어렵당.
}
}
}


MoebiuSmilePlayerFinder - 플레이어 탐지 코드, KleinBomb과 메카니즘은 동일




using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MoebiuSmilePlayerFinder : MonoBehaviour
{
//기본 구조는 KleinBombPlayerFinder와 똑같은 코드.
private MoebiuSmileController parent;

void Start()
{
parent = GetComponentInParent();
}

// Update is called once per frame
void OnTriggerStay2D(Collider2D collision)
{
if (collision.CompareTag("Player"))
{
parent.target = collision.transform;
parent.chasing = true;
Debug.Log("???");
}

}

void OnTriggerExit2D(Collider2D collision)
{
if (collision.CompareTag("Player"))
{
parent.chasing = false;
Debug.Log("놓쳤당?");
}
}
}


MoebiuSmileShootingRange - 사격을 시작하는 범위




using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MoebiuSmileShootingRange : MonoBehaviour
{
//기본 구조는 MoebiuSmilePlayerFinder 코드와 똑같음. 다만 이건 MoebiuSmileController의 FixedUpdate() => if(firing)에 영향을 준다는 게 다를 뿐.
private MoebiuSmileController parent;

void Awake()
{
parent = GetComponentInParent();
}

void OnTriggerStay2D(Collider2D collision)
{
if (collision.CompareTag("Player"))
{
parent.target = collision.transform;
parent.firing = true;
}
}

void OnTriggerExit2D(Collider2D collision)
{
if (collision.CompareTag("Player"))
{
parent.firing = false;
}
}
}


Bullet - 총알에 부여된 스크립트

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Bullet : MonoBehaviour
{
public int damage = 10;
public bool playHitReaction = false;

void Awake()
{
Destroy(gameObject, 10f);
//사실은 일정 범위 이상 넘어가면 없어지게끔 하고 싶었으나...그걸 어떻게 짜야할지 몰라서 일정 시간 후에 사라지게끔 설정함.
}

void OnTriggerEnter2D(Collider2D collider)
{
if(collider.CompareTag("Player"))
{
ChaerimStats stats = collider.gameObject.GetComponent();
stats.TakeDamage(this.damage, this.playHitReaction);
Destroy(gameObject);
//기본은 ExplosionDamage와 동일, 총알은 적에게 맞으면 없어져야 하니 destroy 추가.
}
}
}


EnemyStats - 보스를 제외한 적 모두 공통으로 쓸 것. 체력 및 상태를 나타내는 스크립트




sing System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyStats : MonoBehaviour
{
//이 코드는 모든 적에게 다 쓰이는 코드임. 아마도...(보스는 다시 짜야 할 듯?)

public int startingHealth = 100; // 시작할 때 체력
public int currentHealth; // 현재 체력. 이거 퍼블릭으로 놓은 이유는 엔진 내에서 각각 수정하려고.

public float flashSpeed = 5f; // 피격받을 때 깜빡이는 조건 만들려고 한 건데 아직은 제대로 못 만듬
public int damage; // 데미지. 이건 공격에 따라 달라지기 때문에 수치를 따로 지정하지 않음

Animator anim; // 애니메이터 불러
//AudioSource EnemyAudio; // 오디오소스인데 아직 안 만들었음.

public bool isDead;
public bool damaged; // 사망과 피격 조건인데 아직 완벽히 구현은 못함.

public bool isImmune = false;
public float immunityDuration = 3f;
private float immunityTime = 0f; //이것도 플레이어와 동일한 구조에서 수치만 변경하려고 했으나 예상치 못한 사태로 적어두기만 함

void Awake()
{

anim = GetComponent();

// 시작할 때 체력은?
currentHealth = startingHealth;
}


void Update()
{
if (this.isImmune == true)
{
immunityTime = immunityTime + Time.deltaTime;
if (immunityTime >= immunityDuration)
{
this.isImmune = false;
this.damaged = false;
}
//이건 역시나 제대로 구현 X, 채림 스크립트 그대로 따온 것.

}
}


public void TakeDamage(int damage, bool playHitReaction)
{

if (this.isImmune == false)
{
currentHealth -= damage;

if (playHitReaction == true)
{
PlayHitReaction();
}
//이 역시도 잘 안 됨.

}

if (currentHealth <= 0)
{
// 체력 없으면?
Death();

//죽음 코드 불러오세요.
}

else if (playHitReaction == true)
{
PlayHitReaction();
//리액션 코드 불러와야 하는데 정상 작동 안 함...뭐지
}
}

void PlayHitReaction()
{
this.isImmune = true;
this.immunityTime = 0f;
this.gameObject.GetComponent().SetBool("Damage", damaged);

//일단 제대로 잘 안 작동하는데 어쨌든 나중에 수정하기로.
}

void Death()
{
Destroy(gameObject);
//죽었으면 그냥 이건 destroy. 게임오버 씬으로 넘어가거나 다시 부활하거나 그런 기믹이 없으므로.
}
}


■ UI와 관련된 스크립트

ESCOut - ESC 누르면 게임 종료, UIManager에 삽입

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ESCOut : MonoBehaviour
{

void Update()
{
if(Input.GetKeyDown(KeyCode.Escape))
{
Application.Quit();
}
}
//ESC를 누르면 게임이 종료됩니다.

}


UIClear - Clear씬으로 가게 하는 스크립트, UI 매니저에 삽입

using System.Collections;
using System.Collections.Generic;
using UnityEngine.SceneManagement;
using UnityEngine;

public class UIClear : MonoBehaviour
{
public void Clear()
{
SceneManager.LoadScene("Clear");
}
//클리어 씬을 불러오는 함수
}


UIManager - Stage1_1로 가게 하는 스크립트

using System.Collections;
using System.Collections.Generic;
using UnityEngine.SceneManagement;
using UnityEngine;

public class UIManager : MonoBehaviour
{
// Start is called before the first frame update
public void RestartGame()
{
SceneManager.LoadScene("Stage1_1");
}
//Stage 1_1 첫 씬으로 돌아가게 하는 코드
}


UIQuit - UI에 있는 버튼을 누르면 게임 종료하게 만드는 스크립트

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UIQuit : MonoBehaviour
{
// Start is called before the first frame update
public void QuitGame()
{
Application.Quit();
}
// 해당 앱을 종료하시겠습니까? Yes!
}


WalltoGoal - 플레이어가 특정 지점에 도달했을 때 Clear씬으로 가게 하는 스크립트

using System.Collections;
using System.Collections.Generic;
using UnityEngine.SceneManagement;
using UnityEngine;

public class WalltoGoal : MonoBehaviour
{
// Start is called before the first frame update
void OnTriggerEnter2D(Collider2D collision)
{
if(collision.CompareTag("Player"))
{
SceneManager.LoadScene("Clear");
}
//여기에 플레이어가 부딪히면 Clear씬으로 이동합니다~
}
}


UItoMain - StartGame씬으로 이동하는 스크립트

using System.Collections;
using System.Collections.Generic;
using UnityEngine.SceneManagement;
using UnityEngine;

public class UItoMain : MonoBehaviour
{
// Start is called before the first frame update
public void GoToMain()
{
SceneManager.LoadScene("StartGame");
}

//이번엔 StartGame씬으로 이동!
}


ScreenResolution - 해상도 조절하는 스크립트, 맨 첫 씬(StartGame)의 UIManager에만 삽입

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ScreenResolution : MonoBehaviour
{
void Awake()
{
Screen.sleepTimeout = SleepTimeout.NeverSleep;
Screen.SetResolution(1024, 768, false);
}

//해상도 고정 및 창모드로 진행하게 만들기 위한 코드.
}

댓글

새로고침
새로고침

기사 목록

1 2 3 4 5