在XXXXGameModeBase.h中添加一个要重载的虚函数StartPlay,并在相应的.cpp文件中实现。代码如下:
void AMyFPSGameProjectGameModeBase::StartPlay()
{
Super::StartPlay();
/* Global engine pointer. Can be 0 so don't use without checking. */
if (GEngine)
{
// The -1 "Key" value indicates that we will never need to update or refresh this message
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, TEXT("Hello World, this is MyFPSGameMode!"));
}
}
函数的功能就是简单地在屏幕上打印出一条文本信息。打印函数的原型为:
void UEngine::AddOnScreenDebugMessage(uint64 Key, float TimeToDisplay, FColor DisplayColor, const FString& DebugMessage, bool bNewerOnTop, const FVector2D& TextScale);
// @param Key: A unique key to prevent the same message from being added multiple times.
添加功能后编译,但是启动游戏后并不能看到日志信息在屏幕上打印出来,还需要根据此GameModeBase类来创建一个蓝图类,并在项目设置中将新创建的蓝图类设置为默认的Game Mode。
至此,启动游戏就能看到打印出来的信息了。
用WSAD来控制前后左右走动,根据鼠标的位置来控制相机的旋转,具体代码实现:
void AMyFPSCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAxis("MoveForward", this, &AMyFPSCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AMyFPSCharacter::MoveRight);
PlayerInputComponent->BindAxis("Turn", this, &AMyFPSCharacter::AddControllerYawInput);
PlayerInputComponent->BindAxis("LookUp", this, &AMyFPSCharacter::AddControllerPitchInput);
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &AMyFPSCharacter::StartJump);
PlayerInputComponent->BindAction("Jump", IE_Released, this, &AMyFPSCharacter::StopJump);
}
void AMyFPSCharacter::MoveForward(float AxisValue)
{
// Controller currently possessing this Actor
FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::X);
AddMovementInput(Direction, AxisValue);
}
void AMyFPSCharacter::MoveRight(float AxisValue)
{
FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::Y);
AddMovementInput(Direction, AxisValue);
}
void AMyFPSCharacter::StartJump()
{
// bPressedJump 是基类中就定义好的用来控制跳的变量
bPressedJump = true;
}
void AMyFPSCharacter::StopJump()
{
bPressedJump = false;
}
由于第一视角只需要看到双手,所以在相机下面再添加了手的模型。组件添加代码:
AMyFPSCharacter::AMyFPSCharacter()
{
// 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;
MyFPSCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("MyCamera"));
MyFPSCameraComponent->SetupAttachment((USceneComponent*)GetCapsuleComponent());
MyFPSCameraComponent->SetRelativeLocation(FVector(0.0f, 0.0f, 50.0f + BaseEyeHeight));
// Allow the pawn to control camera rotation
MyFPSCameraComponent->bUsePawnControlRotation = true;
MyFPSMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FirstPersonMesh"));
MyFPSMesh->SetOnlyOwnerSee(true);
MyFPSMesh->SetupAttachment(MyFPSCameraComponent);
// 禁用环境阴影
MyFPSMesh->bCastDynamicShadow = false;
MyFPSMesh->CastShadow = false;
// 设置玩家看不见身体模型(?没看到效果) GetMesh返回绑定在character类中的骨骼模型
GetMesh()->SetOwnerNoSee(true);
}
创建一个子弹类
UCLASS()
class MYFPSGAMEPROJECT_API AMyFPSProjectile : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyFPSProjectile();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
void FireInDirection(const FVector& ShootDirection);
UPROPERTY(VisibleDefaultsOnly, Category = "Projectile")
USphereComponent* CollisionComponent;
UPROPERTY(VisibleAnywhere, Category = "Movement")
UProjectileMovementComponent* ProjectileMovementComponent;
// Function that is called when the projectile hits something
UFUNCTION()
void OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComponent, FVector NormalImpuse, const FHitResult& Hit);
};
AMyFPSProjectile::AMyFPSProjectile()
{
// 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;
CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
CollisionComponent->InitSphereRadius(15.0f);
CollisionComponent->BodyInstance.SetCollisionProfileName(TEXT("Projectile"));
// 注册碰撞函数
CollisionComponent->OnComponentHit.AddDynamic(this, &AMyFPSProjectile::OnHit);
RootComponent = CollisionComponent;
ProjectileMovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovementComponent"));
ProjectileMovementComponent->SetUpdatedComponent(CollisionComponent);
ProjectileMovementComponent->InitialSpeed = 4000.0f;
ProjectileMovementComponent->MaxSpeed = 4000.0f;
ProjectileMovementComponent->bRotationFollowsVelocity = true;
ProjectileMovementComponent->bShouldBounce = true;
ProjectileMovementComponent->Bounciness = 0.3f;
InitialLifeSpan = 3.0f; // 此处需注意对应的蓝图类中是否作出改动,如果没有需要手动设置
}
void AMyFPSProjectile::FireInDirection(const FVector & ShootDirection)
{
// 给子弹的移动组件一个初始速率向量
ProjectileMovementComponent->Velocity = ShootDirection * ProjectileMovementComponent->InitialSpeed;
}
void AMyFPSProjectile::OnHit(UPrimitiveComponent * HitComponent, AActor * OtherActor, UPrimitiveComponent * OtherComponent, FVector NormalImpuse, const FHitResult & Hit)
{
if (OtherActor != this && OtherComponent->IsSimulatingPhysics())
{
/* AddImpulseAtLocation
* Add an impulse to a single rigid body at a specific location.
*
* @param Impulse Magnitude and direction of impulse to apply.
* @param Location Point in world space to apply impulse at.
* @param BoneName If a SkeletalMeshComponent, name of bone to apply impulse to. 'None' indicates root body.
*/
OtherComponent->AddImpulseAtLocation(ProjectileMovementComponent->Velocity * 100.0f, Hit.ImpactPoint); // 子弹击中其它物体时给它添加一个力的作用
}
}
在人物类中添加发射子弹的功能
void AMyFPSCharacter::Fire()
{
if (ProjectileClass)
{
FVector CameraLocation;
FRotator CameraRotation;
GetActorEyesViewPoint(CameraLocation, CameraRotation);
// 获取相机的位置再加上相应的偏移量作为子弹的起始点
FVector MuzzleLocation = CameraLocation + FTransform(CameraRotation).TransformVector(MuzzleOffset);
FRotator MuzzleRotation = CameraRotation;
MuzzleRotation.Pitch += 10.0f;
UWorld* World = GetWorld();
if (World)
{
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = this;
SpawnParams.Instigator = Instigator;
AMyFPSProjectile* Projectile = World->SpawnActor<AMyFPSProjectile>(ProjectileClass, MuzzleLocation, MuzzleRotation, SpawnParams);
if (Projectile)
{
FVector launchDirection = MuzzleRotation.Vector();
Projectile->FireInDirection(launchDirection);
}
}
}
}
完整项目代码:https://github.com/PoorMonk/FPSGame