This commit is contained in:
Oscar 2025-05-25 19:14:50 +03:00
parent df4b259d17
commit 4d212f2ff8
27 changed files with 735 additions and 142 deletions

View File

@ -105,14 +105,14 @@
}, },
"CamOffsetX": 72, "CamOffsetX": 72,
"DuckSpeed": 100, "DuckSpeed": 100,
"JumpStrength": 500, "JumpStrength": 350,
"OnComponentDestroy": null, "OnComponentDestroy": null,
"OnComponentDisabled": null, "OnComponentDisabled": null,
"OnComponentEnabled": null, "OnComponentEnabled": null,
"OnComponentFixedUpdate": null, "OnComponentFixedUpdate": null,
"OnComponentStart": null, "OnComponentStart": null,
"OnComponentUpdate": null, "OnComponentUpdate": null,
"RunSpeed": 500, "RunSpeed": 350,
"WalkSpeed": 200 "WalkSpeed": 200
}, },
{ {

View File

@ -183,7 +183,7 @@
"Mins": "-8159.455,-4074.396,-8423.556", "Mins": "-8159.455,-4074.396,-8423.556",
"Maxs": "8402.695,3846.003,8167.05" "Maxs": "8402.695,3846.003,8167.05"
}, },
"FalloffExponent": 0.26, "FalloffExponent": 0.2370001,
"OnComponentDestroy": null, "OnComponentDestroy": null,
"OnComponentDisabled": null, "OnComponentDisabled": null,
"OnComponentEnabled": null, "OnComponentEnabled": null,
@ -387,6 +387,49 @@
} }
], ],
"Children": [] "Children": []
},
{
"__guid": "9166999a-aeae-4d37-b268-3126e2feb9a3",
"Flags": 0,
"Name": "NormalGravity",
"Position": "31.83635,-19.53155,195.1224",
"Rotation": "-0.00484778,-0.004847862,-0.70709,0.7070903",
"Scale": "5.599995,6.799998,3.799999",
"Components": [
{
"__type": "NormalGravityTrigger",
"__guid": "71846c11-20ac-4814-b369-e5b7fa6153cd",
"OnComponentDestroy": null,
"OnComponentDisabled": null,
"OnComponentEnabled": null,
"OnComponentFixedUpdate": null,
"OnComponentStart": null,
"OnComponentUpdate": null,
"TriggerOnEnter": true
},
{
"__type": "Sandbox.BoxCollider",
"__guid": "8a088c30-0f57-4c82-a3f9-3c8311d65a71",
"Center": "-0.2596588,7.563915,-14.42428",
"Friction": null,
"IsTrigger": true,
"OnComponentDestroy": null,
"OnComponentDisabled": null,
"OnComponentEnabled": null,
"OnComponentFixedUpdate": null,
"OnComponentStart": null,
"OnComponentUpdate": null,
"OnObjectTriggerEnter": null,
"OnObjectTriggerExit": null,
"OnTriggerEnter": null,
"OnTriggerExit": null,
"Scale": "88.76582,118.9528,128.8486",
"Static": true,
"Surface": null,
"SurfaceVelocity": "0,0,0"
}
],
"Children": []
} }
] ]
}, },
@ -1102,7 +1145,8 @@
"__guid": "4fe4f239-d8dc-43ba-a375-99056e22d6c6", "__guid": "4fe4f239-d8dc-43ba-a375-99056e22d6c6",
"Flags": 0, "Flags": 0,
"Name": "ClubTeleportPosition", "Name": "ClubTeleportPosition",
"Position": "521.1795,58.51978,-452.6237", "Position": "769.3529,-524.0504,-333.4725",
"Rotation": "0,0,1,-0.00000004371139",
"Scale": "1.666667,1.666667,1.666667", "Scale": "1.666667,1.666667,1.666667",
"Enabled": true, "Enabled": true,
"Components": [], "Components": [],
@ -1119,6 +1163,7 @@
{ {
"__type": "Sandbox.ModelRenderer", "__type": "Sandbox.ModelRenderer",
"__guid": "bb9a6471-baa0-411c-8825-cc4388863ddc", "__guid": "bb9a6471-baa0-411c-8825-cc4388863ddc",
"__enabled": false,
"BodyGroups": 18446744073709551615, "BodyGroups": 18446744073709551615,
"CreateAttachments": false, "CreateAttachments": false,
"MaterialGroup": null, "MaterialGroup": null,
@ -1163,6 +1208,7 @@
{ {
"__type": "Teleporter", "__type": "Teleporter",
"__guid": "76f3c916-1052-42fd-9e9d-a7bad9f383d8", "__guid": "76f3c916-1052-42fd-9e9d-a7bad9f383d8",
"Building": null,
"OnComponentDestroy": null, "OnComponentDestroy": null,
"OnComponentDisabled": null, "OnComponentDisabled": null,
"OnComponentEnabled": null, "OnComponentEnabled": null,
@ -1192,7 +1238,8 @@
"OnComponentEnabled": null, "OnComponentEnabled": null,
"OnComponentFixedUpdate": null, "OnComponentFixedUpdate": null,
"OnComponentStart": null, "OnComponentStart": null,
"OnComponentUpdate": null "OnComponentUpdate": null,
"TriggerOnEnter": false
}, },
{ {
"__type": "Sandbox.BoxCollider", "__type": "Sandbox.BoxCollider",
@ -2156,13 +2203,15 @@
"__guid": "285c9910-d341-4fa0-9e80-c89a2e31f4cb", "__guid": "285c9910-d341-4fa0-9e80-c89a2e31f4cb",
"Flags": 0, "Flags": 0,
"Name": "TeleportToClub", "Name": "TeleportToClub",
"Position": "1.621101,-302.5153,-740.4399", "Position": "132.7686,525.2025,-686.1906",
"Rotation": "0,-0.1031487,0,0.9946659",
"Scale": "1.954177,1.954177,1.954177", "Scale": "1.954177,1.954177,1.954177",
"Enabled": true, "Enabled": true,
"Components": [ "Components": [
{ {
"__type": "Sandbox.ModelRenderer", "__type": "Sandbox.ModelRenderer",
"__guid": "5481c2cb-7d0a-4b6e-ac58-dbe01f527e8e", "__guid": "5481c2cb-7d0a-4b6e-ac58-dbe01f527e8e",
"__enabled": false,
"BodyGroups": 18446744073709551615, "BodyGroups": 18446744073709551615,
"CreateAttachments": false, "CreateAttachments": false,
"MaterialGroup": null, "MaterialGroup": null,
@ -2186,7 +2235,7 @@
{ {
"__type": "Sandbox.BoxCollider", "__type": "Sandbox.BoxCollider",
"__guid": "d8862ade-55d3-457e-9307-bcf4b2698233", "__guid": "d8862ade-55d3-457e-9307-bcf4b2698233",
"Center": "0,0,0", "Center": "2.781189,0,0",
"Friction": null, "Friction": null,
"IsTrigger": true, "IsTrigger": true,
"OnComponentDestroy": null, "OnComponentDestroy": null,
@ -2199,7 +2248,7 @@
"OnObjectTriggerExit": null, "OnObjectTriggerExit": null,
"OnTriggerEnter": null, "OnTriggerEnter": null,
"OnTriggerExit": null, "OnTriggerExit": null,
"Scale": "50,50,50", "Scale": "76.94324,50,50",
"Static": true, "Static": true,
"Surface": null, "Surface": null,
"SurfaceVelocity": "0,0,0" "SurfaceVelocity": "0,0,0"
@ -2207,6 +2256,7 @@
{ {
"__type": "Teleporter", "__type": "Teleporter",
"__guid": "587a82f6-7821-40db-9fdc-e62d098430f0", "__guid": "587a82f6-7821-40db-9fdc-e62d098430f0",
"Building": null,
"OnComponentDestroy": null, "OnComponentDestroy": null,
"OnComponentDisabled": null, "OnComponentDisabled": null,
"OnComponentEnabled": null, "OnComponentEnabled": null,
@ -2224,7 +2274,7 @@
"__guid": "15224d57-c82e-410a-beea-e0cb76e40e0f", "__guid": "15224d57-c82e-410a-beea-e0cb76e40e0f",
"Flags": 0, "Flags": 0,
"Name": "Out", "Name": "Out",
"Position": "9.995585,41.46935,-23.90106", "Position": "9.99558,-62.89205,-23.90107",
"Scale": "0.5117243,0.5117243,0.5117243", "Scale": "0.5117243,0.5117243,0.5117243",
"Enabled": true, "Enabled": true,
"Components": [], "Components": [],
@ -2305,8 +2355,8 @@
"__type": "Sandbox.DSPReverb", "__type": "Sandbox.DSPReverb",
"__guid": "1fd82892-2a6d-42bd-8f19-a3792bb23f6a", "__guid": "1fd82892-2a6d-42bd-8f19-a3792bb23f6a",
"Bounds": { "Bounds": {
"Mins": "-370.7,-356.5996,-81.70001", "Mins": "-370.7,-597.2996,-147.1001",
"Maxs": "464.7,443,145.1001" "Maxs": "464.7,443,267.5002"
}, },
"FadeDuration": 1, "FadeDuration": 1,
"OnComponentDestroy": null, "OnComponentDestroy": null,
@ -2333,8 +2383,8 @@
"NetworkMode": 1, "NetworkMode": 1,
"Components": [ "Components": [
{ {
"__type": "Sandbox.MusicPlayer", "__type": "Sandbox.MyMusicPlayer",
"__guid": "c556fb67-e030-4b1b-aa6f-b47904bfc2ce", "__guid": "b1b3132b-9530-43ac-ad37-c3319cd6844b",
"_sounds": [], "_sounds": [],
"_speakers": [], "_speakers": [],
"OnComponentDestroy": null, "OnComponentDestroy": null,
@ -2342,7 +2392,8 @@
"OnComponentEnabled": null, "OnComponentEnabled": null,
"OnComponentFixedUpdate": null, "OnComponentFixedUpdate": null,
"OnComponentStart": null, "OnComponentStart": null,
"OnComponentUpdate": null "OnComponentUpdate": null,
"PlayOnStart": false
} }
], ],
"Children": [ "Children": [
@ -2587,7 +2638,7 @@
{ {
"__guid": "dcf53b7e-d474-43bf-973b-b56e3d0ad19d", "__guid": "dcf53b7e-d474-43bf-973b-b56e3d0ad19d",
"Flags": 0, "Flags": 0,
"Name": "Cube", "Name": "NextTrackInteraction",
"Position": "-175.5451,372.674,4760.318", "Position": "-175.5451,372.674,4760.318",
"Rotation": "0,0,-0.4159509,0.9093871", "Rotation": "0,0,-0.4159509,0.9093871",
"Scale": "0.4874838,1.38488,0.08132961", "Scale": "0.4874838,1.38488,0.08132961",
@ -2617,73 +2668,6 @@
"RenderType": "On", "RenderType": "On",
"Tint": "1,1,1,1" "Tint": "1,1,1,1"
}, },
{
"__type": "Sandbox.ButtonComponent",
"__guid": "0f2a90d6-0c9d-4c6a-93e6-e57e95065b88",
"OnClick": {
"__version": 9,
"__guid": "ad39acdb-cfd4-4416-aa98-cf387c135e41",
"__changeId": 22,
"UserData": {
"Title": "On Click",
"ReferencedComponentTypes": []
},
"Variables": [],
"Nodes": [
{
"Id": 0,
"Type": "input"
},
{
"Id": 4,
"Type": "scene.ref",
"UserData": {
"Position": "12,36"
}
},
{
"Id": 7,
"Type": "call",
"Properties": {
"_isStatic": false,
"_name": "Next",
"_type": "Sandbox.MusicPlayer"
},
"UserData": {
"Position": "192,24"
}
}
],
"Links": [
{
"SrcId": 0,
"SrcName": "_signal",
"DstId": 7,
"DstName": "_signal"
},
{
"SrcId": 4,
"SrcName": "_result",
"DstId": 7,
"DstName": "_target"
}
],
"Defaults": {
"$4.component": {
"_type": "component",
"component_id": "c556fb67-e030-4b1b-aa6f-b47904bfc2ce",
"go": "c3d837ce-6d50-4121-88df-7ce18b8cb3b5",
"component_type": "MusicPlayer"
}
}
},
"OnComponentDestroy": null,
"OnComponentDisabled": null,
"OnComponentEnabled": null,
"OnComponentFixedUpdate": null,
"OnComponentStart": null,
"OnComponentUpdate": null
},
{ {
"__type": "Sandbox.BoxCollider", "__type": "Sandbox.BoxCollider",
"__guid": "dc47d030-3ddf-41a6-836f-d6aaed8928dc", "__guid": "dc47d030-3ddf-41a6-836f-d6aaed8928dc",
@ -2704,6 +2688,33 @@
"Static": true, "Static": true,
"Surface": null, "Surface": null,
"SurfaceVelocity": "0,0,0" "SurfaceVelocity": "0,0,0"
},
{
"__type": "MusicPlayerInteractions",
"__guid": "a9465371-6855-4fb1-b0de-777e7f0cc2c2",
"Collider": {
"_type": "component",
"component_id": "dc47d030-3ddf-41a6-836f-d6aaed8928dc",
"go": "dcf53b7e-d474-43bf-973b-b56e3d0ad19d",
"component_type": "BoxCollider"
},
"InteractionDistance": 120,
"InteractionEnabled": true,
"InteractionHold": false,
"InteractionHoldDuration": 0.5,
"InteractionString": "Next track",
"MusicPlayer": {
"_type": "component",
"component_id": "b1b3132b-9530-43ac-ad37-c3319cd6844b",
"go": "c3d837ce-6d50-4121-88df-7ce18b8cb3b5",
"component_type": "MyMusicPlayer"
},
"OnComponentDestroy": null,
"OnComponentDisabled": null,
"OnComponentEnabled": null,
"OnComponentFixedUpdate": null,
"OnComponentStart": null,
"OnComponentUpdate": null
} }
], ],
"Children": [] "Children": []

View File

@ -0,0 +1,28 @@
using Sandbox;
public sealed class NormalGravityTrigger : Component, Component.ITriggerListener
{
[Property] public bool TriggerOnEnter { get; set; } = false;
public void OnTriggerEnter(Collider other)
{
if ( !TriggerOnEnter ) return;
var otherEntity = other.GameObject;
if (otherEntity.Components.TryGet<Dedugan>(out var controller))
{
controller.OverrideGravity = WorldRotation.Down;
}
}
public void OnTriggerExit(Collider other)
{
var otherEntity = other.GameObject;
if (otherEntity.Components.TryGet<Dedugan>(out var controller))
{
controller.OverrideGravity = Vector3.Zero;
}
}
}

View File

@ -4,6 +4,7 @@
public sealed class Teleporter : Component, Component.ITriggerListener public sealed class Teleporter : Component, Component.ITriggerListener
{ {
[Property] public GameObject ToPosGameObject { get; set; } [Property] public GameObject ToPosGameObject { get; set; }
[Property] public GameObject Building { get; set; }
public void OnTriggerEnter( Collider other ) public void OnTriggerEnter( Collider other )
{ {
@ -18,8 +19,13 @@ public sealed class Teleporter : Component, Component.ITriggerListener
otherEntity.WorldPosition = ToPosGameObject.WorldPosition; otherEntity.WorldPosition = ToPosGameObject.WorldPosition;
controller.OverrideGravity = Vector3.Down; controller.OverrideGravity = Vector3.Down;
controller.EyeAngles = new Angles( 0, 180, 0 ).ToRotation(); controller.EyeAngles = new Angles( 0, ToPosGameObject.WorldRotation.Yaw(), 0 ).ToRotation();
controller.Renderer.LocalRotation = new Angles( 0, 180, 0 ).ToRotation(); controller.Renderer.LocalRotation = new Angles( 0, ToPosGameObject.WorldRotation.Yaw(), 0 ).ToRotation();
if ( Building != null )
{
Building.Enabled = !Building.Enabled;
}
} }
} }
} }

View File

@ -1,26 +0,0 @@
using Sandbox;
public sealed class NormalGravityTrigger : Component, Component.ITriggerListener
{
// public void OnTriggerEnter(Collider other)
// {
// var otherEntity = other.GameObject;
//
// if (otherEntity.Components.TryGet<Dedugan>(out var controller))
// {
// Log.Info($"{otherEntity.Name} вошел в зону нормальной гравитации");
//
// controller.OverrideGravity = Vector3.Down;
// }
// }
public void OnTriggerExit(Collider other)
{
var otherEntity = other.GameObject;
if (otherEntity.Components.TryGet<Dedugan>(out var controller))
{
controller.OverrideGravity = Vector3.Zero;
}
}
}

View File

@ -14,7 +14,6 @@ public sealed class RagdollController : Component
bodyPhysics.Enabled = value; bodyPhysics.Enabled = value;
bodyPhysics.MotionEnabled = value; bodyPhysics.MotionEnabled = value;
bodyRenderer.UseAnimGraph = !value; bodyRenderer.UseAnimGraph = !value;
bodyPhysics.
if ( !value ) if ( !value )
{ {

View File

@ -0,0 +1,13 @@
using Sandbox;
public sealed class MusicPlayerInteractions : SimpleInteractions.SimpleInteraction
{
[Property] public MyMusicPlayer MusicPlayer { get; set; }
[Rpc.Broadcast]
protected override void OnInteract()
{
// Log.Info($"{Rpc.Caller.DisplayName} interacted with {this.GameObject.Name}!");
MusicPlayer.Next();
}
}

View File

@ -1,6 +1,6 @@
 namespace Sandbox;  namespace Sandbox;
public sealed class MusicPlayer : Component public sealed class MyMusicPlayer : Component
{ {
[Property] private List<SoundEvent> _sounds; [Property] private List<SoundEvent> _sounds;
[Property] private List<SoundPointComponent> _speakers; [Property] private List<SoundPointComponent> _speakers;

View File

@ -1,9 +0,0 @@
using Sandbox;
public sealed class Test : Component
{
protected override void OnUpdate()
{
}
}

View File

@ -1,6 +0,0 @@
namespace Sandbox;
public class VectorExtension
{
}

View File

@ -1,15 +0,0 @@
using System;
using Sandbox;
using Sandbox.UI;
namespace Sandbox;
public sealed class ButtonComponent : Component, IInteractable
{
[Property] public Action OnClick { get; set; }
public void OnUse()
{
OnClick?.Invoke();
}
}

View File

@ -0,0 +1,4 @@
[
"package.base",
"package.guusconl.simpleinteractions"
]

View File

@ -0,0 +1,28 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>package.guusconl.simpleinteractions</name>
</assembly>
<members>
<member name="T:SimpleInteractions.SimpleInteraction">
<summary>
Simple interaction component
</summary>
</member>
<member name="P:SimpleInteractions.SimpleInteraction.Collider">
<summary>
If not set, will try to find a collider on the same GameObject.
</summary>
</member>
<member name="M:Sandbox.InteractionPanel.BuildHash">
<summary>
the hash determines if the system should be rebuilt. If it changes, it will be rebuilt
</summary>
</member>
<member name="M:Sandbox.InteractionPanel.TriggerInteractAnimation">
<summary>
Triggers the interaction animation.
</summary>
</member>
</members>
</doc>

View File

@ -0,0 +1 @@
1.0.84848

View File

@ -0,0 +1,63 @@
{
"RootObject": {
"__guid": "43c5a0e3-1bec-414e-a5b7-101e4f3070c7",
"Flags": 0,
"Name": "interactionspanel",
"Enabled": true,
"NetworkMode": 0,
"Components": [
{
"__type": "Sandbox.WorldPanel",
"__guid": "e77429f1-f1a2-46f9-a266-6758afc60489",
"HorizontalAlign": "Left",
"InteractionRange": 0,
"LookAtCamera": false,
"PanelSize": "1024,512",
"RenderOptions": {
"GameLayer": true,
"OverlayLayer": true,
"BloomLayer": false,
"AfterUILayer": false
},
"RenderScale": 1,
"VerticalAlign": "Center"
},
{
"__type": "Sandbox.InteractionPanel",
"__guid": "127b843f-1d34-4b1e-b2f9-3e0229d52935"
}
],
"Children": [],
"__variables": [],
"__properties": {
"FixedUpdateFrequency": 50,
"MaxFixedUpdates": 5,
"NetworkFrequency": 30,
"NetworkInterpolation": true,
"PhysicsSubSteps": 1,
"ThreadedAnimation": true,
"TimeScale": 1,
"UseFixedUpdate": true,
"Metadata": {},
"NavMesh": {
"Enabled": false,
"IncludeStaticBodies": true,
"IncludeKeyframedBodies": true,
"EditorAutoUpdate": true,
"AgentHeight": 64,
"AgentRadius": 16,
"AgentStepSize": 18,
"AgentMaxSlope": 40,
"ExcludedBodies": "",
"IncludedBodies": ""
}
}
},
"ShowInMenu": false,
"MenuPath": null,
"MenuIcon": null,
"DontBreakAsTemplate": false,
"ResourceVersion": 1,
"__references": [],
"__version": 1
}

View File

@ -0,0 +1,22 @@
"Layer0"
{
"shader" "shaders/complex.shader"
"g_flAmbientOcclusionDirectDiffuse" "0.000000"
"g_flAmbientOcclusionDirectSpecular" "0.000000"
"TextureAmbientOcclusion" "materials/default/default_ao.tga"
"g_flModelTintAmount" "1.000000"
"g_vColorTint" "[1.000000 1.000000 1.000000 0.000000]"
"TextureColor" "materials/default/default_color.tga"
"g_flFadeExponent" "1.000000"
"g_bFogEnabled" "1"
"g_flDirectionalLightmapMinZ" "0.050000"
"g_flDirectionalLightmapStrength" "1.000000"
"g_flMetalness" "0.000000"
"TextureNormal" "materials/default/default_normal.tga"
"TextureRoughness" "materials/default/default_rough.tga"
"g_nScaleTexCoordUByModelScaleAxis" "0"
"g_nScaleTexCoordVByModelScaleAxis" "0"
"g_vTexCoordOffset" "[0.000 0.000]"
"g_vTexCoordScale" "[1.000 1.000]"
"g_vTexCoordScrollSpeed" "[0.000 0.000]"
}

View File

@ -0,0 +1,227 @@
using Sandbox;
using Sandbox.Utility;
using Sandbox.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace SimpleInteractions {
/// <summary>
/// Simple interaction component
/// </summary>
[Icon( "touch_app" )]
[Title( "Simple Interaction" )]
public class SimpleInteraction : Component
{
[Property]
public bool InteractionEnabled {get; set;} = true;
[Property, Title("Interaction Name")]
public string InteractionString {get; set;} = "Interact";
[Property]
public float InteractionDistance {get; set;} = 120f;
[Property, ToggleGroup("InteractionHold")]
public bool InteractionHold {get; set;} = false;
[Property, Group("InteractionHold")]
public float InteractionHoldDuration {get; set;} = 0.5f;
/// <summary>
/// If not set, will try to find a collider on the same GameObject.
/// </summary>
[Property, Title("Override collider")]
public Collider Collider { get; set; }
private GameObject CurrentPanel = null;
private TimeSince HoldTime = 0;
private bool Holding = false;
private bool HoldingInteractionHappened = false;
static protected GameObject InteractionPanelPrefab ;
protected override void OnStart()
{
InteractionPanelPrefab = GameObject.GetPrefab("InteractionsPanel.prefab");
Assert.True(InteractionPanelPrefab.IsValid(), $"No InteractionPanel prefab found for {this.GameObject.Name}!");
if (!Collider.IsValid()) {
Collider = this.GameObject.GetComponent<Collider>();
Assert.True(Collider.IsValid(), $"No collider found for {this.GameObject.Name}!");
}
this.GameObject.Tags.Add("Interact");
}
protected override void OnUpdate()
{
if (!InteractionEnabled)
{
// Reset everything just in case
Holding = false;
HoldingInteractionHappened = false;
// Delete the Interaction panel otherwise it would just float there...
if (CurrentPanel.IsValid())
{
InteractionPanel panel = CurrentPanel.GetComponent<InteractionPanel>();
if (panel.IsValid()) {
_ = DeletePanel();
}
}
return;
}
Ray ray = Scene.Camera.GameObject.Transform.World.ForwardRay;
var traces = Scene.Trace.Ray(ray, InteractionDistance)
.WithoutTags("IgnoreInteract")
.HitTriggers()
.RunAll();
// Gizmo.Draw.Line(tr.StartPosition, tr.EndPosition);
if (traces.Count() <= 0)
{
_ = DeletePanel();
// Force repressing use in case you looked away while holding down.
HoldingInteractionHappened = true;
return;
}
foreach (var tr in traces)
{
Collider HitCollider = tr.Shape.Collider as Collider;
// If it's a trigger and it doesn't have the interact tag, skip it.
// We can see through it.
if (HitCollider.IsTrigger && !HitCollider.GameObject.Tags.Has("Interact"))
{
continue;
} else if (!HitCollider.IsTrigger && !HitCollider.GameObject.Tags.Has("Interact"))
{
// Something is blocking the interaction.
_ = DeletePanel();
// Force repressing use in case you looked away while holding down.
HoldingInteractionHappened = true;
break;
}
Vector3 offset = Vector3.Zero;
if (HitCollider is BoxCollider)
{
offset = (HitCollider as BoxCollider).Center;
} else if (HitCollider is SphereCollider)
{
offset = new Vector3((HitCollider as SphereCollider).Center);
}
if (HitCollider == Collider)
{
Vector3 pos = new Vector3(offset.x, offset.y, - offset.z);
OnHover(HitCollider.GameObject.WorldPosition - pos);
break;
} else
{
_ = DeletePanel();
// Force repressing use in case you looked away while holding down.
HoldingInteractionHappened = true;
}
}
}
private void OnHover(Vector3 pos)
{
if (!CurrentPanel.IsValid())
{
CurrentPanel = InteractionPanelPrefab.Clone();
}
CurrentPanel.WorldPosition = pos;
// Flip the panel to face the camera
Rotation camRotation = Scene.Camera.WorldRotation;
Angles ang = camRotation.Angles();
ang.roll += 180;
ang.pitch += 180;
Rotation rot = ang.ToRotation();
CurrentPanel.WorldRotation = rot;
InteractionPanel panel = CurrentPanel.GetComponent<InteractionPanel>();
panel.InteractionString = InteractionString;
panel.IsHoldInteraction = InteractionHold;
panel.ProgressionHold = 0;
if (!InteractionHold)
{
if (Input.Pressed("use"))
{
_ = panel.TriggerInteractAnimation();
OnInteract();
}
return;
}
if (!Input.Down("use"))
{
Holding = false;
HoldingInteractionHappened = false;
return;
}
// Interaction already happened. Player needs to release and press again.
if (HoldingInteractionHappened)
{
return;
}
if (Holding)
{
panel.ProgressionHold = Easing.QuadraticInOut(HoldTime / InteractionHoldDuration);
if (HoldTime >= InteractionHoldDuration)
{
HoldingInteractionHappened = true;
OnInteract();
}
} else
{
// Started holding.
Holding = true;
HoldTime = 0;
_ = panel.TriggerInteractAnimation();
}
}
async private Task DeletePanel()
{
if(!CurrentPanel.IsValid()) return;
CurrentPanel.GetComponent<PanelComponent>().Panel.Delete();
await Task.DelaySeconds( 0.1f );
CurrentPanel.Destroy();
}
[Rpc.Broadcast]
protected virtual void OnInteract()
{
Log.Error($"Interaction not implemented for {this.GameObject.Name}!");
}
}
}

View File

@ -0,0 +1,66 @@
@using Sandbox;
@using Sandbox.UI;
@using System.Threading.Tasks;
@inherits PanelComponent
@namespace Sandbox
<root class="@(IsInteracting ? "interact" : "")" >
<!-- times 90 and plus 10 so that there is a minimal width. Otherwise the rounding doesn't play nice -->
<div class="ProgressBar" style="
width:@(ProgressionHold * 90 + 10)%;
@(ProgressionHold > 0 ? "opacity: 1;" : "opacity: 0;")
"></div>
<div class="Left">
<Image class="InteractionGlyph" Texture=@InputTexture/>
@if (IsHoldInteraction)
{
<div class="InteractionHold"> Hold </div>
}
</div>
<div class="Right">
<div class="InteractionTitle"> @InteractionString </div>
</div>
</root>
@code
{
private Texture InputTexture;
public string InteractionString {get; set;}
private bool IsInteracting = false;
public bool IsHoldInteraction = false;
public float ProgressionHold = 0;
/// <summary>
/// the hash determines if the system should be rebuilt. If it changes, it will be rebuilt
/// </summary>
protected override int BuildHash() => System.HashCode.Combine( InputTexture, InteractionString, IsHoldInteraction, ProgressionHold);
protected override void OnUpdate()
{
InputTexture = Input.GetGlyph("use", InputGlyphSize.Medium, true);
}
/// <summary>
/// Triggers the interaction animation.
/// </summary>
public async Task TriggerInteractAnimation()
{
if (IsInteracting)
return; // Prevent overlapping animations
IsInteracting = true;
StateHasChanged();
// Wait for the animation duration (e.g., 300ms)
await Task.Delay(100);
IsInteracting = false;
StateHasChanged();
}
}

View File

@ -0,0 +1,78 @@
InteractionPanel
{
position: absolute;
top: 0;
left: 0;
background-color: #444;
justify-content: center;
align-items: center;
font-weight: bold;
border-radius: 20px;
font-size: 25px;
font-family: Poppins;
color: #fff;
text-stroke: 8px black;
transform-origin: left center;
transition: all 0.1s ease-out;
transform: scale( 1 );
// When the element is created make it expand from nothing.
&:intro {
transform: scale( 0 );
}
&:outro {
transform: scale( 0 );
}
.InteractionTitle
{
color: #fff;
margin-right: 15px;
white-space: normal;
max-width: 300px;
align-items: center;
}
.ProgressBar {
position: absolute; /* Place the progress bar within the root */
top: 0;
left: 0;
height: 100%; /* Fill the entire height of the root */
z-index: 0; /* Ensure it is below the content */
border-radius: 20px;
background-color: #a2a2a2;
}
.Left
{
flex-direction: column;
align-items: center;
padding-left: 20px;
min-width: 110px;
}
.Right
{
width: 100%;
align-items: center;
margin-left: 30px;
}
.InteractionHold
{
margin-top: -13px;
margin-bottom: 5px;
}
&.interact {
transition: all 0.1s ease-out;
transform: scale(0.98);
}
}

View File

@ -0,0 +1,43 @@
using Editor;
[Icon( "touch_app" )]
[Title( "SimpleInteraction" )]
[Description( "A simple interaction component so you can interact with objects" )]
public partial class SimpleInteractionTemplate : ComponentTemplate
{
public override void Create( string componentName, string path )
{
var content = $$"""
using Sandbox;
public sealed class {{componentName}} : SimpleInteractions.SimpleInteraction
{
protected override void OnStart()
{
base.OnStart();
// Put your initialization code here if you have any
}
protected override void OnUpdate()
{
base.OnUpdate();
// Put your update code here if you have any
}
[Rpc.Broadcast]
protected override void OnInteract()
{
Log.Info($"{Rpc.Caller.DisplayName} interacted with {this.GameObject.Name}!");
}
}
""";
var directory = System.IO.Path.GetDirectoryName( path );
System.IO.File.WriteAllText( System.IO.Path.Combine( directory, componentName + Suffix ), content );
}
}

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 GuuscoNL
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,26 @@
# Simple Interactions
Simple Interactions is an interaction system that focusses on keeping it simple. It is mostly meant for prototypes where it is handy for players to interact with object. For example you are creating particle effects and you have a demo game were people can see them in action. You can use this library so people can press buttons that activate your particles.
Only first person is supported using the build in player controller.
Feel free to fork this and make your own changes. I hope it helps you out!
# How to use
Once installed you can go to `add new component` and select `New component`. Under `Create Script From Template` select `New SimpleInteraction` component.
`OnInteract()` is called when the player interacts with the object. You can use this to do whatever you want. For example, you can make a door open, play a sound, or spawn a particle effect.
`InteractionString` is the text that is displayed when the player can interact with the object. For example, you can set this to 'Open door'. Available in the editor.
`InteractionEnabled` is a boolean that you can use to enable or disable the interaction. For example, you can set this to false when the door is already open. Available in the editor.
`InteractionDistance` is the distance the player has to be from the object to interact with it. Available in the editor.
`InteractionHold` is a boolean that you can use to make the player hold the interaction button to interact with the object. Available in the editor.
`InteractionHoldDuration` is the duration the player has to hold the interaction button to interact with the object. Available in the editor.
`Collider` is the collider the interaction system uses to check if the player is looking at the object. This is automatically set to the object's collider. But you can override this by setting it to another collider. Within the editor. Available in the editor.
# Demo
I made a demo scene to show you how to use the library. You can find it [here](https://sbox.game/guusconl/simple_interactions_demo)

View File

@ -0,0 +1,13 @@
{
"Title": "SimpleInteractions",
"Type": "library",
"Org": "guusconl",
"Ident": "simpleinteractions",
"Schema": 1,
"IncludeSourceFiles": false,
"Resources": null,
"PackageReferences": [],
"EditorReferences": null,
"IsWhitelistDisabled": false,
"Metadata": {}
}