fix: use BitmapMask inside container so image follows card during animations
This commit is contained in:
1 parent
0a8c89610b
commit
4d09102464
1 file changed
+57
-65
@@ -19,6 +19,7 @@ export class CardObject extends Phaser.GameObjects.Container {
|
||||
this.cardData = cardData
|
||||
this._selected = false
|
||||
this._baseY = y
|
||||
this._maskImg = null // référence au masque pour le détruire proprement
|
||||
this._build()
|
||||
scene.add.existing(this)
|
||||
}
|
||||
@@ -38,67 +39,61 @@ export class CardObject extends Phaser.GameObjects.Container {
|
||||
this.add(shadow)
|
||||
|
||||
// ── 2. Fond couleur équipe ────────────────────────────────────────────────
|
||||
const bgColor = this._darken(teamHex, 0.25)
|
||||
const bg = this.scene.add.graphics()
|
||||
bg.fillStyle(bgColor)
|
||||
bg.fillStyle(this._darken(teamHex, 0.25))
|
||||
bg.fillRoundedRect(-CARD_W / 2, -CARD_H / 2, CARD_W, CARD_H, RADIUS)
|
||||
this.add(bg)
|
||||
|
||||
// ── 3. Image portrait via RenderTexture (reste solidaire du container) ───
|
||||
// ── 3. Image portrait ─────────────────────────────────────────────────────
|
||||
if (hasTexture) {
|
||||
// RenderTexture aux dimensions de la carte — clippe naturellement
|
||||
const rt = this.scene.add.renderTexture(
|
||||
-CARD_W / 2, -CARD_H / 2, CARD_W, CARD_H
|
||||
)
|
||||
|
||||
// Dessiner le fond arrondi dans la RT (pour que les coins soient propres)
|
||||
const rtBg = this.scene.make.graphics({ x: 0, y: 0, add: false })
|
||||
rtBg.fillStyle(bgColor)
|
||||
rtBg.fillRoundedRect(0, 0, CARD_W, CARD_H, RADIUS)
|
||||
rt.draw(rtBg, 0, 0)
|
||||
rtBg.destroy()
|
||||
|
||||
// Calculer le scale "cover" et le centrage dans la RT
|
||||
const frame = this.scene.textures.get(textureKey).getSourceImage()
|
||||
const scaleX = CARD_W / frame.width
|
||||
const scaleY = CARD_H / frame.height
|
||||
const scale = Math.max(scaleX, scaleY)
|
||||
const dw = frame.width * scale
|
||||
const dh = frame.height * scale
|
||||
const ox = (CARD_W - dw) / 2
|
||||
const oy = (CARD_H - dh) / 2
|
||||
|
||||
// Créer un Image temporaire pour dessiner dans la RT
|
||||
const tmpImg = this.scene.make.image({ key: textureKey, add: false })
|
||||
tmpImg.setScale(scale)
|
||||
rt.draw(tmpImg, ox + dw / 2, oy + dh / 2)
|
||||
tmpImg.destroy()
|
||||
const img = this.scene.add.image(0, 0, textureKey)
|
||||
img.setScale(scale)
|
||||
this.add(img)
|
||||
|
||||
// Dégradé haut (pour lisibilité des badges)
|
||||
const gradT = this.scene.make.graphics({ x: 0, y: 0, add: false })
|
||||
gradT.fillGradientStyle(0x000000, 0x000000, 0x000000, 0x000000, 0.75, 0.75, 0, 0)
|
||||
gradT.fillRect(0, 0, CARD_W, BADGE + 10)
|
||||
rt.draw(gradT, 0, 0)
|
||||
gradT.destroy()
|
||||
// Masque arrondi rendu dans une texture offline, puis appliqué comme BitmapMask
|
||||
// Le BitmapMask suit le container automatiquement car on l'attache à un objet du container
|
||||
const maskKey = `mask-card`
|
||||
if (!this.scene.textures.exists(maskKey)) {
|
||||
const maskGfx = this.scene.make.graphics({ x: 0, y: 0, add: false })
|
||||
maskGfx.fillStyle(0xffffff)
|
||||
maskGfx.fillRoundedRect(0, 0, CARD_W, CARD_H, RADIUS)
|
||||
maskGfx.generateTexture(maskKey, CARD_W, CARD_H)
|
||||
maskGfx.destroy()
|
||||
}
|
||||
|
||||
// Dégradé bas (pour lisibilité du titre)
|
||||
const gradB = this.scene.make.graphics({ x: 0, y: 0, add: false })
|
||||
gradB.fillGradientStyle(0x000000, 0x000000, 0x000000, 0x000000, 0, 0, 0.85, 0.85)
|
||||
gradB.fillRect(0, CARD_H - TITLE_H - 12, CARD_W, TITLE_H + 12)
|
||||
rt.draw(gradB, 0, 0)
|
||||
gradB.destroy()
|
||||
// L'image masque est ajoutée AU container → elle suit ses mouvements
|
||||
this._maskImg = this.scene.add.image(0, 0, maskKey)
|
||||
this._maskImg.setVisible(false)
|
||||
this.add(this._maskImg)
|
||||
|
||||
this.add(rt)
|
||||
img.setMask(this._maskImg.createBitmapMask())
|
||||
}
|
||||
|
||||
// ── 4. Bordure couleur équipe ─────────────────────────────────────────────
|
||||
// ── 4. Dégradé haut (lisibilité badges) ──────────────────────────────────
|
||||
const gradT = this.scene.add.graphics()
|
||||
gradT.fillGradientStyle(0x000000, 0x000000, 0x000000, 0x000000, 0.75, 0.75, 0, 0)
|
||||
gradT.fillRect(-CARD_W / 2, -CARD_H / 2, CARD_W, BADGE + 10)
|
||||
this.add(gradT)
|
||||
|
||||
// ── 5. Dégradé bas (lisibilité titre) ────────────────────────────────────
|
||||
const gradB = this.scene.add.graphics()
|
||||
gradB.fillGradientStyle(0x000000, 0x000000, 0x000000, 0x000000, 0, 0, 0.85, 0.85)
|
||||
gradB.fillRect(-CARD_W / 2, CARD_H / 2 - TITLE_H - 12, CARD_W, TITLE_H + 12)
|
||||
this.add(gradB)
|
||||
|
||||
// ── 6. Bordure couleur équipe ─────────────────────────────────────────────
|
||||
const border = this.scene.add.graphics()
|
||||
border.lineStyle(2.5, teamHex, 1)
|
||||
border.strokeRoundedRect(-CARD_W / 2, -CARD_H / 2, CARD_W, CARD_H, RADIUS)
|
||||
this.add(border)
|
||||
|
||||
// ── 5. Titre en BAS ───────────────────────────────────────────────────────
|
||||
const nameText = this.scene.add.text(
|
||||
// ── 7. Titre en BAS ───────────────────────────────────────────────────────
|
||||
this.add(this.scene.add.text(
|
||||
0, CARD_H / 2 - TITLE_H / 2,
|
||||
this.cardData.name, {
|
||||
fontSize: '12px',
|
||||
@@ -106,19 +101,17 @@ export class CardObject extends Phaser.GameObjects.Container {
|
||||
fontStyle: 'bold',
|
||||
color: '#f5e6c8',
|
||||
stroke: '#000000',
|
||||
strokeThickness: 3,
|
||||
backgroundColor: undefined // pas de fond blanc
|
||||
strokeThickness: 3
|
||||
}
|
||||
).setOrigin(0.5, 0.5)
|
||||
this.add(nameText)
|
||||
).setOrigin(0.5, 0.5))
|
||||
|
||||
// ── 6. Badges attributs dans les 4 coins ─────────────────────────────────
|
||||
// ── 8. Badges attributs dans les 4 coins ─────────────────────────────────
|
||||
this._addCornerBadge('food', -CARD_W / 2, -CARD_H / 2, 'topLeft')
|
||||
this._addCornerBadge('war', CARD_W / 2, -CARD_H / 2, 'topRight')
|
||||
this._addCornerBadge('health', -CARD_W / 2, CARD_H / 2, 'bottomLeft')
|
||||
this._addCornerBadge('diplo', CARD_W / 2, CARD_H / 2, 'bottomRight')
|
||||
|
||||
// ── 7. Interactivité ──────────────────────────────────────────────────────
|
||||
// ── 9. Interactivité ──────────────────────────────────────────────────────
|
||||
this.setSize(CARD_W, CARD_H)
|
||||
this.setInteractive({ cursor: 'pointer' })
|
||||
this.on('pointerover', this._onHover, this)
|
||||
@@ -157,15 +150,13 @@ export class CardObject extends Phaser.GameObjects.Container {
|
||||
fontStyle: 'bold',
|
||||
color: '#ffffff',
|
||||
stroke: '#000000',
|
||||
strokeThickness: 2,
|
||||
backgroundColor: undefined
|
||||
strokeThickness: 2
|
||||
}).setOrigin(0.5, 0.5))
|
||||
|
||||
this.add(this.scene.add.text(cx, cy + 9, label, {
|
||||
fontSize: '6px',
|
||||
fontFamily: 'Arial, sans-serif',
|
||||
color: '#ffffffdd',
|
||||
backgroundColor: undefined
|
||||
fontSize: '6px',
|
||||
fontFamily:'Arial, sans-serif',
|
||||
color: '#ffffffdd'
|
||||
}).setOrigin(0.5, 0.5))
|
||||
}
|
||||
|
||||
@@ -173,17 +164,13 @@ export class CardObject extends Phaser.GameObjects.Container {
|
||||
|
||||
_onHover() {
|
||||
if (this._selected) return
|
||||
this.scene.tweens.add({
|
||||
targets: this, y: this._baseY - 18, duration: 120, ease: 'Power2'
|
||||
})
|
||||
this.scene.tweens.add({ targets: this, y: this._baseY - 18, duration: 120, ease: 'Power2' })
|
||||
this.setDepth(10)
|
||||
}
|
||||
|
||||
_onOut() {
|
||||
if (this._selected) return
|
||||
this.scene.tweens.add({
|
||||
targets: this, y: this._baseY, duration: 120, ease: 'Power2'
|
||||
})
|
||||
this.scene.tweens.add({ targets: this, y: this._baseY, duration: 120, ease: 'Power2' })
|
||||
this.setDepth(0)
|
||||
}
|
||||
|
||||
@@ -199,17 +186,22 @@ export class CardObject extends Phaser.GameObjects.Container {
|
||||
select() {
|
||||
this._selected = true
|
||||
this.setDepth(20)
|
||||
this.scene.tweens.add({
|
||||
targets: this, y: this._baseY - 28, duration: 150, ease: 'Back.Out'
|
||||
})
|
||||
this.scene.tweens.add({ targets: this, y: this._baseY - 28, duration: 150, ease: 'Back.Out' })
|
||||
}
|
||||
|
||||
deselect() {
|
||||
this._selected = false
|
||||
this.setDepth(0)
|
||||
this.scene.tweens.add({
|
||||
targets: this, y: this._baseY, duration: 150, ease: 'Power2'
|
||||
})
|
||||
this.scene.tweens.add({ targets: this, y: this._baseY, duration: 150, ease: 'Power2' })
|
||||
}
|
||||
|
||||
destroy(fromScene) {
|
||||
// Nettoyer le masque proprement
|
||||
if (this._maskImg) {
|
||||
this._maskImg.destroy()
|
||||
this._maskImg = null
|
||||
}
|
||||
super.destroy(fromScene)
|
||||
}
|
||||
|
||||
// ── Utilitaires ───────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in new issue
Block a user