根据UE4官方文档实现一个FPS游戏

1 设置项目并添加打印日志的功能

在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。
根据UE4官方文档实现一个FPS游戏_第1张图片
至此,启动游戏就能看到打印出来的信息了。

2 添加人物及模型并加入控制功能

根据UE4官方文档实现一个FPS游戏_第2张图片
用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;
}

3 导入骨骼模型并加入到创建的人物蓝图类中

根据UE4官方文档实现一个FPS游戏_第3张图片
由于第一视角只需要看到双手,所以在相机下面再添加了手的模型。组件添加代码:

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);
}

4 添加发射子弹的功能

创建一个子弹类

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

你可能感兴趣的:(UE4)