Unity 算法功能 日記

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/KiTok/article/details/77036389


这里写图片描述

Hello ,I am Edwin


首先谢谢大家的支持,其次如果你碰到什么其他问题的话,欢迎来 我自己的一个 讨论群559666429来(扫扫下面二维码),大家一起找答案,共同进步 同时欢迎各大需求商入住,发布自己的需求,给群内伙伴提供副职,赚取外快。

这里写图片描述


点积示例图:
这里写图片描述

##### 首先谢谢大家的支持,其次如果你碰到什么其他问题的话,欢迎来 我自己的一个 讨论群559666429来,大家一起找答案,共同进步

1. 相机根据玩家与玩家位置 进行智能跟随 方法

在使用相机跟随玩家对象的时候。往往会使用多种模式进行。本文将介绍两种方式。
其实,就是就是transform.LookAt 不同参数的效果。
但是,都是可以根据玩家与敌人的距离进行相应的 高度,距离变化的。

由于比较简单,所以直接老规矩上脚本吧。

这里写图片描述

using UnityEngine;

public class FollowCamera : MyMonoSingleton<FollowCamera>
{
    public enum CamerMotionMode
    {
        FollowOnly,  // 只跟随
        Trailing    //尾随,会根据玩家旋转进行旋转
    }

    public CamerMotionMode camerMotionMode = CamerMotionMode.FollowOnly;

    [Tooltip("玩家对象")]
    public Transform SelfTarget;

    [Tooltip("敌人对象")]
    public Transform EnemyTarget;


    [Tooltip("只用于观察")]
    public float spaceBetween = 0; //敌人与自己之间的距离


    public float Distance = 10f;  //后方距离
    public float Height = 5f;   //高度
    public float smooth = 2f;   // 插值系数

    private Vector3 tagerPosition; // 目标位置

    void Awake()
    {
        if (Instance != null)
            Debug.LogError("Instance FollowCamera x2");
        else
        {
            Instance = this;
        }
    }
   
     float dis_register = 0;  //寄存距离值
    
     float height_register = 0;  //寄存高度值

    void LateUpdate()
    {
        //根据双方距离调节相机高度以及距离

        if(EnemyTarget != null)   // 如果存在敌人。则时刻对自己与敌人的距离进行一个插值
        {
            dis_register = Vector3.Distance(SelfTarget.position, EnemyTarget.transform.position);
            spaceBetween = Mathf.Lerp(spaceBetween, dis_register, Time.deltaTime);
        }
        else    // 如果不存在敌人,则缓慢的从新回到最初高度。
        {
            if(spaceBetween > 0 )
            {
                spaceBetween = Mathf.Lerp(spaceBetween, 0,  Time.deltaTime);
            }
            else
            {
                spaceBetween = 0;
            }
        }

        height_register = Height + (spaceBetween / 4);    // 得到最终高度

       
        float currentHeight = Mathf.Lerp(transform.position.y, SelfTarget.position.y + height_register, smooth * Time.deltaTime);    // 对高度进行插值计算。防止出现摄像机位置突然变化



        if(camerMotionMode == CamerMotionMode.FollowOnly)
        {
			// 和下面逻辑一下。
            transform.position = SelfTarget.position;  // 得到玩家位置
            transform.position -= Vector3.forward * (Distance + (spaceBetween / 1.5f));    // 向玩家位置后方移动
            transform.position = new Vector3(transform.position.x, currentHeight, transform.position.z);     // 向上方移动

            transform.LookAt(SelfTarget.position);     // 只看向位置
        }
        else
        {

			/* 下面的代码和上面的代码 效果差不多。逻辑都是一样的。 得到,玩家位置向后以及向上的 一个 向量。然后对让相机去这个向量的位置。 但是如果,使用下面这个方式
			就要对smooth 进行加大,不然,两次插值计算,得到的运动会很慢。或者读者改其他方式,因为这里我不用这个方法所以不多做改动了。哈哈哈 ~ ~ ~ ~
			*/
            tagerPosition = SelfTarget.position + Vector3.up * currentHeight - SelfTarget.forward * (Distance + (spaceBetween / 1.5f));

            transform.position = Vector3.Lerp(transform.position, tagerPosition, Time.deltaTime * smooth);

            transform.LookAt(SelfTarget);     // 看向位置和更具旋转进行相应变化
        }
        
    }

    public void SetTarget(Transform target)
    {
        SelfTarget = target;
    }

    public void SetParame(float dis, float height, float heightDamp)
    {
        Distance = dis;
        Height = height;
        smooth = heightDamp;
    }
}


2. 对象平滑跟随目标

咱还是直接代码吧.官方案例

