Jump to content

setCrop not working as expected


NoxBrutalis
 Share

Recommended Posts

Hi everyone. I'm currently working on health and stamina bars for my game. What I do is have two bar graphics for each bar - a front bar and back bar. In the example of the health bar, the front bar is green and the back one is red. This is so that when health depletes, I can shrink the front bar and it shows how much health the player has out of its maximum health - if that makes sense.

At first I was using this.my_sprite.frame.width to manipulate the the visible part of the front bar. And this works okay, except when the health goes to lower values, the sprite seemed to sort of truncate. By that I mean that both ends of the bar are rounded, but when the health dropped low, the left edge of the image became flat rather than round. This was only a minor bother, but still, I remembered reading something in the latest release about setCrop.

So I tried with setCrop, and the images maintain the desired shape - they don't truncate like when using the frames width, however the setCrops seemed to be interfering with each other, because all the bars were using the same sprite sheet. So I got to a point where my stamina use was draining my health, and taking damage was affecting stamina,  where both were affecting both.

In an attempt to hack around this, I split my sprite sheet into two sprite sheets so the stamina and health bars were on separate textures, and they still interacted as if they were the same crop. I am so confused by this right now. It's similar to the way sounds work, in that when you have a sound playing and another object plays the same sound at a lower volume, that volume affects the first instance of the sound being played. Which is pretty weird to me. I understand only having the one object for performance, but it seems like having to dance around this too much in game code.

I've also tried using different frames, making named frames, adding them and using those, and making sure to set the frame on the sprite itself. 

This is my UI class in it's entirety:
 

class Ui extends Phaser.Scene
{
    constructor()
    {
        super({key:'Ui'});
        this.health;
        this.stamina;
        this.green_bar;
        this.green_bar_width;
        this.red_bar;
        this.yellow_bar;
        this.yellow_bar_width;
        this.orange_bar;
        this.isLoaded = false;

        this.updateHealth = function(health)
        {
            this.health = health;
            if(this.isLoaded)
            {
                if(this.health > 0)
                {
                    this.green_bar_width = this.health * 100 / 100;
                    var repeatHealthVal = Math.abs((this.green_bar_width - this.green_bar.frame.cutWidth) - 1);
                    var h_conf = {
                        callback: function(){
                            if(this.green_bar.frame.cutWidth > this.green_bar_width)
                            {
                                
                                this.green_bar.frame.cutWidth -= 1;
                                this.green_bar.setCrop(0, 0, this.green_bar.frame.cutWidth, 32);

                            }
                            else if(this.green_bar.frame.cutWidth < this.green_bar_width)
                            {
                                
                                this.green_bar.frame.cutWidth += 1;
                                this.green_bar.setCrop(0, 0, this.green_bar.frame.cutWidth, 32);
                            }
                        },
                        delay: 1,
                        repeat: repeatHealthVal,
                        callbackScope: this
                    }
                    this.time.addEvent(new Phaser.Time.TimerEvent(h_conf));
                }
                else
                {
                    this.green_bar.frame.cutWidth = 0;
                }
            }
        }
        this.updateStamina = function(stamina)
        {
            this.stamina = stamina;
            if(this.isLoaded)
            {
                if(this.stamina > 0)
                {
                    this.yellow_bar_width = this.stamina * 100 / 100;
                    var repeatStamVal = Math.abs((this.yellow_bar_width - this.yellow_bar.frame.cutWidth) - 1);
                    var s_conf = {
                        callback: function(){
                            if(this.yellow_bar.frame.cutWidth > this.yellow_bar_width)
                            {
                                
                                this.yellow_bar.frame.cutWidth -= 1;
                                this.yellow_bar.setCrop(0, 0, this.yellow_bar.frame.cutWidth, 32);
                            }
                            else if(this.yellow_bar.frame.cutWidth < this.yellow_bar_width)
                            {
                                
                                this.yellow_bar.frame.cutWidth += 1;
                                this.yellow_bar.setCrop(0, 0, this.yellow_bar.frame.cutWidth, 32);
                            }
                        },
                        delay: 1,
                        repeat: repeatStamVal,
                        callbackScope: this
                    }
                    this.time.addEvent(new Phaser.Time.TimerEvent(s_conf));
                }
                else
                {
                    this.yellow_bar.frame.cutWidth = 0;
                }
            }
        }
    }

