0.AddTorque()를 이용해서 팽이 Rotation 구현하기
2024.10.15 - [UNITY] - [UNITY] 첫 3D 팽이 게임 만들기 - 팽이 프로토타입 만들기
어제 글을 작성하고 인스펙터 창에 이것저것 실험해 보았다.
회전력을 크게 주어도 생각한 만큼 회전이 나오지 않았다.
이에 최대한 고정프레임에서 가장 많이 그리고 빠르게 회전하는 팽이를 만들기 위해 물리 연산에 변수들을 정리 분석해 보았다.
1. AddTorque() 분석을 위한 임시 코드 수정
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using UnityEngine;
using Vector3 = UnityEngine.Vector3;
public class SpinController : MonoBehaviour
{
[SerializeField] private float initRotateSpeed; // 초기회전력
[SerializeField] private float speed; //이동속도
private Vector3 inputVec; //상대 이동벡터
[SerializeField]private float speedDamper; //속도 감소률
[SerializeField]private float rotateSpeed; //회전력
private Rigidbody rb;
private void Awake() {
Application.targetFrameRate = 60; //고정프레임 60FPS
rb = GetComponent<Rigidbody>();
initRotateSpeed = 1f;
speed = initRotateSpeed*0f;
speedDamper = 0.99f;
rotateSpeed = initRotateSpeed;
}
private void Update()
{
//float inputX = Input.GetAxis("Horizontal");
//inputVec = new Vector3(inputX, 0, 0) * speed *Time.deltaTime;
}
void FixedUpdate()
{
//transform.Translate(inputVec, Space.World);
rb.AddTorque(transform.up * initRotateSpeed * Time.fixedDeltaTime);
}
}
이번 분석을 위해 회전과 관련 없는 코드는 삭제 수정했으며, 고정 프레임을 추가하였다.
고정 프레임을 작성한 이유는 매번 돌릴 때마다 200~350 FPS 차이의 변동으로 회전과 이동이 변했기 때문이다.
[결과] 금방 땅에 쓰러지고 남은 회전으로 굴러다니면서 원뿔이 탈출함
하도 바닥에서 뺑뺑 돌기만 해서 바닥에서 얼마나 굴렀는지 확인하는 스크립트를 추가했다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CheckerLogger : MonoBehaviour
{
private void OnTriggerEnter(Collider other) {
if(other.CompareTag("Floor")){
Debug.Log("쓰러짐");
return;
}
}
}
2.RigidBody의 AddTorque(Vector3 vector, ForceMode force);
파라미터 vector = 회전축과 회전력,
파라미터 force = RigidBody의 1. 질량(M), 2. 프레임당 시간(DT), 3. 토크(Tor)의 계산방식을 결정한다.
force의 변수
ForceMode.Force = Tor*DT/M
ForceMode.Acceleration = Tor * DT
ForceMode.Impulse =Tor/M
ForceMode.VelocityChange = T
RigidBody의 Mass(질량), Drag(저항), Angular Drag(각 저항)은 모두 물리연산에 영향을 준다.
이번 팽이 돌리기에 활용 변수는 각저항을 이용하고자 한다.
값을 변경하면서 실험해 본결과 의미 있는 내용 몇 가지를 추려보았다.
[실험 1] M:D:A = 1 : 0 : 0.05
[결과 1]
약 2바퀴 회전 후 쓰러짐, 이후 남은 회전에 의해 밖으로 탈출.
쓰러지는 것을 체크할 필요가 있음.
[실험 2] M:D:A = 1 : 0 : ~50
팽이가 멈출 때까지 각저항을 증가시킴.
[결과 2]
Angular Drag = 50 될 때부터 회전 멈춤.
각저항을 점진적으로 증가시켜서 팽이가 쓰러지고 완전히 멈추게 해야 함.
착지하자마자 각 저항이 오른다면 회전력 유지 못함.
따라서
- rotationSpeed와 initRotateSpeed를 이용해서 회전 유지 시간을 확보 중력을 끄고 키면서 오래 돌아가는 것처럼 보이게 설정.
- Raycast를 이용해 바닥을 체크하고 쓰러질 때의 거리를 측정하여 각저항과 중력을 통제.
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using JetBrains.Annotations;
using Unity.VisualScripting;
using UnityEngine;
using Vector3 = UnityEngine.Vector3;
public class SpinController : MonoBehaviour
{
[SerializeField] private float initRotateSpeed; // 초기회전력
[SerializeField] private float speed; //이동속도
private Vector3 inputVec; //상대 이동벡터
[SerializeField]private float speedDamper; //속도 감소률
[SerializeField]private float rotateSpeed; //회전력
private Rigidbody rb;
private RaycastHit hit;
private bool isGrounded;
private bool isStoped;
private void Awake() {
Application.targetFrameRate = 60; //고정프레임 60FPS
rb = GetComponent<Rigidbody>();
initRotateSpeed = 100f;
speed = initRotateSpeed * 0.1f;
speedDamper = 0.99f;
rotateSpeed = initRotateSpeed;
isGrounded = false;
isStoped = false;
}
private void Update()
{
StateCheck();
float inputX = Input.GetAxis("Horizontal");
inputVec = new Vector3(inputX, 0, 1) * speed * Time.deltaTime;
}
void FixedUpdate()
{
transform.Translate(inputVec, Space.World);
rb.AddTorque(new Vector3 (0,100000,0) * rotateSpeed, ForceMode.VelocityChange);
}
public void StateCheck(){
if(isGrounded){
if (Physics.Raycast(rb.transform.position, transform.right, out hit)) {
Debug.DrawRay(rb.transform.position, transform.right * hit.distance, Color.red);
if(hit.distance < 1f && rb.angularDrag < 50){
Debug.Log("쓰러지이인다");
rb.angularDrag += 0.65f;
}
if(rb.angularDrag >=50){
Debug.Log("멈춤");
isGrounded = false;
isStoped = true;
}
else
{
Debug.DrawRay(rb.transform.position, -transform.up * 1000f, Color.red);
}
}
}
if(!isStoped){
if (Physics.Raycast(rb.transform.position, -transform.up, out hit))
{
Debug.DrawRay(rb.transform.position, -transform.up * hit.distance, Color.red);
if(hit.distance < 0.25f){
Debug.Log("착지");
isGrounded = true;
rb.useGravity = false;
}
}
else
{
Debug.DrawRay(rb.transform.position, transform.right * 1000f, Color.red);
}
if(rotateSpeed < 1){
rb.useGravity = true;
}else{
rotateSpeed *= speedDamper;
speed *= speedDamper;
}
}
else {
speed = 0;
rotateSpeed =0;
}
}
}
실행 결과 어제보다는 쪼금 더 잘 도는 거 같다.
'게임만들기 > UNITY' 카테고리의 다른 글
[UNITY] 첫 3D 팽이 게임 만들기 - 벽 충돌과 Raycast Test (1) | 2024.10.19 |
---|---|
[UNITY] 첫 3D 팽이 게임 만들기 - 원뿔로 팽이 프로토타입 만들기 (1) | 2024.10.15 |