Monday, May 26, 2014

Unreal Engine 4: Come Fly With Me, an Oculus Flying Rail Shooter

Those who have already migrated from Unreal Engine 3 to Unreal Engine 4 know that Unreal Engine 4 is vastly different and that there is a lot to learn… Over last summer I had acquired an Oculus Rift for the purpose of development in UDK.  The project has been just a personal side-project where I had been focused on developing a first-person Star Fox style rail shooter.  Many parts of the project I would like to keep secret until development is in more complete stages.  Since the public release of Unreal Engine 4, I am now focused on migrating my project to Unreal Engine 4, this does mean starting the framework of the project over since Unreal Script is no more, but the logic behind my game will remain the same, so it is a matter of working with different syntax and a different (yet in some ways similar) editor. 
Those who have dove into Unreal Engine 4 know that we have full access to C++, and all the coding can be done in Visual Studio through C++.  For many programmers C++ can be daunting but I assure you Epic Games has done an excellent job with providing an initial framework and many helpful functions which can be used by developers like us.  So let’s get started…
My first steps in redeveloping my game in C++ is to set up my player, controls, physics ect.  For me, a great starting point was the Code First Person which was provided as a starting point by Epic. 


The defaults for this project is a character mesh in first person view which holds a weapon and can walk around in full 3D space.  We of course need to change this up quite a bit if we are to make a Star Fox style rail shooter.



The first steps I will do is change the way in which the player pawn is controlled.  In your newly made Visual Studio Project (note, I am using Visual Studio 2013 for this), you will need to go into your source directory within your project, and find your Character.cpp and Character.h files (this would normally be named [the name of your project]Character).  I will first notice that functions for MoveForward, MoveRight, and turning are already written for us, but I will need the player to move up, a simple way to do this is to copy the contents of an existing movement ( MoveRight), and modify it so that it uses up and down directions.  The MoveRight function looks like this.
void AOculusGameCharacter::MoveRight(float Value)
{
       if (Value != 0.0f)
       {
              // find out which way is right
              const FRotator Rotation = GetControlRotation();
              const FRotator YawRotation(0, Rotation.Yaw, 0);

              // Get right vector
              const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);

              // add movement in that direction
              AddMovementInput(Direction, Value);
       }
}
I will copy it over and modify it by declaring a new function in  the Character header file.
void MoveUp(float Val);

Then calling the function in the Character cpp file.
void AOculusGameCharacter::MoveUp(float Value)
{
}
 Then following the logic of the MoveRight function to find which way is up and down using pitch as a rotator instead of yaw.
const FRotator Rotation = GetControlRotation();
              const FRotator PitchRotation(0, Rotation.Pitch, 0);
Followed by setting the direction vector to the Z direction instead of Y.
const FVector Direction = FRotationMatrix(PitchRotation).GetUnitAxis(EAxis::Z);
And then setting the input to move that direction when the input is pressed.
AddMovementInput(Direction, Value);
You can then encapsulate this logic in an  if condition which finds if the value of the input is not equal to 0.0, then do the movement logic.  Note, this function listens for an input and that input gives us a value depending on a scalar that we can give that input, so in theory we can have the Up key scale the value parameter by 1.0f which will set the Z direction positive which is up, and we could have a Down key scale the value parameter by -1.0f which will make the Z value negative thus moving the character in the down direction.  When we are done writing our function we can make the inputs for this.  In the end my MoveUp function looks like this.
void AOculusGameCharacter::MoveUp(float Value)
{
       if (Value != 0.0f)
       {
              // find out which way is right
              const FRotator Rotation = GetControlRotation();
              const FRotator PitchRotation(0, Rotation.Pitch, 0);

              // Get right vector
              const FVector Direction = FRotationMatrix(PitchRotation).GetUnitAxis(EAxis::Z);

              // add movement in that direction
              AddMovementInput(Direction, Value);
       }
}
Now we need to have button inputs scale the Value parameter as I mentioned in the paragraph above.  So head into your Config folder and open DefaultInput.ini.  You will of course notice ActionNames and AxisNames, since we are scaling a parameter and applying it to a scalar, we will be using AxisNames.  Since we have 2 keys to use, I we will need to have two AxisNames both equal to Move Up.  So to have W equal to the up direction this would be the input for up.
+AxisMappings=(AxisName="MoveUp", Key=W, Scale=1.f)
And if we were wanting the character to move the down direction by pressing the S key, we would simply change the Key to S and change the Scale to -1.f so it can set the Value of the MoveUp parameter to -1.  Additionally, if you already have W and S movements for functions like MoveForward, you should comment these out or delete them.
+AxisMappings=(AxisName="MoveUp", Key=S, Scale=-1.f)
Lastly, you will need to head back into the Character.cpp file and set the key binding for MoveUp within the SetupPlayerInputComponent so that when the player does press one of the MoveUp buttons, it calls the function which sets the direction and character movement.
InputComponent->BindAxis("MoveUp", this, &AOculusGameCharacter::MoveUp);
This should compile with no warnings or errors, but the character will not move up or down at will because by default the character’s physics is set to Walking, which won’t allow a player to move freely into the air.  We need to set the character’s physics to Flying.  In theory you can set this wherever you want, but logically you will want to place this in a spot where it most logically will start.  Since I want flying to start upon player interaction with the game and player starts controlling the game upon firing, I will put a calling to Flying right at the top of the OnFire function.  I may change the way in which the player starts on a future date, but for right now having the player Fire and start will work out for what I am trying to do.  Below is the code that sets the players physics to Flying.  As opposed to Unreal Engine 3, Unreal Engine 4 will use SetMovementMode rather than SetPhysics, inherently this is very similar, since SetMovemementMode contains an enumerator that has various character movement modes such as MOVE_Flying, MOVE_Walking, MOVE_None, ect.
CharacterMovement->SetMovementMode(EMovementMode::MOVE_Flying);
If you play test this you should notice that upon firing, the player will be able to freely fly in all directions… But I want to allow our character to move on a rail and again, similar to Star Fox.  So the easiest way to do that would be to go back to the Character.cpp file and change the MoveFoward function to automatically MoveFoward.  To do that I will eliminate the need to press a button by removing the if condition that checks to see if the Value is not equal to 0.  I will then zero out the YawRotation variable, because forward will just be whichever direction the player spawns at, we no longer care about direction other than what forward is.
FRotator YawRotation(0, 0, 0);
Then I can set the value of AddMovementInput to some constant (note, the higher the constant the faster the player will move forward, I am setting mine to 1).
 AddMovementInput(Direction, 1);
