A physics based flight simulator and first person explorer mini game
LD summery
Flight Simulator & First-Person Explorer: Level Design Document
Game Overview
A physics-based flight simulator that combines open-world exploration with first-person segments. Players pilot a spacecraft across varied terrain, landing to explore interior spaces on foot. The game is designed for short play sessions with self-contained missions that can be completed at the player’s own pace.
Core Experience Goals
- Create an engaging flight experience where simply flying is inherently enjoyable
- Reward exploration with discoveries and resources
- Balance flight segments with on-foot exploration of interior spaces
- Provide a sense of progression through upgradeable gear
- Create memorable environmental storytelling
World Design
Environment Overview
- Open World Structure: A seamless environment designed primarily for aerial navigation
- Abandoned Space Station: Main interior exploration area with multiple missions
- Resource Scarcity: Light is a limited resource, making exploration challenging
- Landing Zones: Specific clearings where players can land and exit their spacecraft
- Environmental Indicators: Visual cues (like vegetation) that signal where landing is impossible
Biomes & Areas
- Space Station Exterior
- Minimalist landing pad structures
- Visual indicators marking entry points
- Low ambient light to create contrast with interior spaces
- Space Station Interior
- Varied lighting conditions with darkest areas containing rare items
- Mix of open halls and narrow corridors for pacing
- Interactive elements (power generators, doors, etc.)
- Energy activation facilities as first mission objective
- Open Wilderness
- Dense vegetation preventing landing in most areas
- Strategic clearings for players to land
- Hidden pathways leading to resources
- Environmental hazards affecting flight capabilities
Progression System
Player Mechanics
- Flight Controls: Physics-based helicopter-style movement
- Landing & Exploration: Ship landing and on-foot navigation
- Gear Upgrades: Focus on light sources to explore darker areas
- Resource Collection: Materials hidden throughout the world
Mission Structure
- Main Missions
- Activate the energy base in the abandoned space station
- Find and repair essential components
- Unlock new areas through power restoration
- Side Objectives
- Resource collection in hard-to-reach places
- Environmental puzzles with light/shadow mechanics
- Discovery of narrative elements through exploration
Level Flow & Pacing
Tutorial Area
- Introduces flight controls in an open area
- Teaches landing mechanics in a clearly marked zone
- Guides player to first interior space with simple objectives
Main Game Loop
- Flight Phase
- Navigate to mission objectives
- Discover new landing zones
- Overcome aerial challenges and hazards
- Landing Phase
- Find suitable landing spots near objectives
- Navigate challenging terrain
- Learn to interpret environmental cues
- Exploration Phase
- On-foot investigation of interiors
- Resource collection in darkness
- Puzzle solving to progress mission objectives
- Return & Upgrade
- Return to ship with collected resources
- Upgrade gear (particularly light sources)
- Unlock new flight capabilities
Technical Guidelines
Flight Mechanics
- Utilize HandleLift function for vertical movement
- CalculateAngles for determining directional forces
- HandleCyclic for directional control and automatic collision avoidance
- AutoLevel for stabilization after collisions
Environmental Design
- Create spaces with varied ceiling heights to challenge flight navigation
- Design interior spaces with lighting contrast to emphasize resource locations
- Use geometry to create natural flight paths that guide players
- Implement collision detection to reduce ship power near obstacles
Player Feedback
- Visual and physics feedback when approaching landing zones
- Clear indicators for interactive elements
- Lighting design that naturally draws attention to objectives
- Progressive difficulty in navigation challenges
Art Direction
Atmosphere
- Inspired by Mass Effect’s planetary scanning
- Emphasis on light as both a gameplay mechanic and aesthetic element
- Abandoned space station with signs of previous habitation
- Contrast between exterior openness and interior claustrophobia
Visual Indicators
- Color-coding for interactive elements
- Light sources marking important pathways
- Vegetation patterns signaling no-landing zones
- Environmental storytelling through object placement
Future Expansion
- Enemy encounters requiring flight combat
- Environmental hazards affecting flight dynamics
- Additional interior locations with unique challenges
- Enhanced upgrade system for the spacecraft
This level design document prioritizes creating an engaging flight experience while balancing it with rewarding on-foot exploration, focusing on short gameplay sessions that respect the player’s time while encouraging curiosity and discovery.
Six weeks
Unity Engine, Blender, Krita, Unity Add-Ons (Probuilder, Gaia, Unity Shader Graph).
As a game physics enthusiast, I wanted to dive deep into Unity’s physics features. People seemed to enjoy the flight simulator the most when I tested out my ideas. I designed the world from a flying perspective instead of the other way around. The game evolved into a flight simulator-first person explorer. This project explores physics-based scripting in Unity, crafting modular kits and designing with Probuilder.
Level design
I created an open world game with short stories to complete at your own pace. when I designed this game i had my older brothers in mind. They love games but can only play for a couple of minutes at a time.

To enjoy the game, I wanted just flying around to be enough. Whenever you fly around, you can discover clues and explore the world. Among many indoor exploration elements, this facility is one. The interiors were designed to reward players who took their time to explore. Providing hints about the goal and then presenting the solutions.


The Mass Effect trilogy inspired me a lot, in particular the part when you get to scan planets for materials. The interior on this planet sets inside an abandoned space station, where the first mission is to activate the energy base. Light is a rare resource, and more rare items are placed in the darkest corners to encourage replayability. A player that upgrades their gear to cast light means they get more material. The interior balances open and narrow spaces to give the player a well paced experience navigating the station and rewarding curiosity.



The design should indicate what needs to be changed. Vegetation makes landing impossible!. Penalties should provide insight. Creating a game world according to what I want the player to experience is my job as a level designer. Experience matters more than logic. Realistic is not necessary, but it should appear reasonable in the world it builds. They’d feel almost successful even though they’d been pretty far off. It’s all about faking it sometimes!


Here are some sketches to illustrate some interactive game mechanics and interior.




Asset creation in Blender
I’ve experimented with Blender for a couple of years now, and when I got accepted at TGA I decided to try and create all the 3D modeling myself. I like how it gives me total control over what I can create, and I don’t have to depend on somebody else’s design.
Blender has been my go-to program for a couple of years now, and when I got accepted to TGA I decided to create all the 3D modeling myself. I like how it makes it possible to show not tell to my team about new Level Design ideas




Scripting
A big part of the game’s fun is the flight controller. My goal was to make flying around and landing enjoyable for novice players. The spaceship is controlled like a helicopter, so whenever the ship leans toward a direction, it will follow that direction. Players can land and exit the ship to explore gated areas. I spent a lot of time tweaking variables for the movement controller to make it feel engaging. Next step is to add enemy encounters and environmental hazards to this project.
HandleLift function
The first function I wrote was to make the spaceship lift, it felt like a good start. I designed the terrain after testing different heights.
protected virtual void HandleLift(Rigidbody rb, KeyboardInput input)
{
// handles the vertical movement of the ship, make sure that the spaceship can have "room to move"
RaycastHit groundHit;
Ray groundRay = new Ray(transform.position, Vector3.down);
if (Physics.SphereCast(groundRay, .1f, out groundHit, Mathf.Infinity, enviormentMask))
{
currentWantedPlayerHeight = groundHit.distance;
currentUsablePlayerHeight = Mathf.Lerp(currentUsablePlayerHeight ,currentWantedPlayerHeight , Time.deltaTime);
RaycastHit playerExitShipHit;
Ray playerExitShipRay = new Ray(transform.position, Vector3.down);
if (Physics.SphereCast(playerExitShipRay, .1f, out playerExitShipHit, Mathf.Infinity))
if (playerExitShipHit.distance < minGroundDistanceForDeloadPlayer)
{
playerPoint = playerExitShipHit.point + transform.up * 6f;
if (Input.GetKey(KeyCode.C))
FPSCam.SetActive(true);
FPSCam.gameObject.transform.position = playerPoint;
if (!canSpawn)
canSpawn = true;
}else canSpawn =false;
RaycastHit forwardHit;
Ray forwardRay = new Ray(groundHit.point, -flatFwd);
if (Physics.Raycast(forwardRay, out forwardHit, maxDiffDistance, enviormentMask))
{
distanceToEnv = forwardHit.distance;
Ray upRay = new Ray(forwardHit.point, Vector3.up);
RaycastHit upHit;
if (Physics.SphereCast(upRay, .5f, out upHit, Mathf.Infinity, topMask))
{
currentEnvAimHeight = upHit.distance + minConsistantHeight;
Ray topRay = new Ray(upHit.point, Vector3.up);
RaycastHit topHit;
if (Physics.SphereCast(topRay, rayRadius, out topHit, Mathf.Infinity, topMask))
{
top = topHit.distance;
currentEnvAimHeight += top / 2;
}
heightModifier = currentEnvAimHeight - currentUsablePlayerHeight + minConsistantHeight;
dir = heightModifier / Mathf.Abs(heightModifier);
powerMuliplier = heightModifier / maxDiffHeight;
}
else
{
heightModifier = maxDiffHeight - currentUsablePlayerHeight + minConsistantHeight;
dir = heightModifier / Mathf.Abs(heightModifier);
powerMuliplier = heightModifier / maxDiffHeight;
}
}
else
{
heightModifier = maxDiffHeight - currentUsablePlayerHeight + minConsistantHeight;
dir = heightModifier / Mathf.Abs(heightModifier);
powerMuliplier = heightModifier / maxDiffHeight;
}
}
power = Mathf.Pow(powerMuliplier, pow) * maxLiftForce * dir;
power = Mathf.Clamp(power, 0, maxLiftForce);
Debug.Log(power);
Vector3 liftForce = (transform.up * (Physics.gravity.magnitude + power) * rb.mass);
rb.AddForce(liftForce * input.StickeyCollectiveInput, ForceMode.Force);
}
CalculateAngles function

