Status
Not open for further replies.

subengari

Veteran
Veteran
Joined
Dec 12, 2020
Messages
36
Reaction score
14
First Language
king's english
Primarily Uses
RMMV
Minasama, konnichiwa-

What do I need to set a sprite's anchor to, to have it rotate in place?

I've tried using 0, 0.5, 1, -1, etc. and it seems to orbit around a point, but not spin in place. Additionally, if I use anything but 0, it offsets the sprite's original X and Y position.

Arigato in advance!
 

caethyril

^_^
Global Mod
Joined
Feb 21, 2018
Messages
3,923
Reaction score
2,980
First Language
EN
Primarily Uses
RMMZ
[Edit: this was a bad approach, see my follow-up post instead: link.]

Try something like this, where sprite is a reference to your sprite:
JavaScript:
// Change anchor to centre
sprite.anchor.x = 0.5;
sprite.anchor.y = 0.5;
// Set angle to 45 degrees clockwise
sprite._angle = 45;

...unless it's a Sprite_Picture. In that case the anchor updates every frame via its updateOrigin method (rmmz_sprites.js).
 
Last edited:

subengari

Veteran
Veteran
Joined
Dec 12, 2020
Messages
36
Reaction score
14
First Language
king's english
Primarily Uses
RMMV
Try something like this, where sprite is a reference to your sprite:
JavaScript:
// Change anchor to centre
sprite.anchor.x = 0.5;
sprite.anchor.y = 0.5;
// Set angle to 45 degrees clockwise
sprite._angle = 45;

...unless it's a Sprite_Picture. In that case the anchor updates every frame via its updateOrigin method (rmmz_sprites.js).
Thanks @caethyril - the ._angle suggestion makes it works a bit better, but the sprite still hops out of place when the anchors are adjusted.

Take a gander at the treasure chest, first, and then the first heart in the HUD at the top of the screen:


Below, is the pertinent code:
spin.png

Neither is a Sprite_Picture... they are a Sprite_Character, and a [custom] Sprite_HudHeart)... so, I'm not sure what I'm doing wrong.
 
Last edited:

caethyril

^_^
Global Mod
Joined
Feb 21, 2018
Messages
3,923
Reaction score
2,980
First Language
EN
Primarily Uses
RMMZ
:kaoswt: OK, so I did some reading/testing and it turns out that my previous post was incorrect for a couple of reasons:
  1. The property should be angle (degrees) or rotation (radians). _angle does nothing. (I was thinking of the Game_Picture property, whoops.)

  2. Rotation actually occurs about the sprite's pivot (source). This has a similar structure to the anchor. The difference is that:
    • anchor defines the origin of the sprite's coordinate system, and
    • pivot defines the point around which it rotates.
    Also, anchor is a percentage (0~1) but pivot is measured in sprite coordinates (px).

    The default pivot is (0, 0); the rotation axis can be "set" via the anchor like I described, but as you've seen, that is not an ideal approach.
The "jump" you were seeing was the change of anchor. Sprite_Character uses an anchor of (0.5, 1); the default anchor is (0, 0).

Try something like this instead:
JavaScript:
// set pivot point to centre of sprite
sprite.pivot.x = sprite.width / 2;
sprite.pivot.y = sprite.height / 2;
// rotate sprite to an angle of 45 degrees
sprite.angle = 45;
I.e. only change the sprite's pivot, not its anchor. :kaophew:

Note that the pivot coordinates are dependent on the size of the sprite, which may be inaccurate if you try to fetch it too early, i.e. before its image is loaded. There are ways to get around this, e.g. reset the pivot every time you change the angle, or attach a suitable load listener to the bitmap.
 
Last edited:

subengari

Veteran
Veteran
Joined
Dec 12, 2020
Messages
36
Reaction score
14
First Language
king's english
Primarily Uses
RMMV
@caethyril - I appreciate your help!

Okay, so I tried your suggestion... below is the modified code:
pivot.png

Unfortunately, the sprite just reverts back to orbiting around a point rather than itself and then completely disappears when finished:


I know you said that you thought ._angle does nothing, but I'm pretty sure using it was what made the sprite in the code's last iteration spin around in place!
 

caethyril

^_^
Global Mod
Joined
Feb 21, 2018
Messages
3,923
Reaction score
2,980
First Language
EN
Primarily Uses
RMMZ
Yes, a change of pivot will affect where the sprite displays on the map. Looks like you'll need an offset for the sprite's map position as well. :kaoslp:

I finally decided to try it myself this time and came up with this:
JavaScript:
/*:
 * @target MZ
 * @plugindesc Make characters rotate.
 * @author Caethyril
 * @help Free to use and/or modify for any project.
 * 
 * Rotate a Game_CharacterBase using this script call:
 *        CHAR.rotate(RATE, REVS);
 *   e.g. $gamePlayer.rotate(2, 1);
 *   e.g. $gameMap.event(1).rotate(6);
 *   Replace:
 *     - CHAR with a Game_CharacterBase reference;
 *     - RATE with the clockwise rotation per frame, in degrees;
 *     - REVS with the total number of revolutions to make.
 *   REVS is optional: if omitted (or 0) then rotation will be indefinite.
 *   Use RATE = 0 to stop rotation.
 */

