Timed Transform instantly completing when inside screen

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
astrobread
Newbie
Posts: 13
Joined: Mon Jan 21, 2013 4:01 am
Contact:

Timed Transform instantly completing when inside screen

#1 Post by astrobread »

I've been struggling with getting a simple fade-in Transform to work right while inside a Screen. The only solution that's worked completely is creating my own custom Displayable (similar to the one found here), but having to make a displayable for every type of transform I end up using seems a bit overkill. Maybe someone can tell me what I'm doing wrong here.

So I have a screen that covers the viewing area, something a bit like a map that gets drawn in as the player advances. Right now I'm trying to fade-in lines that go inbetween destinations. The first one fades-in without a problem, however every time after that the transform immediately ends without performing the fade-in. Since I'm not sure what could be relevant, I'll start with the way I open the map screen:

Code: Select all

label start:
show screen mapbutton
#......
screen mapbutton:
    vbox xalign 0.99 yalign 0.3:
        textbutton "Open Map" action ui.callsinnewcontext("map_label")
#........
label map_label:
    window hide
    call screen map_screen
    window show
    return
Now for the actual map code. I've tried to simplify it as much as possible, now it's just an ImageButton that allows you to increase a counter. The counter increase fires restart_interaction(), and the screen draws one Image with a transform for every counter you've added.

Code: Select all

init python:
    counter = 0
    def MakeLine():
        global counter
        counter += 1
        renpy.restart_interaction()

transform ttfadein:
    alpha 0.0
    1.0
    linear 3.0 alpha 1.0
    
screen map_screen:    
    viewport:
        add Solid("fff")
        imagebutton:
            hover "button3_hover.png"
            idle "button3_idle.png"
            ypos 200
            xpos 200
            clicked MakeLine
        
        for i in range(0, counter):
            add Image("redline.png", xpos= 50 + 50 * i, ypos= 50 ) at ttfadein
It seems like every image created is using the original version of the transform, instead of starting at the beginning of a new one. It was even worse when I had imagebutton hover/unhover events in the mix. That caused it to freeze and update only during mouse overs.

So should I just scrap the idea of using Transforms, or is there something I can change in the above to fix the problem?

User avatar
astrobread
Newbie
Posts: 13
Joined: Mon Jan 21, 2013 4:01 am
Contact:

Re: Timed Transform instantly completing when inside screen

#2 Post by astrobread »

Okay I think I've got a solution that allows me to use Transforms even inside a screen. The original problem was that after the first transform, the ST value being passed kept increasing freely. So when the screen redraws, even a newly added transform thinks it's already been running for 14 seconds. Hence it instantly completing instead of slowly fading in.

So I made a custom displayable that accepts a transform in the constructor. The first render it sets the st_offset to -1 * ST. Thus if the ST begins at 14 seconds the offset will be -14, making the transform start at time zero.

Things to be aware of: The transform must remain in its original state. My constructor makes a deep copy before using it, but if some outside code runs the transform it could muck up the state and you start seeing the original bug again. Even if you return to the main menu it'll remain broken.

Next: Leaving the screen and returning to it resets the ST to zero. If you're caching images like I am, you have to dump the cache and recreate it. Otherwise, if an old image has st_offset == -14, and now ST is at 0 again, your displayable will re-transform after 14 seconds brings you back to time zero. I think image caching like this isn't typical though, I just want it so when hover events redraw the screen my transforms aren't lost. My screen just pulls the images out of a list, and the list gets rebuilt only when necessary.

Alright enough of that, here's the code:

Code: Select all

     # This is how you use the displayable/transform wrapper
     # What's important is providing the xpos, ypos, and trans (the transform you want)
     # SuperTile is just a replacement for LiveTile that implements the bug fix found here:
     # https://github.com/renpy/renpy/commit/13ee355
     # Any other Displayable would work fine though

     DisplayTransformer(SuperTile("redline.png", area= (linkX, linkY, linkDist, 7)) , xpos= linkX, ypos= linkY, trans=ttfadein)

#........ Now the actual class itself ..........

init python:

    import math

    class DisplayTransformer(renpy.Displayable):

        def __init__(self, child, trans, **kwargs):
            # Pass additional properties on to the renpy.Displayable
            # constructor.
            super(DisplayTransformer, self).__init__(**kwargs)

            # The child.
            self.child = renpy.displayable(child)

            # Claim our transform and impregnate it with our displayable
            # Also make a copy of it first, otherwise we'll junk up
            # the state of the original copy
            self.trans = trans.copy()
            self.trans.set_child(self.child)
            
            # Is this our first render? oh the excitement
            self.firstupdate = True
            
        def render(self, width, height, st, at):            
            
            # If this is our first time running this transform
            # Renpy might tell us we're starting at 4.5 seconds
            # So let's offset by -4.5 seconds so we actually
            # begin at step 0 like we should                         
            if self.firstupdate == True:
                self.trans.at_offset = -1 * at
                self.trans.st_offset = -1 * st
                
                # We want to progress beyond time zero so stop this from ever happening again
                self.firstupdate = False

            # Create a render from the child.
            child_render = renpy.render(self.trans, width, height, st, at)

            # Get the size of the child.
            childWidth, childHeight = child_render.get_size()

            # Create the render we will return.
            render = renpy.Render(childWidth, childHeight)

            # Blit (draw) the child's render to our render.
            render.blit(child_render, (0, 0))

            # Return the render.
            return render

Post Reply

Who is online

Users browsing this forum: Google [Bot], MisterPinetree