2 Likes
/* Folowed Third Person Camera by KlnHomeAlone@gmail.com for Xenko*/
/* Inspired by Jm991 https://github.com/jm991/UnityThirdPersonTutorial */
using SiliconStudio.Core.Mathematics;
using SiliconStudio.Xenko.Engine;
using SiliconStudio.Xenko.Physics;
using System;
namespace ThirdPersonCamera
{
public class FollowCamera : SyncScript
{
private Vector3 _targetPosition;
private Vector3 _characterOffset;
private Simulation _simulation;
private CharacterComponent _characterController;
private float _compensationOffset = 0.1f;
public Entity Follow { get; set; }
public float DistanceAway { get; set; } = 3.5f;
public float DistanceUp { get; set; } = 4f;
public float Smooth { get; set; } = 0.2f;
public override void Start()
{
_simulation = this.GetSimulation();
_characterController = Follow.Get<CharacterComponent>();
}
public override void Update()
{
var charRotation = Matrix.RotationQuaternion(_characterController.Orientation);
_characterOffset = Follow.Transform.Position + charRotation.Up * DistanceUp;
_targetPosition = _characterOffset + charRotation.Forward * DistanceAway;
CompensateForWalls(_characterOffset, ref _targetPosition);
SmoothPosition(Entity.Transform.Position, _targetPosition);
Entity.LookAt(Follow.Transform.Position);
}
private void CompensateForWalls(Vector3 characterOffset, ref Vector3 toTarget)
{
Vector3 camPosCache = Entity.Transform.Position;
var near = camPosCache;
var far = Follow.Transform.Position;
far.Y += 1.3f; //Character Height usually it's a capsule collider height
var result = _simulation.Raycast(near, far);
if (!result.Succeeded || result.Collider == null) return;
var colliderComponent = result.Collider;
if (colliderComponent is CharacterComponent) return;
do
{
var normal = result.Normal;
if (normal != Vector3.Zero)
{
toTarget -= (_compensationOffset * normal);
}
near = toTarget;
result = _simulation.Raycast(near, far);
if (!result.Succeeded || result.Collider == null) return;
colliderComponent = result.Collider;
}
while (!(colliderComponent is CharacterComponent));
}
private void SmoothPosition(Vector3 fromPos, Vector3 toPos)
{
var distance = toPos - fromPos;
if (distance.Length() < Smooth) return;
// Making a smooth transition between camera's current position and the position it wants to be in
Entity.Transform.Position = Vector3.SmoothStep(fromPos, toPos, Smooth);
}
}
public static class EntityExtensions
{
public static void LookAt(this Entity e, Vector3 target)
{
float altitude = 0;
float azimuth = GetLookAtAngles(e.Transform.Position, target, out altitude);
var result = Quaternion.RotationYawPitchRoll(azimuth, -altitude, 0);
e.Transform.Rotation = result;
}
private static float GetLookAtAngles(Vector3 source, Vector3 destination, out float altitude)
{
var x = source.X - destination.X;
var y = source.Y - destination.Y;
var z = source.Z - destination.Z;
altitude = (float)Math.Atan2(y, Math.Sqrt(x * x + z * z));
var azimuth = (float)Math.Atan2(x, z);
return azimuth;
}
}
}
1 Like
- Copy the script into your project
- Get sure that you have set an entity with character controller attached
- Reference that entity in the Follow field
- Set desired Distance Away and Distance Up and desired smoothness.
- Enjoy!!!
This camera also detects walls and adjusts its position.
3 Likes
Thanks for your contribution, very much appreciated!