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
    25222
    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 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
                              • Karina
                                Karina last edited by Karina

                                @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()
                                
                                1 Reply Last reply Reply Quote 0
                                • Drizzel
                                  Drizzel last edited by

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

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

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

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

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

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

                                          @Karina

                                          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 current Node's width. Yes when used inside your Subclass of scene.Scene this will be the same value. but this is only becasue you are initiating its View by using the module level run() function. (run always launches in fullscreen). but in the future you may notwant fullscreen and this means you would use a custom ui.View class then add a SceneView to it. now if you use the w() function you will get screen width as expected but now your self.size.w will return a different value referencing the SceneView's frame (frame ⇨ Tuple(x, y, width, height)) you can also retrieve this uing MySceneView.frame[2]. in our case i didnt needto use the w() 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 bfor n 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 Reply Last reply Reply Quote 0
                                          • Karina
                                            Karina last edited by Karina

                                            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)

                                            stephen 2 Replies Last reply Reply Quote 0
                                            • First post
                                              Last post
                                            Powered by NodeBB Forums | Contributors