상세 컨텐츠

본문 제목

유니티 플라잉보드, 호버보드 시스템

유니티/기능

by MJ_119 2024. 10. 19. 14:16

본문

유니티 플라잉보드, 호버보드 시스템

 

 

 

- hbController.cs

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class HbController : MonoBehaviour
{
    Rigidbody rb;

    // 부력 계산에 사용할 멀티플라이어
    public float multiplier;
    // 전진할 때 사용할 힘과 회전할 때 사용할 토크 값
    public float moveForce, turnTorque;

    // 호버보드를 지탱하는 앵커들 (4개의 Transform 위치)
    public Transform[] anchors = new Transform[4];

    // RaycastHit 배열로, 앵커 각각에 대한 바닥 충돌 정보를 저장
    RaycastHit[] hits = new RaycastHit[4];


    void Start()
    {
        rb = GetComponent<Rigidbody>();
    }

    void Update()
    {
        // 현재는 Update에 별다른 로직이 없지만, 입력 등을 여기에 추가할 수 있음
    }

    // FixedUpdate는 매 물리 프레임마다 호출되며, 물리적인 계산을 처리함
    private void FixedUpdate()
    {
        // 4개의 앵커 각각에 대해 물리적인 부력을 적용
        for (int i = 0; i < 4; i++)
        {
            // 앵커와 해당 앵커의 RaycastHit 정보를 전달하여 부력을 계산
            ApplyForce(anchors[i], hits[i], i);
        }

        // 사용자의 입력을 받아 전방으로 움직이는 힘을 적용 (W, S키 등)
        rb.AddForce(Input.GetAxis("Vertical") * moveForce * transform.forward);

        // 사용자의 입력을 받아 좌우 회전 토크를 적용 (A, D키 등)
        rb.AddTorque(Input.GetAxis("Horizontal") * turnTorque * transform.up);


        //rb.AddForce(Input.GetAxis("Horizontal") * turnTorque * transform.up);
    }

    // 앵커 위치를 기준으로 바닥에 Ray를 쏘아 부력을 적용하는 함수
    void ApplyForce(Transform anchor, RaycastHit hit, int index)
    {
        // 앵커의 위치에서 바닥으로 Ray를 쏴서 바닥과 충돌했는지 확인
        if (Physics.Raycast(anchor.position, -anchor.up, out hit))
        {
            // 충돌 정보를 hits 배열에 저장 (Raycast의 결과)
            hits[index] = hit;

            // 부력 계산을 위한 힘 변수 초기화
            float force = 0;

            // 앵커와 바닥 사이의 거리에 따라 부력을 계산 (거리가 짧을수록 더 큰 부력)
            force = Mathf.Abs(1 / (hit.point.y - anchor.position.y));

            // 계산된 부력을 해당 앵커 위치에 적용, 부유하는 힘을 생성
            rb.AddForceAtPosition(transform.up * force * multiplier, anchor.position, ForceMode.Acceleration);
        }
    }
}

 

using System.Collections;
using System.Collections.Generic;
using UnityEditor.Rendering;
using UnityEditor.ShaderGraph;
using UnityEngine;

public class player_move : MonoBehaviour
{
    CharacterController characterController;
    PlayerControlls playerControlls;

    [SerializeField] float runSpeed;
    [SerializeField] float walkSpeed = 3f;

    Vector3 movementDirection;
    public Vector2 moveInput { get; private set; }
    public float speed;
    float verticalVelocity;

    bool isRunning = false;

    bool onHoverBoard = false;
    private void Awake()
    {
        playerControlls = new PlayerControlls();
    }

    void Start()
    {
        characterController = GetComponent<CharacterController>();
        speed = walkSpeed;
        AssignInputEvents();
    }

    void Update()
    {
        GameObject hover = GameObject.FindWithTag("Hover");

        if(onHoverBoard == false)
        {
            movementDirection = new Vector3(moveInput.x, 0, moveInput.y);

            ApplyGravity();

            if (movementDirection.magnitude > 0)
            {
                characterController.Move(movementDirection * speed * Time.deltaTime);
            }
        }
        
        


        if (Input.GetKeyDown(KeyCode.E))
        {
            
            onHoverBoard = true;
            
        }
        if(onHoverBoard) 
        {
            gameObject.transform.localPosition = hover.transform.localPosition;
            gameObject.transform.localRotation = hover.transform.localRotation;
        }

    }


    private void ApplyGravity()
    {
        if (characterController.isGrounded == false)
        {
            verticalVelocity -= 9.81f * Time.deltaTime;
            movementDirection.y = verticalVelocity;
        }
        else
        {
            verticalVelocity = -0.5f;
        }
    }

