UEC++学习(六)之 武器发射射线,发射子弹,造成伤害

目录

枪口发射调试射线

发射子弹

子弹造成伤害


枪口发射调试射线

在武器类中

GetSocketTransform(socketname):通过插槽名获取transform

SocketTransform.GetLocation():获取变换的location

SocketTransform.GetRotation().GetForwardVector():获取向前向量

DrawDebugLine:调试射线:参数;World,开始位置,结束位置,颜色,判断射线是否永久绘制(默认false),存在时间,绘制深度的顺序,粗细 

FHitResult:用于接收检测信息的结构体

GetWorld()->LineTraceSingleByChannel():射线检测:参数:包含交叉点的信息,开始位置,结束位置,需要检测的碰撞类型

#include "Engine/World.h"
#include "DrawDebugHelpers.h"


void ASTUBaseWeapon::MakeShot()
{
	if (!GetWorld())
	return;

	const FTransform SocketTransform = WeaponMesh->GetSocketTransform(MuzzleSocketName);
	//起始位置
	const FVector TraceStart = SocketTransform.GetLocation();
	//发射方向
	const FVector ShootDirection = SocketTransform.GetRotation().GetForwardVector();
	//终点位置
	const FVector TraceEnd = TraceStart + ShootDirection * TraceMaxDistance;
	
	//绘制调试射线 参数:World,开始位置,结束位置,颜色,判断射线是否永久绘制(默认false),存在时间,绘制深度的顺序,粗细 
	DrawDebugLine(GetWorld(),TraceStart,TraceEnd,FColor::Red,false,3.0f,0,3.0f);

	
	FHitResult HitResult;
	FCollisionQueryParams CollisionParams;
	//添加射线检测忽略对象
	CollisionParams.AddIgnoredActor(Player);

	//射线检测 参数:包含交叉点的信息,开始位置,结束位置,需要检测的碰撞类型
	GetWorld()->LineTraceSingleByChannel(HitResult, TraceStart, TraceEnd, ECollisionChannel::ECC_Visibility,CollisionParams);

	//有检测到
	if (HitResult.bBlockingHit)
	{
		//绘制球形调试
		DrawDebugSphere(GetWorld(), HitResult.ImpactPoint, 10.0f, 24, FColor::Red, false, 3.0f);
	}



}

        将碰撞类型修改为Visibility可被阻挡(因为代码中设置的是这个类型,可以自己修改成不同的),

武器添加插槽,设置旋转

效果如下:

枪口发射射线

        但是上面实现的方法还有问题,会导致射线打中角色之后还是会穿透过去,以及当相机视角向下时,射线依旧是从枪口向前发射,所以需要进行修改

        需要使用相机的Transform来替换枪口的Transform,重新进行射线检测

Controller->GetPlayerViewPoint();输出摄像机检测到的位置
    FMath::DegreesToRadians():角度转弧度

    FMath::VRandCone(Dir,halfAngle): 在一个圆锥中获得方向,可以用于实现因为后坐力导致弹道的偏移

    FCollisionQueryParams CollisionParams;
    CollisionParams.AddIgnoredActor(Player);添加忽略对象

#include "Engine/World.h"
#include "DrawDebugHelpers.h"


void ASTUBaseWeapon::MakeShot()
{
	if (!GetWorld())
	return;

	const ACharacter* Player = Cast<ACharacter>(GetOwner());
	if (!Player)
	return;

	const AController* Controller = Player->GetController<APlayerController>();
	if (!Controller)
	return;	

	
	FVector ViewLocation;
	FRotator ViewRotation;
	//输出摄像机检测到的loaction,rotation
	Controller->GetPlayerViewPoint(ViewLocation,ViewRotation);


	const FTransform SocketTransform = WeaponMesh->GetSocketTransform(MuzzleSocketName);
	//位置修改成相机的
	//起始位置
	const FVector TraceStart = ViewLocation;				//SocketTransform.GetLocation();
	//发射方向
	//FMath::VRandCone 在一个圆锥中获得方向
	const float HalfRad = FMath::DegreesToRadians(1.5f);
	const FVector ShootDirection = FMath::VRandCone(ViewRotation.Vector(),HalfRad);//SocketTransform.GetRotation().GetForwardVector();
	//终点位置
	const FVector TraceEnd = TraceStart + ShootDirection * TraceMaxDistance;
	


	//通过摄像机的旋转来检测
	FHitResult HitResult;
	FCollisionQueryParams CollisionParams;
	//添加射线检测忽略对象
	CollisionParams.AddIgnoredActor(Player);

	//射线检测 参数:包含交叉点的信息,开始位置,结束位置,需要检测的碰撞类型
	GetWorld()->LineTraceSingleByChannel(HitResult, TraceStart, TraceEnd, ECollisionChannel::ECC_Visibility,CollisionParams);

	//有检测到
	if (HitResult.bBlockingHit)
	{
		//绘制一条枪口到检测点的射线
		//绘制调试射线 参数:World,开始位置,结束位置,颜色,判断射线是否永久绘制(默认false),存在时间,绘制深度的顺序,粗细 
		DrawDebugLine(GetWorld(), SocketTransform.GetLocation(), HitResult.ImpactPoint, FColor::Red, false, 3.0f, 0, 3.0f);

		//绘制球形调试
		DrawDebugSphere(GetWorld(), HitResult.ImpactPoint, 10.0f, 24, FColor::Red, false, 3.0f);
	}
	else
	{
		//绘制一条枪口到终点的射线
		//绘制调试射线 参数:World,开始位置,结束位置,颜色,判断射线是否永久绘制(默认false),存在时间,绘制深度的顺序,粗细 
		DrawDebugLine(GetWorld(), SocketTransform.GetLocation(), TraceEnd, FColor::Red, false, 3.0f, 0, 3.0f);
	}


}

