Newer
Older
fall / FallUnity / Assets / Leaves / LeafPhysics.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[SelectionBase]
public class LeafPhysics : MonoBehaviour {
    private Rigidbody body;
    public Rigidbody Body { get { return body; } }

    private LeafHandler leafHandler;

    private List<Material> mats = new List<Material>();
    public IEnumerable<Material> Mats { get { return mats; } }

	// Use this for initialization
	void Start () {
        body = GetComponentInChildren<Rigidbody>();
        PlusX = new TweeningForce(0, 1, 0.5f, 2);
        MinusX = new TweeningForce(0, 1, 0.5f, 2);
        PlusZ = new TweeningForce(0, 1, 0.5f, 2);
        MinusZ = new TweeningForce(0, 1, 0.5f, 2);

        TorqueX = new TweeningForce(-1, 1, 0.5f, 2);
        TorqueZ = new TweeningForce(-1, 1, 0.5f, 2);

        leafHandler = GameObject.FindObjectOfType<LeafHandler>();
        if (leafHandler != null)
        {
            leafHandler.AddLeaf(this);
        }

        deadLifeSpan = UnityEngine.Random.Range(20, 40.0f);
        deadLife = deadLifeSpan;

        foreach (MeshRenderer mr in GetComponentsInChildren<MeshRenderer>())
        {
            mats.Add(mr.material);
        }

        Color c = Color.white;
        c.r = UnityEngine.Random.Range(0.6f, 1.0f);
        c.g = UnityEngine.Random.Range(0.6f, 1.0f);
        c.b = UnityEngine.Random.Range(0.6f, 1.0f);
        foreach (Material m in mats)
        {
            m.color = c;
        }
    }

    void OnDestroy()
    {
        if (leafHandler != null)
        {
            leafHandler.RemoveLeaf(this);
        }
    }

    //public float TorqueForceUpScale = 0.3f;
    public float TorqueScale = 1.0f;
    public float TorqueBounds = 5.0f;
    public float TorqueSideScale = 1.0f;

    public float SideRestitution = 0.5f;
    //public float MaxForceUp = 1.0f;
    //
    //public float VelDownForceScale = 0.5f;
    //public float VelDownForceDivide = 3f;
    //
    //public float StartPushVelDown = 3.0f;
    //public float PushUpCurrent = 0;
    //public float PushUpForce = 10;
    //public float PushUpDecayRate = 1;

    public bool Debug = false;

    private TweeningForce PlusX;
    private TweeningForce MinusX;
    private TweeningForce PlusZ;
    private TweeningForce MinusZ;

    private TweeningForce TorqueX;
    private TweeningForce TorqueZ;

    private class TweeningForce
    {
        private float min, max, minMoveSpeed, maxMoveSpeed, current, target, speed;
        public float Current { get { return current; } }

        public TweeningForce(float min, float max, float minMoveSpeed, float maxMoveSpeed)
        {
            this.min = min;
            this.max = max;
            this.minMoveSpeed = minMoveSpeed;
            this.maxMoveSpeed = maxMoveSpeed;
            this.current = UnityEngine.Random.Range(min, max);
            UpdateTarget();
        }

        private void UpdateTarget()
        {
            this.target = UnityEngine.Random.Range(min, max);
            this.speed = UnityEngine.Random.Range(minMoveSpeed, maxMoveSpeed);
        }

        public void Update(float timeElapsed)
        {
            float moveAmount = speed * timeElapsed;
            if (moveAmount >= Math.Abs(current - target))
            {
                current = target;
                UpdateTarget();
            }
            else
            {
                float move = Math.Max(-moveAmount, Math.Min(moveAmount, target - current));
                current += move;
            }
        }
    }

    public float BaseWindForce = 10;
    public float AnglePow = 2;

    private float deadLife = 0;
    private float deadLifeSpan = 1;

    private bool isFading = false;

    public bool Resting = false;
    private void FixedUpdate()
    {
        Resting = false;
    }

    private void OnCollisionStay(Collision collision)
    {
        Resting = collision.contacts.Length > 0 && Mathf.Abs(collision.contacts[0].normal.y) > 0.7f;
    }

    // Update is called once per frame
    void Update () {
        if (!body.isKinematic && body.velocity.sqrMagnitude > 0.01f)
        {
            deadLife = deadLifeSpan;
            float baseForceUp = Mathf.Max(BaseWindForce * Mathf.Pow(Mathf.Abs(transform.up.y), AnglePow) * -body.velocity.y) * Time.deltaTime * 60;

            body.AddForceAtPosition(Vector3.up * baseForceUp * (1 + PlusX.Current), transform.position + Vector3.right * 0.4f);
            body.AddForceAtPosition(Vector3.up * baseForceUp * (1 + MinusX.Current), transform.position - Vector3.right * 0.4f);
            body.AddForceAtPosition(Vector3.up * baseForceUp * (1 + PlusZ.Current), transform.position + Vector3.forward * 0.4f);
            body.AddForceAtPosition(Vector3.up * baseForceUp * (1 + MinusZ.Current), transform.position - Vector3.forward * 0.4f);

            /*
            if (body.velocity.y < -StartPushVelDown)
            {
                PushUpCurrent = PushUpForce;
            }

            body.AddForce(Vector3.up * PushUpCurrent);

            PushUpCurrent = Mathf.Max(0, PushUpCurrent - PushUpDecayRate * Time.deltaTime);
            */
            Vector3 up = transform.up;
            float torqueX = -up.x;
            float torqueZ = -up.z;

            float velYScale = Math.Max(0, -body.velocity.y);

            body.AddRelativeForce(new Vector3(torqueX * TorqueSideScale, 0, torqueZ * TorqueSideScale) * velYScale);
            //body.AddForce(Vector3.up * Mathf.Pow(Mathf.Max(-body.velocity.y / VelDownForceDivide), 2) * VelDownForceScale);
            body.AddRelativeTorque(new Vector3(Mathf.Max(-TorqueBounds, Mathf.Min(TorqueBounds, torqueX * TorqueScale * 1.2f)) + TorqueX.Current * 0.5f, 0, Mathf.Max(-TorqueBounds, Mathf.Min(TorqueBounds, torqueZ * TorqueScale)) + TorqueZ.Current * 0.5f) * velYScale);

            body.AddForce(new Vector3(-body.velocity.x, 0, -body.velocity.z) * SideRestitution * Time.deltaTime * 60);
        }
        else
        {
            deadLife -= Time.deltaTime * (leafHandler.NumLeaves >= leafHandler.MaxLeaves ? 3 : 1);
            if (deadLife <= 0 && !isFading)
            {
                FadeOut(2);                
            }
        }

        if (transform.position.y < -5)
        {
            Destroy(gameObject);
        }
    }

    public void FadeOut(float time)
    {
        isFading = true;
        SimpleTween tweenOut = new SimpleTween();
        tweenOut.Init(v =>
        {
            foreach (Material m in mats)
            {
                Color c = m.color;
                c.a = v;
                m.color = c;
            }
        }, SimpleTween.EaseLinear, 1, 0, time);
        GameObject t = this.gameObject;
        tweenOut.OnFinish = () =>
        {
            Destroy(t);
        };
        Destroy(this);
    }
}