Hey there!
So I’m new to Unreal Engine, and I’m currently trying to make a TPS (third-person shooter) tech demo. I managed to get projectiles firing from my weapon, however the problem is they don’t currently fire where my crosshair is aiming, They’re just a little off.
Red: Where my crosshair Hits
Blue: where my projectile hits
Green is where I want my projectile to hit.
For the player controlled character, they can technically compensate by aiming over more. Problem is for AI it shoots to the right of my pawn:
Now, in my code using the line trace (from the camera, and from the AI controller) and using it’s impact point works for drawing the green line. IE: This is where I want the projectile to launch towads:
// Fill out your copyright notice in the Description page of Project Settings.
#include "Gun.h"
#include "Components/SkeletalMeshComponent.h"
#include "Kismet/GameplayStatics.h"
#include "DrawDebugHelpers.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Kismet/KismetMathLibrary.h"
// Sets default values
AGun::AGun()
{
// 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;
Root = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
SetRootComponent(Root);
Mesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Mesh"));
Mesh->SetupAttachment(Root);
ProjectileSpawnPoint = CreateDefaultSubobject<USceneComponent>(TEXT("Projectile Spawn Point"));
ProjectileSpawnPoint->SetupAttachment(Mesh);
Ammo = MaxAmmo;
}
void AGun::PullTrigger()
{
if (Ammo > 0)
{
UGameplayStatics::SpawnEmitterAttached(MuzzleFlash, Mesh, TEXT("MuzzleFlashSocket"));
UGameplayStatics::SpawnSoundAttached(MuzzleSound, Mesh, TEXT("MuzzleFlashSocket"));
if (ProjectileClass)
{
FHitResult Hit;
FVector ShotDirection;
bool bSuccess = GunTrace(Hit, ShotDirection);
if (bSuccess)
{
FVector Location = ProjectileSpawnPoint->GetComponentLocation();
FRotator Rotation = ProjectileSpawnPoint->GetComponentRotation() + ShotDirection.Rotation();
//FRotator Rotation = ProjectileSpawnPoint->GetComponentRotation();
//FVector Direction = Location - ShotDirection;
FActorSpawnParameters Params;
Params.Owner = this;
Params.Instigator = GetInstigator();
AProjectileBase* Projectile = GetWorld()->SpawnActor<AProjectileBase>(ProjectileClass, Location,Rotation,Params );
if (Projectile)
{
Projectile->FireInDirection(-ShotDirection);
//Projectile->FireInDirection(Hit.Location);
}
Ammo -= 1;
}
}
}
else
{
UGameplayStatics::SpawnSoundAttached(EmtpySound, Mesh, TEXT("MuzzleFlashSocet"));
UGameplayStatics::SpawnEmitterAttached(EmptyMuzzleFlash, Mesh, TEXT("MuzzleFlashSocket"));
}
}
int AGun::GetAmmo()
{
return Ammo;
}
// Called when the game starts or when spawned
void AGun::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AGun::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
AController *OwnerController = GetOwnerController();
//if (!OwnerController) return false;
FVector PlayerLocation;
FRotator PlayerRotation;
OwnerController->GetPlayerViewPoint(PlayerLocation,PlayerRotation);
FVector End = PlayerLocation + PlayerRotation.Vector() * MaxRange;
DrawDebugLine(
GetWorld(),
PlayerLocation,
End,
FColor::Red,
false,
0,
0,
5.f
);
FHitResult WorldHit;
FVector ProjVec = ProjectileSpawnPoint->GetComponentLocation();
FRotator ProjRot = ProjectileSpawnPoint->GetComponentRotation();
FVector EndProj = ProjVec + ProjRot.Vector() * MaxRange;
FCollisionQueryParams Params;
Params.AddIgnoredActor(this);
Params.AddIgnoredActor(GetOwner());
GetWorld()->LineTraceSingleByChannel(WorldHit, PlayerLocation, End, ECollisionChannel::ECC_GameTraceChannel1, Params);
DrawDebugLine(
GetWorld(),
ProjVec,
WorldHit.ImpactPoint,
FColor::Green,
false,
0,
0,
5.f
);
DrawDebugLine(
GetWorld(),
ProjVec,
End,
FColor::Blue,
false,
0,
0,
5.f
);
}
AController* AGun::GetOwnerController() const
{
APawn* OwnerPawn = Cast<APawn>(GetOwner());
if (OwnerPawn == nullptr) return nullptr;
return OwnerPawn->GetController();
}
bool AGun::GunTrace(FHitResult& Hit, FVector& ShotDirection)
{
AController *OwnerController = GetOwnerController();
if (!OwnerController) return false;
FVector PlayerLocation;
FRotator PlayerRotation;
OwnerController->GetPlayerViewPoint(PlayerLocation,PlayerRotation);
ShotDirection = -PlayerRotation.Vector();
FVector End = PlayerLocation + PlayerRotation.Vector() * MaxRange;
FCollisionQueryParams Params;
Params.AddIgnoredActor(this);
Params.AddIgnoredActor(GetOwner());
return GetWorld()->LineTraceSingleByChannel(Hit, PlayerLocation, End, ECollisionChannel::ECC_GameTraceChannel1, Params);
}
However, in the section where the projectile is initiated I have to currently use my old firing system(blue line), as when I try to use the Hit.ImpactPoint, Hit.Location etc in the FireInDirection call, the projectile explodes right at the spawn point. The spawn point is ahead of the barrel (where blue and green start).
Code for projectile fire event:
void AProjectileBase::FireInDirection(const FVector& ShootDirection)
{
ProjectileMovement->Velocity = ShootDirection * ProjectileMovement->InitialSpeed;
}
This has been driving me bonkers for a few days now.