修改后:

修改后的射线检测

发射子弹

对上面的代码进行整理,屏蔽掉调试射线

创建一个子弹类 TSubclassof<>()

	class ASTUProjectile;

    UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
	TSubclassOf<ASTUProjectile> ProjectileClass;

实现逻辑:

GetSafeNormal():归一化,获得单位向量

生成Actor的函数:

GetWorld()->SpawnActor<>():只适用于默认构造函数的情况,不能传参数

GetWorld()->SpawnActorDeferred<>()与FinishSpawning()搭配,可以在执行Actor的BeginPlay()前执行Actor的函数或者传参

UGameplayStatics::BeginDeferredActorSpawnFromClass()与UGameplayStatics::FinishSpawningActor()搭配,也是可以用于传参数

#include "Engine/World.h"
#include "DrawDebugHelpers.h"
#include "Kismet/GameplayStatics.h"

void ASTUBaseWeapon::MakeShot()
{
	if (!GetWorld())
	return;

	const ACharacter* Player = Cast<ACharacter>(GetOwner());
	if (!Player)
	return;

	const AController* Controller = Player->GetController<APlayerController>();
	if (!Controller)
	return;	

	
	FVector ViewLocation;
	FRotator ViewRotation;
	//输出摄像机检测到的loaction,rotation
	Controller->GetPlayerViewPoint(ViewLocation,ViewRotation);


	const FTransform SocketTransform = WeaponMesh->GetSocketTransform(MuzzleSocketName);
	//位置修改成相机的
	//起始位置
	const FVector TraceStart = ViewLocation;				//SocketTransform.GetLocation();
	//发射方向
	//FMath::VRandCone 在一个圆锥中获得方向
	const float HalfRad = FMath::DegreesToRadians(1.5f);
	const FVector ShootDirection = FMath::VRandCone(ViewRotation.Vector(),HalfRad);//SocketTransform.GetRotation().GetForwardVector();
	//终点位置
	const FVector TraceEnd = TraceStart + ShootDirection * TraceMaxDistance;
	


	//通过摄像机的旋转来检测
	FHitResult HitResult;
	FCollisionQueryParams CollisionParams;
	//添加射线检测忽略对象
	CollisionParams.AddIgnoredActor(Player);
	//射线检测 参数:包含交叉点的信息,开始位置,结束位置,需要检测的碰撞类型
	GetWorld()->LineTraceSingleByChannel(HitResult, TraceStart, TraceEnd, ECollisionChannel::ECC_Visibility,CollisionParams);


    //发射子弹
    //计算子弹的终点位置和方向
    const FVector EndPoint = HitResult.bBlockingHit ? HitResult.ImpactPoint : TraceEnd;
	const FVector Direction = (EndPoint - SocketTransform).GetSafeNormal();

    //子弹的生成位置(枪口)
	const FTransform SpawnTransform = FTransform(FRotator::ZeroRotator, SocketTransform);

	//ASTUProjectile* Projectile = GetWorld()->SpawnActor<ASTUProjectile>(ProjectileClass, SpawnTransform);

	//ASTUProjectile* Projectile = GetWorld()->SpawnActorDeferred<ASTUProjectile>(ProjectileClass, SpawnTransform);
	//Projectile->FinishSpawning(SpawnTransform);

    //生成子弹
	ASTUProjectile* Projectile = Cast<ASTUProjectile>(UGameplayStatics::BeginDeferredActorSpawnFromClass(GetWorld(), ProjectileClass, SpawnTransform));

	if (Projectile)
	{
        //将方向传递给子弹类
		Projectile->SetShotDirection(Direction);
        
		UGameplayStatics::FinishSpawningActor(Projectile, SpawnTransform);
	}

}

