게임 오브젝트, 트랜스폼, 컴포넌트, 카메라
게임 오브젝트
GameObjectExample.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameObjectExample : MonoBehaviour
{
// Start is called before the first frame update
[SerializeField]
private GameObject goPrefab;
void Start()
{
// 1. 오브젝트 생성
// PrimitiveType.미리 정의된 오브젝트들
// 0,0,0에 생성됨
// GameObject go = GameObject.CreatePrimitive(PrimitiveType.Cube);
// PrimitiveType.Capsule;
// PrimitiveType.Cylinder;
// PrimitiveType.Plane;
// PrimitiveType.Quad;
// PrimitiveType.Sphere;
// 2. 오브젝트 파괴
// Destroy(go);
// Destroy(go, 3f);
// 커스터마이징 사용할 때 아니면 사용 X
// DestroyImmediate(go);
// 3. 오브젝트의 활성화 여부 확인
// 본인이 체크되어있냐 (부모 활성화에 상관 없이 체크면 true 체크해제면 false)
// 자식이 켜있고 부모가 꺼져있을 때
// ex) preload 미리 불러오기 외부로부터 동적으로 생성 - SetActive 켜져있음
// 이후 부모를 끄는 상태가 있음
// Debug.Log(go.activeSelf);
// 하이어라키상에서 활성화 되어있는지 여부를 알려줌 (부모 비활성화시 비활성화시 체크 false 체크해제 false)
// Debug.Log(go.activeInHierarchy);
//4. Tag 확인
// GameObject의 꼬리표(표식)
// 한 오브젝트 당 하나만 가능
// Array GameObject[] 가 있을 때
// foreach를 돌려서 go.tag == "Player"; go.CompareTag("tag"); 등 물리 엔진을 다룰 때 사용
// 프로젝트를 이관할 때 Export Pakage와 폴더 전체를 전송하는 경우 중 각 폴더만 Export하면
// Project Setting(태그, 등등) 이 포함이 안 된다.
// export 패키지는 중간중간 필요한 리소스를 받을 때 사용한다.
// Jenkins는 파일 / 셋팅, 깃허브 연동 => 프로젝트를 서버에서 빌드
// 소스코드를 GIT, SVN, Mercurial, Collaborate 버전관리툴을 사용해서 올린다.
// 젠킨스로는 바뀐 부분만 올려짐 세팅 같은 건 바꿀 수 없게 세팅을 해놓음
// boolean 반환
// Debug.Log(gameObject.CompareTag("GOExample"));
// Find는 하이러키 창의 위에서부터 찾음
//5. Tag로 검색 - 해당 태그를 가지고있는 오브젝트 저장
// GameObject goWithTag1 = GameObject.FindWithTag("Player");
// GameObject goWithTag2 = GameObject.FindWithTag("Finish");
// Debug.Log(goWithTag1 != null);
// Debug.Log(goWithTag2 != null);
// Transform.Find("");
// 자식들만 가져올 수 있는데 Active false도 가져올 수 있음
// FindWithTag("");
// GameObject.Find("");
// SetActive가 꺼진 애들은 가져오지 못함
// GetComponent(Active or not Active).gameObject();
// 선택해서 가져올 수 있다.
//6. 이름으로 검색
GameObject goWithFind1 = GameObject.Find("FindWithName");
// GameObject goWithFind2 = GameObject.Find("FindWithName2");
// Debug.Log(goWithFind1 != null);
// Debug.Log(goWithFind2 != null);
// 사용자가 정의한 게임오브젝트 생성
GameObject goTest = Instantiate(goPrefab);
goTest.name = "InstanceSphere";
goTest.transform.parent = goWithFind1.transform;
goTest.transform.localPosition = Vector3.zero;
goTest.transform.localRotation = Quaternion.identity;
goTest.transform.localScale = Vector3.one;
}
void Update()
{
}
}
트랜스폼
TransformExample.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TransformExample : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
// Transform 반환
Transform tr1 = transform.Find("FindInChild");
Transform tr2 = transform.Find("FindInChild2");
// GameObject go1 = new GameObject();
// Transform과 GameObject는 상호참조관계
// Transform trG = go1.transform;
// GameObject goT = tr1.gameObject;
Debug.Log(transform.GetChild(0).name);
// tr1의 오브젝트를 맨 밑으로 보내겠다.
tr1.SetAsLastSibling();
Debug.Log(transform.GetChild(0).name);
// tr1의 오브젝트를 맨 위로 보내겠다.
tr1.SetAsFirstSibling();
Debug.Log(transform.GetChild(0).name);
// tr1을 index(1)번으로 보내겠다.
// 언제쓰냐 GetComponents 다 가져올 때 특정 순서를 가져옴 Getcomponent는 첫 번째 것 가져옴
// Sprite (0,0,0) (0,0,0) 두 개가 있을 때 하이어러키 상에서 맨 아래에 있는게 제일 앞에 보임 (z축은 의미가 없음)
// => 인덱스 0번부터 그리기(렌더링하기) 때문에
tr1.SetSiblingIndex(1);
// tr1의 부모 이름 가져오기
Debug.Log(tr1.parent.name);
// 최상위 것 가져옴
// tr1.root
}
void Update()
{
// 부모 자식 관계에서 자식 스크립트에서 transform.position은 월드 좌표 기준 position이다.
// 컴파일 에러
// transform.position은 Vector3이다. Vector는 struct(값에 의한 복사)이므로 못 씀
// transform.position.x += 0.01f;
// 부모 자식 관계에서 자식 스크립트에서 transform.rotation은 월드 좌표 기준 rotation이다.
// Vector3 vecP = transform.localPosition;
// vecP.x += 0.01f;
// transform.localPosition = vecP;
// Vector3 vecR = transform.localRotation.eulerAngles; -> Vector3
// vecR.x += 0.01f;
// transform.localRotation = Quaternion.Euler(vecR);
// lossyScale은 root 스케일(월드) localScale은 로컬 스케일(로컬)
// Vector3 vecS = transform.lossyScale;
// vecS.x += 0.01f;
// 에러
// transform.lossyScale = vecS;
// Euler 3축 Quternion 4축
// Update는 초당 30번 실행된다. 30fps
// 1Unit = 1M
}
}
컴포넌트
ComponentExample.cs
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class ComponentExample : MonoBehaviour // 컴포넌트 상속
{
void Start()
{
// MonoBehaviour를 상속받는 클래스들은 new로 생성하면 안 된다.
// GameObject를 생성하고(이미 있을 시 그냥) AddComponent로 컴포넌트를 붙여야한다.
// 변수로 컴포넌트를 선언하면 asrc.~ 등 바로 접근이 가능하다.
AudioSource asrc = transform.AddComponent<AudioSource>(); // Camera, GameObject (MonoBehaviour)를 상속하는 것이라면 가능
// 이미 있으면 Getcomponent로 이 클래스에 붙어있는 컴포넌트를 가져옴
AudioSource asrc2 = transform.GetComponent<AudioSource>();
transform.GetComponent<Component>();
// 아래 방식으로 Active가 꺼진 애들을 가져올 수 있다.
GameObject go = new GameObject();
GameObject.Find("");
go.transform.Find("");
go.GetComponentInChildren<MeshRenderer>(true);
MeshRenderer[] meshArray = go.transform.GetComponentsInChildren<MeshRenderer>(true);
foreach (var mesh in meshArray)
{
mesh.enabled = false;
}
Component[] coms = transform.GetComponentsInChildren<Component>(true);
AudioSource[] asrcArray = transform.GetComponentsInChildren<AudioSource>(true);
}
void Update()
{
}
}
카메라
// Camera
// Clear Flags는 배경을 뭘로 할거냐 SkyBox, Solid Color,
// Depth Only 카메라의 뒷배경은 투명 - UI, Sprite, Video, WebPage ex) 로그인창 => 3rd party Library, 내부카메라 WebPage
// 이 때 기존 카메라에 UI 띄우는 경우도 있는데 최적화 Draw Call 문제와 UI.Object가 카메라에 같이 비치는 문제가 있다.
// => 웬만하면 UI 카메라와 게임 카메라를 분리를 한다.
// Don't Clear는 (Blit Clear가 클리어를 하는데 전체 프레임을 한 번 지워준다) => 이 클리어를 안 하겠다는 거임
// Culling Mask - Layer 개념 비슷
// 1번 카메라는 오브젝트만 비추고 2번 카메라는 캐릭터만 비추고 싶을 때 Post Processing(후처리 과정) ex) 사진 필터
// 캐릭터에만 효과를 주고싶을 때 Layer와 Culling Mask를 사용
// Projection
// - Perspective : 원근 적용
// FOV(FiledOfView) : Vertical/Horizontal 60도만큼 화면을 찍어서 가져옴
// - Orthographic : 원근 적용 X (카메라 각도가 직각)
// Size : Unity Unit 1(1m) 중점 기준으로 a만큼 사이즈가 잡힘(높이) 이에 대해 가로는 해상도에 따라 달라짐
// 16:9일 때 세로가 5라면 세로는 5/9*16이 됨
// - Clipping Planes
// Near : 제일 가까운 부분 (~보다 가까워지면 안 보임)
// Far : 가시거리 (~가 넘어가면 안 보임)
// - ViewportRect
// X, Y 는 좌측 아래를 기준으로 0~1의 위치
// W, H 는 화면의 0~1만큼의 크기
// TargetTexture
// Render Textrue를 만들고 타겟 텍스처에 넣고 Material 알베도 부분에 입혀서 오브젝트에 입히면
// CCTV같이 입혀진다.
// Occlusion Culling
// Occlusion 계산은 CPU를 사용 Occlusion 가리고 안 가리고는 GPU를 사용
// 겹쳐보이는 부분이 있을 때 안 그려줌
// 인스펙터 위의 static 옆에 Occluder (가리는 애), Occludee (가려지는 애) 잘 선택해서 체크
// GPU와 CPU에 사용량에 따라 사용
// Occulsion -> Yellow box 형성 기준
// 1. Occlusion Area
// 2. Dynamic Area
// 파란색 박스가 unit을 나누는 기준
씬, 벡터 , Input, 물리
씬
SceneExample.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class SceneExample : MonoBehaviour
{
// 씬을 관리하는 경우
// 1. 실제로 게임상/기능상 화면전환/기능전환
// 2. 리소스 관리 차원 -> 던전분리 ex) RPG Dungeon이동
// 3. OpenWorld RPG - Object Pooling, LOD 관리
// 1번 씬에서 2번씬으로 이동시 1번 씬에 있던 오브젝트는 모두 삭제됨 (Additive를 안 했다고 가정)
void Start()
{
DontDestroyOnLoad(this.gameObject); // 씬을 로드를 했을 때 파라미터 부분을 Destroy 안 하겠다.
// File - Build Setting - Scenes In Build에 씬을 드래그앤드롭으로 추가해야함
// 인덱스를 통해서 불러오는 방식
// SceneManager.LoadScene(1);
// 씬의 이름을 통해서 불러오는 방식
// 비슷할 때 풀 경로로 써주면 됨
// SceneManager.LoadScene("2");
// SceneManager.LoadScene("2", LoadSceneMode.Single);
// Scene 1이 살아있는 상태로 불림
// 뒤에 생긴 카메라가 앞에 씬 카메라 덮어씌워짐(UI 설명할 때 처럼 한 번 더 그림)
// 웬만하면 Addtive 씬에는 카메라를 안 넣음 (일부러 겹치고 카메라를 사용해야될 때만 사용)
SceneManager.LoadScene("Scenes/2", LoadSceneMode.Additive);
}
}
벡터
Day5.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
public class Day5 : MonoBehaviour
{
// Vector -> struct 방향 / 크기
// △x △y 방향
Vector3 vec = new Vector3(0,0,0);
Vector3 vec2 = new Vector3(10,10,10);
float currentVel;
// 배열 선언 : 한정자 자료형[] 변수이름 = new 자료형[사이즈];
private Vector3[] vecArray = new Vector3[3];
// 리스트 : 동적 배열 List<자료형/클래스/구조체> 변수명 = new List<앞과 같음>();
private List<Vector3> vecList = new List<Vector3>();
// 딕셔너리 : Dictionary<키의 자료형/인스턴스형, 값의 자료형/인스턴스형> 변수명 = new Dictionary<앞과 동일>();
private Dictionary<string, Vector3> vecDictionary = new Dictionary<string, Vector3>();
private float prev;
private float threshhold = 2;
// Start is called before the first frame update
void Start()
{
// 벡터의 크기
Debug.Log(vec2.magnitude);
// 벡터 정규화 (0~1)사이
// normailized되지만 vector2의 값이 바뀌지는 않음
Debug.Log(vec2.normalized);
// 값이 바뀌는 건 함수
// vec2.Normalize()
// vec2.Scale()
//Vector3.one; (1, 1, 1)
//Vector3.zero; (0, 0, 0)
//Vector3.left; (-1, 0, 0)
//Vector3.right; (1, 0, 0)
//Vector3.up; (0, 1, 0)
//Vector3.down; (0, -1, 0)
//Vector3.forward; (0, 0, 1)
//Vector3.back; (0, 0, -1)
// 두 벡터 사이의 거리를 잼
// ex) 충돌 처리
Debug.Log(Vector3.Distance(vec, vec2));
Transform obj = transform;
Transform obj2 = transform;
Vector3.Distance(obj.position, obj2.position);
// 간단한 충돌처리 각 오브젝트에 가상의 원을 씌움
// 각 원의 센터를 그림
// 반지름 r1, r2일 때
// 원이 겹치는 부분 계산 중심의 거리 < r1 + r2 이면 충돌
vecList.Add(vec2);
vecList.Add(vec);
vecList.Add(vec);
vecList.Add(vec);
// List의 한도값 - 갯수 한정 (따로 안 줬을 때 기본 값은 메모리가 계속 두 배로 늘어남)
// vecList.Capacity = 10;
// ~을 포함하는지 확인하고 bool 값을 반환
// vecList.Contains(vec);
// vecList의 1번 인덱스에 vec값을 넣음 (인덱스 1번부터 우측으로 밀려남)
// vecList.Insert(1, vec);
// vec을 앞에서부터 찾아서 없앰
// vecList.Remove(vec);
// (LINQ)
// 나중에 배움
// 자바의 Map 개념이랑 비슷
// 사전 : 단어, 뜻 Dictionary : 키(key), 값(value)
// vecDictionary.Add("Test", vec);
// Vector3 o = vecDictionary["Test"];
// vecDictionary.Add("vec2", vec2);
// 참조는 없으면 에러 뜰 수 있음
// vecDictionary["Test"].Normalize();
// 대입은 권장하지 않음 => 원래 값이 있거나 키가 null 값일 수 있기 때문
// vecDictionary["vec"] = vec;
// 갖고있는 키를 다 반환
// vecDictionary.Keys
Debug.Log(vecList[0]);
Debug.Log(vecDictionary["vec"]);
}
// Update is called once per frame
void Update()
{
var currentVel = Vector3.one;
// Lerp(Linear Interpolation) 선형 보간
// time 값에 따라 vec과 vec2 사이의 거리를 반환(0~1)
Debug.Log(Vector3.Lerp(vec, vec2, 0.5f));
// clamp는 값을 잘라놓은 것(그냥lerp) unClamp 잘라놓지 않은 것
// 값에 비례해서 자름(-1이면 거리의 -1배만큼 vec에서 거리만큼 전으로 계산해서 반환)
// Vector3.LerpUnclamped(vec, vec2, -1f);
// 2D게임을 만들 땐 Vector3.Lerp 대신 Mathf.Lerp를 써도 쓸만함
// (0~1) 비례해서 0.5만큼의 거리만큼 계산
// Mathf.Lerp(vec.x, vec2.x, 0.5f);
// 50f라는 실제 값을 넣었을 때 비율을 반환
// vec.x가 0이고 vec2.x가 100일 때 50을 넣었다치면 그 비율인 0.5가 나옴
// Mathf.InverseLerp(vec.x, vec2.x, 50f);
// ex) vec과 vec2 사이에 2초만에 도착하고 싶을 때
// transform.position = Vector3.Lerp(vec, vec2, Time.time * 0.5f);
// (Mathf.Sin(Time.time) + 1) / 2) -> 0부터 1 => vec, vec2 사이를 왔다갔다함
// transform.position = Vector3.Lerp(vec, vec2, (Mathf.Sin(Time.time) + 1) / 2);
// 위의 Sin 함수를 쓴 것과 같은 효과
// Sigmoid를 사용 활성함수 0과 1으로 가까워질수록 변화값이 작고
// 0.5로 가까워질수록 변화값이 크다.
// transform.position = Vector3.SmoothDamp(vec, vec2, ref currentVel, Time.deltaTime);
// Debug.Log(Vector3.SmoothDamp(vec,vec2, ref currentVel, Time.deltaTime));
}
}
Input
// Input
private float prev;
private float threshhold = 2;
//Update문 안에서
// 현재 마우스 위치
// vec2 = Input.mousePosition;
// 0:좌클릭, 1:우클릭, 2:휠버튼
// Input.GetMouseButton(0)
// 엔터는 KeyCode.Return을 씀
// KeyPadEnter는 우측 숫자패드의 엔터이다.
// GetKey, GetMouseButton 등 받으면
// 키보드 이벤트 3가지
// 눌렀을 때, 누르고 있을 때, 누른걸 땠을 때
// Input.GetKeyDown(KeyCode.L); // 눌렀을 때
// Input.GetKey(KeyCode.L); // 누르고 있을 때
// Input.GetKeyUp(KeyCode.L); // 누른걸 땠을 때
// Update문에서 GetKey는 프레임당 한번 씩 받음(캐릭터 이동 등)
// 한 번만 받아야되면 GetKeyDown이나 GetKeyUp 사용
// 위와 같음
// Input.GetMouseButtonDown(0);
// Input.GetMouseButton(0);
// Input.GetMouseButtonUp(0);
// esc버튼인데 모바일로 가면 backButton이다.
// Input.GetKey(KeyCode.Escape);
//Input.GetKey(KeyCode.A);
//Debug.Log(Input.GetKey(KeyCode.A));
//Input.GetMouseButton(0);
//0 for left button, 1 for right button, 2 for the middle button
//Input.GetKeyDown(KeyCode.A);
//Input.GetKeyUp(KeyCode.A);
// 기기에 있는 가속도계 => 핸드폰 기울이는 로직을 사용할 때 씀
// Input.acceleration
// 기기 방향
// Input.compass
// 기기 회전 값을 잡을 때
// Input.gyro
// 기기 터치 관련
// Input.touches
// 모바일 멀티터치 index 0 1 2 3 4 ~~
// 터치에 따라 리스트가 구현이 되어있음
Touch t = Input.GetTouch(0);
Touch t2 = Input.GetTouch(0);
// 멀티 터치 갯수를 반환
// Input.touchCount;
// 위와 동치 touches는 멀티터치를 배열로 갖고있음
// Input.touches.Length;
// 터치 이동한 만큼 델타 벡터를 계산
// t.deltaPosition;
// 줌인 줌아웃
if (Input.touchCount == 2)
{
if (Mathf.Abs(Vector2.Distance(t.position, t2.position) - prev) > threshhold) return;
if (prev > Vector2.Distance(t.position, t2.position))
{
//pinch in
}
if (prev < Vector2.Distance(t.position, t2.position))
{
//pinch in
}
prev = Vector2.Distance(t.position, t2.position);
}
물리
FixedUpdateEx.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FixedUpdateEx : MonoBehaviour
{
// 유니티 엔진, 렌더링 엔진, 물리 엔진
// 옛날엔 3D 밖에 지원을 안 했음 => 원래는 3D 물리 엔진만 지원했음(PhysX)
// static
// 3D rigidbody
// Kinematic : 물리법칙 받지 X but 충돌처리는 됨 ex) 맵
// Dynamic : 물리법칙 O ex) 플레이어
// 2d UGUI 나중에 2D물리엔진 지원 (Box2d)
// 강체, static, dynamic, kinematic(3D의 Static과 동치)
// 2D rigidbody
// Kinematic - AddforceV에는 움직이지는 않는데 간접적인 힘에는 움직임
// Static : 3D Kinematic과 동치 - 아예 움직이지 않음
// Dynamic - 다 움직임
// Rigidbody는 물리의 영향을 받아야 될 때 붙이면 됨
// transform.position.x 조작은 Render 엔진 부분이라 static이어도 이동하긴 함
// velocity.x 조작은 이동 안 함
// Animation의 움직임이 기본적으로 transform이나 물리엔진 이동부분을 override함
// 두 객체의 충돌구현
// 적어도 둘 중 하나는 rigidbody를 가져야함
// collider는 둘 다 가져야함
// rigidbody info에서 속도나 이런 거 확인 가능
// mass : 질량
// drag : 위치에 대한 저항
// Angular Drag : 각도에 대한 저항(회전 저항)
// Use Gravity : 중력 체크
// Is Kinematic : 물리 체크
// Interpolate : 충돌 검출은 프레임별로 일어나는데 전의 프레임을 참고함
// Extrapolate : 충돌 검출에 다음 프레임을 참고함
// 객체의 Collider에 줄 수 있고
// Project Setting에서 Physics 부분에 넣으면 모든 물체에 기본으로 적용됨
// Physics Material
// - Dynamic Friction : 움직이고 있을 때 마찰력
// ex) 공이 굴러가고 있을 때 마찰력
// - Static Friction : 멈춰 있을 때 마찰력
// ex) 다른 공에 부딪혔을 때 멈춰있던 공의 마찰력
// - Bounciness : 탄성 계수
// - Friction Combine : 두 개체 사이의 마찰력
// -- averge 평균을 내겠다 minimum 작은 쪽의 마찰력을 내겠다
// -- Maximum 더 큰 값을 내겠다 multiply 곱해서 내겠다
// Bounce Combine : 두 개체 사이의 탄성력
// -- averge 평균을 내겠다 minimum 작은 쪽의 마찰력을 내겠다
// -- Maximum 더 큰 값을 내겠다 multiply 곱해서 내겠다
// Physics
// Default Material
// 기본 Physics Material
// Bounce Threshold
// 다른 물체에 부딪혔을 때 부딪치는 물체의 속력이 이 값보다 낮을 시 탄성 작용 없음
// Sleeping Threshold
// Sleep 모드일 때 일정 값보다 큰 힘이 작용했을 때 Awake 상태가 되는 기준 값
// Default Contact Offset => 얼마를 붙어있는 걸로 기준을 잡을래?
// contact는 모서리의 버텍스를 가져오기 때문에 각 모서리의 길이보다 짧으면 붙었다고 가정
// Queries Hit Backfaces
// 면에 대해서 hit을 감지하는데 뒷면에서 올 때도 감지할거냐?
// Enable Adaptive Force
// 가중치 ex) 당구 벽에 공 겹쳤을 때 하중
// Cloth Gravity
// 천(옷) 중력
// Collision Detection
// - Discrete : 고정
// - Continuous : 더 잦은 충돌 검출 (부하up)
// ex) 유저가 벽뚫기 하는 경우에 씀
// - 속도가 빠를 때(deltapos가 클 때)
// - Collider 빈 부분이 있거나
// - Collider 검출이 안 되거나
// Contraints - 6개 다 키면 Kinematic과 동치
// - Freeze Position 앞으로만 움직여야 되면 다른 좌표 값을 체크(고정됨)
// - Freeze Rotation 달려가다가 옆에서 쳤을 때 주사위 굴러가듯이 넘어가면 Rotation을 막아버림
// ex) 좌우 각도만 있으면 된다 Freeze Rotation의 X,Z를 체크함
// Capsule Collider vs Sphere Collider
// Capsule Collider Height를 1로하면 Sphere와 같아짐
// 실무에서는 Capsule Collider를 더 많이 씀 (범용성이 넓음)
// Collider
// Edit모드에서 크기 조절 가능
// Trigger vs Collision
// Trigger : 범위 침범 O
// ex) Rpg 특정 장소에 도달(보이지 않는 공간을 만들고 플레이어 도달) => Trigger 사용
// Rpg에선 인스턴싱 오브젝트가 많을 시(의도하지 않게 길막 생길 수 있음) 웬만하면 트리거
// Collision : 범위 침범 X
// ex) 당구 게임, 퍼즐 게임이나 어드벤처 게임에서 길을 막을 오브젝트
// Joint : 연결을 시켜주거나 상호작용이 가능하게 만들어줌
// Fixed Joint
// 처음 생성된 거리가 유지가 됨
// - Connected Body(rigidbody 참조해야함)
// - Break Force 일정 값이 넘어가면 끊어짐 Infinity면 절대 안 부셔짐
// - Break Toque 일정 회전 값이 끊어짐 Infinity면 절대 안 부셔짐
// ex) cut the rope(공룡 사탕 먹는 게임) spring Joint 사용
// Character Joint
// 3D 모델 흔들리는 모습 다 구현 가능
// Hinge Joint
// 문 경첩 수직 Axis를 기준으로 회전 가능
// axis 움직이는 축
// 기준점이 되는 좌표
// Spring Joint
// 매달려서 연결 됨
// Terrain
// Mesh Collider
// 3D 객체를 다루는 파일에서 자주 나옴
// Shaded WireFrame에서 볼 수 있음
// 더 자세히 콜라이더 체크를 할 수 있음
// Mesh collider 일반 상태에서는 trigger가 불가능하다
// - Convex
// Convex Hull (구멍채우기)
// 체크해야 Trigger가 가능하다
// 컨벡스헐 알고리즘 - 가장 바깥의 점을 찾아서 이어줌
// 도형에서 안쪽으로 파여져 있는 부분이 컨벡스
// 이렇게 하기 싫으면 버텍스를 다 검출하여
// 컨벡스헐 생성이 안 되면 콜라이더를 생성하여 계산
// ex) 스왐피는 동적생성
// - 정점을 가져와서 MeshFilter에 CreateMesh를 실행하여
// Mesh배열을 가져온 후 메시 콜라이더에 넣음
// 메시를 구획으로 나누어서 동적 생성
// 물줄기가 떨어짐 - 원형의 콜라이더에 파티클을 씌운 것임
// 물 - 흘러야됨 / 뭉쳐져야됨
// 지름보다 작은 콜라이더를 넣고 - 뭉쳐짐
// Friction을 0으로 넣음 - 흘름
// Bounceness를 0.6정도로 하면 더 자연스러움
// Wheel Collider
// 레이싱 게임을 목적으로 만든 콜라이더
// 댐핑, 서스펜션 등등이 있음
// Rigidbody2D
// - Auto Mass 질량을 자동으로 계산해줌(size 기반)
// Distance Joint
// 거리를 조정할 수 있음
// Effector 2D
// Collider에 Used By Effector을 체크 해야함
// Buoyancy Effector 2D
// Particle System Collision
// - Lifetime Loss
// Collision과 만나면 빨리 사라지게 할 수 있다.
// Collider를 타고 파티클이 진행할 수 있다.
private ForceMode m;
Rigidbody m_Rigidbody;
public float m_Thrust = 20f;
private int m_nCount = 0;
void Start()
{
//Fetch the Rigidbody from the GameObject with this script attached
m_Rigidbody = GetComponent<Rigidbody>();
// 물리 끄고싶을 때는 키네마틱을 켜버림
// m_Rigidbody.isKinematic = true;
// 중력 가속도 * 시간 => 속도 / 속도 * 시간 => 거리 // 9.81 * 0.02 * 0.02
}
// Update는 Fps -> Cpu(프레임)에 종속적
// Render쪽에 주로 쓰임 (시각적)
// - 사람이 초당 볼 수 있는 한계에 맞춰져 있음
// Frame과 Delta.Time은 반비례 관계
// FixedUpdate는 Tick(Cpu 시간을 재는 단위 - 고정)에 종속적
// => 프레임 저하에 상관 X(프레임에 종속 X) - 0.02초에 한 번 불림
// 실무에선 0.1초에 한 번으로 불림
// 물리 엔진은 예측 개념이 조금 있는데
// 프레임이 끊겼을 때 순간 이동을 한다거나 버그가 생길 수 있음
void FixedUpdate()
{
// 정속
// m_Rigidbody.velocity = new Vector3(0, 5f, 0);
// 방향을 가진 힘을 줌 (월드 포지션 기준)
// 한 번 더 감싸는 큐브가 있다고 쳤을 때 부모 객체의 각도를 돌렸을 때
// 예상치 못한 방향으로 힘이 가해질 수 있음
// m_Rigidbody.AddForce();
// 해당 트랜스폼이 바라보는 방향에 대한 벡터
// transform.forward;
// 특정 지점에 힘들 줌 (토크랑 같이 먹음)
// m_Rigidbody.AddForceAtPosition();
//F(힘) = ma
//a(가속도) = F / m(질량)
//a = deltaV / deltaT
//a = (v2 - v1) / (t2 - t1) 가속도
//a - 9.8(중력) = v2 / t2
// m = 1, F = 20, t2 = 0.02
// v1 = 0 t1 = 0
// a - 9.8 = v2 / t2
// v2 = (a - 9.8) * t2
// v2 = (F / m - 9.8) * t2
// v2 = (F - 9.8) * t2
// v2 = (20 - 9.8) * 0.02
// v2 = 0.2 RigidbodyInfo에서 확인 가능
// Ex)
// v2 = (a - 9.8) * t2
// v2 = (F/m - 9.8) * t2
// v2 = (F - 9.8) * 0.02
if (Input.GetKeyDown(KeyCode.A))
{
m_nCount = 50;
m = ForceMode.Force;
// force * deltaTime / mass
// F = 20 * 0.02 / 1
// v2 = 0.204
}
if (Input.GetKeyDown(KeyCode.S))
{
m_nCount = 50;
m = ForceMode.Acceleration;
// force * deltaTime
// F = 20 * 0.02
//
}
if (Input.GetKeyDown(KeyCode.D))
{
m_nCount = 50;
m = ForceMode.Impulse;
// force / mass
// F = 20 / 1
//
}
if (Input.GetKeyDown(KeyCode.F))
{
m_nCount = 50;
m = ForceMode.VelocityChange;
// force
// F = 20
}
if (m_nCount > 0)
{
m_nCount--;
m_Rigidbody.AddForce(transform.up * m_Thrust, m);
Debug.Log(m_Rigidbody.velocity);
Debug.Log(Time.fixedDeltaTime);
}
// 원래 개념은 카메라에서 눈에 보이지않는 Ray(선) 을 쐈을 때 처음으로 걸리는 객체
// 유니티에서의 개념은 쏘는 부분(object, 좌표)에서 쏴서(필터링) 특정 객체가 맞으면 true를 반환
// 제일 처음 만난 콜라이더를 반환함
// 다 반환받고 싶으면 CastAll을 붙임 => 반환값 Array
RaycastHit hit;
// 현재 포지션에서 forward 방향으로 레이저를 100fm 만큼 쐈을 때 맞으면 true
// 거리 안 쓰면 무한 : float.PositiveInfinity
// Layer는 특정 객체를 검출하게 할 수 있다.
// out hit
// RaycastHit은 구조체로 값 전달밖에 못 하는데 out으로 전달하면 참조로 받아
// hit을 Raycast함수문 탈출 후에도 사용할 수 있다.
if (Physics.Raycast(transform.position, Vector3.forward, out hit, 100f, LayerMask.NameToLayer("UI")))
{
hit.transform.GetComponent<GameObject>().SetActive(false);
}
// 캐스트 되는 범위를 박스 형태로 날림
if (Physics.BoxCast(transform.position, Vector3.one, Vector3.forward, out hit))
{
}
RaycastHit[] hits = Physics.BoxCastAll(transform.position, Vector3.one, Vector3.forward);
// Physics.CapsuleCast()
// Physics.SphereCast();
// Physics.Linecast();
}
// Edit - Project Setting - Physics Layer Collision Matrix - 체크를 해제하면 충돌을 막을 수 있다.
// OnTriggerEnter/Stay/Exit
// Collider 영역에 진입했을 때
private void OnTriggerEnter(Collider other)
{
}
// Collider 영역에 있을 때
private void OnTriggerStay(Collider other)
{
}
// Collider 영역에서 나왔을 때
private void OnTriggerExit(Collider other)
{
}
// OnCollisionEnter/Stay/Exit
// 부딪쳤을 때
private void OnCollisionEnter(Collision collision)
{
// 사각형 사각형
// 충돌 포인트들을 가져올 수 있음
// collision.contacts
// 부딪친 충돌체의 컴포넌트들을 가져올 수 있음
// collision.transform.GetComponent<>();
}
// 붙어있는 상태
private void OnCollisionStay(Collision collision)
{
}
// 부딪쳤다 떨어졌을 때
private void OnCollisionExit(Collision collision)
{
}
}
실습 과제
물리를 이용한 플랫폼형 게임을 만들어봤다.
한 주 후기
이번 주에는 게임 오브젝트부터 각 컴포넌트가 무슨 일을 하는지, 또 유니티에서 물리 법칙이 어떤 방식으로 이루어지는지를 배웠다. 현실의 물리 법칙(ex : F = ma)이 내부 물리 법칙 계산과 같은 것에 놀랐고 이 값들을 조정하여 나만의 게임을 만들 수도 있다는 것을 알았다.
실습 과제를 진행하면서 어려운 점이 많았다. 내 프로젝트는 2D로 진행했는데 3D를 주로 다뤄보다가 2D 컴포넌트를 쓰려니 까먹고 바꿔써야 하는 걸 안 바꿔 쓴 적이 많았다. 프로젝트 중 첫 난관은 Vector.Velocity의 x축으로 속도를 조절하려니 중력 값이 override돼서 플레이어가 생각대로 움직이지 않는 것이었다. 이는 그냥 내가 수동으로 중력 값을 넣어줘서 해결하였다. 두 번째 난관은 Raycast부분에서 어쩔 때는 hit이 되고 어쩔 때는 hit이 안 되는 것 때문에 애를 먹었다. DrawRay로 선을 그려가면서 해보았지만 안 되는 것에 시간을 많이 낭비한 것 같다. 여기서 첫 번째 문제는 3D환경에서 사용하던 것처럼 Physics를 사용한 것이었고 두 번째 문제는 바닥의 끝으로 갔을 때 Ray로 검출하다보니 캐릭터 transform의 위치 때문에 검출이 안 될 때가 있던 것이었다. 이후에 첫 번째 문제는 Physics2D를 사용해서 해결하였고 두 번째 문제는 Boxcast로 바닥을 체크해 해결하였다.
이번 주차에 토이 프로젝트를 진행하면서 느낀 점은 기능 기능을 따로 구현하는 것이 아니라 작은 게임을 처음부터 구현한다는 점이 좋았다. 처음부터 스스로 구현하다보니 어떻게 구현하면 좋을지에 대해서 먼저 생각하는 시간도 있고 그 생각대로 프로그래밍을 하는 것과 그 구현방식에 따른 다른 구현 방법도 달라지는 것이 재밌었다. 그리고 내가 부족한 점이 어느 부분에 있다는 것을 알 수 있어서 좀 더 성장할 수 있었다고 생각이 들었다.
이전의 개발 교육 기간에 들었던 이론들을 기반으로 눈에 보이는 결과물이 있는 실습 시간으로 들어오니까 학습 성취도도 늘고 재밌게 교육에 참여할 수 있었던 것 같다. 학습이 재밌고 내가 성장하는 걸 보면서 동기부여도 되는 것 자체도 재밌는 것 같다. 이 마음가짐으로 끝까지 교육을 수행했으면 좋겠다. 다음주도 파이팅!
본 포스팅은 유데미-웅진씽크빅 취업 부트캠프 유니티 1기 과정 후기로 작성되었습니다.
'유데미 스타터스 유니티 개발자 1기' 카테고리의 다른 글
유데미 스타터스 유니티 개발자 1기 취업 부트캠프 - 7주차(개발) (0) | 2022.08.07 |
---|---|
유데미 스타터스 유니티 개발자 1기 취업 부트캠프 - 6주차(개발) (0) | 2022.07.31 |
유데미 {따라하면서 배우는 고박사의 유니티 기초 [Part 01] [유니티 기초]} 후기 (0) | 2022.07.17 |
유데미 스타터스 유니티 개발자 1기 취업 부트캠프 - 3주차(개발) (0) | 2022.07.10 |
유데미 스타터스 유니티 개발자 1기 취업 부트캠프 - 2주차(기획) (0) | 2022.07.03 |