renpy/tutorial/game/indepth_minigame.rpy

287 lines
9.3 KiB
Plaintext
Raw Normal View History

2023-01-18 22:13:55 +00:00
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