{"id":1967,"date":"2018-10-31T15:34:55","date_gmt":"2018-10-31T07:34:55","guid":{"rendered":"http:\/\/www.clonefactor.com\/wordpress\/?p=1967"},"modified":"2018-10-31T16:22:19","modified_gmt":"2018-10-31T08:22:19","slug":"fake-physic-obstacle-local-avoidance","status":"publish","type":"post","link":"https:\/\/www.clonefactor.com\/wordpress\/program\/unity3d\/1967\/","title":{"rendered":"Fake physic &#8211; obstacle local avoidance"},"content":{"rendered":"<p><iframe loading=\"lazy\" title=\"Character Control #log17 fake collision &amp; local avoidance\" width=\"1260\" height=\"709\" src=\"https:\/\/www.youtube.com\/embed\/n7KjIqC5NA4?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen><\/iframe><\/p>\n<p>\u79fb\u52d5\u7cfb\u7d71\u66f4\u65b0\u70ba\u5047\u7269\u7406\u5f8c, \u884d\u751f\u51fa\u5f88\u591a\u9055\u53cd\u7269\u7406\u7684Bug, \u4f8b\u5982<\/p>\n<p>&#8220;\u53ef\u4ee5\u7121\u8996\u7246\u58c1\u76f4\u63a5\u8dd1\u9032\u7246&#8221;.<\/p>\n<p>\u800c\u4e14 Unity3D \u7684 Raycast, \u5982\u679c\u5728\u539f\u59cb\u9ede\u7684\u6642\u5019\u5c31\u5df2\u7d93\u548c\u7269\u4ef6\u91cd\u758a, \u8a72\u6b21\u7684 Raycast result \u4e2d\u5c07\u6703\u88ab\u5ffd\u7565<br \/>\n(\u5df2\u78ba\u8a8d\u662f\u5b98\u65b9\u7684\u8a2d\u8a08,\u4e26\u4e0d\u6703\u6539)<\/p>\n<p>\u6240\u4ee5\u5982\u4f55\u89e3\u6c7a\u6389\u5728\u7269\u4ef6\u524d\u9762\u5617\u8a66\u9032\u884c\u79fb\u52d5\u7684\u8def\u7dda\u4fee\u6b63\u65b9\u5f0f, \u5c31\u6210\u4e86\u6700\u8feb\u5207\u7684\u8ab2\u984c.<\/p>\n<p>\u70ba\u4e86\u89e3\u6c7a\u9019\u554f\u984c, \u6211\u7684\u65b9\u6848\u662f\u6e1b\u5c0f capsule \u7684 radius ,\u9810\u7559\u4e00\u500b padding \u503c, \u7136\u5f8c\u5728\u7522\u751f collision \u7d50\u679c\u5f8c\u91cd\u65b0\u52a0\u56de\u53bb.<br \/>\n\u907f\u514d\u5728\u4e0b\u4e00\u6b21\u7269\u7406\u66f4\u65b0\u6642\u767c\u751f\u4e0a\u9762 overlap \u7684\u554f\u984c.<\/p>\n<p>\u66f4\u65b0\u9032\u884c\u4e0a\u9650\u7684 2 \u6b21 Raycast \u7684\u7d44\u5408<\/p>\n<ul>\n<li>\u524d\u65b9\u5411\u6aa2\u67e5\n<ul>\n<li>\u524d\u65b9\u4e0d\u901a\u884c, \u4ee5\u7246\u8eabnormal\u70ba\u6e96<br \/>\n\u53d6\u6a23\u7246\u9762\u7684\u5de6\/\u53f3, \u4e26\u9032\u884c\u78b0\u649e\u6aa2\u67e5<\/li>\n<\/ul>\n<\/li>\n<li>\u5982\u4ee5\u4e0a\u7684\u4fee\u6539\u6c92\u6709\u9032\u884c, \u6aa2\u67e5\u8a72\u5340\u57df\u6709\u5426\u91cd\u758a<\/li>\n<\/ul>\n<p>\u9ec3\u7dda\u70ba\u524d\u65b9\u969c\u7919(\u5927\u6982\u53ea\u80fd\u5728\u7b2c\u4e00\u6b21\u63a5\u89f8\u6642\u770b\u5230)<br \/>\n\u7d05\u7dda\u662f\u907f\u969cAI\u7684\u898f\u907f\u8def\u7dda.<\/p>\n<p>implement the feature for my fake physic reaction dealing with obstacle ahead. &#8211; use raycast + overlap &#8211; since raycast cannot detect the collision object at very begin sometime the avatar will not locate the wall in front of it. solution :<\/p>\n<p>process 2 times raycast checking in fixed update.<\/p>\n<ul>\n<li>check obstacle based on movement ahead\n<ol>\n<li>if blocking, try to project the current direction on wall, and try to override the movement is it&#8217;s possible.<\/li>\n<\/ol>\n<\/li>\n<li>if ALL checking above aren&#8217;t doing anything, check if we WILL overlap any obstacle on target position, when that happen override position.<\/li>\n<\/ul>\n<p><strong>Debug vision :<\/strong><\/p>\n<p>the yellow line is the origin direction input by player.(only saw on first hit)<\/p>\n<p><span style=\"font-size: 1.4rem;\">the red line is the slip away path then choose by the avoid obstacle logic.d<\/span><\/p>\n<p>&nbsp;<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">\/\/\/ &lt;summary&gt;Vector3 = position, w = weight of this position.&lt;\/summary&gt;\r\nprivate Vector3 AvoidObstacleOnPath(Vector3 referencePos)\r\n{\r\n\tVector3 currentPos = m_Avatar.self.transform.position;\r\n\tif (referencePos == currentPos)\r\n\t\treturn currentPos;\r\n\r\n\tDebug.Assert(mode == eMode.Master, \"Mode - \" + mode.ToString(\"F\") + \"AvoidObstacleOnPath() should only called in \" + eMode.Master.ToString(\"F\") + \" mode.\");\r\n\tVector3 targetPos = referencePos;\r\n\tfloat delta = Time.fixedDeltaTime;\r\n\tVector3 direction = referencePos - currentPos;\r\n\tfloat distance = Mathf.Max(float.Epsilon, direction.magnitude);\r\n\tdirection.Normalize();\r\n#if RADIUS_PADDING\r\n\tconst float s_RadiusPadding = 0.01f;\r\n\tfloat radius = m_Avatar.self.capsule.radius - s_RadiusPadding;\r\n#else\r\n\tfloat radius = m_Avatar.self.capsule.radius;\r\n#endif\r\n\tbool posOverrideFlag = false;\r\n\r\n\t\/\/ Physics 1\r\n\t\/\/ Block detection along the path.\r\n\tVector3[] p = CapsuleCastData.GetCapsuleColliderPoints(m_Avatar.self.capsule, currentPos);\r\n\tif (m_Raycast.CapsuleCast(p[0], p[1], radius, direction, distance, m_AvoidCollision, m_QueryTriggerInteraction))\r\n\t{\r\n\t\tposOverrideFlag = true;\r\n\t\t\/\/ by default, we use that point to replace the current result.\r\n\r\n#if RADIUS_PADDING\r\n\t\t\/\/ Hotfix : always have padding for next raycast,\r\n\t\t\/\/ Bug : Raycast overlap at begin will be ignore, result in NO BLOCKING!\r\n\t\ttargetPos = currentPos + direction * (m_Raycast.hitResult.distance - s_RadiusPadding);\r\n#else\r\n\t\tfixedPos = currentPos + direction * m_Raycast.hitResult.distance;\r\n#endif\r\n\t\tif (m_Debug)\r\n\t\t\tDebug.DrawLine(currentPos, targetPos, Color.yellow, 3f);\r\n\r\n\t\t\/\/ Physics 2\r\n\t\t\/\/ Blocking detected, see if we can slip away based on current direction.\r\n\t\tVector3 hitNormal = m_Raycast.hitResult.normal.normalized;\r\n\t\tfloat dot = Vector3.Dot(direction, -hitNormal);\r\n\t\tif (dot &gt;= 0f)\r\n\t\t{\r\n\t\t\t\/\/ try slip away, instead of stuck there.\r\n\t\t\tVector3 projectNormal = Vector3.ProjectOnPlane(direction, hitNormal).normalized;\r\n\t\t\tfloat halfDistance = Mathf.Max(s_MinSlipDistance, distance * 0.5f);\r\n\t\t\t\t\t\r\n\t\t\t\/\/\/ detect obstacle, based on hit result &lt;see cref=\"targetPos\"\/&gt; is point stick on obstacle.\r\n\t\t\tp = CapsuleCastData.GetCapsuleColliderPoints(m_Avatar.self.capsule, targetPos);\r\n\t\t\tif (!m_Raycast.CapsuleCast(p[0], p[1], radius, projectNormal, halfDistance))\r\n\t\t\t{\r\n\t\t\t\t\/\/ path clear.\r\n\t\t\t\ttargetPos = targetPos + projectNormal * halfDistance;\r\n\t\t\t\tif (m_Debug)\r\n\t\t\t\t\tDebug.DrawLine(currentPos, targetPos, Color.red, 3f);\r\n\t\t\t}\r\n\t\t}\r\n\t\t\/\/ else stuck in P1 result.\r\n\t}\r\n\r\n\t\/\/ Physic 3\r\n\t\/\/ final fail safe, if above checking not run at all (Raycast + overlap at begin)\r\n\tif (!posOverrideFlag)\r\n\t{\r\n\t\tCollider obj = IsOverlap(targetPos);\r\n\t\tif (obj != null)\r\n\t\t{\r\n\t\t\tif (m_Debug)\r\n\t\t\t\tDebug.LogWarning(\"AvoidObstacleOnPath fail, we overlap \\'\" + obj.name + \"\\' ahead, clamp mover position.\", this);\r\n\t\t\treturn Vector3.Lerp(m_AvatarBody.position, m_Rigidbody.position, 0.5f);\r\n\t\t}\r\n\t}\r\n\treturn targetPos;\r\n}\r\n\r\nprivate Collider IsOverlap(Vector3 targetPos)\r\n{\r\n\tCollider[] objs = new Collider[5];\r\n\tVector3[] tp = CapsuleCastData.GetCapsuleColliderPoints(m_Avatar.self.capsule, targetPos);\r\n\tfloat smallerRadius = Mathf.Max(float.Epsilon, m_Avatar.self.capsule.radius - 0.001f);\r\n\tint overlapCnt = m_Capsule.OverlapNonAlloc(tp[0], tp[1], smallerRadius, ref objs, m_AvoidCollision, m_QueryTriggerInteraction);\r\n\r\n\t\/\/ skip all avatar's collider. but report any others.\r\n\tfor (int i = 0; i &lt; overlapCnt; i++)\r\n\t{\r\n\t\tif (!objs[i].transform.IsChildOf(m_AvatarBody.transform))\r\n\t\t\treturn objs[i]; \r\n\t}\r\n\treturn null;\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u79fb\u52d5\u7cfb\u7d71\u66f4\u65b0\u70ba\u5047\u7269\u7406\u5f8c, \u884d\u751f\u51fa\u5f88\u591a\u9055\u53cd\u7269\u7406\u7684Bug, \u4f8b\u5982 &#8220;\u53ef\u4ee5\u7121\u8996\u7246\u58c1\u76f4\u63a5\u8dd1\u9032\u7246&#038; &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11],"tags":[61,43],"class_list":["post-1967","post","type-post","status-publish","format-standard","hentry","category-unity3d","tag-ai","tag-unity3d-2"],"_links":{"self":[{"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/posts\/1967","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=1967"}],"version-history":[{"count":0,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/posts\/1967\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/media?parent=1967"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/categories?post=1967"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/tags?post=1967"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}