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.


    Problem with ability to slide tiles.

    Pythonista
    movement game tile touch
    3
    4
    3291
    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.
    • ETPH
      ETPH last edited by

      Hi everyone!

      This is my first post here, and the interface is kind of confusing, so I’m sorry if this is formatted strangely. I’ve been trying to teach myself how to code through this game. It’s a tile sliding game, trying to order the tiles into a correct position. Currently, I’m just trying to get movement to work, and the sprites and abilit to start/end/win a game haven’t been implemented. I’ve gotten most of it going, but I can’t seem to get the tiles to move around correctly.

      I want one of three things to happen: if the tile tries to move past the boundaries, it can’t move; if the tile tries to move onto another tile, it can’t move; if the tile tries to move to a free space, it moves. I’ve been trying to do this using a for loop (as the names of the tiles are stored under the same variable name, so I have to access them in a way other than by name). However, I can’t get the code to do those three things consistently. I’m pretty sure the problem is with the for loop and if statement combination I have, but I can’t think of another method for doing this.
      Sorry that some of the comments haven’t been updated as I’ve been changing the code, but most of it is still correct.

      I’m using an iPad Pro, version 11.2.2, and Pythonista version 3.2.

      Any help would be appreciated. Code starts after this line.

      '''Description: A 4x4 board of numbered tiles has one missing space and is randomly set up. To win the game, the player must slide tiles over to put the tiles back in order.

      Variants: Instead of numbers, you can have a scrambled picture cut up into 4x4 tiles.'''

      from scene import *
      import random
      import ui
      import marshal
      import string
      import sound
      import math
      import time
      import Image, ImageDraw

      class Game(Scene):

      MARGIN_OF_ERROR = 33
      
      #Creates background and initial variables that the rest of the game builds off of.
      def setup(self):
      	self.background_color = '#646464' 
      	self.create_tile()
      	self.drag = False
      	self.Tile = None
      	self.touch_location = None
      	self.touched_tile = None
      	
      	#Creates tiles and adds their graphics by randomly selecting a position (was supposed to have numbers but couldn't get that working), adds that position to actual_position list and removes it from position_options lists so no tile is put in the same spot. It then adds all those tiles the game generated into a list that gets accessed later in the game.
      def create_tile(self):
      	position_options = [(522, 510), (522, 444), (522, 378), (522, 312), (588, 510), (588, 444), (588, 378),
      											(588, 312), (654, 510), (654, 444), (654, 378), (654, 312), (456, 510), (456, 444),
      											(456, 378)] #possible positions
      	actual_position = []
      	self.tiles = []
      	t_shape = 'plf:Tile_BoxCrate' #sprite of tile
      	for j in range(15):
      		position = random.choice(position_options) #chooses position
      		actual_position.append(position) #adds position to other list
      		position_options.remove(position) #removes position so it can't be rechosen
      		self.Tile = SpriteNode(t_shape, position) #creates tile
      		self.tiles.append(self.Tile) #adds that tile to self.tiles list
      		self.add_child(self.Tile) #adds tile to gameboard
      
      #this function determines where player has touched. It goes through every position option and compares the x and y value of where player touched to intial position option with a margin of error of twenty. It then adds that position to a self.drag variable to be used elsewhere in the class.
      def touch_began(self, touch):
      	position_options = [(522, 510), (522, 444), (522, 378), (522, 312), (588, 510), (588, 444), (588, 378),
      											(588, 312), (654, 510), (654, 444), (654, 378), (654, 312), (456, 510), (456, 444),
      											(456, 378), (456, 312)]
      	for j in position_options:
      		if touch.location[0] <= j[0] + self.MARGIN_OF_ERROR and touch.location[0] >= j[0] - self.MARGIN_OF_ERROR and touch.location[1] <= j[1] + self.MARGIN_OF_ERROR and touch.location[1] >= j[1] - self.MARGIN_OF_ERROR:
      			#checks if touch.location is within the margin of error to a position option
      			self.drag = j #sets empty variable self.drag to that position
      						
      #this is the where the function checks if the touch has moved. It first loops through every position of the tiles previously generated and compares that to the self.drag position set earlier. It then sets the self.touched_tile variable to that tile SpriteNode created earlier, which will then allow movement. After that it checks where the player's touch ends and compares that to the inital self.drag position. Then based on that touch movement, it moved the SpriteNode to the next position over in that direction.
      def touch_moved(self, touch):
      	for i in self.tiles: 
      		if i.position == self.drag:
      			self.touched_tile = i #checks to see if there is a tile at that position and sets it to the touched_tile variable
      	if touch.location[0] - self.drag[0] >= 50 and touch.location[1] <= touch.location[1] + self.MARGIN_OF_ERROR and touch.location[1] >= touch.location[1] - self.MARGIN_OF_ERROR: #checks to see if touch movement moves right
      		move_right = Action.move_to(self.drag[0] + 66, self.drag[1], 1, TIMING_SINODIAL) #sets how tile moves
      		for j in self.tiles:
      			if self.drag[0] + 66 > 680:
      				self.drag == False
      				self.touched_tile == None
      				print("Didn't move off gameboard")
      				return self.drag and self.touched_tile
      			elif self.drag[0] + 66 != j.position[0] and self.drag[1] == j.position[1]:
      				self.touched_tile.run_action(move_right)
      				self.drag == False
      				print("I moved right")
      				return self.touched_tile and self.drag
      			else:
      				self.drag == False
      				self.touched_tile == None
      				print("Failed")
      				return self.drag and self.touched_tile
      	elif touch.location[0] - self.drag[0] <= -50 and touch.location[1] <= touch.location[1] + self.MARGIN_OF_ERROR and touch.location[1] >= touch.location[1] - self.MARGIN_OF_ERROR: #checks to see if touch movement moves left
      		move_left = Action.move_to(self.drag[0] - 66, self.drag[1], 1, TIMING_SINODIAL)
      		for j in self.tiles:
      			if self.drag[0] - 66 < 440:
      				self.drag == False
      				self.touched_tile == None
      				print("Didn't move off gameboard")
      				return self.drag and self.touched_tile
      			elif self.drag[0] - 66 != j.position[0] and self.drag[1] == j.position[1]:
      				self.touched_tile.run_action(move_left)
      				self.drag == False
      				print("I moved left")
      				return self.touched_tile and self.drag
      			else:
      				self.drag == False
      				self.touched_tile == None
      				print("Failed")
      				return self.drag and self.touched_tile
      	elif touch.location[1] - self.drag[1] >= 50 and touch.location[0] <= touch.location[0] + self.MARGIN_OF_ERROR and touch.location[0] >= touch.location[0] - self.MARGIN_OF_ERROR: #checks to see if touch movement moves up
      		move_up = Action.move_to(self.drag[0], self.drag[1] + 66, 1, TIMING_SINODIAL)
      		for j in self.tiles:
      			if self.drag[1] + 66 > 530:
      				self.drag == False
      				self.touched_tile == None
      				print("Didn't move off gameboard")
      				return self.drag and self.touched_tile
      			elif self.drag[1] + 66 != j.position[1] and self.drag[0] == j.position[0]:
      				self.touched_tile.run_action(move_up)
      				self.drag == False
      				print("I moved up")
      				return self.touched_tile and self.drag
      			else:
      				self.drag == False
      				self.touched_tile == None
      				print("Failed")
      				return self.drag and self.touched_tile
      	elif touch.location[1] - self.drag[1] <= -50 and touch.location[0] <= touch.location[0] + self.MARGIN_OF_ERROR and touch.location[0] >= touch.location[0] - self.MARGIN_OF_ERROR: #checks to see if touch movement moves down
      		move_down = Action.move_to(self.drag[0], self.drag[1] - 66, 1, TIMING_SINODIAL)
      		for j in self.tiles:
      			if self.drag[1] - 66 < 300:
      				self.drag == False
      				self.touched_tile
      				print("Didn't move off gameboard")
      				return self.drag and self.touched_tile
      			elif self.drag[1] - 66 != j.position[1] and self.drag[0] == j.position[0]:
      				self.touched_tile.run_action(move_down)
      				self.drag == False
      				print("I moved down")
      				return self.touched_tile and self.drag
      			else:
      				self.drag == False
      				self.touched_tile == None
      				print("Failed")
      				return self.drag and self.touched_tile
      				
      #resets self.drag to intial False value for next touch to reset.				
      def touch_ended(self, touch): 
      	if self.drag:
      		self.drag = False
      		return self.drag
      

      #CHECK FOR LOOPS

      if name == 'main':
      run(Game(), PORTRAIT, show_fps=True)
      )

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

        Sorry that I am not really answering your question, but here’s an alternate design for you to consider:

        Have a ”virtual board” that is defined by (for example) max_x and max_y variables, both 4 in your example, but easily changed in the future if you want to experiment with other sizes in the future.

        Use the scene size and the variables above to calculate where tile position (x,y) really should be on the screen. This provides the additional option of easily experimenting with tile sizes and visual gaps between tiles.

        Have a variable like free_spot tell where there is room.

        Each tile should know which position it is in. You can keep it as an additional variable in the Node or just calculate it from the Node screen position.

        Moving tiles:

        touch_began - Make the tile topmost in the z direction ('bring to front') and store the starting position.

        touch_moved - Let the player move the tile anywhere.

        touch_ended - Check that the tile has been moved some minimum amount. Then which direction (up, right, left, down) the tile has been moved the most. Then check if the tile could actually move in that direction. E.g. if 'up', is position (x,y-1) equal to the free_spot? If yes, animate the tile to that spot and update the free spot. If no, just animate the tile to its original spot.

        This approach will also let the player to just 'swipe' tiles in the direction they want them to move.

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

          ... and if you do not like the ’floaty’ behaviour of the design above, you can do the check in touch_moved instead of touch_ended.

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

            Hi @ETPH. Couple of generic advices:

            • Do not use magic constants in your code (like position_options) in multiple places, it's a nightmare when you want to change them. Move them outside of create_tile, touch_began and reuse them there. Like MARGIN_OF_ERROR.
            • Keep your functions / methods short. It's good when they can fit one screen (well, depends how the screen is big :), but you know what I mean. Not a hard rule, but it's about readability and crunching all these bugs quickly. It's easier when it's short. touch_moved is very long and overly complicated.
            • You shouldn't hardcode positions based on your device in case you want to run it on iPhone SE for example.
            • Split your task into several smaller ones. Replace one huge function with many small ones doing just one thing. Again, readability & easier way how to spot a bug.

            I didn't dive into your touch_moved method, sorry. But I did quickly hack an example of generic board & moving tiles. You can find it here.

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