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
User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Resetting animations within a live composite

#31 Post by xela »

Onishion wrote: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.
I am not sure that I follow :(
Onishion wrote: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.
We have a great deal of control here so I imagine that most things can be fixed, I'd appreciate an example just last you provided last time of the setup that fails to resume pause. You can use Solids or Texts instead of your images as Ren'Py should treat them all the same.
Onishion wrote: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

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]
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?
This is easier to answer. As I've said before, this code (while being a little bit different), should work exactly the same as the original ConditionSwitch. I used it's design as it was vastly superior to my "list" idea (in fact it is very easy to implement my list idea using the CS's logic as well as dozens of other setups).

Ren'Py's "gameflow" works off "interactions". On every interaction, whatever the condition you came up with, will be evaluated and applied. They go from first to last (top to bottom). In your examples they would all "carry over", although that may not be the correct term. Better put: They would both be evaluated to cause the effect bound to them. You could have two displayable do different thing off the same condition as well.

Just like with the CS, you can put any conditions there, like:

"(some_var >= 1324 and player.name == 'Stan', 'apple' in player.inverntory) or screw_it_all_to_hell_its_game_over_anyway" will work as well. I think you can even throw callables in there, although I haven't tried that yet.
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

#32 Post by Onishion »

I am not sure that I follow :(
Ok, basically, the code you wrote works 100% fine when I use it on your original examples. But take your original example, and take:

Code: Select all

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

#and then put THAT inside another LC. So if we were to do:

image TestLC2 = LiveComposite(                                                                     
    (100,100),   
    (0, 0), "TestLC",
#    (0, 0), ConditionSwitch(                                                             
#            "True", "TestLC",           
#            ),
    )
Now in my testing, running that as written above would work fine, the LC inside an LC, but if you remove the "(0, 0), "TestLC"," line and replace it with the portion I commented out, basically adding another condition switch to the situation, then it stops resuming. Something about a condition switch inside a condition switch, I guess.
Ren'Py's "gameflow" works off "interactions". On every interaction, whatever the condition you came up with, will be evaluated and applied. They go from first to last (top to bottom). In your examples they would all "carry over", although that may not be the correct term. Better put: They would both be evaluated to cause the effect bound to them. You could have two displayable do different thing off the same condition as well.
Ok, interesting. I'll have to try a bunch of options out. ;)

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

Re: Resetting animations within a live composite

#33 Post by Onishion »

On some more playing with it, it seems like maybe the buttons do the wrong things. I made the following test case:

Code: Select all

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 LC1 = LiveComposite(                                                                     
    (100, 100),    
    (0, 0), ConditionSwitch(                                                             
            "True", "StandardImage",           
            ),
    )
        
image TestLC = LiveComposite(                                                                     
    (100, 100),    
    (0, 0), ConditionSwitch(                                                             
            "True", "test_case",           
            ),
    )

image TestLC2 = LiveComposite(                                                                     
    (100,100),   
    (0, 0), "LC1",    
    (0, 0), "TestLC",
    )
Then I made this routine for testing:

Code: Select all

                        show TestLC2 at topleft
                        $Count2 = 1
                        while Count2:
                            menu:
                                "Reset":
                                    $ a = 1
                                "Pause":
                                    $ a = 2
                                "Resume":                                    
                                    $ a = 4
                                "Yes":
                                    pass
                                "No":
                                    hide TestLC2
                                    $ Count2 = 0
Now, based on your test example, those buttons should work, right? Setting it to 1 resets, 2 pauses, 4 resumes? But that's not how it works in this case. Instead, hitting "pause" starts it moving, and hitting "resume" pauses it. Hitting "reset" resets it to the default position, but won't cause the animation to start until I hit "pause." So basically it all ended up backwards somehow. I checked the variable viewer and the "a" variable is set to what it says it is, it's just not having the intended effect.

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

Re: Resetting animations within a live composite

#34 Post by xela »

Yeah, I now see at least one serious issue... the change function is now ran on every interaction as long as it's condition is true. That needs to be prevented, we only want the method to be called when condition first changes. The resume without reshowing paused first may also be glitchy but the former issue needs to be fixed first (which shouldn't be hard). I'll take it out as time permits.

Ok, so the new Switcher:

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, always_reset=True, **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.always_reset = always_reset
            self.d = self.displayable[start_displayable]
            self.animation_mode = "normal"
            self.last_st = 0
            self.last_condition = None
           
        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"
                        
                        # We only want to change if we got a new condition:
                        if self.last_condition != c or (self.always_reset and "reset" in v):
                            self.last_condition = c
                            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()]

