inconsistant results with transition.to

I’m getting a circumstancial bug and I’m not sure if it is being caused by object.xReference
and object.yReference or by transition.to.

I’m making a game that works a bit like a boardgame. The player is on a grid. The player has a home position on the left side of the screen. There are other objects on the board. When the player moves, what is supposed to happen is that the board then moves to put the player back in home position.

A few data points:
-The home position for the player is on the center left side of the screen.
-The player and the board are in one display group so that everything can shift together when I shift the board.
-The starting reference point of the display group is the upper left corner of the screen.
When the player moves, the board tries to move to return the player to the home position relative to
the screen. To do this, I move the reference point of the board to the player and then move the board with “transition.to” to a position relative to display.contentCenter. This works great if the map is not rotated, but when the map is rotated it gives me very strange results.

If the board is rotated 90 degrees and I move the player to the right, the board moves down the same distance. This suggests that it is moving relative to the direction he moved. he moved -Y so it moved +Y. This is really confusing because the reference point I sent to transition.to was derived from display.contentCenter (which I think is relative to the scene and the scene hasn’t moved.)

If you have any advice about this, I would appreciate it.
[import]uid: 191108 topic_id: 33432 reply_id: 333432[/import]

@mark_steelman,
I believe the problem you are having is this:

If you have two objects in a group and you move one of those objects, you change the relative center of the group, and the bounds for the group. The group is kind of like a tight-fitting reactive rectangle. It has the width and height of it’s left-most vs right-most and top-most vs bottom-most objects.

In your case, you are trying to use a relative reference point to move and your moves are probably coming up short. This gets even more confusing with rotations.

So, let me make a suggestion. (This is kind of hacky, but it should help.)

  1. In the same group as your player and board, insert one large transparent rectangle. Make it huge (10,000 pixels wide/high or more).

  2. Now, move your player and board just like before. You should see a difference. As long as you don’t move beyond the edge of the rectangle, both the group will continue to have the same center reference point.

Note: Of the top of my head I don’t recall, but you may need to offset the rectangle’s initial position by width/2 and height/2. I just don’t remember at this time.
Here is a video of a game prototype I’m working on. It moves both the player and the ‘board’ at the same time to keep the player in the center of the screen. It does this by countering moves. I had a similar problem to yours and then realized that my center reference point was changing.

http://www.youtube.com/watch?v=8iRWHDyiCOc

I hope I have not confused this issue for you. Whatever the case, this is not an intractable problem. If you experiment with small moves and known quantities, I think you’ll eventually get a handle on this.

-Ed

[import]uid: 110228 topic_id: 33432 reply_id: 133017[/import]

I just watched the video and holy crap! Can we take a peek at your code on how you accomplished this? This is very awesome. [import]uid: 14218 topic_id: 33432 reply_id: 133158[/import]

@mark_steelman,
I believe the problem you are having is this:

If you have two objects in a group and you move one of those objects, you change the relative center of the group, and the bounds for the group. The group is kind of like a tight-fitting reactive rectangle. It has the width and height of it’s left-most vs right-most and top-most vs bottom-most objects.

In your case, you are trying to use a relative reference point to move and your moves are probably coming up short. This gets even more confusing with rotations.

So, let me make a suggestion. (This is kind of hacky, but it should help.)

  1. In the same group as your player and board, insert one large transparent rectangle. Make it huge (10,000 pixels wide/high or more).

  2. Now, move your player and board just like before. You should see a difference. As long as you don’t move beyond the edge of the rectangle, both the group will continue to have the same center reference point.

Note: Of the top of my head I don’t recall, but you may need to offset the rectangle’s initial position by width/2 and height/2. I just don’t remember at this time.
Here is a video of a game prototype I’m working on. It moves both the player and the ‘board’ at the same time to keep the player in the center of the screen. It does this by countering moves. I had a similar problem to yours and then realized that my center reference point was changing.

http://www.youtube.com/watch?v=8iRWHDyiCOc

I hope I have not confused this issue for you. Whatever the case, this is not an intractable problem. If you experiment with small moves and known quantities, I think you’ll eventually get a handle on this.

-Ed

