Nim
Futuregames Game Project
Platforms:
PCEngine:
Unreal Engine 4Language:
C++VCS:
PerforcePM Tool:
TrelloDate:
2019Duration of the project:
4 weeksType of game:
Third Person, Puzzle The main mechanic of this game is a thread (energy beam) that you shoot and you can use to move pieces in the map. You are able to grab only specific points on the object and then attach it wherever you want.
In this image you can see the "CPP_ThreadPointComp" that is used in objects that can be attached to something (can be used mutiple in one actor).
This component lets the thread to be attached to the object. It's really simple to setup, once you have
added the component you just need to position it wherever you want and the select the size of the box.
In this image you can see the "CPP_ThreadComp" that is used in the player, and inside this component it's done all the work
to attach objects together.
The system uses the Unreal Physics Constraint Component.
This is the main code for attaching objects together:
Attach to the first object
Attach to the first object
if (ThreadPointComp && !Enemy)
{
// Checking if the component has less threads than the max thread slots
if (ThreadPointComp->GetNumThread() < MaxThreadSlots)
{
FActorSpawnParameters SpawnParams;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
// Spawn the BP_Thread actor in the socket of the mesh
Thread = GetWorld()->SpawnActor(ActorThread, Mesh->GetSocketTransform(SocketSpawn, RTS_World), SpawnParams);
// Attach the BP_Thread actor in the socket
FAttachmentTransformRules AttachRules(EAttachmentRule::SnapToTarget, true);
Thread->AttachToComponent(Mesh, AttachRules, SocketSpawn);
// Set the constraint with SphereA with the mesh ( bone hand_r)
Thread->PhysicsConstraintA->SetConstrainedComponents(Thread->SphereA, NAME_None, Mesh, "hand_r");
// Set the location of the second sphere SphereB to the location of the component
Thread->SphereB->SetWorldLocation(Hit.Component->GetComponentLocation(), false, false);
// Set the constraint with SphereB with the RootComponent hit location
UPrimitiveComponent* Comp = Cast(Hit.GetActor()->GetRootComponent());
Thread->PhysicsConstraintB->SetConstrainedComponents(Thread->SphereB, NAME_None, Comp, Hit.BoneName);
ThreadPointComp->IncreaseNumThread(Thread);
bAttached = true;
}
}
else
{
if (Enemy)
{
ThreadPointComp = Enemy->ThreadPointComp;
if (ThreadPointComp->GetNumThread() < MaxThreadSlots)
{
FActorSpawnParameters SpawnParams;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
// Spawn the BP_Thread actor in the socket of the mesh
Thread = GetWorld()->SpawnActor(ActorThread, Mesh->GetSocketTransform(SocketSpawn, RTS_World), SpawnParams);
// Attach the BP_Thread actor in the socket
FAttachmentTransformRules AttachRules(EAttachmentRule::SnapToTarget, true);
Thread->AttachToComponent(Mesh, AttachRules, SocketSpawn);
// Set the constraint with SphereA with the mesh ( bone hand_r)
Thread->PhysicsConstraintA->SetConstrainedComponents(Thread->SphereA, NAME_None, Mesh, "hand_r");
// Set the location of the second sphere SphereB to the location of the component
Thread->SphereB->SetWorldLocation(Hit.ImpactPoint, false, false);
Enemy->GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision);
Enemy->GetMesh()->SetAnimInstanceClass(nullptr);
Enemy->DetachFromControllerPendingDestroy();
Enemy->Controller = nullptr;
Enemy->GetMesh()->SetSimulatePhysics(true);
UPrimitiveComponent* Comp = Cast(Enemy->GetMesh());
Thread->PhysicsConstraintB->SetConstrainedComponents(Thread->SphereB, NAME_None, Comp, Hit.BoneName);
ThreadPointComp->IncreaseNumThread(Thread);
bAttached = true;
}
}
}
Attach everywhere
Attach everywhere
void UCPP_ThreadComp::Release(UCameraComponent* Camera)
{
if (Camera && bAttached)
{
TArray ActorsToIgnore;
FHitResult Hit;
FVector StartLocation = (Camera->GetForwardVector() * 200.f) + Camera->GetComponentLocation();
FVector LookLocation = (Camera->GetForwardVector() * MaxDistanceThread) + Camera->GetComponentLocation();
if (UKismetSystemLibrary::LineTraceSingleForObjects(GetWorld(), StartLocation,
LookLocation, ObjectTypes, false, ActorsToIgnore, EDrawDebugTrace::None, Hit,
true, FColor::Red, FColor::Green, 2.f))
{
// Detach the SphereA from the socket of the mesh
FDetachmentTransformRules DetachRules(EDetachmentRule::KeepWorld, false);
Thread->DetachFromActor(DetachRules);
// Set the location of the SphereA to the location of the hit
Thread->SphereA->SetWorldLocation(Hit.ImpactPoint, false, false);
// Enable back Physics FROM DEAFULT IT'S PHYSICS DISABLE THE SphereA TO AVOID BUGS
Thread->SphereA->SetSimulatePhysics(true);
// Set the constraint with SphereB with the hit location
Thread->PhysicsConstraintA->SetConstrainedComponents(Thread->SphereA, NAME_None, Hit.GetComponent(), Hit.BoneName);
bAttached = false;
}
else
{
bAttached = false;
ACPP_Character* Player = Cast(GetOwner());
if (Player)
{
Player->Missed();
}
Thread->Destroy();
}
}
}
The AI uses the AIPerception component for the sight (all made in C++). It's a ranged AI that can't move, as soon as it
sees the player it will start shooting rocks that will explode and push you off the platforms.
Working together with the animator we achieved a great look of the throw. Being familiar with the Unreal animator I set it up.
I used animation notifies to make the rock mesh spawn when he has the hands behind, so the player is not able to see the
mesh suddendly appear. Then I attach the rock to a socket in the hand of the skeletal mesh, by doing so the rock will follow
the movements of the enemy hand, and as soon as the animations reaches the notify in the timeline, an event
it's called and the mesh will be destroyed. Instead an actor will spawn that as the same mesh of the rock, but this one will
be launched and at impact it will explode.
The AI can be defeated simply by using the energy beam and attach it wherever you want.