Scrolling Background issue

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Post Reply
Message
Author
User avatar
ChroniclerOfLegends
Regular
Posts: 53
Joined: Thu Feb 16, 2017 5:57 pm
Projects: Scifi Non-Linear Visual Novel/Arcade/Adventure game. (No name yet)
Contact:

Scrolling Background issue

#1 Post by ChroniclerOfLegends »

I am having difficulty implementing a simple horizontally scrolling repeating background in Renpy. I thought I had it going, but I noticed that there is a very thin gap where the image loops back on itself. I have been playing around with it for a week, trying to add offsets to correct for the gap, but no matter what it comes back so I know something must be fundamentally wrong with my code.

My background works by creating 2 copies of the same image, and depending on which direction it is scrolling the 2nd image starts off of the screen and scrolls in while the first scrolls out. The moment one image is entirely off the screen its position is updated to flip sides

I just cannot figure out what is causing the gap.

This is the code for the displayable I created:

Code: Select all

    python:
    	...
    	self.BG = SimpleBKG()
    	self.BG.createNew(Image('exampleimage.png', False, [-1,0], [1280, 720], 1)
    	...
    	self.BG.update(1)
    	...
    	self.BG.render( renderer, shownTimebase, animationTimebase )

Code: Select all

    python:
        import pygame
        
        # Simple Background Object
        class SimpleBKG():
            def __init__(self):
                # Initialize variables
                self.image = None
                self.active = False
                self.position = [ 0, 0 ]
                self.position2 = [ 0, 0 ]
                self.static = True
                self.speed = [0, 0]
                self.dimXY = [0, 0]
                
            def createNew(self, image, static, dirXY, dimXY, speed):
                # Setup the background image
                self.image = image
                self.static = static
                self.position = [ 0, 0 ]
                self.position2 = [ (dimXY[0] * dirXY[0]), (dimXY[1] * dirXY[1]) ]
                self.speed = [ (speed*dirXY[0]), (speed*dirXY[1]) ]
                self.dimXY = [ dimXY[0], dimXY[1] ]
                self.active = True
                
            def update(self, deltaTime):
                if ( self.active ):
                    if ( self.static == False ):
                        # Tile image
                        if ( self.position[0] < (-1 * self.dimXY[0]) ):
                            self.position[0] = self.dimXY[0]
                        if ( self.position[0] > self.dimXY[0] ):
                            self.position[0] = ( -1 * self.dimXY[0] )
                        if ( self.position[1] < (-1 * self.dimXY[1]) ):
                            self.position[1] = self.dimXY[1]
                        if ( self.position[1] > self.dimXY[1] ):
                            self.position[1] = ( -1 * self.dimXY[1] )
                        if ( self.position2[0] < (-1 * self.dimXY[0]) ):
                            self.position2[0] = self.dimXY[0]
                        if ( self.position2[0] > self.dimXY[0] ):
                            self.position2[0] = ( -1 * self.dimXY[0] )
                        if ( self.position2[1] < (-1 * self.dimXY[1]) ):
                            self.position2[1] = self.dimXY[1]
                        if ( self.position2[1] > self.dimXY[1] ):
                            self.position2[1] = ( -1 * self.dimXY[1] )                                               
                        
                        # Update position
                        self.position[0] += (self.speed[0] * deltaTime)
                        self.position[1] += (self.speed[1] * deltaTime)
                        self.position2[0] += (self.speed[0] * deltaTime)
                        self.position2[1] += (self.speed[1] * deltaTime)
                                              
            def render( self, renderer, shownTimebase, animationTimebase ):
                # Display this object
                if ( self.active ):
                    r = renpy.render( self.image, SCENE_RIGHT, SCENE_DOWN, shownTimebase, animationTimebase )
                    renderer.blit( r, (self.position[0], self.position[1]) )
                    renderer.blit( r, (self.position2[0], self.position2[1]) )

User avatar
Remix
Eileen-Class Veteran
Posts: 1628
Joined: Tue May 30, 2017 6:10 am
Completed: None... yet (as I'm still looking for an artist)
Projects: An un-named anime based trainer game
Contact:

Re: Scrolling Background issue

#2 Post by Remix »

Pseudo (almost 5am sleepy time) code and concept...
Will not a LiveComposite (nowadays just Composite) with two LiveCrops (now just Crop) suffice?

Code: Select all

default bg_offset = 150
image bg1 = Composite( 
    (1280, 720),
    (0, 0), Crop( bg_offset, 0, 1280-bg_offset, 720, "bg_image.png"),
    (bg_offset, 0), Crop( 0, 0, bg_offset, 720, "bg_image.png") )
Frameworks & Scriptlets:

User avatar
Remix
Eileen-Class Veteran
Posts: 1628
Joined: Tue May 30, 2017 6:10 am
Completed: None... yet (as I'm still looking for an artist)
Projects: An un-named anime based trainer game
Contact:

Re: Scrolling Background issue

#3 Post by Remix »

Some actual working code to play with (using LiveComposite/LiveCrop rather than newer names)

Code: Select all

init python:

    bg_offset = 0

    def constant_pan_func(trans, st, at, speed=25):
        global bg_offset
        bg_offset = abs(int(round( st * speed ))) % 1280
        return 0.02 # 50fps max

transform constant_pan(speed=25):
    function renpy.curry(constant_pan_func)(speed=speed)

screen panoramic_bg( img, speed=25 ):
    fixed:
        add LiveComposite( 
            (1280, 720),
            (0, 0), LiveCrop( (bg_offset, 0, 1280-bg_offset, 720), img ),
            (1280-bg_offset, 0), LiveCrop( (0, 0, bg_offset, 720), img ) ) at constant_pan(speed=speed)

init python:
    config.per_frame_screens.append('panoramic_bg')

label start:

    show screen panoramic_bg( "images/bg 5.png", 50 )

    "..."
... or (perhaps less resource heavy) replace the LiveCrops just with one double image:

Code: Select all

        add LiveComposite(
            (2560, 720),
            (0, 0), img,
            (1280, 0), img
            ) xpos -bg_offset at constant_pan(speed=speed)
... simplest (maybe)...

Code: Select all

transform constant_pan(speed=25):
    linear 1280/speed xpos -1280
    xpos 0
    repeat

screen panoramic_bg( img, speed=25 ):
    fixed:
        add LiveComposite(
            (2560, 720),
            (0, 0), img,
            (1280, 0), img
            ) at constant_pan(speed=speed)

label start:

    show screen panoramic_bg( "images/bg 5.png", 50 )
    "..."
Frameworks & Scriptlets:

User avatar
ChroniclerOfLegends
Regular
Posts: 53
Joined: Thu Feb 16, 2017 5:57 pm
Projects: Scifi Non-Linear Visual Novel/Arcade/Adventure game. (No name yet)
Contact:

Re: Scrolling Background issue

#4 Post by ChroniclerOfLegends »

Thank you for the help on this. I didn't use your solution exactly, but the livecomposite and simply resetting the position instead of flipping the 2 images fixed it.
Here is the fixed code if it is of use to anybody else:

Code: Select all

    python:
    	...
    	self.BG = SimpleBKG()
    	self.BG.createNew(Image('exampleimage.png', False, [-1,0], [1280, 720], 1)
    	...
    	self.BG.update(1)
    	...
    	self.BG.render( renderer, shownTimebase, animationTimebase )

Code: Select all

# Simple Background Object
        class SimpleBKG():
            def __init__(self):
                # Initialize variables
                self.image = None
                self.active = False
                self.position = [ 0, 0 ]
                self.static = True
                self.speed = [0, 0]
                self.dimXY = [0, 0]
                
            def createNew(self, image, static, dirXY, dimXY, speed):
                # Setup the background image
                if (static == False):
                    img = image
                    if ( ( dirXY[0] != 0) and (dirXY[1] !=0) ):
                        self.image = LiveComposite( (dimXY[0], dimXY[1]), (0, 0), img, 
                                                                          ((-1*dimXY[0]*dirXY[0]), (-1*dimXY[1]*dirXY[1])), img,
                                                                          ((-1*dimXY[0]*dirXY[0]), 0), img,
                                                                          (0, (-1*dimXY[1]*dirXY[1])), img)                    
                    else:
                        self.image = LiveComposite( (dimXY[0], dimXY[1]), (0, 0), img, 
                                                                          ((-1*dimXY[0]*dirXY[0]), (-1*dimXY[1]*dirXY[1])), img) 
                    
                    self.position = [0, 0]
                    self.static = False
                    self.speed = [ (speed*dirXY[0]), (speed*dirXY[1]) ]
                    self.dimXY = [ dimXY[0], dimXY[1] ]
                    self.active = True
                else:
                    self.image = image
                    self.position = [0, 0]
                    self.static = True
                    self.speed = [0, 0]
                    self.dimXY = [ dimXY[0], dimXY[1] ]
                    self.active = True
                
            def update(self, deltaTime):
                if ( self.active ):
                    if ( self.static == False ):
                        # Tile Image - once image reaches maximum range reset to center
                        if ( self.position[0] < (-1 * self.dimXY[0]) ):
                            self.position[0] = 0
                        if ( self.position[0] > (self.dimXY[0]) ):
                            self.position[0] = 0
                        if ( self.position[1] < (-1 * self.dimXY[1]) ):
                            self.position[1] = 0
                        if ( self.position[1] > (self.dimXY[1]) ):
                            self.position[1] = 0
                        
                        # Update position
                        self.position[0] += (self.speed[0] * deltaTime)
                        self.position[1] += (self.speed[1] * deltaTime)
                                              
            def render( self, renderer, shownTimebase, animationTimebase ):
                # Display this object
                if ( self.active ):
                    r = renpy.render( self.image, SCENE_RIGHT, SCENE_DOWN, shownTimebase, animationTimebase )
                    renderer.blit( r, (self.position[0], self.position[1]) )

Post Reply

Who is online

Users browsing this forum: Ocelot