Constructing a projectile

UPDATE: I’ve created a tutorial on how to construct a projectile in C++, based on the information in this thread. You can find it here: Link to tutorial

I’ve tried created a projectile which can fire from the ship in the flying template, but I’ve run into a wall and don’t know how to resolve the issue.

This function is identical to that in the first person shooter template, but for some reason projectile always returns null:

    void AFirstpersonCharacter::OnFire()
    {
    	// try and fire a projectile
    	if (ProjectileClass != NULL)
    	{
    		// Get the camera transform
    		FVector CameraLoc;
    		FRotator CameraRot;
    		GetActorEyesViewPoint(CameraLoc, CameraRot);
    
    		// MuzzleOffset is in camera space, so transform it to world space before offsetting from the camera to find the final muzzle position
    		FVector const MuzzleLocation = CameraLoc + FTransform(CameraRot).TransformVector(MuzzleOffset);
    		FRotator MuzzleRotation = CameraRot;
    		MuzzleRotation.Pitch += 10.0f;			// skew the aim upwards a bit
    
    		UWorld* const World = GetWorld();
    		if (World)
    		{
    			FActorSpawnParameters SpawnParams;
    			SpawnParams.Owner = this;
    			SpawnParams.Instigator = Instigator;
    
    			// spawn the projectile at the muzzle
    			AFirstpersonProjectile* const Projectile = World->SpawnActor(ProjectileClass, MuzzleLocation, MuzzleRotation, SpawnParams);
    			if (Projectile)
    			{
    				// find launch direction
    				FVector const LaunchDir = MuzzleRotation.Vector();
    				Projectile->InitVelocity(LaunchDir);
    			}
    		}
    	}


When it goes to check if a projectile exists, it skips over the rest of the function past the if statement:


        			// spawn the projectile at the muzzle
        			AFirstpersonProjectile* const Projectile = World->SpawnActor(ProjectileClass, MuzzleLocation, MuzzleRotation, SpawnParams);
        			if (Projectile)
        			{
        				// find launch direction
        				FVector const LaunchDir = MuzzleRotation.Vector();
        				Projectile->InitVelocity(LaunchDir);
        			}

What I can’t understand however, is that all of the parameters return values that look correct, and look nearly identical to that in the First Person Shooter.

The Muzzle location and rotation are using the camera’s at the moments (fine), The projectile class returns FirstPerson Projectile (fine), and SpawnParams are using those of the ship (fine).

I can give more details if needed, but I didn’t want to overwhelm this post with too much information. I’m trying to understand how spawning, and spawning of projectiles in particular, work so that I can write a tutorial.

Any help is appreciated.

Hi Dave,

The following potential solution is in reference to your original question and the code you provided. It is not taking into account changes you may have made while working with Nathan. Although Nathan’s very first question of “Where are you setting the value ProjectileClass?” is our primary concern as well.

/** Projectile class to spawn */
    UPROPERTY(EditDefaultsOnly, Category=Projectile)
    TSubclassOf ProjectileClass;

…is a declaration and not the definition.

There are a few ways to set a projectile up with a character depending on which classes are from code and which are blueprint, but our recommended solution for replicating the FPS template is to make a Blueprint of your character class, FlyingPawn, and name it something like BP_FlyingPawn. Open this Blueprint to the Defaults mode, and locate the Projectile Class property. Set the property to your Projectile Blueprint (which you mention in your other post that you’ve created).

With this, you won’t need the section of code:

/** Projectile BP */
/*      UPROPERTY(EditDefaultsOnly, Category=ProjectileBP)
*/UClass* ProjectileBP;

…because it will be set up in the blueprint.

Is your GameMode created with a Blueprint or in C++? Because with these steps, you will also have to change your GameMode to use the BP_FlyingPawn as your default pawn.

I hope this proves helpful. If so, it may also resolve your other Projectile collision issue.

Cheers!

Thanks for being patient and helping me out guys.

In regards to the Projectile BP in code…

/** Projectile BP */
/*      UPROPERTY(EditDefaultsOnly, Category=ProjectileBP)
*/UClass* ProjectileBP;

…that was just left over from when I was playing with Rama’s projectile before. I should have removed / commented that out, as I’m not using it anymore.

I did create a BP for a projectile within rocket, and by default, the flying template includes a BP for the ship. In that BP, I have set the projectile to FirstPersonProjectile, just as it is done in the FPS template.

Attached is an image to show this

Is there something else that I am missing here?

One thing you are missing…

In ShipBP, you need to set the Projectile Class as “Projectile”. This is because that’s the name of your Blueprint. Using “FirstPersonProjectile” will only have the code version and not any changes made in the Blueprint.

You sir, are a hero for the people! That worked like a charm, thank you!

I see exactly what I was missing now. Rama, thank you as well for all of your help. I can finally write something up for this, with the bare essentials of how to get a projectile up and running, Attaching a particle system helped as well, because it allowed me to more easily see the projectile.

You sir, are a hero for the people! That worked like a charm, thank you!

I see exactly what I was missing now. Rama, thank you as well for all of your help. I can finally write something up for this, with the bare essentials of how to get a projectile up and running, Attaching a particle system helped as well, because it allowed me to more easily see the projectile.

Excellent. Glad that helped.

Does it also resolve your issue over in the Projectile Collision post? If so, I’d appreciate if you could write up an answer for it with the resolution, or at least an update on where the issue stands currently.

Thanks

Yes it did. I’ll update that right now as well.

Where are you setting the value ProjectileClass ?

can you post that code?

can you post what projectile class code you are using?

last time I checked there is no AProjectile class, you have copy the shootergame one into your own project

Rama

FirstPersonProjectile.h
FlyingPawn.h

FirstPersonProjectile.cpp
FlyingPawn.cpp

None of these are listed publicly, and are all set to expire relatively soon, so they are safe.

These are the 4 classes which are relevant to my projectile: The projectile and my FlyingPawn. I’m setting the value of the ProjectileClass in FlyingPawn.h

	/** Projectile class to spawn */
	UPROPERTY(EditDefaultsOnly, Category=Projectile)
	TSubclassOf ProjectileClass;

I hope that clears things up!

Hi Dave,

Both of your .h files are listed publicly. Please change that immediately.

Thank you.

I’m sorry about that – I didn’t realize that I only changed ti for the last two.

I’ve reported it to PasteBin, so they will take it down immediately.

Here is the private versions of the two files, both of which expire in 1 week:

FirstFlyingPawn.h
FirstPersonProjectile.h

#A Test

could you try using

AFirstpersonProjectile::StaticClass()

instead of

ProjectileClass

as a test and see what happens?

#Const?

why is this const?

AFirstpersonProjectile* const Projectile

will this function run

Projectile->InitVelocity(LaunchDir)

off a const pointer?

is initVelocity const too ?

I dont see how it could be given that your initializing something

can you remove the const just a test?

AFirstpersonProjectile* Projectile

Rama

Thanks for the help, Rama.

The code never reaches

Projectile->InitVelocity(LaunchDir)

Because projectile is never returning in that if check.

I’m not sure why const is used there, but it was copied directly from Epic’s code, so I figured I would leave it all as-is until I could sort out what the issue is.

I also just tried using

AFirstpersonProjectile::StaticClass()

But it yielded the same results . I’ve attached two images of me debugging, so that you can see the values being passed in. I’m clueless at this point.

just to make sure we on same page could you try this:

AFirstpersonProjectile* Projectile = World->SpawnActor(AFirstpersonProjectile::StaticClass(), MuzzleLocation, MuzzleRotation, SpawnParams);

I removed the const and used static cast

also can you test what MuzzleLocation is?

If somehow it was below the KillZ your projectile would not be getting spawned

you can check what your kilZ is within the editor properties for your level, world properties

It throws an error with

AFirstpersonProjectile* Projectile = World->SpawnActor(AFirstpersonProjectile::StaticClass(), MuzzleLocation, MuzzleRotation, SpawnParams);

stating:

Error C2664: ‘AActor *UWorld::SpawnActor(UClass *,const FVector *,const FRotator *,const FActorSpawnParameters &)’ : cannot convert parameter 2 from ‘const FVector’ to ‘const FVector *’

But

			AFirstpersonProjectile* Projectile = World->SpawnActor(AFirstpersonProjectile::StaticClass(), MuzzleLocation, MuzzleRotation, SpawnParams);

will compile.

Kill Z = -1000.0
Muzzle Location = {X=275.000183 Y=3149.89648 Z=220.016144 } (This is the camera, it’s fine for now, I can set it to the pawn once I actually get this working)

So it looks like it should be spawning.

here’s my projectile class

could you try using mine as a blueprint?

you will need to assign a PSC to the blueprint to get anything to show up visually

.h

// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.

#include "VictoryProjectile.generated.h"

UCLASS()
class AVictoryProjectile : public AActor
{
	GENERATED_UCLASS_BODY()

	/** initial setup */
	virtual void PostInitializeComponents() OVERRIDE;

	/** setup velocity */
	void InitVelocity(FVector& ShootDirection);

	/** handle hit */
	void OnImpact(FHitResult const& HitResult);
	
	/** Bounces */
	void OnBounce(FHitResult const& HitResult);

//multiplayer
public:
	
	//due to first frame not being right because velocity
	//is not set right yet
	void CorrectRotationFollowVelocity();
	
public:
	
	/** Projectile Lifetime in Seconds */
	UPROPERTY(EditDefaultsOnly, Category=ProjLifetime)
	float VictoryLifeTime;
	
	/** Collision Radius */
	UPROPERTY(EditDefaultsOnly, Category=ProjCollision)
	float CollisionSphereRadius;
	
	/** Acceleration Multiplier EVERY TICK, can increase velocity up to VictoryProjMaxSpeed*/
	UPROPERTY(EditDefaultsOnly, Category=ProjVelocity)
	float VictoryAccelMult;
		
protected:

	
	
	//Tick
	virtual void Tick(float DeltaSeconds) OVERRIDE;
	
	/** movement component */
	UPROPERTY(VisibleDefaultsOnly, Category=ProjMovement)
	TSubobjectPtr MovementComp;

	/** collisions */
	UPROPERTY(VisibleDefaultsOnly, Category=ProjCollision)
	TSubobjectPtr CollisionComp;
	

	/** effects for explosion */
	UPROPERTY(EditDefaultsOnly, Category=Effects)
	UParticleSystem* ExplosionTemplate;

	/** controller that fired me (cache for damage calculations) */
	AVictoryGamePlayerController* MyController;

	/** did it explode? */
	UPROPERTY(Transient, ReplicatedUsing=OnRep_Exploded)
	bool bExploded;

	/** [client] explosion happened */
	UFUNCTION()
	void OnRep_Exploded();

	/** trigger explosion */
	void Explode(const FHitResult& Impact);

	/** shutdown projectile and prepare for destruction */
	void DisableAndDestroy();

	/** update velocity on client */
	virtual void PostNetReceiveVelocity(const FVector& NewVelocity) OVERRIDE;
};

.cpp

// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.

#include "VictoryGame.h"

#define ROTATE_FOLLOW_VEL_DELAY 0.03

AVictoryProjectile::AVictoryProjectile(const class FPostConstructInitializeProperties& PCIP) : Super(PCIP)
{
	//Core Properties
	CollisionSphereRadius = 5;
	VictoryAccelMult 	= 1.04;
	VictoryLifeTime 		= 10;
	
	CollisionComp = PCIP.CreateDefaultSubobject(this, TEXT("SphereComp"));
	
	//Collision Size
	CollisionComp->InitSphereRadius(CollisionSphereRadius);
	
	//Network
	CollisionComp->AlwaysLoadOnClient = true;
	CollisionComp->AlwaysLoadOnServer = true;
	
	CollisionComp->BodyInstance.SetCollisionEnabled(ECollisionEnabled::QueryOnly);
	CollisionComp->BodyInstance.SetObjectType(COLLISION_PROJECTILE);
	CollisionComp->BodyInstance.SetResponseToAllChannels(ECR_Ignore);
	CollisionComp->BodyInstance.SetResponseToChannel(ECC_WorldStatic, ECR_Block);
	CollisionComp->BodyInstance.SetResponseToChannel(ECC_WorldDynamic, ECR_Block);
	CollisionComp->BodyInstance.SetResponseToChannel(ECC_Pawn, ECR_Block);
	
	//Set Root
	RootComponent = CollisionComp;
	
	//Add Collision
	Components.Add(CollisionComp);

	MovementComp = PCIP.CreateDefaultSubobject(this, TEXT("ProjectileComp"));
	MovementComp->UpdatedComponent = CollisionComp;
	
	//Proj Speed
	MovementComp->Speed = 1000;
	MovementComp->MaxSpeed = 2000;
	
	//teeesting
	MovementComp->bRotationFollowsVelocity = false;
	
	//add movement component
	Components.Add(MovementComp);

	//tick
	PrimaryActorTick.bCanEverTick = true;
	PrimaryActorTick.TickGroup = TG_PrePhysics;
	
	//Replication
	RemoteRole = ROLE_SimulatedProxy;
	bReplicateInstigator = true;			//for projectiles, see actor description
	bReplicateMovement = true;
		
	//ProjectileMovementComponent Settings
	
	//No gravity
	MovementComp->ProjectileGravityScale = 0;
	
	//Bounces
	MovementComp->BounceVelocityStopSimulatingThreshold = 5; //uu/sec
	MovementComp->bShouldBounce = 1;
	MovementComp->Friction = 0;
	MovementComp->Bounciness=1;
	
}

void AVictoryProjectile::CorrectRotationFollowVelocity()
{
	MovementComp->bRotationFollowsVelocity = true;
}

void AVictoryProjectile::PostInitializeComponents()
{
	Super::PostInitializeComponents();
	
	/** Set the bounce delegate - called when the projectile bounces */
	MovementComp->SetBounceDelegate(FOnImpactDelegate::CreateUObject(this, & AVictoryProjectile::OnBounce)); 

	//Stop Delegate!
	MovementComp->SetStopDelegate(FOnImpactDelegate::CreateUObject(this, &AVictoryProjectile::OnImpact));
	
	//Ignore the spawner of this projectile
	CollisionComp->MoveIgnoreActors.Add(Instigator);

	//Set Lifetime
	SetLifeSpan( VictoryLifeTime );
	
	MyController = Cast(GetInstigatorController());
	
	//correct rot follow vel
	GetWorldTimerManager().SetTimer(this, 
		&AVictoryProjectile::CorrectRotationFollowVelocity, 
		ROTATE_FOLLOW_VEL_DELAY,
		false
	);
}

void AVictoryProjectile::InitVelocity(FVector& ShootDirection)
{
	if (MovementComp)
	{
		MovementComp->Velocity = ShootDirection * MovementComp->Speed;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Projectile Bounce Delegate
void AVictoryProjectile::OnBounce(FHitResult const& HitResult)
{
	//if (MyController) MyController->OptJoyHUD("Bounce!!");
	
	//hit a pawn? Then do impact
	AActor* HitActor = HitResult.GetActor();
	if (!HitActor) return;
	
	//do impact
	if (HitActor->IsA(APawn::StaticClass())) OnImpact(HitResult);
}

//Projectile Stop Delegate
void AVictoryProjectile::OnImpact(FHitResult const& HitResult)
{
	if (Role == ROLE_Authority && !bExploded)
	{
		Explode(HitResult);
		DisableAndDestroy();
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


void AVictoryProjectile::Explode(const FHitResult& Impact)
{
	// effects and damage origin shouldn't be placed inside mesh at impact point
	const FVector NudgedImpactLocation = Impact.ImpactPoint + Impact.ImpactNormal * 10.0f;

	//Spawn Emitter At Impact Location
	if (ExplosionTemplate)
	{
		UGameplayStatics::SpawnEmitterAtLocation(
			GetWorld(), 
			ExplosionTemplate, 
			NudgedImpactLocation, 
			Impact.ImpactNormal.Rotation(), 
			true); //auto destroy when explosion finishes
	}
	
	
	bExploded = true;
}

void AVictoryProjectile::DisableAndDestroy()
{
	SetActorHiddenInGame(true);
	MovementComp->StopMovementImmediately();

	// give clients some time to show explosion
	SetLifeSpan( 1.0f );
}

void AVictoryProjectile::OnRep_Exploded()
{
	FVector ProjDirection = GetActorRotation().Vector();

	const FVector StartTrace = GetActorLocation() - ProjDirection * 200;
	const FVector EndTrace = GetActorLocation() + ProjDirection * 150;
	FHitResult Impact;
	
	//FCollisionQueryParams(FName InTraceTag, bool bInTraceComplex=false, const AActor* InIgnoreActor=NULL)
	if (!GetWorld()->LineTraceSingle(Impact, StartTrace, EndTrace, COLLISION_PROJECTILE, FCollisionQueryParams(TEXT("ProjClient"), true, Instigator)))
	{
		// failsafe
		Impact.ImpactPoint = GetActorLocation();
		Impact.ImpactNormal = -ProjDirection;
	}

	Explode(Impact);
}

void AVictoryProjectile::PostNetReceiveVelocity(const FVector& NewVelocity)
{
	if (MovementComp)
	{
		MovementComp->Velocity = NewVelocity;
	}
}

int32* AVictoryProjectile::GetReplicationList(uint8* Recent, FPropertyRetirement* Retire, int32* Ptr, UPackageMap* Map, UActorChannel* Channel, FReplicationFlags RepFlags)
{
	Ptr = Super::GetReplicationList(Recent, Retire, Ptr, Map, Channel, RepFlags);

	DOREP(AVictoryProjectile, bExploded);

	return Ptr;
}

//```
void AVictoryProjectile::Tick(float DeltaSeconds)
{
	Super::Tick(DeltaSeconds);
	
	if (bExploded) return;
	if (!MovementComp) return;
	//~~~~~~~~~~~~~~~~~~~~~~~~
	
	//Acceleration, server side only
	if (Role == ROLE_Authority)
	{
		/** Current velocity of moved component, replicated to clients using Actor ReplicatedMovement property. */
		MovementComp->Velocity *= VictoryAccelMult;
	}
	
	/*
	if (MyController) MyController->JoyHUD->Optimize("Ticking!");
	if (MyController) MyController->Optimize("Ticking!");
	*/
	
}

A PSC? What is that?

particle system component

need to replace my player controller with yours

or remove all references to MyController

would need to forward declare your controller class in .h file