Resetting animations within a live composite

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.
Message
Author
Onishion
Veteran
Posts: 295
Joined: Mon Apr 20, 2015 10:36 am
Contact:

Re: Resetting animations within a live composite

#16 Post by Onishion »

Ok, getting closer and closer. . .;)

Ok, so I tested out your version, and I can get your test thing to function, and it does all the things you said it should, pausing, continuing, that's awesome and I can't wait to play with it more. I did hit a snag though where I dumped it into my existing LC structure, and it would fail to launch saying "test_case not defined". I seem to remember running into that before, something about LCs needing all their ducks in a row by a certain time, but I'll keep trying to figure that one out.

Anyway, so if I want to use the LC you made, I have to call it as an expression, not just "show TestLC," right?

Also, just to make things super complicated, in the final version I was using, I had one LC, that was combining two other sets of potential LCs, like basically here's the final displayed code:

Code: Select all

image TestFinal:
    LiveComposite(                                                                           
        (100,100),  
        (0,0), ConditionSwitch(            
            "True", "ImageA_AnimA",           
            ),  
        (0,0), ConditionSwitch(            
            "True", "ImageB_AnimA",           
            ),  
        )
    align (0.5,0.0)
    
image ImageA = LiveComposite(                                                                           
        (100,100),  
        (0,0), ConditionSwitch(            
            "True", "SomeImage",           
            ),  
        )    

image ImageB = LiveComposite(                                                                           
        (100,100),  
        (0,0), ConditionSwitch(            
            "True", test_case,   #your displayable switcher here        
            ),  
        )   

image ImageA_AnimA:           
    contains:        
        "ImageA"   
        pause .4
        block: 
            ease .2 ypos 10
            ease .2 ypos 5
            repeat
            
image ImageB_AnimA:           
    contains:        
        "ImageB"   
        pause .4
        block: 
            ease .2 ypos 10
            ease .2 ypos 5
            repeat
Only in the actual version there are more layers and conditions for each of them. The "TestFinal" LC is the one that is manually shown and hidden at fixed locations, the two intermediate LCs allow those portions of the image to move independently while locked to the same piece (so that they don't fly off on their own), and then one of those contains the "complex animation" switcher element that you came up with.

I think I can still figure out a way to make this work using what you've already given me, but if there's a simpler way to make it work within the current set-up, that'd be great. Either way, thanks for the help.

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Resetting animations within a live composite

#17 Post by xela »

Onishion wrote:but if there's a simpler way to make it work within the current set-up, that'd be great. Either way, thanks for the help.
Prolly not, maybe a lot more complicated way...

The reason we have to use the default statement (instantiate displayable after label start) is that we desire to control them and therefor need access to their instances or condition them of variables in a scope we can easily access. I don't remember if I deleted the post where I explained what I wanted to do but I've posted a version of code from my game that I planned to improve into a switcher that can pause/reset stuff, to me it's perfect because I almost always work with instances and do not declare images, especially in large/complex cases.

So in your example:

TestFinal
ImageB
test_case

All got to be defined as instances after label start or with default statement (same thing), in reversed order t I put them in (test_case first, TestFinal last).

Alternatives to such definitions are:

1) Use a variables on global or predefined local scope that can always be accessed to toggle the "mode" and state of the displayable.

2) Add identifier to every displayable and look for them in the shown displayable (somehow, I never done this before) to access the instance and call it's method.

3) Some combination of two.

4) Something can prolly be done with persistent/your own version of persistent, but I wouldn't advice it.

Otherwise, you'll have to instantiate all Switchers and ALL of their parents after label start (or using the default statement) as shown in the examples. There is polly some other workaround but they usually result in really sh!tty code.

If declaration of images with use of the normal Ren'Py system is a must, I'd go with the most convenient/simplest variant of 1) (there are at least three decent ways of coding it) because I have no clue where to start with 2) or 3) without diving into Ren'Py code again.



Note:

Code: Select all

