After some interest in an old post here , I spent the weekend frankensteining snippets of code to make a single script FPCC that could be used like the FPCC from Unity 4~5, including the crouch and run from Aldo, and I also added farcry sliding. There is a Menu Item function to easily create and configure a new FPCC object. Otherwise very easy to set up, it only requires a layer to exclude itself from sphere/raycasts. There is a check to see if there is enough headroom to stand from crouching (it hasnāt pushed through the floor yetā¦). It also has the basic function to affect non-kinematic rigidbodies.
Made in Unity 2018.4, itās been a long time since I even opened Unity, so I donāt know what the standard assets are like these days, or what else is out there. Let me know what you think!
Smooth Look
Jump
Crouch
Run
Slide
Slip down steep angles
Ground Check
Ceiling Check
Setup : the menu item does everything below, except for creating the player layer. By default (for easy use in new projects), it assigns the Ignore Raycast layer.
The Player gameobject needs itās own layer, then the Culling Mask has to be assigned to ignore that layer, so the raycasts can detect the world and not the player.
-
Create an Empty GameObject. Name it Player
-
Create and assign a Layer for the Player gameobject. Click on the button next to Layer > Add Layerā¦ > Type Player in an empty user layer. Click on the Player gameobject and confirm the Layer is assigned as Player
-
Attach the BasicFPCC script. This will also add the CharacterController component. Change the Center variable on the CharacterController component to X 0, Y 1, Z 0
-
Drag the Main Camera as a child of the Player gameobject. Set the Transform Position to X 0, Y 1.7, Z 0 (Rotation 0, Scale 1)
-
(optional player graphic object) Create a cylinder as a child of the Player gameobject. Remove the collider component. Set the Transform Position to X 0, Y 1, Z 0 (Rotation 0, Scale 1)
-
Select the Player gameobject. In the Inspector, drag in the Camera (and GFX if created). Under Grounded Settings, set Casting Mask to Everything, then uncheck the Player layer (it then shows mixed, you can click to confirm)
-
Under Debug, check the box to show where the grounded spherecast starts and ends (starts inside the base of the capsule and then up for the default skin width, set with the groundCheckY variable). When in play mode it also shows a blue line as the slope direction, and a green line as the player gravity and when slipping down steep slopes. The Slope Limit variable is in the CharacterController component.
F to slide, while running (Left Shift)
Toggle lock cursor with the ` console key [~]
// ------------------------------------------
// BasicFPCC.cs
// a basic first person character controller
// with jump, crouch, run, slide
// 2020-10-04 Alucard Jay Kay
// ------------------------------------------
// source :
// https://discussions.unity.com/t/855344
// Brackeys FPS controller base :
// https://www.youtube.com/watch?v=_QajrabyTJc
// smooth mouse look :
// https://discussions.unity.com/t/710168/2
// ground check : (added isGrounded)
// https://gist.github.com/jawinn/f466b237c0cdc5f92d96
// run, crouch, slide : (added check for headroom before un-crouching)
// https://answers.unity.com/questions/374157/character-controller-slide-action-script.html
// interact with rigidbodies :
// https://docs.unity3d.com/2018.4/Documentation/ScriptReference/CharacterController.OnControllerColliderHit.html
// ** SETUP **
// Assign the BasicFPCC object to its own Layer
// Assign the Layer Mask to ignore the BasicFPCC object Layer
// CharacterController (component) : Center => X 0, Y 1, Z 0
// Main Camera (as child) : Transform : Position => X 0, Y 1.7, Z 0
// (optional GFX) Capsule primitive without collider (as child) : Transform : Position => X 0, Y 1, Z 0
// alternatively :
// at the end of this script is a Menu Item function to create and auto-configure a BasicFPCC object
// GameObject -> 3D Object -> BasicFPCC
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR // only required if using the Menu Item function at the end of this script
using UnityEditor;
#endif
[RequireComponent(typeof(CharacterController))]
public class BasicFPCC : MonoBehaviour
{
[Header("Layer Mask")]
[Tooltip("Layer Mask for sphere/raycasts. Assign the Player object to a Layer, then Ignore that layer here.")]
public LayerMask castingMask; // Layer mask for casts. You'll want to ignore the player.
// - Components -
private CharacterController controller; // CharacterController component
private Transform playerTx; // this player object
[Header("Main Camera")]
[Tooltip("Drag the FPC Camera here")]
public Transform cameraTx; // Main Camera, as child of BasicFPCC object
[Header("Optional Player Graphic")]
[Tooltip("optional capsule to visualize player in scene view")]
public Transform playerGFX; // optional capsule graphic object
[Header("Inputs")]
[Tooltip("Disable if sending inputs from an external script")]
public bool useLocalInputs = true;
[Space(5)]
public string axisLookHorzizontal = "Mouse X"; // Mouse to Look
public string axisLookVertical = "Mouse Y"; //
public string axisMoveHorzizontal = "Horizontal"; // WASD to Move
public string axisMoveVertical = "Vertical"; //
public KeyCode keyRun = KeyCode.LeftShift; // Left Shift to Run
public KeyCode keyCrouch = KeyCode.LeftControl; // Left Control to Crouch
public KeyCode keyJump = KeyCode.Space; // Space to Jump
public KeyCode keySlide = KeyCode.F; // F to Slide (only when running)
public KeyCode keyToggleCursor = KeyCode.BackQuote; // ` to toggle lock cursor (aka [~] console key)
// Input Variables that can be assigned externally
// the cursor can also be manually locked or freed by calling the public void SetLockCursor( bool doLock )
[HideInInspector] public float inputLookX = 0; //
[HideInInspector] public float inputLookY = 0; //
[HideInInspector] public float inputMoveX = 0; // range -1f to +1f
[HideInInspector] public float inputMoveY = 0; // range -1f to +1f
[HideInInspector] public bool inputKeyRun = false; // is key Held
[HideInInspector] public bool inputKeyCrouch = false; // is key Held
[HideInInspector] public bool inputKeyDownJump = false; // is key Pressed
[HideInInspector] public bool inputKeyDownSlide = false; // is key Pressed
[HideInInspector] public bool inputKeyDownCursor = false; // is key Pressed
[Header("Look Settings")]
public float mouseSensitivityX = 2f; // speed factor of look X
public float mouseSensitivityY = 2f; // speed factor of look Y
[Tooltip("larger values for less filtering, more responsiveness")]
public float mouseSnappiness = 20f; // default was 10f; larger values of this cause less filtering, more responsiveness
public bool invertLookY = false; // toggle invert look Y
public float clampLookY = 90f; // maximum look up/down angle
[Header("Move Settings")]
public float crouchSpeed = 3f; // crouching movement speed
public float walkSpeed = 7f; // regular movement speed
public float runSpeed = 12f; // run movement speed
public float slideSpeed = 14f; // slide movement speed
public float slideDuration = 2.2f; // duration of slide
public float gravity = -9.81f; // gravity / fall rate
public float jumpHeight = 2.5f; // jump height
[Header("Grounded Settings")]
[Tooltip("The starting position of the isGrounded spherecast. Set to the sphereCastRadius plus the CC Skin Width. Enable showGizmos to visualize.")]
// this should be just above the base of the cc, in the amount of the skin width (in case the cc sinks in)
//public float startDistanceFromBottom = 0.2f;
public float groundCheckY = 0.33f; // 0.25 + 0.08 (sphereCastRadius + CC skin width)
[Tooltip("The position of the ceiling checksphere. Set to the height minus sphereCastRadius plus the CC Skin Width. Enable showGizmos to visualize.")]
// this should extend above the cc (by approx skin width) so player can still move when not at full height (not crouching, trying to stand up),
// otherwise if it's below the top then the cc gets stuck
public float ceilingCheckY = 1.83f; // 2.00 - 0.25 + 0.08 (height - sphereCastRadius + CC skin width)
[Space(5)]
public float sphereCastRadius = 0.25f; // radius of area to detect for ground
public float sphereCastDistance = 0.75f; // How far spherecast moves down from origin point
[Space(5)]
public float raycastLength = 0.75f; // secondary raycasts (match to sphereCastDistance)
public Vector3 rayOriginOffset1 = new Vector3(-0.2f, 0f, 0.16f);
public Vector3 rayOriginOffset2 = new Vector3(0.2f, 0f, -0.16f);
[Header("Debug Gizmos")]
[Tooltip("Show debug gizmos and lines")]
public bool showGizmos = false; // Show debug gizmos and lines
// - private reference variables -
private float defaultHeight = 0; // reference to scale player crouch
private float cameraStartY = 0; // reference to move camera with crouch
[Header("- reference variables -")]
public float xRotation = 0f; // the up/down angle the player is looking
private float lastSpeed = 0; // reference for calculating speed
private Vector3 fauxGravity = Vector3.zero; // calculated gravity
private float accMouseX = 0; // reference for mouse look smoothing
private float accMouseY = 0; // reference for mouse look smoothing
private Vector3 lastPos = Vector3.zero; // reference for player velocity
[Space(5)]
public bool isGrounded = false;
public float groundSlopeAngle = 0f; // Angle of the slope in degrees
public Vector3 groundSlopeDir = Vector3.zero; // The calculated slope as a vector
private float groundOffsetY = 0; // calculated offset relative to height
public bool isSlipping = false;
[Space(5)]
public bool isSliding = false;
public float slideTimer = 0; // current slide duration
public Vector3 slideForward = Vector3.zero; // direction of the slide
[Space(5)]
public bool isCeiling = false;
private float ceilingOffsetY = 0; // calculated offset relative to height
[Space(5)]
public bool cursorActive = false; // cursor state
void Start()
{
Initialize();
}
void Update()
{
ProcessInputs();
ProcessLook();
ProcessMovement();
}
void Initialize()
{
if ( !cameraTx ) { Debug.LogError( "* " + gameObject.name + ": BasicFPCC has NO CAMERA ASSIGNED in the Inspector *" ); }
controller = GetComponent< CharacterController >();
playerTx = transform;
defaultHeight = controller.height;
lastSpeed = 0;
fauxGravity = Vector3.up * gravity;
lastPos = playerTx.position;
cameraStartY = cameraTx.localPosition.y;
groundOffsetY = groundCheckY;
ceilingOffsetY = ceilingCheckY;
RefreshCursor();
}
void ProcessInputs()
{
if ( useLocalInputs )
{
inputLookX = Input.GetAxis( axisLookHorzizontal );
inputLookY = Input.GetAxis( axisLookVertical );
inputMoveX = Input.GetAxis( axisMoveHorzizontal );
inputMoveY = Input.GetAxis( axisMoveVertical );
inputKeyRun = Input.GetKey( keyRun );
inputKeyCrouch = Input.GetKey( keyCrouch );
inputKeyDownJump = Input.GetKeyDown( keyJump );
inputKeyDownSlide = Input.GetKeyDown( keySlide );
inputKeyDownCursor = Input.GetKeyDown( keyToggleCursor );
}
if ( inputKeyDownCursor )
{
ToggleLockCursor();
}
}
void ProcessLook()
{
accMouseX = Mathf.Lerp( accMouseX, inputLookX, mouseSnappiness * Time.deltaTime );
accMouseY = Mathf.Lerp( accMouseY, inputLookY, mouseSnappiness * Time.deltaTime );
float mouseX = accMouseX * mouseSensitivityX * 100f * Time.deltaTime;
float mouseY = accMouseY * mouseSensitivityY * 100f * Time.deltaTime;
// rotate camera X
xRotation += ( invertLookY == true ? mouseY : -mouseY );
xRotation = Mathf.Clamp( xRotation, -clampLookY, clampLookY );
cameraTx.localRotation = Quaternion.Euler( xRotation, 0f, 0f );
// rotate player Y
playerTx.Rotate( Vector3.up * mouseX );
}
void ProcessMovement()
{
// - variables -
float vScale = 1f; // for calculating GFX scale (optional)
float h = defaultHeight;
float nextSpeed = walkSpeed;
Vector3 calc; // used for calculations
Vector3 move; // direction calculation
// player current speed
float currSpeed = ( playerTx.position - lastPos ).magnitude / Time.deltaTime;
currSpeed = ( currSpeed < 0 ? 0 - currSpeed : currSpeed ); // abs value
// - Check if Grounded -
GroundCheck();
isSlipping = ( groundSlopeAngle > controller.slopeLimit ? true : false );
// - Check Ceiling above for Head Room -
CeilingCheck();
// - Run and Crouch -
// if grounded, and not stuck on ceiling
if ( isGrounded && !isCeiling && inputKeyRun )
{
nextSpeed = runSpeed; // to run speed
}
if ( inputKeyCrouch ) // crouch
{
vScale = 0.5f;
h = 0.5f * defaultHeight;
nextSpeed = crouchSpeed; // slow down when crouching
}
// - Slide -
// if not sliding, and not stuck on ceiling, and is running
if ( !isSliding && !isCeiling && inputKeyRun && inputKeyDownSlide ) // slide
{
// check velocity is faster than walkSpeed
if ( currSpeed > walkSpeed )
{
slideTimer = 0; // start slide timer
isSliding = true;
slideForward = ( playerTx.position - lastPos ).normalized;
}
}
lastPos = playerTx.position; // update reference
// check slider timer and velocity
if ( isSliding )
{
nextSpeed = currSpeed; // default to current speed
move = slideForward; // set input to direction of slide
slideTimer += Time.deltaTime; // slide timer
// if timer max, or isSliding and not moving, then stop sliding
if ( slideTimer > slideDuration || currSpeed < crouchSpeed )
{
isSliding = false;
}
else // confirmed player is sliding
{
vScale = 0.5f; // gfx scale
h = 0.5f * defaultHeight; // height is crouch height
nextSpeed = slideSpeed; // to slide speed
}
}
else // - Player Move Input -
{
move = ( playerTx.right * inputMoveX ) + ( playerTx.forward * inputMoveY );
if ( move.magnitude > 1f )
{
move = move.normalized;
}
}
// - Height -
// crouch/stand up smoothly
float lastHeight = controller.height;
float nextHeight = Mathf.Lerp( controller.height, h, 5f * Time.deltaTime );
// if crouching, or only stand if there is no ceiling
if ( nextHeight < lastHeight || !isCeiling )
{
controller.height = Mathf.Lerp( controller.height, h, 5f * Time.deltaTime );
// fix vertical position
calc = playerTx.position;
calc.y += ( controller.height - lastHeight ) / 2f;
playerTx.position = calc;
// offset camera
calc = cameraTx.localPosition;
calc.y = ( controller.height / defaultHeight ) + cameraStartY - ( defaultHeight * 0.5f );
cameraTx.localPosition = calc;
// calculate offset
float heightFactor = ( defaultHeight - controller.height ) * 0.5f;
// offset ground check
groundOffsetY = heightFactor + groundCheckY;
// offset ceiling check
ceilingOffsetY = heightFactor + controller.height - ( defaultHeight - ceilingCheckY );
// scale gfx (optional)
if ( playerGFX )
{
calc = playerGFX.localScale;
calc.y = Mathf.Lerp( calc.y, vScale, 5f * Time.deltaTime );
playerGFX.localScale = calc;
}
}
// - Slipping Jumping Gravity -
// smooth speed
float speed;
if ( isGrounded )
{
if ( isSlipping ) // slip down slope
{
// movement left/right while slipping down
// player rotation to slope
Vector3 slopeRight = Quaternion.LookRotation( Vector3.right ) * groundSlopeDir;
float dot = Vector3.Dot( slopeRight, playerTx.right );
// move on X axis, with Y rotation relative to slopeDir
move = slopeRight * ( dot > 0 ? inputMoveX : -inputMoveX );
// speed
nextSpeed = Mathf.Lerp( currSpeed, runSpeed, 5f * Time.deltaTime );
// increase angular gravity
float mag = fauxGravity.magnitude;
calc = Vector3.Slerp( fauxGravity, groundSlopeDir * runSpeed, 4f * Time.deltaTime );
fauxGravity = calc.normalized * mag;
}
else
{
// reset angular fauxGravity movement
fauxGravity.x = 0;
fauxGravity.z = 0;
if ( fauxGravity.y < 0 ) // constant grounded gravity
{
//fauxGravity.y = -1f;
fauxGravity.y = Mathf.Lerp( fauxGravity.y, -1f, 4f * Time.deltaTime );
}
}
// - Jump -
if ( !isSliding && !isCeiling && inputKeyDownJump ) // jump
{
fauxGravity.y = Mathf.Sqrt( jumpHeight * -2f * gravity );
}
// --
// - smooth speed -
// take less time to slow down, more time speed up
float lerpFactor = ( lastSpeed > nextSpeed ? 4f : 2f );
speed = Mathf.Lerp( lastSpeed, nextSpeed, lerpFactor * Time.deltaTime );
}
else // no friction, speed changes slower
{
speed = Mathf.Lerp( lastSpeed, nextSpeed, 0.125f * Time.deltaTime );
}
// prevent floating if jumping into a ceiling
if ( isCeiling )
{
speed = crouchSpeed; // clamp speed to crouched
if ( fauxGravity.y > 0 )
{
fauxGravity.y = -1f; // 0;
}
}
lastSpeed = speed; // update reference
// - Add Gravity -
fauxGravity.y += gravity * Time.deltaTime;
// - Move -
calc = move * speed * Time.deltaTime;
calc += fauxGravity * Time.deltaTime;
controller.Move( calc );
// - DEBUG -
#if UNITY_EDITOR
// slope angle and fauxGravity debug info
if ( showGizmos )
{
calc = playerTx.position;
calc.y += groundOffsetY;
Debug.DrawRay( calc, groundSlopeDir.normalized * 5f, Color.blue );
Debug.DrawRay( calc, fauxGravity, Color.green );
}
#endif
}
// lock/hide or show/unlock cursor
public void SetLockCursor( bool doLock )
{
cursorActive = doLock;
RefreshCursor();
}
void ToggleLockCursor()
{
cursorActive = !cursorActive;
RefreshCursor();
}
void RefreshCursor()
{
if ( !cursorActive && Cursor.lockState != CursorLockMode.Locked ) { Cursor.lockState = CursorLockMode.Locked; }
if ( cursorActive && Cursor.lockState != CursorLockMode.None ) { Cursor.lockState = CursorLockMode.None; }
}
// check the area above, for standing from crouch
void CeilingCheck()
{
Vector3 origin = new Vector3( playerTx.position.x, playerTx.position.y + ceilingOffsetY, playerTx.position.z );
isCeiling = Physics.CheckSphere( origin, sphereCastRadius, castingMask );
}
// find if isGrounded, slope angle and directional vector
void GroundCheck()
{
//Vector3 origin = new Vector3( transform.position.x, transform.position.y - (controller.height / 2) + startDistanceFromBottom, transform.position.z );
Vector3 origin = new Vector3( playerTx.position.x, playerTx.position.y + groundOffsetY, playerTx.position.z );
// Out hit point from our cast(s)
RaycastHit hit;
// SPHERECAST
// "Casts a sphere along a ray and returns detailed information on what was hit."
if (Physics.SphereCast(origin, sphereCastRadius, Vector3.down, out hit, sphereCastDistance, castingMask))
{
// Angle of our slope (between these two vectors).
// A hit normal is at a 90 degree angle from the surface that is collided with (at the point of collision).
// e.g. On a flat surface, both vectors are facing straight up, so the angle is 0.
groundSlopeAngle = Vector3.Angle(hit.normal, Vector3.up);
// Find the vector that represents our slope as well.
// temp: basically, finds vector moving across hit surface
Vector3 temp = Vector3.Cross(hit.normal, Vector3.down);
// Now use this vector and the hit normal, to find the other vector moving up and down the hit surface
groundSlopeDir = Vector3.Cross(temp, hit.normal);
// --
isGrounded = true;
}
else
{
isGrounded = false;
} // --
// Now that's all fine and dandy, but on edges, corners, etc, we get angle values that we don't want.
// To correct for this, let's do some raycasts. You could do more raycasts, and check for more
// edge cases here. There are lots of situations that could pop up, so test and see what gives you trouble.
RaycastHit slopeHit1;
RaycastHit slopeHit2;
// FIRST RAYCAST
if (Physics.Raycast(origin + rayOriginOffset1, Vector3.down, out slopeHit1, raycastLength))
{
// Debug line to first hit point
#if UNITY_EDITOR
if (showGizmos) { Debug.DrawLine(origin + rayOriginOffset1, slopeHit1.point, Color.red); }
#endif
// Get angle of slope on hit normal
float angleOne = Vector3.Angle(slopeHit1.normal, Vector3.up);
// 2ND RAYCAST
if (Physics.Raycast(origin + rayOriginOffset2, Vector3.down, out slopeHit2, raycastLength))
{
// Debug line to second hit point
#if UNITY_EDITOR
if (showGizmos) { Debug.DrawLine(origin + rayOriginOffset2, slopeHit2.point, Color.red); }
#endif
// Get angle of slope of these two hit points.
float angleTwo = Vector3.Angle(slopeHit2.normal, Vector3.up);
// 3 collision points: Take the MEDIAN by sorting array and grabbing middle.
float[] tempArray = new float[] { groundSlopeAngle, angleOne, angleTwo };
System.Array.Sort(tempArray);
groundSlopeAngle = tempArray[1];
}
else
{
// 2 collision points (sphere and first raycast): AVERAGE the two
float average = (groundSlopeAngle + angleOne) / 2;
groundSlopeAngle = average;
}
}
}
// this script pushes all rigidbodies that the character touches
void OnControllerColliderHit( ControllerColliderHit hit )
{
Rigidbody body = hit.collider.attachedRigidbody;
// no rigidbody
if ( body == null || body.isKinematic )
{
return;
}
// We dont want to push objects below us
if ( hit.moveDirection.y < -0.3f )
{
return;
}
// If you know how fast your character is trying to move,
// then you can also multiply the push velocity by that.
body.velocity = hit.moveDirection * lastSpeed;
}
// Debug Gizmos
#if UNITY_EDITOR
void OnDrawGizmosSelected()
{
if ( showGizmos )
{
if ( !Application.isPlaying )
{
groundOffsetY = groundCheckY;
ceilingOffsetY = ceilingCheckY;
}
Vector3 startPoint = new Vector3( transform.position.x, transform.position.y + groundOffsetY, transform.position.z );
Vector3 endPoint = startPoint + new Vector3( 0, -sphereCastDistance, 0 );
Vector3 ceilingPoint = new Vector3( transform.position.x, transform.position.y + ceilingOffsetY, transform.position.z );
Gizmos.color = ( isGrounded == true ? Color.green : Color.white );
Gizmos.DrawWireSphere( startPoint, sphereCastRadius );
Gizmos.color = Color.gray;
Gizmos.DrawWireSphere( endPoint, sphereCastRadius );
Gizmos.DrawLine( startPoint, endPoint );
Gizmos.color = ( isCeiling == true ? Color.red : Color.white );
Gizmos.DrawWireSphere( ceilingPoint, sphereCastRadius );
}
}
#endif
}
// =======================================================================================================================================
// ** DELETE from here down, if menu item and auto configuration is NOT Required **
// this section adds create BasicFPCC object to the menu : New -> GameObject -> 3D Object
// then configures the gameobject
// demo layer used : Ignore Raycast
// also finds the main camera, attaches and sets position
// and creates capsule gfx object (for visual while editing)
// A using clause must precede all other elements defined in the namespace except extern alias declarations
//#if UNITY_EDITOR
//using UnityEditor;
//#endif
public class BasicFPCC_Setup : MonoBehaviour
{
#if UNITY_EDITOR
private static int playerLayer = 2; // default to the Ignore Raycast Layer (to demonstrate configuration)
[MenuItem("GameObject/3D Object/BasicFPCC", false, 0)]
public static void CreateBasicFPCC()
{
GameObject go = new GameObject( "Player" );
CharacterController controller = go.AddComponent< CharacterController >();
controller.center = new Vector3( 0, 1, 0 );
BasicFPCC basicFPCC = go.AddComponent< BasicFPCC >();
// Layer Mask
go.layer = playerLayer;
basicFPCC.castingMask = ~(1 << playerLayer);
Debug.LogError( "** SET the LAYER of the PLAYER Object, and the LAYERMASK of the BasicFPCC castingMask **" );
Debug.LogWarning(
"Assign the BasicFPCC Player object to its own Layer, then assign the Layer Mask to ignore the BasicFPCC Player object Layer. Currently using layer "
+ playerLayer.ToString() + ": " + LayerMask.LayerToName( playerLayer )
);
// Main Camera
GameObject mainCamObject = GameObject.Find( "Main Camera" );
if ( mainCamObject )
{
mainCamObject.transform.parent = go.transform;
mainCamObject.transform.localPosition = new Vector3( 0, 1.7f, 0 );
mainCamObject.transform.localRotation = Quaternion.identity;
basicFPCC.cameraTx = mainCamObject.transform;
}
else // create example camera
{
Debug.LogError( "** Main Camera NOT FOUND ** \nA new Camera has been created and assigned. Please replace this with the Main Camera (and associated AudioListener)." );
GameObject camGo = new GameObject( "BasicFPCC Camera" );
camGo.AddComponent< Camera >();
camGo.transform.parent = go.transform;
camGo.transform.localPosition = new Vector3( 0, 1.7f, 0 );
camGo.transform.localRotation = Quaternion.identity;
basicFPCC.cameraTx = camGo.transform;
}
// GFX
GameObject gfx = GameObject.CreatePrimitive( PrimitiveType.Capsule );
Collider cc = gfx.GetComponent< Collider >();
DestroyImmediate( cc );
gfx.transform.parent = go.transform;
gfx.transform.localPosition = new Vector3( 0, 1, 0 );
gfx.name = "GFX";
gfx.layer = playerLayer;
basicFPCC.playerGFX = gfx.transform;
}
#endif
}
// =======================================================================================================================================