Jump/JumpPlatform.cs
using System;
/// <summary>
/// 跳一跳平台:纯视觉(模型 + 出生弹出动画),碰撞判定由 JumpGame 用数学矩形完成。
/// </summary>
public sealed class JumpPlatform : Component
{
public float CenterX { get; private set; }
public float CenterY { get; private set; }
public float TopZ { get; private set; }
public float HalfX { get; private set; }
public float HalfY { get; private set; }
/// <summary> 是否为当前目标平台(中心标记会呼吸脉冲提示落点) </summary>
public bool IsTarget { get; set; }
public Vector3 Center => new Vector3( CenterX, CenterY, TopZ );
GameObject _marker;
Vector3 _baseScale;
Vector3 _baseMarkerScale;
TimeSince _born;
bool _spawned;
static readonly Model BoxModel = Model.Load( "models/dev/box.vmdl" );
public void Init( Vector2 center, float halfX, float halfY, float topZ, Color color )
{
CenterX = center.x;
CenterY = center.y;
TopZ = topZ;
HalfX = halfX;
HalfY = halfY;
var height = topZ;
var size = BoxModel.Bounds.Size;
GameObject.WorldPosition = new Vector3( CenterX, CenterY, height * 0.5f );
_baseScale = new Vector3( halfX * 2f / size.x, halfY * 2f / size.y, height / size.z );
GameObject.LocalScale = _baseScale;
var renderer = GameObject.AddComponent<ModelRenderer>();
renderer.Model = BoxModel;
renderer.Tint = color;
// 顶面中心的"完美落点"标记(薄板),跟随平台缩放弹出
_marker = new GameObject( true, "CenterMark" );
_marker.Parent = GameObject;
_marker.LocalPosition = new Vector3( 0f, 0f, ( height * 0.5f + 1.3f ) / _baseScale.z );
_baseMarkerScale = new Vector3( 30f / ( halfX * 2f ), 30f / ( halfY * 2f ), 2.6f / height );
_marker.LocalScale = _baseMarkerScale;
var markerRenderer = _marker.AddComponent<ModelRenderer>();
markerRenderer.Model = BoxModel;
markerRenderer.Tint = color * 0.55f;
_born = 0;
_spawned = true;
}
protected override void OnUpdate()
{
if ( !_spawned ) return;
// 出生动画:从压扁状态带一点回弹地长出来
var t = Math.Clamp( (float)_born / 0.3f, 0f, 1f );
if ( t >= 1f )
{
_spawned = false;
GameObject.LocalScale = _baseScale;
ApplyMarkerScale( 1f );
return;
}
const float c1 = 1.70158f;
const float c3 = c1 + 1f;
var f = 1f + c3 * MathF.Pow( t - 1f, 3f ) + c1 * MathF.Pow( t - 1f, 2f );
GameObject.LocalScale = _baseScale * f;
ApplyMarkerScale( f );
}
void ApplyMarkerScale( float factor )
{
if ( !_marker.IsValid() ) return;
// 目标平台的标记轻微呼吸,帮助识别落点
var pulse = IsTarget && !_spawned ? 1f + 0.14f * MathF.Sin( Time.Now * 5f ) : 1f;
_marker.LocalScale = _baseMarkerScale * factor * pulse;
}
/// <summary> 销毁平台本体和中心标记 </summary>
public void DestroyAll()
{
if ( _marker.IsValid() ) _marker.Destroy();
GameObject.Destroy();
}
}