Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello β I'm @olemoritz on Twitter.
[SPACE ESCAPE]-Game example to help with Game dev
-
@Karina said:
@stephen cool designπ
And this is not thread of the tutorial?this thread for the Example Game π. though it is the Topic the code and questions wont be quite the same. thats why im doing the Tutorial in small chunks. that way there is a thread for each and they wont get flooded with conversation from all over the place. everything should stay based on each "Episode". people in the future, or even right now, that are doing the tutorial may never see the questions you have asked and the answer to even one of your questions could of helped many people. but they cant if they are in the wrong Thread.. and thats a shame because you usually have really good questions!
Links to Tutorial:
At the top of each there is a link to the the Files for each.
-
For myself it was about 4 months of trial and error crash course. and every now and then i would find somthing on this forum (ill note that it was tough to find helpfull threads at the time because it was flooded with spam and general conversation Threads.. thanks to @ccc and a couple others they spent the time to filter the forums for everyone. so thats not an issue anymore βΊοΈ) and somtimes a thread on StackOverflow or GeeksForGeeks.. tdamdouni has a great repository of scripts that helped me alot and still do aswell.
That is why im doing this tutorial and why i made th Example.. To try and help th community Continue to grow and hopfully get some cool games along the way π..
-
@stephen I tried to do like an arrow here
class ButtonNode(SpriteNode): def __init__(self, img, action=None, text=None, parent=None, *args, **kwargs): super().__init__(self, img, *args, **kwargs)
But he tells me that in super().init should be 2 numbers. I don't understand what he means at allπ€·ββοΈ
-
@Karina said:
@stephen I tried to do like an arrow here
class ButtonNode(SpriteNode): def __init__(self, img, action=None, text=None, parent=None, *args, **kwargs): super().__init__(self, img, *args, **kwargs)
But he tells me that in super().init should be 2 numbers. I don't understand what he means at allπ€·ββοΈ
when used in a classbdeffenition, in this case
__init__
, super will gather the needed data from the current environment that it needs to define the instance. so here you dont pass self and super forSpriteNode
is expecting a string fortexture
(string is a sequence of characters) and not self. this automatic defining is why super is used. its called Cooperative Inheritance. as long as there is a super call in all base classes it allows for multiple inheritance without having to bother with explicitly defining who the parent or base class is. for example it would be more practical to made a button a ShapeNode that way you can add a border to the button without adding another sprite in the background by usingpath
. now if we had putsuper().__init__(*args, **kwargs)
and insured the correct args are passed we would just need to change the base class fomButtonNode(SpriteNode):
toButtonNode(ShapeNode):
and super would handle the rest. ππ -
@stephen So here better to put ShapeNode instead of SpriteNode?
-
-
@stephen Yes, I know, I copied it to my pythonista when you showed it. But there a lot of things, don't know from where to begin it. I just used to write more linear, so it's difficult for me to understand. But it goes slowly
-
-
class ButtonNode(SpriteNode): def __init__(self, img, action=None, text=None, parent=None, *args, **kwargs): super().__init__(img, *args, **kwargs) def setup(self): bg = ui.Path.rounded_rect(0, 0, 200, 200, 8) bg.line_width = 7 self.bg = ShapeNode(bg, stroke_color='blue', parent=self) self.add_child(self.bg)
@stephen here he tells me line_width is invalid arg, but Path obj has it, and in example they do the same. Why I can't do that?
-
@Karina said:
class ButtonNode(SpriteNode): def __init__(self, img, action=None, text=None, parent=None, *args, **kwargs): super().__init__(img, *args, **kwargs) def setup(self): bg = ui.Path.rounded_rect(0, 0, 200, 200, 8) bg.line_width = 7 self.bg = ShapeNode(bg, stroke_color='blue', parent=self) self.add_child(self.bg)
@stephen here he tells me line_width is invalid arg, but Path obj has it, and in example they do the same. Why I can't do that?
To start, im not too sure how your executing
setup
you dont make any calls to it. the code insidesetup
Is functional but I would like to point out that you are adding 2 children of this ShapeNode to self.. When you pass parent in any subclass ofNode
it will add that instance to the list of children forself
. i would removeself.add_child(self.bg)
since we know and who the parent will be at construction time π.as for you question here i had zero issues with
bg.line_width
when i ran thissetup
method inside myScene
subclass. ill need to see how you cal calling yoursetup
here -
@Karina said:
@stephen said:
@Karina said:
@stephen So here better to put ShapeNode instead of SpriteNode?
i would personally. But there's nothing wrong with using SpriteNode. Check out this one I made not long ago.
Can you explain why, what ShapeNode has that we need?
you defenently do not need
ShapeNode
but there is no reason not to. I probably should of mentioned this earlier lol im sorry..ShapeNode
is a subclass ofSpriteNode
! πππ So you have everything aSpriteNode
has includingtexture
as well aspath
. this means you can use a image for the background and use a Path for your Border..# Just to show using Borders with a image Background class ButtonNode(ShapeNode): def __init__(self, rect=(0, 0, 150, 75), corner_radius=6, *args, **kwargs): super().__init__(*args, **kwargs) x, y, width, height = rect if corner_radius > 0: self.path = ui.Path.rounded_rect(x, y, width, height, corner_radius) else: self.path = ui.Path.rect(x, y, width, height) lw=self.line_width=10 self.fill_color=None self.stroke_color='blue' self.bg_texture=SpriteNode( texture=Texture('pzl:Blue8'), size=self.size-(lw, lw), parent=self)
If you were to try and do this with a normal
SpriteNode
you can never have path borders with a image background without covering the edges of your image. Also by using a SpriteNode as base class only the ShapeNode child has corner_radius so your texture images corners will show outside your rounded edges. -
@Karina said:
@stephen Yes, I know, I copied it to my pythonista when you showed it. But there a lot of things, don't know from where to begin it. I just used to write more linear, so it's difficult for me to understand. But it goes slowly
I shortened my
ButtonNode
and removed rhe Event Manage. there is some short comments on this one too.. hopfully it will help you understand better?import scene class ButtonNode(scene.ShapeNode): def __init__(self, action, name=f'ButtonNode', bg_color='lightgray', accessoryColor='red', icon=None, accessory='emj:Exclamation_Mark_2', anchor_point=(0.5, 0.5), font_family='<system>', font_size=16, parent=None, position=(0, 0), size=(120, 45), corner_radius=8, border_size=20, borderColor='black', text='', text_color='black', enabled=True, *args, **kwargs): # these mainly for call to super() self.x, self.y = position self.w, self.h = size super().__init__( path=scene.ui.Path.rounded_rect( self.x, self.y, self.w, self.h, corner_radius), fill_color=bg_color, stroke_color=borderColor, shadow=None, parent=parent, *args, **kwargs) # Normal Properties for Instance() self.enabled=enabled self.button_action=action self.name=name self.position=position self.size=size self.anchor_point=anchor_point self.icon=self._init_textures(icon) # for border self.border_size=border_size self.borderColor=borderColor self.corner_radius=corner_radius # for accessory self.accessory=self._init_textures(accessory) self.showAccesory=False self.accessoryColor=accessoryColor # for Label self.text=text if text != '' else self.name self.text_color=text_color self.font_family=font_family self.font_size=font_size # Container to hold each component. # is just a dict version of self.children but specific. self.components=dict({ 'accessory':None, 'icon':None, 'label':None}) self._setup(self.icon, self.accessory, self.components) # Type Check to make sure img is a string or ui.Image def _init_textures(self, img): if type(img) == str or type(img) == scene.ui.Image: return scene.Texture(img) else: return None # setup our Components def _setup(self, i, a, c): if a != None: # small indicator image in top left corner # if its enabled it will disable and hide when pressed # intended for stuff like new item in inventory... c['accessory']=scene.SpriteNode( texture=a, size=(self.size[1]/4, self.size[1]/5*1.5), position=scene.Point(-self.size[0]/2+7, self.size[1]/2-10), parent=self, z_position=4, color=self.accessoryColor) if i != None: # button image c['icon']=scene.SpriteNode( texture=i, size=scene.Size(self.size[1]/2, self.size[1]/2), position=scene.Point(self.w/2 - self.size[1]/3 , 0), parent=self, z_position=9) if self.text: # button text.. c['label']=scene.LabelNode( text=self.text, font=(self.font_family, self.font_size), position=scene.Point(0 , 0), anchor_point=(0.5, 0.5), color=self.text_color, parent=self, z_position=10) # called when you tap the button def Button_Tapped(self): if self.components['accessory'].alpha > 0: self.components['accessory'].alpha = 0 if self.enabled: self.button_action(self) # custom action def my_button_action(sender): print(f'{sender.name}: {sender.text}..') class main(scene.Scene): def setup(self): self.buttons=list([]) self.background_color='lightgray' self.my_button=ButtonNode( text='My Button', parent=self, action=my_button_action, position=scene.Point(self.size[0]/2, self.size[1]/2+100)) self.buttons.append(self.my_button) def touch_began(self, touch): for btn in self.buttons: if self.point_from_scene(touch.location) in btn.frame: btn.Button_Tapped() scene.run(main())
-
ShapeNode is a subclass of SpriteNode!
Yeah, they say it in docs, I just forgotπ€¦ββοΈ. Then it's more clever to make ShapeNode base class
-
lw=self.line_width=10
Here why you're lw and self.line_width? One is not enough? And it seems it should be self.path.line_width cause Path has that attr.
self.bg_texture=SpriteNode( texture=Texture('pzl:Blue8'), size=self.size-(lw, lw), parent=self)
You're trying make here the form of Blue8, but with rounded angles?
-
@stephen in built in examples sometimes there are with pyui files. Do you use them?
-
@Karina said:
lw=self.line_width=10
Here why you're lw and self.line_width? One is not enough? And it seems it should be self.path.line_width cause Path has that attr.
ShapeNode
has a few setting for path so that you dont need to set them for every path. when you pass a new path object to yourShapeNode
it doesva check on the follwing Properties and if the value is not None it applies this to your new Path object.with that said if you have say
ShapeNode().line_width
set to 3 and for just the next path object you want the width set to 6. you would need to setShapeNode().path
to the new path object and then setShapeNode().path.line_width = 6
otherwise yourShapeNode().line_width
will overide the path's predefined value. Only reason not to just changeShapeNode().line_width
before creating the path is in this example you want it to only change once and would hate to forget to change it back lol.ShapeNode Path Properties:
ShapeNode().line_width ShapeNode().fill_color ShapeNode().path ShapeNode().shadow ShapeNode().stroke_color
-
@Karina said:
Here why you're lw and self.line_width? One is not enough?
Yes one is more than enough. I do this at times to shorten line length
-
as for you question here i had zero issues with bg.line_width when i ran this setup method inside my Scene subclass. ill need to see how you cal calling your setup here
@stephen here's the full code though I've changed a little reading your advices
from scene import * import ui def sw(): return get_screen_size()[0] def sh(): return get_screen_size()[1] def bw(): return 150 def bh(): return 140 class ButtonNode(ShapeNode): def __init__(self, rect=(0, 0, 150, 75), corner_radius=7, *args, **kwargs): super().__init__(img, *args, **kwargs) x, y, width, height = rect def setup(self): bg = ui.Path.rounded_rect(0, 0, 200, 200, 8) bg.line_width = 7 self.bg = ShapeNode(bg, stroke_color='blue', parent=self) self.add_child(self.bg) def button_tapped(sender): print(sender.text) class Main(Scene): def setup(self): self.background_color = '#eaeaea' down = ButtonNode('iob:arrow_right_b_256', size=Size(bw(), bh()), line_width=7, position=Point(sw()-70, sh()/2)) down.make_button() self.add_child(down) run(Main(), LANDSCAPE)
-
Do you know how to make it in portrait orientation? No matter which attr I pass, it's still in landscape. Or that's just because of version,of my iPad?
-
@Karina said:
Do you know how to make it in portrait orientation? No matter which attr I pass, it's still in landscape. Or that's just because of version,of my iPad?
Heres the script back. i did a few corrections. you were ading the new code but not using it. lol but thats ok. this stuff can make you pull your hair out from time to time. Af for your error message for line_width, it's not part of the constructor parameters so this must be set after calling the super's Init. Interpreter didn't know the property existed yet. I'm assuming it has to do with execution inside ShapeNode itself.
Also you need to remove your fill color to see your texture. But we are getting there!
At the moment we can not control our Orientation on iPad. It has to do with ios13 and multi-tasking support for Pythonista. But a few of us are working on that
from scene import * import ui def sw(): return get_screen_size()[0] def sh(): return get_screen_size()[1] def bw(): return 150 def bh(): return 140 class ButtonNode(ShapeNode): def __init__(self, rect=(0, 0, 150, 75), corner_radius=8, bakground_color='lightgreen', line_width=0, *args, **kwargs): super().__init__(*args, **kwargs) self.tint = bakground_color x, y, width, height = rect self.position, self.size = get_screen_size()/2, (rect[2], rect[3]) # def setup(self): bg = ui.Path.rounded_rect(x, y, width, height, corner_radius) bg.line_width = line_width self.bg = ShapeNode(bg, stroke_color='blue', fill_color='clear',parent=self) # self.add_child(self.bg) def button_tapped(sender): print(sender.text) class Main(Scene): def setup(self): self.background_color = '#b7b7b7' down = ButtonNode( texture=Texture('iob:arrow_right_b_256'), size=Size(bw(), bh()), line_width=5, position=Point(sw()/2, sh()/2), parent=self) # self.add_child(down) run(Main(), LANDSCAPE)