[import]uid: 110228 topic_id: 33432 reply_id: 133017[/import]

Thank you for your response Emaurina. I am doing something very similar to that except that in my case the board is square. Imagine a chess board with a single character on it. The player and the board are in the same group and the group is rotating. The character movement works because I have a table that translates coordinates based on rotation. The character moves wherever you direct regardless of how the board is rotated but it can’t seem to keep him in the home position when the board is rotated.

I am rotating the board by setting the xReference and yReference to the location of the player and then using transition.to to rotate and move the board to an absolute position on the screen. For the sake of keeping it simple, We’ll say display.contentCenterX and display.contentCenterY (the actual position is that with an int modifier in both directions to get him exactly where I want him).

This works when the map is not rotated, but it doesn’t when the map is rotated. When the map is rotated, the board counter moves relative to the movement of the player character. Eg. If the map is rotated 90 degrees and the player moves 1 space on the y axis of the board (from your perspective it moves to the *right*) then the board will move *down* an equal amount. This would make sense if I were using translate, but I am using transition.to a location on the display.

If I knew how to post code, I would show you some code. Can you guide me on how I might do that?
I see some tags at the bottom of this page but I don’t know what is what. [import]uid: 191108 topic_id: 33432 reply_id: 133234[/import]

You can post code by using an opening and closing code tag. Just insert the less than and greater than tags.

code
Insert Code Here
/code [import]uid: 14218 topic_id: 33432 reply_id: 133256[/import]

Ok, this is as simple as I could get it. You don’t need any assets to run it except a image for the player named player.png. I suggest using your smart phone to take a picture of your head facing to the right and then name it player.png and use that.

tileSize = 40  
  
relDelta = {}  
relDelta.mapRotate0 = function(dX, dY) return dX, dY end  
relDelta.mapRotate90 = function(dX, dY) return dY, -dX end  
relDelta.mapRotate180 = function(dX, dY) return -dX, -dY end  
relDelta.mapRotate270 = function(dX, dY) return -dY, dX end  
  
function centerOnPlayer(playerGroup, n)  
 mapTiles.xReference = playerGroup.x  
 mapTiles.yReference = playerGroup.y  
 local newX, newY = 20, display.contentCenterY\* 141/240 -- home position  
 if n then mapTiles.x = newX; mapTiles.y = newY; return end -- moves player instantly  
 transition.to(mapTiles, {time = 500, x = newX, y=newY })  
end  
  
function correctRotation(object)  
 object.rotation = object.rotation % 360  
 if object.rotation \< 0 then object.rotation = 360 + object.rotation end  
end  
  
function newCoordsFromDelta(startX, startY, moveDeltaX, moveDeltaY)  
 local mapRotation = "mapRotate" .. mapTiles.rotation  
 local relDeltaX, relDeltaY = relDelta[mapRotation](moveDeltaX, moveDeltaY)  
 local x  
 local y  
  
 --checking relative to 0 is so that you get rounding that is relative to the original location  
 if relDeltaX \> 0 then  
 x = (relDeltaX + startX) - (relDeltaX % tileSize)  
 elseif relDeltaX == 0 then  
 x = startX  
 else  
 x = (relDeltaX + startX) + (tileSize-(relDeltaX % tileSize))  
 end  
  
 if relDeltaY \> 0 then  
 y = (relDeltaY + startY) - (relDeltaY % tileSize)  
 elseif relDeltaY == 0 then  
 y = startY  
 else  
 y = (relDeltaY + startY) + (tileSize-(relDeltaY % tileSize))  
 end  
  
 return x, y  
end  
  
function createTurnButton(playerGroup)  
 local turnButton = display.newRect( display.contentCenterX - 25, display.contentCenterY + 175, 50, 50 )  
  
 turnButton.rotation = player.rotation  
 function turnButton:tap(event)  
 local newPlayerAngle = playerGroup.rotation - 90  
 local newMapAngle = mapTiles.rotation + 90  
 transition.to( playerGroup, { time=150, rotation=newPlayerAngle, onComplete = correctRotation })  
 mapTiles.xReference = playerGroup.x  
 mapTiles.yReference = playerGroup.y  
 transition.to( mapTiles, { time= 200, rotation = newMapAngle, onComplete = correctRotation })  
 return true  
 end  
 turnButton:addEventListener("tap")  
 return turnButton  
