[유니티] 3D 게임 기본문법
1. Asset 다운받기
Stylized Grass Texture Import
Micro Dragon Fino - Faceted style Import
Cartoon FX Free Import
2. 스프라이트
Prefabs -> Micro_Dragon_fino 드래그
3. 애니메이션 폴더 만들기
Create -> Animations 폴더 생성 -> MicroDragon 폴더 생성 -> Animator Controller 생성 이름 바꾸기 MicroDragonAnimator
기존 Animations 폴더안에 idle 애니메이션 복제 -> 새로 만든 애니메이션 폴더에 붙여넣기
Animator에 idle 드래그
MicroDragon의 Amiator Controller에 드래그&드롭
재생해보면 스프라이트가 마구 움직이는 것을 볼 수 있음
스프라이트 보면 기본적으로 설정되어 있는 오브젝트가 있음
* 참고
Mesh : 모양을 나타냄
Materials은 스프라이트에 스킨이고, 프라이트에 올려서 쓸 수 있음
4. Material 만들기
Materials 폴더에 Create -> Materials ->
5. 바닥 만들기
3D Object -> Plane 생성 -> Scale 10,1,10 설정
Auto Generate Lighting On
Stylize_Grass_diffuse -> Shader Standard(Specular setup) -> Specular 색상 0,0,0 -> Smoothness 0 -> Tiling 10,10
Micro_dragon_fino_red -> Shader Standard(Specular setup) -> Specular 색상 0,0,0 -> Smoothness 0
6. 카메라 정렬
Main Camera 클릭 -> GameObject -> Align with view
7. 캐릭터 복제
스프라이트 2개 복재 -> 위치 일렬로 -> 색상 파랑, 노랑 설정
8. 이동시키기 3가지 방법
Scripts 폴더 생성 -> C# Scripts 이름 바꾸기 TransformMove -> 스프라이트에 스크립트 붙이기
스프라이트1
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TransformMove : MonoBehaviour
{
[SerializeField]
private float speed;
// Update is called once per frame
void Update()
{
//Transform.Translate(방향벡터 * 속도 * Time.deltaTime);
transform.Translate(Vector3.forward * speed * Time.deltaTime);
// =>transform.Translate(transform.forward * speed * Time.deltaTime, Space World);
}
}
Scripts 폴더 생성 -> C# Scripts 이름 바꾸기 VelocityMove(속도를 이용한 이동)
Add Component -> Rigidbody -> 스크립트 드래그&드롭 -> Speed 2 적용
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class VelocityMove : MonoBehaviour
{
[SerializeField]
private float speed;
public Rigidbody rd;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
private void FixedUpdate()
{
rd.velocity = transform.forward * speed;
}
}
Scripts 폴더 생성 -> C# Scripts 이름 바꾸기 RigidbodyMove(속도를 이용한 이동)
Add Component -> Rigidbody -> 스크립트 드래그&드롭 -> Speed 2 적용
Rigidbody 안에 Translate 처럼 이동
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RigidbodyMove : MonoBehaviour
{
public float speed;
public Rigidbody rd;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void FixedUpdate()
{
// Rigidbody.MovePosition(현재위치 + (방향벡터 * 속도) * Time.deltaTime)
rd.MovePosition(transform.position + (transform.forward * speed) * Time.deltaTime);
}
}
9. 회전시키기 3가지 방법
스프라이트1
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TransformMove : MonoBehaviour
{
//[SerializeField]
//private float speed;
public float moveSpeed; //이동속도
public float rotSpeed; //회전속도
// Update is called once per frame
void Update()
{
//Move();
Rotate();
}
void Move()
{
//Transform.Translate(방향벡터 * 속도 * Time.deltaTime);
transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);
// =>transform.Translate(transform.forward * speed * Time.deltaTime, Space World);
}
void Rotate()
{
//transform.Rotate(회전축벡터 * 회전속도 * Time.deltaTime);
transform.Rotate(Vector3.up * rotSpeed * Time.deltaTime);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class VelocityMove : MonoBehaviour
{
//[SerializeField]
//private float moveSpeed;
public float moveSpeed;
public float rotSpeed;
public Rigidbody rd;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
private void FixedUpdate()
{
//Move();
Rotate();
}
void Move()
{
//Rigidbody.velocity = 방향벡터 * 속도
rd.velocity = transform.forward * moveSpeed;
}
void Rotate()
{
//y축 회전 예시
//Rigidbody.angularVelocity = 회전벡터(라디안)
rd.angularVelocity = new Vector3(0f, rotSpeed * Mathf.Deg2Rad, 0f);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RigidbodyMove : MonoBehaviour
{
public float moveSpeed;
public float rotSpeed;
public Rigidbody rd;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void FixedUpdate()
{
//Move();
Rotate();
}
void Move()
{
// Rigidbody.MovePosition(현재위치 + (방향벹거 * 속도) * Time.deltaTime)
rd.MovePosition(transform.position + (transform.forward * moveSpeed) * Time.deltaTime);
}
void Rotate()
{
// 회전 쿼터니언
// 오일러 벡터 생성
Vector3 eulerRot = new Vector3(0f, rotSpeed, 0f);
// 오일러 -> 쿼터니언 변환 : Quaternion.Euler(오일러벡터)
Quaternion qt = Quaternion.Euler(eulerRot * Time.deltaTime);
// Rigidbody.MoveRotation(현재회전쿼터니언 * 회전쿼터니언);
rd.MoveRotation(transform.rotation * qt);
}
}
10. 스프라이트에 키 입력 값을 받아 이동&회전
*참고 : Edit -> Project Settings -> Input Manager -> 키 값 세팅
스프라이트 1
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TransformMove : MonoBehaviour
{
//[SerializeField]
//private float speed;
public float moveSpeed; //이동속도
public float rotSpeed; //회전속도
public float h; //회전 방향키값
public float v; // 이동 방향키값
// Update is called once per frame
void Update()
{
h = Input.GetAxis("Horizontal");
v = Input.GetAxis("Vertical");
Move();
Rotate();
}
void Move()
{
//Transform.Translate(방향벡터 * 속도 * Time.deltaTime);
transform.Translate(Vector3.forward * v * moveSpeed * Time.deltaTime);
// =>transform.Translate(transform.forward * speed * Time.deltaTime, Space World);
}
void Rotate()
{
//transform.Rotate(회전축벡터 * 회전속도 * Time.deltaTime);
transform.Rotate(Vector3.up * h * rotSpeed * Time.deltaTime);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class VelocityMove : MonoBehaviour
{
//[SerializeField]
//private float moveSpeed;
public float moveSpeed;
public float rotSpeed;
public float h;
public float v;
public Rigidbody rd;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
private void Update()
{
h = Input.GetAxis("Horizontal");
v = Input.GetAxis("Vertical");
}
private void FixedUpdate()
{
Move();
Rotate();
}
void Move()
{
//Rigidbody.velocity = 방향벡터 * 속도
rd.velocity = transform.forward * v * moveSpeed;
}
void Rotate()
{
//y축 회전 예시
//Rigidbody.angularVelocity = 회전벡터(라디안)
rd.angularVelocity = new Vector3(0f, rotSpeed * h * Mathf.Deg2Rad, 0f);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RigidbodyMove : MonoBehaviour
{
public float moveSpeed;
public float rotSpeed;
public float h;
public float v;
public Rigidbody rd;
// Start is called before the first frame update
void Start()
{
}
private void Update()
{
h = Input.GetAxis("Horizontal");
v = Input.GetAxis("Vertical");
}
// Update is called once per frame
void FixedUpdate()
{
Move();
Rotate();
}
void Move()
{
// Rigidbody.MovePosition(현재위치 + (방향벹거 * 속도) * Time.deltaTime)
rd.MovePosition(transform.position + (transform.forward * v * moveSpeed) * Time.deltaTime);
}
void Rotate()
{
// 회전 쿼터니언
// 오일러 벡터 생성
Vector3 eulerRot = new Vector3(0f, rotSpeed * h, 0f);
// 오일러 -> 쿼터니언 변환 : Quaternion.Euler(오일러벡터)
Quaternion qt = Quaternion.Euler(eulerRot * Time.deltaTime);
// Rigidbody.MoveRotation(현재회전쿼터니언 * 회전쿼터니언);
rd.MoveRotation(transform.rotation * qt);
}
}
11. 명세(코드) 중복 발생 해결
이동속도, 회전속도, 키입력변수, 키입력 처리 중복 해결 => 상속!!!
부모 코드
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CommonMove : MonoBehaviour
{
public float moveSpeed;
public float rotSpeed;
public float h;
public float v;
public virtual void InputAxis()
{
h = Input.GetAxis("Horizontal");
v = Input.GetAxis("Vertical");
}
public virtual void Movement()
{
Move();
Rotate();
}
public virtual void Move()
{
}
public virtual void Rotate()
{
}
}
스프라이트 1
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TransformMove : CommonMove
{
// Update is called once per frame
private void Update()
{
InputAxis();
Movement();
}
public override void Move()
{
//Transform.Translate(방향벡터 * 속도 * Time.deltaTime);
transform.Translate(Vector3.forward * v * moveSpeed * Time.deltaTime);
// =>transform.Translate(transform.forward * speed * Time.deltaTime, Space World);
}
public override void Rotate()
{
//transform.Rotate(회전축벡터 * 회전속도 * Time.deltaTime);
transform.Rotate(Vector3.up * h * rotSpeed * Time.deltaTime);
}
}
스프라이트 2
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TransformMove : CommonMove
{
// Update is called once per frame
private void Update()
{
InputAxis();
Movement();
}
public override void Move()
{
//Transform.Translate(방향벡터 * 속도 * Time.deltaTime);
transform.Translate(Vector3.forward * v * moveSpeed * Time.deltaTime);
// =>transform.Translate(transform.forward * speed * Time.deltaTime, Space World);
}
public override void Rotate()
{
//transform.Rotate(회전축벡터 * 회전속도 * Time.deltaTime);
transform.Rotate(Vector3.up * h * rotSpeed * Time.deltaTime);
}
}
스프라이트3
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RigidbodyMove : CommonMove
{
public Rigidbody rd;
private void Update()
{
InputAxis();
}
// Update is called once per frame
private void FixedUpdate()
{
Movement();
}
public override void Move()
{
// Rigidbody.MovePosition(현재위치 + (방향벹거 * 속도) * Time.deltaTime)
rd.MovePosition(transform.position + (transform.forward * v * moveSpeed) * Time.deltaTime);
}
public override void Rotate()
{
// 회전 쿼터니언
// 오일러 벡터 생성
Vector3 eulerRot = new Vector3(0f, rotSpeed * h, 0f);
// 오일러 -> 쿼터니언 변환 : Quaternion.Euler(오일러벡터)
Quaternion qt = Quaternion.Euler(eulerRot * Time.deltaTime);
// Rigidbody.MoveRotation(현재회전쿼터니언 * 회전쿼터니언);
rd.MoveRotation(transform.rotation * qt);
}
}
12. 추상 메소드
세 가지 다른 move 메소드를 override 하면 부모 메소드가 필요가 없기 때문에 추상메소드를 사용
가상 메서드를 사용 하여 후기 바인딩을 구현하는 반면
추상 메서드를 사용하면 형식의 하위 클래스가 메서드를 명시 적으로 재정의하도록 강제 할 수 있습니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class CommonMove : MonoBehaviour
{
public float moveSpeed;
public float rotSpeed;
public float h;
public float v;
public virtual void InputAxis()
{
h = Input.GetAxis("Horizontal");
v = Input.GetAxis("Vertical");
}
public virtual void Movement()
{
Move();
Rotate();
}
// 추상메소드
//=> 상속 구조에서 메소드의 선언만 필요한 메소드를 선언할때는
// abstract 키워드를 사용해 추상 메소드로 선언함 (메소드의 body가 없음)
// abstract 메소를 가진 클래스는 반드시 클래스도 abstract 클래스여야 함
public abstract void Move();
public abstract void Rotate();
}
Virtual 쓰는 경우
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class CommonMove : MonoBehaviour
{
public float moveSpeed;
public float rotSpeed;
//protected 접근지정자 : 현재 클래스를 상속받은 자식 클래스에서만 접근 허용함
//인스펙터에 드러내지 않기
protected float h;
protected float v;
public virtual void InputAxis()
{
h = Input.GetAxis("Horizontal");
v = Input.GetAxis("Vertical");
}
public virtual void Movement()
{
Move();
Rotate();
}
// 추상메소드
//=> 상속 구조에서 메소드의 선언만 필요한 메소드를 선언할때는
// abstract 키워드를 사용해 추상 메소드로 선언함 (메소드의 body가 없음)
// abstract 메소를 가진 클래스는 반드시 클래스도 abstract 클래스여야 함
public virtual void Move()
{
Debug.Log("현재 위치 : " + transform.position);
}
public abstract void Rotate();
}
스프라이트1
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TransformMove : CommonMove
{
// Update is called once per frame
private void Update()
{
InputAxis();
Movement();
}
public override void Move()
{
//Transform.Translate(방향벡터 * 속도 * Time.deltaTime);
transform.Translate(Vector3.forward * v * moveSpeed * Time.deltaTime);
// =>transform.Translate(transform.forward * speed * Time.deltaTime, Space World);
}
public override void Rotate()
{
//transform.Rotate(회전축벡터 * 회전속도 * Time.deltaTime);
transform.Rotate(Vector3.up * h * rotSpeed * Time.deltaTime);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class VelocityMove : CommonMove
{
public Rigidbody rd;
// Update is called once per frame
private void Update()
{
InputAxis();
}
private void FixedUpdate()
{
Movement();
}
public override void Move()
{
// 부모의 Move 메소드 호출
base.Move();
//Rigidbody.velocity = 방향벡터 * 속도
rd.velocity = transform.forward * v * moveSpeed;
}
public override void Rotate()
{
//y축 회전 예시
//Rigidbody.angularVelocity = 회전벡터(라디안)
rd.angularVelocity = new Vector3(0f, rotSpeed * h * Mathf.Deg2Rad, 0f);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RigidbodyMove : CommonMove
{
public Rigidbody rd;
private void Update()
{
InputAxis();
}
// Update is called once per frame
private void FixedUpdate()
{
Movement();
}
public override void Move()
{
// Rigidbody.MovePosition(현재위치 + (방향벹거 * 속도) * Time.deltaTime)
rd.MovePosition(transform.position + (transform.forward * v * moveSpeed) * Time.deltaTime);
}
public override void Rotate()
{
// 회전 쿼터니언
// 오일러 벡터 생성
Vector3 eulerRot = new Vector3(0f, rotSpeed * h, 0f);
// 오일러 -> 쿼터니언 변환 : Quaternion.Euler(오일러벡터)
Quaternion qt = Quaternion.Euler(eulerRot * Time.deltaTime);
// Rigidbody.MoveRotation(현재회전쿼터니언 * 회전쿼터니언);
rd.MoveRotation(transform.rotation * qt);
}
}
13. 프리팹으로 2번 3번 만들고
14. 스프라이트 따라다니는 카메라 만들기
Main Camera를 스프라이트에 드래그&드롭
15. 충돌
Asset 받기
RPG Food & Drinks pack
Prefab Ham 드래그
Assets Models에서 Scale 7로 올리기
16. 빈오프젝트 추가
이름바꾸기 Item -> Add Component Sphere Collider 추가
17. 스크립트 만들기
Create -> C# Script -> 이름바꾸기 ItemRotate
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ItemRotate : MonoBehaviour
{
public float rotSpeed;
// Update is called once per frame
void Update()
{
transform.Rotate(Vector3.up * rotSpeed * Time.deltaTime);
}
}
18.
Create -> C# Script -> 이름바꾸기 HamItemState
Item에 스크립트 붙이고 Ham Item Size에 드래그&드롭
(인스펙터에 자물쇠 잠그고 선택 후 한번에 드래그)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HamItemState : MonoBehaviour
{
public int itemType;
//배열 Ham 모양이 다른 게임오브젝트들을 참조하는 배열 참조 변수 선언
public GameObject[] hamItems;
// Start is called before the first frame update
void Start()
{
itemType = Random.Range(0, hamItems.Length);
hamItems[itemType].SetActive(true);
}
// Update is called once per frame
void Update()
{
}
}
19. 아이템 생성기 만들기
Create Empty 이름 바꾸기 ItemGenerator
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//코루틴
public class ItemGenerator : MonoBehaviour
{
public GameObject itemPrefab;
// Start is called before the first frame update
void Start()
{
StartCoroutine("GenCoroutine");
}
// 코루틴 문법
//IEnumerator 코루틴메소드명(매개변수)
// 여러개 사용할 수 있음
IEnumerator GenCoroutine()
{
int count = 1;
// 랜덤한 지연시간을 추첨
float delayTime = Random.Range(1f, 10f);
Debug.Log(delayTime + " 초 지연 시작");
// 지연 객체를 생성하여 코루틴을 지연함
yield return new WaitForSeconds(delayTime);
Debug.Log((count++) + "번째 지연 코드 실행!!!");
delayTime = Random.Range(1f, 10f);
Debug.Log(delayTime + " 초 지연 시작");
yield return new WaitForSeconds(delayTime);
Debug.Log((count++) + "번째 지연 코드 실행!!!");
delayTime = Random.Range(1f, 10f);
Debug.Log(delayTime + " 초 지연 시작");
yield return new WaitForSeconds(delayTime);
Debug.Log((count++) + "번째 지연 코드 실행!!!");
}
}
20. 코루틴 2개 만들기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//코루틴
public class ItemGenerator : MonoBehaviour
{
public GameObject itemPrefab;
// Start is called before the first frame update
void Start()
{
StartCoroutine("SampleCoroutine", "코루틴1");
StartCoroutine("SampleCoroutine", "코루틴2");
}
// 코루틴 문법
//IEnumerator 코루틴메소드명(매개변수)
// 여러개 사용할 수 있음
IEnumerator SampleCoroutine(string name)
{
int count = 1;
// 랜덤한 지연시간을 추첨
float delayTime = Random.Range(1f, 10f);
Debug.Log(name + " => " + delayTime + " 초 지연 시작");
// 지연 객체를 생성하여 코루틴을 지연함
yield return new WaitForSeconds(delayTime);
Debug.Log(name + " => " + (count++) + "번째 지연 코드 실행!!!");
delayTime = Random.Range(1f, 10f);
Debug.Log(delayTime + " 초 지연 시작");
yield return new WaitForSeconds(delayTime);
Debug.Log(name + " => " + (count++) + "번째 지연 코드 실행!!!");
delayTime = Random.Range(1f, 10f);
Debug.Log(delayTime + " 초 지연 시작");
yield return new WaitForSeconds(delayTime);
Debug.Log(name + " => " + (count++) + "번째 지연 코드 실행!!!");
}
}
21. 아이템 랜덤하게 생성하기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//코루틴
public class ItemGenerator : MonoBehaviour
{
public GameObject itemPrefab;
public float itemGenMinTime;
public float itemGenMaxTime;
// Start is called before the first frame update
void Start()
{
StartCoroutine("ItemGenCoroutine");
//StartCoroutine("SampleCoroutine", "코루틴1");
//StartCoroutine("SampleCoroutine", "코루틴2");
}
//아이템 생성 코루틴
IEnumerator ItemGenCoroutine()
{
while (true)
{
float genDelayTime = Random.Range(itemGenMinTime, itemGenMaxTime);
Debug.Log(genDelayTime + " 시간 뒤에 아이템을 생성함");
yield return new WaitForSeconds(genDelayTime);
float posX = Random.Range(-20f, 20f);
float posZ = Random.Range(-20f, 20f);
Vector3 genPos = new Vector3(posX, 0f, posZ);
//아이템 생성
GameObject item = Instantiate(itemPrefab, genPos, Quaternion.identity);
Debug.Log(item.transform.position + " 위치에 아이템이 생성됨");
}
}
// 코루틴 문법
//IEnumerator 코루틴메소드명(매개변수)
// 여러개 사용할 수 있음
IEnumerator SampleCoroutine(string name)
{
int count = 1;
// 랜덤한 지연시간을 추첨
float delayTime = Random.Range(1f, 10f);
Debug.Log(name + " => " + delayTime + " 초 지연 시작");
// 지연 객체를 생성하여 코루틴을 지연함
yield return new WaitForSeconds(delayTime);
Debug.Log(name + " => " + (count++) + "번째 지연 코드 실행!!!");
delayTime = Random.Range(1f, 10f);
Debug.Log(delayTime + " 초 지연 시작");
yield return new WaitForSeconds(delayTime);
Debug.Log(name + " => " + (count++) + "번째 지연 코드 실행!!!");
delayTime = Random.Range(1f, 10f);
Debug.Log(delayTime + " 초 지연 시작");
yield return new WaitForSeconds(delayTime);
Debug.Log(name + " => " + (count++) + "번째 지연 코드 실행!!!");
}
}
22. 아이템 충돌
Sphere Collider 추가 -> Is Trigger 체크 -> Rigidbody 추가
23. 스크립트 추가 -> ItemPickup ->스프라이트에 붙이기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ItemPickup : MonoBehaviour
{
//3D 충돌 이벤트 메소드(Collider 컴포넌트의 isTrigger = true 일때)
private void OnTriggerEnter(Collider itemCollider)
{
if (itemCollider.tag == "Item")
{
Debug.Log("아이템을 획득함");
Destroy(itemCollider.gameObject);
}
}
}
24. Open Item Tag 추가
Is Trigger 체크