Creación de un Sistema de Lanzamiento de Proyectiles Dirigido con C++ y Blueprint en Unreal Engine 5
Este tutorial te guiará paso a paso en la creación de un sistema de lanzamiento de proyectiles dirigido en Unreal Engine 5. Combinaremos la eficiencia de C++ para la lógica central y la flexibilidad de Blueprints para la implementación y los efectos visuales. Aprenderás a configurar la detección del objetivo, la trayectoria del proyectil y el manejo de impacto.
🎯 Introducción al Sistema de Proyectiles Dirigidos
En el desarrollo de videojuegos, un sistema de lanzamiento de proyectiles es fundamental para muchos géneros, desde shooters hasta RPGs de acción con hechizos. Un proyectil dirigido va un paso más allá, ya que no solo se lanza en una dirección, sino que activamente busca y persigue un objetivo hasta impactar o desaparecer. Esto añade una capa de complejidad y dinamismo a tus habilidades y ataques.
En este tutorial, construiremos un sistema robusto que permitirá a tus personajes lanzar proyectiles que persiguen a sus enemigos. Utilizaremos C++ para establecer la base lógica de nuestro proyectil y su componente de búsqueda, y Blueprints para refinar el comportamiento, añadir efectos visuales (VFX) y sonidos (SFX).
¿Por qué C++ y Blueprint? 💡
La combinación de C++ y Blueprint es una práctica común y muy potente en Unreal Engine:
- C++: Ofrece mayor rendimiento, acceso de bajo nivel al motor y facilita la creación de bases de código reusables y complejas. Ideal para la lógica central y los componentes que requieren alta eficiencia.
- Blueprint: Permite una rápida iteración, prototipado y visualización del flujo de lógica. Es excelente para integrar arte, sonidos, animaciones y para la lógica específica de cada proyectil o habilidad.
Al usar ambos, obtenemos lo mejor de ambos mundos: un núcleo eficiente y una implementación flexible y fácil de ajustar.
🛠️ Configuración Inicial del Proyecto
Antes de sumergirnos en el código, necesitamos configurar nuestro entorno de desarrollo.
1. Crear un Nuevo Proyecto
Para este tutorial, usaremos una plantilla de Third Person o First Person en Unreal Engine 5, ya que ya tienen un personaje y un controlador configurados. Asegúrate de que el proyecto sea C++.
- Abre el Epic Games Launcher.
- Inicia Unreal Engine 5.
- Selecciona Games en la categoría de proyectos.
- Elige la plantilla Third Person o First Person.
- Asegúrate de que el tipo de proyecto sea C++.
- Elige una ubicación y dale un nombre, por ejemplo,
ProyectilesDirigidos. - Haz clic en Create.
2. Abrir el Entorno de Desarrollo
Una vez que el proyecto se ha creado, Unreal Engine abrirá Visual Studio (o tu IDE C++ preferido) automáticamente. Si no es así, puedes ir a File > Open Visual Studio desde el editor de Unreal.
🚀 Creando el Actor del Proyectil (C++)
Vamos a empezar creando la clase base de nuestro proyectil en C++. Este AActor será el encargado de la representación visual, el movimiento básico y la detección de colisiones.
1. Clase Base del Proyectil AProjectileBase
Crearemos una nueva clase C++ que herede de AActor.
- En el Editor de Unreal, ve a
Tools > New C++ Class.... - Selecciona
Actorcomo la clase padre. - Nombra la clase
ProjectileBase. - Haz clic en Create Class.
Esto generará los archivos ProjectileBase.h y ProjectileBase.cpp.
ProjectileBase.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ProjectileBase.generated.h"
class USphereComponent;
class UProjectileMovementComponent;
class UParticleSystemComponent;
class USoundCue;
UCLASS()
abstract class PROYECTILESDIRECCION_API AProjectileBase : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AProjectileBase();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
/** Componente de colisión para el proyectil */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
USphereComponent* CollisionSphere;
/** Componente de movimiento del proyectil */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
UProjectileMovementComponent* ProjectileMovement;
/** Componente para efectos visuales (partículas) */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
UParticleSystemComponent* ParticleFXComponent;
/** Mesh del proyectil (opcional, para representación visual) */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
UStaticMeshComponent* ProjectileMesh;
/** Daño que aplica el proyectil al impactar */
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Projectile")
float DamageAmount;
/** Partículas a spawnear en el impacto */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Projectile")
UParticleSystem* ImpactFX;
/** Sonido a reproducir en el impacto */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Projectile")
USoundCue* ImpactSound;
/** Función a llamar al impactar con algo */
UFUNCTION()
virtual void OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
/** Destruye el proyectil después de un breve delay tras el impacto para permitir que los FX se reproduzcan */
UFUNCTION(BlueprintCallable, Category = "Projectile")
void DestroyProjectileDelayed();
protected:
/** Duración del delay antes de destruir el proyectil tras impacto */
UPROPERTY(EditDefaultsOnly, Category = "Projectile")
float DestroyDelayOnImpact;
FTimerHandle DestroyTimerHandle;
};
ProjectileBase.cpp
#include "ProjectileBase.h"
#include "Components/SphereComponent.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "Particles/ParticleSystemComponent.h"
#include "Kismet/GameplayStatics.h"
#include "Sound/SoundCue.h"
#include "Components/StaticMeshComponent.h"
// Sets default values
AProjectileBase::AProjectileBase()
{
// 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;
CollisionSphere = CreateDefaultSubobject<USphereComponent>(TEXT("CollisionSphere"));
RootComponent = CollisionSphere;
CollisionSphere->SetSphereRadius(20.0f);
CollisionSphere->SetCollisionProfileName(TEXT("Projectile")); // Usaremos un perfil personalizado
CollisionSphere->OnComponentHit.AddDynamic(this, &AProjectileBase::OnHit);
ProjectileMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ProjectileMesh"));
ProjectileMesh->SetupAttachment(RootComponent);
ProjectileMesh->SetRelativeScale3D(FVector(0.5f, 0.5f, 0.5f));
ProjectileMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision); // El mesh no colisiona, solo la esfera
ParticleFXComponent = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("ParticleFXComponent"));
ParticleFXComponent->SetupAttachment(RootComponent);
ParticleFXComponent->SetRelativeLocation(FVector(0.0f, 0.0f, 0.0f));
ProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovement"));
ProjectileMovement->SetUpdatedComponent(CollisionSphere);
ProjectileMovement->InitialSpeed = 1500.0f;
ProjectileMovement->MaxSpeed = 1500.0f;
ProjectileMovement->bRotationFollowsVelocity = true;
ProjectileMovement->bShouldBounce = false;
ProjectileMovement->ProjectileGravityScale = 0.0f; // No queremos gravedad por defecto
DamageAmount = 20.0f;
DestroyDelayOnImpact = 0.5f; // 0.5 segundos antes de destruir el actor tras impacto
}
// Called when the game starts or when spawned
void AProjectileBase::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AProjectileBase::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AProjectileBase::OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
// Ignorar impactos con el propio lanzador o si ya está marcado para destrucción
if (GetInstigator() == OtherActor || OtherActor == this)
{
return;
}
// Desactivar el movimiento y la colisión para evitar múltiples impactos
ProjectileMovement->StopMovementImmediately();
CollisionSphere->SetCollisionEnabled(ECollisionEnabled::NoCollision);
// Reproducir efectos de impacto
if (ImpactFX)
{
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), ImpactFX, GetActorLocation(), GetActorRotation());
}
if (ImpactSound)
{
UGameplayStatics::PlaySoundAtLocation(GetWorld(), ImpactSound, GetActorLocation());
}
// Aplicar daño (si el otro actor implementa una interfaz de daño, por ejemplo)
// UGameplayStatics::ApplyDamage(OtherActor, DamageAmount, GetInstigatorController(), this, UDamageType::StaticClass());
// Detener el sistema de partículas del proyectil
if (ParticleFXComponent)
{
ParticleFXComponent->DeactivateSystem();
}
// Destruir el proyectil después de un delay
GetWorldTimerManager().SetTimer(DestroyTimerHandle, this, &AProjectileBase::DestroyProjectileDelayed, DestroyDelayOnImpact, false);
}
void AProjectileBase::DestroyProjectileDelayed()
{
Destroy();
}
2. Configuración del Perfil de Colisión
Necesitamos un perfil de colisión específico para nuestros proyectiles.
- En el Editor de Unreal, ve a
Edit > Project Settings. - En la sección
Engine, seleccionaCollision. - Haz clic en
New Trace Channely nómbraloProjectile, conDefault ResponsecomoBlock. - Haz clic en
New Object Channely nómbraloProjectile, conDefault ResponsecomoIgnore. - En la sección
Preset, haz clic enNewpara crear un nuevo preset de colisión. - Nombra el preset
Projectile. - Ajusta las respuestas como se indica en la tabla:
| Canal de Objeto | Respuesta | Canal de Rastro | Respuesta |
|---|---|---|---|
| --- | --- | --- | --- |
| WorldStatic | Block | Visibility | Block |
| WorldDynamic | Block | Camera | Ignore |
| --- | --- | --- | --- |
| Pawn | Block | Projectile | Block |
| PhysicsBody | Block | ||
| --- | --- | ||
| Vehicle | Ignore | ||
| Destructible | Block | ||
| --- | --- | ||
| Projectile | Ignore |
🧭 Creando el Componente de Búsqueda de Proyectiles (C++)
Aquí es donde reside la magia del "dirigido". Crearemos un UActorComponent que se encargará de encontrar y seguir al objetivo.
1. Clase UProjectileHomingComponent
- En el Editor de Unreal, ve a
Tools > New C++ Class.... - Selecciona
ActorComponentcomo la clase padre. - Nombra la clase
ProjectileHomingComponent. - Haz clic en Create Class.
ProjectileHomingComponent.h
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "ProjectileHomingComponent.generated.h"
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class PROYECTILESDIRECCION_API UProjectileHomingComponent : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UProjectileHomingComponent();
protected:
// Called when the game starts
virtual void BeginPlay() override;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
/** Actor al que el proyectil intentará seguir */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Homing")
AActor* HomingTarget;
/** Velocidad de rotación para apuntar al objetivo */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Homing", meta=(ClampMin = "0.0", UIMin = "0.0"))
float HomingTurnSpeed;
/** Distancia mínima para considerar que se ha llegado al objetivo y detener la búsqueda */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Homing", meta=(ClampMin = "0.0", UIMin = "0.0"))
float HomingStopDistance;
/** Establece el objetivo de búsqueda para el proyectil */
UFUNCTION(BlueprintCallable, Category = "Homing")
void SetHomingTarget(AActor* NewTarget);
private:
/** El ProyectileMovementComponent del actor propietario */
UPROPERTY()
UProjectileMovementComponent* OwnerProjectileMovement;
/** Referencia al actor propietario del componente */
UPROPERTY()
AActor* OwnerActor;
/** Inicializa el componente de movimiento del proyectil del propietario */
void InitializeOwnerProjectileMovement();
};
ProjectileHomingComponent.cpp
#include "ProjectileHomingComponent.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "Kismet/GameplayStatics.h"
// Sets default values for this component's properties
UProjectileHomingComponent::UProjectileHomingComponent()
{
// Set this component to be initialized when the game starts, and to be ticked every frame.
PrimaryComponentTick.bCanEverTick = true;
HomingTurnSpeed = 5.0f;
HomingStopDistance = 100.0f;
}
// Called when the game starts
void UProjectileHomingComponent::BeginPlay()
{
Super::BeginPlay();
OwnerActor = GetOwner();
InitializeOwnerProjectileMovement();
}
void UProjectileHomingComponent::InitializeOwnerProjectileMovement()
{
if (OwnerActor)
{
OwnerProjectileMovement = OwnerActor->FindComponentByClass<UProjectileMovementComponent>();
if (OwnerProjectileMovement)
{
// Habilitar el homing en el ProjectileMovementComponent
OwnerProjectileMovement->bIsHomingProjectile = true;
OwnerProjectileMovement->HomingAccelerationMagnitude = HomingTurnSpeed * 1000.0f; // Ajustar la aceleración
}
}
}
// Called every frame
void UProjectileHomingComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (HomingTarget && OwnerProjectileMovement)
{
// Actualizar el objetivo de homing si el ProjectileMovementComponent no lo hace automáticamente
// O si queremos una lógica de homing más personalizada.
// En este caso, el ProjectileMovementComponent ya gestiona el homing si bIsHomingProjectile es true
// Y su HomingTargetComponent es establecido. Simplemente nos aseguramos de que el objetivo exista.
// Si el objetivo está demasiado cerca, podríamos querer detener el homing
float DistanceToTarget = FVector::Dist(OwnerActor->GetActorLocation(), HomingTarget->GetActorLocation());
if (DistanceToTarget <= HomingStopDistance)
{
OwnerProjectileMovement->bIsHomingProjectile = false;
// Opcionalmente, hacer algo más, como activar una explosión inmediata
}
else
{
// Asegurarse de que el homing esté activo si el objetivo no está cerca y sigue siendo válido
OwnerProjectileMovement->bIsHomingProjectile = true;
OwnerProjectileMovement->SetHomingTargetComponent(HomingTarget->GetRootComponent());
}
// Si el objetivo ya no es válido (destruido, etc.), detener el homing
if (!HomingTarget->IsValidLowLevel() || HomingTarget->IsPendingKill())
{
HomingTarget = nullptr;
OwnerProjectileMovement->bIsHomingProjectile = false;
}
}
else if (OwnerProjectileMovement && OwnerProjectileMovement->bIsHomingProjectile)
{
// Si no hay objetivo, desactivar el homing
OwnerProjectileMovement->bIsHomingProjectile = false;
}
}
void UProjectileHomingComponent::SetHomingTarget(AActor* NewTarget)
{
HomingTarget = NewTarget;
if (OwnerProjectileMovement && HomingTarget)
{
OwnerProjectileMovement->bIsHomingProjectile = true;
OwnerProjectileMovement->HomingAccelerationMagnitude = HomingTurnSpeed * 1000.0f; // Reajustar por si acaso
OwnerProjectileMovement->SetHomingTargetComponent(HomingTarget->GetRootComponent());
}
}
🎨 Blueprint del Proyectil Dirigido
Ahora crearemos un Blueprint a partir de nuestras clases C++ para poder configurar la estética y el comportamiento específico sin necesidad de recompilar el código.
1. Crear el Blueprint del Proyectil
- En el Content Browser, haz clic derecho y selecciona
Blueprint Class. - En la ventana
Pick Parent Class, buscaProjectileBasey selecciónala. Nombra el BlueprintBP_HomingProjectile. - Abre
BP_HomingProjectile.
2. Configurar el BP_HomingProjectile
En el editor de Blueprint, verás los componentes que definimos en C++:
- CollisionSphere: Mantén el perfil
Projectileque creamos. - ProjectileMovement: Puedes ajustar
Initial Speed,Max Speed, etc. Asegúrate de queProjectile Gravity Scaleesté en0.0para que el proyectil no caiga.bRotationFollowsVelocityes útil para que el mesh se oriente correctamente. - ParticleFXComponent: Selecciona una plantilla de sistema de partículas para el rastro del proyectil. Unreal tiene algunas por defecto en
Engine/Content/FX/P_SmokeStack_01o similar. Experimenta con ellas. - ProjectileMesh: Asigna un
Static Meshsimple, como una esfera (Sphere) o un cono (Cone) para representar el proyectil. Ajusta su tamaño si es necesario.
Añadir el ProjectileHomingComponent
- En la pestaña
Componentsdel Blueprint, haz clic en+ Add. - Busca
Projectile Homing Componenty añádelo. - En la sección
Detailsde este componente, podrás configurarHoming Turn SpeedyHoming Stop Distance.
Configurar las Propiedades del Proyectil (Defaults)
En la sección Defaults del Blueprint, podrás ajustar:
- Damage Amount: El daño que infligirá el proyectil.
- Impact FX: Selecciona un sistema de partículas para el efecto de impacto (ej.
P_Explosionde los assets de Unreal). - Impact Sound: Selecciona un sonido para el impacto.
- Destroy Delay On Impact: Mantén
0.5segundos para que los efectos se reproduzcan antes de que el proyectil se destruya.
🔫 Implementando el Lanzamiento del Proyectil
Ahora que tenemos el proyectil base y su lógica de búsqueda, necesitamos una forma de lanzarlo. Lo haremos desde el personaje ThirdPersonCharacter (o FirstPersonCharacter).
1. Modificar ThirdPersonCharacter Blueprint
- Abre el Blueprint de tu personaje (ej.
BP_ThirdPersonCharacteren la carpetaThirdPerson/Blueprints). - Ve al
Event Graph.
2. Lógica de Lanzamiento
Utilizaremos un evento de entrada para lanzar el proyectil. Por ejemplo, el clic izquierdo del ratón o una tecla.
- Haz clic derecho y busca
Input Action Fire(si usas la plantilla de FPS) o crea un nuevoInput ActionenProject Settings > InputparaLanzarProyectily mapea una tecla (ej.Q). - Arrastra el
Projectile Homing Componentdesde elBP_HomingProjectilehacia elEvent Graph. - Desde el evento de input (
Enhanced Input Action 'Fire'oKeyboard Q), arrastra un pinPressedy conecta a un nodoSpawn Actor From Class.- Class: Selecciona
BP_HomingProjectile. - Spawn Transform: Para la ubicación de spawn, puedes usar
Get Actor Location+ unForward Vectorpara desplazarlo ligeramente delante del personaje, o unScene Componentadjunto a la mano del personaje para un punto de lanzamiento más preciso.- Una forma sencilla:
Get Actor Location+Get Actor Forward Vector*SomeOffset(ej. 100 unidades).
- Una forma sencilla:
- Class: Selecciona
- Encontrar un Objetivo: Para el objetivo de búsqueda, haremos un
Line Trace By ChanneloSphere Tracedesde la cámara o el personaje hacia adelante para detectar unPawn(enemigo).- Realiza un
Line Trace By Channeldesde la cámara (o un punto de lanzamiento) hacia un rango máximo. - Asegúrate de que el
Trace ChannelseaVisibilityoCamera. - Si el
Hit Resultes exitoso y elHit Actores unPawn(que no sea el propio lanzador), ese será nuestroHoming Target.
- Realiza un
- Desde el
Return Valuedel nodoSpawn Actor From Class(que es una referencia aBP_HomingProjectile), arrastra un pin y buscaCast To ProjectileBase. - Desde el
As Projectile Basedel nodoCast, arrastra un pin y buscaGet Projectile Homing Component. - Desde el
Projectile Homing Componentresultante, arrastra un pin y buscaSet Homing Target. Conecta elHit ActordelLine Tracea esteHoming Target.
Ejemplo de Blueprint para lanzar proyectil
// En el Event Graph del ThirdPersonCharacter
// Input Action: Lanzar Proyectil (ej. Tecla Q)
Event InputAction_LanzarProyectil (Pressed)
-> Branch (DoOnce)
// Line Trace para buscar objetivo
-> Line Trace By Channel (Start: GetActorLocation, End: GetActorLocation + GetActorForwardVector * 2000)
-> If Hit Result (Is Valid?)
-> Cast To Pawn (Hit Actor)
-> If Valid Cast And Is Not Self
-> Spawn Actor From Class (BP_HomingProjectile)
-> Set Location (GetActorLocation + GetActorForwardVector * 100)
-> Set Rotation (GetActorRotation)
-> Set Instigator (Self)
-> From Spawned Actor Ref -> Get Projectile Homing Component -> Set Homing Target (Cast To Pawn Result)
-> DoOnce (Reset after delay, ej. 0.5s)
-> Else (No valid target)
-> Spawn Actor From Class (BP_HomingProjectile) // Lanza sin homing
-> Set Location (GetActorLocation + GetActorForwardVector * 100)
-> Set Rotation (GetActorRotation)
-> Set Instigator (Self)
-> DoOnce (Reset after delay, ej. 0.5s)
-> Else (No hit)
-> Spawn Actor From Class (BP_HomingProjectile) // Lanza sin homing
-> Set Location (GetActorLocation + GetActorForwardVector * 100)
-> Set Rotation (GetActorRotation)
-> Set Instigator (Self)
-> DoOnce (Reset after delay, ej. 0.5s)
3. Configurar Target Tags para el Line Trace (Opcional pero recomendado)
En lugar de simplemente Cast To Pawn, puedes usar Tags para identificar a los enemigos.
- En el Blueprint de un
Enemy(si tienes uno, o simplemente elBP_ThirdPersonCharactersi quieres que el proyectil se dirija a otro personaje), en la secciónDetails, buscaTagsy añade unActor Tag, por ejemplo,Enemy. - En la lógica de lanzamiento, después del
Line Trace, en lugar deCast To Pawn, usaGet TagsdelHit Actory verifica si el arrayContainsel tagEnemy.
🧪 Pruebas y Ajustes
¡Es hora de probar nuestro sistema!
- Coloca varios personajes (ej.
BP_ThirdPersonCharacterduplicados) en tu nivel para que sirvan de objetivos. - Asegúrate de que tus personajes objetivo tengan el
Actor TagEnemysi optaste por esa aproximación. - Juega el nivel y lanza los proyectiles.
Posibles Problemas y Soluciones:
- Proyectil no se mueve: Revisa el
ProjectileMovementComponentenBP_HomingProjectile. Asegúrate de queInitial SpeedyMax Speedsean > 0. Asegúrate de queSetHomingTargetse esté llamando correctamente. - Proyectil no persigue al objetivo: Verifica el
ProjectileHomingComponentenBP_HomingProjectile. Asegúrate de queHoming Turn Speedsea un valor razonable (ej. 5.0 - 15.0). También verifica queSet Homing Targetesté recibiendo unAActorválido. - Proyectil pasa a través del objetivo: Revisa los perfiles de colisión. El
CollisionSpheredel proyectil debeBlockoOverlapcon elObject Channeldel objetivo (ej.Pawn). El objetivo debe tener un componente de colisión configurado para responder al canalProjectile. - Proyectil se destruye inmediatamente: Revisa
DestroyDelayOnImpactenBP_HomingProjectile. También, verifica la lógicaOnHitpara asegurarte de que no haya unDestroy()inmediato sin delay. - Falta de efectos visuales/sonoros: Asegúrate de que los
Particle SystemsySound Cuesestén asignados en elBP_HomingProjectiley que existan en tu proyecto.
🚀 Mejoras Avanzadas (Ideas)
Una vez que tengas el sistema básico funcionando, puedes extenderlo de varias maneras:
- Múltiples Proyectiles/Habilidades: Crea Blueprints hijos de
BP_HomingProjectilepara diferentes tipos de proyectiles con distintos efectos, velocidades o daños. - Pool de Proyectiles: En lugar de
Spawn Actor From Class, implementa unObject Poolpara reciclar proyectiles y mejorar el rendimiento, especialmente si lanzas muchos. Esto es crucial en juegos de alto rendimiento. - Homing Smoothing: Ajusta el
HomingAccelerationMagnitudey elHomingTurnSpeedpara un seguimiento más suave o más agresivo. Podrías incluso interpolar estos valores. - Anticipación del Objetivo: Para objetivos en movimiento rápido, el proyectil podría intentar predecir la posición futura del objetivo en lugar de apuntar a su posición actual. Esto requiere cálculos de trayectoria más avanzados.
- Limitaciones de Homing: Añade un límite de distancia de búsqueda o un tiempo máximo para el
homingantes de que el proyectil simplemente continúe en línea recta. - Diferentes Tipos de Búsqueda: Implementa diferentes estrategias de búsqueda, como búsqueda en arco, búsqueda con retardo, o búsqueda que se desactiva al pasar cerca del objetivo.
- Aplicación de Daño y Estados: Implementa una interfaz de daño en tus personajes para que el
OnHitdel proyectil pueda llamar a una funciónTakeDamageen el objetivo, y aplicar estados (quemado, lento, etc.).
Preguntas Frecuentes (FAQ)
P: Mi proyectil no sigue al objetivo, solo vuela en línea recta.
R: Verifica que hayas añadido el ProjectileHomingComponent a tu BP_HomingProjectile y que Set Homing Target esté recibiendo un actor válido. Además, revisa que bIsHomingProjectile esté activo en el ProjectileMovementComponent cuando se asigna un objetivo, y que HomingAccelerationMagnitude sea un valor lo suficientemente alto.
P: ¿Cómo puedo hacer que el proyectil tenga un rastro de partículas?
R: En tu BP_HomingProjectile, selecciona el ParticleFXComponent y asigna un Template de sistema de partículas (como un P_SmokeStack_01 o similar).
P: Quiero que mi proyectil caiga con gravedad.
R: En el ProjectileMovementComponent de tu BP_HomingProjectile, establece Projectile Gravity Scale a un valor mayor que 0.0 (ej. 1.0 para gravedad normal).
🏁 Conclusión
¡Felicidades! Has creado un sistema de lanzamiento de proyectiles dirigido en Unreal Engine 5 utilizando la potencia de C++ para la lógica central y la flexibilidad de Blueprints para la configuración. Este sistema es una base excelente que puedes expandir y adaptar para una amplia gama de habilidades, hechizos y ataques en tus juegos.
La combinación de estas dos herramientas te permite crear mecánicas de juego complejas de manera eficiente y escalable. ¡Ahora, a experimentar y llevar tus juegos al siguiente nivel!
Tutoriales relacionados
- Creación de un Sistema de Progresión de Habilidades (Skill Tree) en Unreal Engine 5intermediate18 min
- Unreal Engine: Creando Interactividad con el Sistema de Interacciones Basado en Componentesintermediate15 min
- Unreal Engine: Creando un Sistema de Teletransporte con Portales Dinámicos y FXintermediate25 min
- Unreal Engine: Implementando IA Básica con Behaviour Trees y EQS para Enemigosintermediate15 min
- Unreal Engine: Creación de un Sistema de Diálogos Ramificados con Data Tables y Blueprintintermediate15 min
Comentarios (0)
Aún no hay comentarios. ¡Sé el primero!