Делаем нестандартный progress/health bar

Сделать простой хелсбар очень просто - все что нужно это один спрайт для фона и еще один спрайт для отображения текущего значения. После этого можно задать спрайту, отображающему текущее значение, горизонтальное масштабирование по формуле: текущее значение / максимальное значение.

Сделаем маленький пример для тестирования хелсбара. Сначала отрисуем фон, затем добавим наш будущий хелсбар и кнопку, которая будет задавать хелсбару случайное значение. Для кнопки я использовал PushButton из пакета minimalcomps но вы можете навесить событие клика прямо на stage.

Также, для того чтобы лучше протестировать наш хелсбар, добавим слушатель события ENTER_FRAME в котором будем увеличивать значение хелсбара, если оно меньше максимального значения, и будем обновлять отрисовку хелсбара с новым значением.

Код Example.as:

package {
    import flash.display.*;
    import flash.events.*;
    import flash.geom.*;
    import flash.text.*;
    
    import com.bit101.components.*;
    
    
    [SWF(width="660", height="260", backgroundColor="0xeeeeee", frameRate="30")]
    public class Example extends Sprite {
        private const WIDTH:int = 660;
        private const HEIGHT:int = 260;
       
        private var bar:Bar;
        private var healthLabel:Label;
        private var damageButton:PushButton;
       

        public function Example() {
            var matrix:Matrix = new Matrix();
            matrix.createGradientBox(660, 660, 0, 0, -200);
           
            graphics.beginGradientFill(GradientType.RADIAL, [0xe0e0e0, 0xd0d0d0], [1, 1], [0, 255], matrix);
            graphics.drawRect(0, 0, WIDTH, HEIGHT);
            graphics.endFill();
           
            bar = new Bar();
            bar.x = 330 - bar.width / 2;
            bar.y = 130 - bar.height / 2;
            bar.maximum = 100;
            bar.value = 100;
            stage.addChild(bar);
           
            healthLabel = new Label(stage, 330, 40, "Health: "+bar.value);
            healthLabel.move(330 - healthLabel.width / 2, 40);

            damageButton = new PushButton(stage, 285, 200, "Damage", damage);
            damageButton.setSize(90, 20);
           
            stage.addEventListener(Event.ENTER_FRAME, update);
        }
       
        private function damage(event:MouseEvent):void {
            bar.value = int(Math.random() * 50);
        }
       
        private function update(event:Event):void {
            if (bar.value < bar.maximum) {
                bar.value++;
                healthLabel.text = "Health: "+bar.value;
            }
        }
    }
}

Теперь можно сделать сам хелсбар. Хелсбар будет наследовать от Sprite и у него будет два Shape для отрисовки нужной нам графики: bg для фона и bar для отображения текущего значения. Также у него будут геттеры/сеттеры для максимального и текущего значения, и приватный метод update, который будет задавать горизонтальное масштабирование для bar.

Код Bar.as:

package {
    import flash.display.*;
    import flash.geom.*;
    

    public class Bar extends Sprite {
        private const WIDTH:int = 300;
        private const HEIGHT:int = 8;
       
        private var bg:Shape;
        private var bar:Shape;
       
        private var _value:Number = 0;
        private var _max:Number = 1;
       
       
        public function Bar() {
            bg = new Shape();
            bg.graphics.beginFill(0x222222);
            bg.graphics.drawRect(0, 0, WIDTH, HEIGHT);
            bg.graphics.endFill();
            addChild(bg);
           
            bar = new Shape();
            bar.graphics.beginFill(0xff0060);
            bar.graphics.drawRect(0, 0, WIDTH, HEIGHT);
            bar.graphics.endFill();
            addChild(bar);
        }
       
        private function update():void {
            bar.scaleX = _value / _max;
        }
       
        public function set maximum(m:Number):void {
            _max = m;
            _value = Math.min(_value, _max);
            update();
        }
       
        public function get maximum():Number {
            return _max;
        }

        public function set value(v:Number):void {
            _value = Math.min(v, _max);
            update();
        }
       
        public function get value():Number {
            return _value;
        }
    }
}

Тестируем:

Работает очень хорошо. Но что, если мы захотим хелсбар с закругленными углами или с текстурой? Добавим нашему хелсбару градиент и закругленные уголки:

Смотрится не очень красиво. Чтобы это исправить мы добавим в хелсбар еще один Shape с именем cover, который будет маской для bar. Теперь, для отображения текущего значения, мы будет задавать горизонтальное масштабирование для cover, а не для bar.

Новый код Bar.as:

package {
    import flash.display.*;
    import flash.geom.*;
    

    public class Bar extends Sprite {
        private const WIDTH:int = 300;
        private const HEIGHT:int = 30;
        private const CORNER:int = 30;
       
        private var bg:Shape;
        private var bar:Shape;
        private var cover:Shape;
       
        private var _value:Number = 0;
        private var _max:Number = 1;
       
       
        public function Bar() {
            bg = new Shape();
            bg.graphics.beginFill(0x333333);
            bg.graphics.drawRoundRect(0, 0, WIDTH, HEIGHT, CORNER);
            bg.graphics.endFill();
            addChild(bg);

            var matrix:Matrix = new Matrix();
            matrix.createGradientBox(WIDTH / 2, HEIGHT);
           
            bar = new Shape();
            bar.graphics.beginGradientFill(GradientType.LINEAR, [0x4488ff, 0x00c8ff], [1, 1], [0, 255], matrix, SpreadMethod.REFLECT);
            bar.graphics.drawRoundRect(0, 0, WIDTH, HEIGHT, CORNER);
            bar.graphics.endFill();
            addChild(bar);
           
            cover = new Shape();
            cover.graphics.beginFill(0xffffff);
            cover.graphics.drawRect(0, 0, WIDTH, HEIGHT);
            cover.graphics.endFill();
            addChild(cover);
           
            bar.mask = cover;
        }
       
        private function update():void {
            cover.scaleX = _value / _max;
        }
       
        public function set maximum(m:Number):void {
            _max = m;
            _value = Math.min(_value, _max);
            update();
        }
       
        public function get maximum():Number {
            return _max;
        }

        public function set value(v:Number):void {
            _value = Math.min(v, _max);
            update();
        }
       
        public function get value():Number {
            return _value;
        }
    }
}

Тестируем:

Смотрится намного лучше. Для bar и bg вместо Shape вы также можете использовать изображения, спрайты из библиотеки или анимированые MovieClip:

Ваш хелсбар может иметь нестандартную форму или может быть вертикальным, а не горизонтальным: