3.8.4在2D项目中怎么实现用一根线链接多个节点

  • Creator 版本:3.8.4

  • 目标平台: 网页、安卓原生、小游戏

在2D项目下,怎么可以实现用一根线连接两个或两个以上的节点呢?如图:
image

这个线还会根据节点的位置调整首尾和距离

image
image

另外,这种仿3D的效果是怎么实现的?两个线圈套住了两个桶,中间的链接线根据线圈的位置调整首尾和距离

QQ20260608-165410

就是个平铺的黄色图片吧,直接覆盖上去的。写个脚本控制就可以了。记录两个点的连接圆心,连接,然后圈内不渲染。就可以达到这个效果了吧

但节点不是只有一个坐标吗,没有首尾坐标的概念啊,如果要实现控制首尾和长度,我目前想到的是,读取需要链接的两个节点坐标,然后获取设置链接线的坐标为两点中间,然后设置长度和角度来实现首尾链接,但感觉好麻烦啊,而且圈内不渲染这个是怎么弄的?图片还能控制部分渲染吗?

用Graphics画线,然后给它加个渐变的shader
这个线的遮挡关系可以给桶加个Mask,遮挡的区域就是半个绳圈
不过这个桶的描边和绳子的描边,可能需要给camera加一个renderTexture后处理

写个shader,控制首尾空白的圆大小不就好了

看得我头都大了

我还只会一些简单的shader,还是在G老师的帮助下实现的 :sob:

2Dshader不难。AI都可以写,准确率还可以

https://github.com/LanceLemon/cocos_graphics_draw
我之前弄过要描边的线,你看看效果行不行,改一下shader就行了

好的,回头我研究研究

也只能多试试看了,目前也没其他的现成方案了

20260608173750_rec_

没什么难的,拉伸方向让美术裁掉半圆不就可以了。不用写shader,这个场景写shader还有点蛋疼

美术这样不就好了

这是怎么实现的?不用shader的话?

你说的哪个怎么实现的

这个

编写sz脚本。要求:这是个杆子组件,设置A、B两个节点。每帧判断AB两个节点是否有位移。移动的话杆子自动调节。调节要求:x方向是长,y方向是宽。计算AB两点距离。然后用杆子连接他们。连接方式按AB两点中心。 AI提示词 :sweat_smile:,手写也不难,主要不想写

import { _decorator, Component, Node, Vec3, UITransform, EventTouch, input, Input } from ‘cc’;
const { ccclass, property } = _decorator;