using UnityEngine;

public class SmoothFollow : MonoBehaviour
	{

		// The target we are following
		[SerializeField]
		private Transform target;
		// The distance in the x-z plane to the target
		[SerializeField]
		private float distance = 10.0f;
		// the height we want the camera to be above the target
		[SerializeField]
		private float height = 5.0f;

		[SerializeField]
		private float rotationDamping;
		[SerializeField]
		private float heightDamping;

		// Use this for initialization
		void Start() { }

		// Update is called once per frame
		void LateUpdate()
		{
			// Early out if we don't have a target
			if (!target)
				return;

			// Calculate the current rotation angles
			var wantedRotationAngle = target.eulerAngles.y;
			var wantedHeight = target.position.y + height;

			var currentRotationAngle = transform.eulerAngles.y;
			var currentHeight = transform.position.y;

			// Damp the rotation around the y-axis
			currentRotationAngle = Mathf.LerpAngle(currentRotationAngle, wantedRotationAngle, rotationDamping * Time.deltaTime);

			// Damp the height
			currentHeight = Mathf.Lerp(currentHeight, wantedHeight, heightDamping * Time.deltaTime);

			// Convert the angle into a rotation
			var currentRotation = Quaternion.Euler(0, currentRotationAngle, 0);

			// Set the position of the camera on the x-z plane to:
			// distance meters behind the target
			transform.position = target.position;
			transform.position -= currentRotation * Vector3.forward * distance;

			// Set the height of the camera
			transform.position = new Vector3(transform.position.x ,currentHeight , transform.position.z);

			// Always look at the target
			transform.LookAt(target);
		}
	}


3. 确定一个对象是否在这个对象的锥形范围之内。可以不使用碰撞或者粒子检测

咱还是直接代码吧.


public class GameAlgorithm
{
    public static bool JudgmentRoundRange(Transform self , Transform tager, float high = 1, float wide = 1,float distance = 100)
    {
        if (Vector3.Distance(self.position, tager.position) > distance || Vector3.Dot(self.position, (tager.position - self.position)) < 0)    // 距离 范围内部 
        {
            Debug.Log("car of distances : Ok");
            return false;
        }
        else
        {
            Debug.Log("two of distances : Ok");
            
            Vector3 shootdir = (tager.position - self.position).normalized;  // 射击的方向
            Vector3 right = self.transform.right;       // 玩家 右边
            Vector3 forward = self.transform.forward;    // 玩家 前面
            Vector3 up = self.transform.up;                     //玩家 下面

            //算出在xoz方向上的投影
            Vector3 dirRight = Vector3.Cross(Vector3.Cross(up, shootdir).normalized, up).normalized;

            //yoz方向上的投影
            Vector3 dirUp = Vector3.Cross(Vector3.Cross(right, shootdir).normalized, right).normalized;
            //左右cos,递减区间(-60,60):(>=)

            bool condition3 = Vector3.Dot(forward, dirRight) >= Mathf.Cos(35 * Mathf.Deg2Rad);   // 大于 20 度的 弧
            //上下cos(-45,45)

            return condition3 && Vector3.Dot(forward, dirUp) >= Mathf.Cos(35 * Mathf.Deg2Rad);    

        }

    }

}

4. 获取网络时间(用户软件的限时操作,或者需要网络时间的操作)

哭笑,还是直接代码吧.

 public class NetWorkManage : MonoBehaviour
    {
        private float timeStamp;
        
        // private string timeStamp_Str;
        private StringBuilder timeStamp_Str = new StringBuilder(25, 30);
        
        Double timerStr;
        DateTime time = DateTime.MinValue;
        DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));

        public void Awake()
        {
            StartCoroutine(GetTimeStamp());   //开始 获取网络时间
        }
        
        // 获取一次网络时间之后,让时间自己进行累加。不需要重复获取。
        private void FixedUpdate()
        {
            timerStr += Time.deltaTime * 1000;

            time = startTime.AddMilliseconds(timerStr);

            timeStamp_Str.Remove(0, timeStamp_Str.Length);
            timeStamp_Str.Append(time.ToString());
            timeStamp_Str.Append("\n");
            timeStamp_Str.Append(time.Millisecond.ToString());
            //  timeStamp_Str = time.ToString();
            timeStamp = time.Millisecond;
            
        }
   

        #region 网络时间

        /// <summary>
        /// 获取 时间的 当前 秒
        /// </summary>
        public int GetGameTimeSecond
        {
            get
            {
                return time.Second;
            }
        }
        /// <summary>
        /// 获取当前时间戳
        /// </summary>
        public float GetGameTimeStamp
        {
            get
            {
                return timeStamp;
            }
        }
        
	    /// <summary>
        /// 获取当前时间,以及当前毫秒数值
        /// </summary>
        public string GetGameTimeStampStr
        {
            get
            {
                if (timeStamp_Str != null)
                {
                    return timeStamp_Str.ToString();
                }
                else
                {
                    return null;
                }
                
            }
        }

       float waitTime = 1;
       private IEnumerator GetTimeStamp()
        {
            WWW www = new WWW("http://www.hko.gov.hk/cgi-bin/gts/time5a.pr?a=1");
            yield return www;
            try
            {
                if(!Double.TryParse(www.text.Substring(2),out timerStr))
                {
                    StartCoroutine(GetTimeStamp());
                   
                }
            }
            catch (Exception e)
            {
                PublicClass.Log("NetWork error : " + e);
            }

            yield return new WaitForSeconds(waitTime);   // 防止 时间有偏差,刚开始会快速更新。后面十秒间隔更新一次

            waitTime++;

            waitTime = Mathf.Min(waitTime,10);
            StartCoroutine(GetTimeStamp());
        }
           #endregion 网络时间
  }

