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.
Flappy bird
-
@Karina said:
@Drizzel i see now what brushes are. Do you know some article to read about it? But what difference between w() and self.size.w?
And MaxBrushSize() just returns 8?
And what difference between creating Potint and just passing a tuple to the position argument?
Sorry about bombarding you with questions)I'll just forward that to @stephen for now (I'm rather pressed on time for the next few hours), he's the mastermind behind brushes.
-
@stephen thank you for help and can you give me some articles about brushes and games on pythonista?
And do you know a way to work with time like in pygame.Clock? I already made a column move and need to add column every 5 seconds -
@Karina said:
@Drizzel you say you clumsy, but I don't know how to do theese things at all)
your explanations helped to understand the beginnin, I think it'll take time to understand it all
Do you know anything like Clock in pygame to work with time? I need to make a new column every 10 secs for exampleThe update(...) function (is used in my code and the code by @stephen) is executed 60 times a second (as long as your device can handle the load). Here's a good explanation. You can use self.dt to check how much time has passed since the last frame was calculated, and self.t to check for how long the scene has been running.
Then just do put this line into setup(...):self.time_of_last_column = self.t
and merge this with your update(self):
def update(self): if self.t - self.time_of_last_column >= 10: #put the code to place a new column here self.time_of_last_column = self.t
-
@Drizzel yes I have the docs in pythonista
I tried to do it like that, but I only get the black screen that I can't even close
def update(self): time_passed = 0 while True: time_passed += self.dt if time_passed >= 0.5: print(time_passed) self.add_column() time_passed = 0 self.column_moves()
-
You have to remove the loop, like this:
def update(self): time_passed += self.dt if time_passed >= 0.5: print(time_passed) self.add_column() time_passed = 0 self.column_moves()
Everything inside update() gets executed 60 times per second. Just put self.time_passed = 0 in setup()
-
This is a bit smarter:
from scene import * import sound import random import math A = Action class MyScene (Scene): def setup(self): self.background_color = 'white' self.blocks = [] for x in range(5): block = ShapeNode(rect(0,0,10,10)) block.color = 'black' block.size = (30, 30) block.position = (self.size.x - 30, x*block.size[1] + block.size[1]/2) self.add_child(block) self.blocks.append(block) self.previous_time = self.t #self.t is the time that the game has been running def update(self): #everything in Here gets called 60 times a second if self.t - self.previous_time >= .5: self.previous_time = self.t self.move_blocks() def move_blocks(self): for block in self.blocks: position = block.position new_x = position[0] - 10 block.position = (new_x, position[1]) if __name__ == '__main__': run(MyScene(), show_fps=False)
-
@stephen about your brushes I began to implement it by I don 't understand some things:
what the difference between w() and self.size.w and
MaxBrushSize just returns 8??
And the program and the graphics look great๐ -
from scene import * import random import time A = Action() def Ground(parent): return SpriteNode('plf:Ground_Grass', parent=parent) def ColumnBlock(parent): return SpriteNode('plc:Brown_Block', parent=parent) class BottomBrush(Node): def __init__(self): self.ground = GroundBlock(self) self.position = (self.size.w) class Game(Scene): def setup(self): self.background_color = '#99d7ff' #to track when 5 seconds passed and need to add a new column self.time_passed = 0 #add the first column so you don't have to wait for it 5 seconds self.columns = [] self.add_column() x = 0 ground = Node(parent=self) ground.z_position = -1 #building the upper and lower ground while x < self.size.w: lower_tile = SpriteNode('plf:Ground_Grass', (x, 30)) higher_tile = SpriteNode('plf:Ground_GrassCenter', (x, 738)) x += 60 ground.add_child(lower_tile) ground.add_child(higher_tile) self.speed = 1 def update(self): self.time_passed += self.dt if self.time_passed >= 5: self.add_column() self.time_passed = 0 self.column_moves() def add_column(self): lower = random.randint(0, 360) // 64 higher = 9 - lower #building the lower part y = 35 for i in range(lower): block = ColumnBlock(parent=self) block.anchor_point = (0.5, 0) block.position = (self.size.w, y) self.columns.append(block) y += 64 #building the higher part y = 738 for i in range(higher): block = ColumnBlock(parent=self) block.anchor_point = (0.5, block.size.h) block.position = (self.size.w, y) self.columns.append(block) y -= 64 def column_moves(self): actions = [A.move_by(-self.size.w, 0, 30/self.speed), A.remove()] for i in self.columns: i.run_action(A.sequence(actions)) run(Game())``` For now I have this. It makes columns move just like I need. There will be the bird and the functions it needs, and I'm done๐
-
Outstanding work so far! im very impressed on how fast you are grasping this.
first ill answer your questions ๐ค
what the difference between w() and self.size.w?
In my example
w()
is recieving the width size of the screen directly.self.size.w
is reference to the currentNode's
width. Yes when used inside yourSubclass
ofscene.Scene
this will be the same value. but this is only becasue you are initiating itsView
by using the module levelrun()
function. (run always launches in fullscreen). but in the future you may notwant fullscreen and this means you would use a customui.View
class then add aSceneView
to it. now if you use thew()
function you will get screen width as expected but now yourself.size.w
will return a different value referencing theSceneView's
frame (frame โจ Tuple(x, y, width, height)
) you can also retrieve this uingMySceneView.frame[2]
. in our case i didnt needto use thew()
function but we practice how we wantto playon game day. ๐ good habets produce great programs.
MaxBrushSize just returns 8??
yes i know the
return 10-2
seems useless. but maybe down the road you want to create, lets say, powerups. one might be the gaps get larger bforn
seconds. since we already have this in out code all we need to do is make a quick change.. this would be the replacement โฅฅdef MaxBrushSize(mod): return 10 - mod
then somwhere in your code you you add somthis similar to this
... def StartGapPowerUp(self, value ): self.maxBrushSize = MaxBrushSize(value) time.sleep(10) #waits 10 seconds self.maxBrushSize = MaxBrushSize(default) ...
ive been playthingwith game development for many years in multiple languges so i have built some habits that i dont notic i do anymore lol.
next post will be some notes nd pointers of you latest code posted and ill put a script showing my previous answeres in action! stay tuned ๐ค๐ค
-
1. self.point=Point(w() + self.position.w, h() - self.size.h)
If w() and self.size.w are the same here, then we got 2*self.position.w? And why instead of sec arg don't just right 0??2. When I tried to use sleep(), my screen just got black and I couldn't close it. And also was a problem and it all hang and didn't move. I did smth and fixed it, don't know how๐ . But why theese things can happen?
I have hundred of questions on each couple lines)
-
''' I realized i didnt fully implemate Brushes and that might have been confusing. so ill post a new script using actual brushes. and as a bonus for showing you actually want to learn and not just copy/paste ill add some extra functionality for your 'cookbook'. i do apologise about the amount on notations lol there beingbalot of comments doesnt mean its a bd script. most is advice. i did change a few things mostly for your convenience and to smooth out animation. but first here is some notes on hat you have provided thus far.. ''' from scene import * import random import time A = Action() ''' i added these so you only have one place to change sizes instead of bouncing around sw โ screen width sh โ screen height bw โ block width bh โ block height so โ screen orientation lw โ level view port width lh โ level view port height ''' def sw(): return get_screen_size()[0] def sh(): return get_screen_size()[1] def bw(): return 64 def bh(): return 96 def so(): return 1 if sw() < sh() else 2 def lw(): return 1024 if so() is 2 else 768 def lh(): return 1024 if so() is 1 else 768 def Ground(parent): return SpriteNode('plf:Ground_Grass', parent=parent, size=Size(bw(), bh())) def ColumnBlock(parent): return SpriteNode('plc:Brown_Block', parent=parent, size=Size(bw(), bh())) ''' not sure what happened here but as you probably know if called wil throw exceptions. im assuming this is why you dont actually use it. self.position = (self.size.w) first a regular Node object hase no size property. BUT it has a bbox wich is a scene.Rect with a width value. even with that said a poisition property much recieve a 2 Tuple (x, y) preferably a Point object but not neccesary. i.e: self.position = Point(x, y) let me know if your having a problem here it seem as your trying to use self to refer to your Scene class. and you can. but not through self. if your not sure how self work i can tell u jut say ''' class BottomBrush(Node): def __init__(self): self.ground = GroundBlock(self) self.position = (self.size.w) class Game(Scene): def setup(self): self.background_color = '#99d7ff' #to track when 5 seconds passed and need to add a new column self.time_passed = 0 #add the first column so you don't have to wait for it 5 seconds self.columns = [] self.add_column() x = 0 ground = Node(parent=self) #building the upper and lower ground ''' Generally a while loop is not a very big deal but in this case its not a great idea. i say this because what if for some unknown reason self.size.w is somthing crazy like 2389488754589433357. ๐ค๐ unlikely i know but just for fun... now your game is hung up seeming like it is frozen to end user. now a for loop, at least i feel, is much safer in this. matter. but to be honest.. a premade pillar object would be best use. ill show this in next post. ''' while x < lw()+bw(): lower_tile = SpriteNode('plf:Ground_Grass', position=Point(x, 0), size=Size(bw(), bw()), anchor_point=(0.5, 0.0)) higher_tile = SpriteNode('plf:Ground_GrassCenter', position=Point(x, lh()), size=Size(bw(), bw())) x += bw() ground.add_child(lower_tile) ground.add_child(higher_tile) self.speed = 1 # Node.speed is defaulted to 1.0 ''' changed z_position to keep to and bottom over blocks ''' ground.z_position = 999 def update(self): self.time_passed += self.dt ''' good job with >= hen comparing floats in this manner never use == sence your calling self.column_moves() every frame we can manually move our blocks. (see method comment) # note: changing "5" to eithere a random int or an instance property will alow a more dynamin level generation. ''' if self.time_passed >= 5: self.add_column() self.time_passed = 0 self.column_moves() def add_column(self): lower = random.randint(1, 360) // bw() higher = 9 - int(lower) #building the lower part y = 35 ''' here you can get rid of the variable "lower" this should reduse memory use. for this game it doesnt matter but in future it could make a diference. use this instead.. for i in range(random.randint(0, 360) // 64): this insures the memory is released after forloop. also i would suggest moving block.anchor_point = (0.5, 0) and block.position = (self.size.w, y) to your ColumnBlock function like this def ColumnBlock(parent, x, y): return SpriteNode('plc:Brown_Block', anchor_point=(0.5, 0), position=Point(x, y), parent=parent) then in your for loop make your "i" var useful and get rid of "y" for i in range(lower): self.columns.append(ColumnBlock(self, self.size.w, i*64)) now we dont add a new block to memory then copy to list. we just add one to list and memory at same time. anchor_point is values 0 to 1 0 being botom left a 1 being top/right (1, 1) would be top right (0.5, 0.5) would be center. changed block.size.h to 1.0 ''' for i in range(1, lower+1): block = ColumnBlock(parent=self) block.anchor_point = (0.5, 0.5) block.size= Size(bw(), bh()) block.position = (lw(), bh()/3+i*bw()) block.z_position = -i self.columns.append(block) y += bw() #building the higher part y = lh() for i in range(1, higher+1): block = ColumnBlock(parent=self) block.anchor_point = (0.5, 0.5) block.position = (lw(), (lh()-i*bh()/2)) block.z_position = i self.columns.append(block) y -= bh() ''' great work on this part only sugestion would be to set your interp timing. probably TIMING_SINODIAL in this case cuz its mich smoother than linear. and this will go where you have "30/self.speed". and this oddly doesnt thow an excption and id avoid "0" for duration. 0.1 seems fast enough. or even 0.01 if needed. so somthing like ths. A.move_by(-self.size.w, 0.1, TIMING_SINODIAL) you also dont need remove. this is meant o remove objects not Actions. sinve you pass none or () its does nothing. one more thing the "self.speed is used to modify Action speed. but its automatically implemented. to if you want a 2x animation speed just set self.speed = 2 and everything else is done for you." you also should useba Node object to group the sprites this way theres no need for the for loop. you just move the parent and child nodes will follow. i changed the folowing method so that it stops the "jerking"bwhen it moves. they now move smoothly ::from update comment:: with that all said, lets be nice to our cpu and instead of runing an Action proccess lets just do some simple math. im not changing thisone but i would write the following: #note: i wouldnt do this for every block i would group the colums (โ_โ) #note: velocity would be set inside startup/__init__ and can be used # with powerups โบ๏ธ for i in self.columns: i.position = Point( i.position[0] - self.velocity*self.dt, i.position[1]) ''' def column_moves(self): actions = [A.move_by(-self.size.w/2, 1, TIMING_SINODIAL)] for i in self.columns: i.run_action(A.sequence(actions)) ''' dont forget you can set some properties when calling the "run()" function scene.run() ## you know this one lol ## โด scene ## alloud orientations. - FROM DOC: Important: The orientation - parameter has no effect on iPads starting with iOS 10 because - it is technically not possible to lock the orientation in an app - that supports split-screen multitasking. ## โต orientation=DEFAULT_ORIENTATION ## frame rate controle FROM DOC: By default, the sceneโs update() method is called 60 times per second. Set the frame_interval parameter to 2 for 30fps, 3 for 20, etc. ## โถ frame_interval=1 ## this game wont need but here you go FROM DOC: Set this to True to enable 4x multisampling. This has a (sometimes significant) performance cost, and is disabled by default. ## โท anti_alias=False ## explains itself. i tent do make my own so i can control the color and position. ## โธ show_fps=False ## you shouldnt need this for this game if kept simple โน multi_touch=True ) ''' run(Game()) # now to write that version two example for u. and again great work!
-
@Karina said:
1. self.point=Point(w() + self.position.w, h() - self.size.h)
If w() and self.size.w are the same here, then we got 2*self.position.w? And why instead of sec arg don't just right 0??2. When I tried to use sleep(), my screen just got black and I couldn't close it. And also was a problem and it all hang and didn't move. I did smth and fixed it, don't know how๐ . But why theese things can happen?
I have hundred of questions on each couple lines)
1.
self.point=Point(w() + self.position.w, h() - self.size.h)
If w() and self.size.w are the same here, then we got 2self.position.w? And why instead of sec arg don't just right 0??*self.position.w should be self.position.x
translatedself.point=Point(w() + self.position.w, h() - self.size.h)
means
Node's Position is a Point Object with x coordinate at screen width plus node's position x and y coordinate at screen height minus nodes hieght.or in other words
x = screen_width + self.position.x
y = screen_hieght - self.size.hieght
self.position = Point(x, y)2.
When I tried to use sleep(), my screen just got black and I couldn't close it. And also was a problem and it all hang and didn't move. I did smth and fixed it, don't know how๐ . But why theese things can happen?
this was my fault lol i forgot you need to att a decorator from ui module
@ui.in_background def add_column(self): time.sleep(5)
this will run sleep on a different thread than your scene
-
never too many questions
-
@Karina now im off to write the game in the way i would. so you can compare. in no way is MY way the best or even the most correct. but it is the way i learned to implement different objects and functionality over the years.
-
@Karina just an update ive been a bit busy but i might have my project ready in the next 20 hours or so. it wont have a lot of comments but ill put a few. but im trying to use only descriptive naming so it should not beva problem.hope to see what you learned from this project ๐ค๐ค๐ค
-
@stephen said:
self.point=Point(w() + self.position.w, h() - self.size.h) <
Ah, I forgot that we're inside Node, not Scene
i understand what lw() does, but what is lower port width? And lw() is the returns the width, if add bw(), we should get beyond the screen?
self.size.w is somthing crazy like 2389488754589433357
I went to check the self.size.w, and it is 1024. What do you it is smth like that๐? And why in that case for is better than while?
-
@karina said:
@stephen said:
self.point=Point(w() + self.position.w, h() - self.size.h)
Ah, I forgot that we're inside Node, not Scenei understand what lw() does, but what is lower port width? And lw() is the returns the width, if add bw(), we should get beyond the screen?
Response:
sw โ screen width bw โ block width lw โ level view port width
top part and lower part , at least in this case, are the same size width. so both top and bottom parts get their size width from our
bw()
Block Width.
Yes we do want to add AT LEAST block width past screen width.
Video Games are Feuled by immersion. You wand the End User to feel that our Level already exists.. so they must not see the objects pop into our level. so we do this just outside the view port. same for removinghe objects. we wait till they are at least Block Width negative positionif block.w < -block.w: del block
.
Also in our position checks we want to compare<
for removing blocks and>
for adding blocks and not<=
and>=
. by doing this we get a 1 point buffer insuring our create and delete actions are not visible bo End User.
@karina said:
@stephen said:
self.size.w is somthing crazy like 2389488754589433357
I went to check the self.size.w, and it is 1024. What do you it is smth like that๐? And why in that case for is better than while?
Response:
โ ๐ค๐ unlikely i know but just for fun... โ
the value
2389488754589433357
this game is simple so this shouldnt happen. you coud see similar if you had bad execution order while using large open world type game environment...since python has a
GIL
Global Interpreter Lock both thefor
and thewhie
loops are "blocking" and both can be used to achive hat you want.. The only prolem i can see making thefor
loop better choice here is that awhile
loop has the possibility of being infinit if somthing doesnt go right where thefor
loop has a predefined end point. insuring the game will not freeze forever do to your code withen.
did i cover what you were confused for these? im not sure i completey understook your questions. ๐
-
I thought of grouping the blocks but don't know how to do it.
It's quite difficult to me to formulate them now cause I don't have anything to write on and can't buy it๐ฟ But:
I don't know what the timing_sinodial, timing_ease in and others mean. I tried to play with them in examples but didn't see the difference, also anti-alias and its 4x multisampling
And the Brushes, so didn't implement it and MaxSizeBrush, but maybe I will when do the jumps -
@Karina ill whip up a quick example that will show the diferences and ill Annotate as much as i can ๐ hang tight
what do you mean you have nothing to write on?
-
@stephen a notebook or smth and I wouldn't have to look what was in the beginning and had important things in front of me