애니메이션 맛보기(1-3)
이미지를 애니메이션화 하기
- 'Project' -> Images 폴더+ -> 이미지+
- 'Hierarchy' -> 2D Object -> Sprites -> Square+ -> 'Inspector' -> Sprite Renderer -> Sprite -> 이미지+
- 'Project' -> Assets -> Animations 폴더+ -> Animation Clip+ -> 'InsPector' -> Loop Time 체크
- 'Hierarchy' -> Square 클릭 -> 'Project' -> Animations Clip -> 'InsPector'
- 'Project' -> Animations Clip -> 이미지+
캐릭터 움직이기(1-4)
스크립트의 정의
- 스크립트는 class라고 하는 단위에서 움직이게 된다. 하나의 스크립트 - 하나의 class. 나중엔 하나에 여러 class를 함께 둘 수도 있긴 하다.
- ; (세미콜론) = '코드 한 줄이 끝났다'고 하는 범위를 구분 지어 주는 역할.
- 빨간 줄이 남았다 = 아직 더 작성할 코드가 남았다
- Debug.Log = 일종의 프린트
- 연산 = 계속해서 어떤 값을 변화시켜 주는 것.
- Start는 딱 한 번만 실행되기 때문에 반복은 Update
- // Update is called once per frame <- 프레임은 1초에 연산되는 횟수.
PC의 성능이 좋으면 프레임 수 많아짐 -> 1초에 연산하는 횟수가 많아짐
Transform의 Position값 설정
- 르탄이를 움직여 보자.
- 가로 값을 더해주려면 Update에다 작성한다.
void Update()
{
transform.position.x +=
}
- +=은 더해서 넣어준다는 뜻
- 코딩에서 =(이퀄)은 수학적 기호와 다름
- 수학 -> 같다 / 코딩 -> 넣어준다
void Update()
{
transform.position.x += 1
}
- 이러면 에러가 뜬다. (에러코드 CS1612)
- 포지션은 X, Y, Z 하나하나에 직접 접근할 수는 없다. 직접 값 넣기 불가
- 가지고 올 수는 있지만, 넣어줄 수는 없음
void Update()
{
transform.position.x += new Vector3(1f, 0, 0);
}
- Vector3 = Position 값
- InsPector 창 Transform의 X, Y, Z값을 각각 벡터에서 담당
- 다만 매번 new Vector3(1f, 0, 0); 값을 넣어주기는 번거로움. 그래서 C# 스크립트에서 좀 더 편한 기능을 제공한다.
void Update()
{
transform.position += Vector3.right;
}
- 이렇게 할 수 있다.
- 그런데 또 이렇게 하면 PC의 성능에 따라 속도 차이가 발생.
(1초마다 x에 1을 더해주는 연산 속도가 저마다 다르면 -> aPC는 1초에 60번 계산, bPC는 1초에 120번 계산..)
- 모든 기기들이 똑같은 속도를 낼 수 있게끔 맞춰줘야 함 -> 프레임 설정
- 결국 프레임을 똑같이 맞춰주는 코드 작성 필요
void Start()
{
Application.targetFrameRate = 60;
}
- 이렇게 해주면, 이 코드가 실행되는 모든 기기들은 이 프레임의 타겟이 60이 된다.
- 모든 기기들이 1초에 60번만 계산할 수 있게끔 맞춰주는 것
Player의 속도 조절
void Update()
{
transform.position += Vector3.right;
}
- 여기서 Vector3.right가 1이 된다. 이 숫자를 수정하려면 곱셈을 활용하자.
void Update()
{
transform.position += Vector3.right * 0.05f;
}
- 0.05 뒤의 f는 소수점 단위의 수를 표현한다는 의미
- 현재 x, y, z의 값이 1, 0, 0 이니까 그 값에 각 0.05를 곱해주는 것
- 결국 x값만 0.05가 된다.
캐릭터 움직이기(1-4)
direction
- 르탄이가 벽 끝에 부딪히면 반대로 이동하게 하자.
void Update()
{
transform.position += Vector3.right * 0.05f;
}
- 조건문 문법을 활용해 로직 구현
- float direction = 0.05f; 변수 입력하자.
float direction = 0.05f;
void Update()
{
transform.position += Vector3.right * direction;
}
- 0.05f를 direction;으로 변경
- 변수: 일종의 상자. 누군가가 설정해 놓은 데이터를 그대로 받아올 수 있게 해주는 기능
- direction이라는 상자 안에 0.05라는 데이터를 넣어 놓은 것.
- 반대로 Start에 direction = -0.05f를 넣어줬다면, 연산하는 곳의 direction값도 -0.05가 됨
- 그러면 Vector.right에 1에다가 -0.05가 곱해지니, -0.05가 Position x값에 계속 더해짐 -> 르탄 왼쪽 이동
- 소수점(0.05f)은 float라고 하는 자료형을 가진 변수에다 넣어줘야 함
- 자료형: 일종의 타입. (등기우편, 일반우편...)
자료형의 종류
float direction = 0.05f;
int number = 1;
string str = "안녕";
- int = 정수형. 소수점 사용 불가
- string = 문자열
- str = 문자를 넣을 수 있는 자료형. 숫자 불가
- 파란색으로 표시되는 자료형, 초록색으로 표시되는 값, 데이터의 자료형을 맞춰줘야 함
- 이제 르탄이의 x값이 2.6(오른쪽 벽)이 되었을 때 direction값을 음수로 바꿔주고, x값이 -2.6(왼쪽 벽)보다 작아지려고 할 때 direction값을 양수로 바꿔주면 된다.
- 이를 위해 조건문 if 작성 필요
if(transform.position.x > 2.6f)
{
}
- = 만약, 르탄 게임 오브젝트에 붙어 있는 컴포넌트 transform에 있는 position의 x값이 2.6보다 커진다면
if(transform.position.x < -2.6f)
{
}
- 만약, 르탄 게임 오브젝트에 붙어 있는 컴포넌트 transform에 있는 position의 x값이 -2.6보다 작아진다면
- 이렇게 조건을 붙일 수 있다. 조건 내용은 중괄호 안에 넣자.
void Update()
{
if(transform.position.x > 2.6f)
{
direction = -0.05f;
}
if(transform.position.x < -2.6f)
{
direction = 0.05f;
}
transform.position += Vector3.right * direction;
}
- 이렇게 해주면 오른쪽 왼쪽으로 움직이는 르탄이 완성. 이제 못나간다.
GetComponent
- 르탄이가 벽에 부딪힐 때 이미지를 반대로 만들어 주자.
- 'Inspector' -> Sprite Renderer -> Flip의 x, y 체크해주면 뒤집히는 르탄이를 볼 수 있다.
- 이 flip이라는 옵션을 스크립트로 가져와야 함
if(transform.position.x > 2.6f)
{
SpriteRenderer.flip;
direction = -0.05f;
}
- 근데 이렇게 하면 안됨ㅠ
- 검색을 해보자. >SpriteRenderer 스크립트<
- 'renderer = GetComponent(); // 게임오브젝트의 스프라이트 렌더러 컴포넌트 가져오기'
- 변수는 일종의 박스니까 SpriteRenderer라고 하는 컴포넌트 데이터를 담아줘야 함. 그래서 GetComponent를 활용한다.
SpriteRenderer renderer;
void Start()
{
Application.targetFrameRate = 60;
renderer = GetComponent<SpriteRenderer>();
}
void Update()
{
if(transform.position.x > 2.6f)
{
renderer.flipX = true;
direction = -0.05f;
}
if(transform.position.x < -2.6f)
{
renderer.flipX = false;
direction = 0.05f;
}
- 이렇게 하면, 정상적으로 뒤집히는 르탄이를 볼 수 있다.
- GetComponent는 말 그대로 컴포넌트를 가지고 올 수 있는 기능. 메서드 또는 함수라고도 부를 수 있다.
- GetComponent 사용 시 주의할 점
- 스크립트가 붙어 있는 곳과 같은 위치에 있어야 함(같은 인스펙터 상의 위치)
- 르탄 스크립트가 붙어있는 인스펙터 창에 같이 있어야 함(같은 줄)
- 그럼 우리가 갖고 올 수 있는 컴포넌트는? Transform, Sprite Renderer, Animator 등.
- 하지만 Main Camera에 있는 Camera 컴포넌트는 불가능하다는 뜻.만약 쓰려면 카메라 인스펙터 창에 르탄 스크립트를 넣어주면 ok
클릭 시 반응 구현(direction, renderer.flipX)
- 클릭했을 때 르탄이의 방향(이미지, x값)을 바꿔주자.
void Update()
{
if(Input.GetMouseButtonDown(0))
{
}
}
- 키보드, 마우스, 카메라 등 외부 입력 장치들의 정보는 Input이라고 하는 키워드에 담겨져 있음
- 왼쪽 버튼이 0, 오른쪽 버튼이 1
- 0을 눌렀을 때 우리는 방향 전환, 실제 위치를 반대로 이동해줘야 함 -> 조건문
void Update()
{
if(Input.GetMouseButtonDown(0))
{
direction *= -1;
}
}
- *(별표): 곱센연산. 곱해서 넣는다
- direction(0.05)에 -1을 곱해서 넣는다 -> -0.05
void Update()
{
if(Input.GetMouseButtonDown(0))
{
direction *= -1;
renderer.flipX = !renderer.flipX;
}
if(transform.position.x > 2.6f)
{
renderer.flipX = true;
direction = -0.05f;
}
if(transform.position.x < -2.6f)
{
renderer.flipX = false;
direction = 0.05f;
}
transform.position += Vector3.right * direction;
}
- renderer.flipX도 현재 값과 반대로 넣어줘야 함.
- true와 false, 참과 거짓 이렇게 두 가지 값만 있는 것을 불(bool)값이라고 한다.
- 현재의 불값과 반대되게 바꿔주는 키워드는 느낌표(!) 사용
- 이렇게 해주면 클릭할 때마다 방향과 x값이 바뀌는 르탄이 완성
빗방울 구현하기 - 빗방울 코딩하기(1-5)
Rigidbody 2D
- 빗방울을 구현해 보자.
- 빗방울(2D Object)에 Rigidbody 2D 컴포넌트 추가
- Rigidbody 2D: 물리에 필요한 다양한 힘을 적용시킬 수 있음 (중력, 물체의 무게값, 마찰력 등)
- Rigidbody 2D를 추가하고 실행해 보면 빗방울이 떨어지는 걸 볼 수 있다. 충돌 구현은 아직 불가능
- Ground와 충돌하게 하려면 Circle Collider 2D 컴포넌트 추가
- 빗방울과 충돌할 Ground에도 Box Collider 2D 컴포넌트 추가
- 이러면 땅과 충돌하는 빗방울 완성
Destroy
- Ground에 닿았을 때 파괴되는 빗방울을 만들어 보자.
private void OnCollisionEnter2D(Collision2D collision)
{
}
- 충돌 현상은 OnCollisionEnter2D 안에 적어주면 됨
- On은 어떤 이벤트, 어떤 현상이 발생했을 때 쓰게 됨
- OnCollisionEnter(2D): (2D 환경에서) 충돌이 시작되는 순간
private void OnCollisionEnter2D(Collision2D collision)
{
Debug.Log("충돌");
}
- Rain 컴포넌트에 Rain 스크립트를 추가해 주고 디버그 해보면 충돌을 확인할 수 있다.
이제 여기에 조건문을 넣자.
private void OnCollisionEnter2D(Collision2D collision)
{
if(collision.gameObject.name == "Ground")
{
}
}
- =(이퀄) 기호가 두 개 있으면 같다는 뜻.
- 게임 오브젝트의 이름이 Ground와 같다면 -> true값이 나오면서 중괄호 로직이 실행됨
private void OnCollisionEnter2D(Collision2D collision)
{
if(collision.gameObject.name == "Ground")
{
Destroy(collision.gameObject);
}
게임 오브젝트의 이름이 Ground와 같다면(if) 파괴시켜라(Destroy),
충돌한 물체의 게임 오브젝트를.
- 이러면 대지가 갈라지는 모습을 볼 수 있다.
- Destroy(collision.gameObject);를 Destroy(this.gameObject);로 바꿔주자.
- this는 해당 Rain 스크립트가 붙어있는 자기 자신을 의미
private void OnCollisionEnter2D(Collision2D collision)
{
if(collision.gameObject.name == "Ground")
{
Destroy(this.gameObject);
}
}
CompareTag
- 그런데 게임 오브젝트의 이름이 바뀌면 이 로직이 제대로 동작을 하지 않게 됨.
그래서 방법을 살짝 바꿔줘야 한다.
- Ground 인스펙터 창 맨 위 Tag에 Ground 태그를 추가해 주고 태그에 넣으면 된다.
- 이제 gameObject.name의 name 대신 태그를 넣어주자.
private void OnCollisionEnter2D(Collision2D collision)
{
if(collision.gameObject.CompareTag("Ground"))
{
Destroy(this.gameObject);
}
}
- Ground라는 Tag값이 있을 때 파괴시켜 준다.
- 프로젝트 내에 Ground라는 이름이 여러 개일 수도 있기 때문에, 필요한 Ground에만 Tag를 달아준 것
- 이러면 똑같지만 보다 안전하게(?) 파괴가 된다.
빗방울 구현하기 - 랜덤한 빗방울(1-6)
Random.Range
void Start()
{
float x = Random.Range(-2.4f, 2.4f);
float y = Random.Range(3.0f, 5.0f);
}
- Range는 범위. 최소값과 최대값이 들어가게 됨
- 빗방울 랜덤 위치 x, y값이 소수점이니 float 사용
- 다음엔 이 추출된 값을 Position 값에 넣어줘야 함
void Start()
{
float x = Random.Range(-2.4f, 2.4f);
float y = Random.Range(3.0f, 5.0f);
transform.position = new Vector3(x, y, 0);
}
- 이렇게 하면 랜덤하게 위치하는 빗방울을 확인할 수 있다.
int type
- 이제 랜덤한 크기의 빗방울을 만들어 보자.
- 빗방울S, 빗방울M, 빗방울L -> 타입1 빗방울, 타입2 빗방울, 타입3 빗방울
- 먼저 랜덤한 타입 값을 뽑아 보자.
- 타입 1, 2 이렇게 정수이기 때문에 int를 사용하자.
- 변수 이름은 type
void Start()
{
int type = Random.Range(1, 4);
}
- 1에서 4까지 넣은 이유는 마지막 4는 포함되지 않기 때문. 이건 실수도 마찬가지(2.4f라면 2.39f)
- 실수는 큰 차이가 없기에 일단은 정수만 조심하자.
void Start()
{
int type = Random.Range(1, 4);
if(type == 1)
{
}
else if(type == 2)
{
}
else if(type == 3)
{
}
}
- 연관된 if문이 연속되면, if문으로 다 나눠주는 게 아니라 else if라고 적어주면 됨
- 묶여져 있기 때문에, 해당되는 조건문이 있다면 해당 중괄호 안의 코드만 실행되고 뒷부분은 생략되어 다음 코드로 넘어감
- if if if 이렇게 있으면 맨 위가 true여도 다 비교를 하게 됨. 굳이 그럴 필요 없다.
- 이제 사이즈 세팅, 색깔 세팅, 점수 세팅 필요. 사이즈랑 점수는 따로 변수로 빼주도록 하자.
- size는 float으로 지정하고, 맨 마지막에 size는 localScale에다가 적용시켜 주자.
float size = 1.0f;
int score = 1;
void Start()
{
int type = Random.Range(1, 4);
if(type == 1)
{
size = 0.8f;
score = 1;
}
else if(type == 2)
{
size = 1.0f;
score = 2;
}
else if(type == 3)
{
size = 1.2f;
score = 3;
}
transform.localScale = new Vector3(size, size, 0);
}
- 만약 0.8이라는 값이 사이즈에 세팅이 되면, 나머지 조건문들은 걸리지 않게 됨(타입1에 true가 되기 때문에). 그럼 바로 transform.localScale 세팅으로 넘어가게 되는 것
- 그러면 사이즈는 0.8이 들어가 있으니까 0.8로 맞춰지게 되는 것
- 이제 색깔 세팅이 필요하다.
SpriteRenderer renderer;
void Start()
{
int type = Random.Range(1, 4);
if(type == 1)
{
size = 0.8f;
score = 1;
renderer.color = new Color(100 / 255f, 100/255f, 1f, 1f);
}
else if(type == 2)
{
size = 1.0f;
score = 2;
renderer.color = new Color(130 / 255f, 130/255f, 1f, 1f);
}
else if(type == 3)
{
size = 1.2f;
score = 3;
renderer.color = new Color(150 / 255f, 150/255f, 1f, 1f);
}
transform.localScale = new Vector3(size, size, 0);
}
- color 값은 float 값이 들어감. 그래서 최대값으로 나눠줘야 함
- 예를 들어 150이라고 하는 값을 컬러에 세팅해 줬다면, 150을 최대값인 255로 나눠서 new color 안에다가 넣어줘야 함
- 이렇게 하면 최종적으로 랜덤한 위치에서, 랜덤한 크기의, 랜덤한 색깔의 빗방울이 떨어지게 된다.