{"id":2467,"date":"2021-05-25T18:00:40","date_gmt":"2021-05-25T10:00:40","guid":{"rendered":"https:\/\/www.clonefactor.com\/wordpress\/?p=2467"},"modified":"2021-05-25T18:24:09","modified_gmt":"2021-05-25T10:24:09","slug":"1000-navmeshagent-test","status":"publish","type":"post","link":"https:\/\/www.clonefactor.com\/wordpress\/program\/unity3d\/2467\/","title":{"rendered":"1000 NavMeshAgent test"},"content":{"rendered":"\n<p>\u9084\u539f\u57fa\u672c\u6b65\u91cd\u65b0\u5beb\u4e00\u5beb NavMeshAgent.<br data-rich-text-line-break=\"true\" \/>\u57281000 agent, 0gc \u9084\u514d\u5f37\u9054\u5230 60fps \u6211\u89ba\u5f97\u5df2\u7d93\u8db3\u5920\u7684\u4e86.<\/p>\n<ul>\n<li>Pathfinding AI \u5728 Player \u67d0\u500b\u7279\u5b9a\u534a\u5f91\u4e0a\u505c\u4e0b.<\/li>\n<li>\u5728 Player \u4e0d\u79fb\u52d5\u6216\u6539\u8b8a\u5f88\u5c11\u7684\u6642\u5019\u4e0d\u66f4\u65b0<\/li>\n<li>\u5728 Agent \u4e0d\u8d85\u904e\u67d0\u7279\u5b9a\u534a\u5f91\u7bc4\u570d\u4e0b\u4e0d\u66f4\u65b0<\/li>\n<li>\u8a02\u7acb\u6700\u5c11\u66f4\u65b0\u6642\u9593\u9593\u9694<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"457\" src=\"https:\/\/www.clonefactor.com\/wordpress\/wp-content\/uploads\/2021\/05\/NavMeshAgent1000test-1024x457.jpg\" alt=\"\" class=\"wp-image-2476\" srcset=\"https:\/\/www.clonefactor.com\/wordpress\/wp-content\/uploads\/2021\/05\/NavMeshAgent1000test-1024x457.jpg 1024w, https:\/\/www.clonefactor.com\/wordpress\/wp-content\/uploads\/2021\/05\/NavMeshAgent1000test-300x134.jpg 300w, https:\/\/www.clonefactor.com\/wordpress\/wp-content\/uploads\/2021\/05\/NavMeshAgent1000test-768x342.jpg 768w, https:\/\/www.clonefactor.com\/wordpress\/wp-content\/uploads\/2021\/05\/NavMeshAgent1000test-1536x685.jpg 1536w, https:\/\/www.clonefactor.com\/wordpress\/wp-content\/uploads\/2021\/05\/NavMeshAgent1000test-359x160.jpg 359w, https:\/\/www.clonefactor.com\/wordpress\/wp-content\/uploads\/2021\/05\/NavMeshAgent1000test.jpg 1920w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>The agent&#8217;s decision debug view<\/figcaption><\/figure>\n\n\n\n<ul class=\"wp-block-list\"><li>\u9ec3\u7dda &#8211; Agent \u5e0c\u671b\u5230\u9054\u7684\u76ee\u7684\u5730<\/li><li>\u9ec3\u5708 \/ \u7d05\u5708 &#8211; Agent \u5177\u9ad4\u505c\u7559\u7684\u8ddd\u96e2, \u4e0d\u8d85\u904e\u4e0d\u9032\u884c\u66f4\u65b0<\/li><li>\u7070\u5708 &#8211; Target \u4e0a\u6b21\u7d00\u9304\u7684\u5730\u9ede\u534a\u5f91, \u4e0d\u8d85\u904e\u4e0d\u66f4\u65b0.<\/li><\/ul>\n\n\n\n<p><br>\u904b\u884c\u7d50\u679c\u5982\u4e0b<\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"Unity3D, 1000 NavMeshAgent performance test\" width=\"1260\" height=\"709\" src=\"https:\/\/www.youtube.com\/embed\/-V7QS3FH8SE?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">using System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\nusing UnityEngine.AI;\n\nnamespace Kit.Avatar\n{\n    [RequireComponent(typeof(NavMeshAgent))]\n    public class NPCOrbitMovement : MonoBehaviour\n    {\n        #region Variables\n        [SerializeField]\n        private NavMeshAgent m_NavMeshAgent = null;\n        [SerializeField] bool m_DebugGizmos = false;\n\n        [Help(\"The min period for AI's to remake decision.\"), SerializeField]\n        private float m_ThinkInterval = 0.02f;\n        private float m_LastThinkTime = 0f;\n\n        [Tooltip(\"The target we tracking.\"), SerializeField]\n        private Transform m_Target = null;\n        [SerializeField]\n        private Constraints m_Constraints = new Constraints();\n        [System.Serializable]\n        private class Constraints\n        {\n            [Tooltip(\"The agent will attempt to keep the distance between target, not too far, not too close.\")]\n            public float maintainDistance = 2f;\n            [Tooltip(\"when target position changed,\" +\n                \"\\nif position remain inside this radius,\" +\n                \"\\nwill concider as no change at all.\")]\n            public float agentPosDeviation = 0.25f;\n            public Color minRangeColor = Color.red;\n            public Color maxRangeColor = Color.yellow;\n\n            public float targetPosDeviation = 0.25f;\n            public Color deviationColor = Color.gray;\n            public void Validate()\n            {\n                maintainDistance = Mathf.Max(float.Epsilon, maintainDistance);\n                agentPosDeviation = Mathf.Clamp(agentPosDeviation, float.Epsilon, maintainDistance * 2f);\n            }\n        }\n        #endregion Variables\n\n        #region System\n        private void Reset()\n        {\n            m_NavMeshAgent = GetComponent&lt;NavMeshAgent>();\n        }\n        private void OnValidate()\n        {\n            m_Constraints.Validate();\n            if (!Application.isPlaying)\n                Think(m_Target, true);\n        }\n        private void Awake()\n        {\n            if (m_NavMeshAgent == null)\n                m_NavMeshAgent = GetComponent&lt;NavMeshAgent>();\n            m_Decision = new Decision(OnDestinationChanged);\n        }\n\n        private void OnDrawGizmos()\n        {\n            if (!m_DebugGizmos)\n                return;\n            if (!Application.isPlaying)\n                Think(m_Target);\n            m_Decision.DrawGizmos(m_Constraints);\n        }\n\n        private void FixedUpdate()\n        {\n            if (Time.timeSinceLevelLoad - m_LastThinkTime > m_ThinkInterval)\n            {\n                Think(m_Target);\n            }\n        }\n        #endregion System\n\n        #region Decision making\n        private void Think(Transform _target, bool forceUpdate = false)\n        {\n            m_LastThinkTime = Time.timeSinceLevelLoad;\n            m_Decision.MakeDecision(transform, _target, m_Constraints, forceUpdate);\n        }\n\n        private void OnDestinationChanged(Vector3 destination)\n        {\n            m_NavMeshAgent.SetDestination(destination);\n        }\n\n        private Decision m_Decision = new Decision(null);\n        private class Decision\n        {\n            public Transform target;\n            public Vector3 targetPos, myPos, myFacing, groundNormal, xzVector, xzDir, destination;\n            public float minDistance, maxDistance, deviationDistance, targetPosDeviation;\n            public delegate void DestinationChanged(Vector3 pos);\n            public DestinationChanged Event_DestinationChanged;\n\n            public Decision(DestinationChanged destinationChanged)\n            {\n                Event_DestinationChanged = destinationChanged;\n            }\n            public void MakeDecision(Transform _my, Transform _target, Constraints _constraints, bool forceUpdate)\n            {\n                if (!forceUpdate)\n                {\n                    if (_target == target &amp;&amp;\n                        IsTargetNotMoving(_target.position, _constraints.targetPosDeviation))\n                    {\n                        return;\n                    }\n                }\n                if (_target == null)\n                {\n                    Clear();\n                    return;\n                }\n\n                \/\/ define new destination\n                target = _target;\n                groundNormal = Vector3.up;\n                myPos = _my.position;\n                myFacing = Vector3.ProjectOnPlane(_my.forward, groundNormal);\n                targetPos = target.position;\n                targetPosDeviation = _constraints.targetPosDeviation;\n\n                \/\/ flatten local Y-axis\n                xzVector = Vector3.ProjectOnPlane(myPos - targetPos, groundNormal);\n                xzDir = xzVector == Vector3.zero ?\n                    -myFacing : \/\/ bias when it's not difference, we step back.\n                    xzVector.normalized;\n                float halfDeviation = _constraints.agentPosDeviation * 0.5f;\n                minDistance = _constraints.maintainDistance - halfDeviation;\n                maxDistance = _constraints.maintainDistance + halfDeviation;\n                deviationDistance = _constraints.maintainDistance - xzVector.magnitude;\n                destination = myPos + xzDir * deviationDistance;\n                Event_DestinationChanged?.Invoke(destination);\n            }\n\n            private bool IsTargetNotMoving(Vector3 newPos, float posDeviation)\n            {\n                float diffSqr = (targetPos - newPos).sqrMagnitude;\n                float acceptDevSqr = posDeviation * posDeviation;\n                return diffSqr &lt; acceptDevSqr; \/\/ within acceptable deviation\n            }\n\n            public void Clear()\n            {\n                target = null;\n                targetPos = myPos = myFacing = groundNormal = xzVector = xzDir = destination = default;\n                minDistance = maxDistance = deviationDistance = targetPosDeviation = 0f;\n            }\n\n            public void DrawGizmos(Constraints c)\n            {\n                if (target == null)\n                    return;\n                \/**** This code will not work without gizmos lib.\n                GizmosExtend.DrawCircle(targetPos, groundNormal, c.deviationColor, targetPosDeviation);\n                GizmosExtend.DrawCircle(targetPos, groundNormal, c.minRangeColor, minDistance);\n                GizmosExtend.DrawCircle(targetPos, groundNormal, c.maxRangeColor, maxDistance);\n                Color deviationColor = deviationDistance > 0 ? c.minRangeColor : c.maxRangeColor;\n                GizmosExtend.DrawLine(myPos, destination, deviationColor);\n                GizmosExtend.DrawPoint(destination, deviationColor, 0.3f);\n                \/\/****\/\n            }\n\n        }\n        #endregion Decision making\n    }\n}<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>\u9084\u539f\u57fa\u672c\u6b65\u91cd\u65b0\u5beb\u4e00\u5beb NavMeshAgent.\u57281000 agent, 0gc \u9084\u514d\u5f37\u9054\u5230 60f &hellip;<\/p>\n","protected":false},"author":1,"featured_media":2476,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11],"tags":[],"class_list":["post-2467","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-unity3d"],"_links":{"self":[{"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/posts\/2467","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/comments?post=2467"}],"version-history":[{"count":8,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/posts\/2467\/revisions"}],"predecessor-version":[{"id":2480,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/posts\/2467\/revisions\/2480"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/media\/2476"}],"wp:attachment":[{"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/media?parent=2467"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/categories?post=2467"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/tags?post=2467"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}