5.第三人称的人物摄像头控制,中间控制远近,右键滑动控制旋转,如果检测到障碍物,自己调节相机远近距离

咱还是直接代码吧

using UnityEngine; 
using System.Collections; 

public class PlayerCamera : MonoBehaviour 
{ 
    public Transform target; 
    public  float attackTimer;

    public float targetHeight = 1.7f; 
    public float distance = 5.0f;
    public float offsetFromWall = 0.1f;

    public float maxDistance = 20; 
    public float minDistance = .6f; 

    public float xSpeed = 200.0f; 
    public float ySpeed = 200.0f; 

    public int yMinLimit = -80; 
    public int yMaxLimit = 80; 

    public int zoomRate = 40; 

    public float rotationDampening = 3.0f; 
    public float zoomDampening = 5.0f; 
    
    public LayerMask collisionLayers = -1;

    private float xDeg = -53.2f; 
    private float yDeg = 22.4f; 
    private float currentDistance; 
    private float desiredDistance; 
    private float correctedDistance;

    void Start () 
    {
		attackTimer=0.05f;
        currentDistance = distance; 
        desiredDistance = distance; 
        correctedDistance = distance; 


        if (GetComponent<Rigidbody>()) 
            GetComponent<Rigidbody>().freezeRotation = true; 
    } 
 
    void LateUpdate () 
    {
	    if(attackTimer>0)
			attackTimer-=Time.deltaTime;
		
		if(attackTimer<0)
			attackTimer=0;
		if(attackTimer==0){
		    target = GameObject.FindGameObjectWithTag("Player").transform;
		}
    	Vector3 vTargetOffset;
    	
  
        if (!target){ 
            return; 
		}
	
        if (Input.GetMouseButton(1) ) 
        { 
            xDeg += Input.GetAxis ("Mouse X") * xSpeed * 0.02f; 
            yDeg -= Input.GetAxis ("Mouse Y") * ySpeed * 0.02f; 
        } 
        
        yDeg = ClampAngle (yDeg, yMinLimit, yMaxLimit); 
        xDeg = ClampAngle (xDeg, -360, 360); 
		
        Quaternion rotation = Quaternion.Euler (yDeg, xDeg, 0); 


        desiredDistance -= Input.GetAxis ("Mouse ScrollWheel") * Time.deltaTime * zoomRate * Mathf.Abs (desiredDistance); 
        desiredDistance = Mathf.Clamp (desiredDistance, minDistance, maxDistance); 
        correctedDistance = desiredDistance; 
		

        vTargetOffset = new Vector3 (0, -targetHeight, 0);
        Vector3 position = target.position - (rotation * Vector3.forward * desiredDistance + vTargetOffset); 

        RaycastHit collisionHit; 
        Vector3 trueTargetPosition = new Vector3 (target.position.x, target.position.y + targetHeight, target.position.z); 

      
        bool isCorrected = false; 
        if (Physics.Linecast (trueTargetPosition, position, out collisionHit, collisionLayers.value)) 
        { 
            correctedDistance = Vector3.Distance (trueTargetPosition, collisionHit.point) - offsetFromWall; 
            isCorrected = true;
        }

        currentDistance = !isCorrected || correctedDistance > currentDistance ? Mathf.Lerp (currentDistance, correctedDistance, Time.deltaTime * zoomDampening) : correctedDistance; 
        currentDistance = Mathf.Clamp (currentDistance, minDistance, maxDistance); 

        position = target.position - (rotation * Vector3.forward * currentDistance + vTargetOffset); 
        
        transform.rotation = rotation; 
        transform.position = position; 
	
    } 

