The Displayable will be an image you can use as any other image within Renpy. Feel free to use it, change it and comment or give feedback if you have suggestions regarding the code!
Example picture: Code for the Displayable including detailed comments and argument description:
Code: Select all
init python:
import math
class CurvedText(renpy.Displayable):
def __init__(self, what, points, rot=True, t_start=0.0, t_end=1.0, **kwargs):
'''
what: the text we want to curve
points: a list of n > 2 points that define the curve, e.g. [(0, 0), (150, 150), (300, 0)]
rot: whether or not to rotate the letters along the curve
t_start: where to start on the curve (0.0 <= t_start < 1.0)
t_end: where to end on the curve (0.0 < t_end <= 1.0)
optional: pass keyword arguments for the text like "style", "font", "size", "color" etc.
'''
super(CurvedText, self).__init__(**kwargs)
# a list of arguments we will use for the final composite image
args = []
l = len(what)
n = len(points)
# current position along the curve (0.0 = start, 1.0 = end)
t = t_start
# equal step distance depending on the text length
td = (t_end - t_start) / max(1, l - 1)
# loop through all characters of the text
for i in range(l):
# skip whitespaces
if not what[i].strip():
t += td
continue
# use "De Casteljau's algorithm" to find the point (x, y) on the curve at value t
q = [list(p) for p in points]
t0 = 1 - t
for j in range(1, n):
for k in range(n - j):
q[k][0] = q[k][0] * t0 + q[k+1][0] * t
q[k][1] = q[k][1] * t0 + q[k+1][1] * t
# the point on the curve was saved in q[0]
x = int(q[0][0])
y = int(q[0][1])
# if we want to rotate, we need the vector of the tangent through this point (x, y)
if rot:
# the last character's tangent if t_end == 1.0 is not found via the algorithm
# but we know that it equals the line between the last two points
if i == l - 1 and t_end == 1.0:
u = points[-1][0] - points[-2][0]
v = points[-1][1] - points[-2][1]
# in all other cases the algorithm's nature already got it
else:
u = q[1][0] - q[0][0]
v = q[1][1] - q[0][1]
# now just calculate the angle between the vector and the x-axis
r = math.degrees(math.atan2(v, u))
args.append(Transform(Text(what[i], **kwargs), xcenter=x, ycenter=y, rotate=r, subpixel=True))
else:
args.append(Transform(Text(what[i], **kwargs), xcenter=x, ycenter=y, subpixel=True))
# move one step along the curve
t += td
self.child = Fixed(*args)
return
def render(self, width, height, st, at):
cr = renpy.render(self.child, width, height, st, at)
self.width, self.height = cr.get_size()
rv = renpy.Render(self.width, self.height)
rv.blit(cr, (0, 0))
return rv
Code: Select all
define curve_3_points = [(50, 50), (250, 150), (450, 50)]
define curve_4_points = [(50, 150), (400, 50), (500, 450), (800, 350)]
define curve_5_points = [(200, 550), (400, 400), (600, 700), (1000, 650), (800, 300)]
image ct1 = CurvedText("Hello, I'm a 3-point curved text!", curve_3_points)
image ct2 = CurvedText("I will be written from start to end by default!", curve_4_points, size=16, color="#f0f")
image ct3 = CurvedText("I am on the same curve, but start at 25%!", curve_4_points, t_start=0.25, size=16, color="#f0f")
image ct4 = CurvedText("And I love stopping prematurely!", curve_4_points, t_end=0.75, size=16, color="#f0f")
image ct5 = CurvedText("This one is without letter rotation!", curve_4_points, rot=False, color="#ff0")
image ct6 = CurvedText("Wow, 5 point curves really rock...!", curve_5_points, color="#0ff")
label start:
show ct1
show ct2
show ct3:
yoffset 50
show ct4:
yoffset 100
show ct5:
yoffset 150
show ct6
window auto hide
pause