end  
  
function createPlayer()  
 local newPlayer = display.newImageRect( "player.png", 40, 40 )  
 -- player drag event  
 function newPlayer:touch(event)  
 if event.phase == "began" then  
 --set touch focus  
 display.getCurrentStage():setFocus(self)  
 self.isFocus = true   
 self.markX = self.x -- store x location of object  
 self.markY = self.y -- store y location of object  
 elseif self.isFocus then  
 local moveDeltaX = event.x - event.xStart  
 local moveDeltaY = event.y - event.yStart  
 local x, y = newCoordsFromDelta(self.markX, self.markY, moveDeltaX, moveDeltaY)  
 if event.phase == "moved" then  
 self.x, self.y = x, y  
 elseif event.phase == "ended" or event.phase == "cancelled" then  
 -- reset touch focus  
 display.getCurrentStage():setFocus(nil)  
 self.isFocus = nil  
 centerOnPlayer(player)  
 end  
 end  
 return true  
 end  
 newPlayer:addEventListener( "touch", playerGroup)  
 return newPlayer  
end  
  
function createMap()  
 -- create a display group  
 local floorTiles = display.newGroup()  
 local gradient1 = graphics.newGradient({100, 0, 0}, {100, 100, 100})  
 local gradient2 = graphics.newGradient({0, 0, 100}, {100, 100, 100})  
 local startX, finishX, deltaX = 0, 2560, 40  
 local startY, finishY, deltaY = 0, 2560, 40  
 local color = 0  
 for i=startX, finishX, deltaX do  
 for j=startY, finishY, deltaY do  
 local thisTile = display.newRect( i,j, 40, 40 ) -- initialize to stone  
 if color == 0 then thisTile:setFillColor(gradient1) else thisTile:setFillColor(gradient2) end  
 floorTiles:insert(thisTile)  
 if color == 0 then color = 1 else color = 0 end -- checkerboard so we can see what is going on  
 end  
 end  
 function dragMap(event)  
 local self = event.target  
 local screenOrientation = system.orientation  
  
 if event.phase == "began" then  
 --set touch focus  
 display.getCurrentStage():setFocus(self)  
 self.isFocus = true   
 self.markX = self.x -- store x location of object  
 self.markY = self.y -- store y location of object  
 elseif self.isFocus then  
 local deltaX = event.x - event.xStart  
 local deltaY = event.y - event.yStart  
 local relDeltaX, relDeltaY = relDelta["mapRotate0"](deltaX, deltaY)  
 local x = self.markX + relDeltaX  
 local y = self.markY + relDeltaY  
 if event.phase == "moved" then  
 self.x, self.y = x,y  
 elseif event.phase == "ended" or event.phase == "cancelled" then  
 self.x, self.y = x,y  
 -- reset touch focus  
 display.getCurrentStage():setFocus(nil)  
 self.isFocus = nil  
 centerOnPlayer(player) -- put the player back in the home position  
 end  
 end  
 return true  
 end  
  
 floorTiles:addEventListener( "touch", dragMap)  
 return floorTiles  
end  
  
display.setDefault("background", 0, 0, 0)  
mapTiles = createMap()  
  
player = createPlayer()  
player.x, player.y = 20, 20 + 4 \* tileSize  
mapTiles:insert(player) -- note that the player is in the same group as the map  
  
turnLeft = createTurnButton(player)  
centerOnPlayer(player, true)  

try dragging the player or the map and then let go and the map will go back to the home position.

click the white square at the bottom of the screen to turn. Now drag the board and you will see that it returns as expected. Now drag the player and you will see that it starts getting messed up. If you drag the board after it gets messed up, it just kind of gets worse.

However, if you click turn it snaps him back to the correct position. This makes me suspect a bug because the turn function doesn’t call the function centerOnPlayer. [import]uid: 191108 topic_id: 33432 reply_id: 133322[/import]

