1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
|
example minigame:
init python:
class PongDisplayable(renpy.Displayable):
def __init__(self):
renpy.Displayable.__init__(self)
# The sizes of some of the images.
self.PADDLE_WIDTH = 12
self.PADDLE_HEIGHT = 95
self.PADDLE_X = 240
self.BALL_WIDTH = 15
self.BALL_HEIGHT = 15
self.COURT_TOP = 129
self.COURT_BOTTOM = 650
# Some displayables we use.
self.paddle = Solid("#ffffff", xsize=self.PADDLE_WIDTH, ysize=self.PADDLE_HEIGHT)
self.ball = Solid("#ffffff", xsize=self.BALL_WIDTH, ysize=self.BALL_HEIGHT)
# If the ball is stuck to the paddle.
self.stuck = True
# The positions of the two paddles.
self.playery = (self.COURT_BOTTOM - self.COURT_TOP) / 2
self.computery = self.playery
# The speed of the computer.
self.computerspeed = 380.0
# The position, delta-position, and the speed of the
# ball.
self.bx = self.PADDLE_X + self.PADDLE_WIDTH + 10
self.by = self.playery
self.bdx = .5
self.bdy = .5
self.bspeed = 350.0
# The time of the past render-frame.
self.oldst = None
# The winner.
self.winner = None
def visit(self):
return [ self.paddle, self.ball ]
# Recomputes the position of the ball, handles bounces, and
# draws the screen.
def render(self, width, height, st, at):
# The Render object we'll be drawing into.
r = renpy.Render(width, height)
# Figure out the time elapsed since the previous frame.
if self.oldst is None:
self.oldst = st
dtime = st - self.oldst
self.oldst = st
# Figure out where we want to move the ball to.
speed = dtime * self.bspeed
oldbx = self.bx
if self.stuck:
self.by = self.playery
else:
self.bx += self.bdx * speed
self.by += self.bdy * speed
# Move the computer's paddle. It wants to go to self.by, but
# may be limited by it's speed limit.
cspeed = self.computerspeed * dtime
if abs(self.by - self.computery) <= cspeed:
self.computery = self.by
else:
self.computery += cspeed * (self.by - self.computery) / abs(self.by - self.computery)
# Handle bounces.
# Bounce off of top.
ball_top = self.COURT_TOP + self.BALL_HEIGHT / 2
if self.by < ball_top:
self.by = ball_top + (ball_top - self.by)
self.bdy = -self.bdy
if not self.stuck:
renpy.sound.play("pong_beep.opus", channel=0)
# Bounce off bottom.
ball_bot = self.COURT_BOTTOM - self.BALL_HEIGHT / 2
if self.by > ball_bot:
self.by = ball_bot - (self.by - ball_bot)
self.bdy = -self.bdy
if not self.stuck:
renpy.sound.play("pong_beep.opus", channel=0)
# This draws a paddle, and checks for bounces.
def paddle(px, py, hotside):
# Render the paddle image. We give it an 800x600 area
# to render into, knowing that images will render smaller.
# (This isn't the case with all displayables. Solid, Frame,
# and Fixed will expand to fill the space allotted.)
# We also pass in st and at.
pi = renpy.render(self.paddle, width, height, st, at)
# renpy.render returns a Render object, which we can
# blit to the Render we're making.
r.blit(pi, (int(px), int(py - self.PADDLE_HEIGHT / 2)))
if py - self.PADDLE_HEIGHT / 2 <= self.by <= py + self.PADDLE_HEIGHT / 2:
hit = False
if oldbx >= hotside >= self.bx:
self.bx = hotside + (hotside - self.bx)
self.bdx = -self.bdx
hit = True
elif oldbx <= hotside <= self.bx:
self.bx = hotside - (self.bx - hotside)
self.bdx = -self.bdx
hit = True
if hit:
renpy.sound.play("pong_boop.opus", channel=1)
self.bspeed *= 1.10
# Draw the two paddles.
paddle(self.PADDLE_X, self.playery, self.PADDLE_X + self.PADDLE_WIDTH)
paddle(width - self.PADDLE_X - self.PADDLE_WIDTH, self.computery, width - self.PADDLE_X - self.PADDLE_WIDTH)
# Draw the ball.
ball = renpy.render(self.ball, width, height, st, at)
r.blit(ball, (int(self.bx - self.BALL_WIDTH / 2),
int(self.by - self.BALL_HEIGHT / 2)))
# Check for a winner.
if self.bx < -50:
self.winner = "eileen"
# Needed to ensure that event is called, noticing
# the winner.
renpy.timeout(0)
elif self.bx > width + 50:
self.winner = "player"
renpy.timeout(0)
# Ask that we be re-rendered ASAP, so we can show the next
# frame.
renpy.redraw(self, 0)
# Return the Render object.
return r
# Handles events.
def event(self, ev, x, y, st):
import pygame
# Mousebutton down == start the game by setting stuck to
# false.
if ev.type == pygame.MOUSEBUTTONDOWN and ev.button == 1:
self.stuck = False
# Ensure the pong screen updates.
renpy.restart_interaction()
# Set the position of the player's paddle.
y = max(y, self.COURT_TOP)
y = min(y, self.COURT_BOTTOM)
self.playery = y
# If we have a winner, return him or her. Otherwise, ignore
# the current event.
if self.winner:
return self.winner
else:
raise renpy.IgnoreEvent()
screen pong():
default pong = PongDisplayable()
add "bg pong field"
add pong
text _("Player"):
xpos 240
xanchor 0.5
ypos 25
size 40
text _("Eileen"):
xpos (1280 - 240)
xanchor 0.5
ypos 25
size 40
if pong.stuck:
text _("Click to Begin"):
xalign 0.5
ypos 50
size 40
label demo_minigame:
e "You may want to mix Ren'Py with other forms of gameplay. There are a couple of ways to do this."
e "The first is with the screen system, which can be used to display data and create button and menu based interfaces."
e "Screens will work for many simulation-style games and RPGs."
e "When screens are not enough, you can write a creator-defined displayable to extend Ren'Py itself. A creator-defined displayable can process raw events and draw to the screen." id demo_minigame_a92baa6b
e "That makes it possible to create all kinds of minigames. Would you like to play some pong?"
example minigame hide:
label play_pong:
window hide # Hide the window and quick menu while in pong
$ quick_menu = False
call screen pong
$ quick_menu = True
window show
show eileen vhappy
if _return == "eileen":
e "I win!"
else:
e "You won! Congratulations."
label pong_done:
show eileen happy
menu:
e "Would you like to play again?"
"Sure.":
jump play_pong
"No thanks.":
pass
show example minigame large
e "Here's the source code for the minigame. It's very complex, and assumes you understand Python well."
e "I won't go over it in detail here. You can read more about it in the {a=https://www.renpy.org/doc/html/udd.html}Creator-Defined Displayable documentation{/a}."
hide example
e "Minigames can spice up your visual novel, but be careful – not every visual novel player wants to be good at arcade games."
e "Part of the reason Ren'Py works well is that it's meant for certain types of games, like visual novels and life simulations."
e "The further afield you get from those games, the more you'll find yourself fighting Ren'Py. At some point, it makes sense to consider other engines."
show eileen vhappy
e "And that's fine with us. We'll always be here for you when you're making visual novels."
show eileen happy
return
|