You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
6.9 KiB
211 lines
6.9 KiB
/**
|
|
* MoveDashedLine.ts
|
|
* Author: David de ShineYoung
|
|
* Creation Date: 2025/10/27
|
|
* Description: 绘制带箭头指示的虚线移动路径
|
|
*
|
|
*/
|
|
import { _decorator, Component, Graphics, Vec3, Color, Vec2 } from 'cc';
|
|
const { ccclass, property } = _decorator;
|
|
|
|
const _arrowDir = new Vec3();
|
|
const _arrowCenter = new Vec3();
|
|
const _arrowTip = new Vec3();
|
|
const _arrowLeft = new Vec3();
|
|
const _arrowRight = new Vec3();
|
|
const _leftOffset = new Vec3();
|
|
const _rightOffset = new Vec3();
|
|
const _segStart = new Vec3();
|
|
const _segEnd = new Vec3();
|
|
|
|
@ccclass('MoveDashedLine')
|
|
export class MoveDashedLine extends Component {
|
|
|
|
@property(Graphics)
|
|
graphics: Graphics = null!;
|
|
|
|
@property([Vec2])
|
|
paths: Vec2[] = [];
|
|
|
|
@property
|
|
dashLength: number = 10; // 虚线每段长度
|
|
|
|
@property
|
|
gapLength: number = 8; // 虚线间隙长度
|
|
|
|
@property
|
|
lineWidth: number = 2; // 线宽
|
|
|
|
@property(Color)
|
|
lineColor: Color = Color.WHITE; // 线条颜色
|
|
|
|
@property
|
|
moveSpeed: number = 3; // 移动速度
|
|
|
|
@property
|
|
arrowCount: number = 3; // 箭头数量
|
|
|
|
@property
|
|
arrowSize: number = 20; // 箭头大小
|
|
|
|
@property
|
|
arrowSpacingFactor: number = 20; // 箭头间距因子,值越大间距越大
|
|
|
|
private _currentOffset: number = 0;
|
|
private _totalPathLength: number = 0;
|
|
private _pathSegments: { start: Vec3; end: Vec3; length: number }[] = [];
|
|
/** 引导线固定两点,setPathEndpoints 复用 */
|
|
private _pathA = new Vec2();
|
|
private _pathB = new Vec2();
|
|
|
|
onLoad() {
|
|
this.calculatePathData();
|
|
}
|
|
|
|
start() {
|
|
|
|
}
|
|
|
|
setLineColor(color: Color) {
|
|
this.lineColor = color;
|
|
}
|
|
|
|
startSchedule() {
|
|
this.schedule(this.updateLine); // 约60帧
|
|
}
|
|
|
|
private calculatePathData() {
|
|
this._pathSegments.length = 0;
|
|
this._totalPathLength = 0;
|
|
|
|
for (let i = 0; i < this.paths.length - 1; i++) {
|
|
const p0 = this.paths[i];
|
|
const p1 = this.paths[i + 1];
|
|
_segStart.set(p0.x, p0.y, 0);
|
|
_segEnd.set(p1.x, p1.y, 0);
|
|
const length = Vec3.distance(_segStart, _segEnd);
|
|
|
|
this._pathSegments.push({
|
|
start: _segStart.clone(),
|
|
end: _segEnd.clone(),
|
|
length,
|
|
});
|
|
|
|
this._totalPathLength += length;
|
|
}
|
|
}
|
|
|
|
private updateLine() {
|
|
this._currentOffset += this.moveSpeed;
|
|
// 当偏移超过路径长度时,循环
|
|
if (this._currentOffset > this._totalPathLength) {
|
|
this._currentOffset = 0;
|
|
}
|
|
this.drawArrows();
|
|
}
|
|
|
|
|
|
// 绘制箭头>
|
|
private drawArrows() {
|
|
if (!this.graphics || this._pathSegments.length === 0) return;
|
|
|
|
// 清除之前的绘制内容
|
|
this.graphics.clear();
|
|
|
|
this.graphics.lineWidth = this.lineWidth;
|
|
this.graphics.strokeColor = this.lineColor;
|
|
this.graphics.fillColor = this.lineColor;
|
|
// 使用arrowSpacingFactor作为箭头之间的最小间距(像素单位)
|
|
const minArrowSpacing = Math.max(this.arrowSpacingFactor, this.arrowSize * 0.8);
|
|
|
|
// 计算需要绘制的箭头数量
|
|
// 从起点开始绘制,每隔minArrowSpacing距离绘制一个箭头,直到终点
|
|
const totalArrows = Math.max(1, Math.floor(this._totalPathLength / minArrowSpacing));
|
|
|
|
// 计算实际的间距,确保箭头均匀分布在整个路径上
|
|
const actualSpacing = this._totalPathLength / totalArrows;
|
|
|
|
for (let i = 0; i <= totalArrows; i++) {
|
|
// 计算箭头位置:从起点到终点均匀分布
|
|
const arrowPosition = (this._currentOffset + actualSpacing * i) % this._totalPathLength;
|
|
this.drawArrowAtPosition(arrowPosition);
|
|
}
|
|
}
|
|
|
|
// 在指定位置绘制箭头>
|
|
private drawArrowAtPosition(position: number) {
|
|
if (!this.graphics) return;
|
|
|
|
let accumulatedLength = 0;
|
|
for (const segment of this._pathSegments) {
|
|
if (accumulatedLength + segment.length > position) {
|
|
const arrowSegmentPosition = position - accumulatedLength;
|
|
Vec3.subtract(_arrowDir, segment.end, segment.start);
|
|
Vec3.normalize(_arrowDir, _arrowDir);
|
|
|
|
Vec3.multiplyScalar(_arrowCenter, _arrowDir, arrowSegmentPosition);
|
|
Vec3.add(_arrowCenter, segment.start, _arrowCenter);
|
|
|
|
const perpX = -_arrowDir.y;
|
|
const perpY = _arrowDir.x;
|
|
|
|
const arrowSize = this.arrowSize;
|
|
const halfWidth = arrowSize * 0.3;
|
|
|
|
Vec3.multiplyScalar(_arrowTip, _arrowDir, arrowSize * 0.5);
|
|
Vec3.add(_arrowTip, _arrowCenter, _arrowTip);
|
|
|
|
Vec3.multiplyScalar(_arrowLeft, _arrowDir, -arrowSize * 0.3);
|
|
Vec3.add(_arrowLeft, _arrowCenter, _arrowLeft);
|
|
_leftOffset.set(perpX * halfWidth, perpY * halfWidth, 0);
|
|
Vec3.add(_arrowLeft, _arrowLeft, _leftOffset);
|
|
|
|
Vec3.multiplyScalar(_arrowRight, _arrowDir, -arrowSize * 0.3);
|
|
Vec3.add(_arrowRight, _arrowCenter, _arrowRight);
|
|
_rightOffset.set(perpX * -halfWidth, perpY * -halfWidth, 0);
|
|
Vec3.add(_arrowRight, _arrowRight, _rightOffset);
|
|
|
|
this.graphics.moveTo(_arrowTip.x, _arrowTip.y);
|
|
this.graphics.lineTo(_arrowLeft.x, _arrowLeft.y);
|
|
this.graphics.lineTo(_arrowRight.x, _arrowRight.y);
|
|
this.graphics.close();
|
|
this.graphics.fill();
|
|
|
|
break;
|
|
}
|
|
accumulatedLength += segment.length;
|
|
}
|
|
}
|
|
|
|
/** 更新两端点(复用内部 Vec2,避免引导每帧 new) */
|
|
public setPathEndpoints(x1: number, y1: number, x2: number, y2: number): void {
|
|
this._pathA.set(x1, y1);
|
|
this._pathB.set(x2, y2);
|
|
if (this.paths.length !== 2 || this.paths[0] !== this._pathA || this.paths[1] !== this._pathB) {
|
|
this.paths = [this._pathA, this._pathB];
|
|
}
|
|
this.calculatePathData();
|
|
this._currentOffset = 0;
|
|
this.arrowSpacingFactor = 50;
|
|
this.drawArrows();
|
|
}
|
|
|
|
// 设置新的路径点
|
|
public setPaths(newPaths: Vec2[]) {
|
|
this.paths = newPaths;
|
|
this.calculatePathData();
|
|
this._currentOffset = 0;
|
|
this.arrowSpacingFactor = 50;
|
|
this.drawArrows();
|
|
|
|
}
|
|
|
|
// 设置移动速度
|
|
public setMoveSpeed(speed: number) {
|
|
this.moveSpeed = speed;
|
|
}
|
|
|
|
onDestroy() {
|
|
this.unschedule(this.updateLine);
|
|
}
|
|
}
|
|
|