I've added new always_reset kwarg there but it's just for fun or in unlikely event that you wish to reset on every interaction. Proper way to cause resets is to add two different conditions that do the same reset effect and use them one after the other as required. Obviously if you change the condition after any reset at least one, reset condition will work again.

This is working with your setup at my end :D (so we're slowly "getting there").

Note: And just one more time: It's not useful to use ConditionSwitch paired with the new Switcher as they both do the same thing and you'll be just replicating code.
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

#35 Post by Onishion »

I've added new always_reset kwarg there but it's just for fun or in unlikely event that you wish to reset on every interaction. Proper way to cause resets is to add two different conditions that do the same reset effect and use them one after the other as required. Obviously if you change the condition after any reset at least one, reset condition will work again.
So, do you mean something like:

Code: Select all

image test_case = DisplayableSwitcher(displayable={"test": "TestAnimatedImage"}, conditions=(("a == 0", ("test", )),
     ("a == 1", ("test", "reset")),
     ("a == 2", ("test", "pause")),
     ("a == 3", ("test", "reset")),                                    #changed this to also be reset
     ("a == 4", ("test", "resume")),
     ("a > 4", ("default", ))))

menu:
    "reset":
        $ a = 1
        $ a = 3
and doing that would cause it to properly reset? I don't have time to play with it right now, but I'll give it a shot later. Thanks for the tweaks, I know this is a complicated situation. ;)

edit: I just imported the new code, and tested my old test, and it seemed to work so far, causing the animation to pause and resume as intended. I didn't even have to do the stuff I was talking about above yet. The one slight issue is that when the image launches, it launches paused and in it's "finished" state, and only starts moving when I hit Resume, rather than appearing in it's initial state, animating to the finish, and waiting for new instructions if left alone. I'll keep playing with it though.
Note: And just one more time: It's not useful to use ConditionSwitch paired with the new Switcher as they both do the same thing and you'll be just replicating code.
Well, I'll play with minimizing overlap, but I think in my implementation ConditionSwitch will still have a role, just because a lot of the things I'm working with probably won't need the added functionality, they work fine doing default stuff, and the new switcher (currently at least) seems a bit more complicated, so I only planned to use it for the portions that are animated in a way that really benefits from the pausing and other new functions. Of course as I grow more comfortable using it that might change, I tend to go back through and redo portions once I learn a new trick. ;)

I'm also not really sure how I'm supposed to use the new switcher to fill the role of a ConditionSwitch. The current example shows a way to show a single displayable and select from multiple animation states for that displayable. Most of my ConditionSwitches are used to choose between several completely different displayables.

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

Re: Resetting animations within a live composite

#36 Post by xela »

Onishion wrote:

Code: Select all

image test_case = DisplayableSwitcher(displayable={"test": "TestAnimatedImage"}, conditions=(("a == 0", ("test", )),
     ("a == 1", ("test", "reset")),
     ("a == 2", ("test", "pause")),
     ("a == 3", ("test", "reset")),                                    #changed this to also be reset
     ("a == 4", ("test", "resume")),
     ("a > 4", ("default", ))))

menu:
    "reset":
        $ a = 1
        $ a = 3
and doing that would cause it to properly reset?
No, just the $ a = 1 will cause it to reset properly! So will the $ a = 3! But, lets say you have this code:

Code: Select all

"My name is Stan!"
$ a = 1
"I am a lvl 25 Noob with a stick trying to become a Warrior!"
$ a = 1
The second a will NOT work because the Switcher knows that it was the last condition it evaluated and will not run it! Now this:

Code: Select all

"My name is Stan!"
$ a = 1
"I am a lvl 25 Noob with a stick trying to become a Warrior!"
$ a = 3
will work perfectly, and if you need to reset it again after the next line, you'll have to do $ a = 1 again! But the switch will work again if you are not looking to reset twice in the row! like:

Code: Select all

"My name is Stan!"
$ a = 1
"I am a lvl 25 Noob with a stick trying to become a Warrior!"
$ a = 2
"But I always get kicked in the nuts when trying to take the Warrior exam :("
$ a = 1
because we switched to a different condition, $ a = 1 will work again! so if you are not looking to reset two or three times in the row, you do not even need a second reset condition at all.

Same thing counts for everything else but not really relevant (since pausing two times without resuming is meaningless, just like resuming twice ot showing normally twice. So this only really matters for resets).
Onishion wrote:I know this is a complicated situation. ;)
Previous version of Switcher was meant to work with the function calls on it's instance, the way I coded the conditions there were not useful for any situation, no matter how simple or complex. But that mistake should now be fixed.

Onishion wrote:Well, I'll play with minimizing overlap, but I think in my implementation ConditionSwitch will still have a role, just because a lot of the things I'm working with probably won't need the added functionality, they work fine doing default stuff, and the new switcher (currently at least) seems a bit more complicated, so I only planned to use it for the portions that are animated in a way that really benefits from the pausing and other new functions. Of course as I grow more comfortable using it that might change, I tend to go back through and redo portions once I learn a new trick. ;)
Yeah, while writing this post, I saw how both can be useful in the same way you might put one CS into another (although that can also be done with two/more Switchers).
Onishion wrote:I'm also not really sure how I'm supposed to use the new switcher to fill the role of a ConditionSwitch. The current example shows a way to show a single displayable and select from multiple animation states for that displayable. Most of my ConditionSwitches are used to choose between several completely different displayables.
You can use any number of displayable, there is nothing limiting it to just the one:

Code: Select all

image test_case = DisplayableSwitcher(displayable={"default": Solid("F00", xysize=(10, 10)),
                                                                                   "test": "TestAnimatedImage",
                                                                                   "displayable_2": "LC_2",
                                                                                   "displayable_3": "my_image_10"},
                                                                conditions=(("a == 0", ("test", )),
                                                                                   ("a == 1", ("test", "reset")),
                                                                                   ("a == 2", ("test", "pause")),
                                                                                   ("a == 3", ("test", "resume")),
                                                                                   ("b == 1", ("displayable_2", )),
                                                                                   ("b == 2", ("displayable_2", "reset")),
                                                                                   ("b == 3", ("displayable_2", "pause")),
                                                                                   ("b == 4", ("displayable_2", "resume")),
                                                                                   ("c", ("displayable_3", )),
                                                                                   ("True", ("default", ))))
One difference between the two is that CS will crash if no condition is met while DS will just keep on doing whatever it was doing before. There is no requirement for any of the conditions to be evaluated to True but it is a small difference.
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

#37 Post by xela »

Onishion wrote:edit: I just imported the new code, and tested my old test, and it seemed to work so far, causing the animation to pause and resume as intended. I didn't even have to do the stuff I was talking about above yet. The one slight issue is that when the image launches, it launches paused and in it's "finished" state, and only starts moving when I hit Resume, rather than appearing in it's initial state, animating to the finish, and waiting for new instructions if left alone. I'll keep playing with it though.
It really shouldn't... are you sure that it's a paused state and not just the set of ATL instructions completing itself (just like in your original problem?) (you can check this by adding repeat to the animation and see if it is trully paused).

You can add a "start_displayable" kwarg if you're using method calls or make sure proper condition is at place to show the image (if conditions are used), like:

Code: Select all

default a = 0
instead of (for example):

Code: Select all

default a = 100
==>>
Basically, we still do not track if the transform is shown or not so the st of your displayable remains as it was with CS (so the st of the main displayable shown is used). But now you can reset st for the specific transforms.
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

#38 Post by Onishion »

because we switched to a different condition, $ a = 1 will work again! so if you are not looking to reset two or three times in the row, you do not even need a second reset condition at all.