When finished, my MoveFoward function looks like this.
void AOculusGameCharacter::MoveForward(float Value)
{
              // find out which way is forward
              const FRotator Rotation = GetControlRotation();
              FRotator YawRotation(0, 0, 0);

              // Get forward vector
              const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);

              // add movement in that direction
              AddMovementInput(Direction, 1);
       }

Upon play testing we get closer to the results we need, but the player will now rush forward immediately before the player presses fire to actually start playing.  So I figured the best thing to do is to encapsulate the movement logic into a bool.  If the bool is true, we MoveFoward, if not we do nothing.  So you would need to declare a bool variable and define it as false somewhere outside of your function.
bool started = false;
Then upon firing (since firing starts the game at this point), set started equal to true. I set this at the top of my OnFire function.
void AOculusGameCharacter::OnFire()
{
       CharacterMovement->SetMovementMode(EMovementMode::MOVE_Flying);
       started = true;
       // try and fire a projectile
       if (ProjectileClass != NULL)
       {
              const FRotator SpawnRotation = GetControlRotation();
              // MuzzleOffset is in camera space, so transform it to world space before offsetting from the character location to find the final muzzle position
              const FVector SpawnLocation = GetActorLocation() + SpawnRotation.RotateVector(GunOffset);

              UWorld* const World = GetWorld();
              if (World != NULL)
              {
                     // spawn the projectile at the muzzle
                     World->SpawnActor<AOculusGameProjectile>(ProjectileClass, SpawnLocation, SpawnRotation);
              }
       }

       // try and play the sound if specified
       if (FireSound != NULL)
       {
              UGameplayStatics::PlaySoundAtLocation(this, FireSound, GetActorLocation());
       }

       // try and play a firing animation if specified
       if(FireAnimation != NULL)
       {
              // Get the animation object for the arms mesh
              UAnimInstance* AnimInstance = Mesh1P->GetAnimInstance();
              if(AnimInstance != NULL)
              {
                     AnimInstance->Montage_Play(FireAnimation, 1.f);
              }
       }

}
Lastly, I encapsulated the logic within my MoveFoward function in an If started equals true.
void AOculusGameCharacter::MoveForward(float Value)
{
       if (started == true)
       {
              // find out which way is forward
              const FRotator Rotation = GetControlRotation();
              FRotator YawRotation(0, 0, 0);

              // Get forward vector
              const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);

              // add movement in that direction
              AddMovementInput(Direction, 1);
       }
}
So the logic is basically, started equals false at the start of the program.  When the player presses fire, start is set to true which also begins to move the player forward.  Upon testing the game now, you should notice that the player moves freely up, down left and right, and gets pushed forward.  You will notice that the player can still turn their view with the mouse button, since we are constantly moving forward, when the player turns the player will not switch directions which is exactly what I want.  If for whatever reason you did not want that, for instance if you did not want “head turning”, you can eliminate the functions and function calls of turning (or the keybinds that allow for turning , which is even easier).  Since we are building this for the Oculus, I want the player to freely be able to turn their head as they are flying through the air, again this is a pure design call…
I also want my game to work with the Xbox 360 controller.  By default most of the keybinds are in the DefaultInput.ini file for these controls, but MoveFoward is set to the left stick Y direction (up and down),  we need to set MoveUp to be in the Left stick Y direction instead.
+AxisMappings=(AxisName="MoveUp", Key=Gamepad_LeftY, Scale=1.f)
The right analog stick is set well for the Y direction, (LookUpRate), but not set for the X direction, so to do that I will make the X direction to call turning.
+AxisMappings=(AxisName="Turn", Key=Gamepad_RightX, Scale=1.f)
Since the logic for these functions are already set up, we do not need to do anything else.  If you are doing this all from scratch just note that the logic is very similar to the logic that we did above for MoveUp, we will just be calling and utilizing different axis. If you play test this, you should notice that your Xbox360 controller should work well with the left thumbstick moving the character around, right thumbstick allowing the character to look in 360 degrees, and right trigger allowing the player to fire.
In accordance to my design, I also do not want the player to actually see the character mesh, however upon later testing I may decide to use a character mesh.  So for now one of the tricks that I utilized within the inlitization of my player character in the Character.cpp file is SetOwnerNoSee() which is a function call that can be set to true or false.  If it is true, the player cannot see their own mesh, and if it is false the player can see their own mesh, so I set this to true.
Mesh1P->SetOwnerNoSee(true);
We will now be able to test out the Oculus Rift with this and see if everything with the Oculus Rift works with our likings.  Lucky for us, Oculus Rift for Unreal Engine 4 is plug and play so we may not have to do anything in order to get it to work.


 Just kidding, the Star Fox music is just for fun.  I have something much better in mind for the real game.  In the next Unreal 4 blog I will show what I have done with Landscape.  So I will take a little break with coding and play around with creating an environment that fits the game.

No comments:

Post a Comment