I just watched the video and holy crap! Can we take a peek at your code on how you accomplished this? This is very awesome. [import]uid: 14218 topic_id: 33432 reply_id: 133158[/import]

Thank you for your response Emaurina. I am doing something very similar to that except that in my case the board is square. Imagine a chess board with a single character on it. The player and the board are in the same group and the group is rotating. The character movement works because I have a table that translates coordinates based on rotation. The character moves wherever you direct regardless of how the board is rotated but it can’t seem to keep him in the home position when the board is rotated.

I am rotating the board by setting the xReference and yReference to the location of the player and then using transition.to to rotate and move the board to an absolute position on the screen. For the sake of keeping it simple, We’ll say display.contentCenterX and display.contentCenterY (the actual position is that with an int modifier in both directions to get him exactly where I want him).

This works when the map is not rotated, but it doesn’t when the map is rotated. When the map is rotated, the board counter moves relative to the movement of the player character. Eg. If the map is rotated 90 degrees and the player moves 1 space on the y axis of the board (from your perspective it moves to the *right*) then the board will move *down* an equal amount. This would make sense if I were using translate, but I am using transition.to a location on the display.

If I knew how to post code, I would show you some code. Can you guide me on how I might do that?
I see some tags at the bottom of this page but I don’t know what is what. [import]uid: 191108 topic_id: 33432 reply_id: 133234[/import]

You can post code by using an opening and closing code tag. Just insert the less than and greater than tags.

code
Insert Code Here
/code [import]uid: 14218 topic_id: 33432 reply_id: 133256[/import]

Ok, this is as simple as I could get it. You don’t need any assets to run it except a image for the player named player.png. I suggest using your smart phone to take a picture of your head facing to the right and then name it player.png and use that.

tileSize = 40  
  
relDelta = {}  
relDelta.mapRotate0 = function(dX, dY) return dX, dY end  
relDelta.mapRotate90 = function(dX, dY) return dY, -dX end  
relDelta.mapRotate180 = function(dX, dY) return -dX, -dY end  
relDelta.mapRotate270 = function(dX, dY) return -dY, dX end  
  
function centerOnPlayer(playerGroup, n)  
 mapTiles.xReference = playerGroup.x  
 mapTiles.yReference = playerGroup.y  
 local newX, newY = 20, display.contentCenterY\* 141/240 -- home position  
 if n then mapTiles.x = newX; mapTiles.y = newY; return end -- moves player instantly  
 transition.to(mapTiles, {time = 500, x = newX, y=newY })  
end  
  
function correctRotation(object)  
 object.rotation = object.rotation % 360  
 if object.rotation \< 0 then object.rotation = 360 + object.rotation end  
end  
  
function newCoordsFromDelta(startX, startY, moveDeltaX, moveDeltaY)  
 local mapRotation = "mapRotate" .. mapTiles.rotation  
 local relDeltaX, relDeltaY = relDelta[mapRotation](moveDeltaX, moveDeltaY)  
 local x  
 local y  
  
 --checking relative to 0 is so that you get rounding that is relative to the original location  
 if relDeltaX \> 0 then  
 x = (relDeltaX + startX) - (relDeltaX % tileSize)  
 elseif relDeltaX == 0 then  
 x = startX  
 else  
 x = (relDeltaX + startX) + (tileSize-(relDeltaX % tileSize))  
 end  
  
 if relDeltaY \> 0 then  
 y = (relDeltaY + startY) - (relDeltaY % tileSize)  
 elseif relDeltaY == 0 then  
 y = startY  
 else  
 y = (relDeltaY + startY) + (tileSize-(relDeltaY % tileSize))  
 end  
  
 return x, y  
end  
  
function createTurnButton(playerGroup)  
 local turnButton = display.newRect( display.contentCenterX - 25, display.contentCenterY + 175, 50, 50 )  
  
 turnButton.rotation = player.rotation  
 function turnButton:tap(event)  
 local newPlayerAngle = playerGroup.rotation - 90  
 local newMapAngle = mapTiles.rotation + 90  
 transition.to( playerGroup, { time=150, rotation=newPlayerAngle, onComplete = correctRotation })  
 mapTiles.xReference = playerGroup.x  
 mapTiles.yReference = playerGroup.y  
 transition.to( mapTiles, { time= 200, rotation = newMapAngle, onComplete = correctRotation })  
 return true  
 end  
 turnButton:addEventListener("tap")  
 return turnButton  
