휴... 지난번에 달랑 한번하고 포스팅을 하지 않았더니 완전 감각이 다 죽어버렸을지도 모르겠다. 결국 뭐든지 열심히 해야지 느는 건데... ㅠㅠ
오늘 실수로 스터디에 늦어버렸다. 밥먹고 나니까 벌써 6시 20분... 그래서 그때 동안 했던 것을 복습하고 2주차에 했던 내용들을 한번 포스팅 하기로 해보았다.(근데 정작 하고나니 애니매이션에 관한 내용은 거의 없어서 그건 다음 포스팅으로 넘기기로 했다.)
이번 포스팅에서 배울 것은!
->스프라이트 쪼개는 방법과 Sprite Editor 설치 방법(정작 이건 나중에 쓰인다)
->rigidBody의 velocity를 이용할 때의 주의사항
->rigidBody의 AddForce
->Box콜라이더를 이용한 발판과 움직일 공간 만들기
의외로 모아보니 스프라이트는 정말 많지 않았다. 전부, 내 손으로 나머지 것을 만들어야하는 것이라는게 새삼 느껴진다.
아무튼 스프라이트 명칭은 각 스프라이트의 성격에 따라서 다르게 설정하고, 최대한 일관성 있게 만들기 위해서 노력했다. Fill_, Multi_, Single_같은 명칭은 좀 더 스프라이트를 찾기 쉽게하기 위해서 설정해 둔 것이다.
아무튼, 이런 스프라이트를 전부 맞는 타입으로 설정해두어야 한다. Single과 Fill은 타입만 Default에서 Sprite(2D and UI)를 눌러 두기만 하면 된다.
Mutil의 경우에는 좀 복잡할 수도 있는데, 우선 밑의 Sprite Mode를 Mutilple로 설정해둔다.
그런 다음 스프라이트 에디터를 이용해야 하는데, 이게 설치가 안되어 있어서 창이 안뜰 수도 있다. 그럼 Package Manager에 들어가서 2D Sprite를 깔아야한다.
이렇게 설치가 전부 끝났다면 Sprite Editor를 이용할 수 있다!
그렇게 안에 들어간 다음, 창의 왼쪽 상단의 Silce를 클릭하고, Grid By Cell Count를 이용해서 Unity가 알아서 스프라이트를 자를 수 있게 한다. 이 스프라이트의 경우 3x3으로 이루어져 있었기 때문에, C와 R에 각각 3을 넣어 주었다. 그 후에는 간단히 Slice를 눌러주면 된다.
그 이후에는, 이렇게 복습할 몇몇 아이템들을 가져다 놓았다. 현재 오브젝트 생성만 해놓고, Inspector와 Script는 하나도 건드리지 않았다.
Slime에 RigidBody2D를 넣고, 스크립트를 작성해보자.
우선 Slime.cs는 다음과 같이 작성한다. 각 버튼에서 불리는 함수들이 있을 것이다 :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Silme : MonoBehaviour
{
[SerializeField] public float speed;
[SerializeField] public float jumpforce;
public Vector2 presistAddVelocity;
private Rigidbody2D rigidBody;
public bool is_jumping;
// Start is called before the first frame update
void Start()
{
rigidBody = this.GetComponent<Rigidbody2D>();
presistAddVelocity = new Vector2(0, 0);
is_jumping = false;
}
// Update is called once per frame
void Update()
{
//끝에 rigidBody.velocity를 더해줘서 기존 스피드를 제거하지 않도록 한다.
rigidBody.velocity = presistAddVelocity * speed + rigidBody.velocity;
}
public void Jump(){
Vector2 jumpVec = is_jumping ? Vector2.zero : new Vector2(0, jumpforce);
rigidBody.AddForce(jumpVec, ForceMode2D.Impulse);
}
public void Attack_Start(){
}
public void Attack_Stop(){
}
private void OnCollisionStay2D(Collision2D collision2D){
is_jumping = false;
}
private void OnCollisionExit2D(Collision2D collision2D){
is_jumping = true;
}
}
그리고 SlimeBtns.cs는 다음과 같이 작성한다. 각 버튼이 하는 기능을 수행하기 위한 함수들이다 :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class SlimeBtns : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
[SerializeField] Silme silme;
//모든 지속적인 인풋관리가 필요한 슬라임을 조정하는 버튼들.
public void OnPointerDown(PointerEventData ped){
switch(gameObject.name){
case "LeftButton":
silme.presistAddVelocity += new Vector2(-1f, 0);
break;
case "RightButton":
silme.presistAddVelocity += new Vector2(1f, 0);
break;
case "AttackButton":
silme.Attack_Start();
break;
default:
break;
}
}
public void OnClickJumpButton(){
silme.Jump();
}
public void OnPointerUp(PointerEventData ped){
silme.presistAddVelocity = Vector3.zero;
silme.Attack_Stop();
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
이벤트 시스템 처리를 위해서 위에 using UnityEngine.EventSystems;를 더해주는 것을 잊지 말자.
-> 지난 1주차 포스트 링크 : passingprogram.tistory.com/15
지난 1주차에서는 EventTrigger, 즉 각 오브젝트의 컴포넌트를 이용해서 직접 이벤트 상황에 불릴 함수를 지정했다. 근데 그렇게 하면 나중에 지정할 함수가 많아질수록 관리가 복잡해지는 문제가 있다. 따라서 같은 OnPointerDown을 이용한다면, EventSystem을 이용해서 대신 CallBack을 해주는게 보통이라고 선배님이 가르쳐 주셨다. 그게 코드의 이 부분이다.
using UnityEngine.EventSystems;
public class SlimeBtns : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
[SerializeField] Silme silme;
//모든 지속적인 인풋관리가 필요한 슬라임을 조정하는 버튼들.
public void OnPointerDown(PointerEventData ped){
switch(gameObject.name){
case "LeftButton":
silme.presistAddVelocity += new Vector2(-1f, 0);
break;
case "RightButton":
silme.presistAddVelocity += new Vector2(1f, 0);
break;
case "AttackButton":
silme.Attack_Start();
break;
default:
break;
}
}
IPointerDownHanlder를 따로 상속(아마도 Java의 Interface와 비슷하지 않을까...?)함으로써, 알아서 이벤트를 적용한다.게다가, 모든 버튼들에 대해서 같은 스크립트를 간편하게 이용할 수 있기 때문에 여간 편한 것이 아니다.
다만 점프버튼은 단순히 OnPointerDown이 아니라 Button의 OnClick메소드를 이용해서 CallBack하기로 했다.(누르고 뗐을 때 1번만 불리기 때문에 OnClick을 이용한다.)
Jump함수에는 전에 보이지 않았던 rigidBody의 AddForce라는 함수가 쓰였다. ForceMode.Impulse의 뜻은 "잠시 동안 큰 힘"을 주라는 명령을 내리는 것이다. 우리가 넘겨준 벡터의 방향까지 고려하면, "윗 방향으로 큰 힘을 잠시 동안 주어라"라는 뜻으로 생각할 수 있다. 이 함수와 gravity Scale의 콜라보라면 자연스럽게 점프하는 슬라임을 만들 수 있다.
나는 이번에 기본적인 rigidBody의 함수들을 다시 접하면서 진짜진짜 중요한 걸 알았는데, 왠만하면 rigidBody.velocity를 대입문(=)으로 초기화하지 말라는 것이다. 절대로. 그렇게 하면 rigidBody의 Addforce가 먹통이 되는 것은 물론이요, update함수 뒤에 불리는 event계열 함수는 거의 없다는 것 때문에, 이 슬라임의 동작을 바꾸려는 시도는 대부분 막히고 중력 조차 제대로 안먹는다... 따라서, velocity를 바꿀 일이 있다면 더하는 식으로 해주는 것이 좋다.
이렇게 스크립트 세팅을 해주면 바닥을 깔아주지 않을 수는 없다!
그래서 Box콜라이더가 무엇인가... 하면, 단순히 이미지 뿐이었던 게임오브젝트들 끼리 실제로 부딛힐 수 있게 만들어 주는, 유니티의 아주 좋은 시스템이라고 할 수 있다.(3D에선 Tile-Based Collider도 만들기 까다롭다...) 슬라임에도 BoxCollider2D를 Add Compent해주자. (그냥 BoxCollider 말고. 그건 3D용이다.) 그 다음 바닥 오브젝트를 생성해야 하는데, Box콜라이더를 넣지 않은 채로 타일이 될 오브젝트를 Scene메뉴를 통해서 집어넣는다.
그리고 그 중에 1개만 Box콜라이더2D를 만든다. 어짜피 전부 같은 높이에 있는데다가 굳이 여러 개를 만들면 타일에 모서리가 끼이듯이 가끔 안넘어가지는 경우도 있는듯 하니까.(특이 여기서는 RigidBody에 rotation 락을 걸어놓았다.) 하나만 만드는 것이 게임에도 부담이 덜하고 일석이조이다.
또 넘쳐나는 플로어들은 이렇게 매니저 안에다가 넣어놓으면 좀 더 쉽게 관리할 수 있다.
이렇게 한 다음 Slime 스크립트의 speed, jumpforce, 그리고 rigidbody의 LinearDrag, Gravity Scale을 알맞게 조절하자! 그렇게 하면 이런 결과를 얻을 수 있다.
우선, 움직이는 것에 관해서라면 이렇게 차라리 한 개의 포스트로 정리하는 것이 좋아보일 것 같으니, 애니메이션에 관한 포스트는 다음 포스트에 계속해야겠다.
'프로그래밍 > Unity' 카테고리의 다른 글
Unity - Drag & Drop - How to make Post (2) | 2021.11.25 |
---|---|
Unity - Coroutine(코루틴) - Reference Post (0) | 2021.08.13 |
내가 유니티에선 왕초보? - 4주차 - 레이어와 Hp (0) | 2021.05.08 |
내가 유니티에선 왕초보? - 3주차 - 애니메이션 (0) | 2021.05.07 |
내가 유니티에선 왕초보? - 1주차 - 기본기 (0) | 2021.04.03 |