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.


    Collisions

    Pythonista
    5
    13
    2542
    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.
    • timjhinton
      timjhinton last edited by

      I’m having trouble with getting collisions to work with scene. I have a player sprite that is controlled with up/down/left/right arrows and trying to prevent the player form being able to walk through the walls I have placed. Hopefully one of you can help me spot the problem. I’m thinking (hoping) its just a small error that is causing it. I have tried it several different ways and will try to get them all in here.

      ground = Node(parent=self)
      		x = 0
      		y = 870
      		while x <= self.size.w + 50:
      			xwall = SpriteNode('plc:Dirt_Block', position=(x, 0))
      			xwall2 = SpriteNode('plc:Dirt_Block', position=(x, 850))
      			ground.add_child(xwall)
      			self.wall_list.append(xwall)
      			ground.add_child(xwall2)
      			self.wall_list.append(xwall2)
      			if x >= 150 and x <= 1200:
      				xwall3 = SpriteNode('plc:Dirt_Block', position=(x, 125))
      				ground.add_child(xwall3)
      				self.wall_list.append(xwall3)
      

      There is the basic creation of the wall nodes for the display and then adding each one to the list to iterate through during update when attempting to move. Here is that piece:

      def update(self):
      		for touch in self.touches.values():
      			if touch.location in self.left_button.bbox:
      				new_x = self.player1.position.x - 3
      				if new_x >= 50 and new_x <= 1150:
      					for wall in self.wall_list:
      						if new_x in wall.bbox:
      							pass
      						else:	
      							self.player1.position = (new_x, self.player1.position.y)
      					
      			if touch.location in self.right_button.bbox:
      				new_x = self.player1.position.x + 3
      				if new_x >= 0 and new_x <= 1150:
      					for wall in self.wall_list:
      						if self.player1.bbox.intersects(wall.frame) == True:
      							pass
      						else:	
      							self.player1.position = (new_x, self.player1.position.y)
      					
      			if touch.location in self.up_button.bbox:
      				new_y = self.player1.position.y + 3
      				if new_y >= 0 and new_y <= 800:
      					for wall in self.wall_list:
      						if self.player1.position(self.player1.position.x, new_y) in wall.bbox:
      							pass
      						else:		
      							self.player1.position = (self.player1.position.x, new_y)		
      	
      			if touch.location in self.down_button.bbox:
      				new_y = self.player1.position.y - 3
      				if new_y >= 50 and new_y <= 800:
      					for wall in self.wall_list:
      						if self.player1.bbox.intersects(wall.bbox):
      							pass
      						else:
      							self.player1.position = (self.player1.position.x, new_y)
      			
      

      I have each of the buttons worded a little different on purpose so that you all could see the different ways I have tried to find that collision.Any suggestions on how to prevent my little sprite from running through walls??

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

        @timjhinton said:

        if x >= 150 and x <= 1200:

        `150 <= x <= 1200ˋ also works in Python.

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

          Yes, it would. I’ll probably make that change. Thanks

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

            I haven't tried you code yet, but I have recently been helping my son debug a similar issue on a javascript game he is trying to write.

            What I suggested was to have a debugging mode, where you display a label on each wall block showing it's bbox. Likewise, attach a label to your player node,and display it's bbox. The color of the label for each wall should change depending on whether it interests the player bbox (and maybe a color to indicate whether it interests the top, bottom, left or right), or draw lines showing which edges are touching, etc)

            One thing that you do need to watch out for is that, IIRC, bbox or frame is with respect to the parent node. So if your walls are all children to a ground node, but your player is child of the root scene, their coordinate systems might have an offset that you have to account for. Also, if you are using scale or other tranaforms, that can change what these return...

            Is your character walking through all walls, or does it eventually stop, say after it is halfway or all the way inside the wall? There could be issues with where you have your anchor point set.

            timjhinton 1 Reply Last reply Reply Quote 0
            • JonB
              JonB last edited by

              Actually, I think I see the issue:

              for wall in self.wall_list:
                                      if new_x in wall.bbox:
                                          pass
                                      else:   
                                          self.player1.position = (new_x, self.player1.position.y)
                       
              

              Suppose there are two walls. The player intersects wall 1, but not wall 2. Your code will pass when checking wall1, but the next loop it will not interest wall2, and thus sets the new position.

              Thus, you need to check all walls first, and then if any intersect, disallow the move.

              allow_move=True
              for wall in self.wall_list:
                  if new_x in wall.bbox:
                      allow_move=False
                      break # short circuit once a hit is found
              if allow_move:
                  self.player1.position = (new_x, self.player1.position.y)
              

              There is probably an efficient way to do this using the any function and an iterator, which will do the proper short circuit logic, in a compact form.

              Something like (I haven't tried this, the syntax might not be quite right)

              if not any(( w.bbox.intersects(newbbox) for w in self.walls) ):
                  # move player
              
              timjhinton 2 Replies Last reply Reply Quote 0
              • mikael
                mikael @timjhinton last edited by mikael

                @timjhinton, also, if you have just four walls, would seem simpler to have a rectangle that defines the area inside the walls, and restrict player position to be within that rectangle (by making a ”tentative” change to the coordinates and using Rect.contains_point to check if it can be really applied). This has the additional benefit that the check code can be shared by all of the four buttons.

                (Thanks for a well-presented question!)

                Jokovich timjhinton 2 Replies Last reply Reply Quote 0
                • Jokovich
                  Jokovich @mikael last edited by

                  This post is deleted!
                  1 Reply Last reply Reply Quote 0
                  • timjhinton
                    timjhinton @JonB last edited by ccc

                    @JonB Fantastic idea. I tried to implement your suggestion by putting in another method so i could run it through easy for each of the buttons like so:

                    def player_collision(self, new_xy):
                    		for wall in self.wall_list:
                    			if new_xy in wall.bbox:
                    				return False
                    			else:
                    				return True
                    

                    Then in my update(self) section i put

                    if touch.location in self.left_button.bbox:
                    				new_x = self.player1.position.x - 3
                    				if 50 <= new_x <= 1150:
                    					if self.player_collision(self.player1.position(new_x, self.player1.position.y)) == True:
                    						self.player1.position = (new_x, self.player1.position.y)
                    

                    I thought that this should work but it doesn’t like it. It gives me a TypeError: ‘Point” object is not callable. I’m not exactly sure what that means, but it sounds like I need to use a bbox or frame instead of player1.position??

                    1 Reply Last reply Reply Quote 0
                    • timjhinton
                      timjhinton @mikael last edited by

                      @mikael said:

                      @timjhinton, also, if you have just four walls, would seem simpler to have a rectangle that defines the area inside the walls, and restrict player position to be within that rectangle (by making a ”tentative” change to the coordinates and using Rect.contains_point to check if it can be really applied). This has the additional benefit that the check code can be shared by all of the four buttons.

                      (Thanks for a well-presented question!)

                      Thanks!
                      I did set up the overall dimensions of the rectangle with only allowing the sprite to move in certain x and y limits. I have this sprite going through a small maze, so i have other walls present besides the outside 4 walls. This is where i am having the trouble. I dont want to have to hard limit every wall space on the screen.

                      1 Reply Last reply Reply Quote 0
                      • timjhinton
                        timjhinton @JonB last edited by

                        @JonB said:

                        Actually, I think I see the issue:

                        for wall in self.wall_list:
                                                if new_x in wall.bbox:
                                                    pass
                                                else:   
                                                    self.player1.position = (new_x, self.player1.position.y)
                                 
                        

                        Suppose there are two walls. The player intersects wall 1, but not wall 2. Your code will pass when checking wall1, but the next loop it will not interest wall2, and thus sets the new position.

                        Thus, you need to check all walls first, and then if any intersect, disallow the move.

                        allow_move=True
                        for wall in self.wall_list:
                            if new_x in wall.bbox:
                                allow_move=False
                                break # short circuit once a hit is found
                        if allow_move:
                            self.player1.position = (new_x, self.player1.position.y)
                        

                        There is probably an efficient way to do this using the any function and an iterator, which will do the proper short circuit logic, in a compact form.

                        Something like (I haven't tried this, the syntax might not be quite right)

                        if not any(( w.bbox.intersects(newbbox) for w in self.walls) ):
                            # move player
                        

                        Also tried to copy and paste your first suggestion, the part without using the any function, with no success. The sprite node is still walking through walls..do i have something fundamentally wrong with how I’m doing this?

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

                          Is self.player1.position a Point object? If so then self.player1.position(x, y) is going to raise TypeError: ‘Point” object is not callable

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

                            def player_collision(self, new_xy):
                                    for wall in self.wall_list:
                                        if new_xy in wall.bbox:
                                            return True  # new_xy is in a wall
                                    return False  # new_xy is not in any wall
                                    # or as a one liner...
                                    return any(new_xy in wall.bbox for wall in self.wall_list)
                            
                            1 Reply Last reply Reply Quote 1
                            • timjhinton
                              timjhinton @JonB last edited by

                              @JonB said:

                              I haven't tried you code yet, but I have recently been helping my son debug a similar issue on a javascript game he is trying to write.

                              What I suggested was to have a debugging mode, where you display a label on each wall block showing it's bbox. Likewise, attach a label to your player node,and display it's bbox. The color of the label for each wall should change depending on whether it interests the player bbox (and maybe a color to indicate whether it interests the top, bottom, left or right), or draw lines showing which edges are touching, etc)

                              One thing that you do need to watch out for is that, IIRC, bbox or frame is with respect to the parent node. So if your walls are all children to a ground node, but your player is child of the root scene, their coordinate systems might have an offset that you have to account for. Also, if you are using scale or other tranaforms, that can change what these return...

                              Is your character walking through all walls, or does it eventually stop, say after it is halfway or all the way inside the wall? There could be issues with where you have your anchor point set.

                              I am now seeing this. Maybe I just wasn’t moving the sprite around enough to see it but there are definitely places where my left button wont work for large swathes of the screen and then it will start to work again once I get above a certain point. I have my sprite scaled down to 80%, but that can’t account for the position being off by 1/3 of the screen...how can i display the bbox’s? I’m not sure I know how to even do that...

                              1 Reply Last reply Reply Quote 0
                              • First post
                                Last post
                              Powered by NodeBB Forums | Contributors