mirror of
https://gitlab.com/MaddoScientisto/cirnogodot.git
synced 2026-06-01 06:55:34 +00:00
Laser
This commit is contained in:
parent
8e5c575917
commit
75c0a7d994
11 changed files with 813 additions and 293 deletions
|
|
@ -1202,6 +1202,24 @@
|
|||
( 112 872 -16 ) ( 113 872 -16 ) ( 112 872 -15 ) __TB_empty [ -1 1.0718754395722282e-15 0 -16 ] [ 0 0 -1 0 ] 90 1 1
|
||||
( 288 432 16 ) ( 288 431 16 ) ( 288 432 17 ) __TB_empty [ 1.0718754395722282e-15 1 0 16 ] [ 0 0 -1 0 ] 90 1 1
|
||||
}
|
||||
// brush 133
|
||||
{
|
||||
( -128 128 16 ) ( -128 129 16 ) ( -128 128 17 ) Floors/Floor0 [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
( -128 128 16 ) ( -128 128 17 ) ( -127 128 16 ) Floors/Floor0 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
( -128 128 16 ) ( -127 128 16 ) ( -128 129 16 ) Floors/Floor0 [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
|
||||
( -112 144 32 ) ( -112 145 32 ) ( -111 144 32 ) Floors/Floor0 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
|
||||
( -112 144 32 ) ( -111 144 32 ) ( -112 144 33 ) Floors/Floor0 [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
( -112 144 32 ) ( -112 144 33 ) ( -112 145 32 ) Floors/Floor0 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
}
|
||||
// brush 134
|
||||
{
|
||||
( -128 0 16 ) ( -128 1 16 ) ( -128 0 17 ) Floors/Floor0 [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
( -128 0 16 ) ( -128 0 17 ) ( -127 0 16 ) Floors/Floor0 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
( -128 0 16 ) ( -127 0 16 ) ( -128 1 16 ) Floors/Floor0 [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
|
||||
( -112 16 32 ) ( -112 17 32 ) ( -111 16 32 ) Floors/Floor0 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
|
||||
( -112 16 32 ) ( -111 16 32 ) ( -112 16 33 ) Floors/Floor0 [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
( -112 16 32 ) ( -112 16 33 ) ( -112 17 32 ) Floors/Floor0 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1
|
||||
}
|
||||
}
|
||||
// entity 1
|
||||
{
|
||||
|
|
@ -1477,25 +1495,44 @@
|
|||
// entity 51
|
||||
{
|
||||
"classname" "actor_table_002"
|
||||
"origin" "-104 136 20"
|
||||
"origin" "-216 168 20"
|
||||
}
|
||||
// entity 52
|
||||
{
|
||||
"classname" "actor_table_002"
|
||||
"origin" "-104 120 20"
|
||||
"origin" "-232 216 20"
|
||||
}
|
||||
// entity 53
|
||||
{
|
||||
"classname" "actor_table"
|
||||
"origin" "-88 124 20"
|
||||
"origin" "-264 172 20"
|
||||
}
|
||||
// entity 54
|
||||
{
|
||||
"classname" "actor_table_004"
|
||||
"origin" "-72 120 20"
|
||||
"origin" "-248 136 20"
|
||||
}
|
||||
// entity 55
|
||||
{
|
||||
"classname" "actor_table_003"
|
||||
"origin" "-136 352 20"
|
||||
}
|
||||
// entity 56
|
||||
{
|
||||
"classname" "solid_bullet_permeable"
|
||||
// brush 0
|
||||
{
|
||||
( -120 96 24 ) ( -120 97 24 ) ( -120 96 25 ) Manual/Glass_001 [ 0 -1 0 0 ] [ 0 0 -1 0 ] 90 1 1
|
||||
( -128 96 24 ) ( -128 96 25 ) ( -127 96 24 ) Manual/Glass_001 [ 1 0 0 0 ] [ 0 0 -1 0 ] 180 1 1
|
||||
( -128 96 24 ) ( -127 96 24 ) ( -128 97 24 ) Manual/Glass_001 [ -1 0 0 0 ] [ 0 -1 0 0 ] 180 1 1
|
||||
( -96 104 48 ) ( -96 105 48 ) ( -95 104 48 ) Manual/Glass_001 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
|
||||
( -96 104 32 ) ( -95 104 32 ) ( -96 104 33 ) Manual/Glass_001 [ -1 0 0 0 ] [ 0 0 -1 0 ] 180 1 1
|
||||
( -96 104 32 ) ( -96 104 33 ) ( -96 105 32 ) Manual/Glass_001 [ 0 1 0 0 ] [ 0 0 -1 0 ] 180 1 1
|
||||
}
|
||||
}
|
||||
// entity 57
|
||||
{
|
||||
"classname" "actor_emitter_wall"
|
||||
"origin" "-120 18 24"
|
||||
"angles" "0 -90 0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAnimatableBody3D_002Ecs_002Fl_003AC_0021_003FUsers_003FMaddo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F0ee1eaca83dd77fd528cc635331ed45c3aa8291ced99dc12fb0c7b4393ef64_003FAnimatableBody3D_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AArray_002Ecs_002Fl_003AC_0021_003FUsers_003FMaddo_003FAppData_003FLocal_003FJetBrains_003FShared_003FvAny_003FDecompilerCache_003Fdecompiler_003F4cff0a608e114644b749bd11413a95c6583e00_003F73_003F8aac4f93_003FArray_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AArray_00601_002Ecs_002Fl_003AC_0021_003FUsers_003FMaddo_003FAppData_003FLocal_003FJetBrains_003FShared_003FvAny_003FDecompilerCache_003Fdecompiler_003F4fd22cd129a84c16b5d8004b467c426f518800_003F67_003F90d26b57_003FArray_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AArray_00601_002Ecs_002Fl_003AC_0021_003FUsers_003FMaddo_003FAppData_003FLocal_003FJetBrains_003FShared_003FvAny_003FDecompilerCache_003Fdecompiler_003F69e3acf0074a4ef8afaea275d4055b96522800_003Fa3_003Fb99d585e_003FArray_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
302
Scripts/Actors/3D/Laser.cs
Normal file
302
Scripts/Actors/3D/Laser.cs
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
using Godot;
|
||||
|
||||
namespace Cirno.Scripts.Actors._3D;
|
||||
|
||||
public partial class Laser : Area3D
|
||||
{
|
||||
public enum LaserState
|
||||
{
|
||||
Inactive,
|
||||
Warning,
|
||||
Expanding,
|
||||
Active,
|
||||
Finished
|
||||
}
|
||||
|
||||
// ================= CONFIG =================
|
||||
|
||||
[Export] public LaserConfig Config;
|
||||
|
||||
// ================= NODES =================
|
||||
|
||||
private MeshInstance3D _mesh;
|
||||
private CollisionShape3D _collision;
|
||||
private RayCast3D _ray;
|
||||
|
||||
private ShaderMaterial _beamMaterial;
|
||||
private float _currentRadius;
|
||||
|
||||
// ================= STATE =================
|
||||
|
||||
private LaserState _state = LaserState.Inactive;
|
||||
private float _stateTimer = 0f;
|
||||
|
||||
private Vector3 _origin;
|
||||
private Vector3 _direction;
|
||||
private float _currentLength;
|
||||
|
||||
// ================= SETUP =================
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_mesh = GetNode<MeshInstance3D>("MeshInstance3D");
|
||||
_collision = GetNode<CollisionShape3D>("CollisionShape3D");
|
||||
_ray = GetNode<RayCast3D>("RayCast3D");
|
||||
|
||||
_beamMaterial = _mesh.MaterialOverride as ShaderMaterial;
|
||||
|
||||
_ray.CollisionMask = Config.GeometryLayer;
|
||||
ChangeCollisionStateDeferred(true);
|
||||
//_collision.Disabled = true;
|
||||
}
|
||||
|
||||
private void ChangeCollisionStateDeferred(bool value)
|
||||
{
|
||||
_collision.SetDeferred(CollisionShape3D.PropertyName.Disabled, value);
|
||||
}
|
||||
|
||||
// ================= SPAWN API =================
|
||||
|
||||
public void SpawnFromDirection(Vector3 origin, Vector3 direction)
|
||||
{
|
||||
_origin = origin;
|
||||
_direction = direction.Normalized();
|
||||
StartLaser();
|
||||
}
|
||||
|
||||
public void SpawnFromTarget(Vector3 origin, Vector3 target)
|
||||
{
|
||||
_origin = origin;
|
||||
_direction = (target - origin).Normalized();
|
||||
StartLaser();
|
||||
}
|
||||
|
||||
private void StartLaser()
|
||||
{
|
||||
GlobalPosition = _origin;
|
||||
//Basis = Basis.LookingAt(_origin + _direction, Vector3.Up);
|
||||
|
||||
_mesh.Scale = Vector3.One;
|
||||
_collision.Scale = Vector3.One;
|
||||
|
||||
_stateTimer = 0f;
|
||||
_state = Config.WarningDuration > 0
|
||||
? LaserState.Warning
|
||||
: LaserState.Expanding;
|
||||
|
||||
//UpdateLaserLength(MaxLength);
|
||||
UpdateVisualOrientation();
|
||||
SetRadius(Config.WarningRadius);
|
||||
}
|
||||
|
||||
// ================= PROCESS =================
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
if (_state == LaserState.Finished)
|
||||
return;
|
||||
|
||||
_stateTimer += (float)delta;
|
||||
|
||||
UpdateRaycast();
|
||||
UpdateBeam();
|
||||
|
||||
switch (_state)
|
||||
{
|
||||
case LaserState.Warning:
|
||||
if (_stateTimer >= Config.WarningDuration)
|
||||
TransitionTo(LaserState.Expanding);
|
||||
break;
|
||||
|
||||
case LaserState.Expanding:
|
||||
HandleExpansion();
|
||||
break;
|
||||
|
||||
case LaserState.Active:
|
||||
if (Config.ActiveDuration >= 0 &&
|
||||
_stateTimer >= Config.ActiveDuration)
|
||||
TransitionTo(LaserState.Finished);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ================= STATE HANDLING =================
|
||||
|
||||
private void TransitionTo(LaserState next)
|
||||
{
|
||||
_state = next;
|
||||
_stateTimer = 0f;
|
||||
|
||||
switch (next)
|
||||
{
|
||||
case LaserState.Expanding:
|
||||
if (Config.ExpansionDuration <= 0f)
|
||||
{
|
||||
SetRadius(Config.DamageRadius);
|
||||
EnableCollision();
|
||||
TransitionTo(LaserState.Active);
|
||||
}
|
||||
break;
|
||||
|
||||
case LaserState.Active:
|
||||
EnableCollision();
|
||||
SetRadius(Config.DamageRadius);
|
||||
break;
|
||||
|
||||
case LaserState.Finished:
|
||||
QueueFree();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleExpansion()
|
||||
{
|
||||
if (_stateTimer < Config.ExpansionDelay)
|
||||
return;
|
||||
|
||||
var t = Mathf.Clamp(
|
||||
(_stateTimer - Config.ExpansionDelay) / Mathf.Max(Config.ExpansionDuration, 0.001f),
|
||||
0f, 1f
|
||||
);
|
||||
|
||||
var radius = Mathf.Lerp(Config.WarningRadius, Config.DamageRadius, t);
|
||||
SetRadius(radius);
|
||||
|
||||
if (t >= 1f)
|
||||
TransitionTo(LaserState.Active);
|
||||
}
|
||||
|
||||
// ================= RAYCAST =================
|
||||
|
||||
private void UpdateRaycast()
|
||||
{
|
||||
_ray.TargetPosition = _direction * Config.MaxLength;
|
||||
_ray.CollisionMask = Config.GeometryLayer; // only solid obstacles
|
||||
_ray.ForceRaycastUpdate();
|
||||
|
||||
_currentLength = _ray.IsColliding()
|
||||
? GlobalPosition.DistanceTo(_ray.GetCollisionPoint())
|
||||
: Config.MaxLength;
|
||||
}
|
||||
|
||||
// ================= VISUALS & COLLISION =================
|
||||
|
||||
private void UpdateVisualOrientation()
|
||||
{
|
||||
// Godot's LookingAt aligns the -Z axis to the target direction.
|
||||
// Our beam is built along +Y, so we must compensate.
|
||||
|
||||
Basis look = Basis.LookingAt(_direction, Vector3.Up);
|
||||
|
||||
// Rotate so +Y becomes forward instead of -Z
|
||||
// This is a fixed correction: -Z → +Y
|
||||
Basis correction = new Basis(Vector3.Right, Mathf.Pi / 2f);
|
||||
|
||||
Basis finalBasis = look * correction;
|
||||
|
||||
_mesh.Basis = finalBasis;
|
||||
_collision.Basis = finalBasis;
|
||||
}
|
||||
|
||||
private void UpdateBeam()
|
||||
{
|
||||
// 1. Set mesh height (NO SCALE)
|
||||
if (_mesh.Mesh is CylinderMesh cyl)
|
||||
cyl.Height = _currentLength;
|
||||
|
||||
// 2. Set collision height
|
||||
if (_collision.Shape is CapsuleShape3D capsule)
|
||||
capsule.Height = _currentLength;
|
||||
|
||||
// 3. Position at exact world-space center
|
||||
Vector3 center = _origin + _direction * (_currentLength * 0.5f);
|
||||
|
||||
Transform3D meshXform = _mesh.GlobalTransform;
|
||||
meshXform.Origin = center;
|
||||
_mesh.GlobalTransform = meshXform;
|
||||
|
||||
Transform3D colXform = _collision.GlobalTransform;
|
||||
colXform.Origin = center;
|
||||
_collision.GlobalTransform = colXform;
|
||||
}
|
||||
|
||||
|
||||
private Vector3 GetBeamCenterWorld()
|
||||
{
|
||||
return _origin + _direction * (_currentLength * 0.5f);
|
||||
}
|
||||
|
||||
private void SetRadius(float radius)
|
||||
{
|
||||
_currentRadius = radius;
|
||||
|
||||
if (_mesh.Mesh is CylinderMesh cyl)
|
||||
{
|
||||
cyl.TopRadius = radius;
|
||||
cyl.BottomRadius = radius;
|
||||
}
|
||||
|
||||
if (_collision.Shape is CapsuleShape3D capsule)
|
||||
capsule.Radius = radius;
|
||||
|
||||
UpdateMaterial();
|
||||
}
|
||||
|
||||
private void EnableCollision()
|
||||
{
|
||||
ChangeCollisionStateDeferred(false);
|
||||
//_collision.Disabled = false;
|
||||
}
|
||||
|
||||
// ======================================================
|
||||
// POINT-INSIDE-LASER QUERY (NO PHYSICS REQUIRED)
|
||||
// ======================================================
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given point lies within the
|
||||
/// current laser segment and its damage radius.
|
||||
/// </summary>
|
||||
public bool IsPointInsideBeam(Vector3 worldPoint)
|
||||
{
|
||||
if (_state != LaserState.Active)
|
||||
return false;
|
||||
|
||||
Vector3 local = worldPoint - _origin;
|
||||
|
||||
float projection = local.Dot(_direction);
|
||||
|
||||
if (projection < 0 || projection > _currentLength)
|
||||
return false;
|
||||
|
||||
Vector3 closestPoint =
|
||||
_origin + _direction * projection;
|
||||
|
||||
float distanceSq =
|
||||
worldPoint.DistanceSquaredTo(closestPoint);
|
||||
|
||||
float radius = Config.DamageRadius;
|
||||
return distanceSq <= radius * radius;
|
||||
}
|
||||
|
||||
private void UpdateMaterial()
|
||||
{
|
||||
if (_beamMaterial == null)
|
||||
return;
|
||||
|
||||
_beamMaterial.SetShaderParameter("beam_length", _currentLength);
|
||||
_beamMaterial.SetShaderParameter("beam_radius", _currentRadius);
|
||||
|
||||
switch (_state)
|
||||
{
|
||||
case LaserState.Warning:
|
||||
_beamMaterial.SetShaderParameter("beam_color", new Color(1f, 1f, 0.2f));
|
||||
_beamMaterial.SetShaderParameter("intensity", 0.5f);
|
||||
break;
|
||||
|
||||
case LaserState.Active:
|
||||
_beamMaterial.SetShaderParameter("beam_color", new Color(1f, 0.2f, 0.2f));
|
||||
_beamMaterial.SetShaderParameter("intensity", 1.0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
1
Scripts/Actors/3D/Laser.cs.uid
Normal file
1
Scripts/Actors/3D/Laser.cs.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://drhprlnl1e44m
|
||||
25
Scripts/Actors/3D/LaserConfig.cs
Normal file
25
Scripts/Actors/3D/LaserConfig.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
using Godot;
|
||||
|
||||
namespace Cirno.Scripts.Actors._3D;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class LaserConfig : Resource
|
||||
{
|
||||
[Export] public float MaxLength = 50f;
|
||||
|
||||
[Export] public float WarningRadius = 0.05f;
|
||||
[Export] public float DamageRadius = 0.3f;
|
||||
|
||||
[Export] public float WarningDuration = 0.5f;
|
||||
[Export] public float ExpansionDelay = 0.0f;
|
||||
[Export] public float ExpansionDuration = 0.2f;
|
||||
|
||||
// If < 0 → infinite
|
||||
[Export] public float ActiveDuration = 1.0f;
|
||||
|
||||
[Export(PropertyHint.Layers3DPhysics)]
|
||||
public uint GeometryLayer = 1 << 0;
|
||||
|
||||
[Export(PropertyHint.Layers3DPhysics)]
|
||||
public uint Damagelayer = 2 << 0;
|
||||
}
|
||||
1
Scripts/Actors/3D/LaserConfig.cs.uid
Normal file
1
Scripts/Actors/3D/LaserConfig.cs.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://5utr3255hbln
|
||||
24
Scripts/Actors/3D/LaserStarterTest3D.cs
Normal file
24
Scripts/Actors/3D/LaserStarterTest3D.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
using Godot;
|
||||
|
||||
namespace Cirno.Scripts.Actors._3D;
|
||||
|
||||
public partial class LaserStarterTest3D : Node3D, IActivable
|
||||
{
|
||||
[Export]
|
||||
private Laser _laser;
|
||||
|
||||
public bool Activate(ActivationType activationType = ActivationType.Toggle)
|
||||
{
|
||||
GD.Print("Activating laser");
|
||||
_laser.SpawnFromDirection(_laser.GlobalPosition, Vector3.Forward);
|
||||
|
||||
_laser.SpawnFromTarget(_laser.GlobalPosition, _laser.GlobalPosition + new Vector3(20, 0, 0));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Toggle()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
1
Scripts/Actors/3D/LaserStarterTest3D.cs.uid
Normal file
1
Scripts/Actors/3D/LaserStarterTest3D.cs.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://uaponrre7ew6
|
||||
23
Shaders/minimal_laser.gdshader
Normal file
23
Shaders/minimal_laser.gdshader
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
shader_type spatial;
|
||||
render_mode unshaded, blend_add, cull_disabled, depth_draw_opaque;
|
||||
|
||||
uniform vec3 beam_color : source_color = vec3(1.0, 0.2, 0.2);
|
||||
uniform float glow_radius = 0.2; // fraction of cylinder radius
|
||||
uniform float noise_speed = 1.0;
|
||||
uniform float flicker_strength = 0.2;
|
||||
|
||||
void fragment() {
|
||||
// Compute distance from center axis
|
||||
// Cylinder UV: Y goes along height, X around circumference
|
||||
float d = length(UV - vec2(0.5, 0.0)); // UV.x = around circumference, UV.y along height
|
||||
|
||||
// Glow: smooth edge
|
||||
float alpha = smoothstep(0.5, 0.5 - glow_radius, d);
|
||||
|
||||
// Optional flicker along the beam (animated by Y)
|
||||
float flicker = sin(UV.y * 20.0 + TIME * noise_speed) * flicker_strength;
|
||||
alpha = clamp(alpha + flicker, 0.0, 1.0);
|
||||
|
||||
ALBEDO = beam_color;
|
||||
ALPHA = alpha;
|
||||
}
|
||||
1
Shaders/minimal_laser.gdshader.uid
Normal file
1
Shaders/minimal_laser.gdshader.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://cd8n7vsgxkfib
|
||||
Loading…
Add table
Add a link
Reference in a new issue