end  
  
function createPlayer()  
 local newPlayer = display.newImageRect( "player.png", 40, 40 )  
 -- player drag event  
 function newPlayer:touch(event)  
 if event.phase == "began" then  
 --set touch focus  
 display.getCurrentStage():setFocus(self)  
 self.isFocus = true   
 self.markX = self.x -- store x location of object  
 self.markY = self.y -- store y location of object  
 elseif self.isFocus then  
 local moveDeltaX = event.x - event.xStart  
 local moveDeltaY = event.y - event.yStart  
 local x, y = newCoordsFromDelta(self.markX, self.markY, moveDeltaX, moveDeltaY)  
 if event.phase == "moved" then  
 self.x, self.y = x, y  
 elseif event.phase == "ended" or event.phase == "cancelled" then  
 -- reset touch focus  
 display.getCurrentStage():setFocus(nil)  
 self.isFocus = nil  
 centerOnPlayer(player)  
 end  
 end  
 return true  
 end  
 newPlayer:addEventListener( "touch", playerGroup)  
 return newPlayer  
end  
  
function createMap()  
 -- create a display group  
 local floorTiles = display.newGroup()  
 local gradient1 = graphics.newGradient({100, 0, 0}, {100, 100, 100})  
 local gradient2 = graphics.newGradient({0, 0, 100}, {100, 100, 100})  
 local startX, finishX, deltaX = 0, 2560, 40  
 local startY, finishY, deltaY = 0, 2560, 40  
 local color = 0  
 for i=startX, finishX, deltaX do  
 for j=startY, finishY, deltaY do  
 local thisTile = display.newRect( i,j, 40, 40 ) -- initialize to stone  
 if color == 0 then thisTile:setFillColor(gradient1) else thisTile:setFillColor(gradient2) end  
 floorTiles:insert(thisTile)  
 if color == 0 then color = 1 else color = 0 end -- checkerboard so we can see what is going on  
 end  
 end  
 function dragMap(event)  
 local self = event.target  
 local screenOrientation = system.orientation  
  
 if event.phase == "began" then  
 --set touch focus  
 display.getCurrentStage():setFocus(self)  
 self.isFocus = true   
 self.markX = self.x -- store x location of object  
 self.markY = self.y -- store y location of object  
 elseif self.isFocus then  
 local deltaX = event.x - event.xStart  
 local deltaY = event.y - event.yStart  
 local relDeltaX, relDeltaY = relDelta["mapRotate0"](deltaX, deltaY)  
 local x = self.markX + relDeltaX  
 local y = self.markY + relDeltaY  
 if event.phase == "moved" then  
 self.x, self.y = x,y  
 elseif event.phase == "ended" or event.phase == "cancelled" then  
 self.x, self.y = x,y  
 -- reset touch focus  
 display.getCurrentStage():setFocus(nil)  
 self.isFocus = nil  
 centerOnPlayer(player) -- put the player back in the home position  
 end  
 end  
 return true  
 end  
  
 floorTiles:addEventListener( "touch", dragMap)  
 return floorTiles  
end  
  
display.setDefault("background", 0, 0, 0)  
mapTiles = createMap()  
  
player = createPlayer()  
player.x, player.y = 20, 20 + 4 \* tileSize  
mapTiles:insert(player) -- note that the player is in the same group as the map  
  
turnLeft = createTurnButton(player)  
centerOnPlayer(player, true)  

try dragging the player or the map and then let go and the map will go back to the home position.

click the white square at the bottom of the screen to turn. Now drag the board and you will see that it returns as expected. Now drag the player and you will see that it starts getting messed up. If you drag the board after it gets messed up, it just kind of gets worse.

However, if you click turn it snaps him back to the correct position. This makes me suspect a bug because the turn function doesn’t call the function centerOnPlayer. [import]uid: 191108 topic_id: 33432 reply_id: 133322[/import]