From fff99339c2e5150785b6bb5ee4cfab53c75d0cdb Mon Sep 17 00:00:00 2001 From: devpedrofurquim Date: Mon, 28 Oct 2024 22:03:40 -0300 Subject: [PATCH] feat: Implement Fade class and transition effect to level transitions --- lib/components/fade.dart | 37 +++++++++++++++++++++++++++++++++++++ lib/components/npc.dart | 28 ++++++++++++++++++++++------ lib/moonshiner.dart | 38 ++++++++++++++++++++++++++++++++++---- 3 files changed, 93 insertions(+), 10 deletions(-) create mode 100644 lib/components/fade.dart diff --git a/lib/components/fade.dart b/lib/components/fade.dart new file mode 100644 index 0000000..16b6a1f --- /dev/null +++ b/lib/components/fade.dart @@ -0,0 +1,37 @@ +import 'package:flame/components.dart'; +import 'package:flame/effects.dart'; +import 'package:flutter/material.dart'; + +class FadeEffect extends RectangleComponent { + OpacityEffect? fadeEffect; + + FadeEffect(Vector2 screenSize) + : super( + size: screenSize, + anchor: Anchor.topLeft, + paint: Paint() + ..color = Colors.black.withOpacity(0), // Start transparent + priority: + 100, // High priority to ensure it renders above other components + ); + + void startFade() { + // If a fade effect is already active, remove it + if (fadeEffect != null) { + fadeEffect?.removeFromParent(); + } + + // Create a new fade-in effect + fadeEffect = OpacityEffect.to( + 1.0, // Target opacity (fully opaque) + EffectController(duration: 1.0), // Duration in seconds + onComplete: () { + // Clear the effect after it completes + fadeEffect = null; + }, + ); + + // Add the new effect to the component + add(fadeEffect!); + } +} diff --git a/lib/components/npc.dart b/lib/components/npc.dart index ca8967c..7a78549 100644 --- a/lib/components/npc.dart +++ b/lib/components/npc.dart @@ -17,7 +17,7 @@ class NPC extends SpriteAnimationGroupComponent final List dialogues; // Unique dialogues for each NPC type double moveSpeed = 50; Vector2 velocity = Vector2.zero(); - Vector2 initialPosition; // Define initialPosition + Vector2 initialPosition; CustomHitbox hitbox = CustomHitbox( offsetX: 10, offsetY: 4, @@ -44,7 +44,7 @@ class NPC extends SpriteAnimationGroupComponent position: Vector2(100, 100), ); - Timer? randomDialogueTimer; // Timer for random dialogues + Timer? randomDialogueTimer; NPC({ required this.npcCharacter, @@ -74,7 +74,7 @@ class NPC extends SpriteAnimationGroupComponent @override void onRemove() { - randomDialogueTimer?.stop(); // Stop the timer when NPC is removed + randomDialogueTimer?.stop(); super.onRemove(); } @@ -104,7 +104,7 @@ class NPC extends SpriteAnimationGroupComponent @override void update(double dt) { super.update(dt); - randomDialogueTimer?.update(dt); // Update the random dialogue timer + randomDialogueTimer?.update(dt); final Vector2 currentPlayerPosition = gameRef.player.position; final double distanceToPlayer = currentPlayerPosition.distanceTo(position); @@ -122,16 +122,20 @@ class NPC extends SpriteAnimationGroupComponent } void _triggerRandomDialogue() { - if (!playerColliding && !messageDisplayed) { + if (!gameRef.currentlySpeakingNPC && + !playerColliding && + !messageDisplayed) { hudMessage.message = dialogues[Random().nextInt(dialogues.length)]; hudMessage.position = position + Vector2(0, -30); gameRef.add(hudMessage); + gameRef.currentlySpeakingNPC = true; // Set flag to indicate speaking messageDisplayed = true; Future.delayed(Duration(seconds: 3), () { if (messageDisplayed) { gameRef.remove(hudMessage); + gameRef.currentlySpeakingNPC = false; // Reset flag after speaking messageDisplayed = false; } }); @@ -147,7 +151,7 @@ class NPC extends SpriteAnimationGroupComponent if (other is Player) { playerColliding = true; - if (other.hasInteracted) { + if (other.hasInteracted && !gameRef.currentlySpeakingNPC) { playerHasInteracted = true; hudMessage.message = dialogues[Random().nextInt(dialogues.length)]; @@ -158,8 +162,17 @@ class NPC extends SpriteAnimationGroupComponent } gameRef.add(hudMessage); + gameRef.currentlySpeakingNPC = true; // Set flag to indicate speaking messageDisplayed = true; + Future.delayed(Duration(seconds: 3), () { + // Check if hudMessage is still a child of the game before removing it + if (gameRef.children.contains(hudMessage)) { + gameRef.remove(hudMessage); + } + gameRef.currentlySpeakingNPC = false; // Reset flag after speaking + messageDisplayed = false; + }); other.hasInteracted = false; } } @@ -172,6 +185,7 @@ class NPC extends SpriteAnimationGroupComponent playerColliding = false; if (messageDisplayed) { gameRef.remove(hudMessage); + gameRef.currentlySpeakingNPC = false; // Reset flag after player leaves messageDisplayed = false; } current = NPCState.walking; @@ -199,6 +213,8 @@ class NPC extends SpriteAnimationGroupComponent } } + // Movement methods for NPC types + void _bakerMovement(double dt) { if (movingLeft) { velocity.x = -moveSpeed; diff --git a/lib/moonshiner.dart b/lib/moonshiner.dart index c84159c..c8daddc 100644 --- a/lib/moonshiner.dart +++ b/lib/moonshiner.dart @@ -7,6 +7,7 @@ import 'package:flame_audio/flame_audio.dart'; import 'package:flutter/painting.dart'; import 'package:moonshiner_game/components/button.dart'; import 'package:moonshiner_game/components/developer_message.dart'; +import 'package:moonshiner_game/components/fade.dart'; import 'package:moonshiner_game/components/level_one.dart'; import 'package:moonshiner_game/components/npc.dart'; import 'package:moonshiner_game/components/player.dart'; @@ -33,6 +34,8 @@ class Moonshiner extends FlameGame bool playSounds = true; double soundVolume = 1.0; + bool currentlySpeakingNPC = false; // Track if an NPC is speaking + String developerMessage = ''; // Shared variable to store the developer message @@ -217,14 +220,41 @@ class Moonshiner extends FlameGame // Loading Next and Previous Levels Future loadNextLevel() async { - currentLevelIndex = (currentLevelIndex + 1) % levelNames.length; - _loadLevel(currentLevelIndex); + // Add the fade effect component to the game + final fadeEffect = FadeEffect(size); + add(fadeEffect); + + // Start the fade-in animation + fadeEffect.startFade(); + + // Delay loading the next level until the fade-in completes + Future.delayed(Duration(seconds: 1), () { + currentLevelIndex = (currentLevelIndex + 1) % levelNames.length; + _loadLevel(currentLevelIndex); + + // Remove the fade effect after the transition + fadeEffect.removeFromParent(); + }); } Future loadPreviousLevel() async { + // Only load the previous level if we aren't at the start if (currentLevelIndex > 0) { - currentLevelIndex--; - _loadLevel(currentLevelIndex); + // Add the fade effect component to the game + final fadeEffect = FadeEffect(size); + add(fadeEffect); + + // Start the fade-in animation + fadeEffect.startFade(); + + // Delay loading the previous level until the fade-in completes + Future.delayed(Duration(seconds: 1), () { + currentLevelIndex--; + _loadLevel(currentLevelIndex); + + // Remove the fade effect after the transition + fadeEffect.removeFromParent(); + }); } } }