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
|