前回作成した時限爆弾をキャラクターが投げることで手榴弾に早変わりします。タイマー設定を微調整することでキャラクターが爆発に巻き込まれないようにする必要がありますが、一番の問題は、キャラクターに手榴弾を投げさせる仕組みをどうするかですが、剣を投げ捨てる関数を再利用すれば問題解決です。
オーバーラップ関数を追加する
時限爆弾を剣のようにキャラクターに装備させる必要があります。なので、武器クラスとは別に手榴弾クラス専用の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);
}




コメント