(alias => {
    Sprite_Character.prototype.updatePosition = function() {
        alias.apply(this, arguments);
        this.y += this.pivot.y;       // external anchor offset
    };
})(Sprite_Character.prototype.updatePosition);

(alias => {
    Sprite_Character.prototype.updateOther = function() {
        alias.apply(this, arguments);
        this.updateRotation();      // see below
    };
})(Sprite_Character.prototype.updateOther);

(() => {
    
    /**
     * @param {Number} [rate] Degrees of rotation per frame (default: 0).
     * @param {Number} [revs] Number of complete revolutions to make (default: 0).
     * @returns {{rate:Number,revs:Number,turn:Number}} Character rotation data.
     */
    const mkRotationData = function(rate = 0, revs = 0) {
        const fRate = (Number(rate) || 0) % 360;
        const iRevs = parseInt(revs, 10) || 0;
        return fRate ? { rate: fRate, revs: iRevs, turn: 0 } : null;
    };

    /**
     * Sets y-pivot to the midpoint of the sprite's current frame.
     * @param {Sprite_CharacterBase} sprite Sprite_CharacterBase reference to update.
     */
    const updatePivot = function(sprite) {
        sprite.pivot.y = -sprite.patternHeight() / 2;
        sprite.updatePosition();      // to stop 1-frame positional glitch
    };

    // New: apply character rotation. See mkRotationData, above.
    Game_CharacterBase.prototype.rotate = function(rate, revs) {
        this._rotationData = mkRotationData(rate, revs);
    };

    // New: update character rotation. See Sprite_Character#updateOther, above.
    Sprite_Character.prototype.updateRotation = function() {
        const DATA = this._character?._rotationData;
        if (DATA) {
            const REV = 360;
            updatePivot(this);
            this.angle += DATA.rate;
            if (Math.abs(DATA.turn += DATA.rate) >= REV) {
                DATA.turn -= REV;
                if (--DATA.revs === 0) {            // allow indefinite
                    this._character.rotate(0);
                }
            }
        }
    };

})();
Could probably be improved, but it seems to work OK~ :kaophew:

Edit - and here's a version that seems to be compatible with RMMV's older JS/PIXI base:
JavaScript:
/*:
 * @target MV
 * @plugindesc Make characters rotate.
 * @author Caethyril
 * @help Free to use and/or modify for any project.
 * 
 * Rotate a Game_CharacterBase using this script call:
 *        CHAR.rotate(RATE, REVS);
 *   e.g. $gamePlayer.rotate(2, 1);
 *   e.g. $gameMap.event(1).rotate(6);
 *   Replace:
 *     - CHAR with a Game_CharacterBase reference;
 *     - RATE with the clockwise rotation per frame, in degrees;
 *     - REVS with the total number of revolutions to make.
 *   REVS is optional: if omitted (or 0) then rotation will be indefinite.
 *   Use RATE = 0 to stop rotation.
 */

(alias => {
    Sprite_Character.prototype.updatePosition = function() {
        alias.apply(this, arguments);
        this.y += this.pivot.y;       // external anchor offset
    };
})(Sprite_Character.prototype.updatePosition);

(alias => {
    Sprite_Character.prototype.updateOther = function() {
        alias.apply(this, arguments);
        this.updateRotation();      // see below
    };
})(Sprite_Character.prototype.updateOther);

