Code/ModelViewer/ModelViewerCharacterController/ModelViewerCitizenAnimation.cs
public sealed class ModelViewerCitizenAnimation : Component, Component.ExecuteInEditor
{
[Property] public SkinnedModelRenderer Target { get; set; }
[Property] public GameObject EyeSource { get; set; }
[Property] public GameObject LookAtObject { get; set; }
[Property, Range( 0.5f, 1.5f )] public float Height { get; set; } = 1.0f;
[Property] public GameObject IkLeftHand { get; set; }
[Property] public GameObject IkRightHand { get; set; }
[Property] public GameObject IkLeftFoot { get; set; }
[Property] public GameObject IkRightFoot { get; set; }
[Property] public HoldTypes CurrentHoldType { get; set; }
[Property, Range(0,10)] public int FacesOverride { get; set; }
protected override void OnUpdate()
{
if ( LookAtObject.IsValid() )
{
var eyePos = GetEyeWorldTransform.Position;
var dir = (LookAtObject.WorldPosition - eyePos).Normal;
WithLook( dir, 1, 0.5f, 0.1f );
}
Target.Set( "scale_height", Height );
// SetIk( "left_hand", ... );
// SetIk( "right_hand", ... );
if ( IkLeftHand.IsValid() && IkLeftHand.Active ) SetIk( "hand_left", IkLeftHand.Transform.World );
else ClearIk( "hand_left" );
if ( IkRightHand.IsValid() && IkRightHand.Active ) SetIk( "hand_right", IkRightHand.Transform.World );
else ClearIk( "hand_right" );
if ( IkLeftFoot.IsValid() && IkLeftFoot.Active ) SetIk( "foot_left", IkLeftFoot.Transform.World );
else ClearIk( "foot_left" );
if ( IkRightFoot.IsValid() && IkRightFoot.Active ) SetIk( "foot_right", IkRightFoot.Transform.World );
else ClearIk( "foot_right" );
HoldType = CurrentHoldType;
FaceOverride = FacesOverride;
var groundAngle = CheckForGroundAngle();
var ply = Components.Get<ModelViewerPlayerController>();
var cc = Components.Get<ModelViewerCharacterController>();
if(cc == null && ply == null) return;
if ( !cc.LastPhysicsTrace.Hit )
{
SkidLevel = 0;
}
else
{
SkidLevel = SkidLevel.LerpTo( groundAngle >= cc.GroundAngle ? 100 : 0, Time.Delta * 5f );
}
if ( ply == null ) return;
if ( ply.CurrentCameraMode != ModelViewerPlayerController.CameraMode.Free )
{
if ( Input.Pressed( "attack1" ) )
{
Target.Set( "b_attack", true );
}
if ( Input.Pressed( "slot1" ) )
{
CurrentHoldType = HoldTypes.None;
}
else if ( Input.Pressed( "slot2" ) )
{
CurrentHoldType = HoldTypes.Pistol;
}
else if ( Input.Pressed( "slot3" ) )
{
CurrentHoldType = HoldTypes.Rifle;
}
else if ( Input.Pressed( "slot4" ) )
{
CurrentHoldType = HoldTypes.Shotgun;
}
else if ( Input.Pressed( "slot5" ) )
{
CurrentHoldType = HoldTypes.HoldItem;
}
else if ( Input.Pressed( "slot6" ) )
{
CurrentHoldType = HoldTypes.Punch;
}
else if ( Input.Pressed( "slot7" ) )
{
CurrentHoldType = HoldTypes.Swing;
}
else if ( Input.Pressed( "slot8" ) )
{
CurrentHoldType = HoldTypes.RPG;
}
}
}
public void SetIk( string name, Transform tx )
{
// convert local to model
tx = Target.Transform.World.ToLocal( tx );
Target.Set( $"ik.{name}.enabled", true );
Target.Set( $"ik.{name}.position", tx.Position );
Target.Set( $"ik.{name}.rotation", tx.Rotation );
}
public void ClearIk( string name )
{
Target.Set( $"ik.{name}.enabled", false );
}
public Transform GetEyeWorldTransform
{
get
{
if ( EyeSource.IsValid() ) return EyeSource.Transform.World;
return Transform.World;
}
}
/// <summary>
/// Have the player look at this point in the world
/// </summary>
public void WithLook( Vector3 lookDirection, float eyesWeight = 1.0f, float headWeight = 1.0f, float bodyWeight = 1.0f )
{
Target.SetLookDirection( "aim_eyes", lookDirection );
Target.SetLookDirection( "aim_head", lookDirection );
Target.SetLookDirection( "aim_body", lookDirection );
AimEyesWeight = eyesWeight;
AimHeadWeight = headWeight;
AimBodyWeight = bodyWeight;
}
public void WithVelocity( Vector3 Velocity )
{
var dir = Velocity;
var forward = Target.WorldRotation.Forward.Dot( dir );
var sideward = Target.WorldRotation.Right.Dot( dir );
var angle = MathF.Atan2( sideward, forward ).RadianToDegree().NormalizeDegrees();
Target.Set( "move_direction", angle );
Target.Set( "move_speed", Velocity.Length );
Target.Set( "move_groundspeed", Velocity.WithZ( 0 ).Length );
Target.Set( "move_y", sideward );
Target.Set( "move_x", forward );
Target.Set( "move_z", Velocity.z );
}
public void WithWishVelocity( Vector3 Velocity )
{
var dir = Velocity;
var forward = Target.WorldRotation.Forward.Dot( dir );
var sideward = Target.WorldRotation.Right.Dot( dir );
var angle = MathF.Atan2( sideward, forward ).RadianToDegree().NormalizeDegrees();
Target.Set( "wish_direction", angle );
Target.Set( "wish_speed", Velocity.Length );
Target.Set( "wish_groundspeed", Velocity.WithZ( 0 ).Length );
Target.Set( "wish_y", sideward );
Target.Set( "wish_x", forward );
Target.Set( "wish_z", Velocity.z );
}
public float CheckForGroundAngle()
{
var trace = Scene.Trace.Ray( Target.WorldPosition + Vector3.Up * 2, Target.WorldPosition + Vector3.Down * 6)
.WithoutTags("player", "collider")
.Radius(8)
.Run();
// Gizmo.Draw.Color = Color.Red;
// Gizmo.Draw.Line( Target.WorldPosition + Vector3.Up * 2, Target.WorldPosition + Vector3.Down * 6 );
if ( !trace.Hit )
return 0;
return trace.Normal.Angle( Vector3.Up );
}
public Rotation AimAngle
{
set
{
value = Target.WorldRotation.Inverse * value;
var ang = value.Angles();
Target.Set( "aim_body_pitch", ang.pitch );
Target.Set( "aim_body_yaw", ang.yaw );
}
}
public float AimEyesWeight
{
get => Target.GetFloat( "aim_eyes_weight" );
set => Target.Set( "aim_eyes_weight", value );
}
public float AimHeadWeight
{
get => Target.GetFloat( "aim_head_weight" );
set => Target.Set( "aim_head_weight", value );
}
public float AimBodyWeight
{
get => Target.GetFloat( "aim_body_weight" );
set => Target.Set( "aim_body_weight", value );
}
public float FootShuffle
{
get => Target.GetFloat( "move_shuffle" );
set => Target.Set( "move_shuffle", value );
}
public float DuckLevel
{
get => Target.GetFloat( "duck" );
set => Target.Set( "duck", value );
}
public float SkidLevel
{
get => Target.GetFloat( "skid" );
set => Target.Set( "skid", value );
}
public float VoiceLevel
{
get => Target.GetFloat( "voice" );
set => Target.Set( "voice", value );
}
public bool IsSitting
{
get => Target.GetBool( "b_sit" );
set => Target.Set( "b_sit", value );
}
public bool IsGrounded
{
get => Target.GetBool( "b_grounded" );
set => Target.Set( "b_grounded", value );
}
public bool IsSwimming
{
get => Target.GetBool( "b_swim" );
set => Target.Set( "b_swim", value );
}
public bool IsClimbing
{
get => Target.GetBool( "b_climbing" );
set => Target.Set( "b_climbing", value );
}
public bool IsNoclipping
{
get => Target.GetBool( "b_noclip" );
set => Target.Set( "b_noclip", value );
}
public bool IsWeaponLowered
{
get => Target.GetBool( "b_weapon_lower" );
set => Target.Set( "b_weapon_lower", value );
}
public enum HoldTypes
{
None,
Pistol,
Rifle,
Shotgun,
HoldItem,
Punch,
Swing,
RPG
}
public HoldTypes HoldType
{
get => (HoldTypes)Target.GetInt( "holdtype" );
set => Target.Set( "holdtype", (int)value );
}
public enum Hand
{
Both,
Right,
Left
}
public Hand Handedness
{
get => (Hand)Target.GetInt( "holdtype_handedness" );
set => Target.Set( "holdtype_handedness", (int)value );
}
public void TriggerJump()
{
Target.Set( "b_jump", true );
}
public void TriggerDeploy()
{
Target.Set( "b_deploy", true );
}
public enum MoveStyles
{
Auto,
Walk,
Run
}
/// <summary>
/// We can force the model to walk or run, or let it decide based on the speed.
/// </summary>
public MoveStyles MoveStyle
{
get => (MoveStyles)Target.GetInt( "move_style" );
set => Target.Set( "move_style", (int)value );
}
public enum SpecialMoveStyle
{
None,
LedgeGrab,
Roll,
Slide
}
public SpecialMoveStyle SpecialMove
{
get => (SpecialMoveStyle)Target.GetInt( "special_movement_states" );
set => Target.Set( "special_movement_states", (int)value );
}
public int FaceOverride
{
get => Target.GetInt( "face_override" );
set => Target.Set( "face_override", value );
}
}