ConditionSwitch(           
            "True", test_case,   #your displayable switcher here       
            )
This should never be useful unless you wrote a lot of code in your game and wish to preserve the CS variable controls (in which case you should prolly do the 1) variant that works of the global scope and kill two birds with one stone). If you are writing new code, you should have access to test_case variable anyway and it can do whatever CS did before.
Like what we're doing? Support us at:
Image

Onishion
Veteran
Posts: 295
Joined: Mon Apr 20, 2015 10:36 am
Contact:

Re: Resetting animations within a live composite

#18 Post by Onishion »

All got to be defined as instances after label start or with default statement (same thing), in reversed order t I put them in (test_case first, TestFinal last).
So basically what you're saying is, if I change them all from "image. . ." to "default. . ." as your example did, and then I put them in the order so that the bottom-most (no dependencies) layer of things is read in before the top-most layer, then that should work otherwise more or less how I had it? Worth a shot.

I basically want the easiest way of doing things mostly how I had them. ;) In my current version, I show the image to the stage by calling a small Renpy label that essentially just does "show image at. . ." and some various other little things, and then returns, so essentially it's just a show image/hide image thing. The image that gets shown is the "final" LC, which is made up of two condition blocks, the topmost and bottom most ones. Each of those blocks has several options that are controled in the condition switch by a variable, and essentially just references out to images that have built in animation cycles, so that when I'm "moving forward" then it uses the "moving forward" chassis and the "moving forward" wheels. Then each of those is just animating another LC, the shape of which is defined by various other condition switches.

It's also worth noting that I use "if renpy.showing("imagename"):" at various points in the code, to determine whether that image is visible at the moment it checks, and if this new element would break that test then I would need to do some significant workarounds to accomplish the same functions, but it wouldn't be the end of the world if that's necessary.
This should never be useful unless you wrote a lot of code in your game and wish to preserve the CS variable controls (in which case you should prolly do the 1) variant that works of the global scope and kill two birds with one stone). If you are writing new code, you should have access to test_case variable anyway and it can do whatever CS did before.
Like I said, I put the CS into the example to show in a minimalist way that this is what exists in the actual version, without needing several pages of junk code in the example. The example only shows the one result, but in the actual LCs there are several Condition Switch layers, each with several possible outcomes. So just assume that any time I put a condition switch into my example code, there are actually several others under it, and each of them would have 2-10 different potential options. For layers that don't need condition switches, I would just use (0,0), "imagename".

There may be other methods I could use to achieve the desired level of customization, but so far an LC with condition switches is the best method I've learned.

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Resetting animations within a live composite

#19 Post by xela »

Code: Select all

if renpy.showing("imagename")
This will not work any longer. It could be a potential start point when looking into how option 2) could be achieved. You could look into variant 1), if you understand the Switcher I wrote, it shouldn't be hard to have it work of a local, easy to access scope, then everything could be declared as images just like before. I will not have time for that in the next few days but I can give a shot afterwards. Honestly, I see no reason for it not to work. I can be as simple a list where you throw strings to be analyzed and removed in render method, calling corresponding functions. Prolly 7 - 8 lines of extra code (maybe 20 - 30 tops if something unexpected pops up) and testing if it is working properly, if done right, there will be no need to "default" anything and it all could be done with image definitions as before with everything else working as well (like the rp.showing() func).


My problem is that I do not generally think in image definitions as they take away a bit of freedom while adding some coding speed/convenience.
Like what we're doing? Support us at:
Image

Onishion
Veteran
Posts: 295
Joined: Mon Apr 20, 2015 10:36 am
Contact:

Re: Resetting animations within a live composite

#20 Post by Onishion »

