Mario Galaxy's Gravity and Launch Star Mechanics

Game Engine - Unreal Engine, C++

Learning Outcome - Creating Spherical Gravity and Understanding Spline Component

About the Project :

  • I implemented spherical gravity by adding Physical velocity to Player at each frames.
  • I also created Launch Star system simialr to Original game using Unreal Engine's built-in Spline Component.
  • Unreal Engine's 3rd Person Example Project's default animations were not working because of Spherical Gravity System I have added to Characer. Therefore, I had to change animation blueprints that can work well with character animation and my gravity system.
  • Future Update - Enemies and Camera like Original Game
  • Code Snippets are below.

PlayerPawn Class :

    void AMarioGalaxyPawn::Tick(float DeltaTime)
    {
        Super::Tick(DeltaTime);
                     
        //return if player is not in normal state
        if (CurrentState != EPlayerState::NORMAL)
            return;
                          
        else
        {
            //Rotation from current location to Planet's center
            FRotator NewRotation = UKismetMathLibrary::FindLookAtRotation(CurrentPlanet->GetActorLocation(), GetActorLocation());
                          
            SetActorRotation(UKismetMathLibrary::MakeRotFromZX(UKismetMathLibrary::GetForwardVector(NewRotation), GetActorForwardVector()));
                    
            //Applying Physics velocity to Player
            CharacterCollision->SetPhysicsLinearVelocity(UKismetMathLibrary::GetForwardVector(NewRotation) * GravityForce, true);
             
            //Setting Input vector for turning Player mesh and animation
            InputVector = FVector(ForwardInputValue, RightInputValue, 0.0f);
                          
            //if input vector is 0, then return, else rotate player mesh
            if (InputVector.Size() == 0.0f)
                return;
                          
            FRotator NewInputRotation(0.0f, FollowCamera->GetRelativeRotation().Yaw + (InputVector.Rotation().Yaw - 90.0f), 0.0f);
                          
            CharacterMesh->SetRelativeRotation(NewInputRotation);
                          
            //Clamp Physical Velocity for running around in same speed for all direction
            FVector CurrentVelocity = CharacterCollision->GetPhysicsLinearVelocity().GetClampedToSize(MinimumVelocity, MaximumVelocity);
                          
            //GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Orange, FString::Printf(TEXT("%f"), CurrentVelocity.Size()));
                          
            CharacterCollision->SetPhysicsLinearVelocity(CurrentVelocity);
        }
    }

    void AMarioGalaxyPawn::MoveForward(float Value)
    {
	      if (CurrentState != EPlayerState::NORMAL)
		        return;
	
        /*FRotator NewRotation(0.0f, FollowCamera->GetRelativeRotation().Yaw + (-90.0f * Value), 0.0f);

	      CharacterMesh->SetRelativeRotation(NewRotation);*/
	
        //setting input value for input vector 
	      ForwardInputValue = Value;

        //adding physical velocity for forward movement
	      FVector Move = CharacterCollision->GetForwardVector() * Value * Acceleration;
	      CharacterCollision->SetPhysicsLinearVelocity(Move, true);

	      //CharacterCollision->GetPhysicsLinearVelocity();

	      //GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Orange, FString::Printf(TEXT("%s"), *CharacterCollision->GetPhysicsLinearVelocity().ToString()));
    }

    void AMarioGalaxyPawn::MoveRight(float Value)
    {
	      if (CurrentState != EPlayerState::NORMAL)
		        return;
	
        /*FRotator NewRotation(0.0f, FollowCamera->GetRelativeRotation().Yaw + (180.0f * Value), 0.0f);

	      CharacterMesh->SetRelativeRotation(NewRotation);*/
	      
        //setting input value for input vector 
        RightInputValue = Value;

        //adding physical velocity for right movement
	      FVector Move = CharacterCollision->GetRightVector() * Value * Acceleration;
	      CharacterCollision->SetPhysicsLinearVelocity(Move, true);
    }

    void AMarioGalaxyPawn::Jump()
    {
	      if (!CanJump || CurrentState != EPlayerState::NORMAL)
		        return;

      	MaximumVelocity = 2000.0f;

        //adding physical velocity for jump
	      FVector Move = CharacterCollision->GetUpVector() * JumpForce * Acceleration;
	      CharacterCollision->SetPhysicsLinearVelocity(Move, true);
    }
                        

Launch Star :

    void ALaunchingStart::Tick(float DeltaTime)
    {
        Super::Tick(DeltaTime);
                
        //when player touch the start, start moving player along spline 
        if (PlayerTarget != nullptr && bCanMoveActor)
        {
            //calculating time for getting distance of spline from starting point, based on time to complete spline path 
            float CurrentSplineTime = (GetWorld()->GetTimeSeconds() - StartTime) / TimeToCompletePath;
            
            //getting current distance on spline from time
            float Distance = SplinePath->GetSplineLength() * CurrentSplineTime;
                         
            //getting current position on spline from distance
            FVector Position = SplinePath->GetLocationAtDistanceAlongSpline(Distance, ESplineCoordinateSpace::World);
                       
            //getting current direction on spline from distance
            FVector Direction = SplinePath->GetDirectionAtDistanceAlongSpline(Distance, ESplineCoordinateSpace::Local);
                         
            //setting player position on spline
            PlayerRootPoint->SetWorldLocation(Position);
                 
            //setting player forward vector in spline direction
            FRotator NewRotator = UKismetMathLibrary::MakeRotFromX(Direction);
            PlayerRootPoint->SetWorldRotation(NewRotator);
                          
            //Player about to reach end
            if (CurrentSplineTime >= 0.90f && PlayerTarget->IsStartedRolling == false)
                PlayerTarget->IsStartedRolling = true;
                              
            //Player reach the end                          
            if (CurrentSplineTime >= 1.0f)
            {
                bCanMoveActor = false;
                          
                //dettach Player from scene component of star actor 
                PlayerTarget->RemoveFromRoot();
                     
                //this method will start applying physical velocity to player, for spherical gravity
                PlayerTarget->EndOfSplineFollow();
            }
        }
                          
    }
       
    //Player touch the star
    void ALaunchingStart::OnStarBeginOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
    {
        if ((OtherActor != nullptr) && (OtherActor != this) && (OtherComp != nullptr))
        {
            if (OtherActor != Cast(OtherActor))
                return;
                          
            PlayerTarget = Cast(OtherActor);
                          
            PlayerTarget->AttachPlayerToStar();
              
            //attach player to scene component of star actor, this "PlayerRootPoint" component will follow the spline path 
            FAttachmentTransformRules TransformRules = FAttachmentTransformRules(EAttachmentRule::SnapToTarget, EAttachmentRule::SnapToTarget, EAttachmentRule::KeepWorld, true);
                              
            PlayerTarget->AttachToComponent(PlayerRootPoint, TransformRules);
                          
            //GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Orange, FString::Printf(TEXT("Collision Occured")));
                          
            //Pull back the player actor
            //PullBack(); call launch() inside PullBack
                          
            LaunchPlayer();
        }
    }
                          
    void ALaunchingStart::LaunchPlayer()
    {
        //get time when player touch the star 
        StartTime = GetWorld()->GetTimeSeconds();

        //this bool will trigger Tick to move player along spline
        bCanMoveActor = true;
    }
                        

Inspired by :

Resources :