Was dissapointed there was no lava lamp models in the asset cloud, let alone a functioning one. So here ya go. just make a component with the follow code, slap it on a blob, and assign colors. thanks for trying!
using Sandbox;
using System;
public sealed class LavaBlob : Component
{
[Header( "Vertical Movement" )]
[Property] public float MinHeight { get; set; } = 0f;
[Property] public float MaxHeight { get; set; } = 20f;
[Property] public float Speed { get; set; } = 2.0f;
[Header( "Horizontal Drift" )]
[Property] public float HorizontalLimit { get; set; } = 1.0f; // How far sideways it can go
[Property] public float HorizontalSpeed { get; set; } = 0.2f; // Keep this low for lazy drifting
[Header( "The Squish" )]
[Property, Range( 0f, 0.9f )] public float SquishAmount { get; set; } = 0.4f;
[Property] public float SquishRate { get; set; } = 8.0f;
[Property] public float SquishZone { get; set; } = 4.0f;
private Vector3 _homeBase;
private Vector3 _startScale;
private float _currentHeight;
private bool _movingUp = true;
private float _timeOffset;
protected override void OnStart()
{
_homeBase = LocalPosition;
_startScale = LocalScale;
_currentHeight = MinHeight;
_movingUp = true;
_timeOffset = Game.Random.Float( 0f, 100f );
}
protected override void OnUpdate()
{
float t = Time.Now + _timeOffset;
// --- 1. VERTICAL MOVEMENT ---
if ( _movingUp )
{
_currentHeight += Speed * Time.Delta;
if ( _currentHeight >= MaxHeight )
{
_currentHeight = MaxHeight;
_movingUp = false;
}
}
else
{
_currentHeight -= Speed * Time.Delta;
if ( _currentHeight <= MinHeight )
{
_currentHeight = MinHeight;
_movingUp = true;
}
}
// --- 2. GENTLE HORIZONTAL DRIFT ---
// We use Cosine and Sine at slightly different speeds to create a natural, non-repeating oval wander
float driftX = MathF.Sin( t * HorizontalSpeed ) * HorizontalLimit;
float driftY = MathF.Cos( t * HorizontalSpeed * 0.8f ) * HorizontalLimit;
// --- 3. SQUISH MATH ---
float distToTop = MathF.Abs( MaxHeight - _currentHeight );
float distToBot = MathF.Abs( _currentHeight - MinHeight );
float closestDist = MathF.Min( distToTop, distToBot );
float proximityMultiplier = 0f;
if ( closestDist < SquishZone )
{
proximityMultiplier = 1f - (closestDist / SquishZone);
}
float pulse = MathF.Abs( MathF.Sin( t * SquishRate ) );
float squishFactor = pulse * SquishAmount * proximityMultiplier;
squishFactor = squishFactor.Clamp( 0f, 0.9f );
// Apply scale
LocalScale = new Vector3(
_startScale.x * (1f + (squishFactor * 0.5f)),
_startScale.y * (1f + (squishFactor * 0.5f)),
_startScale.z * (1f - squishFactor)
);
// Apply position (Home Base + Horizontal Drift + Vertical Height)
LocalPosition = new Vector3(
_homeBase.x + driftX,
_homeBase.y + driftY,
_homeBase.z + _currentHeight
);
}
}