omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular

    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

    Pythonista
    4
    81
    25330
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Karina
      Karina last edited by

      Hello everyone! I'm writing the game and at the beginning now

      1 Reply Last reply Reply Quote 1
      • Karina
        Karina last edited by

        from scene import *
        import random
        
        
        class Column(SpriteNode):
        	def __init__(self, **kwargs):
        		SpriteNode.__init__(self, 'plf:Tile_BoxCrate_double', **kwargs)
        		
        		
        class Game(Scene):
        	def setup(self):
        		self.background_color = '#99d7ff'
        		x = 0
        		ground = Node(parent=self)
        		while x < self.size.w:
        			lower_tile = SpriteNode('plf:Ground_Stone', (x, 30))
        			higher_tile = SpriteNode('plf:Ground_Stone', (x, 738))
        
        			x += 60
        			ground.add_child(lower_tile)
        			ground.add_child(higher_tile)
        			self.add_column()
        			
        			
        	def add_column(self):
        		lower = random.randint(0, 360) // 64
        		higher = random.randint(0, 360) // 64
        		y = 45
        		for i in range(lower):
        			column = Column(parent=self)
        			column.anchor_point = (0.5, 0)
        			column.position = (self.size.w, y)
        			y += 64
        		y = 738
        		for i in range(higher):
        			column = Column(parent=self)
        			column.anchor_point = (0.5, column.size.h)
        			column.position = (self.size.w, y)
        			y -= 64
        
        
        run(Game())
        

        This only makes ground and two columns at the end of the screen. The problem is that whenever I run this, the columns always at the same size. But I use random here, so they should get different each time. I can't get why is it so

        stephen Drizzel 2 Replies Last reply Reply Quote 1
        • stephen
          stephen @Karina last edited by

          Hello @Karina

          im going to look into your code and ill br back with somthing for you. i do first want to suggest that instead of Random int you create Brushes kind of like Minecraft did for Trees. you would creat a class to for your brush objects then place them in a list. then use Random Choice instead and this randomly selects from list. gives you control of the objectvdesigns and easier to manage i find than dealing with ranges. that ssid ill be back ☺️😉🤓

          1 Reply Last reply Reply Quote 0
          • Karina
            Karina last edited by

            I am new in programming and this is my first program in scene, so I don't know what are theese things. But thank you anyway

            1 Reply Last reply Reply Quote 0
            • Drizzel
              Drizzel @Karina last edited by Drizzel

              @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

              1 Reply Last reply Reply Quote 2
              • stephen
                stephen last edited by stephen

                @Karina

                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)
                
                
                1 Reply Last reply Reply Quote 2
                • stephen
                  stephen last edited by stephen

                  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

                  1 Reply Last reply Reply Quote 0
                  • Karina
                    Karina last edited by Karina

                    @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

                    stephen 1 Reply Last reply Reply Quote 0
                    • stephen
                      stephen @Karina last edited by

                      @Karina il give a bit of a run down just give me few min to write it ☺️

                      1 Reply Last reply Reply Quote 0
                      • Karina
                        Karina last edited by

                        @Drizzel yes I got it. Now it works how it should

                        stephen 1 Reply Last reply Reply Quote 0
                        • Drizzel
                          Drizzel last edited by

                          @Karina

                          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
                          
                          stephen 1 Reply Last reply Reply Quote 1
                          • stephen
                            stephen @Karina last edited by

                            @Karina
                            i hope this helps..

                            First

                            we have A = Action This is simply convenience. Actions are the scene module's Nimation system. You dont have to use this. As shown in the example you can do all manual animation inside the Scene.update() method. called aprox. 60/sec Read more on actions in the Documentation for Actions

                            Second

                            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 and BottomBrush. 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 as size or bounds to setup your Game screen Invironment.
                            *Note: you can overide the __init__ method but you must call to Scene.__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 and Remove_Brush are very important. there would be no level without Add lol and Without Remove 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 and update methods.. these two bring it all to life.. Only thing to realy spotlight here for you is self.dt
                            this is the Time Delta, time between calls. in this case its the time between Frames and usually 0.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 and ui modules togeather.you only need to call fom scene import * and ui 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 your Scene class to a scene.SceneView and add it to your CustomView with self.add_subview().

                            Ill be glad to help with anything else you need.

                            1 Reply Last reply Reply Quote 1
                            • stephen
                              stephen @Drizzel last edited by

                              @Drizzel well done 🙃🤓😎

                              1 Reply Last reply Reply Quote 0
                              • Drizzel
                                Drizzel last edited by Drizzel

                                @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

                                1 Reply Last reply Reply Quote 1
                                • Karina
                                  Karina last edited by Karina

                                  @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

                                  Drizzel 1 Reply Last reply Reply Quote 0
                                  • ccc
                                    ccc last edited by

                                    If you need inspiration, https://github.com/Pythonista-Tools/Pythonista-Tools/blob/master/Games.md features a Flappy Bird style game called Jumpy Octopus.

                                    1 Reply Last reply Reply Quote 0
                                    • Karina
                                      Karina last edited by Karina

                                      @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)

                                      Drizzel 1 Reply Last reply Reply Quote 0
                                      • Drizzel
                                        Drizzel @Karina last edited by

                                        @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.

                                        1 Reply Last reply Reply Quote 0
                                        • Karina
                                          Karina last edited by

                                          @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

                                          1 Reply Last reply Reply Quote 0
                                          • Drizzel
                                            Drizzel @Karina last edited by

                                            @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 example

                                            The 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
                                            
                                            1 Reply Last reply Reply Quote 0
                                            • First post
                                              Last post
                                            Powered by NodeBB Forums | Contributors