Scene Transition Effects
Smooth transitions between scenes for a polished game feel. McRogueFace supports built-in transitions and custom overlay-based effects.
Built-in Transitions
McRogueFace provides simple built-in scene transitions:
import mcrfpy
# Create scenes, then switch between them with activate()
menu_scene = mcrfpy.Scene("menu")
game_scene = mcrfpy.Scene("game")
# Instant switch (default) -- also settable via mcrfpy.current_scene = scene
menu_scene.activate()
game_scene.activate()
Custom Fade Transition
Create a manual fade transition with more control:
import mcrfpy
class FadeTransition:
"""Custom fade transition with callback support."""
def __init__(self, current_scene, target_scene):
"""
Args:
current_scene: The mcrfpy.Scene object to transition from
target_scene: The mcrfpy.Scene object to transition to
"""
self.current_scene = current_scene
self.target_scene = target_scene
self.overlay = None
def _create_overlay(self, scene):
"""Create a full-screen black overlay in the given scene."""
self.overlay = mcrfpy.Frame(pos=(0, 0), size=(1024, 768))
self.overlay.fill_color = mcrfpy.Color(0, 0, 0, 255)
self.overlay.opacity = 0.0
self.overlay.z_index = 9999 # On top of everything
scene.children.append(self.overlay)
def fade_out_in(self, duration=0.5, on_complete=None):
"""
Fade to black, switch scene, fade in.
Args:
duration: Total transition time (half for out, half for in)
on_complete: Callback when transition finishes
"""
half_duration = duration / 2
# Create overlay in current scene
self._create_overlay(self.current_scene)
# Fade to black
self.overlay.animate("opacity", 1.0, half_duration, mcrfpy.Easing.EASE_IN)
# Switch scene and fade in
def switch_and_fade_in(timer, runtime):
# Switch to new scene
self.target_scene.activate()
# Create overlay in new scene
self._create_overlay(self.target_scene)
self.overlay.opacity = 1.0 # Start fully opaque
# Fade in
self.overlay.animate("opacity", 0.0, half_duration, mcrfpy.Easing.EASE_OUT)
# Cleanup and callback
def cleanup(timer, runtime):
self.target_scene.children.remove(self.overlay)
if on_complete:
on_complete()
mcrfpy.Timer("fade_cleanup", cleanup, int(half_duration * 1000) + 50, once=True)
mcrfpy.Timer("fade_switch", switch_and_fade_in, int(half_duration * 1000), once=True)
# Usage
menu_scene = mcrfpy.Scene("menu")
game_scene = mcrfpy.Scene("game")
def on_transition_complete():
print("Transition complete, game starting!")
# Start on menu, then transition to game
menu_scene.activate()
transition = FadeTransition(menu_scene, game_scene)
transition.fade_out_in(duration=0.8, on_complete=on_transition_complete)
Color Flash Transition
Flash to a color (like white for teleport or red for damage):
import mcrfpy
def flash_transition(current_scene, target_scene, color=(255, 255, 255), duration=0.4):
"""
Flash to a color, then switch scene.
Args:
current_scene: The mcrfpy.Scene object to transition from
target_scene: The mcrfpy.Scene object to transition to
color: Flash color RGB tuple
duration: Total transition time
"""
# Create colored overlay in current scene
overlay = mcrfpy.Frame(pos=(0, 0), size=(1024, 768))
overlay.fill_color = mcrfpy.Color(color[0], color[1], color[2], 255)
overlay.opacity = 0.0
overlay.z_index = 9999
current_scene.children.append(overlay)
quarter = duration / 4
# Flash in (fast)
overlay.animate("opacity", 1.0, quarter, mcrfpy.Easing.EASE_OUT)
# Hold, then switch and fade out
def switch_scene(timer, runtime):
target_scene.activate()
# Create overlay in new scene
new_overlay = mcrfpy.Frame(pos=(0, 0), size=(1024, 768))
new_overlay.fill_color = mcrfpy.Color(color[0], color[1], color[2], 255)
new_overlay.z_index = 9999
target_scene.children.append(new_overlay)
# Fade out (slower)
new_overlay.animate("opacity", 0.0, duration / 2, mcrfpy.Easing.EASE_IN)
# Cleanup
def cleanup(timer, runtime):
target_scene.children.remove(new_overlay)
mcrfpy.Timer("flash_cleanup", cleanup, int(duration * 500) + 50, once=True)
mcrfpy.Timer("flash_switch", switch_scene, int(quarter * 2000), once=True)
# Usage
game_scene = mcrfpy.Scene("game")
next_level_scene = mcrfpy.Scene("next_level")
death_scene = mcrfpy.Scene("death_screen")
flash_transition(game_scene, next_level_scene, color=(255, 255, 255), duration=0.5) # White flash
flash_transition(game_scene, death_scene, color=(255, 0, 0), duration=0.8) # Red flash
Wipe Transition
A frame that expands to cover the screen:
import mcrfpy
def wipe_transition(current_scene, target_scene, direction="right", duration=0.5, color=(0, 0, 0)):
"""
Wipe transition from one edge.
Args:
current_scene: The mcrfpy.Scene object to transition from
target_scene: The mcrfpy.Scene object to transition to
direction: "left", "right", "up", "down"
duration: Total transition time
color: Wipe color
"""
half = duration / 2
# Set up wipe overlay based on direction
if direction == "right":
overlay = mcrfpy.Frame(pos=(0, 0), size=(0, 768))
anim_prop = "w"
anim_target = 1024
elif direction == "left":
overlay = mcrfpy.Frame(pos=(1024, 0), size=(0, 768))
anim_prop = "x" # Move left edge
anim_target = 0
elif direction == "down":
overlay = mcrfpy.Frame(pos=(0, 0), size=(1024, 0))
anim_prop = "h"
anim_target = 768
else: # up
overlay = mcrfpy.Frame(pos=(0, 768), size=(1024, 0))
anim_prop = "y"
anim_target = 0
overlay.fill_color = mcrfpy.Color(color[0], color[1], color[2], 255)
overlay.z_index = 9999
current_scene.children.append(overlay)
# Wipe in
overlay.animate(anim_prop, float(anim_target), half, mcrfpy.Easing.EASE_IN_OUT)
# Switch and wipe out
def switch_and_wipe(timer, runtime):
target_scene.activate()
# Create covering overlay
if direction == "right":
new_overlay = mcrfpy.Frame(pos=(0, 0), size=(1024, 768))
new_anim_prop = "x"
new_anim_target = 1024
elif direction == "left":
new_overlay = mcrfpy.Frame(pos=(0, 0), size=(1024, 768))
new_anim_prop = "w"
new_anim_target = 0
elif direction == "down":
new_overlay = mcrfpy.Frame(pos=(0, 0), size=(1024, 768))
new_anim_prop = "y"
new_anim_target = -768
else:
new_overlay = mcrfpy.Frame(pos=(0, 0), size=(1024, 768))
new_anim_prop = "h"
new_anim_target = 0
new_overlay.fill_color = mcrfpy.Color(color[0], color[1], color[2], 255)
new_overlay.z_index = 9999
target_scene.children.append(new_overlay)
# Wipe out
new_overlay.animate(new_anim_prop, float(new_anim_target), half, mcrfpy.Easing.EASE_IN_OUT)
# Cleanup
def cleanup(timer, runtime):
target_scene.children.remove(new_overlay)
mcrfpy.Timer("wipe_cleanup", cleanup, int(half * 1000) + 50, once=True)
mcrfpy.Timer("wipe_switch", switch_and_wipe, int(half * 1000), once=True)
# Usage
game_scene = mcrfpy.Scene("game")
next_level = mcrfpy.Scene("next_level")
menu_scene = mcrfpy.Scene("menu")
wipe_transition(game_scene, next_level, direction="right", duration=0.6)
wipe_transition(game_scene, menu_scene, direction="down", duration=0.4)
Transition Manager
A centralized manager for consistent transitions:
import mcrfpy
class TransitionManager:
"""Manages scene transitions with multiple effect types."""
def __init__(self, screen_width=1024, screen_height=768):
self.width = screen_width
self.height = screen_height
self.is_transitioning = False
self.current_scene = None # Track active scene
def go_to(self, target_scene, effect="fade", duration=0.5, **kwargs):
"""
Transition to a scene with the specified effect.
Args:
target_scene: The mcrfpy.Scene object to transition to
effect: "fade", "flash", "wipe", "instant"
duration: Transition duration
**kwargs: Effect-specific options (color, direction)
"""
if self.is_transitioning:
return
if self.current_scene is None:
# No current scene tracked, just activate target
target_scene.activate()
self.current_scene = target_scene
return
self.is_transitioning = True
if effect == "instant":
target_scene.activate()
self.current_scene = target_scene
self.is_transitioning = False
elif effect == "fade":
color = kwargs.get("color", (0, 0, 0))
self._fade(target_scene, duration, color)
elif effect == "flash":
color = kwargs.get("color", (255, 255, 255))
self._flash(target_scene, duration, color)
elif effect == "wipe":
direction = kwargs.get("direction", "right")
color = kwargs.get("color", (0, 0, 0))
self._wipe(target_scene, duration, direction, color)
def _fade(self, target_scene, duration, color):
half = duration / 2
overlay = mcrfpy.Frame(pos=(0, 0), size=(self.width, self.height))
overlay.fill_color = mcrfpy.Color(color[0], color[1], color[2], 255)
overlay.opacity = 0.0
overlay.z_index = 9999
self.current_scene.children.append(overlay)
overlay.animate("opacity", 1.0, half, mcrfpy.Easing.EASE_IN)
def phase2(timer, runtime):
target_scene.activate()
new_overlay = mcrfpy.Frame(pos=(0, 0), size=(self.width, self.height))
new_overlay.fill_color = mcrfpy.Color(color[0], color[1], color[2], 255)
new_overlay.z_index = 9999
target_scene.children.append(new_overlay)
new_overlay.animate("opacity", 0.0, half, mcrfpy.Easing.EASE_OUT)
def cleanup(timer, runtime):
target_scene.children.remove(new_overlay)
self.current_scene = target_scene
self.is_transitioning = False
mcrfpy.Timer("fade_done", cleanup, int(half * 1000) + 50, once=True)
mcrfpy.Timer("fade_switch", phase2, int(half * 1000), once=True)
def _flash(self, target_scene, duration, color):
quarter = duration / 4
overlay = mcrfpy.Frame(pos=(0, 0), size=(self.width, self.height))
overlay.fill_color = mcrfpy.Color(color[0], color[1], color[2], 255)
overlay.opacity = 0.0
overlay.z_index = 9999
self.current_scene.children.append(overlay)
overlay.animate("opacity", 1.0, quarter, mcrfpy.Easing.EASE_OUT)
def phase2(timer, runtime):
target_scene.activate()
new_overlay = mcrfpy.Frame(pos=(0, 0), size=(self.width, self.height))
new_overlay.fill_color = mcrfpy.Color(color[0], color[1], color[2], 255)
new_overlay.z_index = 9999
target_scene.children.append(new_overlay)
new_overlay.animate("opacity", 0.0, duration / 2, mcrfpy.Easing.EASE_IN)
def cleanup(timer, runtime):
target_scene.children.remove(new_overlay)
self.current_scene = target_scene
self.is_transitioning = False
mcrfpy.Timer("flash_done", cleanup, int(duration * 500) + 50, once=True)
mcrfpy.Timer("flash_switch", phase2, int(quarter * 2000), once=True)
def _wipe(self, target_scene, duration, direction, color):
# Simplified wipe - right direction only for brevity
half = duration / 2
overlay = mcrfpy.Frame(pos=(0, 0), size=(0, self.height))
overlay.fill_color = mcrfpy.Color(color[0], color[1], color[2], 255)
overlay.z_index = 9999
self.current_scene.children.append(overlay)
overlay.animate("w", float(self.width), half, mcrfpy.Easing.EASE_IN_OUT)
def phase2(timer, runtime):
target_scene.activate()
new_overlay = mcrfpy.Frame(pos=(0, 0), size=(self.width, self.height))
new_overlay.fill_color = mcrfpy.Color(color[0], color[1], color[2], 255)
new_overlay.z_index = 9999
target_scene.children.append(new_overlay)
new_overlay.animate("x", float(self.width), half, mcrfpy.Easing.EASE_IN_OUT)
def cleanup(timer, runtime):
target_scene.children.remove(new_overlay)
self.current_scene = target_scene
self.is_transitioning = False
mcrfpy.Timer("wipe_done", cleanup, int(half * 1000) + 50, once=True)
mcrfpy.Timer("wipe_switch", phase2, int(half * 1000), once=True)
# Usage
# Create scenes
menu_scene = mcrfpy.Scene("menu")
game_scene = mcrfpy.Scene("game")
options_scene = mcrfpy.Scene("options")
next_level_scene = mcrfpy.Scene("next_level")
# Create transition manager and set initial scene
transitions = TransitionManager()
transitions.current_scene = menu_scene
menu_scene.activate()
# Various transition styles
transitions.go_to(game_scene, effect="fade", duration=0.5)
transitions.go_to(menu_scene, effect="flash", color=(255, 255, 255), duration=0.4)
transitions.go_to(next_level_scene, effect="wipe", direction="right", duration=0.6)
transitions.go_to(options_scene, effect="instant")
Key Concepts
- Frame overlays: Use full-screen Frames with high z_index for transition effects
- Opacity animation: Animate
opacityproperty for fade effects - Two-phase transitions: Fade out in current scene, switch, fade in
- Timer coordination: Use timers to sequence transition phases
- Cleanup: Always remove transition overlays after completion
Tips
- Keep transitions short (0.3-0.6s) for responsive feel
- Use faster transitions for frequent switches (inventory, pause)
- Use slower, dramatic transitions for major moments (level complete, death)
- Match transition style to game theme (wipes for action, fades for story)
- Prevent input during transitions to avoid state confusion
- Consider adding sound effects to enhance transitions