[{"data":1,"prerenderedAt":173},["ShallowReactive",2],{"\u002F2020-05-24-ue4-position-deviation-notes":3},{"id":4,"title":5,"body":6,"date":149,"description":12,"extension":150,"meta":151,"navigation":154,"path":166,"seo":167,"stem":168,"tags":169,"__hash__":172},"blogs\u002F_legacy\u002F2020\u002F2020-05-24-ue4-position-deviation-notes.md","UE4物理位置偏差小记两则",{"type":7,"value":8,"toc":138},"minimark",[9,13,16,19,23,26,29,32,43,46,52,55,58,61,64,67,72,75,78,81,84,87,91,94,105,117,120,123,126,129,132,135],[10,11,12],"p",{},"UE4的运动模拟在使用过程中有时会出现位置不在预期的情况。",[10,14,15],{},"虽然并不是会造成很大的问题，但是在需要使用精确位置的时候就会出现一些偏差。",[10,17,18],{},"当前UE4版本为4.25.0。",[20,21,22],"h2",{"id":22},"抛体运动",[10,24,25],{},"在不使用物理模拟移动物体时，包括SetActorLocation的sweep，都会使用到UPrimitiveComponent::MoveComponentImpl进行移动。sweep的好处是移动的结果会保证不会有穿插。",[10,27,28],{},"但是这种运动方式在快速移动时会有一点小问题，就是它在作碰撞回避之后的位置并不是完全贴紧目标碰撞体表面的。",[10,30,31],{},"主要原因是在发生碰撞后的PullBackHit中：",[33,34,39],"pre",{"className":35,"code":37,"language":38},[36],"language-text","static void PullBackHit(FHitResult& Hit, const FVector& Start, const FVector& End, const float Dist)\n{\n    const float DesiredTimeBack = FMath::Clamp(0.1f, 0.1f\u002FDist, 1.f\u002FDist) + 0.001f;\n    Hit.Time = FMath::Clamp( Hit.Time - DesiredTimeBack, 0.f, 1.f );\n}\n","text",[40,41,37],"code",{"__ignoreMap":42},"",[10,44,45],{},"之后执行位置回退时，会使用这个计算的Time",[33,47,50],{"className":48,"code":49,"language":38},[36],"NewLocation = TraceStart + (BlockingHit.Time * (TraceEnd - TraceStart));\n",[40,51,49],{"__ignoreMap":42},[10,53,54],{},"也就是说，如果你是在进行子弹模拟的话，这个回避之后的位置有可能离碰撞表面会比较远。",[10,56,57],{},"这个时候使用返回的HitResult中的ImpactPoint或者Location反而更加接近一些。",[20,59,60],{"id":60},"骨骼位置",[10,62,63],{},"对于依赖于动画更新的骨骼而言，其物理体的位置与其渲染位置是会存在一个微小的偏差的。如果项目中使用了一些特定的优化的话，这个偏差可能会被放大。在使用的时候需要注意。",[10,65,66],{},"取到的位置不在预期点主要由以下两个函数引发：",[68,69,71],"h3",{"id":70},"updatekinematicbonestoanim","UpdateKinematicBonesToAnim",[10,73,74],{},"当我们调用USkinnedMeshComponent::GetBoneTransform的时候，其实并不是去取物理世界中取骨骼的位置。我们取到的结果决定于两个值，一个是当前的ComponentToWorld，另一个是缓存的GetComponentSpaceTransforms。",[10,76,77],{},"而GetComponentSpaceTransforms本身是一个维护的双缓冲系统，其更新依赖于USkinnedMeshComponent::FlipEditableSpaceBases。",[10,79,80],{},"也就是说，在实际更新过程中，这两者有一个过期就会导致出现偏差。",[10,82,83],{},"实际去更新物理位置的逻辑在USkeletalMeshComponent::UpdateKinematicBonesToAnim中能够找到。",[10,85,86],{},"作一个简单的测试，在USkeletalMeshComponent::UpdateKinematicBonesToAnim中直接return。然后在运行时使用pvd连接引擎，你会发现移动角色之后，骨骼在物理世界的位置并没有发生移动。此时渲染的位置却已经移动了。",[68,88,90],{"id":89},"setkinematictarget","setKinematicTarget",[10,92,93],{},"在UpdateKinematicBonesToAnim中还有另一个可能会影响到实际使用的更新，就是setKinematicTarget，如果在移动骨骼的时候没有设置Teleport的话，就会使用这个函数进行物理设置。",[10,95,96,97,104],{},"按照[",[98,99,103],"a",{"href":100,"rel":101},"https:\u002F\u002Fdocs.nvidia.com\u002Fgameworks\u002Fcontent\u002Fgameworkslibrary\u002Fphysx\u002Fapireference\u002Ffiles\u002FclassPxRigidDynamic.html#4464d188e7a1e94582c9cf35da9bbc93",[102],"nofollow","Physx的文档","]描述，这里面其实有个类似缓存的机制",[106,107,108,111,114],"blockquote",{},[10,109,110],{},"The move command will result in a velocity that will move the body into the desired pose. After the move is carried out during a single time step, the velocity is returned to zero. Thus, you must continuously call this in every time step for kinematic actors so that they move along a path.",[10,112,113],{},"This function simply stores the move destination until the next simulation step is processed, so consecutive calls will simply overwrite the stored target variable.",[10,115,116],{},"The motion is always fully carried out.",[10,118,119],{},"也就是说，“真实”的物理位置会在下一次模拟之后才会更新。",[68,121,122],{"id":122},"安全的获取方式",[10,124,125],{},"要“正确”的取到想要的位置，只要使用bodyinstance的GetUnrealWorldTransform就可以了。其中第二个参数bForceGlobalPose就是区分是否取setKineMaticTarget的位置。",[10,127,128],{},"如果是要渲染位置，还是用GetBoneTransform就可以。",[20,130,131],{"id":131},"总结",[10,133,134],{},"由于引擎其实是按帧模拟的，更多的时候，位置偏差其实发生在更新次序问题上。",[10,136,137],{},"所以除非在特殊情况下，还是不要在意比较好……",{"title":42,"searchDepth":139,"depth":140,"links":141},2,3,[142,143,148],{"id":22,"depth":139,"text":22},{"id":60,"depth":139,"text":60,"children":144},[145,146,147],{"id":70,"depth":140,"text":71},{"id":89,"depth":140,"text":90},{"id":122,"depth":140,"text":122},{"id":131,"depth":139,"text":131},"2020-05-24","md",{"layout":152,"status":153,"published":154,"author":155,"author_login":157,"author_email":158,"wordpress_id":159,"wordpress_url":160,"date_gmt":161,"excerpt":162},"post","publish",true,{"display_name":156,"login":157,"email":158,"url":42},"风铃","flinkor","flinkor@foxmail.com",2901,"\u002F?p=2901","2020-05-24 03:20:32 +0000",{"type":7,"value":163},[164],[10,165,12],{},"\u002F2020-05-24-ue4-position-deviation-notes",{"title":5,"description":12},"_legacy\u002F2020\u002F2020-05-24-ue4-position-deviation-notes",[170,171],"UE4","Physx","l-g_jPlFybyQICc4OAhNVRgImE-5AJNP0ExgK98W2OQ",1788763179018]