I’m looking for hints on how to achieve a radial button cooldown effect, like it’s done in Diablo III for example:
The basic technique is a overlay with transparency, but I am stumped on how to achieve the rotating effect.
I’m looking for hints on how to achieve a radial button cooldown effect, like it’s done in Diablo III for example:
The basic technique is a overlay with transparency, but I am stumped on how to achieve the rotating effect.
I would personally just add an animated sprite on top
But maybe some geometry experts have another way?
[edit]
Yeah, after looking at a few examples, it’s clearly possible. Look up for how to draw arc
s (like this one)
Here’s another example for phaser 2 that is very close to what you want to achieve: here
Thanks for pointing me to arc - that seems really close to the solution
I’ll just have to mask it somehow, so that only the part above the button is visible.
If I make progress, I’ll report back.
Using arc - but with fillpath instead of stroke path and then masking it to the size of the inner part of the button I got this:
Because I got a private message about how I solved it, here is my code for the skill butons (typescript) - usage of arc see “update” function.
class SkillButton {
private cdColor = 0x362E3F;
private cooldownEffect: Phaser.GameObjects.Graphics;
private cooldownTime = 0;
private activationTime = 0;
private button: Phaser.GameObjects.Sprite;
private icon: Phaser.GameObjects.Sprite;
private bindOverlay: Phaser.GameObjects.Sprite;
private bindOverlayText: Phaser.GameObjects.BitmapText;
skillId: number;
private readonly rectMask: Phaser.GameObjects.Graphics;
down = false;
constructor(private scene: Phaser.Scene, private action: UIConsumer, x: number, y: number, bindDisplay: string) {
this.button = scene.add.sprite(x, y, "sprites", "buttonEmpty");
this.icon = scene.add.sprite(x + 2, y - 2, "sprites", "skill/none");
let overlay = "ui/keybindBlank";
if (bindDisplay == "M1") {
overlay = "ui/keybindMouseLeft";
bindDisplay = "";
} else if (bindDisplay == "M2") {
overlay = "ui/keybindMouseRight";
bindDisplay = "";
}
let offsetX = 18;
let offsetY = 0;
this.bindOverlay = scene.add.sprite(x + offsetX, y + offsetY, "sprites", overlay);
this.bindOverlayText = scene.add.bitmapText(x + offsetX - 2, y + offsetY - 6, "font", bindDisplay, 10);
this.button.setScrollFactor(0, 0);
this.button.depth = UI_DEPTH_INGAME;
this.button.setInteractive();
this.button.on("pointerover", this.pointerOver, this);
this.button.on("pointerout", this.pointerOut, this);
this.button.on("pointerup", this.pointerUp, this);
this.button.on("pointerdown", this.pointerDown, this);
this.icon.setScrollFactor(0, 0);
this.icon.depth = UI_DEPTH_INGAME + 1;
this.bindOverlay.setScrollFactor(0, 0);
this.bindOverlay.depth = UI_DEPTH_INGAME + 2;
this.bindOverlayText.setScrollFactor(0, 0);
this.bindOverlayText.depth = UI_DEPTH_INGAME + 3;
this.cooldownEffect = scene.add.graphics();
this.cooldownEffect.setPosition(x, y);
this.cooldownEffect.setScrollFactor(0, 0);
this.cooldownEffect.depth = UI_DEPTH_INGAME + 2;
this.rectMask = new Phaser.GameObjects.Graphics(scene);
this.rectMask.fillStyle(0, 1);
this.rectMask.fillRect(0, 0, 16, 16);
this.rectMask.setPosition(x - 8, y - 8);
this.rectMask.setScrollFactor(0, 0);
this.cooldownEffect.mask = new Phaser.Display.Masks.GeometryMask(scene, this.rectMask);
this.setOrigin(0, 1);
}
pointerOver() {
if (this.skillId != undefined) {
this.button.setFrame("buttonHighlight");
}
}
pointerOut() {
this.button.setFrame("buttonEmpty");
}
pointerUp() {
if (this.down) {
this.down = false;
if (this.skillId == DefTypes.SKILL_ID_INVENTROY) {
this.action.inventoryToggle();
} else if (this.skillId == DefTypes.SKILL_ID_PORTAL) {
this.action.castPortal();
} else {
//todo available skills zuweisen (button der ihn bisher hatte skill wegnehmen)
//todo server informieren client-settings (müssen bei welcome zurück kommen)
}
}
}
pointerDown(pointer, x, y, event) {
this.down = true;
}
//todo aufrufen, wenn network das erste mal die available rausrückt (erstmal)
assignSkillId(skillId) {
if (this.skillId != skillId) {
this.skillId = skillId;
if (skillId == undefined) {
this.icon.setFrame("skill_none");
} else {
this.icon.setFrame(ClientDefs.getSkillIcon(skillId));
}
}
}
update(time: number) {
let elapsed = time - this.activationTime;
let disabled = false;
if (this.cooldownTime == -1) {
disabled = true;
}
if (!disabled && elapsed > this.cooldownTime) {
this.cooldownEffect.visible = false;
} else {
this.cooldownEffect.visible = true;
let render = 1;
if (!disabled) {
render = Math.floor(360 * elapsed / this.cooldownTime);
}
this.cooldownEffect.clear();
this.cooldownEffect.fillStyle(this.cdColor, 0.4);
this.cooldownEffect.moveTo(0, 0);
this.cooldownEffect.arc(0, 0, 16, Phaser.Math.DegToRad(270), Phaser.Math.DegToRad(render - 90), true);
this.cooldownEffect.fillPath();
}
}
activate(tick: number, cooldownTimeInMs: number) {
this.cooldownTime = cooldownTimeInMs;
this.activationTime = tick;
}
setOrigin(x: number, y: number) {
this.button.setOrigin(x, y);
this.icon.setOrigin(x, y);
this.rectMask.x = this.button.x - (x - 0.5) * 20 - 8;
this.rectMask.y = this.button.y - (y - 0.5) * 20 - 8;
this.cooldownEffect.x = this.button.x - (x - 0.5) * 20;
this.cooldownEffect.y = this.button.y - (y - 0.5) * 20;
}
isReady() {
return this.skillId != undefined && !this.cooldownEffect.visible;
}
}