Source code for xsge.transition

# xSGE Transition Framework
# Copyright (C) 2014 Julian Marchant <onpon4@riseup.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""
This module provides a framework for transition animations.
"""

from __future__ import division
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals

import math
import random

import sge


__all__ = ["Room"]

FADE = 1
DISSOLVE = 2
PIXELATE = 3
WIPE_LEFT = 4
WIPE_RIGHT = 5
WIPE_TOP = 6
WIPE_BOTTOM = 7
WIPE_TOPLEFT = 8
WIPE_TOPRIGHT = 9
WIPE_BOTTOMLEFT = 10
WIPE_BOTTOMRIGHT = 11
WIPE_MATRIX = 12
IRIS_IN = 13
IRIS_OUT = 14


[docs]class Room(sge.Room): """ This class is a variant of :class:`sge.Room` with transition support. Transitions are done using :meth:`xsge.transition.Room.show_transition`. In general, it is best to use :meth:`xsge.transition.Room.transition_start`, :meth:`xsge.transition.Room.transition_resume`, and :meth:`xsge.transition.Room.transition_end`, which show transitions in between the current and next room. """ transition_update = None transition_sprite = None transition_duration = 0 def event_room_start(self): self.transition_time_passed = 0 self.transition_complete_last = 0 self.transition_variables = {} def _update_fade(self, complete): w = self.transition_sprite.width h = self.transition_sprite.height if complete < 0.5: diff = (complete - self.transition_complete_last) * 2 c = [int(round(diff * 255))] * 3 darkener = sge.Sprite(width=self.transition_sprite.width, height=self.transition_sprite.height) darkener.draw_rectangle(0, 0, w, h, c) self.transition_sprite.draw_sprite( darkener, 0, 0, 0, blend_mode=sge.BLEND_RGB_SUBTRACT) darkener.destroy() else: complete = (complete - 0.5) * 2 c = (0, 0, 0, int(round(255 - complete * 255))) self.transition_sprite.draw_clear() self.transition_sprite.draw_rectangle(0, 0, w, h, fill=c) def _update_dissolve(self, complete): w = self.transition_sprite.width h = self.transition_sprite.height diff = complete - self.transition_complete_last c = (0, 0, 0, int(round(diff * 255))) eraser = sge.Sprite(width=self.transition_sprite.width, height=self.transition_sprite.height) eraser.draw_rectangle(0, 0, w, h, c) self.transition_sprite.draw_sprite(eraser, 0, 0, 0, blend_mode=sge.BLEND_RGBA_SUBTRACT) eraser.destroy() def _update_pixelate(self, complete): w = self.transition_sprite.width h = self.transition_sprite.height if complete < 0.5: complete *= 2 swidth = max(1, w * (1 - complete)) sheight = max(1, h * (1 - complete)) self.transition_sprite.width = swidth self.transition_sprite.height = sheight self.transition_sprite.width = w self.transition_sprite.height = h else: diff = (complete - self.transition_complete_last) * 2 c = (0, 0, 0, int(round(diff * 255))) eraser = sge.Sprite(width=self.transition_sprite.width, height=self.transition_sprite.height) eraser.draw_rectangle(0, 0, w, h, c) self.transition_sprite.draw_sprite( eraser, 0, 0, 0, blend_mode=sge.BLEND_RGBA_SUBTRACT) eraser.destroy() def _update_wipe_left(self, complete): w = self.transition_sprite.width * complete h = self.transition_sprite.height self.transition_sprite.draw_erase(0, 0, w, h) def _update_wipe_right(self, complete): w = self.transition_sprite.width * complete x = self.transition_sprite.width - w h = self.transition_sprite.height self.transition_sprite.draw_erase(x, 0, w, h) def _update_wipe_top(self, complete): w = self.transition_sprite.width h = self.transition_sprite.height * complete self.transition_sprite.draw_erase(0, 0, w, h) def _update_wipe_bottom(self, complete): w = self.transition_sprite.width h = self.transition_sprite.height * complete y = self.transition_sprite.height - h self.transition_sprite.draw_erase(0, y, w, h) def _update_wipe_topleft(self, complete): w = self.transition_sprite.width h = self.transition_sprite.height dw = math.hypot(w, h) lw = int(round(2 * dw * complete)) eraser = sge.Sprite(width=self.transition_sprite.width, height=self.transition_sprite.height) eraser.draw_line(-w, h, w, -h, (0, 0, 0, 255), thickness=lw, anti_alias=True) self.transition_sprite.draw_sprite(eraser, 0, 0, 0, blend_mode=sge.BLEND_RGBA_SUBTRACT) eraser.destroy() def _update_wipe_topright(self, complete): w = self.transition_sprite.width h = self.transition_sprite.height dw = math.hypot(w, h) lw = int(round(2 * dw * complete)) eraser = sge.Sprite(width=self.transition_sprite.width, height=self.transition_sprite.height) eraser.draw_line(0, -h, w + w, h, (0, 0, 0, 255), thickness=lw, anti_alias=True) self.transition_sprite.draw_sprite(eraser, 0, 0, 0, blend_mode=sge.BLEND_RGBA_SUBTRACT) eraser.destroy() def _update_wipe_bottomleft(self, complete): w = self.transition_sprite.width h = self.transition_sprite.height dw = math.hypot(w, h) lw = int(round(2 * dw * complete)) eraser = sge.Sprite(width=self.transition_sprite.width, height=self.transition_sprite.height) eraser.draw_line(-w, 0, w, h + h, (0, 0, 0, 255), thickness=lw, anti_alias=True) self.transition_sprite.draw_sprite(eraser, 0, 0, 0, blend_mode=sge.BLEND_RGBA_SUBTRACT) eraser.destroy() def _update_wipe_bottomright(self, complete): w = self.transition_sprite.width h = self.transition_sprite.height dw = math.hypot(w, h) lw = int(round(2 * dw * complete)) eraser = sge.Sprite(width=self.transition_sprite.width, height=self.transition_sprite.height) eraser.draw_line(0, h + h, w + w, 0, (0, 0, 0, 255), thickness=lw, anti_alias=True) self.transition_sprite.draw_sprite(eraser, 0, 0, 0, blend_mode=sge.BLEND_RGBA_SUBTRACT) eraser.destroy() def _update_wipe_matrix(self, complete): psize = 4 w = self.transition_sprite.width h = self.transition_sprite.height mw = int(round(w / psize)) mh = int(round(h / psize)) if "remaining" in self.transition_variables: remaining = self.transition_variables["remaining"] else: remaining = [] for x in range(mw): for y in range(mh): remaining.append((x, y)) diff = complete - self.transition_complete_last new_erase = int(round(mw * mh * diff)) self.transition_sprite.draw_lock() while new_erase > 0 and remaining: new_erase -= 1 x, y = remaining.pop(random.randrange(len(remaining))) self.transition_sprite.draw_erase(x * psize, y * psize, psize, psize) self.transition_sprite.draw_unlock() self.transition_variables["remaining"] = remaining def _update_iris_in(self, complete): x = self.transition_sprite.width / 2 y = self.transition_sprite.height / 2 r = int(round(math.hypot(x, y) * (1 - complete))) eraser = sge.Sprite(width=self.transition_sprite.width, height=self.transition_sprite.height) eraser_eraser = sge.Sprite(width=self.transition_sprite.width, height=self.transition_sprite.height) eraser_eraser.draw_circle(x, y, r, fill=(0, 0, 0, 255)) eraser.draw_lock() eraser.draw_rectangle(0, 0, eraser.width, eraser.height, fill=(0, 0, 0, 255)) eraser.draw_sprite(eraser_eraser, 0, 0, 0, blend_mode=sge.BLEND_RGBA_SUBTRACT) eraser.draw_unlock() eraser_eraser.destroy() self.transition_sprite.draw_sprite(eraser, 0, 0, 0, blend_mode=sge.BLEND_RGBA_SUBTRACT) eraser.destroy() def _update_iris_out(self, complete): x = self.transition_sprite.width / 2 y = self.transition_sprite.height / 2 r = int(round(math.hypot(x, y) * complete)) eraser = sge.Sprite(width=self.transition_sprite.width, height=self.transition_sprite.height) eraser.draw_circle(x, y, r, fill=(0, 0, 0, 255)) self.transition_sprite.draw_sprite(eraser, 0, 0, 0, blend_mode=sge.BLEND_RGBA_SUBTRACT) eraser.destroy()
[docs] def show_transition(self, transition, sprite, duration): """Show a transition. Arguments: - ``transition`` -- The type of transition to use. Should be one of the following: - :const:`xsge.transition.FADE` -- Fade out (to black) and then fade in. - :const:`xsge.transition.DISSOLVE` -- Gradually replace the first room with the second room. - :const:`xsge.transition.PIXELATE` -- Pixelate the first room, then fade into the second room. If :attr:`sge.game.scale_smooth` is :const:`True`, the effect will instead be to blur and unblur the rooms. This relies on the destructiveness of changing :attr:`sge.Sprite.width` and :attr:`sge.Sprite.height`. - :const:`xsge.transition.WIPE_LEFT` -- Wipe transition from left to right. - :const:`xsge.transition.WIPE_RIGHT` -- Wipe transition from right to left. - :const:`xsge.transition.WIPE_TOP` -- Wipe transition from top to bottom. - :const:`xsge.transition.WIPE_BOTTOM` -- Wipe transition from bottom to top. - :const:`xsge.transition.WIPE_TOPLEFT` -- Diagonal wipe transition from top-left to bottom-right. - :const:`xsge.transition.WIPE_TOPRIGHT` -- Diagonal wipe transition from top-right to bottom-left. - :const:`xsge.transition.WIPE_BOTTOMLEFT` -- Diagonal wipe transition from bottom-left to top-right. - :const:`xsge.transition.WIPE_BOTTOMRIGHT` -- Diagonal wipe transition from bottom-right to top-left. - :const:`xsge.transition.WIPE_MATRIX` -- Matrix wipe transition. - :const:`xsge.transition.IRIS_IN` -- Iris in transition. - :const:`xsge.transition.IRIS_OUT` -- Iris out transition. - ``sprite`` -- The sprite to use as the first image (the one being transitioned out of). Generally should be a screenshot of the previous room. - ``duration`` -- The time the transition should take in milliseconds. """ self.transition_update = { FADE: self._update_fade, DISSOLVE: self._update_dissolve, PIXELATE: self._update_pixelate, WIPE_LEFT: self._update_wipe_left, WIPE_RIGHT: self._update_wipe_right, WIPE_TOP: self._update_wipe_top, WIPE_BOTTOM: self._update_wipe_bottom, WIPE_TOPLEFT: self._update_wipe_topleft, WIPE_TOPRIGHT: self._update_wipe_topright, WIPE_BOTTOMLEFT: self._update_wipe_bottomleft, WIPE_BOTTOMRIGHT: self._update_wipe_bottomright, WIPE_MATRIX: self._update_wipe_matrix, IRIS_IN: self._update_iris_in, IRIS_OUT: self._update_iris_out }.setdefault(transition, lambda c: None) self.transition_sprite = sprite self.transition_duration = duration self.transition_time_passed = 0 self.transition_complete_last = 0 self.transition_variables = {}
[docs] def transition_start(self, transition=FADE, duration=1500): """Start the room, using a transition. See the documentation for :meth:`sge.Room.start` and :meth:`xsge.transition.Room.show_transition` for more information. """ screenshot = sge.Sprite.from_screenshot() self.show_transition(transition, screenshot, duration) self.start()
[docs] def transition_resume(self, transition=FADE, duration=1500): """Resume the room, using a transition. See the documentation for :meth:`sge.Room.resume` and :meth:`xsge.transition.Room.show_transition` for more information. """ screenshot = sge.Sprite.from_screenshot() self.show_transition(transition, screenshot, duration) self.resume()
[docs] def transition_end(self, transition=FADE, duration=1500, next_room=None, resume=True): """End the room, using a transition for the next room. See the documentation for :meth:`sge.Room.end` and :meth:`xsge.transition.Room.show_transition` for more information. """ if next_room is None: next_room = self.room_number + 1 if (next_room >= -len(sge.game.rooms) and next_room < len(sge.game.rooms)): screenshot = sge.Sprite.from_screenshot() sge.game.rooms[next_room].show_transition(transition, screenshot, duration) self.end(next_room=next_room, resume=resume)
def event_step(self, time_passed, delta_mult): if (self.transition_update is not None and self.transition_sprite is not None and self.transition_duration > 0): self.transition_time_passed += time_passed if self.transition_time_passed < self.transition_duration: complete = (self.transition_time_passed / self.transition_duration) self.transition_update(complete) self.transition_complete_last = complete sge.game.project_sprite(self.transition_sprite, 0, 0, 0) else: self.transition_sprite.destroy() self.transition_update = None self.transition_sprite = None self.transition_duration = 0 self.transition_time_passed = 0 self.transition_complete_last = 0 self.transition_variables = {}