mcpField
C
curves-and-paths

curves-and-paths

github.com/phaserjs开发40.6k771 次安装10 周前更新
代码仓库

关于 curves-and-paths

面向 Phaser 4 开发者的路径技能,可用曲线组合路径、采样和绘制路径,并通过 PathFollower 驱动精灵沿路径移动。适用于实现弯曲路线与路径动画。

主要功能

  1. 01用直线、样条、贝塞尔和椭圆曲线构建路径
  2. 02按参数、弧长或距离获取路径点
  3. 03使用 Graphics 绘制路径
  4. 04通过 PathFollower 沿路径移动精灵
  5. 05控制跟随时长、偏移和旋转

适用场景

  1. 01为游戏对象创建弯曲路线
  2. 02绘制路径以用于可视化或游戏画面
  3. 03让精灵按时长沿路线移动并旋转

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/phaserjs/phaser --skill curves-and-paths

安装后在 Claude Code 中输入/curves-and-paths确认已加载。

详细介绍

曲线与路径

使用曲线创建路径、获取路径上的点、用 Graphics 绘制路径,以及在 Phaser 4 中使用 PathFollower 让精灵自动沿路径移动。

关键源路径: src/curves/、src/curves/path/、src/gameobjects/pathfollower/、src/gameobjects/components/PathFollower.js 相关技能: ../sprites-and-images/SKILL.md、../graphics-and-shapes/SKILL.md、../tweens/SKILL.md

快速开始

// In a Scene's create() method:

// 1. Create a Path starting at (50, 300)
const path = this.add.path(50, 300);

// 2. Add curves to the path
path.lineTo(200, 100);
path.splineTo([ new Phaser.Math.Vector2(300, 400), new Phaser.Math.Vector2(500, 200) ]);
path.lineTo(700, 300);

// 3. Draw the path using Graphics
const graphics = this.add.graphics();
graphics.lineStyle(2, 0xffffff, 1);
path.draw(graphics, 64);

// 4. Create a PathFollower sprite that moves along the path
const follower = this.add.follower(path, 50, 300, 'ship');
follower.startFollow({
    duration: 5000,
    rotateToPath: true,
    repeat: -1,
    yoyo: true
});

核心概念

Path

Phaser.Curves.Path 是一个容器,它将多条曲线组合成一条连续的复合曲线。Path 中的曲线不必首尾相连。只有曲线的顺序会影响沿路径的点计算。

通过工厂方法创建:this.add.path(x, y),其中 x/y 是起点。

关键属性:

  • curves -- Path 中 Phaser.Curves.Curve 对象的数组
  • startPoint -- Vector2,定义的起始位置
  • autoClose -- 布尔值,如果为 true,getPoints() 会在末尾追加第一个点
  • defaultDivisions -- 数字(默认值:12),调用 getPoints() 时每条曲线的分段数
  • name -- 字符串,默认为空,供开发者使用
曲线

所有曲线类型都继承自 Phaser.Curves.Curve(基类)。每条曲线都支持:

  • getPoint(t, out) -- 根据曲线参数化,获取位置 t(0-1)处的点
  • getPointAt(u, out) -- 根据弧长(均匀间隔),获取位置 u(0-1)处的点
  • getPoints(divisions, stepRate, out) -- 沿曲线的点数组
  • getSpacedPoints(divisions, stepRate, out) -- 按弧长等距的点数组
  • getDistancePoints(distance) -- 按像素距离间隔的点
  • getLength() -- 总弧长(像素)
  • getBounds(out, accuracy) -- 边界 Rectangle
  • getTangent(t, out) / getTangentAt(u, out) -- 单位切向量
  • getStartPoint(out) / getEndPoint(out) -- 第一个/最后一个点
  • getRandomPoint(out) -- 曲线上的随机点
  • draw(graphics, pointsTotal) -- 将曲线渲染到 Graphics 对象上
  • active -- 布尔值,为 false 时父 Path 会跳过这条曲线
PathFollower

Phaser.GameObjects.PathFollower 是带有 Components.PathFollower mixin 的 Sprite。它使用内部 Tween(一个从 0 到 1 的数字计数器)每帧沿 Path 前进。

通过工厂方法创建:this.add.follower(path, x, y, texture, frame)