    private void AssignInputEvents()
    {
        playerControlls.Character.Movement.performed += context => moveInput = context.ReadValue<Vector2>();
        playerControlls.Character.Movement.canceled += context => moveInput = Vector2.zero;

        playerControlls.Character.Run.performed += context =>
        {
            speed = runSpeed;
            isRunning = true;

        };
        playerControlls.Character.Run.canceled += context =>
        {
            speed = walkSpeed;
            isRunning = false;
        };
    }


    private void OnEnable()
    {
        if (playerControlls == null)
        {
            playerControlls = new PlayerControlls();
        }
        playerControlls.Enable();
    }

    private void OnDisable()
    {
        playerControlls.Disable();
    }
}

 

추천 설정 요약:

  • Dynamic Friction: 0 (또는 매우 낮은 값)
  • Static Friction: 0 ~ 0.2 (정지 시 미끄러짐을 최소화)
  • Friction Combine: Minimum (낮은 마찰력 선택)
  • Bounce Combine: Minimum (충돌 후 튕김을 최소화)

1. Dynamic Friction (동적 마찰력)

  • Dynamic Friction은 물체가 움직일 때 적용되는 마찰력을 의미합니다.
  • 호버보드는 공중에 떠 있듯이 움직여야 하기 때문에 Dynamic Friction 값을 매우 낮게 설정하거나 0으로 설정하는 것이 좋습니다. 이렇게 하면 호버보드가 움직일 때 지면과의 마찰을 최소화하여 부드럽게 이동하게 됩니다.

추천 설정: 0 (또는 매우 낮은 값)

2. Static Friction (정적 마찰력)

  • Static Friction은 물체가 정지해 있을 때 적용되는 마찰력입니다.
  • 호버보드가 정지해 있을 때에도 약간의 미끄러짐을 표현하려면, Static Friction 값도 낮게 설정하는 것이 좋습니다. 하지만 너무 낮게 설정하면 정지 상태에서 호버보드가 계속 미끄러질 수 있으므로 Dynamic Friction보다 약간 높게 설정하는 것이 좋습니다.

추천 설정: 0 ~ 0.2 (정지 시에도 부드럽게 유지하되, 완전히 미끄러지지 않게 설정)

3. Friction Combine (마찰력 조합)

  • Friction Combine은 물체가 다른 표면과 충돌했을 때, 마찰력을 어떻게 결합하여 계산할지를 결정합니다. Average, Minimum, Multiply, Maximum 옵션이 있습니다.
    • Average: 두 물체의 마찰력을 평균으로 계산합니다.
    • Minimum: 더 작은 마찰력을 선택합니다.
    • Multiply: 두 마찰력을 곱합니다.
    • Maximum: 더 큰 마찰력을 선택합니다.
  • 호버보드처럼 낮은 마찰력을 유지해야 할 경우 Minimum이나 Average가 적합합니다. Minimum은 다른 물체와의 마찰력을 더 낮게 계산해 부드러운 움직임을 제공합니다.

추천 설정: Minimum (부드러운 움직임을 위해 작은 마찰력 선택)

4. Bounce Combine (반발력 조합)

  • Bounce Combine은 물체가 충돌할 때 탄성(반발력)을 어떻게 계산할지를 결정합니다. Friction Combine과 유사한 방식으로 작동하며, 충돌 시 물체가 얼마나 튕겨오를지를 결정합니다.
  • 호버보드가 부드럽게 충돌하고 튕겨 오르지 않게 하기 위해 Minimum 또는 Average로 설정하는 것이 좋습니다. Bounciness 값도 낮게 설정하여 튕김을 최소화하세요.

 

Bounciness 설정 가이드:

  • 0: 물체가 전혀 튕겨 오르지 않습니다. (보통 바닥에 착 달라붙는 느낌)
  • 0.1 ~ 0.3: 약간의 탄성. 충돌 후 아주 조금 튕기지만, 거의 눈에 띄지 않을 정도입니다.
  • 0.5 ~ 1.0: 중간에서 높은 탄성. 충돌 후 물체가 눈에 띄게 튕겨 오릅니다.

호버보드에서 Bounciness 설정:

호버보드는 충돌 후에 튕겨 오르는 느낌이 적어야 하므로, Bounciness 값은 낮게 설정해야 합니다.

  • 추천 값: 0 또는 0.1
    이 값으로 설정하면 호버보드가 지면에 닿아도 튕기지 않고 안정적으로 부유하는 느낌을 줄 수 있습니다.

 

 

 

 

