由于我想做的效果是玩家可以在第一人称和第三人称中来回切换,但是由于我的第一人称视角摄像机是附加在角色身上的,当角色进行大幅度动作或视角向最下方看时摄像机很容易和人物模型发生碰撞,从而穿模,极大的影响游戏体验。对此我做了两种解决方案:
(1)给角色添加一个碰撞阻挡,让摄像机无法穿过角色模型,这样就不会穿模,但是我感觉这样会让手感变差,毕竟移鼠标突然移不动了可能会很不爽。
(2)给玩家的躯干部分设置透明材质,这样就算第一人称的摄像机穿进了角色模型,由于是透明材质所以不会发生影响。这样做需要解决两个问题,一是设置仅躯干部分的透明,因为玩家还要看到手部的换弹动作,技能释放动画之类的。第二是玩家切换到第三人称的时候,应设置回原本角色的材质,毕竟第三人称看着一个只有双手其余部位透明的角色太离谱了。第三,不管是第几人称,其他玩家看到你都应该是完整的角色,正常的材质而不是透明的。
接下来我们来看第二种如何实现:
首先是代码部分的实现。先看输入绑定,我这里用V键切换人称视角,正如所见,设置材质和相关同步的代码我是放在切换人称的功能里一起实现的。
1 2
| EnhancedInputComponent->BindAction(IA_ChangeView, ETriggerEvent::Triggered, this, &ABlasterCharacter::InputShiftView);
|
这里是处理同步的逻辑,为什么这样写都写在注释里了。本篇所有的代码都写在角色类里的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| private: UPROPERTY(EditAnywhere, Category = "EffectMaterials") UMaterialInterface* TransparentMaterial;
UPROPERTY(EditAnywhere, Category = "EffectMaterials") UMaterialInterface* NormalMaterialUp;
UPROPERTY(EditAnywhere, Category = "EffectMaterials") UMaterialInterface* NormalMaterialDown;
void ABlasterCharacter::InputShiftView(const FInputActionValue& InputValue) { if(HasAuthority()) { ClientChangeView(); return; } ServerChangeView(); }
void ABlasterCharacter::ServerChangeView_Implementation() { ChangeCameraView(); ClientChangeView(); }
void ABlasterCharacter::ClientChangeView_Implementation() { ChangeCameraView(); }
void ABlasterCharacter::ClientSetMaterial_Implementation() { int32 MaterialSlotIndex0 = GetMesh()->GetMaterialIndex("18 - Default"); int32 MaterialSlotIndex1 = GetMesh()->GetMaterialIndex("12 - Default"); if (IsLocallyControlled()) { GetMesh()->SetMaterial(MaterialSlotIndex0, TransparentMaterial); GetMesh()->SetMaterial(MaterialSlotIndex1, TransparentMaterial); } else { GetMesh()->SetMaterial(MaterialSlotIndex0, NormalMaterialDown); GetMesh()->SetMaterial(MaterialSlotIndex1, NormalMaterialUp); } }
void ABlasterCharacter::ServerSetMaterial_Implementation() { int32 MaterialSlotIndex0 = GetMesh()->GetMaterialIndex("18 - Default"); int32 MaterialSlotIndex1 = GetMesh()->GetMaterialIndex("12 - Default"); if(IsLocallyControlled()) { GetMesh()->SetMaterial(MaterialSlotIndex0, TransparentMaterial); GetMesh()->SetMaterial(MaterialSlotIndex1, TransparentMaterial); } else { GetMesh()->SetMaterial(MaterialSlotIndex0, NormalMaterialDown); GetMesh()->SetMaterial(MaterialSlotIndex1, NormalMaterialUp); } ClientSetMaterial(); }
|
下面就是角色的材质插槽,在修改自己的角色的时候改成自己角色对应的材质插槽就行。

下面来看怎么切换材质的,构造函数已删除无关代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| ABlasterCharacter::ABlasterCharacter() { int32 MaterialSlotIndex0 = GetMesh()->GetMaterialIndex("18 - Default"); int32 MaterialSlotIndex1 = GetMesh()->GetMaterialIndex("12 - Default"); GetMesh()->SetMaterial(MaterialSlotIndex0, NormalMaterialDown); GetMesh()->SetMaterial(MaterialSlotIndex1, NormalMaterialUp); }
void ABlasterCharacter::BeginPlay() { Super::BeginPlay(); ServerSetMaterial(); }
void ABlasterCharacter::ChangeCameraView() { if (FollowCamera->IsActive()) { FollowCamera->SetActive(false); FollowCamera->Deactivate(); TPSCamera->SetActive(true); TPSCamera->Activate(); GetCharacterMovement()->bOrientRotationToMovement = true; bUseControllerRotationYaw = false; if(IsLocallyControlled()) { int32 MaterialSlotIndex0 = GetMesh()->GetMaterialIndex("18 - Default"); int32 MaterialSlotIndex1 = GetMesh()->GetMaterialIndex("12 - Default"); GetMesh()->SetMaterial(MaterialSlotIndex0, NormalMaterialDown); GetMesh()->SetMaterial(MaterialSlotIndex1, NormalMaterialUp); } } else { TPSCamera->SetActive(false); TPSCamera->Deactivate(); FollowCamera->SetActive(true); FollowCamera->Activate(); GetCharacterMovement()->bOrientRotationToMovement = false; bUseControllerRotationYaw = true; if (IsLocallyControlled()) { int32 MaterialSlotIndex0 = GetMesh()->GetMaterialIndex("18 - Default"); int32 MaterialSlotIndex1 = GetMesh()->GetMaterialIndex("12 - Default"); GetMesh()->SetMaterial(MaterialSlotIndex0, TransparentMaterial); GetMesh()->SetMaterial(MaterialSlotIndex1, TransparentMaterial); } } }
|
最后看一下透明材质怎么做的

最后记得将动态阴影投射为遮罩和不透明蒙版剪切值改为0,不然默认的是0.33,那么当你设置不透明度低于0.33时就不会产生阴影了。

最后测试一下结果,以监听服务器模式打开三个端,即一个Server两个Client

可以看到所有的端的人物都有完整的阴影,也可以看到Client1玩家往下看时只能看到手而没有身体部分,但是自身的阴影确实是完整的。