PathFollower 组件提供:

  • path -- 正在跟随的 Phaser.Curves.Path
  • pathTween -- 驱动移动的内部 Tween
  • pathOffset -- Vector2,添加到路径坐标上的偏移量
  • pathVector -- Vector2,路径上的当前位置
  • pathDelta -- Vector2,自上一帧以来移动的距离
  • rotateToPath -- 布尔值,自动旋转以面向路径方向
  • pathRotationOffset -- 数字(度),添加到自动旋转上

常见模式

使用链式曲线创建路径

Path 提供了便捷方法,可从上一条曲线的终点开始创建曲线:

const path = this.add.path(100, 500);

path.lineTo(300, 100);                          // straight line
path.cubicBezierTo(500, 100, 350, 50, 450, 50); // cubic bezier (endX, endY, cp1X, cp1Y, cp2X, cp2Y)
path.quadraticBezierTo(700, 400, 600, 100);     // quadratic bezier (endX, endY, cpX, cpY)
path.splineTo([                                  // spline through points
    new Phaser.Math.Vector2(750, 300),
    new Phaser.Math.Vector2(600, 500)
]);
path.ellipseTo(50, 80, 0, 270, false, 0);       // ellipse arc (xRadius, yRadius, startAngle, endAngle, clockwise, rotation)
path.circleTo(40);                               // shortcut for ellipseTo with equal radii and 0-360

// Jump to a new position without drawing (creates a gap)
path.moveTo(400, 400);
path.lineTo(500, 400);

// Close the path by connecting end to start
path.closePath();
添加独立曲线对象
const path = new Phaser.Curves.Path(0, 0);

// Add pre-constructed curve objects
const line = new Phaser.Curves.Line(new Phaser.Math.Vector2(0, 0), new Phaser.Math.Vector2(200, 200));
path.add(line);

const spline = new Phaser.Curves.Spline([ 200, 200, 300, 100, 400, 300 ]);
path.add(spline);

const ellipse = new Phaser.Curves.Ellipse(400, 300, 100, 60, 0, 360, false, 0);
path.add(ellipse);
获取沿路径的点
// Array of points (uses defaultDivisions per curve)
const points = path.getPoints();

// With explicit divisions per curve
const detailed = path.getPoints(32);

// Equally spaced points along the entire path
const spaced = path.getSpacedPoints(100);

// Single point at normalized position (0-1)
const midpoint = path.getPoint(0.5);

// Tangent vector at a position
const tangent = path.getTangent(0.5);

// Total path length in pixels
const length = path.getLength();

// Bounding rectangle
const bounds = path.getBounds();
使用 Graphics 绘制路径
const graphics = this.add.graphics();

// Draw entire path
graphics.lineStyle(2, 0x00ff00, 1);
path.draw(graphics, 64); // 64 = points per curve for smoothness

// Draw individual curves
graphics.lineStyle(1, 0xff0000, 1);
path.curves[0].draw(graphics, 32);

// Draw debug points
const points = path.getSpacedPoints(50);
points.forEach(p => {
    graphics.fillStyle(0xffff00, 1);
    graphics.fillCircle(p.x, p.y, 3);
});
PathFollower 精灵
const path = this.add.path(100, 200);
path.lineTo(400, 400);
path.lineTo(700, 200);

// Create follower
const enemy = this.add.follower(path, 100, 200, 'enemy');

// Start following with config
enemy.startFollow({
    duration: 3000,       // ms to traverse path
    positionOnPath: true, // snap to path start position
    rotateToPath: true,   // auto-rotate to face direction
    rotationOffset: 90,   // offset added to auto-rotation (degrees)
    repeat: -1,           // -1 = infinite repeat
    yoyo: true,           // reverse on each repeat
    from: 0,              // start position on path (0-1)
    to: 1,                // end position on path (0-1)
    startAt: 0,           // initial seek position
    ease: 'Sine.easeInOut' // any valid Phaser ease
});

// Control during playback
enemy.pauseFollow();
enemy.resumeFollow();
enemy.stopFollow();
enemy.isFollowing(); // returns boolean

// Change path at runtime
enemy.setPath(newPath);
enemy.setPath(newPath, { duration: 2000 }); // auto-starts

// Set rotation independently
enemy.setRotateToPath(true, 90); // (value, offsetDegrees)
使用简单持续时间的 PathFollower
// Shorthand: pass just a duration number
enemy.startFollow(5000);

// Equivalent to:
enemy.startFollow({ duration: 5000 });

所有曲线类型

