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

#46 Post by Onishion »

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.
Huh. I don't get it then. I copy/pasted your version, tried to run it, got a crash error. Says "line 1832, in per_interact. . . self.change(s, mode)" "line 1836, in change. . . self.d = self.displayable" Key Error "Rolling" (or Bouncing, if I tweak which is meant to show up). Note that it does work fine when I use that exact same Displayable Switcher but define it as an image outside of the LC and reference it in. I thought the issue might have been in the position of the code, since I put the Transform and image definitions after the LC in the code in my version, but even straight copying your version gave the same errors. Baring any breakthroughs, I'll just go back to my original method of defining the switch in its own image and functionally it'll work the same, but it annoys me that I don't know why it's bugging out for me. ;)

And sorry I don't conform to standards, I'm totally self-taught, and more through Renpy than through Python first, so I write how it makes sense to me. Object oriented programming still confuses me greatly, so while I can sometimes make use of class-based solutions, I rarely fully understand the underlying elements of it.

But anyway, I really am appreciative of the work you did here, it works beautifully at doing the thing it was originally intended to do. I will be using the displayable switcher in the way that it currently functions and it will improve the performance of my project significantly, so thank you very much for that. I didn't really grasp how different the additional functionality I was looking at would take, but I can live without it if it's way too much hassle, which it seems to be. ;) I'm an optimistic realist, so I'll make it work as best I can with what I have available. ;)

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

Re: Resetting animations within a live composite

#47 Post by xela »

Weird.. can you make a clean project of it? You just need to copypasta the class and three of your definitions + code to execute. If I can get the error myself, I can debug it, yours is a near perfect testing case, pure ATL + simple variables...

