PART 3: POLISH & EFFECTS - 100% COMPLETE! (Phase 29)
COMPLETED FEATURES: PART 1: IMMEDIATE INTEGRATION (30 min) - Crafting system integration verified - Created comprehensive test plans - INTEGRATION_TEST_PLAN.md - QUICK_START_TEST.md PART 3: POLISH & EFFECTS (2h 5min) - 100% DONE! Phase 5C: Lighting & Shadows (20 min) - LightingSystem.js (215 lines) - Dynamic player shadow with time-of-day opacity - Auto-torch at night (flickering effect) - Campfire creation API - Light source management Phase 5B: Enhanced Weather (25 min) - WeatherEnhancementsSystem.js (245 lines) - Dynamic wind system (strength + direction) - Wind affects rain particles - Tree sway animations - Smooth weather transitions (2s fade) - Wind info API (speed km/h, compass) Phase 5D: UI Polish (20 min) - UIPolishSystem.js (330 lines) - Fade in/out & slide animations - Button hover effects with sound - Tooltips (auto + manual, cursor follow) - Pulse, shake, flash animations - Typewriter text effect - Number counter animation - Smooth scroll support Phase 5E: Particle Effects (30 min) - ParticleEnhancementsSystem.js (450 lines) - Craft sparkles (golden burst) - Walk dust clouds (grass/dirt only) - Harvest bursts (crop-colored!) - Dig/till soil particles - Plant sparkles - Level up / damage / heal effects - Integrated with CraftingSystem & FarmingSystem STATS: - 4 new systems created (~1,240 lines) - 5 documentation files - 30+ new features - 7 files modified - Total time: 2h 35min GAME NOW HAS: - Dynamic shadows & lighting - Wind-affected weather - Complete UI animation toolkit - Enhanced particle effects for all actions Files modified: - index.html (4 new script tags) - GameScene.js (4 system initializations + update calls) - CraftingSystem.js (craft sparkles on completion) - FarmingSystem.js (dig/plant/harvest particles) - TASKS.md (Phase 29 updated) - FINAL_IMPLEMENTATION_ROADMAP.md (PART 3 100% complete)
This commit is contained in:
325
src/systems/UIPolishSystem.js
Normal file
325
src/systems/UIPolishSystem.js
Normal file
@@ -0,0 +1,325 @@
|
||||
// UI Polish System - Smooth transitions, animations, tooltips
|
||||
class UIPolishSystem {
|
||||
constructor(scene) {
|
||||
this.scene = scene;
|
||||
|
||||
// Tooltip management
|
||||
this.currentTooltip = null;
|
||||
this.tooltipDelay = 500; // Show after 500ms hover
|
||||
this.tooltipTimer = null;
|
||||
|
||||
// Button animations cache
|
||||
this.buttonAnimations = new Map();
|
||||
|
||||
console.log('🎨 UIPolishSystem initialized');
|
||||
}
|
||||
|
||||
// Fade in a UI element smoothly
|
||||
fadeIn(element, duration = 300, delay = 0) {
|
||||
if (!element) return;
|
||||
|
||||
element.setAlpha(0);
|
||||
|
||||
this.scene.tweens.add({
|
||||
targets: element,
|
||||
alpha: 1,
|
||||
duration: duration,
|
||||
delay: delay,
|
||||
ease: 'Power2'
|
||||
});
|
||||
}
|
||||
|
||||
// Fade out a UI element
|
||||
fadeOut(element, duration = 300, onComplete = null) {
|
||||
if (!element) return;
|
||||
|
||||
this.scene.tweens.add({
|
||||
targets: element,
|
||||
alpha: 0,
|
||||
duration: duration,
|
||||
ease: 'Power2',
|
||||
onComplete: onComplete
|
||||
});
|
||||
}
|
||||
|
||||
// Slide in element from direction
|
||||
slideIn(element, direction = 'left', duration = 400, distance = 200) {
|
||||
if (!element) return;
|
||||
|
||||
const startX = element.x;
|
||||
const startY = element.y;
|
||||
|
||||
// Set starting position
|
||||
switch (direction) {
|
||||
case 'left':
|
||||
element.x -= distance;
|
||||
break;
|
||||
case 'right':
|
||||
element.x += distance;
|
||||
break;
|
||||
case 'top':
|
||||
element.y -= distance;
|
||||
break;
|
||||
case 'bottom':
|
||||
element.y += distance;
|
||||
break;
|
||||
}
|
||||
|
||||
element.setAlpha(0);
|
||||
|
||||
// Animate to original position
|
||||
this.scene.tweens.add({
|
||||
targets: element,
|
||||
x: startX,
|
||||
y: startY,
|
||||
alpha: 1,
|
||||
duration: duration,
|
||||
ease: 'Back.easeOut'
|
||||
});
|
||||
}
|
||||
|
||||
// Add hover effect to button
|
||||
addButtonHover(button, scaleUp = 1.1, duration = 200) {
|
||||
if (!button || this.buttonAnimations.has(button)) return;
|
||||
|
||||
const originalScale = button.scaleX || 1;
|
||||
|
||||
// Mouse over
|
||||
button.on('pointerover', () => {
|
||||
this.scene.tweens.add({
|
||||
targets: button,
|
||||
scaleX: originalScale * scaleUp,
|
||||
scaleY: originalScale * scaleUp,
|
||||
duration: duration,
|
||||
ease: 'Back.easeOut'
|
||||
});
|
||||
|
||||
// Play UI click sound
|
||||
if (this.scene.soundManager && this.scene.soundManager.beepUIClick) {
|
||||
this.scene.soundManager.beepUIClick();
|
||||
}
|
||||
});
|
||||
|
||||
// Mouse out
|
||||
button.on('pointerout', () => {
|
||||
this.scene.tweens.add({
|
||||
targets: button,
|
||||
scaleX: originalScale,
|
||||
scaleY: originalScale,
|
||||
duration: duration,
|
||||
ease: 'Power2'
|
||||
});
|
||||
});
|
||||
|
||||
this.buttonAnimations.set(button, { originalScale, scaleUp });
|
||||
}
|
||||
|
||||
// Pulse animation (for important elements)
|
||||
pulse(element, minScale = 0.95, maxScale = 1.05, duration = 1000) {
|
||||
if (!element) return;
|
||||
|
||||
return this.scene.tweens.add({
|
||||
targets: element,
|
||||
scale: { from: minScale, to: maxScale },
|
||||
duration: duration,
|
||||
yoyo: true,
|
||||
repeat: -1,
|
||||
ease: 'Sine.easeInOut'
|
||||
});
|
||||
}
|
||||
|
||||
// Shake animation (for errors or attention)
|
||||
shake(element, intensity = 10, duration = 500) {
|
||||
if (!element) return;
|
||||
|
||||
const originalX = element.x;
|
||||
|
||||
this.scene.tweens.add({
|
||||
targets: element,
|
||||
x: originalX + intensity,
|
||||
duration: 50,
|
||||
yoyo: true,
|
||||
repeat: Math.floor(duration / 100),
|
||||
ease: 'Sine.easeInOut',
|
||||
onComplete: () => {
|
||||
element.x = originalX; // Reset to original position
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Show tooltip on hover
|
||||
showTooltip(x, y, text, options = {}) {
|
||||
// Clear existing tooltip
|
||||
this.hideTooltip();
|
||||
|
||||
const {
|
||||
backgroundColor = '#000000',
|
||||
textColor = '#ffffff',
|
||||
padding = 10,
|
||||
fontSize = '14px',
|
||||
maxWidth = 200,
|
||||
offsetX = 10,
|
||||
offsetY = -30
|
||||
} = options;
|
||||
|
||||
// Create tooltip background
|
||||
const bg = this.scene.add.rectangle(
|
||||
x + offsetX,
|
||||
y + offsetY,
|
||||
maxWidth,
|
||||
40,
|
||||
Phaser.Display.Color.HexStringToColor(backgroundColor).color,
|
||||
0.9
|
||||
);
|
||||
|
||||
// Create tooltip text
|
||||
const tooltip = this.scene.add.text(
|
||||
x + offsetX,
|
||||
y + offsetY,
|
||||
text,
|
||||
{
|
||||
fontSize: fontSize,
|
||||
color: textColor,
|
||||
align: 'center',
|
||||
wordWrap: { width: maxWidth - padding * 2 }
|
||||
}
|
||||
);
|
||||
|
||||
tooltip.setOrigin(0.5, 0.5);
|
||||
bg.setSize(tooltip.width + padding * 2, tooltip.height + padding * 2);
|
||||
|
||||
// Create container
|
||||
this.currentTooltip = this.scene.add.container(0, 0, [bg, tooltip]);
|
||||
this.currentTooltip.setDepth(1000000); // Always on top
|
||||
|
||||
// Fade in
|
||||
this.fadeIn(this.currentTooltip, 200);
|
||||
}
|
||||
|
||||
// Hide current tooltip
|
||||
hideTooltip() {
|
||||
if (this.currentTooltip) {
|
||||
this.fadeOut(this.currentTooltip, 150, () => {
|
||||
this.currentTooltip.destroy();
|
||||
this.currentTooltip = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Add tooltip to interactive object
|
||||
addTooltip(object, text, options = {}) {
|
||||
if (!object) return;
|
||||
|
||||
let hoverTimer = null;
|
||||
|
||||
object.on('pointerover', (pointer) => {
|
||||
// Delay tooltip appearance
|
||||
hoverTimer = this.scene.time.delayedCall(this.tooltipDelay, () => {
|
||||
this.showTooltip(pointer.worldX, pointer.worldY, text, options);
|
||||
});
|
||||
});
|
||||
|
||||
object.on('pointerout', () => {
|
||||
// Cancel delayed tooltip
|
||||
if (hoverTimer) {
|
||||
hoverTimer.destroy();
|
||||
hoverTimer = null;
|
||||
}
|
||||
this.hideTooltip();
|
||||
});
|
||||
|
||||
object.on('pointermove', (pointer) => {
|
||||
// Move tooltip with mouse
|
||||
if (this.currentTooltip) {
|
||||
this.currentTooltip.setPosition(
|
||||
pointer.worldX + 10,
|
||||
pointer.worldY - 30
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Typewriter text effect
|
||||
typewriterText(textObject, fullText, speed = 50) {
|
||||
if (!textObject) return;
|
||||
|
||||
textObject.setText('');
|
||||
let index = 0;
|
||||
|
||||
const timer = this.scene.time.addEvent({
|
||||
delay: speed,
|
||||
callback: () => {
|
||||
if (index < fullText.length) {
|
||||
textObject.setText(textObject.text + fullText[index]);
|
||||
index++;
|
||||
} else {
|
||||
timer.destroy();
|
||||
}
|
||||
},
|
||||
loop: true
|
||||
});
|
||||
|
||||
return timer;
|
||||
}
|
||||
|
||||
// Number counter animation (for scores, resources)
|
||||
animateNumber(textObject, startValue, endValue, duration = 1000, prefix = '', suffix = '') {
|
||||
if (!textObject) return;
|
||||
|
||||
const data = { value: startValue };
|
||||
|
||||
this.scene.tweens.add({
|
||||
targets: data,
|
||||
value: endValue,
|
||||
duration: duration,
|
||||
ease: 'Power2',
|
||||
onUpdate: () => {
|
||||
textObject.setText(`${prefix}${Math.floor(data.value)}${suffix}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Flash effect (for notifications)
|
||||
flash(element, color = 0xffffff, duration = 300) {
|
||||
if (!element) return;
|
||||
|
||||
const originalTint = element.tint || 0xffffff;
|
||||
|
||||
element.setTint(color);
|
||||
|
||||
this.scene.tweens.add({
|
||||
targets: element,
|
||||
alpha: { from: 1, to: 0.5 },
|
||||
duration: duration / 2,
|
||||
yoyo: true,
|
||||
onComplete: () => {
|
||||
element.setTint(originalTint);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Smooth scroll container (for long lists)
|
||||
enableSmoothScroll(container, scrollSpeed = 5) {
|
||||
if (!container || !this.scene.input.keyboard) return;
|
||||
|
||||
const upKey = this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.UP);
|
||||
const downKey = this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.DOWN);
|
||||
|
||||
this.scene.events.on('update', () => {
|
||||
if (upKey.isDown) {
|
||||
container.y += scrollSpeed;
|
||||
}
|
||||
if (downKey.isDown) {
|
||||
container.y -= scrollSpeed;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Clean up
|
||||
destroy() {
|
||||
this.hideTooltip();
|
||||
this.buttonAnimations.clear();
|
||||
|
||||
console.log('🎨 UIPolishSystem destroyed');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user