Sticker extension javascript

Before proceeding, please use the forum search feature at the top of the page to check if your question has already been answered.

How do I…

I’m trying to update Sticker extension by editing its javascript

What is the expected result

I need the sticker behavior follows Z axis too

What is the actual result

my object is not following Z axis

Related screenshots

Project files (optional)

if (gdjs._stickerExtension) {
    return;
}

// Unstick from deleted objects.
gdjs.registerObjectDeletedFromSceneCallback(function (runtimeScene, deletedObject) {
    const extension = runtimeScene._stickerExtension;
    if (!extension) {
        return;
    }
    /** @type {Set<gdjs.RuntimeBehavior>} */
    const allStickers = runtimeScene._stickerExtension.allStickers;
    for (const behavior of allStickers) {
        /** @type {Sticker} */
        const sticker = behavior._sticker;
        if (sticker.isStuckTo(deletedObject)) {
            if (behavior._getIsDestroyedWithParent()) {
                behavior.owner.deleteFromScene(runtimeScene);
            }
            sticker.unstick();
        }
    }
});

class Sticker {
    /** @type {gdjs.RuntimeBehavior} */
    behavior;
    /** @type {gdjs.RuntimeObject | null} */
    basisObject;
    followingDoneThisFrame = false;
    relativeZ = 0;
    relativeX = 0;
    relativeY = 0;
    relativeAngle = 0;
    relativeRotatedX = 0;
    relativeRotatedY = 0;
    basisOldZ = 0;
    basisOldX = 0;
    basisOldY = 0;
    basisOldAngle = 0;
    basisOldWidth = 0;
    basisOldHeight = 0;
    basisOldCenterZInScene = 0;
    basisOldCenterXInScene = 0;
    basisOldCenterYInScene = 0;

    /**
     * @param {gdjs.RuntimeBehavior} behavior
     */
    constructor(behavior) {
        this.behavior = behavior;
    }

    /**
     * @param {gdjs.RuntimeObject} basisObject
     */
    isStuckTo(basisObject) {
        return this.basisObject === basisObject;
    }

    /**
     * @param {gdjs.RuntimeObject} basisObject
     */
    stickTo(basisObject) {
        this.basisObject = basisObject;
        this.updateOldCoordinates();
        this.updateRelativeCoordinates();
    }

    unstick() {
        this.basisObject = null;
    }

    onStepPreEvents() {
        this.followingDoneThisFrame = false;
    }

    /**
     * Update the coordinates in the basisObject basis.
     * 
     * It uses the basisObject coordinates from the previous frame.
     * This way, the sticker can move relatively to it and still
     * follow basisObject.
     * 
     * @param {gdjs.RuntimeObject} basisObject
     */
    updateRelativeCoordinates() {
        const object = this.behavior.owner;

        // Update relative coordinates
        this.relativeZ = object.getZ() - this.basisOldZ;
        this.relativeX = object.getX() - this.basisOldX;
        this.relativeY = object.getY() - this.basisOldY;
        if (!this.behavior._getOnlyFollowPosition()) {
            this.relativeAngle = object.getAngle() - this.basisOldAngle;
            this.relativeWidth = object.getWidth() / this.basisOldWidth;
            this.relativeHeight = object.getHeight() / this.basisOldHeight;
            const deltaX = object.getCenterXInScene() - this.basisOldCenterXInScene;
            const deltaY = object.getCenterYInScene() - this.basisOldCenterYInScene;
            const angle = this.basisOldAngle * Math.PI / 180;
            const cosA = Math.cos(angle);
            const sinA = Math.sin(angle);
            this.relativeRotatedX = (deltaX * cosA + deltaY * sinA) / this.basisOldWidth;
            this.relativeRotatedY = (-deltaX * sinA + deltaY * cosA) / this.basisOldHeight;

            // Save initial values to avoid calculus and rounding errors
            this.basisOriginalWidth = this.basisObject.getWidth();
            this.basisOriginalHeight = this.basisObject.getHeight();
            this.basisOriginalAngle = this.basisObject.getAngle();
        }
    }

    /**
     * Copy the coordinates to use it the next frame.
     */
    updateOldCoordinates() {
        const object = this.behavior.owner;

        this.ownerOldZ = object.getZ();
        this.ownerOldX = object.getX();
        this.ownerOldY = object.getY();

        this.basisOldZ = this.basisObject.getZ();
        this.basisOldX = this.basisObject.getX();
        this.basisOldY = this.basisObject.getY();

        if (!this.behavior._getOnlyFollowPosition()) {
            this.ownerOldAngle = object.getAngle();
            this.ownerOldWidth = object.getWidth();
            this.ownerOldHeight = object.getHeight();

            this.basisOldAngle = this.basisObject.getAngle();
            this.basisOldWidth = this.basisObject.getWidth();
            this.basisOldHeight = this.basisObject.getHeight();
            this.basisOldCenterXInScene = this.basisObject.getCenterXInScene();
            this.basisOldCenterYInScene = this.basisObject.getCenterYInScene();
        }
    }

    /**
     * Follow the basisObject (called in doStepPostEvents).
     * 
     * This method is also called by children to ensure
     * parents are updated first.
     */
    followBasisObject() {
        if (this.followingDoneThisFrame) {
            return;
        }
        this.followingDoneThisFrame = true;
        const basisObject = this.basisObject;
        if (!basisObject) {
            return;
        }
        // If the behavior on the basis object has a different name,
        // the objects will still follow their basis objects
        // but frame delays could happen.
        const behaviorName = this.behavior.getName();
        if (basisObject.hasBehavior(behaviorName)) {
            const basisBehavior = basisObject.getBehavior(behaviorName);
            if (basisBehavior.type === this.behavior.type) {
                // Follow parents 1st to avoid frame delays
                basisBehavior._sticker.followBasisObject();
            }
        }
        if (this.behavior._getOnlyFollowPosition()) {
            this.followPosition();
        } else {
            this.followTransformation();
        }
        this.updateOldCoordinates();
    }

    followPosition() {
        const object = this.behavior.owner;
        const basisObject = this.basisObject;
        if (!basisObject) {
            return;
        }
        if (object.getX() !== this.ownerOldX
            || object.getY() !== this.ownerOldY || object.getZ() !== this.ownerOldZ) {
            this.updateRelativeCoordinates();
        }
        if (this.basisOldX !== basisObject.getX()
            || this.basisOldY !== basisObject.getY() || this.basisOldZ !== basisObject.getZ()) {
            object.setPosition(
                basisObject.getX() + this.relativeX,
                basisObject.getY() + this.relativeY,
                basisObject.getZ() + this.relativeZ);
        }
    }

    followTransformation() {
        const object = this.behavior.owner;
        const basisObject = this.basisObject;
        if (!basisObject) {
            return;
        }
        if (object.getX() !== this.ownerOldX
            || object.getY() !== this.ownerOldY
            || object.getAngle() !== this.ownerOldAngle
            || object.getWidth() !== this.ownerOldWidth
            || object.getHeight() !== this.ownerOldHeight) {
            this.updateRelativeCoordinates();
        }
        if (this.basisOldAngle !== this.basisObject.getAngle()
            || this.basisOldWidth !== this.basisObject.getWidth()
            || this.basisOldHeight !== this.basisObject.getHeight()
            || this.basisOldCenterXInScene !== this.basisObject.getCenterXInScene()
            || this.basisOldCenterYInScene !== this.basisObject.getCenterYInScene()) {
            // Unproportional dimensions changes won't work as expected
            // if the object angle is not null but nothing more can be done
            // because there is no full affine transformation on objects.
            if (basisObject.getWidth() !== this.basisOriginalWidth) {
                object.setWidth(this.relativeWidth * basisObject.getWidth());
            }
            if (basisObject.getHeight() !== this.basisOriginalHeight) {
                object.setHeight(this.relativeHeight * basisObject.getHeight());
            }
            // Follow basisObject
            if (basisObject.getAngle() === this.basisOriginalAngle
                && this.basisOriginalAngle === 0) {
                if (basisObject.getWidth() === this.basisOriginalWidth
                    || basisObject.getHeight() === this.basisOriginalHeight) {
                    if (this.basisOldX !== basisObject.getX() ||
                        this.basisOldY !== basisObject.getY() ||
                        this.basisOldZ !== basisObject.getZ()) {
                        object.setPosition(
                            basisObject.getX() + this.relativeX,
                            basisObject.getY() + this.relativeY,
                            basisObject.getZ() + this.relativeZ);
                    }
                } else {
                    object.setCenterPositionInScene(
                        basisObject.getCenterXInScene() + this.relativeRotatedX * basisObject.getWidth(),
                        basisObject.getCenterYInScene() + this.relativeRotatedY * basisObject.getHeight());
                }
            } else {
                object.setAngle(basisObject.getAngle() + this.relativeAngle);

                const deltaX = this.relativeRotatedX * basisObject.getWidth();
                const deltaY = this.relativeRotatedY * basisObject.getHeight();
                const angle = -basisObject.getAngle() * Math.PI / 180;
                const cosA = Math.cos(angle);
                const sinA = Math.sin(angle);
                object.setX(
                    basisObject.getCenterXInScene() + object.getX() - object.getCenterXInScene()
                    + deltaX * cosA + deltaY * sinA);
                object.setY(
                    basisObject.getCenterYInScene() + object.getY() - object.getCenterYInScene()
                    - deltaX * sinA + deltaY * cosA);
            }
        }
    }
}

gdjs._stickerExtension = {
    Sticker
}
1 Like

the following code works as desired

if (gdjs._stickerExtension) {
    return;
}

// Unstick from deleted objects.
gdjs.registerObjectDeletedFromSceneCallback(function (runtimeScene, deletedObject) {
    const extension = runtimeScene._stickerExtension;
    if (!extension) {
        return;
    }
    /** @type {Set<gdjs.RuntimeBehavior>} */
    const allStickers = runtimeScene._stickerExtension.allStickers;
    for (const behavior of allStickers) {
        /** @type {Sticker} */
        const sticker = behavior._sticker;
        if (sticker.isStuckTo(deletedObject)) {
            if (behavior._getIsDestroyedWithParent()) {
                behavior.owner.deleteFromScene(runtimeScene);
            }
            sticker.unstick();
        }
    }
});

class Sticker {
    /** @type {gdjs.RuntimeBehavior} */
    behavior;
    /** @type {gdjs.RuntimeObject | null} */
    basisObject;
    followingDoneThisFrame = false;
    relativeX = 0;
    relativeY = 0;
    relativeZ = 0;
    relativeAngle = 0;
    relativeRotatedX = 0;
    relativeRotatedY = 0;
    basisOldX = 0;
    basisOldY = 0;
    basisOldZ = 0;
    basisOldAngle = 0;
    basisOldWidth = 0;
    basisOldHeight = 0;
    basisOldCenterXInScene = 0;
    basisOldCenterYInScene = 0;

    /**
     * @param {gdjs.RuntimeBehavior} behavior
     */
    constructor(behavior) {
        this.behavior = behavior;
    }

    /**
     * @param {gdjs.RuntimeObject} basisObject
     */
    isStuckTo(basisObject) {
        return this.basisObject === basisObject;
    }

    /**
     * @param {gdjs.RuntimeObject} basisObject
     */
    stickTo(basisObject) {
        this.basisObject = basisObject;
        this.updateOldCoordinates();
        this.updateRelativeCoordinates();
    }

    unstick() {
        this.basisObject = null;
    }

    onStepPreEvents() {
        this.followingDoneThisFrame = false;
    }

    /**
     * Update the coordinates in the basisObject basis.
     * 
     * It uses the basisObject coordinates from the previous frame.
     * This way, the sticker can move relatively to it and still
     * follow basisObject.
     * 
     * @param {gdjs.RuntimeObject} basisObject
     */
    updateRelativeCoordinates() {
        const object = this.behavior.owner;

        // Update relative coordinates 
        this.relativeX = object.getX() - this.basisOldX;
        this.relativeY = object.getY() - this.basisOldY;
        this.relativeZ = object.getZ() - this.basisOldZ;
        if (!this.behavior._getOnlyFollowPosition()) {
            this.relativeAngle = object.getAngle() - this.basisOldAngle;
            this.relativeWidth = object.getWidth() / this.basisOldWidth;
            this.relativeHeight = object.getHeight() / this.basisOldHeight;
            const deltaX = object.getCenterXInScene() - this.basisOldCenterXInScene;
            const deltaY = object.getCenterYInScene() - this.basisOldCenterYInScene;
            const angle = this.basisOldAngle * Math.PI / 180;
            const cosA = Math.cos(angle);
            const sinA = Math.sin(angle);
            this.relativeRotatedX = (deltaX * cosA + deltaY * sinA) / this.basisOldWidth;
            this.relativeRotatedY = (-deltaX * sinA + deltaY * cosA) / this.basisOldHeight;

            // Save initial values to avoid calculus and rounding errors
            this.basisOriginalWidth = this.basisObject.getWidth();
            this.basisOriginalHeight = this.basisObject.getHeight();
            this.basisOriginalAngle = this.basisObject.getAngle();
        }
    }

    /**
     * Copy the coordinates to use it the next frame.
     */
    updateOldCoordinates() {
        const object = this.behavior.owner;

        this.ownerOldX = object.getX();
        this.ownerOldY = object.getY();
        this.ownerOldZ = object.getZ();

        this.basisOldX = this.basisObject.getX();
        this.basisOldY = this.basisObject.getY();
         this.basisOldZ = this.basisObject.getZ();

        if (!this.behavior._getOnlyFollowPosition()) {
            this.ownerOldAngle = object.getAngle();
            this.ownerOldWidth = object.getWidth();
            this.ownerOldHeight = object.getHeight();

            this.basisOldAngle = this.basisObject.getAngle();
            this.basisOldWidth = this.basisObject.getWidth();
            this.basisOldHeight = this.basisObject.getHeight();
            this.basisOldCenterXInScene = this.basisObject.getCenterXInScene();
            this.basisOldCenterYInScene = this.basisObject.getCenterYInScene();
        }
    }

    /**
     * Follow the basisObject (called in doStepPostEvents).
     * 
     * This method is also called by children to ensure
     * parents are updated first.
     */
    followBasisObject() {
        if (this.followingDoneThisFrame) {
            return;
        }
        this.followingDoneThisFrame = true;
        const basisObject = this.basisObject;
        if (!basisObject) {
            return;
        }
        // If the behavior on the basis object has a different name,
        // the objects will still follow their basis objects
        // but frame delays could happen.
        const behaviorName = this.behavior.getName();
        if (basisObject.hasBehavior(behaviorName)) {
            const basisBehavior = basisObject.getBehavior(behaviorName);
            if (basisBehavior.type === this.behavior.type) {
                // Follow parents 1st to avoid frame delays
                basisBehavior._sticker.followBasisObject();
            }
        }
        if (this.behavior._getOnlyFollowPosition()) {
            this.followPosition();
        } else {
            this.followTransformation();
        }
        this.updateOldCoordinates();
    }

    followPosition() {
        const object = this.behavior.owner;
        const basisObject = this.basisObject;
        if (!basisObject) {
            return;
        }
        if (object.getX() !== this.ownerOldX
            || object.getY() !== this.ownerOldY
            || object.getZ() !== this.ownerOldZ) {
            this.updateRelativeCoordinates();
        }
        if (this.basisOldX !== basisObject.getX()
            || this.basisOldY !== basisObject.getY()
            || this.basisOldZ !== basisObject.getZ()) {
            object.setPosition(
                basisObject.getX() + this.relativeX,
                basisObject.getY() + this.relativeY);
            object.setZ(
                basisObject.getZ() + this.relativeZ);
        }
    }

    followTransformation() {
        const object = this.behavior.owner;
        const basisObject = this.basisObject;
        if (!basisObject) {
            return;
        }
        if (object.getX() !== this.ownerOldX
            || object.getY() !== this.ownerOldY
            || object.getAngle() !== this.ownerOldAngle
            || object.getWidth() !== this.ownerOldWidth
            || object.getHeight() !== this.ownerOldHeight) {
            this.updateRelativeCoordinates();
        }
        if (this.basisOldAngle !== this.basisObject.getAngle()
            || this.basisOldWidth !== this.basisObject.getWidth()
            || this.basisOldHeight !== this.basisObject.getHeight()
            || this.basisOldCenterXInScene !== this.basisObject.getCenterXInScene()
            || this.basisOldCenterYInScene !== this.basisObject.getCenterYInScene()) {
            // Unproportional dimensions changes won't work as expected
            // if the object angle is not null but nothing more can be done
            // because there is no full affine transformation on objects.
            if (basisObject.getWidth() !== this.basisOriginalWidth) {
                object.setWidth(this.relativeWidth * basisObject.getWidth());
            }
            if (basisObject.getHeight() !== this.basisOriginalHeight) {
                object.setHeight(this.relativeHeight * basisObject.getHeight());
            }
            // Follow basisObject
            if (basisObject.getAngle() === this.basisOriginalAngle
                && this.basisOriginalAngle === 0) {
                if (basisObject.getWidth() === this.basisOriginalWidth
                    || basisObject.getHeight() === this.basisOriginalHeight) {
                    if (this.basisOldX !== basisObject.getX() ||
                        this.basisOldY !== basisObject.getY()) {
                        object.setPosition(
                            basisObject.getX() + this.relativeX,
                            basisObject.getY() + this.relativeY);
                        object.setZ(
                            basisObject.getZ() + this.relativeZ);
                    }
                } else {
                    object.setCenterPositionInScene(
                        basisObject.getCenterXInScene() + this.relativeRotatedX * basisObject.getWidth(),
                        basisObject.getCenterYInScene() + this.relativeRotatedY * basisObject.getHeight());
                        object.setZ(
                basisObject.getZ() + this.relativeZ);
                }
            } else {
                object.setAngle(basisObject.getAngle() + this.relativeAngle);

                const deltaX = this.relativeRotatedX * basisObject.getWidth();
                const deltaY = this.relativeRotatedY * basisObject.getHeight();
                const angle = -basisObject.getAngle() * Math.PI / 180;
                const cosA = Math.cos(angle);
                const sinA = Math.sin(angle);
                object.setX(
                    basisObject.getCenterXInScene() + object.getX() - object.getCenterXInScene()
                    + deltaX * cosA + deltaY * sinA);
                object.setY(
                    basisObject.getCenterYInScene() + object.getY() - object.getCenterYInScene()
                    - deltaX * sinA + deltaY * cosA);
                object.setZ(
                    basisObject.getZ() + this.relativeZ);
            }
        }
    }
}

gdjs._stickerExtension = {
    Sticker
}

Excellent stuff!

Will your modified version work in both 2D and 3D? if so, I’d suggest you submit this as an update to the sticker extension.

If it doesn’t, then I’d suggest you submit it as a new extension, with a hat-tip to the original.

Either way, submit it as there will be others who will be interested in using it.

I have not tested on 2D, after testing it I will submit an update or new extension accordingly to the test result. Thank you for your feedback

the code I’ve posted here before works only when not following rotation
this new code below works on 3D with follow rotation

if (gdjs._stickerExtension) {
    return;
}

// Unstick from deleted objects.
gdjs.registerObjectDeletedFromSceneCallback(function (runtimeScene, deletedObject) {
    const extension = runtimeScene._stickerExtension;
    if (!extension) {
        return;
    }
    /** @type {Set<gdjs.RuntimeBehavior>} */
    const allStickers = runtimeScene._stickerExtension.allStickers;
    for (const behavior of allStickers) {
        /** @type {Sticker} */
        const sticker = behavior._sticker;
        if (sticker.isStuckTo(deletedObject)) {
            if (behavior._getIsDestroyedWithParent()) {
                behavior.owner.deleteFromScene(runtimeScene);
            }
            sticker.unstick();
        }
    }
});


class Sticker {
    /** @type {gdjs.RuntimeBehavior} */
    behavior;
    /** @type {gdjs.RuntimeObject | null} */
    basisObject;
    followingDoneThisFrame = false;
    relativeX = 0;
    relativeY = 0;
    relativeZ = 0;
    relativeAngle = 0;
    relativeRotatedX = 0;
    relativeRotatedY = 0;
    basisOldX = 0;
    basisOldY = 0;
    basisOldZ = 0;
    basisOldAngle = 0;
    basisOldWidth = 0;
    basisOldHeight = 0;
    basisOldCenterXInScene = 0;
    basisOldCenterYInScene = 0;
    basisOldCenterZInScene = 0;

