PortraitRenderer.cs
using Sandbox;
using System;
using System.Linq;
public sealed class PortraitRenderer : Component
{
[Property]
public CameraComponent Camera { get; set; }
public Texture RenderedTexture { get; private set; }
private GameObject _headObj;
private Vector3 _localFaceDir;
protected override void OnStart()
{
if ( Camera == null ) Camera = Components.Get<CameraComponent>();
var bodyRenderer = GameObject.Root.Components.GetAll<SkinnedModelRenderer>( FindMode.EverythingInSelfAndDescendants ).FirstOrDefault();
if ( bodyRenderer != null )
{
string[] boneNames = { "head", "Head", "mixamorig:Head", "Bip01_Head", "Bip01 Head", "neck", "Neck" };
foreach ( var name in boneNames )
{
var obj = bodyRenderer.GetBoneObject( name );
if ( obj.IsValid() )
{
_headObj = obj;
break;
}
}
if ( _headObj != null )
{
// На старті гри персонаж дивиться туди ж, куди й Root (вперед).
// Ми вираховуємо, яка саме локальна вісь кістки голови відповідає за "перед" обличчя!
var rootForward = GameObject.Root.Transform.Rotation.Forward;
_localFaceDir = _headObj.Transform.Rotation.Inverse * rootForward;
}
bodyRenderer.GameObject.Tags.Add( "portrait_target" );
}
}
protected override void OnUpdate()
{
if ( Camera != null && RenderedTexture == null )
{
RenderedTexture = Texture.CreateRenderTarget()
.WithSize( 256, 256 )
.WithFormat( ImageFormat.RGBA8888 )
.Create();
Camera.RenderTarget = RenderedTexture;
Camera.RenderTags.Add( "portrait_target" );
Camera.ZNear = 1f;
}
if ( Camera != null )
{
if ( _headObj != null )
{
// Отримуємо поточний СВІТОВИЙ напрямок обличчя (крутиться разом з анімаціями)
Vector3 worldFaceDir = _headObj.Transform.Rotation * _localFaceDir;
Vector3 headPos = _headObj.Transform.Position;
// Жорстко ставимо камеру на 30 юнітів ПЕРЕД обличчям (ігноруючи те, як вона стоїть у редакторі)
Vector3 cameraPos = headPos + worldFaceDir * 30f;
// Камера дивиться на лице. Вектор Up забороняє їй завалюватися набік.
Rotation cameraRot = Rotation.LookAt( -worldFaceDir, Vector3.Up );
Transform.World = new Transform( cameraPos, cameraRot );
}
else
{
// Запасний варіант, якщо кістки взагалі немає
var rootForward = GameObject.Root.Transform.Rotation.Forward;
var headPos = GameObject.Root.Transform.Position + Vector3.Up * 64f;
Transform.World = new Transform( headPos + rootForward * 30f, Rotation.LookAt( -rootForward, Vector3.Up ) );
}
}
}
}