開始する前に
前のセクションの「キャラクターを装備する」で次の目標が完了していることを確認してください。
リスポーンするピックアップ アイテムを作成し、レベルに追加する
キャラクターが持って使用できる装備可能なダーツ ランチャーを作成する
基本の発射物
キャラクターはダーツ ランチャーを保持でき、ツールにはそれを使用するためのコントロール バインディングがありますが、名付けただけでそのとおりに動くわけではありません。 このセクションでは、発射物ロジックを実装し、装備したアイテムからダーツを発射するようにします。
Unreal Engine には、アクタに追加して発射物に変えることができる発射物移動コンポーネント クラスがあります。 これには、発射物の速度、弾性、重力スケールなど、多くの便利な変数が含まれています。
発射物の実装による演算を処理するには、以下のようなことについて検討する必要があります。
発射物の初期トランスフォーム、速度、方向。
キャラクターの中心から発射物をスポーンするか、装備しているツールの中心からスポーンするか。
発射物が別のオブジェクトと衝突したときに、どのような動作をさせるか。
発射物の基本クラスを作成する
まず、基本となる発射物クラスを作成し、次にサブクラス化してツール用の固有の発射物を作成します。
基本となる発射物クラスの設定を開始するには、次の手順を実行します。
Unreal Editor で、[Tools (ツール)] > [New C++ Class (新規C++クラス)] に移動します。 親クラスとして [Actor (アクタ)] を選択し、クラスに「
FirstPersonProjectile」という名前を付けます。 [Create Class (クラスを作成)] をクリックします。VS で、「
FirstPersonProjectile.h」 に移動します。 ファイルの先頭で、UProjectileMovementComponentとUSphereComponentの両方を前方宣言します。球体コンポーネントを使用して、発射物と他のオブジェクトとのコリジョンをシミュレートします。
C++// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "FirstPersonProjectile.generated.h" class UProjectileMovementComponent; class USphereComponent;BlueprintType指定子とBlueprintable指定子を追加し、このクラスをブループリントに公開します。C++UCLASS(BlueprintType, Blueprintable) class FIRSTPERSON_API AFirstPersonProjectile : public AActor「
FirstPersonProjectile.cpp」を開きます。ファイルの先頭に、”GameFramework/ProjectileMovementComponent.h”の include 文を追加し、発射物移動コンポーネントクラスをインクルードします。
#include "FirstPersonProjectile.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "Components/SphereComponent.h"
// Sets default values
AFirstPersonProjectile::AFirstPersonProjectile()オブジェクトにヒットしたときの発射物の動作を実装する
発射物をよりリアルにするには、発射物が当たるオブジェクトに力 (力積) を及ぼすようにします。 たとえば、物理対応のブロックを射撃している場合、発射物は地面に沿ってそのブロックを押し動かします。 次に、デフォルトの有効期限を待たずに、衝撃後に発射物を除去します。 この動作を実装するために OnHit() 関数を作成します。
発射物のオンヒット動作を実装するには、次の手順を実行します。
「
FirstPersonProjectile.h」のpublicセクションで、「PhysicsForce」という名前のfloatプロパティを定義します。EditAnywhere、BlueprintReadOnly、Category = “発射物 | 物理”のUPROPERTY()マクロを追加します。これは、発射物が何かにヒットしたときに受ける力の量です。
C++// The amount of force this projectile imparts on objects it collides with UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Projectile | Physics") float PhysicsForce = 100.0f;void関数OnHit()を定義します。 これは、アクタが別のコンポーネントやアクタと衝突したときに呼び出される、AActor からの関数です。 以下の引数を指定します。HitComp:ヒットされたコンポーネント。OtherActor:ヒットされたアクタ。OtherComp:ヒットを作成したコンポーネント (この場合は、発射物の CollisionComponent)。NormalImpulse:ヒットの法線の力積。Hit:時間、距離、場所など、ヒット イベントに関するより多くのデータを含むFHitResult参照。
C++// Called when the projectile collides with an object UFUNCTION() void OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);「
FirstPersonProjectile.cpp」で、ヘッダ ファイルで定義したOnHit()関数を実装します。OnHit()内のif文で、以下を確認します。OtherActorが null でなく、発射物自体ではないこと。OtherCompが null でなく、物理もシミュレートされていること。
C++void AFirstPersonProjectile::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit) { // Only add impulse and destroy projectile if we hit a physics if ((OtherActor != nullptr) && (OtherActor != this) && (OtherComp != nullptr) && OtherComp->IsSimulatingPhysics()) { } }これは、発射物が、自身以外のオブジェクトにヒットし、物理に関与していることを確認します。
if文内で、AddImpulseAtLocation()を使用して、OtherCompコンポーネントに力積を追加します。PhysicsForceで乗算された発射物の速度をこの関数に渡し、発射物アクタの位置で適用します。C++if ((OtherActor != nullptr) && (OtherActor != this) && (OtherComp != nullptr) && OtherComp->IsSimulatingPhysics()) { OtherComp->AddImpulseAtLocation(GetVelocity() * PhysicsForce, GetActorLocation()); }AddImpulseAtLocation()は、Unreal Engine の物理関数で、ワールド空間の特定の位置であり、シミュレートされた物理オブジェクトに瞬間的な力 (力積) を加えます。 何かが飛び出す爆発、オブジェクトにヒットする弾丸、素早く開くドアなど、衝撃をシミュレートする場合に便利です。最後に、この発射物が別のアクタにヒットしたため、
Destroy()を呼び出すことで、プロジェクターをシーンから削除します。
完成した OnHit() 関数は次のようになります。
void AFirstPersonProjectile::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
// Only add impulse and destroy projectile if we hit a physics
if ((OtherActor != nullptr) && (OtherActor != this) && (OtherComp != nullptr) && OtherComp->IsSimulatingPhysics())
{
OtherComp->AddImpulseAtLocation(GetVelocity() * PhysicsForce, GetActorLocation());
Destroy();
}
}発射物のメッシュ、移動、コリジョン コンポーネントを追加する
次に、スタティックメッシュ、発射物移動ロジック、コリジョン球体を発射物に追加し、発射物がどのように移動するかを定義します。
これらのコンポーネントを発射物に追加するには、次の手順を実行します。
「
FirstPersonProjectile.h」のパブリックセクションに、ProjectileMesh という名前のUStaticMeshComponentへのTObjectPtrを宣言します。これはワールド内の発射物のスタティックメッシュです。EditAnywhere、BlueprintReadOnly、Category = “発射物 | メッシュ”のUPROPERTY()マクロを追加します。C++// Mesh of the projectile in the world UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Projectile | Mesh") TObjectPtr<UStaticMeshComponent> ProjectileMesh;Protectedセクションで、以下を宣言します。「
CollisionComponent」という名前のUSphereComponentにTObjectPtrを宣言。「
ProjectileMovement」という名前のUProjectileMovementComponentにTObjectPtrを宣言。
両方に
VisibleAnywhere、BlueprintReadOnly、Category = “発射物 | コンポーネント”のUPROPERTY()マクロを追加します。C++// Sphere collision component UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Projectile | Components") TObjectPtr<USphereComponent> CollisionComponent; // Projectile movement component UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Projectile | Components") TObjectPtr<UProjectileMovementComponent> ProjectileMovement;ProjectileMovementComponent が移動ロジックを処理します。 このクラスは、速度、重力、その他の変数に基づいて親アクタが行く場所を計算します。 それから、
ティック中に発射物に動きを適用します。「
FirstPersonProjectile.cpp」のAFirstPersonProjectile()コンストラクタ関数で、発射物のメッシュ、コリジョン、発射物移動コンポーネントのデフォルトのサブオブジェクトを作成します。 次に、発射物メッシュをコリジョン コンポーネントにアタッチします。C++// Sets default values AFirstPersonProjectile::AFirstPersonProjectile() { // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. PrimaryActorTick.bCanEverTick = true; // Use a sphere as a simple collision representation CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("CollisionComponent")); check(CollisionComponent != nullptr);InitSphereRadius()を呼び出し、CollisionComponentを半径5.0fで初期化します。BodyInstance.SetCollisionProfileName()を使用して、コリジョン コンポーネントのコリジョン プロファイルの名前を「Projectile」に設定します。Unreal Editor では、コリジョン プロファイルは [Project Settings (プロジェクト設定)] > [Engine (エンジン)] > [Collision (コリジョン)] に格納され、コードで使用するために最大 18 個のカスタム コリジョン プロファイルを定義できます。 この「Projectile」コリジョンプロファイルのデフォルトの動作は「Block」であり、衝突したすべてのオブジェクトとコリジョンを発生させます。
C++CollisionComponent->InitSphereRadius(5.0f); // Set the collision profile to the "Projectile" collision preset CollisionComponent->BodyInstance.SetCollisionProfileName("Projectile");発射物が何かにヒットしたときに起動する
OnHit()関数を先ほど定義しましたが、そのコリジョンが発生したときを通知する方法が必要です。 これを実行するには、AddDynamic()マクロを使用して、CollisionComponent内のOnComponentHitEventに関数をサブスクライブします。 このマクロにOnHit()関数を渡します。C++// Set up a notification for when this component hits something blocking CollisionComponent->OnComponentHit.AddDynamic(this, &AFirstPersonProjectile::OnHit);CollisionComponentを発射物のRootComponentとして、およびトラックする移動コンポーネントのUpdatedComponentとして設定します。C++// Set as root component RootComponent = CollisionComponent; ProjectileMovement->UpdatedComponent = CollisionComponent;ProjectileMovementコンポーネントで次の値を初期化します。InitialSpeed:発射物がスポーンするときの初期速度。 これを3000.0fに設定します。MaxSpeed:発射物の最高速度。 これを3000.0fに設定します。bRotationFollowVelocity:オブジェクトを速度の方向に回転させるかどうか。 例えるなら、紙飛行機が上昇したり下降したりするにつれて、どのように上下に傾くかです。 これを、trueに設定します。bShouldBounce:発射物が障害物を跳ね返るかどうか。 これを、trueに設定します。
C++ProjectileMovement->UpdatedComponent = CollisionComponent; ProjectileMovement->InitialSpeed = 3000.f; ProjectileMovement->MaxSpeed = 3000.f; ProjectileMovement->bRotationFollowsVelocity = true; ProjectileMovement->bShouldBounce = true;
発射物の有効期限を設定する
デフォルトでは、発射してから数秒後に発射物が消えます。 しかし、エディタで発射物のブループリントを派生させると、このデフォルト時間を変更したり削除したりして、レベルにフォーム ダーツを追加してみることができます。
数秒後に発射物を消すには、次の手順を実行します。
「
FirstPersonProjectile.h」のpublicセクションで、「ProjectileLifespan」という名前の float プロパティを宣言します。EditAnywhere、BlueprintReadOnly、Category = “発射物 | 有効期限”のUPROPERTY()マクロを追加します。これは秒単位の発射物の有効期限です。
C++// Despawn after 5 seconds by default UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Projectile | Lifespan") float ProjectileLifespan = 5.0f;「
FirstPersonProjectile.cpp」で、AFirstPersonProjectile()コンストラクタ関数の最後に、発射物のInitialLifeSpanをProjectileLifespanに設定し、5 秒後に消えるようにします。C++// Disappear after 5.0 seconds by default. InitialLifeSpan = ProjectileLifespan;InitialLifeSpanは、AActor から継承されたプロパティです。 アクタが消滅するまでの期間を設定する float です。 値が0の場合、アクタはゲームが停止するまで存続します。
完成した FirstPersonProjectile.h は次のようになります。
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "FirstPersonProjectile.generated.h"
class UProjectileMovementComponent;
class USphereComponent;
完成した AFirstPersonProjectile コンストラクタ関数は次のようになります。
// Sets default values
AFirstPersonProjectile::AFirstPersonProjectile()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// Use a sphere as a simple collision representation
CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("CollisionComponent"));
check(CollisionComponent != nullptr);
キャラクターのカメラ方向を取得する
発射物はダーツ ランチャー自体からスポーンする必要があるため、ダーツ ランチャーがどこにあり、どこを指しているのかを知るために何らかの計算を行う必要があります。 ランチャーはプレイヤー キャラクターにアタッチされているため、これらの値はキャラクターがどこにいて、どこを見ているのかに応じて変化します。
一人称視点キャラクターにはダーツを発射するために必要な位置情報が含まれているため、まずキャラクター クラスを変更してライン トレースでその情報をキャプチャし、結果を返します。
トレースを使用してキャラクターから必要な情報を取得するには、次の手順を実行します。
VS でキャラクターの
.hファイルと.cppファイルを開きます。.hファイルのpublicセクションで、FVectorを返す「GetCameraTargetLocation()」という名前の新しい関数を宣言します。 この関数は、キャラクターが見ているワールド内の位置を返します。C++// Returns the location in the world the character is looking at UFUNCTION() FVector GetCameraTargetLocation();キャラクターの
.cppファイルで、GetCameraTargetLocation()関数を実装します。 まず、返す「TargetPosition」という名前の FVector を宣言します。GetWorld()を呼び出すことでUWorldへのポインタを作成します。C++// The target position to return FVector TargetPosition; UWorld* const World = GetWorld();if文を追加して、Worldが null ではないことを確認します。if文内で、「Hit」という名前のFHitResultを宣言します。C++if (World != nullptr) { // The result of the line trace FHitResult Hit;FHitResultは、ヒットしたアクタまたはコンポーネント、ヒットした場所などのコリジョン クエリの結果に関する情報を格納する UE の構造体です。キャラクターが見ているポイントを見つけるには、キャラクターが見下ろしているどこか遠くのポイントまでベクターに沿ってライン トレースをシミュレートします。 ライン トレースがオブジェクトに衝突した場合は、キャラクターがワールド内でどこを見ているかがわかります。
「TraceStart」と「TraceEnd」という名前の 2 つの const FVector 値を宣言し、次の手順を実行します。
TraceStartをFirstPersonCameraComponentの位置に設定します。TraceEndをTraceStartおよびカメラ コンポーネントの前方ベクターに非常に大きな数を乗算した値に設定します。 これにより、キャラクターが空を見つめていないかぎり、トレースはワールド内のほとんどのオブジェクトと衝突できる距離に到達します。 (キャラクターが空を見ている場合、TraceEndがライン トレースの終点です。)C++// Simulate a line trace from the character along the vector they're looking down const FVector TraceStart = FirstPersonCameraComponent->GetComponentLocation(); const FVector TraceEnd = TraceStart + FirstPersonCameraComponent->GetForwardVector() * 10000.0;
UWorldからLineTraceSingleByChannel()を呼び出し、トレースをシミュレートします。 これに、Hit、TraceStart、TraceEnd、ECollisionChannel::ECC_Visibilityを渡します。これは
TraceStartからTraceEndまでのライン トレースをシミュレートし、表示されているオブジェクトと衝突して、トレースの結果をHitに格納します。ECollisionChannel::ECC_Visibilityは、トレーシングに使用するチャンネルです。これらのチャンネルは、トレースがヒットを試行するオブジェクトのタイプを定義します。 視線カメラのチェックにはECC_Visibilityを使用します。C++World->LineTraceSingleByChannel(Hit, TraceStart, TraceEnd, ECollisionChannel::ECC_Visibility);Hit値に衝撃の位置や法線などのヒット結果の情報が含まれるようになりました。 また、ヒットがオブジェクトのコリジョンによるものであるかどうかも判断します。 衝撃の位置 (またはトレース ラインの終点) は、返すカメラ ターゲットの位置です。三項演算子を使用して、ブロッキング ヒットだった場合は
Hit.ImpactPointに、そうでなかった場合はHit.TraceEndにTargetPositionを設定します。 それから、TargetPositionを返します。C++TargetPosition = Hit.bBlockingHit ? Hit.ImpactPoint : Hit.TraceEnd; } return TargetPosition;
完成した GetCameraTargetLocation() 関数は次のようになります。
FVector AADventureCharacter::GetCameraTargetLocation()
{
// The target position to return
FVector TargetPosition;
UWorld* const World = GetWorld();
if (World != nullptr)
{
// The result of the line trace
FHitResult Hit;
DartLauncher::Use() で発射物をスポーンする
これでキャラクターがどこを見ているかを把握できるようになったため、ダーツ ランチャーの Use() 関数に残りの発射物ロジックを実装することができるようになりました。 発射物を発射する位置と方向を取得して、発射物をスポーンします。
発射物がスポーンする位置と回転を取得するには、次の手順を実行します。
「
DartLauncher.h」のファイルの先頭に、AFirstPersonProjectileの前方宣言を追加します。publicセクションで、「ProjectileClass」という名前のTSubclassOf<AFirstPersonProjectile>プロパティを宣言します。 これがダーツ ランチャーがスポーンする発射物です。 これに、EditAnywhereとCategory = 発射物のUPROPERTY()マクロを追加します。「
DartLauncher.h」は次のようになります。C++// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "AdventureGame/EquippableToolBase.h" #include "AdventureGame/FirstPersonProjectile.h" #include "DartLauncher.generated.h" class AFirstPersonProjectile;「
DartLauncher.cpp」に”Kismet/KismetMathLibrary.h”の include 文を追加します。 発射物の演算は複雑になる可能性があるため、このファイルには発射物の発射に使用できる便利な関数がいくつか含まれます。C++#include "DartLauncher.h" #include "Kismet/KismetMathLibrary.h" #include "AdventureGame/AdventureCharacter.h"次の手順を実行して、DartLauncher の
Use()関数を実装します。GetWorld()を呼び出すことでUWorldを取得します。if文を追加して、WorldとProjectileClassが null でないことを確認します。if文内でOwningCharacter->GetCameraTargetLocation()を呼び出すことでキャラクターが見ている位置を取得します。
C++void ADartLauncher::Use() { UWorld* const World = GetWorld(); if (World != nullptr && ProjectileClass != nullptr) { FVector TargetPosition = OwningCharacter->GetCameraTargetLocation(); } }キャラクターが持っているツールから発射物をスポーンしたいものの、オブジェクトの中心から直接発射物をスポーンしたくない場合があります。 ダーツ ランチャーの
SKM_Pistolメッシュにはダーツの正確なスポーン ポイントを設定するために使用できる「銃口」ソケットがあります。「
SocketLocation」という名前の新しいFVectorを宣言し、これをToolMeshComponentでGetSocketLocation(“Muzzle”)を呼び出した結果に設定します。C++// Get the correct socket to spawn the projectile from FVector SocketLocation = ToolMeshComponent->GetSocketLocation("Muzzle");次に、「
SpawnRotation」という名前のFRotatorを宣言します。 これは、発射物がスポーンするときの回転 (ピッチ、ヨー、ロールの値) です。Kismet 数学ライブラリから
FindLookAtRotation()を呼び出した結果にこれを設定し、プレイヤー キャラクターから取得したSocketLocationとTargetPositionを渡します。C++FRotator SpawnRotation = UKismetMathLibrary::FindLookAtRotation(SocketLocation, TargetPosition);FindLookAtRotationは、SocketLocationでTargetPositionを向くために必要な回転を計算し、返します。「
SpawnLocation」という名前のFVectorを宣言し、それをSocketLocationとSpawnRotationの前方ベクターに10.0を乗算したものを追加した結果に設定します。銃口ソケットはランチャーの正面にないため、ベクターにオフセットを乗算して、発射物が正しい位置から発射されるようにする必要があります。
C++FVector SpawnLocation = SocketLocation + UKismetMathLibrary::GetForwardVector(SpawnRotation) * 10.0;
位置と回転を取得したため、発射物をスポーンする準備ができました。
発射物をスポーンするには、次の手順を実行します。
そのまま
Use()関数で、「ActorSpawnParams」という名前のFActorSpawnParametersを宣言します。 このクラスには、アクタをどこで、どのようにスポーンするかに関する情報が含まれています。C++//Set Spawn Collision Handling Override FActorSpawnParameters ActorSpawnParams;ActorSpawnParamsのSpawnCollisionHandlingOverride値をESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfCollidingに設定します。これは、発射物がスポーンする、別のアクタと衝突しない場所 (壁の中など) の検索を試み、適切な場所が見つからない場合はスポーンしません。
C++ActorSpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfColliding;SpawnActor()を使用してProjectileClass、SpawnLocation、SpawnRotation、ActorSpawnParamsを渡し、ダーツ ランチャーの銃口から発射物がスポーンするようにします。C++// Spawn the projectile at the muzzle World->SpawnActor<AFirstPersonProjectile>(ProjectileClass, SpawnLocation, SpawnRotation, ActorSpawnParams);
完成した Use() 関数は次のようになります。
void ADartLauncher::Use()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, TEXT("Using the dart launcher!"));
UWorld* const World = GetWorld();
if (World != nullptr && ProjectileClass != nullptr)
{
FVector TargetPosition = OwningCharacter->GetCameraTargetLocation();
// Get the correct socket to spawn the projectile from
フォーム ダーツ クラスとブループリントを派生させる
すべてのスポーン ロジックが完成したため、次に実際の発射物を作成します。 ダーツ ランチャー クラスでは、起動する AFirstPersonProjectile のサブクラスが必要であるため、レベルで使用するサブクラスをコード内でビルドする必要があります。
インゲームで使用するフォーム ダーツ発射物クラスを派生させるには、次の手順を実行します。
Unreal Editor で、[Tools] > [New C++ Class] に移動します。
[All Classes (すべてのクラス)] で、「First Person Projectile」を検索して、親クラスとして選択し、クラスに「
FoamDart」と名前を付けます。 [Create Class] をクリックします。VS で、これらのファイルをそのままにし、コードを保存してコンパイルします。
このチュートリアルでは、FirstPersonProjectile で定義したもの以外のカスタム発射物コードは実装しませんが、プロジェクトのニーズに合わせて FoamDart クラスを独自に変更することができます。 たとえば、ダーツの発射物が消えたり跳ねたりするのではなく、オブジェクトにくっつくようにすることもできます。
フォーム ダーツのブループリントを作成するには、次の手順を実行します。
Unreal Editor で、FoamDart に基づいてブループリント クラスを作成し、「
BP_FoamDart」という名前を付けます。 これを「FirstPerson/Blueprints/Tools」フォルダに保存します。ブループリントを開いた状態で、発射物メッシュ コンポーネントを選択し、スタティックメッシュを SM_FoamBullet に設定します。
ブループリントをコンパイルして保存します。
コンテンツ ブラウザで、「
BP_DartLauncher」を開きます。[Details] パネルの [Projectile] セクションで、[Projectile Class (発射物クラス)] を
BP_FoamDartに設定し、ブループリントをコンパイルして保存します。BP_FoamDartがリストに表示されない場合は、コンテンツ ブラウザでBP_FoamDartを選択し、Projectile Class プロパティに戻って [コンテンツ ブラウザで選択中のアセットを使用] をクリックします。
ついにお披露目です。 アセットを保存し、[Play (プレイ)] をクリックします。 ゲームが開始したら、ダーツ ランチャーまで走ってダーツを手に入れることができます。 マウスの左ボタンを押すと、ダーツ ランチャーの銃口から発射物がスポーンし、レベル内を跳ねます。 これらの発射物は 5 秒後に消え、衝突するオブジェクトに小さな物理的力を与えるはずです (自分自身を含む)。
次の内容
おつかれさまでした! 一人称視点のプログラマー トラック チュートリアルを完了し、多くのことを学ぶことができました。
カスタム キャラクターとカスタム移動を実装し、ピックアップとデータ アセットを作成して、独自の発射物のある装備可能なツールを作成しました。 このプロジェクトを独自のものに仕上げるために必要なものがすべて揃いました。
以下は推奨事項です。
さまざまなタイプのアイテムでプレイヤーのインベントリを拡張できますか? アイテムのスタックについてはどうでしょうか?
ピックアップと発射物を組み合わせてピックアップ可能な弾薬を作成できますか? ダーツランチャーにこの弾薬システムを実装するのはどうでしょうか?
消耗品を装備可能な消耗品に発展させることはできるでしょうか? プレイヤーが持つヘルス パックや、拾って投げることができるボールはどうでしょうか?
完全なコード
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "FirstPersonProjectile.generated.h"
class UProjectileMovementComponent;
class USphereComponent;
// Fill out your copyright notice in the Description page of Project Settings.
#include "FirstPersonProjectile.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "Components/SphereComponent.h"
// Sets default values
AFirstPersonProjectile::AFirstPersonProjectile()
{
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Camera/CameraComponent.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "GameFramework/Character.h"
#include "InputActionValue.h"
// Copyright Epic Games, Inc. All Rights Reserved.
#include "AdventureCharacter.h"
#include "EquippableToolBase.h"
#include "EquippableToolDefinition.h"
#include "ItemDefinition.h"
#include "InventoryComponent.h"
// Sets default values
AAdventureCharacter::AAdventureCharacter()
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "AdventureGame/EquippableToolBase.h"
#include "AdventureGame/FirstPersonProjectile.h"
#include "DartLauncher.generated.h"
class AFirstPersonProjectile;
// Copyright Epic Games, Inc. All Rights Reserved.
#include "DartLauncher.h"
#include "Kismet/KismetMathLibrary.h"
#include "AdventureGame/AdventureCharacter.h"
void ADartLauncher::Use()
{
UWorld* const World = GetWorld();