├── .gitignore ├── 01_fancytext.rpy ├── LICENSE ├── README.md ├── example.gif └── script.rpy /.gitignore: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /01_fancytext.rpy: -------------------------------------------------------------------------------- 1 | # Permission is hereby granted, free of charge, to any person 2 | # obtaining a copy of this software and associated documentation files 3 | # (the "Software"), to deal in the Software without restriction, 4 | # including without limitation the rights to use, copy, modify, merge, 5 | # publish, distribute, sublicense, and/or sell copies of the Software, 6 | # and to permit persons to whom the Software is furnished to do so, 7 | # subject to the following conditions: 8 | # 9 | # The above copyright notice and this permission notice shall be 10 | # included in all copies or substantial portions of the Software. 11 | # 12 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 13 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 14 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 15 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 16 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 17 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 18 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | python early: 21 | 22 | def slow_typewriter(st, gt, delay): 23 | return Transform() 24 | 25 | def slow_fade(st, gt, delay): 26 | t = Transform() 27 | 28 | t.alpha = 1.0 29 | 30 | if not gt == -1: 31 | 32 | if st < gt: 33 | t.alpha = 0.0 34 | 35 | elif gt == 0.0 or st > gt + delay: 36 | t.alpha = 1.0 37 | 38 | else: 39 | t.alpha = float(st - gt) / float(delay) 40 | 41 | return t 42 | 43 | def _slow_slide(st, gt, delay, x = 0, y = 0): 44 | t = Transform() 45 | 46 | if not gt == -1: 47 | p = float(st - gt) / float(delay) 48 | 49 | if st < gt: 50 | t.alpha = 0.0 51 | t.xoffset = x 52 | t.yoffset = y 53 | 54 | elif gt == 0.0 or st > gt + delay: 55 | t.alpha = 1.0 56 | t.xoffset = 0 57 | t.yoffset = 0 58 | 59 | else: 60 | t.alpha = p 61 | t.xoffset = x - (x * p) 62 | t.yoffset = y - (y * p) 63 | 64 | return t 65 | 66 | def slow_slide_up(y = 20): 67 | return renpy.partial(_slow_slide, y = y) 68 | def slow_slide_down(y = 20): 69 | return renpy.partial(_slow_slide, y = -y) 70 | def slow_slide_right(x = 20): 71 | return renpy.partial(_slow_slide, x = -x) 72 | def slow_slide_left(x = 20): 73 | return renpy.partial(_slow_slide, x = x) 74 | 75 | def _slow_shake(st, gt, delay, x = 0, y = 0): 76 | t = Transform() 77 | 78 | if not gt == -1: 79 | p = float(st - gt) / float(delay) 80 | 81 | if st < gt: 82 | t.alpha = 0.0 83 | t.xoffset = x 84 | t.yoffset = y 85 | 86 | elif gt == 0.0 or st > gt + delay: 87 | t.alpha = 1.0 88 | t.xoffset = 0 89 | t.yoffset = 0 90 | 91 | else: 92 | t.alpha = 1.0 93 | t.xoffset = renpy.random.randint(-x, x) 94 | t.yoffset = renpy.random.randint(-y, y) 95 | 96 | return t 97 | 98 | def slow_shake(x = 0, y = 0): 99 | return renpy.partial(_slow_shake, x = x, y = y) 100 | 101 | def slow_rotate(st, gt, delay): 102 | t = Transform() 103 | 104 | if not gt == -1: 105 | p = float(st - gt) / float(delay) 106 | 107 | if st < gt: 108 | t.alpha = 0.0 109 | t.rotate = 0.0 110 | 111 | elif gt == 0.0 or st > gt + delay: 112 | t.alpha = 1.0 113 | t.rotate = 0.0 114 | 115 | else: 116 | t.alpha = 1.0 117 | t.rotate = 360.0 * p 118 | 119 | return t 120 | 121 | def _slow_shaking_slide(st, gt, delay, shake_x = 0, shake_y = 0, slide_x = 0, slide_y = 0): 122 | t = Transform() 123 | 124 | if not gt == -1: 125 | p = float(st - gt) / float(delay) 126 | 127 | if st < gt: 128 | t.alpha = 0.0 129 | t.xoffset = slide_x 130 | t.yoffset = slide_y 131 | 132 | elif gt == 0.0 or st > gt + delay: 133 | t.alpha = 1.0 134 | t.xoffset = 0 135 | t.yoffset = 0 136 | 137 | else: 138 | t.alpha = p 139 | t.xoffset = slide_x - (slide_x * p) + renpy.random.randint(-shake_x, shake_x) 140 | t.yoffset = slide_y - (slide_y * p) + renpy.random.randint(-shake_y, shake_y) 141 | 142 | return t 143 | 144 | def slow_shaking_slide(shake_x = 0, shake_y = 0, slide_x = 0, slide_y = 0): 145 | return renpy.partial(_slow_shaking_slide, shake_x = shake_x, shake_y = shake_y, slide_x = slide_x, slide_y = slide_y) 146 | 147 | def slow_nonsense(st, gt, delay): 148 | t = Transform() 149 | 150 | if not gt == -1: 151 | p = float(st - gt) / float(delay) 152 | 153 | if st < gt: 154 | t.alpha = 0.0 155 | 156 | elif gt == 0.0 or st > gt + delay: 157 | t.alpha = 1.0 158 | 159 | else: 160 | t.alpha = renpy.random.random() 161 | t.xoffset = renpy.random.randint(-10, 10) 162 | t.yoffset = renpy.random.randint(-10, 10) 163 | t.rotate = renpy.random.randint(0, 360) 164 | 165 | return t 166 | 167 | ################################################################################ 168 | 169 | def _always_shake(st, gt, delay, x = 0, y = 0): 170 | t = Transform() 171 | 172 | t.xoffset = renpy.random.randint(-x, x) 173 | t.yoffset = renpy.random.randint(-y, y) 174 | 175 | return t 176 | 177 | def always_shake(x = 0, y = 0): 178 | return renpy.partial(_always_shake, x = x, y = y) 179 | 180 | def always_pulse(st, gt, delay): 181 | import math 182 | return Transform(alpha = math.cos((st - gt - delay) * 2.0)) 183 | 184 | ################################################################################ 185 | 186 | class FancyText(renpy.text.text.Text): 187 | 188 | def __init__(self, text, slow_effect = slow_typewriter, slow_effect_delay = 0.0, always_effect = None, **properties): 189 | super(FancyText, self).__init__(text, **properties) 190 | 191 | self.slow_effect = slow_effect 192 | self.slow_effect_delay = slow_effect_delay 193 | self.always_effect = always_effect 194 | 195 | def render(self, width, height, st, at): 196 | 197 | import math 198 | 199 | if self.style.vertical: 200 | height, width = width, height 201 | 202 | # If slow is None, the style decides if we're in slow text mode. 203 | if self.slow is None: 204 | if self.style.slow_cps: 205 | self.slow = True 206 | else: 207 | self.slow = False 208 | 209 | if self.dirty or self.displayables is None: 210 | self.update() 211 | 212 | # Render all of the child displayables. 213 | renders = { } 214 | 215 | for i in self.displayables: 216 | renders[i] = renpy.display.render.render(i, width, self.style.size, st, at) 217 | 218 | # Find the virtual-resolution layout. 219 | virtual_layout = self.get_virtual_layout() 220 | 221 | if virtual_layout is None or virtual_layout.width != width or virtual_layout.height != height: 222 | 223 | virtual_layout = renpy.text.text.Layout(self, width, height, renders, drawable_res=False, size_only=True) 224 | 225 | if len(renpy.text.text.virtual_layout_cache_new) > renpy.text.text.LAYOUT_CACHE_SIZE: 226 | renpy.text.text.virtual_layout_cache_new.clear() 227 | 228 | renpy.text.text.virtual_layout_cache_new[id(self)] = virtual_layout 229 | 230 | # Find the drawable-resolution layout. 231 | layout = self.get_layout() 232 | 233 | if layout is None or layout.width != width or layout.height != height: 234 | 235 | layout = renpy.text.text.Layout(self, width, height, renders, splits_from=virtual_layout) 236 | 237 | if len(renpy.text.text.layout_cache_new) > renpy.text.text.LAYOUT_CACHE_SIZE: 238 | renpy.text.text.layout_cache_new.clear() 239 | 240 | renpy.text.text.layout_cache_new[id(self)] = layout 241 | 242 | # The laid-out size of this Text. 243 | vw, vh = virtual_layout.size 244 | w, h = layout.size 245 | 246 | # Get the list of blits we want to undertake. 247 | if not self.slow and not self.always_effect: 248 | blits = [ renpy.text.text.Blit(0, 0, w - layout.xborder, h - layout.yborder, left=True, right=True, top=True, bottom=True) ] 249 | redraw = None 250 | else: 251 | # TODO: Make this changeable. 252 | blits = self.blits_slow(layout, st) 253 | redraw = self.redraw_slow(layout, st) 254 | 255 | # Blit text layers. 256 | rv = renpy.display.render.Render(vw, vh) 257 | # rv = renpy.Render(*layout.unscale_pair(w, h)) 258 | 259 | if renpy.config.draw_virtual_text_box: 260 | fill = renpy.display.render.Render(vw, vh) 261 | fill.fill((255, 0, 0, 32)) 262 | fill.forward = layout.reverse 263 | fill.reverse = layout.forward 264 | 265 | rv.blit(fill, (0, 0)) 266 | 267 | for o, color, xo, yo in layout.outlines: 268 | tex = layout.textures[o, color] 269 | 270 | if o: 271 | oblits = renpy.text.text.outline_blits(blits, o) 272 | else: 273 | oblits = blits 274 | 275 | for b in oblits: 276 | 277 | b_x = b.x 278 | b_y = b.y 279 | b_w = b.w 280 | b_h = b.h 281 | 282 | # Bound to inside texture rectangle. 283 | if b_x < 0: 284 | b_w += b.x 285 | b_x = 0 286 | 287 | if b_y < 0: 288 | b_h += b_y 289 | b_y = 0 290 | 291 | if b_w > w - b_x: 292 | b_w = w - b_x 293 | if b_h > h - b_y: 294 | b_h = h - b_y 295 | 296 | if b_w <= 0 or b_h <= 0: 297 | continue 298 | 299 | # Expand the blits and offset them as necessary. 300 | if b.right: 301 | b_w += layout.add_right 302 | b_w += o 303 | 304 | if b.bottom: 305 | b_h += layout.add_bottom 306 | b_h += o 307 | 308 | if b.left: 309 | b_w += layout.add_left 310 | else: 311 | b_x += layout.add_left 312 | 313 | if b.top: 314 | b_h += layout.add_top 315 | else: 316 | b_y += layout.add_top 317 | 318 | # We're cheating by using the alpha property to store a full 319 | # Transform because other functions that use the Blit create 320 | # them from scratch, so we'd lose anything we hacked in. 321 | surf = tex.subsurface((b_x, b_y, b_w, b_h)) 322 | char = renpy.display.render.Render(b_w, b_h) 323 | 324 | if isinstance(b.alpha, Transform): 325 | trans = b.alpha 326 | 327 | # Apply rotation 328 | if trans.rotate: 329 | theta = math.radians(trans.rotate) 330 | c, s = math.cos(theta), math.sin(theta) 331 | char.reverse = renpy.display.matrix.Matrix2D(c, -s, s, c) 332 | 333 | # Apply offset 334 | char.absolute_blit( 335 | surf, 336 | (trans.xoffset, trans.yoffset) 337 | ) 338 | 339 | # Apply alpha 340 | char.add_shader("renpy.alpha") 341 | char.add_uniform("u_renpy_alpha", trans.alpha) 342 | char.add_uniform("u_renpy_over", 1.0) 343 | 344 | else: 345 | char.absolute_blit( 346 | surf, 347 | (0, 0) 348 | ) 349 | 350 | # Blit. 351 | rv.absolute_blit( 352 | char, 353 | layout.unscale_pair(b_x + xo + layout.xoffset - o - layout.add_left, 354 | b_y + yo + layout.yoffset - o - layout.add_top) 355 | ) 356 | 357 | # Blit displayables. 358 | if layout.displayable_blits: 359 | 360 | self.displayable_offsets = [ ] 361 | 362 | drend = renpy.display.render.Render(w, h) 363 | drend.forward = layout.reverse 364 | drend.reverse = layout.forward 365 | 366 | for d, x, y, width, ascent, line_spacing, t in layout.displayable_blits: 367 | 368 | if self.slow and t > st: 369 | continue 370 | 371 | xo, yo = renpy.display.core.place( 372 | width, 373 | ascent, 374 | width, 375 | line_spacing, 376 | d.get_placement()) 377 | 378 | xo = x + xo + layout.xoffset 379 | yo = y + yo + layout.yoffset 380 | 381 | drend.absolute_blit(renders[d], (xo, yo)) 382 | self.displayable_offsets.append((d, xo, yo)) 383 | 384 | rv.blit(drend, (0, 0)) 385 | 386 | # Add in the focus areas. 387 | for hyperlink, hx, hy, hw, hh in layout.hyperlinks: 388 | 389 | h_x, h_y = layout.unscale_pair(hx + layout.xoffset, hy + layout.yoffset) 390 | h_w, h_h = layout.unscale_pair(hw, hh) 391 | 392 | rv.add_focus(self, hyperlink, h_x, h_y, h_w, h_h) 393 | 394 | # Figure out if we need to redraw or call slow_done. 395 | if self.slow and not self.always_effect and not redraw is None: 396 | renpy.display.render.redraw(self, max(redraw, 0)) 397 | 398 | elif self.always_effect: 399 | renpy.display.render.redraw(self, 0) 400 | 401 | if self.slow and redraw is None: 402 | self.call_slow_done(st) 403 | 404 | rv.forward = layout.forward 405 | rv.reverse = layout.reverse 406 | 407 | if self.style.vertical: 408 | vrv = renpy.display.render.Render(rv.height, rv.width) 409 | vrv.forward = VERT_FORWARD 410 | vrv.reverse = VERT_REVERSE 411 | vrv.blit(rv, (rv.height, 0)) 412 | rv = vrv 413 | 414 | return rv 415 | 416 | ################################################################################ 417 | 418 | def blits_slow(self, layout, st): 419 | """ 420 | Given a st and an outline, returns a list of blit objects that 421 | can be used to blit those objects. 422 | This also sets the extreme points when creating a Blit. 423 | """ 424 | 425 | width, max_height = layout.size 426 | 427 | rv = [ ] 428 | 429 | if not layout.lines: 430 | return rv 431 | 432 | max_y = 0 433 | top = True 434 | 435 | if not self.always_effect: 436 | 437 | # First run through and blit any lines that are entirely complete. 438 | for l in layout.lines: 439 | 440 | if l.max_time + self.slow_effect_delay > st: 441 | break 442 | 443 | max_y = min(l.y + l.height + layout.line_overlap_split, max_height) 444 | 445 | else: 446 | l = None 447 | 448 | if max_y: 449 | rv.append(renpy.text.text.Blit(0, 0, width, max_y, top=top, left=True, right=True, bottom=(l is None))) 450 | top = False 451 | 452 | if l is None: 453 | return rv 454 | 455 | # Then go back through for any that *aren't* complete and blit as needed. 456 | for l in layout.lines: 457 | 458 | if not self.always_effect and st > l.max_time + self.slow_effect_delay: 459 | continue 460 | 461 | for g in l.glyphs: 462 | 463 | if st < g.time and self.slow: 464 | continue 465 | 466 | if self.slow_effect and self.slow and st < g.time + self.slow_effect_delay: 467 | effect = self.slow_effect 468 | elif self.always_effect: 469 | effect = self.always_effect 470 | else: 471 | effect = self.slow_effect 472 | 473 | transform = None 474 | if effect: 475 | transform = effect(st, g.time, self.slow_effect_delay) 476 | 477 | left = False 478 | right = False 479 | if g is l.glyphs[0]: 480 | left = True 481 | 482 | if g is l.glyphs[-1]: 483 | right = True 484 | 485 | if transform is None: 486 | continue 487 | 488 | rv.append(renpy.text.text.Blit(g.x, l.y, g.width, l.height + layout.line_overlap_split, alpha = transform, left=left, right=right, top=top, bottom=(l is layout.lines[-1]))) 489 | 490 | return rv 491 | 492 | def redraw_slow(self, layout, st): 493 | """ 494 | Return the time of the first glyph that should be shown after st. 495 | """ 496 | 497 | for l in layout.lines: 498 | if not l.glyphs: 499 | continue 500 | 501 | if l.max_time + self.slow_effect_delay > st: 502 | break 503 | 504 | else: 505 | return None 506 | 507 | return 0 508 | 509 | ################################################################################ 510 | 511 | renpy.register_sl_displayable("fancytext", FancyText, "text", 0, scope=True, replaces=True) \ 512 | .add_positional("text") \ 513 | .add_property("slow_effect") \ 514 | .add_property("slow_effect_delay") \ 515 | .add_property("always_effect") \ 516 | .add_property("slow") \ 517 | .add_property("slow_done") \ 518 | .add_property("substitute") \ 519 | .add_property("scope") \ 520 | .add_property_group("text") 521 | 522 | ################################################################################ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 2 | 3 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ren'Py FancyText Module 2 | 3 | FancyText is a drop-in module for Ren'Py 7.3.5 that lets you display text on screens with a little more pizazz than vanilla. 4 | 5 | ![Sample](./example.gif) 6 | 7 | ### How to Use 8 | 9 | Simply drop `01_fancytext.rpy` into your game's directory and you'll have access to the new `fancytext` screen language statement. `fancytext` is identical to the built-in `text`, but with three new parameters added. 10 | 11 | * `slow_effect`: An effect that applies to each character of text as it's being displayed. 12 | * `slow_effect_delay`: The time, in seconds, `slow_effect` will take to complete. 13 | * `always_effect`: An effect that applies to each character of text for the duration that it's on-screen, after `slow_effect` has completed. 14 | 15 | In simpler terms, this allows you to make your text gently fade in or slide in instead of the default "typewriter" effect in Ren'Py. 16 | 17 | For specific examples and to see the effects in action, check out `script.rpy`, or drop the whole file into a new project. 18 | 19 | #### Included Effects 20 | 21 | Effects that begin with `slow_` are for use with the `slow_effect` parameter. Effects that have parameters must have those parameters provided. 22 | 23 | * `slow_typewriter`: A non-effect that replicates the built-in typewriter effect. 24 | * `slow_fade`: Slowly fades characters in over `slow_effect_delay` seconds. 25 | * `slow_slide_up(y = 20)`: Slides the text up `y` pixels into place over `slow_effect_delay` seconds. 26 | * `slow_slide_down(y = 20)`: Slides the text down `y` pixels into place over `slow_effect_delay` seconds. 27 | * `slow_slide_right(x = 20)`: Slides the text right `x` pixels into place over `slow_effect_delay` seconds. 28 | * `slow_slide_left(x = 20)`: Slides the text left `x` pixels into place over `slow_effect_delay` seconds. 29 | * `slow_shake(x = 0, y = 0)`: Causes each character to shake for `slow_effect_delay` seconds with an intensity of `x` horizontal pixels and `y` vertical pixels. 30 | * `slow_rotate`: Causes each character to rotate 360 degrees over `slow_effect_delay` seconds. 31 | * `slow_shaking_slide(shake_x = 0, shake_y = 0, slide_x = 0, slide_y = 0)`: A combination of `slow_shake` and the `slow_slide` functions. 32 | * `slow_nonsense`: Changes the position, alpha, and angle of every character wildly over `slow_effect_delay` seconds. 33 | 34 | Effects that begin with `always_` are for use with the `always_effect` parameter, and will affect the text for as long as it's on screen 35 | 36 | * `always_shake(x = 0, y = 0)`: Causes each character to shake with an intensity of `x` horizontal pixels and `y` vertical pixels. 37 | * `always_pulse`: Causes the text to slowly cycle between visible and invisible. 38 | 39 | Not all of these effects are practically useful, nor do I endorse them being used in games, but you do you. 40 | 41 | ### Advanced Usage 42 | 43 | To create your own effects, all you have to do is write a function with the signature of `def slow_fade(st, gt, delay)` that returns a `Transform` object. 44 | 45 | `st` is the current time. `gt` is the time at which the current glyph is expected to start displaying. And `delay` is the value of `slow_effect_delay` passed into the displayable, which is the amount of time the glyph has allotted to finish displaying. 46 | 47 | In other words, when `st >= gt + delay`, the glyph is required to be in its final location (typically given by a default `Transform` object), otherwise slow display may not work as expected. 48 | 49 | Right now, we only support a few transform parameters: 50 | 51 | * alpha 52 | * xoffset 53 | * yoffset 54 | * rotate 55 | 56 | ### License 57 | 58 | The majority of the code used here is copied/modified from the Ren'Py source code, and as such, FancyText is released under the same license as Ren'Py itself. See LICENSE for more details. -------------------------------------------------------------------------------- /example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukinogatari/Ren-Py-FancyText/d9103fde6b4f943bdcfe12e49b81118e08427f3b/example.gif -------------------------------------------------------------------------------- /script.rpy: -------------------------------------------------------------------------------- 1 | # The script of the game goes in this file. 2 | 3 | 4 | init -1: 5 | # FancyText: To use this say screen, you need to add the three parameters exactly as given! 6 | screen say(who, what, slow_effect = slow_typewriter, slow_effect_delay = 0, always_effect = None): 7 | style_prefix "say" 8 | 9 | window: 10 | id "window" 11 | 12 | if who is not None: 13 | 14 | window: 15 | id "namebox" 16 | style "namebox" 17 | text who id "who" 18 | 19 | # FancyText: Here's where all the magic happens. 20 | # Replace your usual "text" statement with "fancytext" to enable 21 | # some fancy effects on text display. 22 | fancytext what id "what" slow_effect slow_effect slow_effect_delay slow_effect_delay always_effect always_effect 23 | 24 | define e = Character("Eileen") 25 | define s = Character("Shaky", show_always_effect = always_shake(x = 1, y = 1)) 26 | define n = Character("", show_slow_effect = slow_fade, show_slow_effect_delay = 0.5) 27 | define p = Character("Pulse", show_slow_effect = slow_slide_up(15), show_slow_effect_delay = 0.5, show_always_effect = always_pulse) 28 | define r = Character("Rotate", show_slow_effect = slow_rotate, show_slow_effect_delay = 1.0) 29 | define aah = Character("AAAA", show_slow_effect = slow_nonsense, show_slow_effect_delay = 1.0) 30 | 31 | # The game starts here. 32 | 33 | label start: 34 | 35 | # Show a background. This uses a placeholder by default, but you can 36 | # add a file (named either "bg room.png" or "bg room.jpg") to the 37 | # images directory to show it. 38 | 39 | scene bg room 40 | 41 | # This shows a character sprite. A placeholder is used, but you can 42 | # replace it by adding a file named "eileen happy.png" to the images 43 | # directory. 44 | 45 | show eileen happy 46 | 47 | # These display lines of dialogue. 48 | 49 | e "Here's a regular character, like you'd use in default Ren'Py." 50 | 51 | "To see the effects of FancyText in action, make sure reduce the text speed in the preferences menu before proceeding!" 52 | 53 | n "This is a narrator whose text gently fades in as it displays, instead of popping into existence like the normal typewriter display." 54 | 55 | s "And here's a shaky character, whose text is always shaking.\nIt's kind of like the dialogue in Celeste, except it only works on the whole line." 56 | 57 | r "Every letter in the dialogue of this character rotates 360 degrees after appearing, which is really annoying! I was unable to figure out how to change the origin of the rotation, so it only rotates around the top-left." 58 | 59 | p "This character's dialogue slides up into place, and then slowly pulses in and out. I don't know what you'd use it for, but it's kind of cool!" 60 | 61 | aah "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 62 | 63 | # This ends the game. 64 | 65 | return 66 | --------------------------------------------------------------------------------