Regardless of what is causing this for you, it can be fixed with ease and I am more than willing to do it. It can be a lot of things, I am on pre-release update channel, if you're ahead of me, there could be issues, I've already said that this is not "future" safe (we're using some undocumented sh!t which is subject to a change without notice).
It is safe enough to expect that you can always adapt to any changes to the core code just by changing one or two lines of the class.

Errors is something we can fix, as I've said, two hours after we started using it in our project, it became apparent that it's functionality was flawed. I've rigged it to work
perfectly only for PyTom to point out in an unrelated post that Render.place() could have just been used for the class :D (pissed me off a little bit that I didn't see it sooner)

Now I've updated 6 other UDDs we're using to work with Render.place(), this one is kind if en exception and should just work with any ATL. If you have a failed case, I'd like to know and get rid of the error!
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

#48 Post by Onishion »

Huh. I created a new project, nothing in there but copy/pasting your example above and the Displayable Switcher definition, and it still threw me the error. If it works fine for you then I can only assume it's something that changed since the Renpy version I'm using (I'm still on 99.4). I guess maybe it's time to upgrade. ;)

DragoonHP
Miko-Class Veteran
Posts: 758
Joined: Tue Jun 22, 2010 12:54 am
Completed: Christmas
IRC Nick: DragoonHP
Location: Zion Island, Solario
Contact:

Re: Resetting animations within a live composite

#49 Post by DragoonHP »

Change:

Code: Select all

            if not isinstance(displayable, dict):
to

Code: Select all

            if not isinstance(displayable, _dict):

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

Re: Resetting animations within a live composite

#50 Post by xela »

Code: Select all

    dict = _dict
    set = _set
    list = _list
    # object = _object
    _rollback = False
bah... my bad :( I have some version of this in almost all of my projects. Should have tried it on a clean one myself :D
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

#51 Post by Onishion »

So, where should I drop that? Like at the end of the Displayable switcher definition? Would it mess up anything else I have going on to add 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

#52 Post by xela »

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"]
            render = rp.Render(width, height)
            render.place(d)
            rp.redraw(self, 0)
            return render
           
        def visit(self):
            return [v["d"] for v in self.displayable.values()]

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 start:
    while 1:
        show CompositeImage as ci
        "meow"
        hide ci
        "meow"
    return
This... nothing will be effected. I've also changed all of that BS I wrote earlier to pass correct placement properties with a simple render.place() which should do exactly the same thing.
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

#53 Post by Onishion »

Hey, it worked! Thank you.

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

Re: Resetting animations within a live composite

#54 Post by Onishion »

Ugh. I'm getting frustrated. I had this working, I had a bunch of different animations on switchers and it was working fine, and then I tried pausing and resuming and that sort of thing, which I hadn't tried in a while, and for some reason they just don't work at all anymore. If I hit pause, it pauses for like half a second, then just continues on. Checking the variable viewer, the variables I was using are what they're supposed to be (like , and I tried all sorts of troubleshooting like paring my image down to a single switcher and single possible set of options, I tried reverting to the old switcher code, I just don't know what the deal is. What could cause the switcher code to just completely ignore what the "AnimVariable" value is?

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

Re: Resetting animations within a live composite

#55 Post by xela »

Onishion wrote:Ugh. I'm getting frustrated. I had this working, I had a bunch of different animations on switchers and it was working fine, and then I tried pausing and resuming and that sort of thing, which I hadn't tried in a while, and for some reason they just don't work at all anymore.
Hmm, maybe changing:

Code: Select all

render.place(d)
to

Code: Select all

render.place(d, st=st)
under the new code will work?
Onishion wrote:What could cause the switcher code to just completely ignore what the "AnimVariable" value is?
We do make a number of assumptions such as you do not change the at of the image, so basically that you don't have:

Code: Select all

image meow = HugeMessOfDisplayble()
image meow next = AnotherHugeMessOfDisplayable()

label start:
    show meow
    # ... some/loads of code
    show meow next
In general, there shouldn't be instances where a single Switcher is working but a number of them do not. As I've said, ATL is not written to be paused or reset and etc. but this should work. It works for me under a number of circumstances when switching between states between interactions, which is harder. I think that we're using it only once or twice through variables control as you do so you're the only tester of this on the larger scale.

====================================>>>
This can always be made to work and to function correctly in the end, but I need testing cases where it fails so whatever is wrong can be tracked down and fixed.
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

#56 Post by Onishion »

Ok, I'll need to test some stuff to be sure, but I think the "render.place(d, st=st)" thing fixed it, at least it's working again so far. Thanks. I did have a slight issue with two layers of the LC falling out of sync, but I can see if maybe the transforms used are to blame for that. Hitting "reset" at least seemed to fix that.

Anyway, I never use the "image name othername" sort of way of doing things, most of my images are done by showing an image once that is a live composite, and if I want to change bits of it, that all happens internally to the live composite. Within that LC though, I do have cases where the same image is reshown using a different animation, such as in the example above where the ball is shown using either the "rolling" or "bouncing" animation.

I hesitate to ask, because this again goes a bit out of the scope of the intent of this project, but how tricky would it be to make an additional condition to the switcher that would cause a looping animation to continue running until it hit a "repeat," and then pause, waiting to be resumed? Like a bouncing ball that if you hit "pause after" or whatever, the animation would continue until it hit the ground and then stop?

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

Re: Resetting animations within a live composite

#57 Post by xela »

Onishion wrote:until it hit a "repeat," and then pause, waiting to be resumed??
Not really, Ren'Py knows when the next loop executes by the repeat but that information in not exposed to us, at least I don't believe that it is. This might not be possible to to in "generic auto" mode without modifying core code. Plus this feels like sort of a weird thing to be doing.

But I wonder if you add a function that would change your control variable the INSTEAD of a repeat? I am also not entirely sure how this is different from an ATL without repeat that you reset?
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

#58 Post by Onishion »

So you're saying instead of having a repeat at the end of the ATL, have a function that basically activates the reset at the end of the animation, causing it to repeat, but in a way that could be broken by having some sort of if-then inside the function if the goal is to break out? Huh. Might be worth a shot. I'll see what I can try with that, I'm not in the habit of putting functions inside ATL but I remember reading something about it a while back. I mean, functionally that could work so long as it could execute reliably and smoothly, it just hadn't really occurred to me.

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

Re: Resetting animations within a live composite

#59 Post by xela »

More or less, as I've said, this seems like a weird case to me :D Also don't forget that these variables only take effect when interaction occurs.
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

#60 Post by Onishion »

Ok, I'm having some slight de-synching errors when I have multiple animations running using switchers, so I'd like to clarify a few things.

1. In the case example of (taken from the larger example code above):

Code: Select all

 DisplayableSwitcher(displayable={
                    "Rolling": At("BallImage", Rolling()),   
                    "Bouncing": At("BallImage", Bouncing()),
                    },
It's important that "rolling" and "bouncing" tags are two different names, because that's how later in the switcher it decides what to do with each of those objects when the conditions change, right? But say I have two completely different switchers active at a time, switcherA and switcherB, each doing different animations that I want to behave in sync with each other. Is it important that the tags used in switcherA be different than the ones in switcherB? If they are the same, would they share properties or would they conflict with each other preventing one from working right? Or is it a case that each tag is completely internal to its own animation and cannot impact the other?

2. In the example of:

Code: Select all

                    ("BallState == 1 and AnimVariable == 0", ("Rolling", )),
                    ("BallState == 1 and AnimVariable == 1", ("Rolling", "reset")),
                    ("BallState == 1 and AnimVariable == 2", ("Rolling", "reset")),
I'm using Animvariable to decide whether the animation is active or not. Now, should I have a unique variable for each different switcher, or can I use one variable that applies to all switchers? I've been trying to use only one, but I keep running into issues where swapping between animations causes them to desync with each other, or reset to stop functioning properly and wonder if this is the cause.

3. Is it only possible to update the "pause/resume/rest" status of an animation after it has been displayed, or can it be changed while the image is still hidden?

Post Reply

Who is online

Users browsing this forum: No registered users