Same thing counts for everything else but not really relevant (since pausing two times without resuming is meaningless, just like resuming twice ot showing normally twice. So this only really matters for resets).
Hmmm, so is there no way to make it so that you can reset twice in a row using the same variable? I ask because I don't typically write in a linear fashion, so I won't know which was the previous version used, and will likely often be resetting without using the other elements. I suppose I could just use the
$ a = 1
$ a = 3
method to just reset it twice really fast. If the 1 is sufficient then the 3 causes no harm, while if the 3 is necessary then that works too.
You can use any number of displayable, there is nothing limiting it to just the one:
Oh, I think I get it. So you set up the displayables with references in the first bit, and then set up conditions in the second. That could be handy for more than I was originally thinking.
It really shouldn't... are you sure that it's a paused state and not just the set of ATL instructions completing itself (just like in your original problem?) (you can check this by adding repeat to the animation and see if it is trully paused).
I will try that, but the point is that it never ran even once, this was the first time it appeared since I launched the game (whereas default animations would only start animating when first displayed). Sounds like the animation started playing before it was even displayed for the first time? In any case, it wouldn't be that hard to workaround by just including a reset with each display.

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

Re: Resetting animations within a live composite

#39 Post by xela »

Onishion wrote:
because we switched to a different condition, $ a = 1 will work again! so if you are not looking to reset two or three times in the row, you do not even need a second reset condition at all.

Same thing counts for everything else but not really relevant (since pausing two times without resuming is meaningless, just like resuming twice ot showing normally twice. So this only really matters for resets).
Hmmm, so is there no way to make it so that you can reset twice in a row using the same variable? I ask because I don't typically write in a linear fashion, so I won't know which was the previous version used, and will likely often be resetting without using the other elements. I suppose I could just use the
$ a = 1
$ a = 3
method to just reset it twice really fast. If the 1 is sufficient then the 3 causes no harm, while if the 3 is necessary then that works too.
Does just defining a variable cause an interaction? If not, this will not work.

But why not use something like:

Code: Select all

$ a = 1 if a == 3 else 3
if you don't know which was used last???

That will always set a to 1 if a is bound to 3 and to 3 in all other cases. You'll always get a reset, no matter what a was bound to last.
Onishion wrote:I will try that, but the point is that it never ran even once, this was the first time it appeared since I launched the game (whereas default animations would only start animating when first displayed). Sounds like the animation started playing before it was even displayed for the first time? In any case, it wouldn't be that hard to workaround by just including a reset with each display.
I need an example, just use Solids and Texts instead of images. It should be possible to get everything to work perfectly.
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

#40 Post by xela »

We've started using this in our project but a rather nasty "bug" was found. The last variant only uses pos (xpos, ypos) to position the displayable with achors and offsets completely ignored. That caused serious issues when used with complex animations that use anchors/offsets/alignments. This is a better variant:

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, always_reset=True, **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.always_reset = always_reset
            self.d = self.displayable[start_displayable]
            self.animation_mode = "normal"
            self.last_st = 0
            self.last_condition = None
           
        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"
                        
                        # We only want to change if we got a new condition:
                        if self.last_condition != c or (self.always_reset and "reset" in v):
                            self.last_condition = c
                            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(width, height)
            placement = d.get_placement()
            subpixel = placement[6]
            pos = rp.display.core.place(width, height, size[0], size[1], placement)
            if subpixel:
                render.subpixel_blit(cr, pos, 1, 1, None)
            else:
                render.blit(cr, pos, 1, 1, None)
            
            rp.redraw(self, 0)
            return render
           
        def visit(self):
            return [v["d"] for v in self.displayable.values()]
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

#41 Post by Onishion »

Ok, cool.

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

Re: Resetting animations within a live composite

#42 Post by Onishion »

Ok, I've been playing with the DisplayableSwitcher some more, and I've got a few questions:

1. You suggested using this DisplayableSwitcher in place of a condition switch. I've been able to apply it like this "image RFace_Test = DisplayableSwitcher( . . .", but when I tried to apply it as I would a Condition Switch in an LC, like "(0,0), DisplayableSwitcher(. . ." it didn't work. Was my syntax just wrong when trying to apply it, or is that impossible? I mean, worst case I just reference the image in the LC, but having all the contions in one place would be more legible.

