Esta página abrange alguns conceitos avançados de programação da Unreal Engine. Para as explicações abaixo, presume-se que você conheça C# em Unity e queira aprender C++ na Unreal Engine. Dito isso, para a maioria das ações, você pode usar Blueprint e chegar aos mesmos resultados, por isso adicionamos exemplos em C++ e Blueprint sempre que possível.
Vamos falar sobre alguns padrões de jogabilidade mais comuns e como tratar deles na Unreal Engine. Os exemplos abaixo abordam algumas funções comuns do Unity e como implementar o mesmo recurso na Unreal Engine.
Como instanciar GameObject/ator de geração
No Unity, você usava a função Instantiate para criar instâncias novas de objetos. A função recebia o tipo UnityEngine.Object (GameObject, MonoBehaviour, etc.) e fazia uma cópia dele.
public GameObject EnemyPrefab;
public Vector3 SpawnPosition;
public Quaternion SpawnRotation;
void Start()
{
GameObject NewGO = (GameObject)Instantiate(EnemyPrefab, SpawnPosition, SpawnRotation);
NewGO.name = "MyNewGameObject";
}
A Unreal Engine possui duas funções diferentes para instanciar objetos:
NewObjectcria novos tipos deUObject.SpawnActorgera tiposAActor.
UObjects e NewObject
Criar subclasses de UObject na Unreal Engine é semelhante a criar subclasses ScriptableObject no Unity. Essas classes de jogabilidade são úteis e não precisam ser geradas no mundo nem anexadas a componentes, como os atores.
No Unity, se você tiver criado sua própria subclasse de ScriptableObject, será preciso instanciá-la desta maneira:
MyScriptableObject NewSO = ScriptableObject.CreateInstance<MyScriptableObject>();
Na Unreal Engine, se você criar seu próprio tipo derivado de UObject, ele poderá ser instanciado desta maneira:
UMyObject* NewObj = NewObject<UMyObject>();
AActors e SpawnActor
Os atores são gerados por meio do método SpawnActor em um objeto de mundo (UWorld no C++). Alguns UObjects vêm com um método GetWorld (todos os atores também, por exemplo). É por meio desse método que você obtém um objeto do mundo.
No exemplo a seguir, observe que, em vez de passar em outro ator, passamos a classe do ator que queremos gerar. No nosso exemplo, a classe pode ser qualquer subclasse de AMyEnemy.
E se você quiser fazer uma cópia de outro objeto igual ao uso de Instantiate no Unity permite fazer?
As funções NewObject e SpawnActor também podem receber um objeto de modelo o qual manipularão. A Unreal Engine fará uma cópia do objeto em vez de criar um novo objeto do zero. Assim, todos os componentes e UPROPERTYs serão copiados.
AMyActor* CreateCloneOfMyActor(AMyActor* ExistingActor, FVector SpawnLocation, FRotator SpawnRotation)
{
UWorld* World = ExistingActor->GetWorld();
FActorSpawnParameters SpawnParams;
SpawnParams.Template = ExistingActor;
World->SpawnActor<AMyActor>(ExistingActor->GetClass(), SpawnLocation, SpawnRotation, SpawnParams);
}
Você deve estar se perguntando o que "do zero" significa nessa situação. Cada classe de objeto criada tem um modelo padrão que contém valores padrão para suas respectivas propriedades e componentes. Se você não substituir as propriedades e não fornecer seu próprio modelo, a Unreal Engine usará esses valores padrão para construir seu objeto. Para ajudar a demonstrar isso, vejamos primeiro o exemplo MonoBehaviour:
public class MyComponent : MonoBehaviour
{
public int MyIntProp = 42;
public SphereCollider MyCollisionComp = null;
void Start()
{
// Crie o componente de colisão se ainda não tiver um
if (MyCollisionComp == null)
{
MyCollisionComp = gameObject.AddComponent<SphereCollider>();
MyCollisionComp.center = Vector3.zero;
MyCollisionComp.radius = 20.0f;
}
}
}
No exemplo acima, temos uma propriedade int cujo valor padrão é 42 e um componente SphereCollider cujo raio padrão é 20.
Podemos conseguir a mesma coisa na Unreal usando o construtor do objeto:
UCLASS()
class AMyActor : public AActor
{
GENERATED_BODY()
UPROPERTY()
int32 MyIntProp;
UPROPERTY()
USphereComponent* MyCollisionComp;
AMyActor()
{
MyIntProp = 42;
MyCollisionComp = CreateDefaultSubobject<USphereComponent>(FName(TEXT("CollisionComponent"));
MyCollisionComp->RelativeLocation = FVector::ZeroVector;
MyCollisionComp->SphereRadius = 20.0f;
}
};
Definimos os valores padrão da propriedade para a classe no construtor de AMyActor. Observe que o uso da função CreateDefaultSubobject permite a criação de componentes e a atribuição de propriedades padrão a ele. Todos os subobjetos criados com essa função se comportam como um modelo padrão, assim, é possível modificá-los na subclasse ou Blueprint.
Como projetar de um tipo em outro
Nesse caso, obtemos um componente que sabemos que temos, depois convertemos em um tipo específico e fazemos algo condicionalmente.
C# em Unity
Collider collider = gameObject.GetComponent<Collider>;
SphereCollider sphereCollider = collider as SphereCollider;
if (sphereCollider != null)
{
// ...
}
C++ na Unreal Engine
UPrimitiveComponent* Primitive = MyActor->GetComponentByClass(UPrimitiveComponent::StaticClass());
USphereComponent* SphereCollider = Cast<USphereComponent>(Primitive);
if (SphereCollider != nullptr)
{
// ...
}
Como destruir o GameObject/ator
Unity
|
C++
|
Blueprint ![]() |
Como destruir o GameObject/atores (com atraso de 1 segundo)
Unity
|
C++
|
Blueprint
|
Como desabilitar GameObjects/atores
Unity
|
C++
|
Blueprint
|
Como acessar o GameObject/ator de um componente
Unity
|
C++
|
Blueprint
|
Como acessar um componente do GameObject/ator
C# em Unity
MyComponent MyComp = gameObject.GetComponent<MyComponent>();
C++ na Unreal Engine
UMyComponent* MyComp = MyActor->FindComponentByClass<UMyComponent>();
Blueprint
Como encontrar GameObjects/atores
C# em Unity
// Localizar GameObject por nome
GameObject MyGO = GameObject.Find("MyNamedGameObject");
// Encontre objetos por tipo
MyComponent[] Components = Object.FindObjectsOfType(typeof(MyComponent)) as MyComponent[];
foreach (MyComponent Component in Components)
{
// ...
}
// Encontre GameObjects por tag
GameObject[] GameObjects = GameObject.FindGameObjectsWithTag("MyTag");
foreach (GameObject GO in GameObjects)
{
// ...
}
C++ na Unreal Engine
// Encontre o ator pelo nome (também funciona em UObjects)
AActor* MyActor = FindObject<AActor>(nullptr, TEXT("MyNamedActor"));
// Encontre atores por tipo (precisa de um objeto UWorld)
for (TActorIterator<AMyActor> It(GetWorld()); It; ++It)
{
AMyActor* MyActor = *It;
// ...
}
Blueprint
C++ na Unreal Engine
// Encontre objetos por tipo
for (TObjectIterator<UMyObject> It; It; ++it)
{
UMyObject* MyObject = *It;
// ...
}
// Encontre atores por tag (também funciona em ActorComponents, mas use TObjectIterator)
for (TActorIterator<AActor> It(GetWorld()); It; ++It)
{
AActor* Actor = *It;
if (Actor->ActorHasTag(FName(TEXT("Mytag"))))
{
// ...
}
}
Blueprint
Como adicionar tags a GameObjects/atores
C# em Unity
MyGameObject.tag = "MyTag";
C++ na Unreal Engine
// Os atores podem ter várias tags
MyActor.Tags.AddUnique(TEXT("MyTag"));
Blueprint
Como adicionar tags a MonoBehaviours/ActorComponents
C# em Unity
// Para alterar a tag no GameObject ao qual foi anexada
MyComponent.tag = "MyTag";
C++ na Unreal Engine
// Os componentes têm sua própria matriz de tags
MyComponent.ComponentTags.AddUnique(TEXT("MyTag"));
Como comparar tags em GameObjects/atores e MonoBehaviours/ActorComponents
C# em Unity
if (MyGameObject.CompareTag("MyTag"))
{
// ...
}
// Verifica a tag no GameObject ao qual foi anexada
if (MyComponent.CompareTag("MyTag"))
{
// ...
}
C++ na Unreal Engine
// Verifica se um ator tem esta tag
if (MyActor->ActorHasTag(FName(TEXT("MyTag"))))
{
// ...
}
Blueprint
C++ na Unreal Engine
// Verifica se um ActorComponent tem esta tag
if (MyComponent->ComponentHasTag(FName(TEXT("MyTag"))))
{
// ...
}
Blueprint
Física: RigidBody e componente primitivo
No Unity, para fornecer qualquer característica física do GameObject, primeiro era necessário fornecer a ele um componente RigidBody.
Na Unreal Engine, qualquer componente primitivo (UPrimitiveComponent no C++) pode ser um objeto físico. Alguns componentes primitivos comuns são:
- Componentes de forma (cápsula, esfera, caixa)
- Componentes de malha estática
- Componentes de malha do esqueleto
Ao contrário do Unity, que separa a visualização e responsabilidades de colisão em componentes separados, a Unreal Engine combina os conceitos de "potencialmente físico" e "potencialmente visível" em um único componente primitivo. Qualquer componente que teria uma geometria no mundo, que pudesse ser renderizado ou ter interação física, é uma subclasse de PrimitiveComponent.
Camadas e canais
Os canais de colisão na Unreal Engine equivalem às camadas do Unity. Para saber mais, consulte Collision Filtering (filtro de colisão).
RayCast e RayTrace
C# em Unity
GameObject FindGOCameraIsLookingAt()
{
Vector3 Start = Camera.main.transform.position;
Vector3 Direction = Camera.main.transform.forward;
float Distance = 100.0f;
int LayerBitMask = 1 << LayerMask.NameToLayer("Pawn");
RaycastHit Hit;
bool bHit = Physics.Raycast(Start, Direction, out Hit, Distance, LayerBitMask);
if (bHit)
{
return Hit.collider.gameObject;
}
return null;
}
C++ na Unreal Engine
APawn* AMyPlayerController::FindPawnCameraIsLookingAt()
{
// Use para personalizar várias propriedades do traçado
FCollisionQueryParams Params;
// Ignore o pawn do jogador
Params.AddIgnoredActor(GetPawn());
// O resultado da incidência é preenchido pelo traçado da linha
FHitResult Hit;
// O traçado de raio emitido da câmera, colide apenas com os pawns (estão no canal de colisão ECC_Pawn)
FVector Start = PlayerCameraManager->GetCameraLocation();
FVector End = Start + (PlayerCameraManager->GetCameraRotation().Vector() * 1000.0f);
bool bHit = GetWorld()->LineTraceSingle(Hit, Start, End, ECC_Pawn, Params);
if (bHit)
{
// O Hit.Actor contém um ponteiro fraco para o ator em que o traçado incide
return Cast<APawn>(Hit.Actor.Get());
}
return nullptr;
}
Blueprint
Clique na imagem para ver no tamanho original.
Volumes de gatilho
C# em Unity
public class MyComponent : MonoBehaviour
{
void Start()
{
collider.isTrigger = true;
}
void OnTriggerEnter(Collider Other)
{
// ...
}
void OnTriggerExit(Collider Other)
{
// ...
}
}
C++ na Unreal Engine
UCLASS()
class AMyActor : public AActor
{
GENERATED_BODY()
// Meu componente de gatilho
UPROPERTY()
UPrimitiveComponent* Trigger;
AMyActor()
{
Trigger = CreateDefaultSubobject<USphereComponent>(TEXT("TriggerCollider"));
// Ambos os colisores precisam definir esta configuração como verdadeiro para os evento serem acionados
Trigger.bGenerateOverlapEvents = true;
// Defina o modo de colisão para o colisor
// Este modo habilita o colisor para traçados de raios, varreduras e sobreposições
Trigger.SetCollisionEnabled(ECollisionEnabled::QueryOnly);
}
virtual void NotifyActorBeginOverlap(AActor* Other) override;
virtual void NotifyActorEndOverlap(AActor* Other) override;
};
Blueprint da Unreal Engine
Para saber mais sobre a configuração de repostas de colisão, consulte a página Collision.
Corpos rígidos cinemáticos
C# em Unity
public class MyComponent : MonoBehaviour
{
void Start()
{
rigidbody.isKinematic = true;
rigidbody.velocity = transform.forward * 10.0f;
}
}
Na Unreal Engine, o componente de colisão e o componente de corpo rígido são o mesmo. A classe-base é UPrimitiveComponent, que tem várias subclasses (USphereComponent, UCapsuleComponent, etc.) para atender às suas necessidades.
C++ na Unreal Engine
UCLASS()
class AMyActor : public AActor
{
GENERATED_BODY()
UPROPERTY()
UPrimitiveComponent* PhysicalComp;
AMyActor()
{
PhysicalComp = CreateDefaultSubobject<USphereComponent>(TEXT("CollisionAndPhysics"));
PhysicalComp->SetSimulatePhysics(false);
PhysicalComp->SetPhysicsLinearVelocity(GetActorRotation().Vector() * 100.0f);
}
};
Evento de entrada
C# em Unity
public class MyPlayerController : MonoBehaviour
{
void Update()
{
if (Input.GetButtonDown("Fire"))
{
// ...
}
float Horiz = Input.GetAxis("Horizontal");
float Vert = Input.GetAxis("Vertical");
// ...
}
}
C++ na Unreal Engine:
UCLASS()
class AMyPlayerController : public APlayerController
{
GENERATED_BODY()
void SetupInputComponent()
{
Super::SetupInputComponent();
InputComponent->BindAction("Fire", IE_Pressed, this, &AMyPlayerController::HandleFireInputEvent);
InputComponent->BindAxis("Horizontal", this, &AMyPlayerController::HandleHorizontalAxisInputEvent);
InputComponent->BindAxis("Vertical", this, &AMyPlayerController::HandleVerticalAxisInputEvent);
}
void HandleFireInputEvent();
void HandleHorizontalAxisInputEvent(float Value);
void HandleVerticalAxisInputEvent(float Value);
};
Blueprint
Esta é a aparência das propriedades de entrada nas Configurações de Projeto:
Para saber mais sobre como definir as entradas para seu projeto da Unreal Engine, consulte a página Input.
