【Creator 3.8.0 弹簧组件】如何使用SpringJoint2D组件制作一个弹簧,并且使图片跟随弹簧一并拉伸和挤压

  1. 如何使用SpringJoint2D组件制作一个弹簧,并且使图片跟随弹簧一并拉伸和挤压。
  2. SpringJoint2D我会用,但是不知道如何让贴图跟随弹簧一并拉伸和挤压。
  3. 示例动图如下:
    4
    image
    大家能帮帮我么,我太难了,谢谢!

上面的示意图不是已经有拉伸了吗,这道题不会等物理引擎熟悉的大佬来解答,吃瓜

示意图是我录屏的别人的小游戏,我不知道他怎么做的,我想学习一下,所以发上来请高手指点

我好像知道怎么做了,等我测试一下,等我吃完饭,如果可行我就把解决方案发出来

1赞

吃完了吗?

  1. 图片拉伸和挤压的方法非常简单,就是字面意思,单纯的在长度方向进行拉伸和挤压。
  2. 例如弹簧贴图的原始尺寸为10x50,如果拉伸它的话,就将它的尺寸变为10x100,如果挤压它的话,就将它的尺寸变为10x20。
  3. 然后再根据弹簧的两个端点计算一下贴图的坐标,和旋转,每帧都更新就行了。
  4. 弹簧的贴图用ps在透明背景中画几条直线就行了,或者画几个圆柱绘制上阴影,都行。
    image
1赞
import { _decorator, Color, Component, director, EventMouse, Input, input, Node, Quat, RigidBody2D, SpringJoint2D, Sprite, SpriteFrame, UITransform, Vec2, Vec3 } from 'cc';
const { ccclass, property } = _decorator;

@ccclass('Spring')
export class Spring extends Component {

    spring: Node; // 弹簧图片
    point1: Node; // 保存弹簧的A端(本组件所在的节点上的标记点)
    point2: Node; // 保存弹簧的B端(弹簧的另一端)
    counter: number = 0;

    start() {
        // 此脚本挂在拥有SpringJoint2D组件的node上
        const sj = this.node.getComponent(SpringJoint2D);

        // 在本物体的弹簧锚点位置创建一个空Node用于标记
        let point1 = new Node('mark_point');
        point1.position = new Vec3(sj.anchor.x, sj.anchor.y, 0);
        point1.parent = this.node;
        this.point1 = point1;

        // 在对方物体的弹簧锚点位置创建一个空Node用于标记
        let point2 = new Node('mark_point');
        point2.position = new Vec3(sj.connectedAnchor.x, sj.connectedAnchor.y, 0);
        point2.parent = sj.connectedBody.node;
        this.point2 = point2;

        // 我将弹簧图片直接放到了场景中了, 方便从中提取spriteFrame
        // 开发的时候, 我喜欢这样加载资源, 就不用从AB包或者resources中加载了
        // 总之这里我需要找到弹簧图片的spriteFrame
        const canvas = director.getScene().getChildByPath('Canvas');
        const spf = canvas.getChildByPath('spring').getComponent(Sprite).spriteFrame;

        // 创建弹簧图片
        const spring = new Node('springxxx');
        spring.parent = canvas;
        const tf = spring.addComponent(UITransform);
        const sp = spring.addComponent(Sprite);
        sp.spriteFrame = spf;
        sp.color = Color.YELLOW;
        // 添加完Sprite后再修改UITransform的属性, 否则添加Sprite的时候会将UITransform属性重置
        tf.width = 50;
        this.spring = spring;

        // 监听鼠标, 给物体力, 使物体产生移动
        input.on(Input.EventType.MOUSE_DOWN, this.on_mouse_down, this);
    }

    update(deltaTime: number) {
        // 找到两个标记点
        const p1 = this.point1.worldPosition;
        const p2 = this.point2.worldPosition;
        // 图片的世界位置是这两个标记点的中点
        const pos = new Vec3((p1.x + p2.x) / 2, (p1.y + p2.y) / 2, 0);
        const vector = new Vec2(p2.x - p1.x, p2.y - p1.y);
        // 图片的世界角度是这两个标记点构成的直线的方向, 由于我的图片是竖直的, 所以我要计算这条直线与Y轴的夹角
        // 负号是我测试的, 原理不详, 你也可以调换一下p1-p2这个向量的方向, 随意
        const angle = -vector.signAngle(new Vec2(0, 1)) / Math.PI * 360 / 2;
        // 图片的长度是这两个标记点的距离
        const length = vector.length();
        // 更新弹簧图片
        this.spring.worldPosition = pos;
        const quat = new Quat();
        Quat.fromAngleZ(quat, angle);
        this.spring.worldRotation = quat;
        this.spring.getComponent(UITransform).height = length;
    }

    on_mouse_down(e: EventMouse) {
        const rb = this.node.getComponent(RigidBody2D);
        const mass = rb.getMass();
        if (e.getButton() == EventMouse.BUTTON_LEFT) {
            // 按左键, 给物体一个向下的力来拉伸弹簧
            const i = new Vec2(0, -mass * 20);
            rb.applyLinearImpulseToCenter(i, true);
        } else {
            // 按右键, 给物体一个向右的力来使弹簧旋转
            const i = new Vec2(mass, 0);
            rb.applyLinearImpulseToCenter(i, true);
        }
    }
}

此脚本会在start的时候给弹簧的两个锚点创建两个空Node,使用这两个辅助Node来计算弹簧图片的位置、旋转、大小。
5

喝多了,但是不影响写demo

gif帧率太低,导致观看效果不好,实测效果特别丝滑,好像装了一个真弹簧上去一样,此问题结案了。

6

大佬, 好强

大佬, 有源码么

我不知道如何发cocos的项目代码,我刚开始接触cocos1个月,还没研究cocos的项目结构,不知道应该发哪些文件。我把关键的代码已经发到了5楼,你们自己研究一下吧。