2. I'm wondering if there is any way to cause the Displayableswitcher to store variables to be used by other animations. Below is a simplified version of a case I'd like to try. I have a Live Composite, in which a Dynamic Switcher image is sandwiched between two other images. The Dynamic Switcher itself displays one of two things, either a ball that bounces up and down, or a ball that rolls left to right, depending on the state of a variable.

In the form that I know currently works, using the transforms Rolling()/Bouncing() shown in the examples, the result would be that the ball would always return to its starting position when swapping between the two motions. What I would like to see would be the ball continuing from where it was left instead.

so what I mean is that if I had the ball moving left to right, and switched to the bouncing motion when the ball reached the 75% point of its transit, then it would start bouncing using that location's X coordinate, rather than returning to yoffset 0. If I then switched back to rolling when the ball was 30% off the ground, then it would start moving left to right from that height, rather than appearing on the ground again.

I'm thinking that there would be some way to make the RollingAlt()/BouncingAlt() transforms viable instead, where instead of just storing the positional data to be used when you unpause the animations, the DisplayableSwitcher could store the transform data in a way that other transforms could access it. I don't think it's currently possible, but would it be a huge lift to add to the functionality?

Code: Select all


image CompositeImage:                       
    LiveComposite(    
        (100,100), 
        (0,0), "ImageA",
        (0,0), "BallSwitcher",
        (0,0), "ImageB",
        )
    
image BallSwitcher = DisplayableSwitcher(displayable={ 
                    "Rolling": At("BallImage", Rolling()),    
                    "Bouncing": At("BallImage", Bouncing()),
                    }, 
                conditions=(
                    
                    ("BallState == 1 and AnimVariable == 0", ("Rolling", )),
                    ("BallState == 1 and AnimVariable == 1", ("Rolling", "reset")),
                    ("BallState == 1 and AnimVariable == 2", ("Rolling", "reset")),
                    ("BallState == 1 and AnimVariable == 3", ("Rolling", "pause")),
                    ("BallState == 1 and AnimVariable == 4", ("Rolling", "show_paused")),
                    ("BallState == 1 and AnimVariable == 5", ("Rolling", "resume")),
                    ("BallState == 1 and AnimVariable > 5", ("default", )),
                                        
                    ("BallState == 2 and AnimVariable == 0", ("Bouncing", )),
                    ("BallState == 2 and AnimVariable == 1", ("Bouncing", "reset")),
                    ("BallState == 2 and AnimVariable == 2", ("Bouncing", "reset")),
                    ("BallState == 2 and AnimVariable == 3", ("Bouncing", "pause")),
                    ("BallState == 2 and AnimVariable == 4", ("Bouncing", "show_paused")),
                    ("BallState == 2 and AnimVariable == 5", ("Bouncing", "resume")),
                    ("BallState == 2 and AnimVariable > 5", ("default", )),
                    
                    ("True", ("default", ))))

transform Rolling():            
    subpixel True 
    ease 2 offset (0,0) 
    ease 2 offset (100,0)
    repeat
    
transform Bouncing():            
    subpixel True 
    easein 1 offset (0,0) 
    easeout 1 offset (0,100)
    repeat    

#What I would like is something like this, where the Y position of the rolling animation is pulled from the result of the
#Bouncing animation, and the X of the bouncing animation is pulled from the result of the Rolling animation. 
transform RollingAlt():            
    subpixel True 
    ease 2 offset (0,GlobalY) 
    ease 2 offset (100,GlobalY)
    repeat
    
transform BouncingAlt():            
    subpixel True 
    easein 1 offset (GlobalX,0) 
    easeout 1 offset (GlobalX,100)
    repeat   

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

Re: Resetting animations within a live composite

#43 Post by xela »

Onishion wrote:1. You suggested using this DisplayableSwitcher in place of a condition switch. I've been able to apply it like this "image RFace_Test = DisplayableSwitcher( . . .", but when I tried to apply it as I would a Condition Switch in an LC, like "(0,0), DisplayableSwitcher(. . ." it didn't work. Was my syntax just wrong when trying to apply it, or is that impossible? I mean, worst case I just reference the image in the LC, but having all the contions in one place would be more legible.
My guess would be that it was syntax, there is nothing "special" about it, the issue we had previously where it seemed impossible was due to having instantiating it first which is no longer required. Basically there should be no cases where can use CS but cannot use DS.
Onishion wrote:2. I'm wondering if there is any way to cause the Displayableswitcher to store variables to be used by other animations. Below is a simplified version of a case I'd like to try. I have a Live Composite, in which a Dynamic Switcher image is sandwiched between two other images. The Dynamic Switcher itself displays one of two things, either a ball that bounces up and down, or a ball that rolls left to right, depending on the state of a variable.
LMAO @ "simplified", I had to read the text twice :D
Onishion wrote:

Code: Select all

transform RollingAlt():            
    subpixel True 
    ease 2 offset (0,GlobalY) 
    ease 2 offset (100,GlobalY)
    repeat
    
transform BouncingAlt():            
    subpixel True 
    easein 1 offset (GlobalX,0) 
    easeout 1 offset (GlobalX,100)
    repeat   
Not like this and also not through DS class storing that data. Lets say that it's possible, what is the point of this? It will work once or twice/trice if you're lucky. If you're unlucky, your ball will keep moving further and further away from it's original position (offset/offset/offset/offset/offset... without position being restored).

Something like this might make sense for rotation/circling (if you want to reverse direction or change speed for example). Maybe for cropping/zooming as well but for offsets you need to have a smart system in place to keep it bound to an area and that should be separate from DS...

===>>
I feel like we're approaching limits what this class is supposed to be able to do, we are no longer talking about switching between displayable, we're talking passing their state around which to the best of my knowledge impossible to do between atl object that are not equal to one another, aka:

Code: Select all

self.d["d"].take_execution_state(self.d["atl"])
this line is meaningless when used on the two transforms above. Before ATL gets a lot more generic and this becomes possible (although even that would solve 1/2 of the issue at best cause even if you know the state, st/at at that state is a whole different issue), we have access to parameter data, st/at + the original code of ATL. A lot can be done just with that information. I utterly failed in making one generic class that could control any atl in any way :( but on the other hand, controlling specific atl functions under known conditions using UDD is an awesome and super powerful tool, allowing creation of really cool minigames and animations in general.
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

#44 Post by Onishion »

My guess would be that it was syntax, there is nothing "special" about it, the issue we had previously where it seemed impossible was due to having instantiating it first which is no longer required. Basically there should be no cases where can use CS but cannot use DS.
Ok, well then I think the following code should work, but it does not:

Code: Select all

image CompositeImage:                       
    LiveComposite(    
        (100,100), 
        (0,0), DisplayableSwitcher(displayable={ 
                    "Rolling": At("BallImage", Rolling()),    
                    "Bouncing": At("BallImage", Bouncing()),
                    }, 
                conditions=(
                    
                    ("BallState == 1 and AnimVariable == 0", ("Rolling", )),
                    ("BallState == 1 and AnimVariable == 1", ("Rolling", "reset")),
                    ("BallState == 1 and AnimVariable == 2", ("Rolling", "reset")),
                    ("BallState == 1 and AnimVariable == 3", ("Rolling", "pause")),
                    ("BallState == 1 and AnimVariable == 4", ("Rolling", "show_paused")),
                    ("BallState == 1 and AnimVariable == 5", ("Rolling", "resume")),
                    ("BallState == 1 and AnimVariable > 5", ("default", )),
                                        
                    ("BallState == 2 and AnimVariable == 0", ("Bouncing", )),
                    ("BallState == 2 and AnimVariable == 1", ("Bouncing", "reset")),
                    ("BallState == 2 and AnimVariable == 2", ("Bouncing", "reset")),
                    ("BallState == 2 and AnimVariable == 3", ("Bouncing", "pause")),
                    ("BallState == 2 and AnimVariable == 4", ("Bouncing", "show_paused")),
                    ("BallState == 2 and AnimVariable == 5", ("Bouncing", "resume")),
                    ("BallState == 2 and AnimVariable > 5", ("default", )),
                    
                    ("True", ("default", )))),
        )
If I try that, it throws back an error in "self.d = self.displayable" That block is just copy/pasted from the image version which does work, but I'm not sure where I'm messing it up.
LMAO @ "simplified", I had to read the text twice :D


That gives you an idea of what the "complex" version looks like. ;) I wanted to make sure I covered all the salient points though.

