前回作成した時限爆弾をキャラクターが投げることで手榴弾に早変わりします。タイマー設定を微調整することでキャラクターが爆発に巻き込まれないようにする必要がありますが、一番の問題は、キャラクターに手榴弾を投げさせる仕組みをどうするかですが、剣を投げ捨てる関数を再利用すれば問題解決です。
オーバーラップ関数を追加する
時限爆弾を剣のようにキャラクターに装備させる必要があります。なので、武器クラスとは別に手榴弾クラス専用のEquipメカニズムを構築する必要があります。兎にも角にも先ずGrenade.hにオーバーラッピング関連の関数を宣言する必要があります。
virtual void OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) override; virtual void OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex) override;
Grenade.cppに関数の定義を記述します。
void AGrenade::OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) { Super::OnOverlapBegin(OverlappedComponent, OtherActor, OtherComp, OtherBodyIndex, bFromSweep, SweepResult); if (OtherActor && GetItemState() == EItemState::EIS_Pickup) { AMyCharacter* MyChar = Cast(OtherActor); if (MyChar) { MyChar->SetOverlappingItem(this); } } } void AGrenade::OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex) { Super::OnOverlapEnd(OverlappedComponent, OtherActor, OtherComp, OtherBodyIndex); if (OtherActor) { AMyCharacter* MyChar = Cast(OtherActor); if (MyChar) { MyChar->SetOverlappingItem(nullptr); } } }
手榴弾用装備メカニズムの構築
次に、MyCharacter.hに手榴弾用の装備関数を宣言します。
void EquipGrenade(class AGrenade* GrenadeToEquip); void SwapGrenade(AGrenade* GrenadeToSwap); UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Weapon) AGrenade* EquippedGrenade;
MyCharacter.cppに関数の定義を記述します。
void AMyCharacter::EquipGrenade(AGrenade* GrenadeToEquip) { if (GrenadeToEquip) { const USkeletalMeshSocket* HandSocket = GetMesh()->GetSocketByName( FName("RightHandSocket")); if (HandSocket) { HandSocket->AttachActor(GrenadeToEquip, GetMesh()); } EquippedGrenade = GrenadeToEquip; EquippedGrenade->SetItemState(EItemState::EIS_Equipped); } } void AMyCharacter::SwapGrenade(AGrenade* GrenadeToSwap) { if (GrenadeToSwap) { DropWeapon(); EquipGrenade(GrenadeToSwap); } }
装備関数を書き換える必要があります。
void AMyCharacter::Equipping() { if (EquippedWeapon == nullptr && EquippedGrenade == nullptr) { if (OverlappingItem) { AWeapon* Weapon = Cast(OverlappingItem); AGrenade* Grenade = Cast(OverlappingItem); if (Weapon) { EquipWeapon(Weapon); SetOverlappingItem(nullptr); } else if (Grenade) { EquipGrenade(Grenade); SetOverlappingItem(nullptr); } } } else if (EquippedWeapon != nullptr || EquippedGrenade != nullptr) { if (OverlappingItem) { AWeapon* Weapon = Cast(OverlappingItem); AGrenade* Grenade = Cast(OverlappingItem); if (Weapon) { SwapWeapon(Weapon); SetOverlappingItem(nullptr); } else if (Grenade) { SwapGrenade(Grenade); SetOverlappingItem(nullptr); } } } }
手榴弾を装備している状況で武器を剣にスワップする場合、当然のことながら、手に持っている手榴弾は投げ捨てることになるので、投げた後は爆発して消滅してしまいます。なので、剣のように投げ捨てた後も再装備することはできません。
手榴弾用ドロップ・スローメカニズム構築
MyCharacter.cppのDropWeapon関数を書き換えます。
void AMyCharacter::DropWeapon() { if (EquippedWeapon != nullptr && EquippedGrenade == nullptr) { FDetachmentTransformRules DetachmentTransformRules(EDetachmentRule::KeepWorld, true); EquippedWeapon->GetItemMesh()->DetachFromComponent(DetachmentTransformRules); EquippedWeapon->SetWeaponState(EWeaponState::EWS_Falling); EquippedWeapon->ThrowWeapon(); EquippedWeapon = nullptr; } else if (EquippedWeapon == nullptr && EquippedGrenade != nullptr) { FDetachmentTransformRules DetachmentTransformRules(EDetachmentRule::KeepWorld, true); EquippedGrenade->GetItemMesh()->DetachFromComponent(DetachmentTransformRules); EquippedGrenade->SetItemState(EItemState::EIS_Falling); EquippedGrenade->ThrowGrenade(); EquippedGrenade = nullptr; } }
Grenade.hに手榴弾用のスロー関数ThrowGrenadeを作成します。
UFUNCTION(BlueprintCallable) void ThrowGrenade(); UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Damage) float TimerCount;
Grenade.cppのコンストラクタにTimerCountのデフォルト値を設定します。
TimerCount = 5.0f
Grenade.cppにThrowGrenade関数の定義を記述します。
void AGrenade::ThrowGrenade() { FRotator MeshRotation{ GetItemMesh()->GetComponentRotation().Pitch, GetItemMesh()->GetComponentRotation().Yaw, GetItemMesh()->GetComponentRotation().Roll }; GetItemMesh()->SetWorldRotation(MeshRotation, false, nullptr, ETeleportType::TeleportPhysics); const FVector MeshForward{ GetItemMesh()->GetForwardVector() }; const FVector MeshRight{ GetItemMesh()->GetRightVector() }; // Direction in which we throw the Weapon FVector ImpulseDirection = MeshRight.RotateAngleAxis(60.f, MeshForward); float RandomRotation{ 30.f }; ImpulseDirection = ImpulseDirection.RotateAngleAxis(RandomRotation, FVector(0.f, 1.f, 1.f)); ImpulseDirection *= 400000.f; GetItemMesh()->AddImpulse(ImpulseDirection); FTimerHandle TimerHandle; GetWorld()->GetTimerManager().SetTimer(TimerHandle, this, &AGrenade::OnExplode, TimerCount, false); }
GPYPressed関数も書き換える必要があります。
void AMyCharacter::GPYPressed() { if (EquippedWeapon == nullptr && EquippedGrenade == nullptr) return; DropWeapon(); }
TimerCountの数値をいじることで爆発のタイミングを自由に変えられます。
これで時限爆弾を装備して投げることができます。ただ、手榴弾の投げ方がボーリングのような感じなのですが、野球のように上手投げではなく、ソフトボールのように下手投げで手榴弾を投げるケースも多々見かけるので特に問題はないかと思いますが、投げ方をエディタ内で修正できるようにしておきます。Grenade.hに幾つかの変数を宣言します。
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Damage) float RightAngle; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Damage) float RandomAngle; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Damage) float ThrowImpulse;
Grenade.cppのコンストラクタに各変数のデフォルト値を設定します。
RightAngle = 10.f; RandomAngle = 10.f; ThrowImpulse = 50000.f;
Grenade.cppのスロー関数を書き換えます。
void AGrenade::ThrowGrenade() { FRotator MeshRotation{ GetItemMesh()->GetComponentRotation().Pitch, GetItemMesh()->GetComponentRotation().Yaw, GetItemMesh()->GetComponentRotation().Roll }; GetItemMesh()->SetWorldRotation(MeshRotation, false, nullptr, ETeleportType::TeleportPhysics); const FVector MeshForward{ GetItemMesh()->GetForwardVector() }; const FVector MeshRight{ GetItemMesh()->GetRightVector() }; // Direction in which we throw the Weapon FVector ImpulseDirection = MeshRight.RotateAngleAxis(RightAngle, MeshForward); float RandomRotation{ RandomAngle }; ImpulseDirection = ImpulseDirection.RotateAngleAxis(RandomRotation, FVector(0.f, 1.f, 1.f)); GetItemMesh()->AddImpulse(ImpulseDirection * ThrowImpulse); FTimerHandle TimerHandle; GetWorld()->GetTimerManager().SetTimer(TimerHandle, this, &AGrenade::OnExplode, TimerCount, false); }
コメント