287 lines
9.3 KiB
Plaintext
287 lines
9.3 KiB
Plaintext
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
|