曲线类构造函数参数描述
LinePhaser.Curves.Line(p0, p1) Vector2 端点,或 ([x0,y0,x1,y1])两点之间的直线段
SplinePhaser.Curves.Spline(points) Vector2 数组、扁平数字或嵌套数组穿过控制点的 Catmull-Rom 样条曲线
CubicBezierPhaser.Curves.CubicBezier(p0, p1, p2, p3) 或 ([x0,y0,...x3,y3])具有起点、2 个控制点、终点的三次贝塞尔曲线
QuadraticBezierPhaser.Curves.QuadraticBezier(p0, p1, p2) 或 ([x0,y0,...x2,y2])具有起点、1 个控制点、终点的二次贝塞尔曲线
EllipsePhaser.Curves.Ellipse(x, y, xRadius, yRadius, startAngle, endAngle, clockwise, rotation) 或配置对象椭圆弧;角度以度为单位;yRadius 默认为 xRadius
椭圆曲线属性

椭圆曲线具有用于运行时修改的 get/set 属性:

  • x、y -- 中心位置
  • xRadius、yRadius -- 半径
  • startAngle、endAngle -- 以度为单位(get/set 在内部转换为弧度/从弧度转换)
  • clockwise -- 布尔值
  • rotation -- 以弧度为单位
  • angle -- 以度为单位旋转(rotation 的替代)
  • setWidth(value) / setHeight(value) -- 将半径设置为 value/2

API 快速参考

Path (Phaser.Curves.Path)
API类型描述
add(curve)方法将任意 Curve 追加到路径
lineTo(x, y)方法从当前终点添加 Line
splineTo(points)方法从当前终点添加 Spline
cubicBezierTo(x, y, cp1X, cp1Y, cp2X, cp2Y)方法从当前终点添加 CubicBezier
quadraticBezierTo(x, y, cpX, cpY)方法从当前终点添加 QuadraticBezier
ellipseTo(xR, yR, start, end, cw, rot)方法从当前终点添加 Ellipse 弧
circleTo(radius, clockwise, rotation)方法等半径 ellipseTo 的快捷方式
moveTo(x, y)方法移动终点而不绘制(创建间隙)
closePath()方法如果尚未闭合,从终点到起点添加 Line
getPoint(t, out)方法整个路径上归一化位置(0-1)处的点
getPoints(divisions, stepRate)方法点数组,每条曲线的 divisions
getSpacedPoints(divisions)方法沿整个路径的等距点
getRandomPoint(out)方法路径上任意位置的随机点
getStartPoint(out)方法路径起点
getEndPoint(out)方法路径终点
getTangent(t, out)方法位置 t 处的单位切向量
getCurveAt(t)方法返回归一化位置 t 处的 Curve
getLength()方法路径总长度(像素)
getCurveLengths()方法累积曲线长度数组
getBounds(out, accuracy)方法边界矩形
draw(graphics, pointsTotal)方法将所有曲线绘制到 Graphics 对象上
toJSON() / fromJSON(data)方法序列化
updateArcLengths()方法强制重新计算缓存的长度
destroy()方法清除内部引用
基础曲线(Phaser.Curves.Curve)
API类型描述
getPoint(t, out)方法参数 t(0-1)处的点——抽象方法,由各子类实现
getPointAt(u, out)方法弧长位置 u(0-1)处的点——均匀分布
getPoints(divisions, stepRate, out)方法点数组
getSpacedPoints(divisions, stepRate, out)方法按弧长等距分布的点
getDistancePoints(distance)方法按像素距离间隔分布的点
getLength()方法曲线总弧长
getTangent(t, out) / getTangentAt(u, out)方法单位切向量
getTFromDistance(distance)方法将像素距离转换为 t 值
draw(graphics, pointsTotal)方法渲染到 Graphics 上(默认 32 个点)
getBounds(out, accuracy)方法边界矩形
activeboolean为 false 时,父 Path 会跳过此曲线
defaultDivisionsnumber独立曲线的默认值为 5
arcLengthDivisionsnumber弧长计算的精度(默认 100)
PathFollower 组件
API类型描述
setPath(path, config)方法设置新的 Path(可选自动开始)
startFollow(config, startAt)方法开始跟随;config = 持续时间数值或 PathConfig
pauseFollow()方法暂停移动
resumeFollow()方法恢复暂停的移动
stopFollow()方法停止跟随
isFollowing()方法若正在路径上主动移动则返回 true
setRotateToPath(value, offset)方法启用/禁用带偏移的自动旋转
pathPath当前路径引用
pathTweenTween驱动移动的内部补间
pathOffsetVector2相对于路径坐标的偏移
pathVectorVector2路径上的当前位置
pathDeltaVector2自上次更新以来的移动增量
rotateToPathboolean自动旋转到路径方向
pathRotationOffsetnumber旋转偏移(以度为单位)
PathConfig(Phaser.Types.GameObjects.PathFollower.PathConfig)
属性类型默认值描述
durationnumber1000遍历路径所需的时间(毫秒)
fromnumber0路径上的起始位置(0-1)
tonumber1路径上的结束位置(0-1)
positionOnPathbooleanfalse开始时将跟随者对齐到路径起点
rotateToPathbooleanfalse自动旋转以面向路径方向
rotationOffsetnumber0添加到自动旋转上的度数
startAtnumber0路径上的初始寻道位置(0-1)

