-----------------------------2015.8.9---------------------李明阳---------------------
MyMOBAPawn的刚体效果:
MOBAHero的兔子角色因为形体不同于人体,角色倒地后重新站起来的视觉效果不易测试,下面的代码测试部分是在SwordGame项目SwordPlayer中完成。
Mesh转换刚体代码段:
UDN平台游戏的AI被玩家Kill后进入物理状态,经过LifeSpan的时间后消失,但是UDN平台游戏的Pawn没有任何Controller。
UTGame的Pawn在被Kill后进入物理状态,ABC的Pawn在被Kill后进入物理状态,它们的逻辑都差不多,只是UTGame逻辑更多。
为了简化,直接复制UDN平台AI进入物理状态的代码:
UTGame玩家按“F ”后角色倒地,Fire后角色站起来,看起来比较自然。按照UTPawn的代码方式定义UDKMOBAHeroPawn,把上面的代表放入函数PlayFeignDeath();
按ABC角色物理效果做好相应设置后,按下F后,角色倒地,角色转换为刚体后,位置回到了玩家的出生点。在ABC和UDN平台中,这种转换方式用在AIPawn上没有什么问题,在MyMOBA的项目中角色转为刚体后却回到了出生点。SetLocation不能解决这个问题。
ABC和UDN平台都不行,回到UTGame中分析代码;Pawn假装倒地设置了一个变量“bFeigningDeath”,通过查找这个变量,在UTPlayerController中有这样一个函数Mesh.SetRBPosition(),通过测试这个函数的确起作用。
最终UDKMOBAHeroPawn转换成刚体后的代码:
Actor和Component数组是两个不同的部分,Component通过Add(Attach)到Actor,类似于“父子关系”,通过Remove移除,SetLocation仅作用在Actor上。
if (bFeigningDeath)
Mesh.MinDistFactorForKinematicUpdate = 0.0;
Mesh.ForceSkelUpdate();
Mesh.SetTickGroup(TG_PostAsyncWork);
CollisionComponent = Mesh;
CylinderComponent.SetActorCollision(false, false);
Mesh.SetActorCollision(true, false);
Mesh.SetTraceBlocking(true, true);
SetPawnRBChannels(true);
SetPhysics(PHYS_RigidBody);
Mesh.PhysicsWeight = 1.f;
if (Mesh.bNotUpdatingKinematicDueToDistance)
{
Mesh.UpdateRBBonesFromSpaceBases(true, true);
}
Mesh.PhysicsAssetInstance.SetAllBodiesFixed(false);
Mesh.bUpdateKinematicBonesFromAnimation = false;
Mesh.WakeRigidBody();
//同步RB组件的Position和Mesh的Position
Mesh.SetRBPosition(Mesh.GetPosition());
Pawn倒地后站起来:
UTPawn倒地后,玩家开火Pawn会再次站起来,按照UTPawn的代码,Controller.Fire→Pawn.Fire。
重写UDKMOBAHeroPawn::StartFire
simulatedfunctionStartFire(byteFireModeNum)
{
// firing cancels feign death
if (bFeigningDeath)
{
//ComeFromfire用来判断是F键还是StartFire在调用StartFireFeignDeath();
ComeFromfire = true;
FeignDeath();
}
else
{
Super.StartFire(FireModeNum);
}
}
如果是StartFier调用角色假装倒地,角色就站起来,代码如下:
//碰撞组件改为CylinderComponent
CollisionComponent = CylinderComponent;
//Consolcomand::Show Collision时显示CylinderComponent
CylinderComponent.SetActorCollision(true, true);
// don't need collision events anymore
Mesh.SetNotifyRigidBodyCollision(false);
// don't allow player to move while animation is in progress
//SetPhysics(PHYS_None);
Mesh.PhysicsWeight = 0.f;
SetPawnRBChannels(FALSE);
if (Physics == PHYS_RigidBody)
{
setPhysics(PHYS_Falling);
}
角色站起来动作自然:
UTPawnPlayFeignDeath()函数else部分,(在MOBA项目中暂不需要,略过)
MyMOBAHerPawn ::StopFire
当角色倒地后,StarFire重新站起来,加入条件屏蔽StopFire重新生成Pawn
if(Pawn == None)
{
SwitchTeam();
SelectDemoGuyHero();
}
完善角色物理状态:
虽然按上面的逻辑,角色物理状态能正常州切换,但是有一点很不好,从站立姿势切换成刚体,第一次切换时效果还算可以,第二次切换时,角色要么会从地上弹起来,要么掉到地面下,要么Die。为了解决这个无法接受的Bug,我几乎耗完了一天的时间调试代码。最后得出这样一个原因:进入物理状态时,骨骼刚体间的碰撞比较强烈如果Box或Sphere之间有穿插,角色被撕扯得厉害。第二次进入刚体时角色会弹起来,原因正是MeshPhysics和CyllinderComponent发生了碰撞,因为Mesh始终放在Cyllinder中间,它们有穿插,碰撞过程中Mesh就会从Cyllinder中弹出去。
知道了原因,解决两个碰撞组件交换的问题。通过改变CollisionComponent的值配合SetActorCollision(),在视觉效果上能达到理想的结果,但是在刚体运算过程中Mesh还是会弹。直接屏蔽掉CyllinderComponent(CyllinderComponent去掉会有影响,不能去掉),角色只用Mesh来碰撞,OK角色不会弹,但是物理效果仍不是十分地好,但是已经可以了,,但是没有CyllinderComponent处理复杂碰撞时难保一切正常。
第二天,脑袋稍微清晰一些,既然第一次倒地能正常,第二次不正常。第一次倒地后Mesh的Pyhsics被激活了,如果第二次倒地重新激活,Mesh和Cyllinder如同第一次不会弹。查看相关函数或变量,找到这样一个函数:Mesh.SetHasPhysicsAssetInstance(true,false);经过测试起作用了,效果很好,角色在跑的过程中突然倒下在运动方向上有惯性,而且不用屏蔽CyllinderComponent。
在不改变CyllinderComponent为常规碰撞组件情况下,必要时启动Physics
进入刚体时代碰撞转换码逻辑:
CylinderComponent.SetActorCollision(false, false);
CollisionComponent = Mesh; //关掉这个,相机目标不是Mesh而是Actor(玩家倒下时相机不会抖一下)
Mesh.SetActorCollision(true, true);
Mesh.SetHasPhysicsAssetInstance(true,false);
玩家站起来时碰撞转换代码逻辑:
CollisionComponent = CylinderComponent;
CylinderComponent.SetActorCollision(true, true);
Mesh.SetActorCollision(false, false);
Mesh.SetHasPhysicsAssetInstance(false,false)
最终效果:
MyMOBAGame刚体碰撞切换时,为了让角色不容易掉到地面下,取消FootPlacement。为了缓解角色悬空,当角色站在比较斜的位置直接调用刚体,让角色倒下。
(相关代码:WalkableFloorZ,上不去就滑下)