[SOLVED] Ren'Py Snake Game Runs Too Fast (How to Achieve PyGame-Like Clock Ticking)

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Post Reply
Message
Author
User avatar
GemstoneSystem
Newbie
Posts: 11
Joined: Sun Jun 19, 2022 5:04 pm
Projects: Shards
itch: gemmysystem
Contact:

[SOLVED] Ren'Py Snake Game Runs Too Fast (How to Achieve PyGame-Like Clock Ticking)

#1 Post by GemstoneSystem » Thu Aug 04, 2022 4:51 pm

I've ported a game from PyGame to a Creator Defined Displayable in Ren'Py but currently, it runs WAY too fast and I can't seem to get it to slow down without horrible input lag, such as using this to limit the framerate:

Code: Select all

import pygame

FPS = 20
clock = pygame.time.Clock()
clock.tick(FPS)
Is there any other way I can slow down my snake without having to sacrifice smooth input and things like that?

EDIT: My updated Snake code:

Code: Select all

init python:

    import random

    class SnakeDisplayable(renpy.Displayable):

        def __init__(self):

            renpy.Displayable.__init__(self)

            # Set game values
            self.SNAKE_SIZE = 60

            self.score = 0

            # Some displayables we use
            self.snake = Solid("#009933", xsize=self.SNAKE_SIZE, ysize=self.SNAKE_SIZE)
            self.snake_body = Solid("#004d1a", xsize=self.SNAKE_SIZE, ysize=self.SNAKE_SIZE)
            self.apple = Solid("#cc0000", xsize=self.SNAKE_SIZE, ysize=self.SNAKE_SIZE)

            # The positions of the displayables
            self.shx = 1920/2
            self.shy = 1080/2
            self.shdx = 0
            self.shdy = 0
            self.shxmin = 0
            self.shxmax = 1920
            self.shymin = 0
            self.shymax = 1080
            self.key_pressed = None

            self.sbxy = []

            self.ax = random.randint(0, 1920 - self.SNAKE_SIZE)
            self.ay = random.randint(0, 1080 - self.SNAKE_SIZE)

            # The time of the past render-frame
            self.oldst = None

            self.lose = False

            return

        # 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

            # This draws the snake
            def snake(shx, shy):

                # Render the snake image
                snake = renpy.render(self.snake, width, height, st, at)

                # renpy.render returns a Render object, which we can
                # blit to the Render we're making
                r.blit(snake, (int(shx), int(shy)))
            
            # This draws the apple
            def snake_body(sbxy):

                # Render the snake body image
                snake_body = renpy.render(self.snake_body, width, height, st, at)

                # renpy.render returns a Render object, which we can
                # blit to the Render we're making
                for body in sbxy:
                    r.blit(snake_body, (int(body[0]), int(body[1])))

            # This draws the apple
            def apple(ax, ay):

                # Render the apple image
                apple = renpy.render(self.apple, width, height, st, at)

                # renpy.render returns a Render object, which we can
                # blit to the Render we're making
                r.blit(apple, (int(ax), int(ay)))

            snake(self.shx, self.shy)
            snake_body(self.sbxy)
            apple(self.ax, self.ay)

            if self.key_pressed == "up":
                self.shdx = 0
                self.shdy = -1 * self.SNAKE_SIZE
            elif self.key_pressed == "down":
                self.shdx = 0
                self.shdy = self.SNAKE_SIZE
            elif self.key_pressed == "left":
                self.shdx = -1 * self.SNAKE_SIZE
                self.shdy = 0
            elif self.key_pressed == "right":
                self.shdx = self.SNAKE_SIZE
                self.shdy = 0

            # Add a segment onto the snake's body
            self.sbxy.insert(0, (self.shx, self.shy))
            self.sbxy.pop()

            # Update the x, y position of the snake's head
            self.shx += self.shdx
            self.shy += self.shdy

            if self.shx < self.shxmin or self.shx + self.SNAKE_SIZE > self.shxmax :

                self.lose = True

                renpy.timeout(0)

            if self.shy < self.shymin or self.shy + self.SNAKE_SIZE > self.shymax:

                self.lose = True

                renpy.timeout(0)
            
            if (self.shx, self.shy) in self.sbxy:

                self.lose = True

                renpy.timeout(0)

            # Check for collisions
            def is_colliding():
                return (
                    self.shx <= self.ax + self.SNAKE_SIZE and
                    self.shx + self.SNAKE_SIZE >= self.ax and
                    self.shy <= self.ay + self.SNAKE_SIZE and
                    self.shy + self.SNAKE_SIZE >= self.ay
                )
            
            if is_colliding():
                self.score += 1
                renpy.sound.play("audio/minigames/s_pick_up_sound.wav")
                self.ax = random.randint(0, 1920 - self.SNAKE_SIZE)
                self.ay = random.randint(0, 1080 - self.SNAKE_SIZE)
                self.sbxy.append((self.shx, self.shy))

            # Ask that we be re-rendered so we can show the next frame
            renpy.redraw(self, 0.04)

            # Return the Render object
            return r

        # Handles events
        def event(self, ev, x, y, st):
            
            import pygame

            if renpy.map_event(ev, "focus_up") and (self.key_pressed != "down" or self.sbxy == []):
                self.key_pressed = "up"
            elif renpy.map_event(ev, "focus_down") and (self.key_pressed != "up" or self.sbxy == []):
                self.key_pressed = "down"
            elif renpy.map_event(ev, "focus_left") and (self.key_pressed != "right" or self.sbxy == []):
                self.key_pressed = "left"
            elif renpy.map_event(ev, "focus_right") and (self.key_pressed != "left" or self.sbxy == []):
                self.key_pressed = "right"
            elif renpy.map_event(ev, "pad_lefty_zero") or renpy.map_event(ev, "pad_righty_zero") or ev.type == pygame.KEYUP:
                self.key_pressed = None

            # Ensure the screen updates
            renpy.restart_interaction()

            # If the player loses, return it
            if self.lose:
                return self.lose
            else:
                raise renpy.IgnoreEvent()

    def display_s_score(st, at):
        return Text(_("Score: ") + "%d" % snake.score, size=90, color="#cc0000", outlines=[ (4, "#800000", 0, 0) ], font="gui/font/Gallagher.ttf"), .1

