NoxBrutalis Posted July 18, 2018 Share Posted July 18, 2018 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 More sharing options...
rich Posted July 18, 2018 Share Posted July 18, 2018 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 More sharing options...
NoxBrutalis Posted July 18, 2018 Author Share Posted July 18, 2018 (edited) 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 Edited July 18, 2018 by NoxBrutalis clarification Link to comment Share on other sites More sharing options...
rich Posted July 18, 2018 Share Posted July 18, 2018 Arse, I know what it is! https://github.com/photonstorm/phaser/commit/ab35dfab95a698bb7bed573eaad0bd6ac3b5cae5 This will be fixed in 3.12 (it's in the master branch now if you want to build it from there). Sorry about that NoxBrutalis 1 Link to comment Share on other sites More sharing options...
NoxBrutalis Posted July 18, 2018 Author Share Posted July 18, 2018 Hey Rich, no problem. Thanks for the swift reply, and I'm only happy I could help Link to comment Share on other sites More sharing options...
Recommended Posts