Not like this and also not through DS class storing that data. Lets say that it's possible, what is the point of this? It will work once or twice/trice if you're lucky. If you're unlucky, your ball will keep moving further and further away from it's original position (offset/offset/offset/offset/offset... without position being restored).


Fair question, and again, this was just a simplified example of what I was actually attempting. In the actual version, I would probably use something involving a starting transform and then a block statement after, so that basically the object would start right where you left it (by importing the previous animation's positional data), then move to where it's meant to be, then go back to repeating its original cycle. It wouldn't ever move to a location outside it's boundaries because it would be using offset, and both the min and max ranges would be pre-established. The point of doing this is so that instead of hard cutting between one set of repeating animations and another, causing the object to instantly leap from where it was in the last animation to where it starts in the new one, it would instead start the second animation where it left off, and move smoothly to where it's supposed to be to continue the new one.

If this is too complex, fair enough, I was just throwing the idea out there to see if it made any sense. I mean, I gather that the displayable switcher stores this positional data for the purposes of pausing the image and restoring it later, right? So I was hoping there would be a way for it to post these values in a way that some other transform could borrow them. I mean, you can put a variable into an ATL, like the "alt" transforms I mentioned above would work if the GlobalX/GlobalY variables were actually useful variables, the bit I don't know how to do would be to get the Displayable switcher to export the current positional data to a place where the next ATL could find it.

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

Re: Resetting animations within a live composite

#45 Post by xela »

Onishion wrote:Ok, well then I think the following code should work, but it does not:

If I try that, it throws back an error in "self.d = self.displayable" That block is just copy/pasted from the image version which does work, but I'm not sure where I'm messing it up.

Code: Select all

image BallImage:
    Solid("F00", xysize=(100, 100))
    
transform Rolling():           
    subpixel True
    ease 2 offset (0,0)
    ease 2 offset (100,0)
    repeat
   
transform Bouncing():           
    subpixel True
    easein 1 offset (0,0)
    easeout 1 offset (0,100)
    repeat   
    
default BallState = 1
default AnimVariable = 2

image CompositeImage:                       
    LiveComposite(   
        (100,100),
        (0,0), DisplayableSwitcher(displayable={
                    "Rolling": At("BallImage", Rolling()),   
                    "Bouncing": At("BallImage", Bouncing()),
                    },
                conditions=(
                   
                    ("BallState == 1 and AnimVariable == 0", ("Rolling", )),
                    ("BallState == 1 and AnimVariable == 1", ("Rolling", "reset")),
                    ("BallState == 1 and AnimVariable == 2", ("Rolling", "reset")),
                    ("BallState == 1 and AnimVariable == 3", ("Rolling", "pause")),
                    ("BallState == 1 and AnimVariable == 4", ("Rolling", "show_paused")),
                    ("BallState == 1 and AnimVariable == 5", ("Rolling", "resume")),
                    ("BallState == 1 and AnimVariable > 5", ("default", )),
                                       
                    ("BallState == 2 and AnimVariable == 0", ("Bouncing", )),
                    ("BallState == 2 and AnimVariable == 1", ("Bouncing", "reset")),
                    ("BallState == 2 and AnimVariable == 2", ("Bouncing", "reset")),
                    ("BallState == 2 and AnimVariable == 3", ("Bouncing", "pause")),
                    ("BallState == 2 and AnimVariable == 4", ("Bouncing", "show_paused")),
                    ("BallState == 2 and AnimVariable == 5", ("Bouncing", "resume")),
                    ("BallState == 2 and AnimVariable > 5", ("default", )),
                   
                    ("True", ("default", )))),
        )
    
label test_me:
    while 1:
        show CompositeImage as ci
        "meow"
        hide ci
        "meow"
    return
Beautifully defined (I still hate that you're not following basis Python guidelines, but indents are awesome!), and it works... for me at least, just copypasta, no changes to definition itself.
Onishion wrote:Fair question, and again, this was just a simplified example of what I was actually attempting. In the actual version, I would probably use something involving a starting transform and then a block statement after, so that basically the object would start right where you left it (by importing the previous animation's positional data), then move to where it's meant to be, then go back to repeating its original cycle. It wouldn't ever move to a location outside it's boundaries because it would be using offset, and both the min and max ranges would be pre-established. The point of doing this is so that instead of hard cutting between one set of repeating animations and another, causing the object to instantly leap from where it was in the last animation to where it starts in the new one, it would instead start the second animation where it left off, and move smoothly to where it's supposed to be to continue the new one.
Well, that is what I meant... but it's conditional animating, not switching.
Onishion wrote:If this is too complex, fair enough, I was just throwing the idea out there to see if it made any sense. I mean, I gather that the displayable switcher stores this positional data for the purposes of pausing the image and restoring it later, right? So I was hoping there would be a way for it to post these values in a way that some other transform could borrow them. I mean, you can put a variable into an ATL, like the "alt" transforms I mentioned above would work if the GlobalX/GlobalY variables were actually useful variables, the bit I don't know how to do would be to get the Displayable switcher to export the current positional data to a place where the next ATL could find it.
Your idea does make sense but not for this class. Also you're still thinking about ATL all wrong, it's like you're not reading my posts and we're back to the clock thing from which we've started :D Your understanding that was/is incorrect. st/at in most (if not all) cases are the only things that matters, especially with ATL funcs like you have there, that run towards infinity (on endless repeats). There is no point is storing a state, because the only thing you give a damn about is st. If you know that, you can get a state at that st.

Global is not useful there. Is it too complex? That's relative, I do that all the time but on case per case basis. I actually tried making a universal state manager but when dealing with this:
{'raw_child': <Solid at 2dfc710>, 'at_offset': 0.0, 'done': False, 'at': 0.8029999732971191, 'render_size': (100.00499987500623, 100.00499987500623), 'last_child_transform_event': 'show', 'children': [<Solid at 2dfc710>], 'hide_response': True, 'style': <anonymous style is style transform @ 0x2dfb7c8>, 'style_arg': 'transform', 'parameters': <renpy.ast.ParameterInfo object at 0x02DFC950>, 'st_offset': 0.0, 'parent_transform': None, 'state': <renpy.display.motion.TransformState object at 0x02DFC990>, 'arguments': None, 'kwargs': {}, 'forward': Matrix2D(xdx=0.304532, xdy=0.952502, ydx=-0.952502, ydy=0.304532), 'child_st_base': 0, 'replaced_response': True, 'function': <bound method ATLTransform.execute of <ATL Transform 2dfcad0 (u'game/script.rpy', 13)>>, 'atl_state': (4, 0.0, 0, 0, [], ({'rotate': (0.0, 360)}, None, [])), 'offsets': [(97.47534132972706, 34.29965528601563)], 'replaced_request': False, 'child_size': (1.0, 100.0), 'child': <Solid at 2dfc710>, 'active': True, 'properties': None, 'atl_st_offset': 0, 'hide_request': False, 'reverse': Matrix2D(xdx=0.304532, xdy=-0.952502, ydx=0.952502, ydy=0.304532), 'default': False, 'atl': <renpy.atl.RawBlock object at 0x03F127B0>, 'transform_event': 'show', 'st': 0.8029999732971191, 'last_transform_event': 'show', 'context': <renpy.atl.Context object at 0x02DFCAB0>, 'focus_name': None, 'predict_block': None, 'block': <renpy.atl.Block object at 0x042FD170>}
knowing that some of these properties have their own properties... my response was: F*ck, that is too complex (not at first, I actually tried translating all of that info (and more) an object that could be passes around)! :D So I decided to deal with this on case per case basis to serve my own needs and leave generalization to PyTom who knows it's best or someone who's better motivated :D

This is not a task for this class or this thread. The original intent of this class was allowing updating it's state between interactions. CS cannot do that, period... this class can and that was the point. You asked for additional functionality of pausing/resetting those displayable which made a heck load of sense and I am already using it in my project as well. This also has a hell load of potential to be used with screens and do really cool stuff which not normally be possible. But keeping animated displayable within specified bounds, regardless of complexity (which isn't all that difficult), is not what this class was created for!
Like what we're doing? Support us at:
Image

Post Reply

Who is online

Users browsing this forum: No registered users