호버 보드 시스템 - 보완중

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class HoverboardController : MonoBehaviour
{
    public float hoverHeight = 2.0f;  // 호버 높이
    public float hoverForce = 50.0f;  // 부유 힘
    public float moveSpeed = 10.0f;   // 전진 속도
    public float turnSpeed = 100.0f;  // 회전 속도
    public float strafeSpeed = 7.0f;  // 좌우 이동 속도 (스트레이프)
    public LayerMask groundLayer;     // 바닥 감지용 레이어

    private Rigidbody rb;

    void Start()
    {
        rb = GetComponent<Rigidbody>();
        rb.useGravity = true;  // 중력 사용
    }

    void FixedUpdate()
    {
        Hover();
        Move();
    }

    void Hover()
    {
        // 바닥에서부터의 거리 측정
        Ray ray = new Ray(transform.position, Vector3.down);
        RaycastHit hit;

        if (Physics.Raycast(ray, out hit, hoverHeight * 2, groundLayer))
        {
            // 목표 높이에서 얼마나 떨어졌는지 계산
            float heightDifference = hoverHeight - hit.distance;

            // 부유 힘 적용
            Vector3 force = Vector3.up * heightDifference * hoverForce;
            rb.AddForce(force, ForceMode.Acceleration);
        }
    }

    void Move()
    {
        // 캐릭터처럼 움직임 처리
        //float moveInput = Input.GetAxis("Vertical");
        //float turnInput = Input.GetAxis("Horizontal");
        // 즉각적인 전방 이동 및 회전
        //transform.Translate(Vector3.forward * moveInput * moveSpeed * Time.deltaTime);
        //transform.Rotate(Vector3.up * turnInput * turnSpeed * Time.deltaTime);

        // 전방/후방 이동
        float moveInput = Input.GetAxis("Vertical");
        transform.Translate(Vector3.forward * moveInput * moveSpeed * Time.deltaTime);

        // 좌우 스트레이프 이동 (스트레이프)
        float strafeInput = Input.GetAxis("Horizontal");
        transform.Translate(Vector3.right * strafeInput * strafeSpeed * Time.deltaTime);

        // 좌우 회전 (회전은 별도의 입력으로 분리)
        //float turnInput = Input.GetAxis("Horizontal");
        //transform.Rotate(Vector3.up * turnInput * turnSpeed * Time.deltaTime);


    }
}

 

 

--- 아래 코드가 원하는 방향에 가깝게 움직임.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class HoverboardController : MonoBehaviour
{
    public float hoverHeight = 2.0f;  // 호버 높이
    public float hoverForce = 50.0f;  // 부유 힘
    public float moveSpeed = 10.0f;   // 전진 속도
    public float strafeSpeed = 7.0f;  // 좌우 이동 속도
    public float turnSensitivity = 2.0f;  // 마우스 회전 감도

    public Transform cameraTransform; // 카메라 트랜스폼 참조
    public LayerMask groundLayer;     // 바닥 감지용 레이어

    private Rigidbody rb;
    private float rotationY = 0f;

    void Start()
    {
        rb = GetComponent<Rigidbody>();
        rb.useGravity = true;  // 중력 사용
        Cursor.lockState = CursorLockMode.Locked; // 마우스 잠금
    }

    void FixedUpdate()
    {
        Hover();
        Move();
        RotateWithMouse();
    }

    void Hover()
    {
        // 바닥에서부터의 거리 측정
        Ray ray = new Ray(transform.position, Vector3.down);
        RaycastHit hit;

        if (Physics.Raycast(ray, out hit, hoverHeight * 2, groundLayer))
        {
            // 목표 높이에서 얼마나 떨어졌는지 계산
            float heightDifference = hoverHeight - hit.distance;

            // 부유 힘 적용
            Vector3 force = Vector3.up * heightDifference * hoverForce;
            rb.AddForce(force, ForceMode.Acceleration);
        }
    }

    void Move()
    {
        // 전후/좌우 이동 (카메라 기준 방향)
        float moveInput = Input.GetAxis("Vertical");
        float strafeInput = Input.GetAxis("Horizontal");

        // 카메라 방향을 기준으로 이동 벡터 계산
        Vector3 forward = cameraTransform.forward;
        Vector3 right = cameraTransform.right;

        // 수직축은 무시하여 평면에서만 움직이도록 처리
        forward.y = 0f;
        right.y = 0f;

        forward.Normalize();
        right.Normalize();

        // 전후/좌우 이동
        Vector3 moveDirection = (forward * moveInput + right * strafeInput).normalized;
        rb.MovePosition(rb.position + moveDirection * moveSpeed * Time.deltaTime);
    }

    void RotateWithMouse()
    {
        // 마우스 X 입력을 사용하여 회전
        float mouseX = Input.GetAxis("Mouse X") * turnSensitivity;
        rotationY += mouseX;

        // Y축 회전 적용
        transform.rotation = Quaternion.Euler(0, rotationY, 0);
    }
}

관련글 더보기