This will not work any longer. It could be a potential start point when looking into how option 2) could be achieved. You could look into variant 1), if you understand the Switcher I wrote, it shouldn't be hard to have it work of a local, easy to access scope, then everything could be declared as images just like before. I will not have time for that in the next few days but I can give a shot afterwards.
If you could, that would be awesome, and no rush on this end, I have other elements I could be focusing on for months. I believe I would understand how to "drive" the switcher code that you built, and will try to do so for perhaps at least some other elements of my game, but I understand almost nothing about how it actually works, how to take it apart and put it back together. That sort of class-based coding is a bit beyond me at the moment and I'd have to look up at least half the terms being used.

In any case, I greatly appreciate the help you've offered, and think I can make at least some use of the tools already provided. Thanks again and good luck on your own projects. ;)

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Resetting animations within a live composite

#21 Post by xela »

Oki, I'll finish it closer to weekend or on weekend, I'll consider the best approach in the meanwhile, although list does sound really appealing cause it's easy to code and super fast to check inside of the render method. Still, looking into how CS does it is also a must cause PyTom may have though of a good way that I haven't even considered to do the same thing.

===
I'll try to do it in a way similar to SC, it's approach is way better than my list idea.
Like what we're doing? Support us at:
Image

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Resetting animations within a live composite

#22 Post by xela »

Oki, so I've came up with the first version of Displayable Switcher that can also work off logic similar to CS. Now it will take extra "conditions" kwargs, which if provided, will evaluate a conditional string. Rules are the same as with normal CS so conditions are updated once per interaction. All previous logic is still at place so if you have access to the instance, it is possible to change the displayable even in the middle of any interaction. There could be holes in this... so we'll keep it updated if required. Still works off the same example and now will work with normal Ren'Py image declarations.

Code: Select all

init python:
    
    from collections import OrderedDict
    
    class DisplayableSwitcher(renpy.Displayable, NoRollback):
        DEFAULT = {"d": Null(), "start_st": 0, "pause_st": 0, "force_pause": 0, "force_resume": 0}
        
        """This plainly switches displayable without reshowing the image/changing any variables by calling change method.
        """
        
        def __init__(self, start_displayable="default", displayable=None, conditions=None, **kwargs):
            """Expects a dict of displayable={"string": something we can show in Ren'Py}
           
            Default is Null() unless specified otherwise.
            """
            super(DisplayableSwitcher, self).__init__(**kwargs)
            if not isinstance(displayable, dict):
                self.displayable = {"default": self.DEFAULT.copy()}
            else:
                self.displayable = {}
                for s, d in displayable.iteritems():
                    self.displayable[s] = self.DEFAULT.copy()
                    d = renpy.easy.displayable(d)
                    if isinstance(d, ImageReference):
                        d = renpy.display.image.images[(d.name)]
                    self.displayable[s]["d"] = d
                    if isinstance(d, renpy.atl.ATLTransformBase):
                        self.displayable[s]["atl"] = d.copy()
                        
                self.displayable["default"] = displayable.get("default", self.DEFAULT.copy())
                        
            if not isinstance(conditions, (tuple, list)):
                self.conditions = None
            else:
                self.conditions = OrderedDict()
                for c, a in conditions:
                    code = renpy.python.py_compile(c, 'eval')
                    self.conditions[c] = a
                    
            self.d = self.displayable[start_displayable]
            self.animation_mode = "normal"
            self.last_st = 0
           
        def per_interact(self):
            if self.conditions:
                for c, v in self.conditions.iteritems():
                    if renpy.python.py_eval_bytecode(c):
                        s = v[0]
                        if len(v) > 1:
                            mode = v[1]
                        else:
                            mode = "normal"
                        self.change(s, mode)
                        break
                    
        def change(self, s, mode="normal"):
            self.d = self.displayable[s]
            
            self.animation_mode = mode
            if mode == "reset":
                self.d["force_restart"] = 1
            elif mode == "pause":
                self.d["pause_st"] = self.last_st - self.d["start_st"]
            elif mode == "resume":
                self.d["force_resume"] = 1
            
        def render(self, width, height, st, at):
            if not st:
                for d in self.displayable.itervalues():
                    d["start_st"] = 0
                    d["pause_st"] = 0
                    
            rp = store.renpy
            
            self.last_st = st
            
            if self.animation_mode == "reset":
                if self.d["force_restart"]:
                    self.d["force_restart"] = 0
                    if "atl" in self.d:
                        self.d["d"].take_execution_state(self.d["atl"])
                        self.d["d"].atl_st_offset = st
                    else:
                        self.d["start_st"] = st
                st = st - self.d["start_st"] if not "atl" in self.d else st
            elif self.animation_mode in ("pause", "show_paused"):
                st = self.d["pause_st"]
            elif self.animation_mode == "resume":
                if self.d["force_resume"]:
                    self.d["force_resume"] = 0
                    self.d["start_st"] = st
                st = st - self.d["start_st"] + self.d["pause_st"]
                
            d = self.d["d"]
            cr = d.render(width, height, st, at)
            size = cr.get_size()
            render = rp.Render(size[0], size[1])
            
            try:
                position = d.get_placement()
                x, y = position[:2]
                if x is None:
                    x = 0
                if y is None:
                    y = 0
            except:
                x, y = 0, 0
            render.blit(cr, (x, y))
            
            rp.redraw(self, 0)
            return render
           
        def visit(self):
            return [v["d"] for v in self.displayable.values()]
            