在子弹类中创建一个球形碰撞体USphereComponent以及物理模拟组件UProjectileMovementComponent

	class USphereComponent;
    class UProjectileMovementComponent;


	void SetShotDirection(const FVector& Direction) { ShotDirection = Direction; };

	UPROPERTY(VisibleDefaultsOnly, Category = "Weapon")
		USphereComponent* CollisionComponent;

	UPROPERTY(VisibleDefaultsOnly, Category = "Weapon")
		UProjectileMovementComponent* ProjectileMovementComponent;

	FVector ShotDirection;

设置子弹的发射

ASTUProjectile::ASTUProjectile()
{
 	PrimaryActorTick.bCanEverTick = false;

	CollisionComponent = CreateDefaultSubobject<USphereComponent>("CollisionComponent");
	//初始碰撞体的半径
	CollisionComponent->InitSphereRadius(5.0f);
	SetRootComponent(CollisionComponent);

	ProjectileMovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>("ProjectileMovementComponent");
}

void ASTUProjectile::BeginPlay()
{
	Super::BeginPlay();
	check(ProjectileMovementComponent);

	ProjectileMovementComponent->Velocity = ShotDirection * ProjectileMovementComponent->InitialSpeed;
	SetLifeSpan(5.0f);
}

在蓝图中设置速度和重力影响

也可以通过代码控制

	ProjectileMovementComponent->InitialSpeed = 1000.0f;
	ProjectileMovementComponent->ProjectileGravityScale = 0.0f;

效果:

发射子弹

子弹造成伤害

设置子弹碰撞体的碰撞

创建一些变量方便来蓝图修改

	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Weapon",meta =(ClampMin= "0"))
		float DamageRadius = 200.0f;
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Weapon",meta = (ClampMin = "0"))
		float DamageAmount = 50.0f;
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Weapon")
		bool DoFullDamage = false;
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Weapon", meta = (ClampMin = "0"))
		float LifeSeconds = 5.0f;


	UFUNCTION()
		void OnProjectileHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);

SetCollisionEnabled(ECollisionEnabled::类型):设置碰撞启用类型

SetCollisionResponseToAllChannels(ECollisionResponse::类型):设置所有通道的碰撞响应类型

IgnoreActorWhenMoving():添加子弹在飞行过程中的忽略对象,这里是为了防止对角色自身造成伤害

OnComponentHit.AddDynamic():订阅 委托,在碰撞体中有许多的委托

StopMovementImmediately():命中目标后停止移动

UGameplayStatics::ApplyRadialDamage():范围伤害接口,还有PointDamage,ApplyDamage等

DrawDebugSphere():绘制调试球形

#include "Components/SphereComponent.h"
#include "Gameframework/ProjectileMovementComponent.h"
#include "Kismet/GameplayStatics.h"
#include "DrawDebugHelpers.h"


ASTUProjectile::ASTUProjectile()
{
 	PrimaryActorTick.bCanEverTick = false;

	CollisionComponent = CreateDefaultSubobject<USphereComponent>("CollisionComponent");
	//初始碰撞体的半径
	CollisionComponent->InitSphereRadius(5.0f);

	//设置碰撞启用为纯扫描
	CollisionComponent->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
	//设置获取所有通道的碰撞响应为阻挡
	CollisionComponent->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Block);

	SetRootComponent(CollisionComponent);

	ProjectileMovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>("ProjectileMovementComponent");
	ProjectileMovementComponent->InitialSpeed = 2000.0f;
	ProjectileMovementComponent->ProjectileGravityScale = 0.0f;

}

void ASTUProjectile::BeginPlay()
{
	Super::BeginPlay();
	check(ProjectileMovementComponent);
	check(CollisionComponent);


	ProjectileMovementComponent->Velocity = ShotDirection * ProjectileMovementComponent->InitialSpeed;

	//忽略owner
	CollisionComponent->IgnoreActorWhenMoving(GetOwner(),true);
	//订阅委托,命中目标
	CollisionComponent->OnComponentHit.AddDynamic(this, &ASTUProjectile::OnProjectileHit);

	SetLifeSpan(LifeSeconds);
}

void ASTUProjectile::OnProjectileHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{

	if (!GetWorld())
	{
		return;
	}

	//停止移动
	ProjectileMovementComponent->StopMovementImmediately();

	//范围伤害接口
	UGameplayStatics::ApplyRadialDamage
	(	GetWorld(),
		DamageAmount, 
		GetActorLocation(),
		DamageRadius, 
		UDamageType::StaticClass(), 
		{ GetOwner() }, 
		this, 
		GetController(), 
		DoFullDamage);

	//在击中点绘制一个球形
	DrawDebugSphere(GetWorld(), GetActorLocation(), DamageRadius, 24, FColor::Red, false, 3.0f);
}

AController* ASTUProjectile::GetController()
{
	APawn* Pawn = Cast<APawn>(GetOwner());
	return Pawn ? Pawn->GetController() : nullptr;
}

对应在蓝图中的函数

SetCollisionEnabled:

OnComponentHit.AddDynamic:

实现效果:

子弹造成伤害

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值