    /**
     * @param {gdjs.RuntimeBehavior} behavior
     */
    constructor(behavior) {
        this.behavior = behavior;
    }

    /**
     * @param {gdjs.RuntimeObject} basisObject
     */
    isStuckTo(basisObject) {
        return this.basisObject === basisObject;
    }

    /**
     * @param {gdjs.RuntimeObject} basisObject
     */
    stickTo(basisObject) {
        this.basisObject = basisObject;
        this.updateOldCoordinates();
        this.updateRelativeCoordinates();
    }

    unstick() {
        this.basisObject = null;
    }

    onStepPreEvents() {
        this.followingDoneThisFrame = false;
    }

    /**
     * Update the coordinates in the basisObject basis.
     * 
     * It uses the basisObject coordinates from the previous frame.
     * This way, the sticker can move relatively to it and still
     * follow basisObject.
     * 
     * @param {gdjs.RuntimeObject} basisObject
     */
    updateRelativeCoordinates() {
        const object = this.behavior.owner;

        // Update relative coordinates 
        this.relativeX = object.getX() - this.basisOldX;
        this.relativeY = object.getY() - this.basisOldY;
        this.relativeZ = object.getZ() - this.basisOldZ;
        if (!this.behavior._getOnlyFollowPosition()) {
            this.relativeAngle = object.getAngle() - this.basisOldAngle;
            this.relativeWidth = object.getWidth() / this.basisOldWidth;
            this.relativeHeight = object.getHeight() / this.basisOldHeight;
            const deltaX = object.getCenterXInScene() - this.basisOldCenterXInScene;
            const deltaY = object.getCenterYInScene() - this.basisOldCenterYInScene;
            const angle = this.basisOldAngle * Math.PI / 180;
            const cosA = Math.cos(angle);
            const sinA = Math.sin(angle);
            this.relativeRotatedX = (deltaX * cosA + deltaY * sinA) / this.basisOldWidth;
            this.relativeRotatedY = (-deltaX * sinA + deltaY * cosA) / this.basisOldHeight;

            // Save initial values to avoid calculus and rounding errors
            this.basisOriginalWidth = this.basisObject.getWidth();
            this.basisOriginalHeight = this.basisObject.getHeight();
            this.basisOriginalAngle = this.basisObject.getAngle();
        }
    }

    /**
     * Copy the coordinates to use it the next frame.
     */
    updateOldCoordinates() {
        const object = this.behavior.owner;

        this.ownerOldX = object.getX();
        this.ownerOldY = object.getY();
        this.ownerOldZ = object.getZ();

        this.basisOldX = this.basisObject.getX();
        this.basisOldY = this.basisObject.getY();
        this.basisOldZ = this.basisObject.getZ();

        if (!this.behavior._getOnlyFollowPosition()) {
            this.ownerOldAngle = object.getAngle();
            this.ownerOldWidth = object.getWidth();
            this.ownerOldHeight = object.getHeight();

            this.basisOldAngle = this.basisObject.getAngle();
            this.basisOldWidth = this.basisObject.getWidth();
            this.basisOldHeight = this.basisObject.getHeight();
            this.basisOldCenterXInScene = this.basisObject.getCenterXInScene();
            this.basisOldCenterYInScene = this.basisObject.getCenterYInScene();
            this.basisOldCenterZInScene = this.basisObject.getZ();
        }
    }

    /**
     * Follow the basisObject (called in doStepPostEvents).
     * 
     * This method is also called by children to ensure
     * parents are updated first.
     */
    followBasisObject() {
        if (this.followingDoneThisFrame) {
            return;
        }
        this.followingDoneThisFrame = true;
        const basisObject = this.basisObject;
        if (!basisObject) {
            return;
        }
        // If the behavior on the basis object has a different name,
        // the objects will still follow their basis objects
        // but frame delays could happen.
        const behaviorName = this.behavior.getName();
        if (basisObject.hasBehavior(behaviorName)) {
            const basisBehavior = basisObject.getBehavior(behaviorName);
            if (basisBehavior.type === this.behavior.type) {
                // Follow parents 1st to avoid frame delays
                basisBehavior._sticker.followBasisObject();
            }
        }
        if (this.behavior._getOnlyFollowPosition()) {
            this.followPosition();
        } else {
            this.followTransformation();
        }
        this.updateOldCoordinates();
    }

