Forum Archive

SpriteNode.texture issue

Auer

I have what I believe to be a simple question..... how do i get the last line of code here to work.

tile.texture=current_tile

tile is a SpriteNode Object thing, and the current tile is just a png

I'm attempting to edit a tilemap of SpriteNodes.... I'm quite clueless as all of my programming i learned in pygame.

```
from scene import *
import random
import ui

A = Action
g1 ='g1.PNG'
g2 ='g2.PNG'
g3 = 'g3.PNG'
g4='g4.PNG'
g5='g5.PNG'
tb='toolbar.PNG'

all_tiles=[g1,g2,g3,g4,g5]
tiles=[g1,g2,g1,g3,g4,g5,g2,g1]
current_tile=g1

mapsize=100

themap=[[] for _ in range(mapsize)]

class Arrow (SpriteNode):
def init(self, kwargs):
SpriteNode.init(self, 'arrow.PNG',
kwargs)

class MyScene (Scene):
def setup(self):
self.unit=20
unit =self.unit
self.items=[]
self.points=0
#states of buttons
self.edit_mode=False
self.recenter_gravity=0,0,0

    self.background_color = '#000000'
    sizex,sizey=self.size
    self.ground = Node(parent=self)

    self.gen_map()

    #player
    self.ship = SpriteNode('p1.PNG')
    self.ship.size=(unit,unit)
    self.ship.position = self.size / 2
    self.add_child(self.ship)

    #__________BUTTONS&UI_____________
    self.toolbar=SpriteNode('toolbar.PNG')
    self.toolbar.size=(unit*16,unit*2)
    self.toolbar.position=(unit*8,unit)
    self.add_child(self.toolbar)

    self.edit_button=SpriteNode('emj:Wrench')
    self.edit_button.size=(unit*2,unit*2)
    self.edit_button.position=(unit,unit)
    self.add_child(self.edit_button)

    self.re_center=SpriteNode('emj:Anger_Symbol')
    self.re_center.size=(unit*2,unit*2)
    self.re_center.position=(unit*3,unit)

    self.selected=SpriteNode(g1)
    self.selected.size=(unit*2,unit*2)
    self.selected.position=(unit*8,unit)
    self.draws=SpriteNode('emj:Black_Nib')
    self.draws.size=(unit*2,unit*2)
    self.draws.position=(unit*6,unit)


    self.tree = SpriteNode('leaves.PNG')
    self.tree.size=(unit*3,unit*3)
    #self.add_child(self.pb)

    #text on screen
    self.score= LabelNode('0')
    self.score.position=10,sizey-10
    self.add_child(self.score)




def update_map(self):
    sizex,sizey=self.size
    self.ground.position.x
    for _ in themap:
        for tile in _:

            if tile.position.x+self.ground.position.x>=-self.unit and tile.position.y+self.ground.position.y>=-self.unit and tile.position.x+self.ground.position.x<= sizex +self.unit and tile.position.y+self.ground.position.y<= sizey+ self.unit:
                tile.z_position=-1
                self.ground.add_child(tile)
            else:
                tile.remove_from_parent()

def gen_map(self):
    sizex,sizey=self.size
    unit=self.unit
    #laggyyyy if multi press!!!
    for _ in themap:
        for tile in _:
            tile.remove_from_parent

    #themap=[[] for _ in range(mapsize)]
    #generate a random map
    y=-unit/5
    zpos=mapsize
    ypos=0
    for _ in range (mapsize):

        x=-unit
        y+=unit*2
        for _ in range (mapsize):

            x+=unit*2
            tile = SpriteNode(random.choice(tiles), position=(x, y),)
            tile.size=unit*2,unit*2
            tile.z_position=zpos
            themap[ypos].append(tile)
        zpos-=1
        ypos+=1

def place_tree(self,xy):    
    self.tree.position=xy-self.ground.position
    self.tree.z_position=2

    self.ground.add_child(self.tree)

def projectile(self,x,y):
    ammo = Arrow(parent=self)
    ammo.size=self.unit*2,self.unit*2
    ammo.position = (self.ship.position)
    #ammo.rotation=self.ship.rotation
    actions = [A.move_to(x,y,1,6), A.remove()]
    ammo.run_action(A.sequence(actions))
    ammo.rotation=self.ship.rotation
    self.items.append(ammo)

def check_ammo_collisions(self):
    sizex,sizey=self.size
    for ammo in self.items:
        if ammo.position in     self.pb.frame:
            self.items.remove(ammo)
            ammo.remove_from_parent()
            self.points+=1
            self.pb.position=random.randint(0,sizex),random.randint(0,sizey)

def update(self):
    self.update_map()
    self.check_ammo_collisions()
    #self.score.text=str(self.points)

    #save old posotion
    pos=self.ground.position
    posx,posy=pos

    sizex,sizey=self.size
    x, y, z = gravity()
    #adjust for held angle of device
    xx,yy,zz = self.recenter_gravity
    x-=xx
    y-=yy
    z-=zz

    self.ground.position-=(x * 10, y * 10)

    #use old and new posotion for rotation
    newpos=self.ground.position
    newposx,newposy=newpos
    deltax=posx-newposx
    deltay=posy-newposy
    self.ship.rotation = math.atan2(deltay,deltax)-4.7


def wrench(self,xy):
    if xy in self.edit_button.bbox:
        if self.edit_mode==True:
            self.edit_mode=False
            self.edit_button.color='red'
            self.re_center.remove_from_parent()
            self.draws.remove_from_parent()
            self.selected.remove_from_parent()
        elif self.edit_mode==False:
            self.edit_mode=True 
            self.edit_button.color='#00ff00'
            self.add_child(self.re_center)
            self.add_child(self.draws)
            self.add_child(self.selected)

def editmode(self,xy):  
    if xy in self.re_center.bbox:
        self.recenter_gravity=gravity()

    elif xy in self.draws.bbox:
        pass

    elif xy in self.selected.bbox:
        pass

def playmode(self,xy):      
    pass

def touch_began(self, touch):
    xy =touch.location
    if xy in self.toolbar.bbox:
        self.wrench(xy)
        if self.edit_mode==True:
            self.editmode(xy)   
        else:
            self.playmode(xy)
    else:
        xy-=self.ground.position
        #non toolbar interactions
        if self.edit_mode==True:
            for _ in themap:
                for tile in _:
                    if xy in tile.bbox:
                        tile.texture=current_tile

run(MyScene(), PORTRAIT)

JonB

@Auer

SpriteNode.texture
The Texture used to draw the sprite.

You need to convert the string image names to scene.Textures. Best to do that up front, and keep a list of Textures around.

all_tiles=[Texture(n) for n in [g1, g2, g3, g4, g5] ]
etc
Auer

So i made the change and it doesnt give me the texture error, but now something crazy is happening. The texture color is overriding the background color and surrounding tiles.

tile.texture=current_tile

Turns my PRE-EDIT GAME MAP into this.
BROKEN TEXTURED MAP

But this simple code

tile.color='#ff0000'

CHANGES THE COLOR JUST FINE

I'm so confused why it doesn't just change the texture, yet it changes the color correctly???

JonB

Can you post a gist containing your code? (Along with any other resources needed to run it?). From the pythonista file browser menu, you can tap edit, then select multiple files, then share button to share to gist. Or if just one file, you can do it from the wrench menu.

Have you verified that themap is populated correctly?

Supposedly, spritenode.color will tint a texture. Not sure, but you might need to set the UI.Image rendering mode to RENDERING_MODE_TEMPLATE, which allows tinting. see
image docs, there is a method to create a copy with a different mode.

Auer

The Gist
I also commented all the images, really any images can be used tho since i specify size.... i think
Thanks for the help!

I assumed that since tile.color='#whatever' works perfectly it must not be 'themap' 's problem

JonB

I couldnt reproduce your issue, but i was using your original code. Ill try with your gist.

one issue is that SpriteNodes take up resources, and it is not a good idea to have 10000 of them in a scene. Your code ran at about 3fps for me.. though i think the images i was using were larger than 20x20, so all the rescaling might be hurting performance too.

it seems to work well to render a single ground texture(which can be stored to a ui.Image, once), then just update bits as needed. This could be done prior to starting the scene, or even saved in a file for faster initial starting. It is not really necessary to try to do screen bounds checking, except maybe if you start getting into really huge maps, but in that case it makes more sense to divide the map into "zones", and you would only ever be showing the 9 adjacent zones to the current zone.

https://gist.github.com/f55322ba716116cf7bf4cbc2e52a0b97
Here is a version that operates at 60fps on my old ipad3 using this idea. themap was changed to an array of indexes, so you could still do things like collision checking with ground tiles of certain type. I render the entire map to an image/texture.

edit mode has a second of lag or so, because it still has to draw the original map, then draw a new tile on top, then convert that to a texture again. Dividing the ground into a small number, say 10x10 of chunks each containing a single texture of 10x10 tiles probably would speed that up even more.

i was lazy on the edit mode tile indexing, it works, but may be somewhat imprecise. probably need something like (xy.x+unit) // (2*unit) to match the center, not corner, of grid, but you maybe would want to draw the touch, and print the grid Coords to confirm.