default a = 100

image TestAnimatedImage:
    contains:
        Text("Meow")
    contains:
        Text("Meow2")
        pos (0, 100)
        ease .5 pos (100, 0)
        
image test_case = DisplayableSwitcher(displayable={"test": "TestAnimatedImage"}, conditions=(("a == 0", ("test", )),
                                                                                                                                                         ("a == 1", ("test", "reset")),
                                                                                                                                                         ("a == 2", ("test", "pause")),
                                                                                                                                                         ("a == 3", ("test", "show_paused")),
                                                                                                                                                         ("a == 4", ("test", "resume")),
                                                                                                                                                         ("a > 4", ("default", ))))

image TestLC = LiveComposite(                                                                     
    (100, 100),
    (0, 0), "test_case")

label start:
    show TestLC at truecenter
    while 1:
        $ a = 0
        "Shown Normally"
        $ a = 100
        "Hidden"
        $ a = 1
        "Reset"
        $ a = 2
        "Pause"
        $ a = 100
        "Hide Again"
        $ a = 3
        "Show Paused"
        $ a = 4
        "Resume From where we left off"

=====================================================>>>>>
I should add that this design can actually solve a bunch of issues like using delayed ATL on screens for example that usually go unanswered. Reset mode can be bound to a screen action for example. Prolly some other issues can be solved with this as well.. still a lot of testing is required.
Like what we're doing? Support us at:
Image

Onishion
Veteran
Posts: 295
Joined: Mon Apr 20, 2015 10:36 am
Contact:

Re: Resetting animations within a live composite

#23 Post by Onishion »

Wow, that was fast. I've got it tentatively working, doing what I'd intended. I need to play with it some more to figure out the best ways to apply it, but it's very cool. One minor issue, which may just require playing around with it, is that the pause function works, but the "resume from pause" thing doesn't exactly. I've just been using your looping test script for now, and what happens is that the script works as intended up until the line reads "resume from where we left off," which does not cause it to move yet (while previous steps do show it doing what it says), and then when I click to advance it just returns to "show normally," rather than resuming where it left off. This is still an improvement, since I can at least reset the animation each time, but that resume function was nice, and worked in the previous iteration.

Anyway, thanks again, this is already super helpful.

edit: I mentioned something about the main LC moving position, but I think that was an unrelated issue, nm

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Resetting animations within a live composite

#24 Post by xela »

