K-digital traning/3D 콘텐츠 제작

Placid Plastic Duck Simulator - 개발일지4

내꺼블로그 2023. 10. 10. 18:20

앞서 간단하게 물(같지 않은 물)을 구현하고 이제는 오리를 둥둥 띄울 차례

오리를 띄우기 위해 아래의 영상들을 참고해보았다.

 

 

 

 

https://www.youtube.com/watch?v=eL_zHQEju8s 

 

 

 

 

https://www.youtube.com/watch?v=iasDPyC0QOg&t=404s 

 

 

 

 

 

 

위의 영상들을 참고하여 피사체의 position.y 값이 wave의 position.y값보다 작을 경우 floatingPower를 AddForce하는 방식으로 구현해보았다.

물에 가라앉지 X, 물에 가라앉은 상태 두 가지로 구분하여 Drag값을 조절하였으며 피사체의 모양이 오리임을 감안하여 floating을 한 부분이 아닌 여러 부분에 두어 여러 방향으로 부력을 받을 수 있도록 설정하였다.

오리에 floating 오브젝트를 달아놓고 오리 mesh의 normal방향이 floating 오브젝트의 y방향이 되도록 조절해보았다.

 

 

 

덕지덕지 붙어있는 floating들,,,,

 

 

 

사실 오리 mesh에 있는 vertex들을 이용하여 아들의 위치값과 파도 높이를 비교하여 물에 잠긴게 맞다면 vertex의 normal방향으로 힘을 넣어주는 생각도 해보았는데 값이 제대로 나오는지를 확인하기도 전에 렉이 오지게 걸리는 현상이 발생하여 이 방법은 그냥 포기해버렸다,,,ㅎ

(+계속 더 생각해보니 water의 mesh collider를 triger로 바꾸고(triger로 바꿀 수 있는지를 전에는 몰랐던,,ㅎ) 충돌체크를 하는 방향도 시도해봐야겠다는 생각이 든다,,,ㅎ)

 

 

 

 

 

 Floating.cs

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

public class Floating : MonoBehaviour
{
    [SerializeField]
    private Transform[] arrFloatingTransform;
    public float floatingPow = 1f;
    public float waterDrag;
    public float waterAngluarDrag;
    public float airDrag;
    public float airAngluarDrag;
    private bool isUnderWater;
    private int underWaterFloatingCount;
    private Rigidbody rBody;
    void Start()
    {
        this.rBody = GetComponent<Rigidbody>();
        this.rBody.drag = this.airDrag;
        this.rBody.angularDrag = this.airAngluarDrag;
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        for (int i = 0; i < arrFloatingTransform.Length; i++) {
            float distance = this.arrFloatingTransform[i].position.y - WaveManager.Instance.GetWaveHeight(this.arrFloatingTransform[i].position);
            //Debug.Log(distance);
            if (distance < 0)
            {
                this.rBody.AddForceAtPosition(this.arrFloatingTransform[i].up* floatingPow * Mathf.Abs(distance), this.arrFloatingTransform[i].position, ForceMode.Force);
                underWaterFloatingCount++;
                if (!isUnderWater)
                {
                    this.isUnderWater = true;
                    SwitchDrag();
                }
            } 
        }
        if(isUnderWater&& underWaterFloatingCount==0)
        {
            this.isUnderWater = false;
            SwitchDrag();
        }
        underWaterFloatingCount = 0;
    }

    private void SwitchDrag()
    {
        if(this.isUnderWater)
        {
            this.rBody.drag = this.waterDrag;
            this.rBody.angularDrag = this.waterAngluarDrag;
        }
        else
        {
            this.rBody.drag = this.airDrag;
            this.rBody.angularDrag= this.airAngluarDrag;
        }
    }
}

 

 

 

 

 

 

 

또한 이와 같이 구현하기 위해서는 wave의 position.y값을 알아야만 했다.

초반의 계획은 uv vertex를 움직임으로써 파도를 구현하였으니, 각각의 uv vertex들의 y값을 알아내어 이 값을 오리에 달려있는 floating 오브젝트의 y값과 비교하는 방식으로 구현하는 것이었다.

그러나 uv vertex의 world 좌표를 알아내는 방법을 잘 모르겠어서 차선책으로 위의 영상에 나와있는 것처럼 cs로 mesh의 vertex를 직접 움직이도록 구현하는 방법을 차용해보았다.

값을 계산하여 vertex에 집어넣어주고 오리의 위치와 비교하는 방식을 적용하였다.

이와 같은 구현을 위해 shader로 직접 구현해놓은 파도는 잠시 꺼두었다는 슬픈 비밀,,,ㅎ

 

 

 

WaveManager.cs

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

public class WaveManager : MonoBehaviour
{
    [SerializeField]
    private MeshFilter[] water;
    [SerializeField]
    private GameObject waterGo;
    //private MeshFilter water;
    public float Amplitude;
    public float waveSpeed;
    private float elapsedTime;
    public static WaveManager Instance;
    private void Awake()
    {
        Instance = this;
        //this.water=waterGo.GetComponent<MeshFilter>();
    }

    private void Update()
    {
        for (int j = 0; j < water.Length; j++)
        {
            this.elapsedTime += Time.deltaTime;
            Vector3[] arrPos = water[j].mesh.vertices;
            for (int i = 0; i < arrPos.Length; i++)
            {
                Vector3 pos = arrPos[i]+this.water[j].gameObject.transform.position;
                arrPos[i].y = CalculateWaveHeight(pos.x, pos.z);
                //Debug.LogFormat("<color=yellow>pos[{0}].x: {1}</color>", i, arrPos[i].x);
                //Debug.LogFormat("<color=cyan>pos[{0}].z: {1}</color>", i, arrPos[i].z);
            }
            water[j].mesh.vertices = arrPos;
        }
    }

    public float GetWaveHeight(Vector3 pos)
    {
        return this.CalculateWaveHeight(pos.x, pos.z)+waterGo.transform.position.y;
    }

    private float CalculateWaveHeight(float x, float z)
    {
        float y1 = Mathf.Sin(x + this.elapsedTime * this.waveSpeed) * this.Amplitude * 0.05f;
        float y2 = Mathf.Sin(z + this.elapsedTime * this.waveSpeed) * this.Amplitude * 0.05f;

        float y3 = Mathf.Sin(x + this.elapsedTime * this.waveSpeed * -0.5f) * this.Amplitude * 0.05f;
        float y4 = Mathf.Sin(z + this.elapsedTime * this.waveSpeed * -0.5f) * this.Amplitude * 0.05f;

        return (y1 + y2 + y3 + y4) / 4;
    }
}

 

 

 

 

 

 

---------------------------------------------------------------------------------------------------------------------------------------------------

+ 지금 생각해보니 mesh의 vertex를 움직이게 하지 않고도 shader로 파도를 구현한 상태에서 cs에서 다시 파도 높이 값만 따로 계산해서 그에 맞춰서 오리를 띄워도 됐을 것 같다는 생각이 들었다...ㅎ

그부분을 또 따로 수정해봐야 되겠다 싶은,,,ㅎ