Im new to programming ue with c++, I am following this tutorial:
All of the code is at the bottom of that tutorial, but i am mostly concerned with the FPSProjectile.h and FPSProjectile.cpp files^^
It has the mesh for the projectile hard coded with a reference path, but that doesnt seem ideal because then i cant change the file path, or change the asset without having to copy a new reference path. I know I can inherit the c++ class to a blueprint and change the values there but how do i use that in the c++ code?
If you haven’t already in the code declare a static mesh component in the header that is blueprint read write and in the cpp file you can do an if to check if mymeshcomp.mesh not null then use that otherwise use the hard-coded path
I can see the mesh in the blueprint now, but when i spawn the projectile the mesh does not appear. If I place the blueprint in the world and run it that one shows up fine though
I reverted it back to the hard coding but here is my header file
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/SphereComponent.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "FPSProjectile.generated.h"
UCLASS()
class FPSPROJECT_API AFPSProjectile : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AFPSProjectile();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Function that is called when the projectile hits something.
UFUNCTION()
void OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComponent, FVector NormalImpulse, const FHitResult& Hit);
// Sphere collision component
UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
USphereComponent* CollisionComponent;
// Projectile movement component
UPROPERTY(VisibleAnywhere, Category = Movement)
UProjectileMovementComponent* ProjectileMovementComponent;
// Projectile mesh
UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
UStaticMeshComponent* ProjectileMeshComponent;
// Projectile material
UPROPERTY(VisibleDefaultsOnly, Category = Movement)
UMaterialInstanceDynamic* ProjectileMaterialInstance;
// Function that initializes the projectile's velocity in the shoot direction.
void FireInDirection(const FVector& ShootDirection);
};
Here is the cpp file
// Copyright Epic Games, Inc. All Rights Reserved.
#include "FPSProjectile.h"
// Sets default values
AFPSProjectile::AFPSProjectile()
{
// 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;
if (!RootComponent)
{
RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("ProjectileSceneComponent"));
}
if (!CollisionComponent)
{
// Use a sphere as a simple collision representation.
CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
// Set the sphere's collision profile name to "Projectile".
CollisionComponent->BodyInstance.SetCollisionProfileName(TEXT("Projectile"));
// Event called when component hits something.
CollisionComponent->OnComponentHit.AddDynamic(this, &AFPSProjectile::OnHit);
// Set the sphere's collision radius.
CollisionComponent->InitSphereRadius(15.0f);
// Set the root component to be the collision component.
RootComponent = CollisionComponent;
}
if (!ProjectileMovementComponent)
{
// Use this component to drive this projectile's movement.
ProjectileMovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovementComponent"));
ProjectileMovementComponent->SetUpdatedComponent(CollisionComponent);
ProjectileMovementComponent->InitialSpeed = 3000.0f;
ProjectileMovementComponent->MaxSpeed = 3000.0f;
ProjectileMovementComponent->bRotationFollowsVelocity = true;
ProjectileMovementComponent->bShouldBounce = true;
ProjectileMovementComponent->Bounciness = 0.3f;
ProjectileMovementComponent->ProjectileGravityScale = 0.0f;
}
if (!ProjectileMeshComponent)
{
ProjectileMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ProjectileMeshComponent"));
static ConstructorHelpers::FObjectFinder<UStaticMesh>Mesh(TEXT("'/Game/Blueprint/Sphere.Sphere'"));
if (Mesh.Succeeded())
{
ProjectileMeshComponent->SetStaticMesh(Mesh.Object);
}
static ConstructorHelpers::FObjectFinder<UMaterial>Material(TEXT("'/Game/Blueprint/SphereMaterial.SphereMaterial'"));
if (Material.Succeeded())
{
ProjectileMaterialInstance = UMaterialInstanceDynamic::Create(Material.Object, ProjectileMeshComponent);
}
ProjectileMeshComponent->SetMaterial(0, ProjectileMaterialInstance);
ProjectileMeshComponent->SetRelativeScale3D(FVector(0.09f, 0.09f, 0.09f));
ProjectileMeshComponent->SetupAttachment(RootComponent);
}
// Delete the projectile after 3 seconds.
InitialLifeSpan = 3.0f;
}
// Called when the game starts or when spawned
void AFPSProjectile::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AFPSProjectile::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AFPSProjectile::OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComponent, FVector NormalImpulse, const FHitResult& Hit)
{
if (OtherActor != nullptr && OtherActor != this && OtherComponent != nullptr && OtherComponent->IsSimulatingPhysics())
{
OtherComponent->AddImpulseAtLocation(ProjectileMovementComponent->Velocity * 100.0f, Hit.ImpactPoint);
}
Destroy();
}
// Function that initializes the projectile's velocity in the shoot direction.
void AFPSProjectile::FireInDirection(const FVector& ShootDirection)
{
ProjectileMovementComponent->Velocity = ShootDirection * ProjectileMovementComponent->InitialSpeed;
}
Im trying to have a blueprint inheriting this class that i can set the mesh in and then when the projectile is fired it’ll have that mesh
First thing to change is your uproperty visibledefaultsonly refer to the ue documentation on why you should change it to blueprint read write next when you refrence the meshcomponent you need to add an arrow getmesh as your current implementation will always be true as theres always a mesh component but no mesh which is the value we are interested in and move the create default subobject out before this.
The reason for moving it out the if and changimg the if is you’re creating the projectile on actor spawn in another class i pressume and by default you are going to always create this so checking it is just one more thing for the pc to do that is essentially redundant now if the projectile spawns on one actor at a slow rate its fine but say you have 50 npcs or players all calling it at the same time like on a fast firing machine gun this will become a very taxing check still it will be quick and negligible on modern day hardware but say you want it to run older gen pcs ppl may get upset as on lower hardware it will frag there fps and tie up cpu gpu threads on something that could be needed elsewhere like detect onhit on the colision.
If you give me an hour ill post a screen shot of what to do, but view the ue doc on upropertys and look at the various macros and there definitions as this is good habit to learn and help you’re understanding of it all. Next look at the documentation on mesh comp and the methods associated with it
The mesh is now changeable in the Blueprint but does not show when i spawn the projectile. If I drag the blueprint into the scene I can see the mesh but it gets locked at world 0,0,0.
also it would be worth checking putting the actor in world next to 0,0,0 to see if it is being spawned but at that point as then the method calling the spawn is setting it to 0,0,0 and you are likely passing relative space not world space to the method
There is a fire function in a separate character class that fires the projectile.
Here is the function in the header:
// fire projectiles
UFUNCTION()
void Fire();
And here is the function in the cpp file:
void AFPSCharacter::Fire()
{
// Attempt to fire a projectile.
if (ProjectileClass)
{
// Get the camera transform.
FVector CameraLocation;
FRotator CameraRotation;
GetActorEyesViewPoint(CameraLocation, CameraRotation);
// Set MuzzleOffset to spawn projectiles slightly in front of the camera.
MuzzleOffset.Set(100.0f, 0.0f, 0.0f);
// Transform MuzzleOffset from camera space to world space.
FVector MuzzleLocation = CameraLocation + FTransform(CameraRotation).TransformVector(MuzzleOffset);
// Skew the aim to be slightly upwards.
FRotator MuzzleRotation = CameraRotation;
MuzzleRotation.Pitch += .5f;
UWorld* World = GetWorld();
if (World)
{
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = this;
SpawnParams.Instigator = GetInstigator();
// Spawn the projectile at the muzzle.
AFPSProjectile* Projectile = World->SpawnActor<AFPSProjectile>(ProjectileClass, MuzzleLocation, MuzzleRotation, SpawnParams);
if (Projectile)
{
// Set the projectile's initial trajectory.
FVector LaunchDirection = MuzzleRotation.Vector();
Projectile->FireInDirection(LaunchDirection);
}
}
}
}
Im not sure im following, are you suggesting that the UPROPERTY is affecting the visibility since the bottom two are incompatible with “Visible” Specifiers etc?
EDIT: I just tried it with all three of the ones you showed and they all had the same result, they show when I drag the bp into the world but not when I shoot from the character
The Original code also had the mesh become the root component, and that takes care of the mesh spawning at 0,0,0.
Ok so the likely cause is camera location if you look at in blueprint. Camera location will most likely be 0,0,0 you either need to call getworld location on Camera or worldlocaction, as what you are doing is either referring to custom camera location property ypu made which by default will be 0,0,0 or getting relative location which will be the value you see of camera in location (say camera is attached tp manny and you moved it to head will be like 0,0,90 or if root then be 0,0,0) so when you covert to spawn it will be spawning in world at 0,0,0 or 0,0,90 plus the offset logic you do
both the relative and world locations for the camera are 0,0,114 in the character class.
Im not sure if the problem is the camera either because when the projectile mesh was hard coded it showed up and worked fine
Ok sorry was a late night here and off to work now, but may need to set the variable from shooter on spawn but ill post some code where it checks the blueprint value or uses hardcoded value and if i get time i will show how to do it from the shooter
I think i know what your problem is because you are spawning this in code and refrencing the code class not a bp class so this is why and where the nesh needs to be set hence why it works hard coded and etc. So you need a variable that can be set on the shooter as the mesh and assign it to the projectile so…
Spawn the projectile and then do projectile.mymeshcomponent->setstaticmesh(myshooterprojectilemeshcomp.mesh) then shoot
Alternatively and cleaner would be projectile.meshcomp = myshooterprojmrshcomp
But you need to alter the visibledefaultsonly for this to work as it will use the default value you set in the projectile construct and will likely fail also ensure that the projectile comp is either public or protected otherwise if it is private you are going to need a public setter function or better a public forceinline setter. If you are wanting bp mesh setting on projectile itself then it will require a little more time to work
We are getting closer!
I made the mesh component edit anywhere in the header file, and i also added another mesh that i am going to put into the static mesh component called myMesh. On top of that i created a function that would set myMesh to the components mesh.
HEADER FILE:
// Projectile mesh component
UPROPERTY(EditAnywhere, Category = Projectile)
UStaticMeshComponent* ProjectileMeshComponent;
//mesh to give to component
UPROPERTY(EditAnywhere, Category = THISISMYMESH)
UStaticMesh* MyMesh;
//call when spawned
void Spawned();
C++ FILE:
if (!ProjectileMeshComponent)
{
ProjectileMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ProjectileMeshComponent"));
ProjectileMeshComponent->SetupAttachment(RootComponent);
ProjectileMeshComponent->SetStaticMesh(MyMesh);
}
// Delete the projectile after 3 seconds.
InitialLifeSpan = 3.0f;
}
//SPAWNED DEFINED HERE, SETS MESH
void AFPSProjectile::Spawned()
{
ProjectileMeshComponent->SetStaticMesh(MyMesh);
}
// Called when the game starts or when spawned
void AFPSProjectile::BeginPlay()
{
Super::BeginPlay();
// SPAWNED CALLED HERE
Spawned();
}
I placed the Spawned(); function in the begin play function and it worked for the blueprint in the level, meaning it had no mesh before i played it, and when i played the mesh appeared on the projectile! However when i shot the projectiles from the character they did not spawn, this leads me to believe the issue is in the way the projectile spawns from the character?