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 这种语法了
