首页 > 经验记录 > RenPy怎么实现下雨演出?

RenPy怎么实现下雨演出?

下面代码是我抄了一下Github上别人写的程序,然后自己改了改,目前找不到原地址了

改的主要是两个点,一个是雨水大小,一个是新增了透明通道

是因为我发现抄过来之后,下雨会导致视野变得更昏暗一点,不太符合直觉,所以我给小修复了一下

两个文件

 

1、生成下雨素材

generate-rain.py

#!/usr/bin/python3

# This generates the rain images that serve as the base for the rain effect,
# and isn't expected to be used at runtime.
#
# You will need an installation of Python 3 with Pillow library in it.

from PIL import Image, ImageDraw, ImageFilter
import random

# Background color, serves as a shadow.
BLANK = (0, 0, 0, 0)
# Foreground color.
RAIN = (255, 255, 255, 180)

# Width and height must be even divisors of screen width and height,
# respectively, but they can (and should) be smaller than the screen.
# For my 1920x1080 screen, 6x3 makes a reasonable 320x360 texture.
WIDTH = 1920 // 6
HEIGHT = 1080 // 3

# Playing with these values and re-generating the images
# will be required to get good results for a specific use case.

# Thickness affects how dense the rain is.
THICKNESS = 0.3
# Angle of rain: multiplier for y offset.
ANGLE = 8

# ---------------
# These constants were arrived at through trial and error.
SHORT = 350
MEDIUM = 250
LONG = 150

MARGIN = int((max(WIDTH, HEIGHT) / 100) * 20)

def drawlines(image, number, scale):
    number = int(number)
    draw = ImageDraw.Draw(image)
    for line in range(0, int(number)):
        x = random.randint(0 - MARGIN, WIDTH + MARGIN)
        y = random.randint(0 - MARGIN, HEIGHT + MARGIN)
        scaleoff = random.random() * 0.5
        xoff = x - int(5 * (scale + scaleoff))
        yoff = y + int(5 * ANGLE * (scale + scaleoff))
        # This trick makes the generated texture tile seamlessly
        # by drawing each line four extra times, so that if
        # it crosses any of the edges, it definitely has a match
        # on the other end.
        draw.line((x, y, xoff, yoff), fill=RAIN)
        draw.line((x - WIDTH, y, xoff - WIDTH, yoff), fill=RAIN)
        draw.line((x + WIDTH, y, xoff + WIDTH, yoff), fill=RAIN)
        draw.line((x, y - HEIGHT, xoff, yoff - HEIGHT), fill=RAIN)
        draw.line((x, y + HEIGHT, xoff, yoff + HEIGHT), fill=RAIN)

    return image

def generate(multiplier, step):
    plane = Image.new("RGBA", (WIDTH, HEIGHT), BLANK)

    # 后续逻辑保持不变
    if step == 1:
        plane = drawlines(plane, SHORT * multiplier, 1)
    if step == 2:
        plane = drawlines(plane, MEDIUM * multiplier, 3)
    if step == 3:
        plane = drawlines(plane, LONG * multiplier, 6)

    return plane

random.seed()

img = generate(THICKNESS, 1)
img.save("_rain-short.png")

img = generate(THICKNESS, 2)
img.save("_rain-medium.png")

img = generate(THICKNESS, 3)
img.save("_rain-long.png")

生成几张雨水素材后,可将其置入 game/effects 中

 

2、定义下雨的演出

raineffect.rpy

init:
    python:

        # RainBlur is how much to blur rain.
        RainBlur = 4

        # RainAlpha is the total alpha level of the entire rain sheet.
        RainAlpha = 0.75

        # RainY is rain speed, basically how long does it take
        # for the rain sheet to fall down by one tile
        RainY = 0.16

        # RainX is the same for horizontal movement,
        # and needs to be manually adjusted to fit the chosen raindrop angle
        # for the rain to fall naturally.
        RainX = RainY * 9
        
        # Total alpha of the rain is the sum of the alpha of all three layers,
        # so each sheet has a third of it.
        RainLayerAlpha = RainAlpha / 3

        # Speed of the medium sheet of rain is two times faster than the front sheet.
        RainYM = RainY / 2
        RainXM = RainX / 2

        # Speed of the futhest sheet of rain is two times faster than that.
        RainYF = RainYM / 2
        RainXF = RainXM / 2

        ######################################

        # Automatically find our rain files, which are assumed to live in the module directory:
        RainFiles = renpy.os.path.dirname(renpy.get_filename_line()[0]).split("game/")[-1]

        # We're making three displayables, each bigger than the screen by the size of one rain 
        # tile, in both directions.
        #
        # Check generate-rain.py for how the raindrops are made.

        RainTileSizeX, RainTileSizeY = renpy.image_size(RainFiles+"/_rain-long.png")
        
        # Amazingly, using ATLs xtile/ytile to tile the rain images actually results
        # in a lot more CPU usage.
        RainsheetLong = Composite(
            (config.screen_width + RainTileSizeX, config.screen_height + RainTileSizeY),
            (0, 0), Tile(RainFiles+"/_rain-long.png"))
        RainsheetMedium = Composite(
            (config.screen_width + RainTileSizeX, config.screen_height + RainTileSizeY),
            (0,0), Tile(RainFiles+"/_rain-medium.png"))
        RainsheetShort = Composite(
            (config.screen_width + RainTileSizeX, config.screen_height + RainTileSizeY),
            (0,0), Tile(RainFiles+"/_rain-short.png"))
    
    # This defines the far sheet of the rain.
    # You show this one /behind/ character sprites.
    # It has the shortest (more distant) and fastest moving raindrops.
    # In theory you can split it and put sprites between each of the three sheets,
    # but I didn't need that.
    image rainback scroll:
        # Distant drops
        contains:
            RainsheetShort
            blur RainBlur
            alpha RainLayerAlpha
            subpixel True
            parallel:
                ypos -RainTileSizeY
                linear RainYF ypos 0
                repeat
            parallel:
                xpos 0
                linear RainXF xpos -RainTileSizeX
                repeat
        # Medium drops
        contains:
            RainsheetMedium
            blur RainBlur
            alpha RainLayerAlpha
            subpixel True
            parallel:
                ypos -RainTileSizeY
                linear RainYM ypos 0
                repeat
            parallel:
                xpos 0
                linear RainXM xpos -RainTileSizeX
                repeat

    # This is the front sheet of the rain, it goes /above/ the 
    # character sprites.
    image rainfront scroll:    
        contains:
            RainsheetMedium
            blur RainBlur
            alpha RainLayerAlpha
            subpixel True
            parallel:
                ypos -RainTileSizeY
                linear RainYF ypos 0
                repeat
            parallel:
                xpos 0
                linear RainXF xpos -RainTileSizeX
                repeat
        # Medium drops
        contains:
            RainsheetLong
            blur RainBlur
            alpha RainLayerAlpha
            subpixel True
            parallel:
                ypos -RainTileSizeY
                linear RainYM ypos 0
                repeat
            parallel:
                xpos 0
                linear RainXM xpos -RainTileSizeX
                repeat

    # Static sheets of rain for use when the rain does not need to animate,
    # e.g. when the time has stopped.
    image rainback static:
        contains:
            RainsheetShort
            alpha RainLayerAlpha
            blur RainBlur
        contains:
            RainsheetMedium
            alpha RainLayerAlpha
            blur RainBlur

    image rainfront static:        
        contains:
            RainsheetLong
            alpha RainLayerAlpha
            blur RainBlur

这里定义的几个image可以看着调整。

 

 

准备工作做完后,就可以引用了。

我们定义了雨水的背景、雨水的前景,使用时可以直接使用命令:

show rainback scroll

show rainfront scroll

 

 

需要注意,RenPy有一个图层概念。

它默认有一系列的定义好的图层:master、transient、screens、overlay

但是一般都不需要管,因为你设计演出时和其他的图层八成没关系。

你只要不是直接去指定其图层属于哪儿,那么它就默认会在【master】这个图层里,而你在一个图层中的show语法,只要不显示指定zorder,那么默认zorder = 0;此时会直接根据你命令的先后顺序来决定由谁覆盖谁。

比如你的命令顺序为

  • 展示背景(场景)
  • 展示雨水背景
  • 展示人物立绘
  • 展示雨水前景

那么,这个图层的顺序默认就是符合直觉的,严格按照命令先后来决定图层层级,无需调整。

如果有需要,也可以手动设置zorder,比如先让立绘出现,一段剧情后,再在背景、前景里下雨。此时就需要写 show xxx zorder 5 这种语法了

 

           


CAPTCHAis initialing...
EA PLAYER &

历史记录 [ 注意:部分数据仅限于当前浏览器 ]清空

      00:00/00:00