@ccclass(‘sz’)
export class sz extends Component {
@property({ type: Node, tooltip: “节点A” })
nodeA: Node = null;

@property({ type: Node, tooltip: "节点B" })
nodeB: Node = null;

@property({ tooltip: "杆子的宽度(Y方向)" })
rodWidth: number = 10;

// 记录上一帧的位置,用于判断是否移动
private lastPosA: Vec3 = new Vec3();
private lastPosB: Vec3 = new Vec3();

// 杆子的UITransform组件
private uiTransform: UITransform = null;

// 拖拽相关变量
private isDraggingA: boolean = false;
private isDraggingB: boolean = false;
private dragStartPos: Vec3 = new Vec3();
private nodeStartPos: Vec3 = new Vec3();

start() {
    // 获取杆子自身的UITransform组件
    this.uiTransform = this.node.getComponent(UITransform);
    if (!this.uiTransform) {
        console.error("杆子节点缺少UITransform组件,请添加UITransform组件");
        return;
    }

    // 初始化杆子宽度
    this.uiTransform.height = this.rodWidth;

    // 检查节点是否存在
    if (!this.nodeA || !this.nodeB) {
        console.error("请设置节点A和节点B");
        return;
    }

    // 初始化记录上一帧位置
    this.lastPosA.set(this.nodeA.position);
    this.lastPosB.set(this.nodeB.position);

    // 为A、B节点添加拖拽事件监听
    this.setupDragEvents();

    // 初始时计算一次杆子状态
    this.updateRod();
}

/**
 * 设置拖拽事件
 */
private setupDragEvents() {
    // 为节点A添加拖拽监听
    if (this.nodeA) {
        this.nodeA.on(Input.EventType.TOUCH_START, this.onTouchStartA, this);
        this.nodeA.on(Input.EventType.TOUCH_MOVE, this.onTouchMoveA, this);
        this.nodeA.on(Input.EventType.TOUCH_END, this.onTouchEndA, this);
        this.nodeA.on(Input.EventType.TOUCH_CANCEL, this.onTouchEndA, this);
    }

    // 为节点B添加拖拽监听
    if (this.nodeB) {
        this.nodeB.on(Input.EventType.TOUCH_START, this.onTouchStartB, this);
        this.nodeB.on(Input.EventType.TOUCH_MOVE, this.onTouchMoveB, this);
        this.nodeB.on(Input.EventType.TOUCH_END, this.onTouchEndB, this);
        this.nodeB.on(Input.EventType.TOUCH_CANCEL, this.onTouchEndB, this);
    }
}

/**
 * 节点A拖拽开始
 */
private onTouchStartA(event: EventTouch) {
    this.isDraggingA = true;
    this.dragStartPos.set(event.getUILocation().x, event.getUILocation().y, 0);
    this.nodeStartPos.set(this.nodeA.position);
    event.propagationStopped = true;
}

/**
 * 节点A拖拽移动
 */
private onTouchMoveA(event: EventTouch) {
    if (!this.isDraggingA) return;

    const currentTouchPos = event.getUILocation();
    const deltaX = currentTouchPos.x - this.dragStartPos.x;
    const deltaY = currentTouchPos.y - this.dragStartPos.y;

    // 计算新位置(考虑父节点的缩放和旋转)
    const newPos = new Vec3(
        this.nodeStartPos.x + deltaX,
        this.nodeStartPos.y + deltaY,
        this.nodeStartPos.z
    );

    this.nodeA.setPosition(newPos);
    event.propagationStopped = true;
}

/**
 * 节点A拖拽结束
 */
private onTouchEndA(event: EventTouch) {
    this.isDraggingA = false;
    event.propagationStopped = true;
}

/**
 * 节点B拖拽开始
 */
private onTouchStartB(event: EventTouch) {
    this.isDraggingB = true;
    this.dragStartPos.set(event.getUILocation().x, event.getUILocation().y, 0);
    this.nodeStartPos.set(this.nodeB.position);
    event.propagationStopped = true;
}

/**
 * 节点B拖拽移动
 */
private onTouchMoveB(event: EventTouch) {
    if (!this.isDraggingB) return;

    const currentTouchPos = event.getUILocation();
    const deltaX = currentTouchPos.x - this.dragStartPos.x;
    const deltaY = currentTouchPos.y - this.dragStartPos.y;

    // 计算新位置
    const newPos = new Vec3(
        this.nodeStartPos.x + deltaX,
        this.nodeStartPos.y + deltaY,
        this.nodeStartPos.z
    );

    this.nodeB.setPosition(newPos);
    event.propagationStopped = true;
}

/**
 * 节点B拖拽结束
 */
private onTouchEndB(event: EventTouch) {
    this.isDraggingB = false;
    event.propagationStopped = true;
}

update(deltaTime: number) {
    // 检查A、B节点是否存在
    if (!this.nodeA || !this.nodeB) {
        return;
    }

    // 获取当前位置
    const currentPosA = this.nodeA.position;
    const currentPosB = this.nodeB.position;

    // 判断是否有位移(位置发生变化)
    const isAMoved = !currentPosA.equals(this.lastPosA);
    const isBMoved = !currentPosB.equals(this.lastPosB);

    if (isAMoved || isBMoved) {
        // 更新记录的位置
        this.lastPosA.set(currentPosA);
        this.lastPosB.set(currentPosB);
        // 重新计算杆子状态
        this.updateRod();
    }
}

/**
 * 更新杆子的长度、宽度和位置
 */
private updateRod() {
    if (!this.uiTransform) return;

    // 获取A、B两点在父节点坐标系下的位置
    const parent = this.node.parent;
    const posA = parent ? parent.inverseTransformPoint(new Vec3(), this.nodeA.worldPosition) : this.nodeA.position;
    const posB = parent ? parent.inverseTransformPoint(new Vec3(), this.nodeB.worldPosition) : this.nodeB.position;

    // 计算AB两点之间的距离(作为杆子的长度)
    const dx = posB.x - posA.x;
    const dy = posB.y - posA.y;
    const distance = Math.hypot(dx, dy);

    // 如果两点重合,避免无效操作
    if (distance < 0.0001) {
        return;
    }

    // 计算杆子的旋转角度(弧度转角度)
    const angle = Math.atan2(dy, dx) * 180 / Math.PI;

    // 计算AB两点的中心坐标
    const centerX = (posA.x + posB.x) / 2;
    const centerY = (posA.y + posB.y) / 2;

    // 更新杆子的变换:
    // 设置杆子大小为 (长度, 宽度)
    this.uiTransform.setContentSize(distance, this.rodWidth);

    // 设置杆子位置为中心点
    this.node.setPosition(centerX, centerY, 0);

    // 设置杆子旋转角度
    this.node.setRotationFromEuler(0, 0, angle);
}

/**
 * 设置杆子宽度
 * @param width 宽度值
 */
public setRodWidth(width: number) {
    this.rodWidth = width;
    if (this.uiTransform) {
        this.uiTransform.height = width;
    }
    this.updateRod();
}

onDestroy() {
    // 清理事件监听
    if (this.nodeA) {
        this.nodeA.off(Input.EventType.TOUCH_START, this.onTouchStartA, this);
        this.nodeA.off(Input.EventType.TOUCH_MOVE, this.onTouchMoveA, this);
        this.nodeA.off(Input.EventType.TOUCH_END, this.onTouchEndA, this);
        this.nodeA.off(Input.EventType.TOUCH_CANCEL, this.onTouchEndA, this);
    }

    if (this.nodeB) {
        this.nodeB.off(Input.EventType.TOUCH_START, this.onTouchStartB, this);
        this.nodeB.off(Input.EventType.TOUCH_MOVE, this.onTouchMoveB, this);
        this.nodeB.off(Input.EventType.TOUCH_END, this.onTouchEndB, this);
        this.nodeB.off(Input.EventType.TOUCH_CANCEL, this.onTouchEndB, this);
    }
}

}

所以还是我之前想到的方法,获取两点之间的距离和角度,动态调整链接线的坐标、长度和旋转角度

那不然呢?这样实现有啥问题