По материалам https://docs.unrealengine.com/latest/INT/Programming/Tutorials/FirstPersonShooter/index.html
Дополнительно:
- Уничтожаемые объекты, удаление объектов
- Цвет папки
- Collections
- Бег (Sprint)
Создаем C++ -> Basic Code Project -> With Starter Content называем его "MyTestProject".
Для урока понадобятся 3д модели с формой столкновений, мы будем использовать стандартные (если проект With Starter Content).
Включите в настройках View Options внутри Content Browser галочки Show Engine Content для просмотра скрытого стандартного контента.
В Content Browser создаем папку "ProjectContent", а в ней подпапки Blueprints, Maps, Models, Textures.
При желании вы можете изменить цвет папки кликнув на ней Правой Кнопкой Мыши (ПКМ) и выбрав пункт "set colour":
- Анимации зеленые
- Скелетные меши розовые
- Карты оранжевые
Сохраняем карту через File -> Save as в папку ProjectContent->Maps под названием FPSMap.
В Edit->Project Settings во вкладке Maps & Modes устанавливаем FPSMap как Editor Startup Map
Сохраняем все через File -> Save All (Ctrl+Shift+S)
Для более удобного поиска вы можете добавить часто используемые внутри Content Browser файлы в коллекции.
Collections - персонализированные группы ссылок на файлы.
Для этого в View Options включите Show Collections.
Раскройте в Content Browser боковую панель нажав на иконку "Show or hide sources panel", находящуюся рядом с Filters и полем поиска.
Создайте коллекцию нажав на иконку "+":
Для добавления в коллекцию достаточно перетащить в нее asset:
см. подробнее на https://docs.unrealengine.com/latest/INT/Engine/Content/Browser/UserGuide/Collections/
Открываем редактор кода File -> Open Visual Studio
Добавляем в MyTestProjectGameModeBase.h:
virtual void StartPlay() override;
Добавляем в MyTestProjectGameModeBase.cpp:
void AMyTestProjectGameModeBase::StartPlay()
{
Super::StartPlay();
if (GEngine)
{
// Display a debug message for five seconds.
// The -1 "Key" value (first argument) indicates that we will never need to update or refresh this message.
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, TEXT("Hello World, this is FPSGameMode!"));
}
}
Сохраняем все, Компилируем код.
Создадим внутри папки Blueprints подпапки Characters, Controllers, GameMode, HUD, Projectile.
Создадим Blueprint class на основе MyTestProjectGameModeBase (/Script/MyTestProject.MyTestProjectGameModeBase).
Для этого в Content Browser выбираем ПКМ MyTestProjectGameModeBase и выбираем пункт Create Blueprint class based on MyTestProjectGameModeBase.
Сохраняем под именем BP_MyTestProjectGameModeBase в папку ProjectContent/Blueprints/GameMode.
В Edit->Project Settings во вкладке Maps & Modes устанавливаем BP_MyTestProjectGameModeBase как Default GameMode
Добавляем в Unreal Editor новый C++ класс через File -> New C++ Class, выбираем родительский класс (Choose Parent Class) равным Character и называем как FPSCharacter.
Заменяем функцию BeginPlay в FPSCharacter.cpp на:
// Called when the game starts or when spawned
void AFPSCharacter::BeginPlay()
{
Super::BeginPlay();
if (GEngine)
{
// Put up a debug message for five seconds. The -1 "Key" value (first argument) indicates that we will never need to update or refresh this message.
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("We are using FPSCharacter."));
}
}
Сохраняем все, Компилируем код.
Создадим Blueprint class на основе FPSCharacter.
Для этого в Content Browser выбираем ПКМ MyTestProjectGameModeBase и выбираем пункт Create Blueprint class based on FPSCharacter.
Сохраняем под именем BP_FPSCharacter в папку ProjectContent/Blueprints/Characters.
В Edit->Project Settings во вкладке Maps & Modes (Selected GameMode) устанавливаем BP_FPSCharacter как Default Pawn
Сохраняем все, Компилируем код, Компилируем Blueprints.
При запуске игры игрок должен перестать двигаться.
Axis Mappings позволяют управлять клавиатурой
В Edit->Project Settings во вкладке Input
Добавляем в Bindings следующие Axis Mappings:
MoveForward
- W : Scale 1.0
- S : Scale -1.0 MoveRight
- D : Scale 1.0
- A : Scale -1.0 Turn
- Mouse X : Scale 1.0 LookUp
- Mouse Н : Scale -1.0
Axis Mappings предназначены контроля входных данных в определенном диапаоне.
Например, на джойстике MoveForward может зависеть от наклона контроллера (хотя на клавиатуре это только два значения: нажатие или отпускание кнопки).
В коде FPSCharacter.h:
// Handles input for moving forward and backward.
UFUNCTION()
void MoveForward(float Value);
// Handles input for moving right and left.
UFUNCTION()
void MoveRight(float Value);
// Handles Yaw camera rotation.
UFUNCTION()
void TurnYaw(float Value);
// Handles Pitch camera rotation.
UFUNCTION()
void LookPitch(float Value);
В коде FPSCharacter.cpp:
// Called to bind functionality to input
void AFPSCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
// Set up "movement" bindings.
PlayerInputComponent->BindAxis("MoveForward", this, &AFPSCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AFPSCharacter::MoveRight);
// Set up "look" bindings.
PlayerInputComponent->BindAxis("Turn", this, &AFPSCharacter::TurnYaw);
PlayerInputComponent->BindAxis("LookUp", this, &AFPSCharacter::LookPitch);
}
void AFPSCharacter::TurnYaw(float Value)
{
if (Value != 0.f && Controller && Controller->IsLocalPlayerController())
{
APlayerController* const PC = CastChecked<APlayerController>(Controller);
PC->AddYawInput(Value);
}
}
void AFPSCharacter::LookPitch(float Value)
{
if (Value != 0.f && Controller && Controller->IsLocalPlayerController())
{
APlayerController* const PC = CastChecked<APlayerController>(Controller);
PC->AddPitchInput(Value);
}
}
void AFPSCharacter::MoveForward(float Value)
{
// Find out which way is "forward" and record that the player wants to move that way.
FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::X);
AddMovementInput(Direction, Value);
}
void AFPSCharacter::MoveRight(float Value)
{
// Find out which way is "right" and record that the player wants to move that way.
FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::Y);
AddMovementInput(Direction, Value);
}
Сохраняем все, Компилируем код.
При запуске игры игрок должен уметь двигаться и вращать камерой.
Action Mappings используются для срабатывания дискретных событий влияющих на игровое поведение.
Action Mappings предназначены контроля нажатий и отпускания кнопок.
Подробнее про Action Mappings и Axis Mappings: https://www.unrealengine.com/blog/input-action-and-axis-mappings-in-ue4
Добавим игроку возможность прыгать через Action Mappings в Project Settings -> Input.
Jump
- Space Bar
В FPSCharacter.h:
// Sets jump flag when key is pressed.
UFUNCTION()
void StartJump();
// Clears jump flag when key is released.
UFUNCTION()
void StopJump();
В FPSCharacter.cpp:
// Called to bind functionality to input
void AFPSCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
// Set up "movement" bindings.
PlayerInputComponent->BindAxis("MoveForward", this, &AFPSCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AFPSCharacter::MoveRight);
// Set up "look" bindings.
PlayerInputComponent->BindAxis("Turn", this, &AFPSCharacter::TurnYaw);
PlayerInputComponent->BindAxis("LookUp", this, &AFPSCharacter::LookPitch);
// Set up "action" bindings.
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &AFPSCharacter::StartJump);
PlayerInputComponent->BindAction("Jump", IE_Released, this, &AFPSCharacter::StopJump);
}
void AFPSCharacter::StartJump()
{
bPressedJump = true;
}
void AFPSCharacter::StopJump()
{
bPressedJump = false;
}
Сохраняем все, Компилируем код.
Скачиваем 3D модельку игрока https://docs.unrealengine.com/latest/attachments/Programming/Tutorials/FirstPersonShooter/2/6/GenericMale.zip
Создаем папку ProjectContent/Models/Characters/GenericMale и в нее импортируем fbx модельку.
В BP_FPSCharacter выбираем компонент Mesh
Устанавливаем Skeletal mesh в GenericMale.
Устанавливаем Location в (0;0;-88)
Compile, Save.
Запустите игру в editor's viewport и нажмите F8 чтобы перейти в режим свободной камеры и увидеть 3D модельку со стороны.
В FPSCharacter.h:
// FPS camera.
UPROPERTY(VisibleAnywhere)
UCameraComponent* FPSCameraComponent;
В FPSCharacter.cpp:
// Sets default values
AFPSCharacter::AFPSCharacter()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// Create a first person camera component.
FPSCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
// Attach the camera component to our capsule component.
FPSCameraComponent->SetupAttachment(GetCapsuleComponent());
// Position the camera slightly above the eyes.
FPSCameraComponent->SetRelativeLocation(FVector(0.0f, 0.0f, 50.0f + BaseEyeHeight));
// Allow the pawn to control camera rotation.
FPSCameraComponent->bUsePawnControlRotation = true;
}
Камера теперь должна быть немного выше модели головы игрока.
Добавим модель рук игрока для режима игры от первого лица.
В FPSCharacter.h:
// First-person mesh (arms), visible only to the owning player.
UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
USkeletalMeshComponent* FPSMesh;
FPSCharacter.cpp:
// Sets default values
AFPSCharacter::AFPSCharacter()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// Create a first person camera component.
FPSCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
// Attach the camera component to our capsule component.
FPSCameraComponent->SetupAttachment(GetCapsuleComponent());
// Position the camera slightly above the eyes.
FPSCameraComponent->SetRelativeLocation(FVector(0.0f, 0.0f, 50.0f + BaseEyeHeight));
// Allow the pawn to control camera rotation.
FPSCameraComponent->bUsePawnControlRotation = true;
// Create a first person mesh component for the owning player.
FPSMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FirstPersonMesh"));
// Only the owning player sees this mesh.
FPSMesh->SetOnlyOwnerSee(true);
// Attach the FPS mesh to the FPS camera.
FPSMesh->SetupAttachment(FPSCameraComponent);
// Disable some environmental shadowing to preserve the illusion of having a single mesh.
FPSMesh->bCastDynamicShadow = false;
FPSMesh->CastShadow = false;
// The owning player doesn't see the regular (third-person) body mesh.
GetMesh()->SetOwnerNoSee(true);
}
Скомпилируйте код.
Импортируйте модель HeroFPP в папку /Game/ProjectContent/Models/Characters/GenericMale
В компоненте FPSMesh внутри BP_FPSCharacter
Установите Location в {240, 0, 35} и Rotation в {-180, 50, -180}.
Нажмите Compile и Save.
Добавим игроку возможность стрелять в Project Settings -> Input.
Fire
- Left Mouse Button
Создадим новый класс:
New C++ Class -> Выбираем родительский класс Actor и называем FPSProjectile.
В FPSProjectile.h:
// Function that initializes the projectile's velocity in the shoot direction.
void FireInDirection(const FVector& ShootDirection);
// Sphere collision component.
UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
USphereComponent* CollisionComponent;
// Projectile movement component.
UPROPERTY(VisibleAnywhere, Category = Movement)
UProjectileMovementComponent* ProjectileMovementComponent;
В FPSProjectile.cpp:
// Sets default values
AFPSProjectile::AFPSProjectile()
{
// 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("SphereComponent"));
// Set the sphere's collision radius.
CollisionComponent->InitSphereRadius(15.0f);
// Set the root component to be the collision component.
RootComponent = CollisionComponent;
// Use this component to drive this projectile's movement.
ProjectileMovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovementComponent"));
ProjectileMovementComponent->SetUpdatedComponent(CollisionComponent);
ProjectileMovementComponent->InitialSpeed = 3000.0f;
ProjectileMovementComponent->MaxSpeed = 3000.0f;
ProjectileMovementComponent->bRotationFollowsVelocity = true;
ProjectileMovementComponent->bShouldBounce = true;
ProjectileMovementComponent->Bounciness = 0.3f;
}
// Function that initializes the projectile's velocity in the shoot direction.
void AFPSProjectile::FireInDirection(const FVector& ShootDirection)
{
ProjectileMovementComponent->Velocity = ShootDirection * ProjectileMovementComponent->InitialSpeed;
}
В FPSCharacter.h:
// Function that handles firing projectiles.
UFUNCTION()
void Fire();
// Gun muzzle's offset from the camera location.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
FVector MuzzleOffset;
// Projectile class to spawn.
UPROPERTY(EditDefaultsOnly, Category = Projectile)
TSubclassOf<class AFPSProjectile> ProjectileClass;
В начале FPSCharacter.cpp подключаем:
#include "FPSProjectile.h"
В FPSCharacter.cpp добавляем:
// Called to bind functionality to input
void AFPSCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
// Set up "movement" bindings.
PlayerInputComponent->BindAxis("MoveForward", this, &AFPSCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AFPSCharacter::MoveRight);
// Set up "look" bindings.
PlayerInputComponent->BindAxis("Turn", this, &AFPSCharacter::TurnYaw);
PlayerInputComponent->BindAxis("LookUp", this, &AFPSCharacter::LookPitch);
// Set up "action" bindings.
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &AFPSCharacter::StartJump);
PlayerInputComponent->BindAction("Jump", IE_Released, this, &AFPSCharacter::StopJump);
PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &AFPSCharacter::Fire);
}
void AFPSCharacter::Fire()
{
// Attempt to fire a projectile.
if (ProjectileClass)
{
// Get the camera transform.
FVector CameraLocation;
FRotator CameraRotation;
GetActorEyesViewPoint(CameraLocation, CameraRotation);
// Transform MuzzleOffset from camera space to world space.
FVector MuzzleLocation = CameraLocation + FTransform(CameraRotation).TransformVector(MuzzleOffset);
FRotator MuzzleRotation = CameraRotation;
// Skew the aim to be slightly upwards.
MuzzleRotation.Pitch += 10.0f;
UWorld* World = GetWorld();
if (World)
{
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = this;
SpawnParams.Instigator = Instigator;
// Spawn the projectile at the muzzle.
AFPSProjectile* Projectile = World->SpawnActor<AFPSProjectile>(ProjectileClass, MuzzleLocation, MuzzleRotation, SpawnParams);
if (Projectile)
{
// Set the projectile's initial trajectory.
FVector LaunchDirection = MuzzleRotation.Vector();
Projectile->FireInDirection(LaunchDirection);
}
}
}
}
Компилируем код.
Скачиваем модельку снаряда https://docs.unrealengine.com/latest/attachments/Programming/Tutorials/FirstPersonShooter/3/2/Sphere.zip
Создаем папки ProjectContent/Models/Props/Projectile и импортируем Sphere.fbx
Создаем папку ProjectContent/Blueprints/Projectile и в ней создайте Blueprint Class с именем BP_FPSProjectile на осовании класса FPSProjectile.
Открываем BP_FPSProjectile и выбираем CollisionComponent. Через Add Component к нему добавляем компонент Static Mesh с названием ProjectileMeshComponent.
Устанавливаем в ProjectileMeshComponent пункт Static Mesh в Sphere.
Устанавливаем Scale в (0.09;0.09;0.09)
Устанавливаем Collision Presets в NoCollision поскольку для столкновений используется SphereComponent, а не Static Mesh.
Compile и Save.
Открываем BP_FPSCharacter. Включаем режим Class Defaults Mode т.е. Class Defaults.
Устанавливаем Projectile Class в BP_FPSProjectile
Устанавливаем MuzzleOffset в {100, 0, 0} чтобы сняряд появлялся перед камерой.
Compile и Save
При запуске игрок на ЛКМ должен стрелять снарядами.
В Project Settings -> Collision -> New Object Channel создаем "Projectile" с Default Response равным Block.
В Project Settings -> Collision внутри Preset нажимаем New с названием "Projectile". Устанавливаем Pawn в Overlap, а все остальное в Block. ObjectType ставим равным Projectile. CollisionEnabled ставим равным Collision Enabled (Query and Physics). Description можно поставить как "Projectile collision profile".
Добавляем в FPSProjectile.h:
// Function that is called when the projectile hits something.
UFUNCTION()
void OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComponent, FVector NormalImpulse, const FHitResult& Hit);
Добавляем в FPSProjectile.cpp:
// Sets default values
AFPSProjectile::AFPSProjectile()
{
// 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("SphereComponent"));
// Set the sphere's collision radius.
CollisionComponent->InitSphereRadius(15.0f);
CollisionComponent->BodyInstance.SetCollisionProfileName(TEXT("Projectile"));
CollisionComponent->OnComponentHit.AddDynamic(this, &AFPSProjectile::OnHit);
// Set the root component to be the collision component.
RootComponent = CollisionComponent;
// Use this component to drive this projectile's movement.
ProjectileMovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovementComponent"));
ProjectileMovementComponent->SetUpdatedComponent(CollisionComponent);
ProjectileMovementComponent->InitialSpeed = 3000.0f;
ProjectileMovementComponent->MaxSpeed = 3000.0f;
ProjectileMovementComponent->bRotationFollowsVelocity = true;
ProjectileMovementComponent->bShouldBounce = true;
ProjectileMovementComponent->Bounciness = 0.3f;
// Die after 3 seconds.
InitialLifeSpan = 3.0f;
}
// Function that is called when the projectile hits something.
void AFPSProjectile::OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComponent, FVector NormalImpulse, const FHitResult& Hit)
{
if (OtherActor != this && OtherComponent->IsSimulatingPhysics())
{
OtherComponent->AddImpulseAtLocation(ProjectileMovementComponent->Velocity * 100.0f, Hit.ImpactPoint);
}
}
SetCollisionProfileName устанавливает Collision Profile в созданный нми ранее. InitialLifeSpan ограничивает время существования снаряда 3 секундами.
Скомпилийте код.
Добавьте на сцену статичный меш, например, Floor и придайте ему форму куба. Во вкладке Physics меша включите Simulate Physics. Можете также включить Simulate Physics на стандартных объектах из начальной сцены со столом и двумя стульями.
Откройте BP_FPSProjectile и в режиме Class Defaults Mode и установите ProjectileMeshComponent в Collision Presets значение Collision равным Projectile.
Скомпилируйте и сохраните Blueprint.
Снаряд долен отскакивать от объектов, сдвигать их.
Выберите static mesh в редакторе и создайте на его основе destructible mesh.
Например, создадим на основе SM_Couch уничтожаемый меш SM_Couch_DM.
Откройте меш и нажмите Fracture Mesh
Voronoi ‘Cell Site Count’ регулирует число кусочков на которые разлетится объект.
Измените конструктор в FPSProjectile.cpp:
// Function that is called when the projectile hits something.
void AFPSProjectile::OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComponent, FVector NormalImpulse, const FHitResult& Hit)
{
if (OtherActor != this && OtherComponent->IsSimulatingPhysics())
{
OtherComponent->AddImpulseAtLocation(ProjectileMovementComponent->Velocity * 100.0f, Hit.ImpactPoint);
}
ADestructibleActor* pDestructible(Cast<ADestructibleActor>(OtherActor));
if (OtherActor != this && pDestructible)
{
pDestructible->DestructibleComponent->ApplyRadiusDamage(1000.0f, pDestructible->GetActorLocation(), 100.0f, 500.0f, true);
pDestructible->DestructibleComponent->ApplyDamage(1000.0f, pDestructible->GetActorLocation(), pDestructible->GetActorLocation(), 1.0f);
pDestructible->DestructibleComponent->WakeAllRigidBodies();
pDestructible->DestructibleComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
}
ApplyRadiusDamage - Hurt locally authoritative actors within the radius. Will only hit components that block the Visibility channel. ApplyDamage - Hurts the specified actor with generic damage. Target is Gameplay Statics WakeAllRigidBodies - Ensure simulation is running for all bodies in this component. Target is Primitive Component SetCollisionEnabled(ECollisionEnabled::NoCollision) - отключает столкновения после разрушения объекта.
Скомпилируйте код, перетащите SM_Couch_DM в игру и выстрелите по нему, меш должен разлететься на кусочки.
Вы также можете удалить объект сразу или через некоторое время после разрушения:
pDestructible->Destroy(); // Мнгновенно уничтожает
pDestructible->SetLifeSpan(6); // Уничтожает с задержкой в 6 секунд
см. также:
- Unreal Engine 4.X By Example, 240 стр.
- Experimenting with PhysX and APEX Destruction
- http://monsho.blog63.fc2.com/blog-entry-126.html
Внимание: Если размер текстуры не является степенью 2 (NPOT) она не будет применен Texture Streaming или генерироваться mipmap.
Создайте папку ProjectContent\Textures\Crosshairs и импортируйте crosshair.TGA
Создайте через New C++ Class новый класс с родительским классом HUD и названием FPSHUD
Добавьте в FPSHUD.h:
// This will be drawn at the center of the screen.
UPROPERTY(EditDefaultsOnly)
UTexture2D* CrosshairTexture;
// Primary draw call for the HUD.
virtual void DrawHUD() override;
Добавьте в FPSHUD.cpp:
void AFPSHUD::DrawHUD()
{
Super::DrawHUD();
if (CrosshairTexture)
{
// Find the center of our canvas.
FVector2D Center(Canvas->ClipX * 0.5f, Canvas->ClipY * 0.5f);
// Offset by half of the texture's dimensions so that the center of the texture aligns with the center of the Canvas.
FVector2D CrossHairDrawPosition(Center.X - (CrosshairTexture->GetSurfaceWidth() * 0.5f), Center.Y - (CrosshairTexture->GetSurfaceHeight() * 0.5f));
// Draw the crosshair at the centerpoint.
FCanvasTileItem TileItem(CrossHairDrawPosition, CrosshairTexture->Resource, FLinearColor::White);
TileItem.BlendMode = SE_BLEND_Translucent;
Canvas->DrawItem(TileItem);
}
}
Скомпилируйте код.
Создайте папку ProjectContent\Blueprints\HUD
Создайте Blueprint класс на основании FPSHUD (Create Blueprint class based on FPSHUD) и назовите его BP_FPSHUD, сохраните его в папке ProjectContent\Blueprints\HUD.
Откройте BP_FPSHUD и установите значение crosshair texture в crosshair.
Скомпилируйте и сохраните созданный Blueprint.
Project Settings -> Maps & Modes установите HUD в BP_FPSHUD
Скачайте анимации https://docs.unrealengine.com/latest/attachments/Programming/Tutorials/FirstPersonShooter/4/1/FPP_Animations.zip
- FPP_Idle.FBX
- FPP_JumpEnd.FBX
- FPP_JumpLoop.FBX
- FPP_JumpStart.FBX
- FPP_Run.FBX
Создайте папку ProjectContent\Animations и импортируйте в нее анимации выбрав Skeleton равным HeroFPP_Skeleton (Используя Ctrl в проводнике можно выделить сразу несколько файлов для импорта и нажать Import All).
Подробнее про импортирование можно узнать на https://docs.unrealengine.com/latest/INT/Engine/Content/ImportingContent/
Сохраните все.
Используя Add New создайте Animation Blueprint с родительским классом AnimInstance и названием Arms_AnimBP.
Откройте Arms_AnimBP и добавьте переменные IsRunning, IsFalling типа Boolean
Откройте EventGraph.
Добавьте Event Blueprint Update Animation если он отсутствует.
Добавьте "Try Get Pawn Owner"
Добавьте "Cast to Character", "Get Character Movement", "Get Movement Mode", "Equal (Enum)", "Get Velocity", "Vector Length". Если не находит по названию, то измените галочку Context Sensetive.
Установите после сединения Equal (Enum) в Falling.
Зажав Alt и перетащив IsFalling на graph создайте Set Is Falling.
Используя поиск по символу > выберите float > float
Зажав Alt и перетащив IsRunning на graph создайте Set Is Running.
Добавьте State Machine: State Machines > Add New State Machine с названием "Arms State Machine" и соедините как на фото:
Нажмите дважды на "Arms State Machine" для редактирования его графа.
Нажмите Add State.
Добавьте пять State:
- Idle
- Run
- JumpStart
- JumpEnd
- JumpLoop
Нажмите дважды на Idle для редактирования его графа.
Добавьте "Play FPP_Idle" и соедините как на фото:
Аналогично добавьте для Run "Play FPP_Run". Аналогично добавьте для оставшихся State соответствующий Play.
Соедините Entry, Idle и Run как на фото:
Дважды нажмите на транзакции (стрелка) от Idle до Run.
Зажав Ctrl и перетащив IsRunning на graph создайте Get Is Running и соедините как на фото:
Аналогично соедините от Run до Idle и откройте созданую транзакцию.
Аналогично перетащите Get Is Running.
Добавьте Not Boolean и соедините как на фото:
Добавьте транзацию от Idle до JumpStart и откройте ее.
Аналогично перетащите IsFalling и соедините как на фото:
Добавьте транзацию от Run до JumpStart и откройте ее.
Аналогично перетащите IsFalling и соедините как на фото:
Добавьте транзацию от JumpStart до JumpLoop и откройте ее.
Добавьте "Time Remaining" и "<=" типа float с значением 0.1.
Соедините как на фото:
Добавьте транзацию от JumpLoop до JumpEnd и откройте ее.
Добавьте "Get Is Falling" и "Not Boolean".
Соедините как на фото:
Добавьте транзацию от JumpEnd до Idle и откройте ее.
Добавьте TimeRemaining для 'FPP_JumpEnd' и <= типа float со значением 0.1.
Соедините как на фото:
Скомпилируйте и сохраните Arms_AnimBP.
Откройте BP_FPSCharacter и выберите FPSMesh установив его AnimationBlueprint в Arms_AnimBP.
В режиме Defaults измените:
- Location в {50, -15, -150}
- Rotation в {0, 25, 350}.
Compile и save.
Запустите игру и сравните анимации в зависимости от движения персонажа.
Добавим игроку возможность бегать через Action Mappings в Project Settings -> Input.
Sprint
- Left Shift
В конструкторе FPS_Character.h добавляем:
UPROPERTY(EditAnyWhere)
float RunSpeed = 800.0f;
UPROPERTY(EditAnyWhere)
float WalkSpeed = 250.0f;
UFUNCTION()
void StartSprint();
UFUNCTION()
void StopSprint();
В конструкторе FPS_Character.cpp добавляем:
PlayerInputComponent->BindAction("Sprint", IE_Pressed, this, &AFPSCharacter::StartSprint);
PlayerInputComponent->BindAction("Sprint", IE_Released, this, &AFPSCharacter::StopSprint);
GetCharacterMovement()->MaxWalkSpeed = WalkSpeed;
Также в FPS_Character.cpp добавляем функции:
void AFPSCharacter::StartSprint()
{
GetCharacterMovement()->MaxWalkSpeed = RunSpeed;
}
void AFPSCharacter::StopSprint()
{
GetCharacterMovement()->MaxWalkSpeed = WalkSpeed;
}
см. также: