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 the issue lies in line 22.
Since add_column is in the loop, it will get called plenty of times. This results in a lot of columns stacked on top of another, and it is very probable that at least once these columns has the maximum number boxes, and therefore the „smallest“ gap.
Just remove one of the indents in line 22 to fix this.Btw, what @stephen said is certainly worth looking into, like just about any of his suggestions
-
heres is example of using brush like objects with scrolling nd random (not too random) spacing. if you have ny questions please ask!
@Drizzel thank you, i alwys try to help out tht way i get to learn kong the wy aswell 😎🤓🤓
from scene import * import sound import random import math A = Action def w(): return get_screen_size()[0] def h(): return get_screen_size()[1] def MaxBrushSize(): return 10-2 def BaseBlock(parent): return SpriteNode(Texture('plf:Tile_BoxItem_boxed'), size=Size(64, 64), parent=parent, anchor_point=(0.0, 0.0)) def Block(parent): return SpriteNode(Texture('plf:Tile_BoxCrate_double'), size=Size(64, 64), parent=parent, anchor_point=(0.0, 0.0)) def Stone(parent, x): return SpriteNode(Texture('plf:Tile_BrickGrey'), size=Size(64, 64), parent=parent, anchor_point=(0.0, 0.0), position=Point(x*64, 0)) class TopBrush(Node): def __init__(self, brushSize, *args, **kwargs): self.base=BaseBlock(self) self.size=Size(64, 64) self.position=Point(w()+(self.size.w), h()-self.size.h) self.brushSize=brushSize self.blocks=list([self.base, ]) for x in range(1, self.brushSize): b=Block(self) b.position=(self.base.position[0], self.base.position[1] - x*b.size[1]) self.blocks.append(b) class BottomBrush(Node): def __init__(self, brushSize, *args, **kwargs): self.base=BaseBlock(self) self.size=Size(64, 64) self.position=Point(w()+(self.size.w), 0) self.brushSize=brushSize self.blocks=list([self.base, ]) for x in range(1, self.brushSize): b=Block(self) b.position=(self.base.position[0], self.base.position[1] + x*b.size[1]) self.blocks.append(b) class MyScene (Scene): def setup(self): self.background=SpriteNode(Texture('plf:BG_Colored_grass'), size=Size(w(), h()), position=self.size/2, parent=self) self.preset=[(1, 7),(2, 6),(3, 5),(4, 4),(5, 3),(6, 2),(7, 1)] self.i=0.0 self.brushSpawnTimer=3.0 self.brushes=list([]) self.scrollSpeed=40 for x in range(int((w()*128)/64)): self.brushes.append(Stone(self, x)) def Add_Brush(self, choice): if self.i > self.brushSpawnTimer: bb=BottomBrush(choice[0]) tb=TopBrush(choice[1]) self.add_child(bb) self.add_child(tb) self.brushes.append(bb) self.brushes.append(tb) self.i=0.0 self.brushSpawnTimer = random.randrange(3, 6) else: sb=Stone(self, (w()+128)/64) self.brushes.append(sb) def Remove_Brush(self, brush): self.brushes.remove(brush) brush.remove_from_parent() self.Add_Brush(random.choice(self.preset)) def did_change_size(self): pass def Scroll(self, brush): x=brush.position[0]-self.scrollSpeed*self.dt y=brush.position[1] return Point(x, y) def update(self): self.i += self.dt for brush in self.brushes: if brush.position[0] <= -64: self.Remove_Brush(brush) brush.position = self.Scroll(brush) def touch_began(self, touch): pass def touch_moved(self, touch): pass def touch_ended(self, touch): pass if __name__ == '__main__': run(MyScene(), show_fps=False)
-
Quick Note
The example i provided is for landscpe if you run portrait the gapps will not adjust. this can be handled by checking orientation and swapping the preset lists accordingly
-
@stephen I don't understand to much things, I've just read the tutorial and tried to do my own game.
I'll try to get it with time -
@Karina il give a bit of a run down just give me few min to write it ☺️
-
@Drizzel yes I got it. Now it works how it should
-
This is an old and sometimes very poorly written clone I did to learn about ai (flappy bird is an awesome game for basic artificial intelligence experiments). I didn’t bother about graphics, but maybe it’s helpful to you.
Run this to play the game:
from scene import * import sound import random import math import gameExtension as extension #import nnExtension as nn A = Action class MyScene (Scene): def setup(self): self.background_color = 'beige' self.gravity = Vector2(0, -self.size.y/1500) self.birds = [] self.running = True extension.setup_walls(self) extension.update_walls(self) #self.birdCount = 1 #uncomment if using ai for x in range(self.birdCount): extension.add_bird(self) pass def did_change_size(self): pass def update(self): if self.running: self.running = False extension.update_walls(self) for bird in self.birds: if bird.distance >= 10000: bird.dead = True if not bird.dead: self.running = True extension.count_distance(self, bird) extension.update_bird(self, bird) bird.data = extension.get_data(self, bird) if bird.position.y-bird.size.y/2 <= 0 or bird.position.y+bird.size.y/2 >= self.size.y: bird.color = 'red' bird.dead = True for wall in self.walls: if bird.frame.intersects(wall.frame): bird.color = 'red' bird.dead = True else: if bird.position.x + bird.size.x/2 >= 0: bird.position = (bird.position.x - 1, bird.position.y) def touch_began(self, touch): self.birds[0].up = True pass def touch_moved(self, touch): pass def touch_ended(self, touch): pass if __name__ == '__main__': run(MyScene(), PORTRAIT, show_fps=True)
Save this in the same folder as the upper main code as gameExtension.py
from scene import * import random def add_bird(self): self.birds.append(ShapeNode(rect(0,0,10,10))) bird = self.birds[len(self.birds)-1] bird.color = 'black' bird.size = (self.size.x/50, self.size.x/50) bird.position = (self.size.x/4, self.size.y/2) bird.z_position = 2 bird.dead = False bird.distance = 0 bird.up = False bird.max_fall_vel = Vector2(0, self.size.y/100) bird.up_acc = Vector2(0, self.size.y/2) bird.vel = Vector2(0, 0) bird.acc = Vector2(0, 0) bird.data = get_data(self, bird) self.add_child(bird) def setup_walls(self): self.wall_distance = self.size.x/4 self.gap_size = self.size.y/6 self.wall_width = self.size.x/14 self.distance_to_next_wall = self.wall_distance + 1 self.walls = [] def count_distance(self, bird): bird.distance = bird.distance + 1 def update_walls(self): if self.distance_to_next_wall >= self.wall_distance: self.distance_to_next_wall = 0 build_wall(self) else: self.distance_to_next_wall = self.distance_to_next_wall+1 removal = [] for x in range(len(self.walls)): wall = self.walls[x] wall.position = (wall.position.x - 1, wall.position.y) if wall.position.x + wall.size.x/2 < 0: removal.append(x) for x in range(len(removal)): wallIndex = removal[x]-x wall = self.walls[wallIndex] wall.remove_from_parent() self.walls.pop(wallIndex) def build_wall(self): posY = random.randint(round(self.gap_size/2), round(self.size.y - self.gap_size/2)) #random position of gap self.walls.append(ShapeNode(rect(0,0,10,10))) #set up the upper wall wall = self.walls[len(self.walls)-1] wall.size = (self.wall_width, self.size.y - posY - self.gap_size/2) wall.color = 'grey' wall.position = (self.size.x + self.wall_width/2, self.size.y - wall.size.y/2) wall.z_position = 1 self.add_child(wall) self.walls.append(ShapeNode(rect(0,0,10,10))) #set up the lower wall wall = self.walls[len(self.walls)-1] wall.size = (self.wall_width, posY - self.gap_size/2) wall.color = 'grey' wall.position = (self.size.x + self.wall_width/2, wall.size.y/2) wall.z_position = 1 self.add_child(wall) def update_bird(self, bird): if bird.up: bird.up = False bird.acc = bird.up_acc else: bird.acc = self.gravity bird.vel = bird.vel + bird.acc if bird.vel[1] > bird.max_fall_vel[1]: bird.vel = bird.max_fall_vel bird.position = bird.position + bird.vel def get_data(self, bird): data = [] for x in range(len(self.walls)): wall = self.walls[x] if wall.position.x + wall.size.x/2 >= bird.position.x - bird.size.x/2: y_to_gap = (wall.position.x - wall.size.x/2) - (bird.position.x + bird.size.x/2) if y_to_gap < 0: y_to_gap = 0 data.append(y_to_gap) x_to_upper_wall = (wall.position.y - wall.size.y/2) - (bird.position.y + bird.size.y/2) data.append(x_to_upper_wall) wall = self.walls[x+1] x_to_lower_wall = (wall.position.y + wall.size.y/2) - (bird.position.y - bird.size.y/2) data.append(x_to_lower_wall) break distance_to_top = self.size.y - bird.position.y + bird.size.y/2 data.append(distance_to_top) distance_to_bottom = bird.position.y - bird.size.y/2 data.append(distance_to_bottom) velocity = bird.vel[1] data.append(velocity) return data
-
@Karina
i hope this helps..First
we have
A = Action
This is simply convenience.Actions
are thescene
module's Nimation system. You dont have to use this. As shown in the example you can do all manual animation inside theScene.update()
method. called aprox. 60/sec Read more on actions in the Documentation for ActionsSecond
def w(): return get_screen_size()[0] def h(): return get_screen_size()[1] def MaxBrushSize(): return 10-2
scene doent handle
Global
variables very well, so im told, o to avoid thi we have some "Global" variable function. all the do is return the value. vlue here is from
get_screen_size()
⇒Tuple(float, float)
the float values is our scren size in Points. More about Points here
MaxBrushSize
is just in case we needed a 'clamp' for Arithmatic functions.Third
def BaseBlock(parent): return SpriteNode(Texture('plf:Tile_BoxItem_boxed'), size=Size(64, 64), parent=parent, anchor_point=(0.0, 0.0)) def Block(parent): return SpriteNode(Texture('plf:Tile_BoxCrate_double'), size=Size(64, 64), parent=parent, anchor_point=(0.0, 0.0)) def Stone(parent, x): return SpriteNode(Texture('plf:Tile_BrickGrey'), size=Size(64, 64), parent=parent, anchor_point=(0.0, 0.0), position=Point(x*64, 0))
Functions returning Our Sprites. By doing this we clean up our code making it eaier to read and debug. Technically we coud of done this in one function and check the
string
input for "BrickGrey" to add the minor position change. I split them up o help understanding whats going on fr yourself.Next
class TopBrush(Node): def __init__(self, brushSize, *args, **kwargs): self.base=BaseBlock(self) self.size=Size(64, 64) self.position=Point(w()+(self.size.w), h()-self.size.h) self.brushSize=brushSize self.blocks=list([self.base, ]) for x in range(1, self.brushSize): b=Block(self) b.position=(self.base.position[0], self.base.position[1] - x*b.size[1]) self.blocks.append(b) class BottomBrush(Node): def __init__(self, brushSize, *args, **kwargs): self.base=BaseBlock(self) self.size=Size(64, 64) self.position=Point(w()+(self.size.w), 0) self.brushSize=brushSize self.blocks=list([self.base, ]) for x in range(1, self.brushSize): b=Block(self) b.position=(self.base.position[0], self.base.position[1] + x*b.size[1]) self.blocks.append(b)
Pretyslf explnitoy here. simply grouping our sprits into two secions and setting thier position for pillar like setup.
TopBrush
andBottomBrush
. Nothing els speceal here.Lastly
Now that we have everything prepared we can play with it ☺️.
class MyScene (Scene): def setup(self): self.background=SpriteNode(Texture('plf:BG_Colored_grass'), size=Size(w(), h()), position=self.size/2, parent=self) self.preset=[(1, 7),(2, 6),(3, 5),(4, 4),(5, 3),(6, 2),(7, 1)] self.i=0.0 self.brushSpawnTimer=3.0 self.brushes=list([]) self.scrollSpeed=40 for x in range(int((w()*128)/64)): self.brushes.append(Stone(self, x))
setup()
is called after the Scene object is returned bu before Event Loop Starts so here you can use Scene Variables such assize
orbounds
to setup your Game screen Invironment.
*Note: you can overide the__init__
method but you must call toScene.__init__(self, *args, **kwargs)
otherwise your loop will become unstable and crash. generally no reason to call it.setup()
usualy is all u need .def Add_Brush(self, choice): if self.i > self.brushSpawnTimer: bb=BottomBrush(choice[0]) tb=TopBrush(choice[1]) self.add_child(bb) self.add_child(tb) self.brushes.append(bb) self.brushes.append(tb) self.i=0.0 self.brushSpawnTimer = random.randrange(3, 6) else: sb=Stone(self, (w()+128)/64) self.brushes.append(sb) def Remove_Brush(self, brush): self.brushes.remove(brush) brush.remove_from_parent() self.Add_Brush(random.choice(self.preset))
Methods
Add_Brush
andRemove_Brush
are very important. there would be no level withoutAdd
lol and WithoutRemove
we could build up memory usage and thats never good. always clean up after yourself 😬🙃def Scroll(self, brush): x=brush.position[0]-self.scrollSpeed*self.dt y=brush.position[1] return Point(x, y) def update(self): self.i += self.dt for brush in self.brushes: if brush.position[0] <= -64: self.Remove_Brush(brush) brush.position = self.Scroll(brush)
Finaly our
Scroll
andupdate
methods.. these two bring it all to life.. Only thing to realy spotlight here for you isself.dt
this is theTime Delta
, time between calls. in this case its the time between Frames and usually0.00165...
. We are using it for two uses. one is a timer for spawning our brushes and other is to smooth out the movment of our objects to make it visually appealing..
I hope thi helps you understand hts going on. Scene is a powerful tool and not just for game design but you have a long frustrating jurny ahead of you.. but its worth it ☺️🤓🤓🤓 i would suggest you look into using
scene
andui
modules togeather.you only need to callfom scene import *
andui
is already imported in scene. ready or you. if ou do this you get a much more devloper freindly User interface along with your game loop. all you need to do is connect yourScene
class to ascene.SceneView
and add it to yourCustomView
withself.add_subview()
.Ill be glad to help with anything else you need.
-
@Drizzel well done 🙃🤓😎
-
@stephen thanks for the kind words, but some things are clumsy at best. Your summary is awesome, though. Although I am really glad that I can help here :) That way I can return at least a small amount of the support this community here has given me
-
@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 example -
If you need inspiration, https://github.com/Pythonista-Tools/Pythonista-Tools/blob/master/Games.md features a Flappy Bird style game called
Jumpy Octopus
. -
@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) -
@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😊