    private static float ClampAngle (float angle, float min, float max) 
    { 
        if (angle < -360) 
            angle += 360; 
        if (angle > 360) 
            angle -= 360; 
        return Mathf.Clamp (angle, min, max); 
    } 
} 





6. 在游戏运行的时候,往往需要 在 正交Orthographic (无消失点投影)透视Perspective (有消失点投影) 两个视角中来回转化。 以达到 不同的 2D 与 3D 视角。

So! 今天的 实例代码就是 描述了 这一个功能:


using UnityEngine;

public enum Views
{
    _2DView = 1,
    _3DView
}
 public class BackupCameraProjectionChange
 {
    /// <summary>
    /// 相机透视改变是否触发(调用只需把此值改为true)
    /// </summary>
    public bool ChangeProjection = false;
    private bool _changing = false;
    private float ProjectionChangeTime = 0.5f;
    private float _currentT = 0.0f;
    private Camera m_Camera;

protected Views cur_Views = Views._3DView;

public Views GetCurViews
{
    get
    {
        return cur_Views;
    }
}

public BackupCameraProjectionChange(Camera camera = null , float speed = 0.5f)
{
    ProjectionChangeTime = speed;
    if (m_Camera == null && camera == null)
    {
        m_Camera = Camera.main;
    }
    else
    {
        m_Camera = camera;
    }
}

///这个 Update 需要在 其他继承自 MonoBehaviour 类的 Update 中 调用
public void Update()
{
    if (m_Camera == null)
        return;

    if (_changing)
    {
        ChangeProjection = false;
    }
    else if (ChangeProjection)
    {
        _changing = true;
        _currentT = 0.0f;
    }

    LateUpdate();
}


void LateUpdate()
{
    if (!_changing)
    {
        return;
    }
    //将当前的 是否正视图值 赋值给currentlyOrthographic变量
    bool currentlyOrthographic = m_Camera.orthographic;

    //定义变量存放当前摄像机的透视和正视矩阵信息;
    Matrix4x4 orthoMat, persMat;
    if (currentlyOrthographic)//如果当前摄像机为正视状态,则切换为3D
    {
        orthoMat = m_Camera.projectionMatrix; //保留 2D矩阵信息

        m_Camera.orthographic = false;  //为 3D
        m_Camera.ResetProjectionMatrix();
        persMat = m_Camera.projectionMatrix;   //保留 3D 矩阵信息

        cur_Views = Views._3DView;
    }
    else//否则当前摄像机为透视状态, 则切换为2D
    {
        persMat = m_Camera.projectionMatrix; //保留 3D 矩阵信息

        m_Camera.orthographic = true;    //为2D
        m_Camera.ResetProjectionMatrix();
        orthoMat = m_Camera.projectionMatrix;  //保留 2D矩阵信息

        cur_Views = Views._2DView;
    }
    m_Camera.orthographic = currentlyOrthographic;


    _currentT += (Time.deltaTime / ProjectionChangeTime);
    if (_currentT < 1.0f)
    {
        if (currentlyOrthographic)
        {
            m_Camera.projectionMatrix = MatrixLerp(orthoMat, persMat, _currentT * _currentT);  //从2D 到 3D
        }
        else
        {
            m_Camera.projectionMatrix = MatrixLerp(persMat, orthoMat, Mathf.Sqrt(_currentT)); //从3D 到 2D
        }
    }
    else
    {
        _changing = false;
        m_Camera.orthographic = !currentlyOrthographic;  //取反
        m_Camera.ResetProjectionMatrix();   // 重置
    }
}



private Matrix4x4 MatrixLerp(Matrix4x4 from, Matrix4x4 to, float t)
{
    t = Mathf.Clamp(t, 0.0f, 1.0f);
    Matrix4x4 newMatrix = new Matrix4x4();
    newMatrix.SetRow(0, Vector4.Lerp(from.GetRow(0), to.GetRow(0), t));
    newMatrix.SetRow(1, Vector4.Lerp(from.GetRow(1), to.GetRow(1), t));
    newMatrix.SetRow(2, Vector4.Lerp(from.GetRow(2), to.GetRow(2), t));
    newMatrix.SetRow(3, Vector4.Lerp(from.GetRow(3), to.GetRow(3), t));
    return newMatrix;
}
}

7. 曲线算法。使用此算法把向量之间曲线话。使用与 AI 的 位移,导弹的曲线轨迹,等等一些曲线路径。

So! 今天的 实例代码就是 描述了 这一个功能:

更多曲线算法:插值与样条

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