该配置还接受所有标准 Tween 属性:ease、repeat、yoyo、delay、hold、onComplete 等。

注意事项

  1. 曲线上的 getPoint(t) 与 getPointAt(u)。 getPoint 使用原始曲线参数 t,在大多数曲线类型上不会产生均匀分布的点。getPointAt 通过弧长映射来实现均匀间距。在 Path 上,getPoint 已经考虑了整条路径上的弧长。

  2. Path 的 moveTo 会创建一条非活动曲线。 MoveTo 伪曲线的 active: false 且长度为零。它只为下一条曲线重新定位终点。它不绘制任何内容,并且会被 getPoints() 和 draw() 跳过。

  3. PathFollower 内部使用 Tween。 startFollow 配置会传给 scene.tweens.addCounter()。所有 tween 属性(ease、delay、repeat、yoyo、callbacks)都有效。该 tween 会自动设置为 persist: true。

  4. PathFollower 的 offset 行为。 当 positionOnPath: false(默认值)时,follower 的当前位置会成为相对于路径起点的偏移量。当 positionOnPath: true 时,follower 会吸附到路径的起点,且偏移量被清零。

  5. Ellipse 的角度以度为单位。 构造函数以及 startAngle/endAngle 属性接受度数。内部以弧度存储。rotation 属性以弧度为单位,但 angle 以度为单位。

  6. closePath 与 autoClose。 closePath() 会添加一条从终点到起点的显式 Line 曲线。autoClose = true 仅通过追加第一个点来影响 getPoints() 和 getSpacedPoints() 的输出,而不会添加曲线。

  7. 缓存长度可能会过期。 getCurveLengths() 仅根据数组长度缓存结果。如果修改了曲线的控制点,请调用 path.updateArcLengths() 强制重新计算。

  8. 使用数字时 cubicBezierTo 的参数顺序。 传入数字时:cubicBezierTo(endX, endY, cp1X, cp1Y, cp2X, cp2Y)。终点在前,而不是控制点。传入 Vector2 对象时:cubicBezierTo(cp1, cp2, endPoint)。

  9. Spline 至少需要 4 个点。 Spline 使用的 Catmull-Rom 插值在 4 个点以上时效果最佳。点数更少时,曲线的行为可能不符合预期。

  10. Line 曲线的 arcLengthDivisions 为 1。 与其他曲线(默认为 100)不同,Line 将其覆盖为 1,因为直线本身就是均匀的。无需调整它。

源文件映射

文件用途
src/curves/path/Path.jsPath 类——组合多条曲线,工厂注册为 this.add.path
src/curves/path/MoveTo.js用于在路径中创建间隙的 MoveTo 伪曲线
src/curves/Curve.js基础 Curve 类——所有曲线类型共享的方法
src/curves/LineCurve.jsLine 曲线(两点线段)
src/curves/SplineCurve.jsSpline 曲线(穿过多个点的 Catmull-Rom)
src/curves/CubicBezierCurve.js三次贝塞尔曲线(4 个控制点)
src/curves/QuadraticBezierCurve.js二次贝塞尔曲线(3 个控制点)
src/curves/EllipseCurve.js支持角度和旋转的椭圆/弧线曲线
src/gameobjects/pathfollower/PathFollower.jsPathFollower 游戏对象(继承 Sprite + PathFollower mixin)
src/gameobjects/pathfollower/PathFollowerFactory.jsthis.add.follower 工厂注册
src/gameobjects/components/PathFollower.jsPathFollower 组件 mixin(setPath、startFollow、pathUpdate 等)
src/gameobjects/pathfollower/typedefs/PathConfig.jsPathConfig 类型定义

评论

支持 Markdown