Unreal Engine:手榴弾(Time-Delay-Grenade)を作成する

前回作成した時限爆弾をキャラクターが投げることで手榴弾に早変わりします。タイマー設定を微調整することでキャラクターが爆発に巻き込まれないようにする必要がありますが、一番の問題は、キャラクターに手榴弾を投げさせる仕組みをどうするかですが、剣を投げ捨てる関数を再利用すれば問題解決です。

スポンサーリンク

オーバーラップ関数を追加する

時限爆弾を剣のようにキャラクターに装備させる必要があります。なので、武器クラスとは別に手榴弾クラス専用の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);
}
スポンサーリンク
スポンサーリンク

コメント

タイトルとURLをコピーしました