    preload()
    {
        this.load.spritesheet('health_bar', 'assets/health_bar.png', {frameWidth: 128, frameHeight: 32});
        this.load.spritesheet('stamina_bar', 'assets/stamina_bar.png', {frameWidth: 128, frameHeight: 32});
    }

    create()
    {
        this.red_bar = this.add.sprite(80, 32, 'health_bar', 1);
        this.green_bar = this.add.sprite(80, 32, 'health_bar', 0);


        this.textures.get('health_bar').add('front_bar', 0, 0, 0, 128, 32);
        this.textures.get('health_bar').add('back_bar', 0, 0, 32, 128, 32);
        this.red_bar.setFrame('back_bar');
        this.green_bar.setFrame('front_bar');

        this.orange_bar = this.add.sprite(80, 80, 'stamina_bar', 1);
        this.yellow_bar = this.add.sprite(80, 80, 'stamina_bar', 0);
        
        this.textures.get('stamina_bar').add('front_bar', 0, 0, 0, 128, 32);
        this.textures.get('stamina_bar').add('back_bar', 0, 0, 32, 128, 32);
        this.orange_bar.setFrame('back_bar');
        this.yellow_bar.setFrame('front_bar');


        this.isLoaded = true;

    }
    update(time, delta)
    {
    

    }
}

the update health and stamina functions get called by things in the game. player taking damage etc. The stamina gets updated when the player sprints, but the player also has an infinite timed event that is basically a stamina regen tick. 10 stamina every second.

Anyways, sorry for the long post, thanks for your time.

 

Link to comment
Share on other sites

In the code above you are changing the frames `cutWidth` value. This will change the width of the frame for every single Sprite using that frame. And then you're using setCrop as well.

setCrop works on a per-Sprite basis, so cropping one sprite will never impact another Sprite, even if it's using the exact same frame. But what you're doing at the moment undoes that benefit, because you're messing with the frame data directly, which will screw up the crop calculations and impact every sprite using the frame. You should really be using setCrop only, and nothing else.

Link to comment
Share on other sites

Hello, thanks for the reply Richard. I have tried your suggestion tho to no avail. I have created a somewhat minimal example replicating my issue. I have attached a zip folder that contains the whole minimal project. It has an index.html, the assets and scripts. If you're uncomfortable opening random zips, then please find the code pasted below and images attached for recreation of the project if needed.

index: 
 

<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Phaser 3 setCrop test</title>
<script src = "js/phaser.js"></script>
<script src = "js/Ui.js"></script>
</head>
<body>
</body>
</html>

js file:

 

