upd
This commit is contained in:
15
Code/Player/Dedugan.Animations.cs
Normal file
15
Code/Player/Dedugan.Animations.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Sandbox;
|
||||
|
||||
public sealed partial class Dedugan
|
||||
{
|
||||
void UpdateCustomAnimations()
|
||||
{
|
||||
if ( Network.IsOwner )
|
||||
{
|
||||
// IsDancing = Input.Down( "Dance" );
|
||||
}
|
||||
|
||||
Log.Info(IsDancing);
|
||||
Renderer.Set( "test_dance", IsDancing );
|
||||
}
|
||||
}
|
||||
@@ -10,9 +10,19 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
// Camera.LocalRotation = EyeAngles.ToRotation();
|
||||
// var offset = CameraPivot.LocalPosition + CameraPivot.LocalRotation.Backward * CamOffsetX;
|
||||
// Camera.LocalPosition = offset * Camera.LocalRotation;
|
||||
|
||||
Camera.LocalRotation = EyeAngles.ToRotation();
|
||||
var offset = CameraPivot.LocalPosition + CameraPivot.LocalRotation.Backward * (CamOffsetX + EyeAngles.pitch * .5f);
|
||||
Camera.LocalPosition = offset * Camera.LocalRotation;
|
||||
var pivotOffset = CameraPivot.LocalRotation.Backward * CamOffsetX;
|
||||
var localPitchOffset = Camera.LocalRotation.Down * MathF.Max( 0f, EyeAngles.pitch ) * 0.32f +
|
||||
Camera.LocalRotation.Backward * MathF.Max( 0f, EyeAngles.pitch ) * 0.7f +
|
||||
Camera.LocalRotation.Up * MathF.Min( 0f, EyeAngles.pitch ) * 0.5f +
|
||||
Camera.LocalRotation.Backward * MathF.Min( 0f, EyeAngles.pitch ) * 0.8f;
|
||||
|
||||
var offset = (CameraPivot.LocalPosition + pivotOffset ) * EyeAngles.ToRotation() + localPitchOffset;
|
||||
Camera.LocalPosition = offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,302 +1,219 @@
|
||||
public sealed partial class Dedugan
|
||||
using Sandbox;
|
||||
using Sandbox.Utility;
|
||||
using Sandbox.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
public sealed partial class Dedugan : Component
|
||||
{
|
||||
public Component Pressed { get; set; }
|
||||
public bool EnablePressing { get; set; } = true;
|
||||
public Component Hovered { get; set; }
|
||||
public Component Pressed { get; set; }
|
||||
public bool EnablePressing { get; set; } = true;
|
||||
public Component Hovered { get; set; }
|
||||
|
||||
[Sync( SyncFlags.Interpolate )]
|
||||
public Vector3 TracedHitPos { get; set; }
|
||||
[Sync]
|
||||
public bool CameraTraceIsHit { get; set; }
|
||||
public Vector3 TracedHitNormal { get; set; }
|
||||
[Sync(SyncFlags.Interpolate)]
|
||||
public Vector3 TracedHitPos { get; set; }
|
||||
[Sync]
|
||||
public bool CameraTraceIsHit { get; set; }
|
||||
public Vector3 TracedHitNormal { get; set; }
|
||||
|
||||
private GameObject interactionPanel;
|
||||
private static GameObject interactionPanelPrefab;
|
||||
// private TimeSince HoldTime = 0;
|
||||
// private bool Holding = false;
|
||||
// private bool HoldingInteractionHappened = false;
|
||||
|
||||
private TimeSince holdTimer;
|
||||
private bool isHolding;
|
||||
private bool triggered;
|
||||
void InteractionsUpdate()
|
||||
{
|
||||
if (!EnablePressing)
|
||||
{
|
||||
// Holding = false;
|
||||
// HoldingInteractionHappened = false;
|
||||
return;
|
||||
}
|
||||
|
||||
private const string InteractionPrefabPath = "prefabs/InteractionPanel.prefab";
|
||||
if (Pressed.IsValid())
|
||||
{
|
||||
UpdatePressed();
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateHovered();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateLookAt()
|
||||
{
|
||||
if ( EnablePressing )
|
||||
{
|
||||
if ( Pressed.IsValid() )
|
||||
{
|
||||
UpdatePressed();
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateHovered();
|
||||
}
|
||||
}
|
||||
}
|
||||
private void UpdatePressed()
|
||||
{
|
||||
bool flag = Input.Down("use");
|
||||
|
||||
private void UpdatePressed()
|
||||
{
|
||||
bool flag = Input.Pressed( "Use" );
|
||||
if (flag && Pressed.Components.TryGet<IPressable>(out var pressable))
|
||||
{
|
||||
flag = pressable.Pressing(new IPressable.Event
|
||||
{
|
||||
Ray = new Ray(Camera.WorldPosition, EyeAngles.ToRotation().Forward),
|
||||
Source = this
|
||||
});
|
||||
}
|
||||
|
||||
if ( flag && Pressed.Components.TryGet<IPressable>( out var pressable ) )
|
||||
{
|
||||
if ( pressable.RequiresHold )
|
||||
{
|
||||
if ( !isHolding )
|
||||
{
|
||||
holdTimer = 0;
|
||||
isHolding = true;
|
||||
triggered = false;
|
||||
}
|
||||
if (GetDistanceFromGameObject(Pressed.GameObject, Camera.WorldPosition) > InteractDistance)
|
||||
{
|
||||
flag = false;
|
||||
}
|
||||
|
||||
if ( triggered ) return;
|
||||
if (!flag)
|
||||
{
|
||||
StopPressing();
|
||||
}
|
||||
}
|
||||
|
||||
var progress = holdTimer / pressable.HoldTime;
|
||||
ShowInteractionUI( TracedHitPos, pressable.DisplayText, true, progress );
|
||||
private void UpdateHovered()
|
||||
{
|
||||
SwitchHovered(TryGetLookedAt());
|
||||
|
||||
if ( holdTimer > pressable.HoldTime )
|
||||
{
|
||||
triggered = true;
|
||||
pressable.Pressing( new IPressable.Event
|
||||
{
|
||||
Ray = new Ray( Camera.WorldPosition, EyeAngles.ToRotation().Forward ),
|
||||
Source = this
|
||||
} );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
flag = pressable.Pressing( new IPressable.Event
|
||||
{
|
||||
Ray = new Ray( Camera.WorldPosition, EyeAngles.ToRotation().Forward ),
|
||||
Source = this
|
||||
} );
|
||||
}
|
||||
}
|
||||
if (Hovered is IPressable pressable)
|
||||
{
|
||||
pressable.Look(new IPressable.Event
|
||||
{
|
||||
Ray = new Ray(Camera.WorldPosition, EyeAngles.ToRotation().Forward),
|
||||
Source = this
|
||||
});
|
||||
}
|
||||
|
||||
if ( GetDistanceFromGameObject( Pressed.GameObject, Camera.WorldPosition ) > InteractDistance )
|
||||
{
|
||||
flag = false;
|
||||
}
|
||||
if (Input.Down("use"))
|
||||
{
|
||||
StartPressing(Hovered);
|
||||
}
|
||||
}
|
||||
|
||||
if ( !flag )
|
||||
{
|
||||
StopPressing();
|
||||
}
|
||||
}
|
||||
public void StartPressing(Component obj)
|
||||
{
|
||||
StopPressing();
|
||||
if (!obj.IsValid())
|
||||
{
|
||||
ISceneEvent<PlayerController.IEvents>.PostToGameObject(GameObject, x => x.FailPressing());
|
||||
return;
|
||||
}
|
||||
|
||||
private void UpdateHovered()
|
||||
{
|
||||
SwitchHovered( TryGetLookedAt() );
|
||||
var component = obj.Components.Get<IPressable>(FindMode.EnabledInSelfAndDescendants);
|
||||
if (component != null)
|
||||
{
|
||||
var pressEvent = new IPressable.Event
|
||||
{
|
||||
Ray = new Ray(Camera.WorldPosition, EyeAngles.ToRotation().Forward),
|
||||
Source = this
|
||||
};
|
||||
|
||||
if ( Hovered is IPressable pressable )
|
||||
{
|
||||
pressable.Look( new IPressable.Event
|
||||
{
|
||||
Ray = new Ray( Camera.WorldPosition, EyeAngles.ToRotation().Forward ),
|
||||
Source = this
|
||||
} );
|
||||
if (!component.CanPress(pressEvent))
|
||||
{
|
||||
ISceneEvent<PlayerController.IEvents>.PostToGameObject(GameObject, x => x.FailPressing());
|
||||
return;
|
||||
}
|
||||
|
||||
ShowInteractionUI( TracedHitPos, pressable.DisplayText );
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearInteractionUI();
|
||||
}
|
||||
component.Press(pressEvent);
|
||||
}
|
||||
|
||||
if ( Input.Pressed( "use" ) )
|
||||
{
|
||||
StartPressing( Hovered );
|
||||
}
|
||||
}
|
||||
Pressed = obj;
|
||||
|
||||
public void StartPressing( Component obj )
|
||||
{
|
||||
StopPressing();
|
||||
if (Pressed.IsValid())
|
||||
{
|
||||
ISceneEvent<PlayerController.IEvents>.PostToGameObject(GameObject, x => x.StartPressing(Pressed));
|
||||
}
|
||||
}
|
||||
|
||||
if ( !obj.IsValid() )
|
||||
{
|
||||
ISceneEvent<PlayerController.IEvents>.PostToGameObject( GameObject, x => x.FailPressing() );
|
||||
return;
|
||||
}
|
||||
public void StopPressing()
|
||||
{
|
||||
if (Pressed.IsValid())
|
||||
{
|
||||
ISceneEvent<PlayerController.IEvents>.PostToGameObject(GameObject, x => x.StopPressing(Pressed));
|
||||
|
||||
var component = obj.Components.Get<IPressable>( FindMode.EnabledInSelfAndDescendants );
|
||||
if (Pressed is IPressable pressable)
|
||||
{
|
||||
pressable.Release(new IPressable.Event
|
||||
{
|
||||
Ray = new Ray(Camera.WorldPosition, EyeAngles.ToRotation().Forward),
|
||||
Source = this
|
||||
});
|
||||
}
|
||||
|
||||
if ( component != null )
|
||||
{
|
||||
if ( !component.CanPress( new IPressable.Event
|
||||
{
|
||||
Ray = new Ray( Camera.WorldPosition, EyeAngles.ToRotation().Forward ),
|
||||
Source = this
|
||||
} ) )
|
||||
{
|
||||
ISceneEvent<PlayerController.IEvents>.PostToGameObject( GameObject, x => x.FailPressing() );
|
||||
return;
|
||||
}
|
||||
Pressed = null;
|
||||
}
|
||||
}
|
||||
|
||||
component.Press( new IPressable.Event
|
||||
{
|
||||
Ray = new Ray( Camera.WorldPosition, EyeAngles.ToRotation().Forward ),
|
||||
Source = this
|
||||
} );
|
||||
}
|
||||
private void SwitchHovered(Component obj)
|
||||
{
|
||||
var e = new IPressable.Event
|
||||
{
|
||||
Ray = new Ray(Camera.WorldPosition, EyeAngles.ToRotation().Forward),
|
||||
Source = this
|
||||
};
|
||||
|
||||
Pressed = obj;
|
||||
if (Hovered == obj)
|
||||
{
|
||||
if (Hovered is IPressable pressable)
|
||||
pressable.Look(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( Pressed.IsValid() )
|
||||
{
|
||||
ISceneEvent<PlayerController.IEvents>.PostToGameObject( GameObject, x => x.StartPressing( Pressed ) );
|
||||
}
|
||||
}
|
||||
if (Hovered is IPressable oldPressable)
|
||||
oldPressable.Blur(e);
|
||||
|
||||
private Component TryGetLookedAt()
|
||||
{
|
||||
for ( float num = 0f; num <= 4f; num += 2f )
|
||||
{
|
||||
var from = Scene.Camera.WorldPosition + Scene.Camera.WorldRotation.Forward;
|
||||
var to = from + Scene.Camera.WorldRotation.Forward * (InteractDistance - num);
|
||||
var eyeTrace = Scene.Trace.Ray( from, to ).IgnoreGameObjectHierarchy( GameObject ).Radius( num ).Run();
|
||||
Hovered = obj;
|
||||
|
||||
TracedHitPos = eyeTrace.Hit ? eyeTrace.HitPosition : eyeTrace.EndPosition;
|
||||
CameraTraceIsHit = eyeTrace.Hit;
|
||||
TracedHitNormal = eyeTrace.Normal;
|
||||
if (Hovered is IPressable newPressable)
|
||||
{
|
||||
newPressable.Hover(e);
|
||||
newPressable.Look(e);
|
||||
}
|
||||
}
|
||||
|
||||
if ( !eyeTrace.Hit || !eyeTrace.GameObject.IsValid() )
|
||||
continue;
|
||||
private Component TryGetLookedAt()
|
||||
{
|
||||
for (float num = 0f; num <= 4f; num += 2f)
|
||||
{
|
||||
var from = Scene.Camera.WorldPosition + Scene.Camera.WorldRotation.Forward;
|
||||
var to = from + Scene.Camera.WorldRotation.Forward * (InteractDistance - num);
|
||||
var trace = Scene.Trace.Ray(from, to).IgnoreGameObjectHierarchy(GameObject).Radius(num).Run();
|
||||
|
||||
Component foundComponent = null;
|
||||
TracedHitPos = trace.Hit ? trace.HitPosition : trace.EndPosition;
|
||||
CameraTraceIsHit = trace.Hit;
|
||||
TracedHitNormal = trace.Normal;
|
||||
|
||||
ISceneEvent<PlayerController.IEvents>.PostToGameObject( GameObject, x =>
|
||||
{
|
||||
foundComponent = x.GetUsableComponent( eyeTrace.GameObject ) ?? foundComponent;
|
||||
} );
|
||||
if (!trace.Hit || !trace.GameObject.IsValid()) continue;
|
||||
|
||||
if ( foundComponent.IsValid() )
|
||||
return foundComponent;
|
||||
Component foundComponent = null;
|
||||
|
||||
foreach ( var component in eyeTrace.GameObject.Components.GetAll<IPressable>() )
|
||||
{
|
||||
if ( component.CanPress( new IPressable.Event
|
||||
{
|
||||
Ray = new Ray( Camera.WorldPosition, EyeAngles.ToRotation().Forward ),
|
||||
Source = this
|
||||
} ) )
|
||||
{
|
||||
return component as Component;
|
||||
}
|
||||
}
|
||||
}
|
||||
ISceneEvent<PlayerController.IEvents>.PostToGameObject(GameObject, x =>
|
||||
{
|
||||
foundComponent = x.GetUsableComponent(trace.GameObject) ?? foundComponent;
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
if (foundComponent.IsValid()) return foundComponent;
|
||||
|
||||
public void StopPressing()
|
||||
{
|
||||
if ( Pressed.IsValid() )
|
||||
{
|
||||
ISceneEvent<PlayerController.IEvents>.PostToGameObject( GameObject, x => x.StopPressing( Pressed ) );
|
||||
foreach (var component in trace.GameObject.Components.GetAll<IPressable>())
|
||||
{
|
||||
if (component.CanPress(new IPressable.Event
|
||||
{
|
||||
Ray = new Ray(Camera.WorldPosition, EyeAngles.ToRotation().Forward),
|
||||
Source = this
|
||||
}))
|
||||
{
|
||||
return component as Component;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( Pressed is IPressable pressable )
|
||||
{
|
||||
pressable.Release( new IPressable.Event
|
||||
{
|
||||
Ray = new Ray( Camera.WorldPosition, EyeAngles.ToRotation().Forward ),
|
||||
Source = this
|
||||
} );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Pressed = null;
|
||||
}
|
||||
private float GetDistanceFromGameObject(GameObject obj, Vector3 point)
|
||||
{
|
||||
Vector3 closest = obj.WorldPosition;
|
||||
float minDist = Vector3.DistanceBetween(closest, point);
|
||||
|
||||
isHolding = false;
|
||||
triggered = false;
|
||||
ClearInteractionUI();
|
||||
}
|
||||
foreach (var col in Pressed.GetComponentsInChildren<Collider>())
|
||||
{
|
||||
Vector3 cp = col.FindClosestPoint(point);
|
||||
float dist = Vector3.DistanceBetween(cp, point);
|
||||
if (dist < minDist)
|
||||
minDist = dist;
|
||||
}
|
||||
|
||||
private void SwitchHovered( Component obj )
|
||||
{
|
||||
var e = new IPressable.Event
|
||||
{
|
||||
Ray = new Ray( Camera.WorldPosition, EyeAngles.ToRotation().Forward ),
|
||||
Source = this
|
||||
};
|
||||
|
||||
if ( Hovered == obj )
|
||||
{
|
||||
if ( Hovered is IPressable pressable )
|
||||
{
|
||||
pressable.Look( e );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( Hovered is IPressable pressable2 )
|
||||
{
|
||||
pressable2.Blur( e );
|
||||
Hovered = null;
|
||||
}
|
||||
|
||||
Hovered = obj;
|
||||
|
||||
if ( Hovered is IPressable pressable3 )
|
||||
{
|
||||
pressable3.Hover( e );
|
||||
pressable3.Look( e );
|
||||
}
|
||||
}
|
||||
|
||||
private float GetDistanceFromGameObject( GameObject obj, Vector3 point )
|
||||
{
|
||||
var a = obj.WorldPosition;
|
||||
var b = Camera.WorldPosition;
|
||||
float minDist = Vector3.DistanceBetween( a, b );
|
||||
|
||||
foreach ( var collider in Pressed.GetComponentsInChildren<Collider>() )
|
||||
{
|
||||
var closest = collider.FindClosestPoint( Camera.WorldPosition );
|
||||
var dist = Vector3.DistanceBetween( closest, Camera.WorldPosition );
|
||||
|
||||
if ( dist < minDist )
|
||||
{
|
||||
minDist = dist;
|
||||
}
|
||||
}
|
||||
|
||||
return minDist;
|
||||
}
|
||||
|
||||
private void ShowInteractionUI( Vector3 position, string text, bool showProgress = false, float progress = 0f )
|
||||
{
|
||||
if ( interactionPanelPrefab == null )
|
||||
{
|
||||
interactionPanelPrefab = GameObject.GetPrefab( InteractionPrefabPath );
|
||||
if ( interactionPanelPrefab == null ) return;
|
||||
}
|
||||
|
||||
if ( !interactionPanel.IsValid() )
|
||||
{
|
||||
interactionPanel = interactionPanelPrefab.Clone( GameObject.Scene );
|
||||
}
|
||||
|
||||
interactionPanel.Transform.Position = position;
|
||||
interactionPanel.Transform.Rotation = Rotation.LookAt( Camera.WorldPosition - position );
|
||||
|
||||
var panel = interactionPanel.GetComponent<PanelComponent>()?.GetPanel();
|
||||
if ( panel is not null )
|
||||
{
|
||||
panel.SetProperty( "InteractionString", text );
|
||||
panel.SetProperty( "IsHoldInteraction", showProgress );
|
||||
panel.SetProperty( "ProgressionHold", progress );
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearInteractionUI()
|
||||
{
|
||||
if ( interactionPanel.IsValid() )
|
||||
{
|
||||
interactionPanel.Destroy();
|
||||
interactionPanel = null;
|
||||
}
|
||||
}
|
||||
return minDist;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,10 @@ public sealed partial class Dedugan : Component
|
||||
{
|
||||
[RequireComponent] public ShrimpleCharacterController.ShrimpleCharacterController Controller { get; set; }
|
||||
[RequireComponent] public CitizenAnimationHelper AnimationHelper { get; set; }
|
||||
|
||||
// [Property] public SkinnedModelRenderer Anim {get; set;}
|
||||
|
||||
public SkinnedModelRenderer Renderer { get; set; }
|
||||
public GameObject Camera { get; set; }
|
||||
[Property] public GameObject Camera { get; set; }
|
||||
[Property] public GameObject CameraPivot { get; set; }
|
||||
|
||||
[Property][Range(1f, 200f, 1f)] public float CamOffsetX { get; set; }
|
||||
@@ -22,7 +23,7 @@ public sealed partial class Dedugan : Component
|
||||
[Sync] public Angles NetworkedEyeAngles { get; set; }
|
||||
public Angles EyeAngles { get; set; }
|
||||
[Sync] private float IsDucking { get; set; } = 0f;
|
||||
|
||||
[Sync] public bool IsDancing { get; set; } = false;
|
||||
private RagdollController RagdollController { get; set; }
|
||||
|
||||
public Vector3 OverrideGravity { get; set; } = Vector3.Zero;
|
||||
@@ -38,13 +39,11 @@ public sealed partial class Dedugan : Component
|
||||
RagdollController = Components.Get<RagdollController>();
|
||||
Renderer = Components.Get<SkinnedModelRenderer>(FindMode.EverythingInSelfAndDescendants);
|
||||
|
||||
var cameraComponent = Camera.GetComponent<CameraComponent>();
|
||||
cameraComponent.Enabled = false;
|
||||
|
||||
if (!Network.IsOwner) return;
|
||||
|
||||
var cameraComponent = Scene.Camera;
|
||||
Camera = cameraComponent.GameObject;
|
||||
Camera.SetParent(GameObject);
|
||||
cameraComponent.ZFar = 32768f;
|
||||
cameraComponent.FieldOfView = 100f;
|
||||
cameraComponent.Enabled = true;
|
||||
}
|
||||
|
||||
protected override void OnUpdate()
|
||||
@@ -53,6 +52,7 @@ public sealed partial class Dedugan : Component
|
||||
{
|
||||
EyeAngles += Input.AnalogLook;
|
||||
EyeAngles = EyeAngles.WithPitch(MathX.Clamp(EyeAngles.pitch, -89f, 89f));
|
||||
NetworkedEyeAngles = EyeAngles;
|
||||
|
||||
var targetRotation = Rotation.LookAt(Rotation.FromYaw(EyeAngles.yaw).Forward, -_directionToAxis);
|
||||
var currentForward = Renderer.LocalRotation.Forward;
|
||||
@@ -60,19 +60,39 @@ public sealed partial class Dedugan : Component
|
||||
|
||||
if (angleDiff > 15f && Controller.Velocity.Length > 10f)
|
||||
{
|
||||
NetworkedEyeAngles = EyeAngles;
|
||||
Renderer.LocalRotation = Rotation.Slerp(Renderer.LocalRotation, Rotation.FromYaw(EyeAngles.yaw), Time.Delta * 3f);
|
||||
}
|
||||
|
||||
RotateCamera();
|
||||
UpdateLookAt();
|
||||
// UpdatePressed();
|
||||
InteractionsUpdate();
|
||||
}
|
||||
else
|
||||
{
|
||||
EyeAngles = NetworkedEyeAngles;
|
||||
Renderer.LocalRotation = Rotation.Slerp(Renderer.LocalRotation, Rotation.FromYaw(EyeAngles.yaw), Time.Delta * 5f);
|
||||
|
||||
var targetRotation = Rotation.LookAt(Rotation.FromYaw(EyeAngles.yaw).Forward, -_directionToAxis);
|
||||
var currentForward = Renderer.LocalRotation.Forward;
|
||||
float angleDiff = currentForward.Angle(targetRotation.Forward);
|
||||
|
||||
if (angleDiff > 15f && Controller.Velocity.Length > 10f)
|
||||
{
|
||||
Renderer.LocalRotation = Rotation.Slerp(Renderer.LocalRotation, Rotation.FromYaw(EyeAngles.yaw), Time.Delta * 3f);
|
||||
}
|
||||
|
||||
// Renderer.LocalRotation = Rotation.Slerp(Renderer.LocalRotation, Rotation.FromYaw(EyeAngles.yaw), Time.Delta * 5f);
|
||||
|
||||
Camera.LocalRotation = EyeAngles.ToRotation();
|
||||
var pivotOffset = CameraPivot.LocalRotation.Backward * CamOffsetX;
|
||||
var localPitchOffset = Camera.LocalRotation.Down * MathF.Max( 0f, EyeAngles.pitch ) * 0.32f +
|
||||
Camera.LocalRotation.Backward * MathF.Max( 0f, EyeAngles.pitch ) * 0.7f +
|
||||
Camera.LocalRotation.Up * MathF.Min( 0f, EyeAngles.pitch ) * 0.5f +
|
||||
Camera.LocalRotation.Backward * MathF.Min( 0f, EyeAngles.pitch ) * 0.8f;
|
||||
|
||||
var offset = (CameraPivot.LocalPosition + pivotOffset ) * EyeAngles.ToRotation() + localPitchOffset;
|
||||
Camera.LocalPosition = offset;
|
||||
}
|
||||
|
||||
UpdateCustomAnimations();
|
||||
}
|
||||
|
||||
protected override void OnFixedUpdate()
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
using Sandbox;
|
||||
using Sandbox.UI;
|
||||
|
||||
public sealed class MusicPlayerInteractions : SimpleInteractions.SimpleInteraction
|
||||
public sealed class MusicPlayerInteractions : InteractionButton
|
||||
{
|
||||
[Property] public MyMusicPlayer MusicPlayer { get; set; }
|
||||
|
||||
[Rpc.Broadcast]
|
||||
protected override void OnInteract()
|
||||
[Property] public MyMusicPlayer MusicPlayer { get; set; }
|
||||
|
||||
public override bool Press(IPressable.Event e)
|
||||
{
|
||||
// Log.Info($"{Rpc.Caller.DisplayName} interacted with {this.GameObject.Name}!");
|
||||
base.Press(e);
|
||||
Log.Info("PRESSED");
|
||||
MusicPlayer.Next();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
namespace Sandbox;
|
||||
|
||||
[Icon( "skip_next" )]
|
||||
public sealed class MusicPlayerNextButton : Component, Component.IPressable
|
||||
{
|
||||
[Property] public string Label { get; set; } = "Next track";
|
||||
|
||||
public bool Press( Component.IPressable.Event e )
|
||||
{
|
||||
Log.Info( $"Press от {e.Source}" );
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Hover( Component.IPressable.Event e )
|
||||
{
|
||||
Log.Info( $"Hover от {e.Source}" );
|
||||
}
|
||||
|
||||
public void Look( Component.IPressable.Event e )
|
||||
{
|
||||
Log.Info( "Look..." );
|
||||
}
|
||||
|
||||
public void Blur( Component.IPressable.Event e )
|
||||
{
|
||||
Log.Info( "Blur — игрок отвёл взгляд" );
|
||||
}
|
||||
|
||||
public void Release( Component.IPressable.Event e )
|
||||
{
|
||||
Log.Info( "Release — игрок отпустил кнопку" );
|
||||
}
|
||||
|
||||
public bool Pressing( Component.IPressable.Event e )
|
||||
{
|
||||
// возвращаем true, чтобы удержание продолжалось
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CanPress( Component.IPressable.Event e )
|
||||
{
|
||||
// например, можно сделать: return !isCoolingDown
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,64 @@
|
||||
@using System.Threading.Tasks
|
||||
@inherits PanelComponent
|
||||
@inherits PanelComponent
|
||||
|
||||
<root class="interaction-panel">
|
||||
<div class="label">@InteractionString</div>
|
||||
<div class="label @(IsPressed ? "pressed" : "")">@InteractionString</div>
|
||||
|
||||
@if (IsHoldInteraction)
|
||||
@if ( IsHoldInteraction )
|
||||
{
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" style="width: @(ProgressionHold * 100)%"></div>
|
||||
<div class="progress-fill" style="width: @( ProgressionHold * 100 )%"></div>
|
||||
</div>
|
||||
}
|
||||
</root>
|
||||
|
||||
<style lang="scss">
|
||||
.interaction-panel {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
transition: all 0.1s ease;
|
||||
|
||||
.progress-bar {
|
||||
width: 100px;
|
||||
height: 8px;
|
||||
background-color: #333;
|
||||
border-radius: 4px;
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background-color: limegreen;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
color: white;
|
||||
font-size: 56px;
|
||||
background-color: rgba(0, 128, 255, 0.2);
|
||||
transition: all 0.1s ease;
|
||||
}
|
||||
|
||||
.label.pressed {
|
||||
background-color: rgba(0, 128, 255, 0.6);
|
||||
transform: scale(1.4);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@code {
|
||||
[Property] public string InteractionString { get; set; } = "Interact";
|
||||
[Property] public bool IsHoldInteraction { get; set; } = false;
|
||||
[Property] public float ProgressionHold { get; set; } = 0f;
|
||||
|
||||
public async Task TriggerInteractAnimation()
|
||||
[Property] public bool IsPressed { get; set; } = false;
|
||||
|
||||
public void SetPressed(bool value)
|
||||
{
|
||||
AddClass("interacted");
|
||||
await Task.Delay(300);
|
||||
RemoveClass("interacted");
|
||||
IsPressed = value;
|
||||
StateHasChanged(); // << 🔁 вручную обновим UI
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
namespace Sandbox.UI;
|
||||
|
||||
public class HoverInfoPanelBase : WorldPanel
|
||||
{
|
||||
[Property] public string Label { get; set; } = "Описание";
|
||||
[Property] public bool Visible { get; set; } = false;
|
||||
|
||||
public HoverInfoPanelBase(SceneWorld world) : base(world)
|
||||
{
|
||||
PanelBounds = new Rect(-200, -100, 400, 200);
|
||||
WorldScale = 0.05f;
|
||||
}
|
||||
}
|
||||
87
Code/UI/InteractionButton.cs
Normal file
87
Code/UI/InteractionButton.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using Sandbox.Diagnostics;
|
||||
|
||||
namespace Sandbox.UI;
|
||||
|
||||
public abstract class InteractionButton : Component, Component.IPressable
|
||||
{
|
||||
[Property] public string Label { get; set; } = "Label";
|
||||
[Property] public bool RequiresHold { get; set; } = false;
|
||||
private HoverInfoPanel _uiPanel;
|
||||
private GameObject _interactionPanelPrefab { get; set; }
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
|
||||
_interactionPanelPrefab = GameObject.GetPrefab("prefabs/InteractionPanel.prefab");
|
||||
Log.Info(_interactionPanelPrefab.Name);
|
||||
Assert.True(_interactionPanelPrefab.IsValid(), $"No InteractionPanel prefab found for {GameObject.Name}!");
|
||||
}
|
||||
|
||||
public virtual void Hover(IPressable.Event e)
|
||||
{
|
||||
CreatePanel();
|
||||
}
|
||||
|
||||
public virtual void Blur(IPressable.Event e)
|
||||
{
|
||||
DestroyPanel();
|
||||
}
|
||||
|
||||
public virtual void Look(IPressable.Event e)
|
||||
{
|
||||
if (_uiPanel != null)
|
||||
{
|
||||
_uiPanel.ProgressionHold = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool CanPress( IPressable.Event e )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool Press(IPressable.Event e)
|
||||
{
|
||||
if (_uiPanel != null)
|
||||
{
|
||||
_uiPanel?.SetPressed(true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual void Release(IPressable.Event e)
|
||||
{
|
||||
if (_uiPanel != null)
|
||||
{
|
||||
_uiPanel?.SetPressed(false);
|
||||
}
|
||||
}
|
||||
|
||||
protected void CreatePanel()
|
||||
{
|
||||
if (_uiPanel != null) return;
|
||||
|
||||
var panelGo = _interactionPanelPrefab.Clone();
|
||||
panelGo.WorldPosition = WorldPosition;
|
||||
panelGo.Parent = Scene;
|
||||
_uiPanel = panelGo.Components.Get<HoverInfoPanel>();
|
||||
if (_uiPanel != null)
|
||||
{
|
||||
_uiPanel.InteractionString = Label;
|
||||
_uiPanel.IsHoldInteraction = RequiresHold;
|
||||
_uiPanel.ProgressionHold = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
protected async void DestroyPanel()
|
||||
{
|
||||
if (_uiPanel == null) return;
|
||||
|
||||
_uiPanel.GameObject.Destroy();
|
||||
_uiPanel = null;
|
||||
|
||||
await Task.DelaySeconds(0.05f);
|
||||
}
|
||||
}
|
||||
22
Code/UI/MusicPlayerNextButton.cs
Normal file
22
Code/UI/MusicPlayerNextButton.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace Sandbox.UI;
|
||||
|
||||
[Icon("skip_next")]
|
||||
public sealed class MusicPlayerNextButton : InteractionButton
|
||||
{
|
||||
[Property] public MyMusicPlayer MusicPlayer { get; set; }
|
||||
|
||||
public override bool Press(IPressable.Event e)
|
||||
{
|
||||
base.Press(e);
|
||||
DoNextRpc(); // вызываем RPC отдельно из за того что в базовом классе абстракт и RPC там не работают
|
||||
return true;
|
||||
}
|
||||
|
||||
[Rpc.Broadcast]
|
||||
private void DoNextRpc()
|
||||
{
|
||||
Log.Info("PRESSED");
|
||||
MusicPlayer?.Next();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
.interaction-panel {
|
||||
padding: 8px;
|
||||
background-color: rgba(0,0,0,0.7);
|
||||
border-radius: 6px;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
.label {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.progress-bar {
|
||||
width: 100px;
|
||||
height: 8px;
|
||||
background-color: #333;
|
||||
border-radius: 4px;
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background-color: limegreen;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
&.interacted {
|
||||
background-color: rgba(0, 128, 255, 0.6);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user