Nim

Futuregames Game Project

Platforms:

PC

Engine:

Unreal Engine 4

Language:

C++

VCS:

Perforce

PM Tool:

Trello

Date:

2019

Duration of the project:

4 weeks

Type 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.