class Ui extends Phaser.Scene
{
    constructor()
    {
        super({key:'Ui'});
        this.health = 150;
        this.stamina = 150;
        this.green_bar;
        this.green_bar_width;
        this.green_bar_current_width;
        this.red_bar;
        this.yellow_bar;
        this.yellow_bar_width;
        this.yellow_bar_current_width;
        this.orange_bar;
        this.isLoaded = false;

        this.updateHealth = function(health)
        {
            this.health = health;
            if(this.isLoaded)
            {
                if(this.health > 0)
                {
                    this.green_bar_width = this.health * 100 / 100;
                    var repeatHealthVal = Math.abs((this.green_bar_width - this.green_bar_current_width) - 1);
                    var h_conf = {
                        callback: function(){
                            if(this.green_bar_current_width > this.green_bar_width)
                            {
                                
                                this.green_bar_current_width -= 1;
                                this.green_bar.setCrop(0, 0, this.green_bar_current_width, 32);

                            }
                            else if(this.green_bar_current_width < this.green_bar_width)
                            {
                                
                                this.green_bar_current_width += 1;
                                this.green_bar.setCrop(0, 0, this.green_bar_current_width, 32);
                            }
                        },
                        delay: 1,
                        repeat: repeatHealthVal,
                        callbackScope: this
                    }
                    this.time.addEvent(new Phaser.Time.TimerEvent(h_conf));
                }
                else
                {
                    this.green_bar_current_width = 0;
                    this.green_bar.setCrop(0, 0, this.green_bar_current_width, 32);
                }
            }
        }
        this.updateStamina = function(stamina)
        {
            this.stamina = stamina;
            if(this.isLoaded)
            {
                if(this.stamina > 0)
                {
                    this.yellow_bar_width = this.stamina * 100 / 100;
                    var repeatStamVal = Math.abs((this.yellow_bar_width - this.yellow_bar_current_width) - 1);
                    var s_conf = {
                        callback: function(){
                            if(this.yellow_bar_current_width > this.yellow_bar_width)
                            {
                                
                                this.yellow_bar_current_width -= 1;
                                this.yellow_bar.setCrop(0, 0, this.yellow_bar_current_width, 32);
                            }
                            else if(this.yellow_bar_current_width < this.yellow_bar_width)
                            {
                                
                                this.yellow_bar_current_width += 1;
                                this.yellow_bar.setCrop(0, 0, this.yellow_bar_current_width, 32);
                            }
                        },
                        delay: 1,
                        repeat: repeatStamVal,
                        callbackScope: this
                    }
                    this.time.addEvent(new Phaser.Time.TimerEvent(s_conf));
                }
                else
                {
                    this.yellow_bar_current_width = 0;
                    this.yellow_bar.setCrop(0, 0, this.yellow_bar_current_width, 32);
                }
            }
        }
    }

    preload()
    {
        this.load.spritesheet('health_bar', 'assets/health_bar.png', {frameWidth: 128, frameHeight: 32});
        this.load.spritesheet('stamina_bar', 'assets/stamina_bar.png', {frameWidth: 128, frameHeight: 32});
    }

    create()
    {
        this.red_bar = this.add.sprite(80, 32, 'health_bar', 1);
        this.green_bar = this.add.sprite(80, 32, 'health_bar', 0);
        this.orange_bar = this.add.sprite(80, 80, 'stamina_bar', 1);
        this.yellow_bar = this.add.sprite(80, 80, 'stamina_bar', 0);

        this.green_bar_current_width = 128;
        this.yellow_bar_current_width = 128;

        this.controls = {
            healthUp: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.W),
            healthDown: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S),
            staminaUp: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A),
            staminaDown: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D)
        }
        this.isLoaded = true;

    }
    update(time, delta)
    {
        if(this.controls.healthUp.isDown)
        {
            this.updateHealth(this.health += 1);
            console.log('health up');
        }
        if(this.controls.healthDown.isDown)
        {
            this.updateHealth(this.health -= 1);
            console.log('health down');
        }
        if(this.controls.staminaUp.isDown)
        {
            this.updateStamina(this.stamina += 1);
            console.log('stamina up');
        }
        if(this.controls.staminaDown.isDown)
        {
            this.updateStamina(this.stamina -= 1);
            console.log('stamina down');
        }

    }
}
var config = {
    type: Phaser.WEBGL,
    physics: {
        default: 'arcade',
        arcade: {
            gravity: { y: 0 },
            debug: false
        }
    },
    width: 800,
    height: 600,
    backgroundColor: '#2d2d2d',
    pixelArt: true,
    scene: [Ui]
};

game = new Phaser.Game(config);

 

this is 'more minimal' because I've taken out the possibility that the crop interference was from outside of the ui code. To do this, I made the ui a standalone scene, and added wasd controls that call the updates for the health and stamina bars. W keys puts health up, S key lowers it, A key increases stamina, D key lowers it. At first they work fine if you just use either stamina or health, but once you press keys that correspond to the other bar, the bars are both having the same crop applied to them, even though they are different sprites, in this case with different textures.

thanks again for your time.

testCrop.zip

health_bar.png

stamina_bar.png

Edited by NoxBrutalis
clarification
Link to comment
Share on other sites

 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...