    followPosition() {
        const object = this.behavior.owner;
        const basisObject = this.basisObject;
        if (!basisObject) {
            return;
        }
        if (object.getX() !== this.ownerOldX
            || object.getY() !== this.ownerOldY
            || object.getZ() !== this.ownerOldZ) {
            this.updateRelativeCoordinates();
        }
        if (this.basisOldX !== basisObject.getX()
            || this.basisOldY !== basisObject.getY()
            || this.basisOldZ !== basisObject.getZ()) {
            object.setPosition(
                basisObject.getX() + this.relativeX,
                basisObject.getY() + this.relativeY);
            object.setZ(
                basisObject.getZ() + this.relativeZ);
        }
    }

    followTransformation() {
        const object = this.behavior.owner;
        const basisObject = this.basisObject;
        if (!basisObject) {
            return;
        }
        if (object.getX() !== this.ownerOldX
            || object.getY() !== this.ownerOldY
            || object.getZ() !== this.ownerOldZ
            || object.getAngle() !== this.ownerOldAngle
            || object.getWidth() !== this.ownerOldWidth
            || object.getHeight() !== this.ownerOldHeight) {
            this.updateRelativeCoordinates();
        }
        if (this.basisOldAngle !== this.basisObject.getAngle()
            || this.basisOldWidth !== this.basisObject.getWidth()
            || this.basisOldHeight !== this.basisObject.getHeight()
            || this.basisOldCenterXInScene !== this.basisObject.getCenterXInScene()
            || this.basisOldCenterYInScene !== this.basisObject.getCenterYInScene()
            || this.basisOldCenterZInScene !== this.basisObject.getZ()
            ) {
            // Unproportional dimensions changes won't work as expected
            // if the object angle is not null but nothing more can be done
            // because there is no full affine transformation on objects.
            if (basisObject.getWidth() !== this.basisOriginalWidth) {
                object.setWidth(this.relativeWidth * basisObject.getWidth());
            }
            if (basisObject.getHeight() !== this.basisOriginalHeight) {
                object.setHeight(this.relativeHeight * basisObject.getHeight());
            }
            // Follow basisObject
            if (basisObject.getAngle() === this.basisOriginalAngle
                && this.basisOriginalAngle === 0) {
                if (basisObject.getWidth() === this.basisOriginalWidth
                    || basisObject.getHeight() === this.basisOriginalHeight) {
                    if (this.basisOldX !== basisObject.getX() ||
                        this.basisOldY !== basisObject.getY() ||
                        this.basisOldZ !== basisObject.getZ()) {
                        object.setPosition(
                            basisObject.getX() + this.relativeX,
                            basisObject.getY() + this.relativeY);
                        object.setZ(
                            basisObject.getZ() + this.relativeZ);
                    }
                } else {
                    object.setCenterPositionInScene(
                        basisObject.getCenterXInScene() + this.relativeRotatedX * basisObject.getWidth(),
                        basisObject.getCenterYInScene() + this.relativeRotatedY * basisObject.getHeight());
                        /*object.setZ(
                basisObject.getZ() + this.relativeZ);*/
                }
            } else {
                object.setAngle(basisObject.getAngle() + this.relativeAngle);

                const deltaX = this.relativeRotatedX * basisObject.getWidth();
                const deltaY = this.relativeRotatedY * basisObject.getHeight();
                const angle = -basisObject.getAngle() * Math.PI / 180;
                const cosA = Math.cos(angle);
                const sinA = Math.sin(angle);
                object.setX(
                    basisObject.getCenterXInScene() + object.getX() - object.getCenterXInScene()
                    + deltaX * cosA + deltaY * sinA);
                object.setY(
                    basisObject.getCenterYInScene() + object.getY() - object.getCenterYInScene()
                    - deltaX * sinA + deltaY * cosA);
                object.setZ(
                    basisObject.getZ() + this.relativeZ);
            }
        }
    }
}

gdjs._stickerExtension = {
    Sticker
}
1 Like

Is this supposed to be a modification for the normal sticker extension, or is it meant to be a new extension?

I’ll try this out. I’m looking for something to attach a 3D object to another one