I could not find the direction I needed to push the spacecraft in based on the world location since it ignored forward and backward motion. Local angle was straight down. As a result, I got a flat angle.
private void CalculateAngles()
{
// Calculate the flat angle in world space
flatRight = transform.right;
flatRight.y = 0f;
flatRight = flatRight.normalized;
Debug.DrawRay(transform.position, flatRight, Color.red);
flatFwd = transform.forward;
flatFwd.y = 0f;
flatFwd = flatFwd.normalized;
Debug.DrawRay(transform.position, flatFwd, Color.blue);
forwardDot = Vector3.Dot(transform.up, flatFwd);
rightDot = Vector3.Dot(transform.up, flatRight);
}
HandleCyclic function
This function handles spaceship force. If anything blocks the path, ship power is reduced. It acts like an automatic break, making maneuvering easier for novice players
protected virtual void HandleCyclic(Rigidbody rb, KeyboardInput input)
{
// Handle the amount of stability the player receives, decrease stability when any input is received to give more control to the steering of the ship.
if (Input.anyKey)
stability = Mathf.Lerp(maxStability, 0.2f, 5 * Time.deltaTime);
else
stability = Mathf.Lerp(maxStability, maxStability, 5 * Time.deltaTime);
//Handle the tilting of the spaceship in forward / backward directions
float cyclicXForce = (MathF.Pow(input.CyclicInput.y, pow) * (cyclicForce * cyclicForceMultiplier));
rb.AddRelativeTorque(Vector3.right * (cyclicXForce * input.CyclicInput.y), ForceMode.Acceleration);
//Handle the tilting of the spaceship in left / right directions
float cyclicZForce = MathF.Pow(input.CyclicInput.x, pow) * (cyclicForce * cyclicForceMultiplier);
rb.AddRelativeTorque(Vector3.forward * (cyclicZForce * input.CyclicInput.x), ForceMode.Acceleration);
Vector3 forwardVec = (flatFwd * forwardDot);
Vector3 rightVec = (flatRight * rightDot);
// calculates the final angle to apply force in.
cyclicDir = Vector3.ClampMagnitude(forwardVec + rightVec, 1f) * (forwardPower * cyclicForce);
RaycastHit hit;
Ray ray = new Ray(transform.position, cyclicDir);
if (Physics.SphereCast(ray, .2f, out hit, maxDiffDistance, enviormentMask))
{
// Calculate the distance to the closest object in the world in the direction witch the ship is going to move toward
distanceModifier = maxDiffDistance - hit.distance;
distanceForceMultiplier = distanceModifier / maxDiffDistance;
if(hit.distance > maxDiffDistance *.1f)
{
forwardPower = (1 - distanceForceMultiplier) * Mathf.Pow(distanceForceMultiplier, pow) * maxForwardPowerMuliplier;
rb.AddForce(cyclicDir, ForceMode.Force);
}
else
{
// handle the "breaking" of the ship by reversing the power when the ship is less than 10m away from the object
forwardPower = distanceForceMultiplier * Mathf.Pow(distanceForceMultiplier, pow) * maxForwardPowerMuliplier;
rb.AddForce(-cyclicDir, ForceMode.Force);
}
// apply force in the direction wich the ship is tilting toward
forwardPower = Mathf.Clamp(forwardPower, 0, maxForwardPowerMuliplier);
}
else {
// apply force in the direction wich the ship is tilting toward
forwardPower = maxForwardPowerMuliplier;
rb.AddForce(cyclicDir, ForceMode.Force);
}
}
AutoLevel function
The force applied to the spaceship is handled by this function. The player will get less power if anything blocks the path. Player input determines auto-level force. A spaceship’s pitch is less controlled since it auto-levels. When a player hits something and begins to wobble, they can take their hands off the keyboard and let the autoleveling do its thing
private void AutoLevel(Rigidbody rb)
{
// Calculate in what direction the spaceship need to apply force to be able to stay steady in the air
Vector3 predictedUp = Quaternion.AngleAxis(
rb.angularVelocity.magnitude * Mathf.Rad2Deg * stability / maxStability,
rb.angularVelocity
) * transform.up;
Vector3 torqueVector = Vector3.Cross(predictedUp, Vector3.up);
rb.AddTorque(torqueVector * maxStability * stability);
Quaternion corrAngle = Quaternion.Euler(0f, 0f, 0f);
float rightForce = -forwardDot * autolevelForce;
float forwardForce = rightDot * autolevelForce;
rb.AddRelativeTorque(Vector3.right * rightForce, ForceMode.Acceleration);
rb.AddRelativeTorque(Vector3.forward * forwardForce, ForceMode.Acceleration);
}
HandlePlasmaCanon function
The cannon moves slowly towards points in the world where the mouse hovers. A projectile moves forward and checks for collisions. After a hit happens, collision effects are spawned.
void HandlePlasmaCanon()
{
firePos.LookAt(lookAt);
Quaternion OriginalRot = gunPos.localRotation;
gunPos.LookAt(lookAt.position);
Quaternion NewRot = gunPos.localRotation;
gunPos.localRotation = OriginalRot;
gunPos.localRotation = Quaternion.Lerp(gunPos.localRotation, NewRot, rotSpeed * Time.deltaTime);
foreach (Projectile projectile in projectileList)
{
RaycastHit projectileHit;
Ray projectileRay = new Ray(projectile.transform.position, projectile.transform.forward);
if (Physics.SphereCast(projectileRay, .2f, out projectileHit, Mathf.Infinity))
{
Debug.DrawLine(projectile.transform.position, projectileHit.point, Color.cyan, 2000000f);
if (projectileHit.distance < 1f && projectileHit.distance > .5f)
{
StartCoroutine(InstansiateHitPlane(projectileHit.point));
Debug.Log("Stat");
StartCoroutine(InstansiateHitEffect(projectileHit.point));
Destroy(projectile.gameObject);
projectileList.Clear();
}
}
}
RaycastHit hit;
Ray ray = new Ray(firePos.position, gunPos.forward);
if (Physics.SphereCast(ray, 0.2f, out hit, shootRange, gunMask))
{
aim.position = hit.point;
aim.rotation = Quaternion.FromToRotation(aim.up, hit.normal) * aim.rotation;
hitEffectPos = aim;
}
if (Input.GetButton("Fire1") && canShoot)
{
Shoot();
}
}
IEnumerator InstantiateHitPlane(Vector3 pos)
{
GameObject planeInstance = Instantiate(impactPlane,pos, hitEffectPos.rotation);
yield return new WaitForSeconds(.1f);
Destroy(planeInstance);
StopCoroutine(InstantiateHitPlane(pos));
}
IEnumerator InstantiateHitEffect(Vector3 pos)
{
VisualEffect hitEffectInsante = Instantiate(impactVFX, pos, hitEffectPos.rotation);
hitEffectInsante.transform.localScale = new Vector3(5, 5, 5);
vFXList.Add(hitEffectInsante);
yield return new WaitForSeconds(hitEffectInsante.playRate);
foreach(VisualEffect vfx in vFXList)
{
Destroy(vfx.gameObject);
}
vFXList.Clear();
StopCoroutine(InstantiateHitEffect(pos));
}
void Shoot()
{
RaycastHit hit;
Ray ray = new Ray(firePos.position, gunPos.forward);
if (Physics.SphereCast(ray, 0.2f, out hit, shootRange, gunMask))
{
Projectile projectileInstance = Instantiate(projectile, firePos.position, gunPos.rotation);
projectileList.Add(projectileInstance);
VisualEffect muzzleFlash = Instantiate(muzzleFlashVFX, firePos);
muzzleFlash.transform.localPosition = new Vector3(0, 0, 0);
muzzleFlash.transform.localScale = new Vector3(5, 5, 5);
}
StartCoroutine(Timer());
}
IEnumerator Timer()
{
canShoot = false;
yield return new WaitForSeconds(1f);
canShoot = true;
StopCoroutine(Timer());
}
HandlePedal function
This function rotates the spacecraft horizontally.
protected virtual void HandlePedal(Rigidbody rb, KeyboardInput input)
{
rb.AddTorque(Vector3.up * input.PedalInput * tailForce, ForceMode.Acceleration);
}
FPSController function
This function controls the camera movement when the player exits the spacecraft to explore.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Spaceship
{
public enum playerMoveStatus { notMoving, crouching, walking, running, notGrounded, landing }
public enum CurvedControlledBobCallBackType {horizontal, vertical}
public delegate void CurvedControlledBobCallback();
[System.Serializable]
public class CurvedControlledBobEvent
{
public float time = 0f;
public CurvedControlledBobCallback Function = null;
public CurvedControlledBobCallBackType Type = CurvedControlledBobCallBackType.vertical;
}
[System.Serializable]
public class CurvedControlledBob
{
[SerializeField] AnimationCurve bobCurve = new AnimationCurve(new Keyframe(0f,0f),new Keyframe(0.5f, 1f)
,new Keyframe(1f, 0f), new Keyframe(1.5f, -1f), new Keyframe(2f, 0f));
[SerializeField] float horizontalMultiplier = 0.01f;
[SerializeField] float verticalMultiplier = 0.02f;
[SerializeField] float verticaltoHorizontalSpeedRatio = 2.0f;
[SerializeField] float baseInterval = 1f;
private float prevXPlayHead;
private float prevYPlayHead;
private float xPlayHead;
private float yPlayHead;
private float cureveEndTime;
private List<CurvedControlledBobEvent> events = new List<CurvedControlledBobEvent>();
public void Initialize ()
{
cureveEndTime = bobCurve[bobCurve.length - 1].time;
xPlayHead = 0f;
yPlayHead = 0f;
prevXPlayHead = 0f;
prevYPlayHead = 0f;
}
public void RegisterEventCallback(float time, CurvedControlledBobCallback function, CurvedControlledBobCallBackType type)
{
CurvedControlledBobEvent ccbeEnvet = new CurvedControlledBobEvent();
ccbeEnvet.time = time;
ccbeEnvet.Function = function;
ccbeEnvet.Type = type;
events.Add(ccbeEnvet);
events.Sort(
delegate(CurvedControlledBobEvent t1, CurvedControlledBobEvent t2)
{
return (t1.time.CompareTo (t2.time));
}
);
}
public Vector3 GetVectorOffset(float speed)
{
xPlayHead += (speed * Time.deltaTime)/ baseInterval;
yPlayHead += ((speed * Time.deltaTime) / baseInterval)* verticaltoHorizontalSpeedRatio;
if(xPlayHead > cureveEndTime)
xPlayHead -= cureveEndTime;
if (yPlayHead > cureveEndTime)
yPlayHead -= cureveEndTime;
for (int i = 0; i < events.Count; i++)
{
CurvedControlledBobEvent ev = events[i];
if(ev!=null)
if(ev.Type == CurvedControlledBobCallBackType.vertical)
if(prevYPlayHead < ev.time && yPlayHead >= ev.time || prevYPlayHead > yPlayHead && (ev.time > prevYPlayHead || ev.time <= yPlayHead))
ev.Function();
if (ev.Type == CurvedControlledBobCallBackType.vertical)
if (prevXPlayHead < ev.time && xPlayHead >= ev.time || prevXPlayHead > xPlayHead && (ev.time > prevXPlayHead || ev.time <= xPlayHead))
ev.Function();
}
float xPos = bobCurve.Evaluate(xPlayHead)*horizontalMultiplier;
float yPos = bobCurve.Evaluate(yPlayHead)*verticalMultiplier;
prevXPlayHead = xPlayHead;
prevYPlayHead = yPlayHead;
return new Vector3(xPos,yPos,0f);
}
}
[RequireComponent(typeof(CharacterController))]
public class FPSController : MonoBehaviour
{
public List<AudioSource> audioSource = new List<AudioSource>();
private int audioToUse = 0;
public Vector3 spawnPoint { get; set; }
#region Gameplay
[SerializeField] private float crouchSpeed = 2.5f;
[SerializeField] private float walkSpeed = 1f;
[SerializeField] private float runSpeed = 4.5f;
[SerializeField] private float jumpSpeed = 7.5f;
[SerializeField] private float stickToGroundForce = 5f;
[SerializeField] private float gravityMultiplier = 2.5f;
[SerializeField] private float runStepLengthen = 0.75f;
[SerializeField] private float walkStepLengthen = 0.75f;
[SerializeField] private float staminaDepletion = 5f;
[SerializeField] private float staminaRecovery = 10f;
[SerializeField] private MouseLook mouseLook;
[SerializeField] private CurvedControlledBob headBob = new CurvedControlledBob();
// [SerializeField] SpaceShipCharacteristics charactaristics;
const float interactDistance = 20f;
public Camera fpsCamera = null;
private bool isJumpButtonPressed = false;
private Vector2 input = Vector2.zero;
private Vector3 moveDiection = Vector2.zero;
private bool pervGrounded;
private bool isWalking = false;
private bool isJumping = false;
private bool isCrouching = false;
private float fallingTimer = 0f;
private float controllerHeight = 0f;
private float stamina = 100f;
private Vector3 localSpaceCameraPos = Vector3.zero;
private CharacterController characterController = null;
private playerMoveStatus movementStatus = playerMoveStatus.notMoving;
public LayerMask groundMask;
public playerMoveStatus movemeStatus { get { return movementStatus; }}
public float WalkSpeed { get { return walkSpeed; }}
public float RunSpeed { get { return runSpeed; }}
[SerializeField] LayerMask interactebleMask;
float vertical;
float horizontal;
void Update ()
{
// if(Input.GetButtonDown("Interact"))
// Interact();
if (characterController.isGrounded) fallingTimer = 0f;
else
fallingTimer += Time.deltaTime;
if(Time.timeScale>Mathf.Epsilon)
mouseLook.LookRotation(transform, fpsCamera.transform);
if (!isJumpButtonPressed && !isCrouching)
isJumpButtonPressed = Input.GetButtonDown("Jump");
if(Input.GetButtonDown("Crouch"))
{
isCrouching = !isCrouching;
characterController.height = isCrouching == true ? controllerHeight/2f : controllerHeight;
}
if (!pervGrounded && characterController.isGrounded)
{
if (fallingTimer > 0.5f)
{
}
moveDiection.y = 0f;
isJumping = false;
movementStatus = playerMoveStatus.landing;
}
else if (characterController.velocity.sqrMagnitude < 0.01f)
movementStatus = playerMoveStatus.notMoving;
else if (isCrouching)
movementStatus = playerMoveStatus.crouching;
else if (isWalking)
movementStatus = playerMoveStatus.walking;
else
movementStatus = playerMoveStatus.running;
pervGrounded = characterController.isGrounded;
if(movementStatus == playerMoveStatus.running)
stamina = Mathf.Max(stamina - staminaDepletion * Time.deltaTime, 0f);
else
stamina = Mathf.Min(stamina + staminaRecovery * Time.deltaTime, 100f);
}
private void FixedUpdate()
{
horizontal = Input.GetAxis("Horizontal");
vertical = Input.GetAxis("Vertical");
bool wasWalking = isWalking;
isWalking = !Input.GetKey(KeyCode.LeftShift);
float speed = isCrouching ? crouchSpeed : isWalking ? walkSpeed : Mathf.Lerp(walkSpeed,runSpeed, stamina/100f);
input = new Vector2(horizontal, vertical);
if (input.sqrMagnitude > 1) input.Normalize();
Vector3 desierdMove = transform.forward * input.y + transform.right * input.x;
RaycastHit hit;
Ray ray = new Ray(transform.position, Vector3.down);
if (Physics.SphereCast(ray, characterController.radius, out hit, characterController.height / 2f, groundMask))
desierdMove = Vector3.ProjectOnPlane(desierdMove, hit.normal).normalized;
moveDiection.x = desierdMove.x * speed;
moveDiection.z = desierdMove.z * speed;
if (characterController.isGrounded)
{
moveDiection.y = -stickToGroundForce;
if (isJumpButtonPressed)
{
moveDiection.y = jumpSpeed;
isJumpButtonPressed = false;
isJumping = true;
}
}
else
moveDiection += Physics.gravity * gravityMultiplier * Time.fixedDeltaTime;
characterController.Move(moveDiection * Time.fixedDeltaTime);
Vector3 speedXZ = new Vector3(characterController.velocity.x, 0f, characterController.velocity.z);
if(speedXZ.magnitude > 0.01f)
fpsCamera.transform.localPosition = new Vector3(0f,0f,0f) + headBob.GetVectorOffset(speedXZ.magnitude * (isWalking || isCrouching? walkStepLengthen : runStepLengthen));
else fpsCamera.gameObject.transform.localPosition = new Vector3(0f, 0f, 0f);
}
#endregion
public float Stamina { get{return stamina; }}
private void Start()
{
characterController = GetComponent<CharacterController>();
controllerHeight = characterController.height;
fpsCamera = GetComponentInChildren<Camera>();
mouseLook.Init(transform, fpsCamera.transform);
headBob.Initialize();
headBob.RegisterEventCallback(1.5f, PlayFootstepSound, CurvedControlledBobCallBackType.vertical);
}
void PlayFootstepSound()
{
if(isCrouching) return;
audioSource[audioToUse].Play();
audioToUse = (audioToUse == 0) ? 1 : 0;
}
// void Interact ()
// {
// RaycastHit hit;
// Ray ray = new Ray (transform.position, transform.forward);
// if(Physics.SphereCast(ray, .1f, out hit, interactDistance))
// {
// if(hit.transform.gameObject.GetComponent<IInteracteble>() != null)
// {
// Debug.Log(hit.transform.gameObject);
// hit.transform.gameObject.GetComponent<IInteracteble>().OnInteract();
// }
// }
// }
private void OnEnable()
{
GameObject spaceship = GameObject.FindGameObjectWithTag("spaceship");
transform.position = spaceship.transform.position;
Debug.Log("iscalled");
characterController = GetComponent<CharacterController>();
controllerHeight = characterController.height;
fpsCamera = GetComponentInChildren<Camera>();
mouseLook.Init(transform, fpsCamera.transform);
headBob.Initialize();
headBob.RegisterEventCallback(1.5f, PlayFootstepSound, CurvedControlledBobCallBackType.vertical);
}
}
}