public class CatmullRom : MonoBehaviour {

    public List<Transform> Ponits;
    public Transform car;
    public float v=2;
    public float t;
    float dt;

    float timer = 0;

    float GetThreeBSplineValue(float p0, float p1, float p2, float p3, float t)
    {
        float A0 = (p0 + 4 * p1 + p2) / 6;
        float A1 = -(p0 - p2) / 2;
        float A2 = (p0 - 2 * p1 + p2) / 2;
        float A3 = -(p0 - 3 * p1 + 3 * p2 - p3) / 6;

        return A0 + A1 * t + A2 * t * t + A3 * t * t * t;
    }

    Vector3 GetThreeBSplineValue(Vector3 P0, Vector3 P1, Vector3 P2, Vector3 P3, float t)
    {
        Vector3 dot;
        dot.x = GetThreeBSplineValue(P0.x, P1.x, P2.x, P3.x, t);
        dot.y = GetThreeBSplineValue(P0.y, P1.y, P2.y, P3.y, t);
        dot.z = GetThreeBSplineValue(P0.z, P1.z, P2.z, P3.z, t);
        return dot;
    }
    

    private void Awake()
    {
        Ponits.Reverse();
    }


    public static Vector3 Caculate(Vector3 P0, Vector3 P1, Vector3 P2, Vector3 P3, float t)
    {
        float factor = 0.5f;
        Vector3 c0 = P1;
        Vector3 c1 = (P2 - P0) * factor;
        Vector3 c2 = (P2 - P1) * 3.0f - (P3 - P1) * factor - (P2 - P0) * 2.0f * factor;
        Vector3 c3 = (P2 - P1) * -2.0f + (P3 - P1) * factor + (P2 - P0) * factor;

        Vector3 curvePoint = c3 * t * t * t + c2 * t * t + c1 * t + c0;

        return curvePoint;
    }


    public void OnGUI()
    {
        if(GUILayout.Button("Run"))
        {
            float dis = Vector3.Distance(transform.position, Ponits[0].position);
            for(int i = 0;i < Ponits.Count - 1; i++)
            {
                dis += Vector3.Distance(Ponits[i].position , Ponits[i+1].position);
            }

            t = dis / v;
            m_Pair = Ponits.Count / 4;
            t = t / m_Pair;
            dt = Time.time;
            m_index = 0;
            m_timer = 0;
        }
    }


    private void OnDrawGizmos()
    {
       Gizmos.color = Color.yellow;
       float step = 100;
       if (Ponits.Count < 4) return;

       Vector3 pp1 = Ponits[0].position;
       for (int j = 0; j < Ponits.Count - 3; j+=1)
       {
            for (int i = 1; i < step; i++)
            {
                Vector3 pp2 = GetThreeBSplineValue(Ponits[j].position, Ponits[j + 1].position, Ponits[j + 2].position, Ponits[j + 3].position, i / step);
                Gizmos.DrawLine(pp1, pp2);
                pp1 = pp2;
            }
        }
        
    }

    float m_timer = 0;
    int m_index = -2;
    int m_Pair = 0;
	// Update is called once per frame
	void Update () {

        if (Vector3.Distance(car.position, Ponits[Ponits.Count-1].position) > 1)
        {
            if (dt < Time.time && t > 0)
            {
                m_timer += Time.deltaTime;

                if (m_index < Ponits.Count - 3)
                {
                    Vector3 head1;
                    Vector3 head2;
                    Vector3 head3;
                    Vector3 head4;

                    float tt = (Time.time - dt) / t;
                    if (m_index == -2)
                    {
                        head1 = transform.position;
                        head2 = transform.position;
                        head3 = Ponits[0].position;
                        head4 = Ponits[1].position;

                        tt = (Time.time - dt) / t;
                    }
                    else if (m_index == -1)
                    {
                        head1 = transform.position;
                        head2 = Ponits[0].position;
                        head3 = Ponits[1].position;
                        head4 = Ponits[2].position;
                    }
                    else
                    {
                        head1 = Ponits[m_index].position;
                        head2 = Ponits[m_index+1].position;
                        head3 = Ponits[m_index+2].position;
                        head4 = Ponits[m_index+3].position;
                    }

                    car.position = GetThreeBSplineValue(head1, head2, head3, head4, tt);
                }

                if(m_timer >= t)
                {
                    if(m_index < Ponits.Count - 3)
                    {
                        Debug.Log(" : " + m_index + " : " + t);
                        m_index += 1;
                    }
                    dt = Time.time;
                    m_timer = 0;
                }
            }
        }

    }
}

猜你喜欢

转载自blog.csdn.net/KiTok/article/details/77036389