default snake = SnakeDisplayable()

screen snake():

    add "minigames/s_background.jpg"

    text _("Snake"):
        xpos 240
        xanchor 0.5
        ypos 25
        size 90
        color "#cc0000"
        outlines [ (4, "#800000", 0, 0) ]
        font "gui/font/Gallagher.ttf"

    add DynamicDisplayable(display_s_score) xpos (1920 - 240) xanchor 0.5 ypos 25

    add snake

label play_snake:

    window hide  # Hide the window and quick menu while in Feed the Dragon
    $ quick_menu = False
    hide screen buttons_ui

    play music s_background_music

    $ snake.lose = False
    $ snake.key_pressed = None
    $ snake.score = 0
    $ snake.shx = 1920/2
    $ snake.shy = 1080/2
    $ snake.shdx = 0
    $ snake.shdy = 0
    $ snake.sbxy = []

    call screen snake

    play music amusement

    show screen buttons_ui(dt)
    $ quick_menu = True
    window auto

label snake_done:

    if persistent.snake_high_score >= snake.score:
        pass
    else:
        $ persistent.snake_high_score = snake.score

    "Score: [snake.score]\n\nHigh Score: [persistent.snake_high_score]"

    menu:
        "Would you like to play again?"

        "Yes.":
            jump play_snake

        "No.":
            jump arcade_game_selection
Last edited by GemstoneSystem on Thu Aug 04, 2022 8:25 pm, edited 6 times in total.

User avatar
Ocelot
Eileen-Class Veteran
Posts: 1882
Joined: Tue Aug 23, 2016 10:35 am
Github: MiiNiPaa
Discord: MiiNiPaa#4384
Contact:

Re: Ren'Py Snake Game Runs Too Fast (How to Achieve PyGame-Like Clock Ticking)

#2 Post by Ocelot » Thu Aug 04, 2022 5:22 pm

You already have basics of code nessesary to properly time your game: dtime variable. What you need is to accumulate elapsed time until it is larger than desired frame time. Only then actually do the processing.
Pseudocode:

Code: Select all

accumulated_time += dtime
if accumulated_time >= frame_duration:
    accumulated_time -= frame_duration
    process_game_step()
< < insert Rick Cook quote here > >

User avatar
GemstoneSystem
Newbie
Posts: 11
Joined: Sun Jun 19, 2022 5:04 pm
Projects: Shards
itch: gemmysystem
Contact:

Re: Ren'Py Snake Game Runs Too Fast (How to Achieve PyGame-Like Clock Ticking)

#3 Post by GemstoneSystem » Thu Aug 04, 2022 6:49 pm

Ocelot wrote:
Thu Aug 04, 2022 5:22 pm
You already have basics of code nessesary to properly time your game: dtime variable. What you need is to accumulate elapsed time until it is larger than desired frame time. Only then actually do the processing.
Pseudocode:

Code: Select all

accumulated_time += dtime
if accumulated_time >= frame_duration:
    accumulated_time -= frame_duration
    process_game_step()
I ended up changing a few things and just using renpy.redraw(self, 0.05) to control the speed of the snake. Now my issue is that it doesn't properly detect collision with its tail. If it's not one thing, it's another. Ha ha.

EDIT: That problem is fixed now, too, btw. My game works!

Post Reply

Who is online

Users browsing this forum: No registered users