2023. 12. 27. 22:29ㆍSKKU DT
HDRI 적용
전체적인 빛 색감을 잡기 위해서 HDRI부터 적용한다.
뒤에 산이 있으므로 나무가 있는 HDRI를 배치하였다.
교실 내부 에셋 채우기
내부 에셋은 모델링 할 시간이 없으므로 무료 에셋으로 채운다.
https://assetstore.unity.com/packages/3d/environments/low-poly-office-props-lite-131438
대략적으로 공간이 채워진 느낌이 든다.
**건물을 포함한 모든 에셋들은 Static 설정을 한다.
캐릭터 가져오기
https://assetstore.unity.com/packages/3d/characters/humanoids/casual-1-anime-girl-characters-185076
메테리얼 오류는 URP-Lit으로 바꾸고 Transparent로 투명도를 주었고,
Alpha Clipping을 체크하여 투명해진 파트가 보이지 않게 만들었다.
Scale은 1.5배로 설정하여 환경과 맞추었다.
플레이어 이동 / 카메라 회전
3인칭 카메라 시점으로 플레이어를 이동하게 만들 예정이다.
Player 캐릭터에 Character Contoller 추가,
Cinemachine 설치
PlayerMovement 스트립트 추가
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMove : MonoBehaviour
{
CharacterController characterController;
private new Transform transform;
private new Camera camera;
//플레이어 이동 속도
public float movespeed = 10f;
//플레이어 카메라 회전
public float rotSpeed = 200f;
//회전값 변수
float mx = 0;
void Start()
{
characterController = GetComponent<CharacterController>();
transform = GetComponent<Transform>();
camera = Camera.main;
}
void Update()
{
Move();
Turn();
}
float h => Input.GetAxis("Horizontal");
float v => Input.GetAxis("Vertical");
void Move()
{
//카메라 설정
Vector3 cameraForward = camera.transform.forward;
Vector3 cameraRight = camera.transform.right;
cameraForward.y = 0;
cameraRight.y = 0;
Vector3 moveDir = (cameraForward * v) + (cameraRight * h);
moveDir.Set(moveDir.x, 0.0f, moveDir.z);
characterController.SimpleMove(moveDir * movespeed);
}
void Turn()
{
float horizontal = Input.GetAxis("Mouse X");
mx += horizontal * rotSpeed * Time.deltaTime;
transform.eulerAngles = new Vector3(0, mx, 0);
}
}
CharacterController의 속성 때문에 실행을 하면 캐릭터가 밑으로 떨어진다. 바닥에 Mesh Collider를 설정하고 Convex Mesh를 체크한다.
캐릭터의 Capsule Collider가 잘 안맞아서 공중부양해서 돌아다니길래 콜라이더를 캐릭터 몸에 맞게 수정하였다.
이제 씨네머신 Virtual 카메라를 적용하여 카메라가 캐릭터를 따라다니게 한다.
using Cinemachine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMove : MonoBehaviour
{
CharacterController characterController;
private new Transform transform;
private new Camera camera;
//플레이어 이동 속도
public float movespeed = 10f;
//플레이어 카메라 회전
public float rotSpeed = 200f;
//회전값 변수
float mx = 0;
//시네머신 가져오기
CinemachineVirtualCamera virtualCamera;
void Start()
{
characterController = GetComponent<CharacterController>();
transform = GetComponent<Transform>();
camera = Camera.main;
//virtualCamera 가져오기
virtualCamera = GameObject.FindObjectOfType<CinemachineVirtualCamera>();
virtualCamera.Follow = transform;
virtualCamera.LookAt = transform;
}
void Update()
{
Move();
Turn();
}
float h => Input.GetAxis("Horizontal");
float v => Input.GetAxis("Vertical");
void Move()
{
//카메라 설정
Vector3 cameraForward = camera.transform.forward;
Vector3 cameraRight = camera.transform.right;
cameraForward.y = 0f;
cameraRight.y = 0f;
Vector3 moveDir = (cameraForward * v) + (cameraRight * h);
moveDir.Set(moveDir.x, 0.0f, moveDir.z);
characterController.SimpleMove(moveDir * movespeed);
}
void Turn()
{
float horizontal = Input.GetAxis("Mouse X");
mx += horizontal * rotSpeed * Time.deltaTime;
transform.eulerAngles = new Vector3(0, mx, 0);
}
}
위와 같은 상황에서는 내 카메라로 내 캐릭터를 볼 수가 없어서, 카메라가 돌아갈 때 캐릭터가 같이 돌아가지는 않게 수정해야 할 것 같다. 수 많은 시도 끝에 Cinemachine 컴포넌트 수정으로 비슷한 구현까지 했다!
Virtual Camera의 Body는 [Orbital Transposer]로, Aim은 [Composer]로 설정했다.
[Orbital Transposer]의 [Follow Offset] 수정은 원하는 만큼 하면 된다.
Virtual Camera의 Recenter는 꺼야 시간 지나도 다시 제자리로 가지 않는다. Input Axis Value에 Invert를 체크 해제하면 마우스가 가는 방향대로 시야가 회전되어 더 직관적이다.
원하고자 했던 플레이어 무브를 구현하였다. 카메라가 비추는 방향 쪽으로 플레이어가 이동하며 상하좌우 키를 입력하면 플레이어가 해당 방향으로 몸을 튼다. [Lock To Target On Assign]을 해서 플레이어가 이동 중 방향을 틀어도 카메라는 그 자리를 유지하도록 설정하였다.
using Cinemachine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMove : MonoBehaviour
{
CharacterController characterController;
private new Transform transform;
private new Camera camera;
//플레이어 이동 속도
public float movespeed = 10f;
//플레이어 카메라 회전
public float rotSpeed = 200f;
//회전값 변수
float mx = 0;
//시네머신 가져오기
CinemachineVirtualCamera virtualCamera;
//플레이어 움직임 true/false
bool isMoving = false;
void Start()
{
characterController = GetComponent<CharacterController>();
transform = GetComponent<Transform>();
camera = Camera.main;
//virtualCamera 가져오기
virtualCamera = GameObject.FindObjectOfType<CinemachineVirtualCamera>();
}
void Update()
{
Move();
}
float h => Input.GetAxis("Horizontal");
float v => Input.GetAxis("Vertical");
void Move()
{
//카메라 설정
Vector3 cameraForward = camera.transform.forward;
Vector3 cameraRight = camera.transform.right;
cameraForward.y = 0f;
cameraRight.y = 0f;
Vector3 moveDir = (cameraForward * v) + (cameraRight * h);
moveDir.Set(moveDir.x, 0.0f, moveDir.z);
characterController.SimpleMove(moveDir * movespeed);
if (Input.GetKey("w"))
{
transform.forward = cameraForward;
}
if (Input.GetKey("a"))
{
transform.right = cameraForward * 90;
}
if (Input.GetKey("s"))
{
transform.forward = -cameraForward;
}
if (Input.GetKey("d"))
{
transform.right = cameraForward * -90;
}
}
}
파이어 베이스를 이용한 로그인 구현
앞서 했던 파이어베이스(Firebase)를 이용한 로그인 구현과 동일하게 진행할 예정이다.
Firebase에서, 유니티 프로젝트로 앱추가를 한다.
FirebaseAuthManager 스크립트 생성
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Firebase.Auth;
using TMPro;
public class FirebaseAuthManager : MonoBehaviour
{
private FirebaseAuth auth; //인증을 위한 변수 선언
public TMP_InputField email;
public TMP_InputField password;
void Start()
{
auth = FirebaseAuth.DefaultInstance;
}
public void Create() //계정 생성
{
auth.CreateUserWithEmailAndPasswordAsync(email.text, password.text).ContinueWith(task => //람다식, 결과는 task가 나온다.
{
if (task.IsFaulted) //계정을 만들지 못했을 경우,
{
Debug.Log("계정 생성 실패");
return;
}
if (task.IsCanceled) //계정 생성을 실패했을 경우(네트워크 장애, 도중 취소)
{
Debug.Log("계정 생성 취소");
return;
}
FirebaseUser newUser = task.Result.User; //계정을 만들지 못하거나 실패했을 경우가 아닐 경우
});
}
public void LogIn() //로그인 함수 계정 생성과 동일한 코드 복붙, 수정
{
auth.SignInWithEmailAndPasswordAsync(email.text, password.text).ContinueWith(task => //람다식, 결과는 task가 나온다.
{
if (task.IsFaulted) //로그인을 못했을 경우,
{
Debug.Log("로그인 실패");
return;
}
if (task.IsCanceled) //로그인을 실패했을 경우(네트워크 장애, 도중 취소)
{
Debug.Log("로그인 취소");
return;
}
Debug.Log("로그인 성공");
});
}
public void LogOut() //로그아웃 함수
{
auth.SignOut();
Debug.Log("로그아웃"); //로그아웃 확인
}
}
로그인 씬 만들기 -InputField와 Button 배치
빈 오브젝트 또는 EventSystem에 만든 FirebaseAuthManager 스크립트 넣기
씬 이동을 위한 LogIn 함수 수정. SceneManager.LoadScene(1); 추가하였다. 비동기 함수이기 때문에 아래와 같이 수정한다. 이전 글 참조.
public async void LogIn() //로그인 함수 계정 생성과 동일한 코드 복붙, 수정
{
try
{
var signInTask = await auth.SignInWithEmailAndPasswordAsync(email.text, password.text);
Debug.Log("로그인 성공");
SceneManager.LoadScene(1);
}
catch (System.Exception e)
{
Debug.Log($"로그인 실패: {e.Message}");
}
}
public으로 TextMeshPro 하나 가져와서 로그인 실패 메시지 출력
public async void LogIn() //로그인 함수 계정 생성과 동일한 코드 복붙, 수정
{
try
{
var signInTask = await auth.SignInWithEmailAndPasswordAsync(email.text, password.text);
Debug.Log("로그인 성공");
SceneManager.LoadScene(1);
}
catch (System.Exception e)
{
Debug.Log($"로그인 실패: {e.Message}");
loginFailedtext.text = "Login Failed";
}
}
완성, 전체 코드
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Firebase.Auth;
using TMPro;
using UnityEngine.SceneManagement;
public class FirebaseAuthManager : MonoBehaviour
{
private FirebaseAuth auth; //인증을 위한 변수 선언
public TMP_InputField email;
public TMP_InputField password;
public TMP_Text loginFailedtext;
void Start()
{
auth = FirebaseAuth.DefaultInstance;
}
public void Create() //계정 생성
{
auth.CreateUserWithEmailAndPasswordAsync(email.text, password.text).ContinueWith(task => //람다식, 결과는 task가 나온다.
{
if (task.IsFaulted) //계정을 만들지 못했을 경우,
{
Debug.Log("계정 생성 실패");
return;
}
if (task.IsCanceled) //계정 생성을 실패했을 경우(네트워크 장애, 도중 취소)
{
Debug.Log("계정 생성 취소");
return;
}
FirebaseUser newUser = task.Result.User; //계정을 만들지 못하거나 실패했을 경우가 아닐 경우
});
}
public async void LogIn() //로그인 함수 계정 생성과 동일한 코드 복붙, 수정
{
try
{
var signInTask = await auth.SignInWithEmailAndPasswordAsync(email.text, password.text);
Debug.Log("로그인 성공");
SceneManager.LoadScene(1);
}
catch (System.Exception e)
{
Debug.Log($"로그인 실패: {e.Message}");
loginFailedtext.text = "Login Failed";
}
}
public void LogOut() //로그아웃 함수
{
auth.SignOut();
Debug.Log("로그아웃"); //로그아웃 확인
}
}
아직 남은 과제
-플레이어 애니메이션 넣기
-포톤 적용