Files
sbox-public/engine/Sandbox.Engine/Scene/Components/Game/ManualHitbox.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

165 lines
3.3 KiB
C#

namespace Sandbox;
/// <summary>
/// A hitbox that can be placed manually on a GameObject, instead of coming from a model
/// </summary>
[Expose]
[Title( "Hitbox" )]
[Category( "Game" )]
[Icon( "psychology_alt" )]
public sealed class ManualHitbox : Component, Component.ExecuteInEditor
{
internal Hitbox Hitbox { get; private set; }
HitboxSystem system;
GameObject _target;
/// <summary>
/// The target GameObject to report in trace hits. If this is unset we'll default to the gameobject on which this component is.
/// </summary>
[Property]
public GameObject Target
{
get => _target;
set
{
if ( _target == value ) return;
_target = value;
Rebuild();
}
}
public enum HitboxShape
{
Sphere,
Capsule,
Box,
Cylinder
}
[Property] public HitboxShape Shape { get; set; } = HitboxShape.Sphere;
[Property, HideIf( nameof( Shape ), HitboxShape.Box )] public float Radius { get; set; } = 10.0f;
[Property] public Vector3 CenterA { get; set; }
[Property, HideIf( nameof( Shape ), HitboxShape.Sphere )] public Vector3 CenterB { get; set; }
[Property] public TagSet HitboxTags { get; set; } = new();
protected override void OnAwake()
{
Scene.GetSystem( out system );
}
protected override void DrawGizmos()
{
if ( !Gizmo.IsSelected )
return;
if ( Shape == HitboxShape.Sphere )
{
Gizmo.Draw.LineSphere( CenterA, Radius );
}
if ( Shape == HitboxShape.Capsule )
{
Gizmo.Draw.LineCapsule( new( CenterA, CenterB, Radius ) );
}
if ( Shape == HitboxShape.Box )
{
Gizmo.Draw.LineBBox( BBox.FromPositionAndSize( CenterA, CenterB ) );
}
if ( Shape == HitboxShape.Cylinder )
{
Gizmo.Draw.LineCylinder( CenterA, CenterB, Radius, Radius, 16 );
}
}
protected override void OnDirty()
{
Rebuild();
}
protected override void OnEnabled()
{
Rebuild();
}
protected override void OnDisabled()
{
Hitbox?.Dispose();
Hitbox = null;
}
public void Rebuild()
{
if ( system is null )
return;
Hitbox?.Dispose();
Hitbox = null;
var body = new PhysicsBody( system.PhysicsWorld );
var tx = WorldTransform;
PhysicsShape shape = null;
var hitbox = new Hitbox( Target ?? GameObject, HitboxTags, body );
if ( Shape == HitboxShape.Sphere )
{
shape = body.AddSphereShape( CenterA * tx.Scale, Radius * tx.UniformScale );
}
else if ( Shape == HitboxShape.Capsule )
{
shape = body.AddCapsuleShape( CenterA * tx.Scale, CenterB * tx.Scale, Radius * tx.UniformScale );
}
else if ( Shape == HitboxShape.Box )
{
shape = body.AddBoxShape( CenterA, Rotation.Identity, CenterB * 0.5f );
hitbox.Bounds = new( CenterA, CenterB );
}
else if ( Shape == HitboxShape.Cylinder )
{
var axis = CenterB - CenterA;
var height = axis.Length;
var position = (CenterA + CenterB) * 0.5f;
var rotation = Rotation.LookAt( axis.Normal, Vector3.Up );
shape = body.AddCylinderShape( position, rotation, height, Radius );
hitbox.Bounds = new( CenterA, CenterB );
}
if ( shape is not null )
{
shape.Tags.SetFrom( GameObject.Tags );
body.Transform = tx.WithScale( 1 );
body.Component = this;
Hitbox = hitbox;
}
else
{
body.Remove();
}
}
public void UpdatePositions()
{
if ( Hitbox is null ) return;
Hitbox.Body.Transform = WorldTransform;
}
/// <summary>
/// Tags have been updated
/// </summary>
protected override void OnTagsChanged()
{
OnPropertyDirty();
}
}