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
-
@stephen why in the scene you sometimes pass texture like a built in image, like
SpriteNode(texture='plz:alien')
, sometimes create texture objectself.texture = Texture()
? The same with position - sometimes pass it in tuple, sometimes createPoint()
. What's the difference? -
@Karina said:
scene is an extension of ui and the Node subclasses are View subclasses just enhanced for a VideoGame Environment.
Then maybe it's better to learn first ui
@stephen those things you wrote are scene-ui features? Like gpu pipeline, custom shaders, openGL?you can do both at the same time if you want. and all those are Examples of stuff that re Graphics card(GPU) focused not CPU
-
@Karina said:
@stephen why in the scene you sometimes pass texture like a built in image, like
SpriteNode(texture='plz:alien')
, sometimes create texture objectself.texture = Texture()
? The same with position - sometimes pass it in tuple, sometimes createPoint()
. What's the difference?if your subclassing
SpriteNode
you must explicitly pass a Texture object. but if you are just creating aSpriteNode
object liketree = SpriteNode(...)
theSpriteNode
class Implicitly Converts a string or ui.Image object to a Texture object but you can still Pass Texture object if you wish.same goes with Point and Size. you dont have to expliitly create them but i try to. they are subclasses of
scene.Vector2
i mainly do this because if i create aSize
orPoint
object that normally would just be a normal 2 Tuple then that gets the extra attributes and thats checkout the Docs For them real quick. -
@Karina said:
So which of the touches needed for game or for future?
if you want to use the one i placed in preperation its going to be,
if(cls.needsTouch): for node in cls.needsTouch: node.touch_began(touch)
@Karina said:
def touch_began(self, touch): self.ChangePose(1) self.idleJet.remove_from_parent() self.jetpack.add_child(self.liftJet) self.animate.Ascend(duration=0.15) if self.isDead != False: self.scene.gameOver=True self.isDead=True self.ChangePose(4)
And why you are ending the game in touch_began? He will be hit when touch began?
''' Step 1: if Main.gameOver has not been enabled send the Touch object to EventManager Main =β― EventManager ''' # Main() def touch_began(self, touch): if(self.gameOver == False): EventManager().touch_began(touch) if(EventManager.canClose and self.gameOver): self.view.close() ''' Step 2: send Touch object to Player EventManager =β― Player ''' # EventManager def touch_began(cls, touch): if(cls.player): cls.player.touch_began(touch) ''' Step 3: if Player.isDead has been enabled we insure Main.gameOver and Player.isDead are set then run the player.ChangePose() for game over. we insure Player.isDead is set even though wevjust got avtrue return because anything could hapen in a frame or two and we dont want out player to end its run and our game still run. Player =β― End of Touch objects life. ''' # Player def touch_began(self, touch): if self.isDead != False: self.scene.gameOver=True self.isDead=True self.ChangePose(4) ''' Step 4: if Main.gameOver has been enabled and our EventHandlercloseTimer still has more than 0 sec we remove delta Time (time since last frames) every frame once 0 we set EventHandler.canClose enabled ''' # EventManager def Pass(cls, dt, game): if(game.gameOver): if(cls.closeTimer > 0): cls.closeTimer -= dt if(cls.closeTimer < 0): cls.canClose=True ''' Step 5: now that both EventManager.canClose and Main.gameOver are both True we have ran all our events and can close the game. ''' #Main def touch_began(self, touch): if(self.gameOver == False): EventManager().touch_began(touch) if(EventManager.canClose and self.gameOver): self.view.close()
-
This post is deleted! -
But in the
cls.player.touch_began(touch)
player is None. I looked forPlayer()
in the class but π€·ββοΈdef touch_began(self, touch): if self.isDead != False: self.scene.gameOver=True self.isDead=True self.ChangePose(4)
And hereπ it seems that you check if the player is dead, and if not, make him dead and end the gameπ³
-
@Karina said:
But in the
cls.player.touch_began(touch)
player is None. I looked forPlayer()
in the class but π€·ββοΈdef touch_began(self, touch): if self.isDead != False: self.scene.gameOver=True self.isDead=True self.ChangePose(4)
And hereπ it seems that you check if the player is dead, and if not, make him dead and end the gameπ³
Essentially Iβm doing the same as bellow.
def touch_began(self, touch): if self.isDead: self.scene.gameOver=True self.ChangePose(4)
You can do this anywhere really. Here we also do this in
def RemoveHP(self, val): hp=self.hitPoints - val if(hp > 0): self.hitPoints = hp Animation(self.scene.infoBox.hpBar).ResizeBar(hp/100) else: self.hitPoints=0 Animation(self.scene.infoBox.hpBar).ResizeBar(0) self.isDead=True self.ChangePose(4)
Then inside
Player.ChangePose
we also resetisDead
... elif(pose == 4): self.isDead=True self.shield.remove_from_parent() self.up.remove_from_parent() self.down.remove_from_parent() self.idle.remove_from_parent() ...
its a bit overkill but at least we know it gets set lol. in relity most of this woulf of been removed but i didnt go over as well as I should of. Only issue with how it sits is we only want one call to
Player.ChangePose
so i would remove the one insidetouch_began
-
So if it's overkill, maybe I don't need to understand it now. I'm trying to get main sense of this, how it works
And what about the first part, self.player that is set None?
-
@Karina
yes ourEventManager.player
gets initialized asNone
. fom here we can set this property from anywhere we need. in our case we set it inside ourPlayer.__init__
.
This way anytime we create a new player we know the touch and update events ate going to the correct object.
I just started a group of Episodes for Tutorials that will run through a step by step of the game explaining each peice.
i will make a post aswell as ading to my GitHub Repository as each episode is finished. π if you would like you can start with thoes. i will be removing bugs and redundent code(the overkill) and presenting a more profesional and consistant coding style/convention. ill have Episode One finished and posted today at the least.
but dont stop the questions! your questions along have helped debug and correct myself a few times already π
-
@Karina Posted the Into and first installment of the Tutorial. its not much yet but its going to explain in more detail and teach alot more than just throwing a bunch of messy code out there. if you can hangin there for me to rlease each one i think it will be much better for many
-
@stephen yes this will help a lot. Asking things one by one will take a year maybeπ . The problem is that after you explain I understand what you did, but not why exactly like that
-
@Karina i could tell i wrote it a bit confusing so i do better π im posting Episode two in justba min then probaby 3 tomorrow
-
@stephen don't worry you wrote and it's πππ. I have lots of stuff to learn now)
-
We will only have one
EventManager
each game sessionI understood that you explain here why to use class methods, but what this means? Game session is time from the start to when view is closed? And one EventManager means we have only one class like that
-
@mikael here is a better answer to your question earlier..
@Karina said:
We will only have one
EventManager
each game sessionI understood that you explain here why to use class methods, but what this means? Game session is time from the start to when view is closed? And one EventManager means we have only one class like that
when i say Game Session i am indeed refering to start time until program termination.
when you use
__init__(self)
this returns a new instanse of the class object and each instance has its own block in memory. just as if you calledFoo()
but__init__
provides instance customizing before returned.Here is a quick example of the memory blocks using Instances
class Bar(object): pass b1=Bar() b2=Bar() b3=Bar() print(f'b1 memory address: {id(b1)}\n', f'b2 memory address: {id(b2)}\n', f'b3 memory address: {id(b3)}')
output:
b1 memory address: 4518930920 b2 memory address: 4518803496 b3 memory address: 4532805080
Now just Using references to a single Class object
class Bar(object): pass b1=Bar b2=Bar b3=Bar print(f'b1 memory address: {id(b1)}\n', f'b2 memory address: {id(b2)}\n', f'b3 memory address: {id(b3)}')
output:
b1 memory address: 4996960280 b2 memory address: 4996960280 b3 memory address: 4996960280
now i have 3 references to the class in diferent places without using extra memory.
class Foo(object): baz = "Class Attribute baz" @classmethod def from_cls_obj(cls): print(cls.baz)
we still have access to class methods even from an instance but not the other way around. lets test if a class with
__init__
still gets access tocls
usingself
..class Foo(object): baz = "Class Attribute baz" def __init__(self): self.bar="Instance Attribute bar" def from_self(self): print(self.bar) @classmethod def from_cls(cls): print(cls.baz) b1=Foo b2=Foo() b1.from_self() # TypeError: from_self() missing 1 required positional argument: 'self' b2.from_self() # Instance Attribute bar b1.from_cls() # Class Attribute baz b2.from_cls() # Class Attribute baz print(f'b1: {b1.baz} / {b1.bar}') # AttributeError: type object 'Foo' has no attribute 'bar' print(f'b1: {b1.baz}') # b1: Class Attribute baz print(f'b2: {b2.baz} / {b2.bar}') # b2: Class Attribute baz / Instance Attribute bar
As expected, only the instanced object had access to self.
so as long as you dont absolutly need the "pre-returned" customizing then using a standard
class
with cls seems to be the most effecient option.
and in theEventManagers
case, its perfect.
hopfully i didnt go overboard or off topic. but this seemed to be the best way to explane the reasoning behind our use ofcls
-
@stephen, why I asked is that it seems to me that you are paying a price by using the class-based approach:
- Bother and clutter of writing
@classmethod
everywhere - Having to remember where to use
self
and where to usecls
- Not able to use standard Python tools like
@property
, should you need them
The price might be small, but seems like you are getting nothing in return. It looks to me that you are using a class just because it is a global, when you could use a global variable instead.
To illustrate:
file1.py
:class EventManager: @property def address(self): return id(self) event_manager = EventManager() # The global instance print(event_manager.address)
file2.py
:from file1 import event_manager def some_function(): print(event_manager.address) some_function()
If you run file2, you get the same object and the same numbers.
Since
event_manager
is an object, you can set it's values without needing to explicitly declare it as aglobal
in the function.If you want to be absolutely sure that there is only 1 EventManager instance ever, you can use a custom
__new__
to make sure thatEventManager()
always returns the same instance.I am not saying you should not use a class if it fits your style, but I guess in a tutorial you need to think about what you promote as best practice.
- Bother and clutter of writing
-
This post is deleted! -
@Karina the original was just a broad example where my current is intended to be a tutorial it will be very similar to the example only because for some people that used the example will feel more comfortable. and since im limiting it to built in assets It's limited on visual theme but that's ok π
@mikael this is Great information! one one of the reasons i wanted to do this tutorial is for feedback lime this π im a strong believer on people should never stop trying to better no mater the topic.
I was using the global variable approach similar to how you present it but I was under the impression that scen's event loop was a bit "sketchy"on how global variables were handled/maintained.
which is why pretty much anything I post that uses scene doesn't use global variables unless it's just aliasing another object. Have you seen/heard any of this?
-
def setup(cls, *args): cls.main=args[0] cls.hud=args[1]
@stephen Here the args is a dict or you access like that first and sec arg?
-
This post is deleted!