using System.Drawing.Drawing2D; namespace TempleRun; /// /// 全局常量与可调参数。想改手感直接改这里。 /// 世界坐标约定:X 向右为正,Y 离地向上为正,Z 玩家前方为正。 /// 玩家永远停在 Z = 0,世界向 Z 负方向滚动。 /// public static class Cfg { // ---- 相机(透视投影参数)---- public const float Focal = 520f; // 焦距:越大视野越窄、远处物体越大 public const float CamZ = 235f; // 相机位于玩家后方多远 public const float CamHeight = 142f; // 相机离地高度 public const float FarZ = 3000f; // 渲染最远距离 public const float MinDepth = 45f; // 相机空间最小深度,防止除零 // ---- 跑道 ---- public const int LaneCount = 3; public const float LaneWidth = 112f; // 单条车道宽 public const float RoadHalfWidth = LaneWidth * 1.5f; // ---- 玩家 ---- public const float PlayerHeight = 62f; // 站立高度 public const float SlideHeight = 30f; // 滑铲高度 public const float PlayerHalfDepth = 22f; public const float JumpSpeed = 430f; public const float Gravity = 950f; public const float LaneLerp = 13f; // 换道插值速度 public const float SlideDuration = 0.55f; // ---- 速度与难度 ---- public const float StartSpeed = 620f; public const float MaxSpeed = 1900f; public const float SpeedAccel = 3.4f; // 每秒自然加速 public const float SpeedPerCoin = 5f; // 每金币额外加速 public const float SpawnIntervalMin = 0.62f; // 相邻障碍组最小时间间隔(秒) public const float SpawnIntervalMax = 1.15f; // ---- 颜色(丛林日落 + 古神庙)---- public static readonly Color SkyTop = Color.FromArgb(30, 34, 72); public static readonly Color SkyMid = Color.FromArgb(122, 78, 96); public static readonly Color SkyHorizon = Color.FromArgb(240, 168, 96); public static readonly Color Fog = Color.FromArgb(200, 152, 118); public static readonly Color GroundNear = Color.FromArgb(44, 36, 28); public static readonly Color GroundFar = Color.FromArgb(122, 100, 78); public static readonly Color RoadA = Color.FromArgb(178, 150, 110); public static readonly Color RoadB = Color.FromArgb(156, 130, 94); public static readonly Color Stone = Color.FromArgb(120, 112, 104); public static readonly Color Wood = Color.FromArgb(122, 82, 48); public static readonly Color Vine = Color.FromArgb(74, 116, 62); public static readonly Color Gold = Color.FromArgb(255, 202, 62); public static readonly Color GoldDark = Color.FromArgb(196, 140, 24); public static readonly Color Player = Color.FromArgb(226, 92, 62); public static float LaneX(int lane) => (lane - 1) * LaneWidth; public static Color Lerp(Color a, Color b, float t) { t = Math.Clamp(t, 0f, 1f); return Color.FromArgb( (int)(a.R + (b.R - a.R) * t), (int)(a.G + (b.G - a.G) * t), (int)(a.B + (b.B - a.B) * t)); } } public enum RunState { Ready, Playing, Paused, GameOver } public enum ObstacleKind { /// 矮障碍:必须跳跃 Low, /// 悬空横梁:必须滑铲 High, /// 整道石墙:必须换道 Full } /// 透视相机:把世界坐标投影到屏幕坐标。 public static class Cam { public static float CenterX; public static float HorizonY; public static int Width; public static int Height; public static void Setup(int w, int h) { Width = w; Height = h; CenterX = w * 0.5f; HorizonY = h * 0.30f; } /// /// 投影一个世界点。 /// scale 为"每世界单位对应多少像素",用于缩放实体自身尺寸。 /// public static void Project(float x, float y, float z, out float sx, out float sy, out float scale) { float depth = z + Cfg.CamZ; if (depth < Cfg.MinDepth) depth = Cfg.MinDepth; scale = Cfg.Focal / depth; sx = CenterX + x * scale; sy = HorizonY + (Cfg.CamHeight - y) * scale; } /// 只投影地面上的点(y = 0)。 public static void ProjectGround(float x, float z, out float sx, out float sy, out float scale) => Project(x, 0f, z, out sx, out sy, out scale); /// 距离雾化系数:0 = 完全清晰,1 = 完全融入雾色。 public static float FogAmount(float z) { float t = Math.Clamp(z / Cfg.FarZ, 0f, 1f); return t * t; // 平方衰减,近处保持通透 } } public class Obstacle { public float Z; public int Lane; public ObstacleKind Kind; public bool Scored; // 是否已计入"躲避成功" public float Width => Kind == ObstacleKind.Full ? 96f : 90f; public float Depth => Kind == ObstacleKind.Full ? 54f : 56f; public float BottomY => Kind == ObstacleKind.High ? 40f : 0f; public float TopY => Kind switch { ObstacleKind.Low => 46f, // 低于跳跃高度,跳过去 ObstacleKind.High => 98f, // 悬空横梁,滑铲钻过去 _ => 108f // 整道石墙,只能换道 }; public float CenterX => Cfg.LaneX(Lane); } public class Coin { public float Z; public int Lane; public float Y = 36f; public bool Taken; public float Phase; public float CenterX => Cfg.LaneX(Lane); } public class Particle { public float X, Y, Z; public float Vx, Vy, Vz; public float Life; public float MaxLife; public float Size; public float GravityScale = 1f; public Color Color; } /// 路边装饰物:石柱、火把、树、雕像碎块。 public class SideProp { public float Z; public int Side; // -1 左,+1 右 public int Type; // 0=石柱 1=火把 2=枯树 3=碎石 public float Height; public float Phase; public float CenterX => Side * (Cfg.RoadHalfWidth + 205f + (Type == 3 ? 40f : 0f)); } public class Player { public float X; // 当前横向世界坐标(插值中) public int TargetLane = 1; // 目标车道 0/1/2 public float Y; // 离地高度 public float Vy; public bool OnGround = true; public bool Sliding; public float SlideTimer; public float RunPhase; // 跑动动画相位 public bool Alive = true; public float Tilt; // 换道时的侧倾(视觉) public float Height => Sliding ? Cfg.SlideHeight : Cfg.PlayerHeight; public void Reset() { TargetLane = 1; X = Cfg.LaneX(1); Y = 0; Vy = 0; OnGround = true; Sliding = false; SlideTimer = 0; RunPhase = 0; Alive = true; Tilt = 0; } }