is faster Sprites or CustomDisplayable? (very technical!)

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
jack_norton
Lemma-Class Veteran
Posts: 4084
Joined: Mon Jul 21, 2008 5:41 pm
Completed: Too many! See my homepage
Projects: A lot! See www.winterwolves.com
Tumblr: winterwolvesgames
Contact:

is faster Sprites or CustomDisplayable? (very technical!)

#1 Post by jack_norton »

I'm trying to speed up the map I have in SOTW:
Image
in practice a 10x10 grid, plus some arrows in the hotspots. The main problem is the slowness when changing maps.
I've improved by preloading the maps on startup (the values) but especially on mobile, is SUPER SLOW, like takes 3-4 seconds to change map!!! :shock:
I don't do anything special in the change map, is just a call, update the map info (array) and then back to the loop where I use call screen to show the map.

I tried using a customdisplayable with blit/render instead of a for/loop to blit the various map cells on screen, and I *think* that speed improved, at least on Desktop. On mobile, even if improves, from 4seconds to 3,5seconds is still an eternity so harder to notice :)

I am now wondering if I could try using the Sprites class instead? would that one be faster than the customdisplayable ? thanks
follow me on Image Image Image
computer games

AxemRed
Veteran
Posts: 482
Joined: Sun Jan 09, 2011 7:10 am
Contact:

Re: is faster Sprites or CustomDisplayable? (very technical!

#2 Post by AxemRed »

It's hard to say what's the bottleneck in your rendering without more information.

Can you post the actual code that draws the tile map?
How large are the tile images?

User avatar
jack_norton
Lemma-Class Veteran
Posts: 4084
Joined: Mon Jul 21, 2008 5:41 pm
Completed: Too many! See my homepage
Projects: A lot! See www.winterwolves.com
Tumblr: winterwolvesgames
Contact:

Re: is faster Sprites or CustomDisplayable? (very technical!

#3 Post by jack_norton »

Well I tried with both, and CustomDisplayable seemed slightly faster, but still the game is unplayable even on a fairly new android tablet :(
The map is 10x10 so 100 tiles, the code is big but the core of the map:

Code: Select all

init python:
    class MapDisplayable(renpy.Displayable):

        def __init__(self, **properties):
            renpy.Displayable.__init__(self, **properties)

            # The child.
#            self.child = renpy.displayable(child)

            # The distance at which the child will become fully opaque, and
            # where it will become fully transparent. The former must be less
            # than the latter.
#            self.opaque_distance = opaque_distance
#            self.transparent_distance = transparent_distance

            # The alpha channel of the child.
            self.alpha = 1.0

            # The width and height of us, and our child.
            self.width = 1280
            self.height = 720

        def render(self, width, height, st, at):

            r = renpy.Render(width, height)

            ofx=0;ofy=0;sfx=578;sfy=4
            for x in range(MyMapsize):
                ofx=sfx;ofy=sfy
                for y in range(MyMapsize):
                    img=("tile%s" % (MyMap[x][y]))
                    if Is_Night and img not in tilenotint:
                        try:
                            img=im.MatrixColor(ImageReference(img),im.matrix.tint(Ir,Ig,Ib)*im.matrix.saturation(Is)*im.matrix.brightness(Il))
                        except:
                            pass
                    if Is_Dusk:
                        try:
                            img=im.MatrixColor(ImageReference(img),im.matrix.tint(.9,.9,1.0)*im.matrix.saturation(.9)*im.matrix.brightness(-.1))
                        except:
                            pass        
                    r.blit(renpy.render(ImageReference(img), width, height, st, at), (ofx,ofy))
                    ofy+=32;ofx+=64
                sfx-=64;sfy+=32
            
            return r

#screen ShowMap:
#    add "main/mainbase.jpg"
#    add MapDisplayable()
        
screen ShowMapHotSpots:
    default mapinfo=False
    default cval1=99
    default cval2=99
    default MTiles=False
    default MArrows=True
#    key "-" action MinusVar
#    key "+" action PlusVar
#    key "v" action ChangeVar
    key "K_F2" action ToggleScreenVariable("mapinfo")
    key "K_F3" action ToggleVariable("Is_Night")
#    key "m" action ToggleScreenVariable("MTiles")
#    key "n" action ToggleScreenVariable("MArrows")
#    add "main/mainbase.jpg"
#    #text ("R:%s G:%s B:%s Sat:%s Lum:%s CurVar:%s" % (Ir,Ig,Ib,Is,Il,CurVar)) outlines [(2,"#000")]
#    add MapDisplayable()
    if MTiles or config.developer:
        #draw the map
        $ofx=0;ofy=0;sfx=578;sfy=4
        for x in range(MyMapsize):
            $ofx=sfx;ofy=sfy
            for y in range(MyMapsize):
#                $img=("tile%s" % (MyMap[x][y]))
#                if Is_Night and img not in tilenotint:
#                    python:
#                        try:
#                            img=im.MatrixColor(ImageReference(img),im.matrix.tint(Ir,Ig,Ib)*im.matrix.saturation(Is)*im.matrix.brightness(Il))
#                        except:
#                            pass
                
#                add (img) xpos (ofx) ypos (ofy)
                if mapinfo:
                    text ("%s,%s" % (x,y)) xpos (ofx+48) ypos (ofy+60) size 12 outlines [(1,"#000")]
                    text ("%s" % MyMap[x][y]) xpos (ofx+48) ypos (ofy+80) size 12 outlines [(1,"#000")]
                #imagebutton idle (im.MatrixColor("cursor.png",im.matrix.opacity(0.5))) hover ("cursor.png") focus_mask True action Return() xpos (ofx) ypos (ofy)
                $ofy+=32;ofx+=64
            $sfx-=64;sfy+=32
    if "touch" in config.variants:
        button:
            xfill True yfill True background None
            action (SetScreenVariable("cval1",99),Hide("InfoBox"))
            activate_sound None
    if not temp_map:
        #map stats
        hbox xalign .8 ypos 10 spacing 10:
            button background None xpadding 0 ypadding 0:
                vbox:
                    add (Frame("map/quest_big.png",1,1,xmaximum=64,ymaximum=64))
                    text ("%s/%s" % (map_quests[cur_season],max_quests[cur_season])) size 20 outlines [(2,"#1e1a16")] font "Stoke-Regular.ttf" xcenter .5
                action Show("MsgBox",msg="This is the total number of quests available in this Season. Besides the main plot quest, there are several sidequests of varying difficulty/length available. Find and solve them all to get more EXP points!",msgtitle="Total Quests")
            button background None xpadding 0 ypadding 0:
                vbox:
                    add (Frame("map/location_big.png",1,1,xmaximum=64,ymaximum=64))
                    text ("%s/%s" % (map_locations[cur_season],max_locations[cur_season])) size 20 outlines [(2,"#1e1a16")] font "Stoke-Regular.ttf" xcenter .5
                action Show("MsgBox",msg="Each Season has a number of locations to unearth. Some are fairly obvious and visible, while others are harder to find, but could yield interesting rewards. Discover all the locations in a Season to earn bonus EXP!",msgtitle="Undiscovered Locations")
            button background None xpadding 0 ypadding 0:
                vbox:
                    add (Frame("map/fame_big.png",1,1,xmaximum=64,ymaximum=64))
                    #text ("%s/%s" % (map_fame[cur_season],max_fame[cur_season])) size 20 outlines [(2,"#1e1a16")] font "Stoke-Regular.ttf" xcenter .5
                    text ("%s") % (map_fame[cur_season]) size 20 outlines [(2,"#1e1a16")] font "Stoke-Regular.ttf" xcenter .5
                action Show("MsgBox",msg="Fame indicates how much people know about you and your deeds. This value increases as you complete quests, and defeat dangerous opponents. Fame relates to this Season only, and will not carry over to the next Season. High fame will lead to discounts in shops, and other sorts of favors from people.",msgtitle="Party Fame")
    
    if MArrows:
        #draw hotspots
        $ofx=0;ofy=0;sfx=580;sfy=4
        for x in range(MyMapsize):
            $ofx=sfx;ofy=sfy
            for y in range(MyMapsize):
                for mapN,mt,mx,my,type,desc,par,arrowC,fTile in maps:
                    if mapN==map_zone and x==mx and y==my and eval(mt):
                        #autofix edge movement arrows!
                        if arrowC=="green":
                            if mx==9:
                                $arrowC="W"
                            if mx==0:
                                $arrowC="E"
                            if my==9:
                                $arrowC="S"
                            if my==0:
                                $arrowC="N"
                        #if mobile
                        if "touch" in config.variants:
                            button background None bottom_padding 30 xpos (ofx+64) ypos (ofy+48) at Boing():
                                add ("map/%s.png" % arrowC)
                                action If(cval1==mx and cval2==my, (Hide("InfoBox"),Return([type,desc,par])), (Show("InfoBox",x=ofx+64,y=ofy+48,type=type,desc=desc,par=par),SetScreenVariable("cval1",mx),SetScreenVariable("cval2",my)) )
                        else:
                            button background None bottom_padding 30 xpos (ofx+64) ypos (ofy+48) at Boing():
                                add ("map/%s.png" % arrowC)
                                hovered Show("InfoBox",x=ofx+64,y=ofy+48,type=type,desc=desc,par=par)
                                unhovered Hide("InfoBox")#,transition=quickdissolve)
                                action (Hide("InfoBox"),Return([type,desc,par]))
                    #display party position ?
                    if showpartyposition and x==pax and y==pay:
                        add ("map/party_%s.png" % partyposname) xpos (ofx+64) ypos (ofy+48) at Rotor()
                $ofy+=32;ofx+=64
            $sfx-=64;sfy+=32

    #Info
    vbox xpos 10 ypos 0 spacing -2:
        text map_name[map_zone] size (75-len(map_name[map_zone])) outlines [(2,"#1e1a16")] font "BerkshireSwash-Regular.ttf"
        text ("Day %d, %s" % (CurDay,Seasons[CurSeason])) color "#DDD" size 32 outlines [(2,"#1e1a16")] font "BerkshireSwash-Regular.ttf"
        if not temp_map:
            text ("Days to next plot event: {color=#FF0}%02d{/color}" % Dday) size 22 outlines [(2,"#1e1a16")] font "Stoke-Regular.ttf"

    if not temp_map:
            
        #display party
        hbox xpos 4 yalign 1.0 spacing 4:
            $c=0
            for entry in party.dragCharacters:
                $img="gfx/gui/unlearnedSkill.png"
                if entry.character in party.available:
                    if entry.character.isAlive():
                        $img="gfx/gui/learnedSkill.png"
                    else:
                        $img="gfx/gui/unlearnedSkill.png"
                    $a=1#.25
                    if entry.character in party.party:
                        $a=1
                    vbox:
                        button:
                            background Frame(img,8,8,tile=True)
                            xmaximum 96 ymaximum 90 xminimum 96 yminimum 90 xmargin 0 ymargin 0 xpadding 4 top_padding 4 bottom_padding 5
                            bar value (entry.character.hp) range (entry.character.getValue("HP")) style "hpbar" xalign 0.025 yalign 1.0 thumb None bar_vertical True
                            bar value (entry.character.sp) range (entry.character.getValue("SP")) style "spbar" xalign 1.0 yalign 1.0 thumb None bar_vertical True
                            $img2="gfx/portraits/p_%s.png" % entry.character.name
                            if entry.character.isAlive():
                                add (img2) xcenter .5 alpha (a) zoom .75 yalign .9
                            else:
                                add (im.Grayscale(img2)) xcenter .5 alpha (a) zoom .75 yalign .9
                            action (SetField(party,"gui_selected_char",entry.character),Show("Char_Screen_Main"))
                        frame ymaximum 24 xminimum 96 xcenter .5 ypadding 10 xpadding 15:
                            background Frame(img,8,8,tile=True)
                            text entry.character.name size 13 xcenter .5
                else:
                    vbox:
                        button:
                            background Frame(img,8,8,tile=True)
                            xmaximum 96 ymaximum 90 xminimum 96 yminimum 90 xmargin 0 ymargin 0 xpadding 4 top_padding 4 bottom_padding 5
                            text "?" size 40 xcenter .5 ycenter .5
    #                        bar value (entry.character.hp) range (entry.character.getValue("HP")) style "hpbar" xalign 0.025 yalign 1.0 thumb None bar_vertical True
    #                        bar value (entry.character.sp) range (entry.character.getValue("SP")) style "spbar" xalign 1.0 yalign 1.0 thumb None bar_vertical True
                        frame ymaximum 24 xminimum 96 xcenter .5 ypadding 10 xpadding 15:
                            background Frame(img,8,8,tile=True)
                            text " " size 13 xcenter .5
                $c+=1
                if c==4:
                    null width 472
                
    use OnScreen_Menu
follow me on Image Image Image
computer games

Asceai
Eileen-Class Veteran
Posts: 1258
Joined: Fri Sep 21, 2007 7:13 am
Projects: a battle engine
Contact:

Re: is faster Sprites or CustomDisplayable? (very technical!

#4 Post by Asceai »

I would suggest not re-rendering identical tiles. Keep a dictionary of the tiles you've already rendered this redraw, keep the render and just blit it multiple times. Other than that, I'm not really sure.

User avatar
jack_norton
Lemma-Class Veteran
Posts: 4084
Joined: Mon Jul 21, 2008 5:41 pm
Completed: Too many! See my homepage
Projects: A lot! See www.winterwolves.com
Tumblr: winterwolvesgames
Contact:

Re: is faster Sprites or CustomDisplayable? (very technical!

#5 Post by jack_norton »

Well the problem is that in many maps there are very different ones. They may not be 100 unique tiles, but likely 60-70.
I am also using a single texture each tileset and created images from it on init:

Code: Select all

    #define the tilesets
    c=1
    for name in ("snowriver","snowterrain","dungeon01","dungeon02","desert","towndesert","greenforest"):
        for y in range(8):#this has to change based on the tile numbers
            for x in range(8):
                renpy.image("tile%d" % (c), im.Crop("map/%s.png" % name, (x*128, y*128, 128, 128)) )
though I don't think that should be an issue.
I know that renpy wasn't made for such games, but would be nice if the speed in the screens could be improved. Even during the battles, it's barely playable on Android, and those are the same as Loren (apart for the higher resolution).
follow me on Image Image Image
computer games

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

Re: is faster Sprites or CustomDisplayable? (very technical!

#6 Post by xela »

Mayheps:

- Have variations of tile images ready and preloaded into image cache
- Reduce the amount of for loops (maybe without screen language or only with screen language)
- use xrange (very marginally faster)
- Reference globals to local namespaces (also marginally faster but prolly makes sense since you're using globals a lot inside of for loops)

Don't know if any of these will be enough to improve the situation significantly, maybe someone else will have better ideas.

Edit: Missed two post while reading that code/typing this :)
Like what we're doing? Support us at:
Image

User avatar
jack_norton
Lemma-Class Veteran
Posts: 4084
Joined: Mon Jul 21, 2008 5:41 pm
Completed: Too many! See my homepage
Projects: A lot! See www.winterwolves.com
Tumblr: winterwolvesgames
Contact:

Re: is faster Sprites or CustomDisplayable? (very technical!

#7 Post by jack_norton »

The fact is that on desktop works fine. But on Android is really slow. I am thinking maybe is an Android-specific problem, I mean maybe there some code executes much slower.
I could play Roommates fine on my old tablet, 1Ghz single core, and that game is 1280x720 too. The old Loren was a bit slow during combats, but still playable. The really worst thing is the map, in particular changing map, takes 2-3s that might not seem much but is quite frustrating during play :P
Only thing I haven't tried is preloading images in the cache, I could try that indeed.
follow me on Image Image Image
computer games

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

Re: is faster Sprites or CustomDisplayable? (very technical!

#8 Post by xela »

jack_norton wrote:The fact is that on desktop works fine. But on Android is really slow. I am thinking maybe is an Android-specific problem, I mean maybe there some code executes much slower.
I could play Roommates fine on my old tablet, 1Ghz single core, and that game is 1280x720 too. The old Loren was a bit slow during combats, but still playable. The really worst thing is the map, in particular changing map, takes 2-3s that might not seem much but is quite frustrating during play :P
Only thing I haven't tried is preloading images in the cache, I could try that indeed.
Never messed around with Android... so there is no advice I can give here. I wrote simple code to load maps into Jake's BE from a software called Tiled to try it out once, worked really well, even on a super old laptop.

Code: Select all

    class TileMap(_object):
        """
        Prototype to import and build of maps from Tiled.
        For now using a single tileset (can be updated to using infinite amount)
        """
        def __init__(self, path):
            self.data = load_json(path)
            
            # Map:
            self.map = self.data["layers"][0]["data"]
            self.height = self.data["layers"][0]["height"]
            self.width = self.data["layers"][0]["width"]
            
            # Tileset:
            # If this works out, I'll have to account for mupliple tilesets in the future.
            # self.imageheight = self.data["tilesets"]["imageheight"]
            # self.imagewidth = self.data["tilesets"]["imagewidth"]
            self.image = "content/gfx/tilesets/tmw_desert_spacing.png" # + self.data["tilesets"][0]["image"]
            self.ts_name = self.data["tilesets"][0]["name"]
            self.tileproperties = self.data["tilesets"][0]["tileproperties"]
            self.col = [int(key) + 1 for key in self.tileproperties.keys()]
            
        def get_args_for_composite(self):
            """
            Returns a list of arguments to be unpacked for Composite
            """
            args = list()
            for y in xrange(self.height):
                for x in xrange(self.width):
                    args.append((x*96, y*96))
                    args.append(self.ts_name + str(self.map[y*(self.height+(self.width-self.height)) + x]))
            return args
            
        def build_map(self):
            """
            Builds the map and returns it as RenPy Image
            """
            # Register the times as images first:
            t = 1
            # Margins + Spacing
            for y in xrange(6):
                for x in xrange(8):
                    _x = x + 1
                    _y = y + 1
                    renpy.image(self.ts_name + "%d"%t, im.Scale(im.Crop(self.image, (x*32+_x, y*32+_y, 32, 32)), 96, 96))
                    t += 1
                    
            # Build the map into single image and return it:
            args = self.get_args_for_composite()
            return LiveComposite((3840, 2400), *args)
Like what we're doing? Support us at:
Image

AxemRed
Veteran
Posts: 482
Joined: Sun Jan 09, 2011 7:10 am
Contact:

Re: is faster Sprites or CustomDisplayable? (very technical!

#9 Post by AxemRed »

Code: Select all

img=im.MatrixColor(ImageReference(img),im.matrix.tint(Ir,Ig,Ib)*im.matrix.saturation(Is)*im.matrix.brightness(Il))
im.MatrixColor creates a copy of its input image and multiplies every pixel with a 4x4 matrix. That's not something you want to be doing every frame.

User avatar
jack_norton
Lemma-Class Veteran
Posts: 4084
Joined: Mon Jul 21, 2008 5:41 pm
Completed: Too many! See my homepage
Projects: A lot! See www.winterwolves.com
Tumblr: winterwolvesgames
Contact:

Re: is faster Sprites or CustomDisplayable? (very technical!

#10 Post by jack_norton »

Yes but that happens only when in the game is night time, so in my tests is never executed and is still super slow. I tried to use caching but seems it didn't improve the situation at all :(
Maybe the problem is python or android lib or something else. I'm doing a tower defense game with a friend coder, and using C plus his own lib I can show 500 sprites on screen at once at 26FPS on the low-end android tablet :D so is possible to do much better, but of course is not fair to compare a C lib with a system like renpy, but thought I could at least make it run a bit faster.
follow me on Image Image Image
computer games

User avatar
jack_norton
Lemma-Class Veteran
Posts: 4084
Joined: Mon Jul 21, 2008 5:41 pm
Completed: Too many! See my homepage
Projects: A lot! See www.winterwolves.com
Tumblr: winterwolvesgames
Contact:

Re: is faster Sprites or CustomDisplayable? (very technical!

#11 Post by jack_norton »

Okay so an update: I tried disabling the map completely on Android, and the map change is still painful. So the slowness is somewhere else (no clue where... on desktop is instant!!) but I think is safe to say that is not a problem of graphic performance at least!
follow me on Image Image Image
computer games

Kinsman
Regular
Posts: 130
Joined: Sun Jul 26, 2009 7:07 pm
Location: Fredericton, NB, Canada
Contact:

Re: is faster Sprites or CustomDisplayable? (very technical!

#12 Post by Kinsman »

I've done some experiments with my Android phone, and once I had a problem with long pauses when I was doing rollback. I eventually discovered the problem was me putting an 'import os' statement in my script, that I was using for something unrelated.

Does your game use any extra imports or libraries?
Flash To Ren'Py Exporter
See the Cookbook thread

User avatar
jack_norton
Lemma-Class Veteran
Posts: 4084
Joined: Mon Jul 21, 2008 5:41 pm
Completed: Too many! See my homepage
Projects: A lot! See www.winterwolves.com
Tumblr: winterwolvesgames
Contact:

Re: is faster Sprites or CustomDisplayable? (very technical!

#13 Post by jack_norton »

Ah I have that import os indeed, to center the window on desktop! I'll try to remove that, thanks :)
follow me on Image Image Image
computer games

User avatar
jack_norton
Lemma-Class Veteran
Posts: 4084
Joined: Mon Jul 21, 2008 5:41 pm
Completed: Too many! See my homepage
Projects: A lot! See www.winterwolves.com
Tumblr: winterwolvesgames
Contact:

Re: is faster Sprites or CustomDisplayable? (very technical!

#14 Post by jack_norton »

In case someone is curious, I solved this, the problem was in my code :lol:
I was iterating with a for cycle for a list of over 100 items, each made of 7-8 elements. Now I copy only the items for the current map which is not longer than 7-8 and is super fast :)

Next challenge is to speed up the battle screen but I don't know how to use a customdisplayable with buttons (or that works as button) :mrgreen:
follow me on Image Image Image
computer games

Anima
Veteran
Posts: 448
Joined: Wed Nov 18, 2009 11:17 am
Completed: Loren
Projects: PS2
Location: Germany
Contact:

Re: is faster Sprites or CustomDisplayable? (very technical!

#15 Post by Anima »

You simply give them an event method.
Avatar created with this deviation by Crysa
Currently working on:
  • Winterwolves "Planet Stronghold 2" - RPG Framework: Phase III

Post Reply

Who is online

Users browsing this forum: No registered users