(() => {
    
    /**
     * @param {Number} [rate] Degrees of rotation per frame (default: 0).
     * @param {Number} [revs] Number of complete revolutions to make (default: 0).
     * @returns {{rate:Number,revs:Number,turn:Number}} Character rotation data.
     */
    const mkRotationData = function(rate = 0, revs = 0) {
        const fRate = ((Number(rate) || 0) * Math.PI / 180) % (2 * Math.PI);
        const iRevs = parseInt(revs, 10) || 0;
        return fRate ? { rate: fRate, revs: iRevs, turn: 0 } : null;
    };

    /**
     * Sets y-pivot to the midpoint of the sprite's current frame.
     * @param {Sprite_CharacterBase} sprite Sprite_CharacterBase reference to update.
     */
    const updatePivot = function(sprite) {
        sprite.pivot.y = -sprite.patternHeight() / 2;
        sprite.updatePosition();      // to stop 1-frame positional glitch
    };

    // New: apply character rotation. See mkRotationData, above.
    Game_CharacterBase.prototype.rotate = function(rate, revs) {
        this._rotationData = mkRotationData(rate, revs);
    };

    // New: update character rotation. See Sprite_Character#updateOther, above.
    Sprite_Character.prototype.updateRotation = function() {
        const DATA = this._character ? this._character._rotationData : null;
        if (DATA) {
            const REV = 2 * Math.PI;
            updatePivot(this);
            this.rotation += DATA.rate;
            if (Math.abs(DATA.turn += DATA.rate) >= REV) {
                DATA.turn -= REV;
                if (--DATA.revs === 0) {            // allow indefinite
                    this._character.rotate(0);
                }
            }
        } else {
            this.rotation = 0;
        }
    };

})();
(Probably also works in MZ, I haven't tested.) I added an else into the updateRotation method to reset rotation to 0 if/when the data runs out, too.
 
Last edited:

subengari

Veteran
Veteran
Joined
Dec 12, 2020
Messages
36
Reaction score
14
First Language
king's english
Primarily Uses
RMMV
Yes, a change of pivot will affect where the sprite displays on the map. Looks like you'll need an offset for the sprite's map position as well. :kaoslp:

I finally decided to try it myself this time and came up with this:
JavaScript:
/*:
 * @target MZ
 * @plugindesc Make characters rotate.
 * @author Caethyril
 * @help Free to use and/or modify for any project.
 *
 * Rotate a Game_CharacterBase using this script call:
 *        CHAR.rotate(RATE, REVS);
 *   e.g. $gamePlayer.rotate(2, 1);
 *   e.g. $gameMap.event(1).rotate(6);
 *   Replace:
 *     - CHAR with a Game_CharacterBase reference;
 *     - RATE with the clockwise rotation per frame, in degrees;
 *     - REVS with the total number of revolutions to make.
 *   REVS is optional: if omitted (or 0) then rotation will be indefinite.
 *   Use RATE = 0 to stop rotation.
 */

(alias => {
    Sprite_Character.prototype.updatePosition = function() {
        alias.apply(this, arguments);
        this.y += this.pivot.y;       // external anchor offset
    };
})(Sprite_Character.prototype.updatePosition);

(alias => {
    Sprite_Character.prototype.updateOther = function() {
        alias.apply(this, arguments);
        this.updateRotation();      // see below
    };
})(Sprite_Character.prototype.updateOther);

(() => {
   
    /**
     * @param {Number} [rate] Degrees of rotation per frame (default: 0).
     * @param {Number} [revs] Number of complete revolutions to make (default: 0).
     * @returns {{rate:Number,revs:Number,turn:Number}} Character rotation data.
     */
    const mkRotationData = function(rate = 0, revs = 0) {
        const fRate = (Number(rate) || 0) % 360;
        const iRevs = parseInt(revs, 10) || 0;
        return fRate ? { rate: fRate, revs: iRevs, turn: 0 } : null;
    };

    /**
     * Sets y-pivot to the midpoint of the sprite's current frame.
     * @param {Sprite_CharacterBase} sprite Sprite_CharacterBase reference to update.
     */
    const updatePivot = function(sprite) {
        sprite.pivot.y = -sprite.patternHeight() / 2;
        sprite.updatePosition();      // to stop 1-frame positional glitch
    };

    // New: apply character rotation. See mkRotationData, above.
    Game_CharacterBase.prototype.rotate = function(rate, revs) {
        this._rotationData = mkRotationData(rate, revs);
    };

    // New: update character rotation. See Sprite_Character#updateOther, above.
    Sprite_Character.prototype.updateRotation = function() {
        const DATA = this._character?._rotationData;
        if (DATA) {
            const REV = 360;
            updatePivot(this);
            this.angle += DATA.rate;
            if (Math.abs(DATA.turn += DATA.rate) >= REV) {
                DATA.turn -= REV;
                if (--DATA.revs === 0) {            // allow indefinite
                    this._character.rotate(0);
                }
            }
        }
    };

})();
Could probably be improved, but it seems to work OK~ :kaophew:
Fantastic! I'll give it a whirl. Thank you, @caethyril !
 

caethyril

^_^
Global Mod
Joined
Feb 21, 2018
Messages
3,923
Reaction score
2,980
First Language
EN
Primarily Uses
RMMZ
You're welcome! :kaohi:

Edit:

This thread is being closed, due to being solved. If for some reason you would like this thread re-opened, please report this post and leave a message why. Thank you.

 
Last edited:
Status
Not open for further replies.

Latest Threads

Latest Profile Posts

DamageEvil_5.png added!
index.php

Rendering the intro for the 4 time today, everytime it finished I noticed something that I found off and had to fix it and render it all over again...
Would not be so bad if it was not for my PC having the speed of a slug.:kaoswt2:
Tried something. Little sneak peak of Unity AD.
M42S7Us.gif

It's the little things. Lengthened the time the bar fades in and out to be a bit more comfortable.

Forum statistics

Threads
125,738
Messages
1,173,121
Members
164,888
Latest member
EternalLord
Top