Onishion wrote: is that the pause function works, but the "resume from pause" thing doesn't exactly. I've just been using your looping test script for now, and what happens is that the script works as intended up until the line reads "resume from where we left off," which does not cause it to move yet (while previous steps do show it doing what it says), and then when I click to advance it just returns to "show normally," rather than resuming where it left off.
It is working for me :) I actually expected some issues when thinking about the design but last version just worked for me without hiccups with new conditions added. This new testcase only has half second of movement in it, maybe you're clicking to continue when the movement is done? Try adding repeat to atl instructions for "Meow2" text or slow it down. If there are still issues, it should be possible to work all of them out.
Onishion wrote:I mentioned something about the main LC moving position, but I think that was an unrelated issue, nm
They should only happen if it is aligned or put in a container that forces positions (or both) and the LC or another element of that container changes it's size. As the size changes, relative position also has to be adjusted. Solution depends on each particular case (but there is always a solution one the cause is known).
Like what we're doing? Support us at:
Image

Onishion
Veteran
Posts: 295
Joined: Mon Apr 20, 2015 10:36 am
Contact:

Re: Resetting animations within a live composite

#25 Post by Onishion »

It is working for me :) I actually expected some issues when thinking about the design but last version just worked for me without hiccups with new conditions added. This new testcase only has half second of movement in it, maybe you're clicking to continue when the movement is done? Try adding repeat to atl instructions for "Meow2" text or slow it down. If there are still issues, it should be possible to work all of them out.
Well, I was actually applying it to my own animation sequence, the one I was working at the start of all this. I just changed the name of my stuff to fit into your system (once I get into it seriously I'll swap it back the other way around but I wanted to change as little as possible). The actual animation cycle I was using was:

Code: Select all

contains:
       "image"   
        zoom .55        
        block:
            zoom .55
            easeout 1 zoom .80           
            pause 1
            ease .5 zoom .50     
            pause 2

contains:
        "image"                
        pos (218,535) 
        block:
            pos (218,535)                                       
            ease 2 pos (218,508)            
            ease .5 pos (218,500) 
            pause 2
Now when I first tested that, I had also left in a "repeat" at the end of each block, but in the intended version it just does the thing and stops, ready to be restarted later. Anyway, I'd tried that with your old one and it worked as you intended, but with your new one, it pauses, it resets, it can reshow where it paused, but when it tries to continue from the pause it just stays put (but then resets when the loop starts over). Btw, I'm aware that this is not the most efficient way to write that animation, but it's a work in progress and I like to start messy and then clean up what I don't need once it works right. There were several other layers to that animation as well, using similar elements and synced up. They did all work in sync too, starting and stopping at the same time, even if the "resume" function wasn't working.
They should only happen if it is aligned or put in a container that forces positions (or both) and the LC or another element of that container changes it's size. As the size changes, relative position also has to be adjusted. Solution depends on each particular case (but there is always a solution one the cause is known).
Yeah, don't worry about that, it must have been something unrelated that I'd changed when I was fiddling around with stuff, since it persisted even when I turned your features off, I'll figure it out but it has nothing to do with your stuff.

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Resetting animations within a live composite

#26 Post by xela »

Are those "image" bits also animations of some sort? I tried this (just adjusted the positions a bit cause I couldn't see second part in my test project):

Code: Select all

image TestAnimatedImage:
    contains:       
        Text("Meow")
        zoom .55       
        block:
            zoom .55
            easeout 1 zoom .80           
            pause 1
            ease .5 zoom .50     
            pause 2
    
    contains:
        Text("Meow2")              
        pos (100, 100)
        block:
            pos (218, 535)                                       
            ease 2 pos (100, 100)           
            ease .5 pos (218, 500)
            pause 2
and this seems to pause and resume from where it left of properly.

Trying to remember what issue I expected to arise (in general) but can't at the moment :D I am also not 100% sure that it can pause every animation that can be created... ATL can do some pretty complex stuff but it worked in simpler cases so far, including the one above.
Like what we're doing? Support us at:
Image

Onishion
Veteran
Posts: 295
Joined: Mon Apr 20, 2015 10:36 am
Contact:

Re: Resetting animations within a live composite

#27 Post by Onishion »

No, in this case at least, the "images" are all just direct links to a png file, and they all display properly, move properly, do everything right except the "resume" function. I'll keep playing with it though, try to troubleshoot a case where it definitely does work or doesn't work. Don't worry about it too much until I can replicate it more precisely.

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Resetting animations within a live composite

#28 Post by xela »

Oki, I finally for back to normal internet so I am downloading the last Ren'Py update. Other than that, I don't really know why it would work one way on my end and differently at yours.

===>
Just tested it on the latest prerelease, it works fine???
Like what we're doing? Support us at:
Image

Onishion
Veteran
Posts: 295
Joined: Mon Apr 20, 2015 10:36 am
Contact:

Re: Resetting animations within a live composite

#29 Post by Onishion »

Ok, I'm still troubleshooting. I loaded up your exact test case as you had it with the meows, and that did work just as you said, so it's something about the interaction of my images and your code that is causing issues. Now I'm going to try turning various bits of mine on and off and see what works and what doesn't.

Onishion
Veteran
Posts: 295
Joined: Mon Apr 20, 2015 10:36 am
Contact:

Re: Resetting animations within a live composite

#30 Post by Onishion »

Ok, after a great deal of trial and error, I've located the problem circumstance, but don't yet have a solution to it. The problem comes up when I am trying to use the surrounding LC with condition switches. Here is a simplified example:

Code: Select all

image TestLC = LiveComposite(                                                                     
    (100,100),    
    (0, 0), "LC1",
    (0, 0), "LC2",
#    (0, 0), ConditionSwitch(                                                             
#            "True", "LC2",            
#            ),
    )
That line of code, in which LC1 and LC2 are both standard LCs with a bunch of condition switches, will run fine, as you intended. It goes through the test routine with flying colors. If, however, I comment out the "LC2" line, and un-comment the other segment, adding condition switches into this final LC, then the resume function (and only the resume function) stops working. So basically the system works fine with a standalone animated image, it works fine if that image is a part of an LC with condition switches, and works fine if the first LC with switches is inside of another LC without switches, but if the first LC is part of another LC with switches, something seems to break. The other functions still work, just not the resume function.

Again, if this can't be easily fixed, I'll figure out some workaround to just not use layered LCs like that, or not use Condition Switches like that in the final LC.

Anyway, as I was playing around with this to troubleshoot it, I'm trying to figure out how best to manipulate it in actual practice. "a" is a variable you added, is it the same variable for all instances of this function, or is it unique to each? Like if I wanted to have two separate "DisplayableSwitcher" animations running at the same time, off of different controlling variables, what would I be changing? Like would this work:

Code: Select all

image test_case = DisplayableSwitcher(displayable={"MyAniTag": "TestAnimatedImage"}, conditions=(
     ("VariableIJustCameUpWith == 0", ("MyAniTag", )),
     ("VariableIJustCameUpWith == 1", ("MyAniTag", "reset")),
     ("VariableIJustCameUpWith == 2", ("MyAniTag", "pause")),
     ("VariableIJustCameUpWith == 'yellow'", ("MyAniTag", "show_paused")),
     ("VariableIJustCameUpWith == 'orange'", ("MyAniTag", "resume")),
     ("True", ("default", ))))

image test_case2 = DisplayableSwitcher(displayable={"MyAniTag": "TestAnimatedImage"}, conditions=(
     ("VariableIJustCameUpWith == 0", ("MyAniTag", )),
     ("VariableIJustCameUpWith == 1", ("MyAniTag", "reset")),
     ("VariableIJustCameUpWith == 2", ("MyAniTag", "pause")),
     ("VariableIJustCameUpWith == 'yellow'", ("MyAniTag", "show_paused")),
     ("VariableIJustCameUpWith == 'orange'", ("MyAniTag", "resume")),
     ("True", ("default", ))))
How much would absolutely need to be different between the two, and how much would carry over?

Post Reply

Who is online

Users browsing this forum: No registered users