Creating a second view on a SpriteNode
mr_w last edited by mr_w
I was taking the scrolling map example helpfully provided by @JonB on the forum and published on github, and looking for ways of creating two views onto the same scrolling map (side by side, for a 2 player mode, kind of thing).
I experimented with a few ways of achieving this, such as trying to add the same node to two parents (nope, that doesn't work), or even writing my own custom shader to render from two different parts of a texture (well, that sort of worked, but when the texture is bigger than the screen you get severe resolution issues.... ....a very kooky solution).
So, I ended up creating a new subclass of a SpriteNode that uses render_to_texture to create texture from another pre-existing pre-existing SpriteNode. This needs to be rendered in every update (well, it does if you want any shaders in the original SpriteNode to work). But, it does seem to work well. I share the code below in case anybody else is trying to do something similar (or in case anybody would observe that I missed a more obvious or more efficient solution). By rendering in this way, you can't use the built-in animations, so I also included a very simple 'move_to' animation.
You can see the whole thing integrated into something that grew out of jsbain's original example here on github: https://github.com/py-mrw/pythonista/blob/main/mapgame2p.py.
And thank you once again to omz for making the wonderful Pythonista, and to all you nice folk on the forums, which are a goldmine of helpful information and ideas.
# class WindowNode # # Create a new view which is a rectangular window on an existing sprite node # - spritenode : The existing sprite node # - position : A Vector2 that is the origin of the window, within the existing sprite node # - size : A Vector2 that is the size of the window # Note that the scale is originally the scale of the existing sprite node, but is deliberately # not kept in synch, since this is an independent view. # # The refresh() function should be called from scene.update(), since this view needs to be refreshed # every update if you want any shaders to work properly. # # To reposition the origin, simply set render_position, or you can use move_to() if you want a simple # animation effect as you move to the new position # class WindowNode(SpriteNode): def __init__(self, spritenode, position, size, **kwargs): self.spritenode = spritenode self.render_position = Vector2(position, position) self.render_size = Vector2(size, size) self.render_rect = Rect(position, position, size/spritenode.scale, size/spritenode.scale) texture = spritenode.render_to_texture(self.render_rect) super(WindowNode, self).__init__(texture, size, **kwargs) self.scale = self.spritenode.scale self.animating = 0 def move_to(self, newpos, t, dt): if self.animating == 1: self.render_position = self.oldpos + self.deltapos self.oldpos = self.render_position self.deltapos = Vector2(newpos,newpos) - self.render_position self.animating = 1 self.t_start = t self.dt = dt def refresh(self, t): if (self.animating): progress = (t-self.t_start)/self.dt if (progress >= 1.0): progress = 1.0 self.animating = 0 self.render_position = self.oldpos + self.deltapos * progress self.texture = self.spritenode.render_to_texture((self.render_position, self.render_position, self.render_size/self.scale, self.render_size/self.scale))