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