Sprite animation in slopes

@thomas6 Could you please explain to me how you will implement character movement in a slope in corona? I am curious to know. Imagine I have a line drawn with many slopes.

Google for “tile based slope physics” or similar:

http://danjb.com/blog/tilebased_platformer_slopes

Mind you, we’re talking about tile based game engines here, which is quite a departure from Box2D physics. I love it, and it’s the “real” Super Mario / Sonic way of doing things. But it does take some time to switch your head around. Thankfully there are plenty of tutorials on the web.

Here’s everything you need. Unfortunately it’s in Flash, so you’ll need to convert to Lua, but the foundation is the same.

I just need a sample code in corona…has no one did this before? me so confused!

If you plan on using built in physics: did’t this tip work for you?

“You can stop a body toppling over by setting isFixedRotation = true.”

If you want to use self created physics, you need to learn. I could give you my sample code, but you would only have a hundred more questions so that would not help you out much.

By the way, I don’t mean to be snarky: if you want to learn tile-based collision detection I don’t mind giving you my code, so let me know if you’d like that. But I don’t have time to explain or answer questions so you’re on your own - and in that case I’m 100% sure that learning on your own will be faster and better. It will take you a couple of weeks at least though.

Finding a solution that works with Box2D is definitely possible. Many people have done it before you. I don’t use it so I can’t help you our with that, I’m sorry.

May I please examine your code? I would like to learn, want to learn the non physics way. I will keep the questions to myself.

Okay - you asked for it :smiley:

self.frameLoop = function(event) -- compute potential horizontal motion for next frame based on key input if self.dead == false then if self.leftKeyPressed == true then self.xVel = self.xVel - self.xAccel elseif self.rightKeyPressed == true then self.xVel = self.xVel + self.xAccel elseif touching == false then self.xVel = self.xVel \* self.friction end else self.xVel = self.xVel \* self.deadFriction end -- limit speed if self.xVel \> self.xMaxVel then self.xVel = self.xMaxVel elseif self.xVel \< -self.xMaxVel then self.xVel = -self.xMaxVel end if self.platform == false then self.xPosNext = self.xPos + self.xVel else self.xPosNext = self.xPos + self.xVel + self.super.objectsManager.platformList[self.platform].xOffset end -- compute potential vertical motion for next frame if self.jumpState == "falling" then self.yVel = self.yVel + self.yAccel jumpTouch = false elseif self.jumpState == "grounded" then if jumpTouch == true then jumpTouch = false self.jump() end end if self.yVel \> self.yMaxVel then self.yVel = self.yMaxVel elseif self.yVel \< -self.yMaxVel then self.yVel = -self.yMaxVel end -- set yPosNext if self.platform == false then -- move self with velocity self.yPosNext = self.yPos + self.yVel else -- move along with platform self.yPosNext = self.super.objectsManager.platformList[self.platform].mainGroup.y end -- first correct the vertical movement, based on current X and next Y position !!! local bottomRow = math.floor((self.yPosNext)/self.super.tileWorld.tileHeight)+1 -- row number is like visual --\> first row you see = row 1 local topRow = math.floor((self.yPosNext-self.collisionHeight)/self.super.tileWorld.tileHeight)+1 -- local leftColumn = math.floor((self.xPos-self.collisionWidth/2)/self.super.tileWorld.tileWidth)+1 -- column number is like visual --\> first column you see = column 1 local rightColumn = math.floor((self.xPos+self.collisionWidth/2)/self.super.tileWorld.tileWidth)+1 -- first column = column 0 so add 1 for Arraypos's if (self.jumpState == "falling") or (self.platform ~= false) then if self.yVel \> 0 then -- moving down -- check if bottom points are in a platform if self.platform == false then for i = 1, #self.super.objectsManager.platformList do local vDistance = self.yPosNext - self.super.objectsManager.platformList[i].mainGroup.y local hDistance = math.abs(self.xPosNext - self.super.objectsManager.platformList[i].mainGroup.x) if (vDistance \> 0) and (vDistance \< 64) then -- 64 for platform height if self.super.objectsManager.platformList[i].platformWidth == 3 then if (hDistance \< 236) then self.jumpState = "grounded" self.platform = i self.yPosNext = self.super.objectsManager.platformList[i].mainGroup.y if self.super.objectsManager.platformList[i].type == "dropform" then self.super.objectsManager.platformList[i].startDropformTimer() end end elseif self.super.objectsManager.platformList[i].platformWidth == 2 then if (hDistance \< 156) then self.jumpState = "grounded" self.platform = i self.yPosNext = self.super.objectsManager.platformList[i].mainGroup.y if self.super.objectsManager.platformList[i].type == "dropform" then self.super.objectsManager.platformList[i].startDropformTimer() end end end end -- if end -- for platformList end -- if self.platform == false branch -- repeat check for bottom points in a dropform!!! local botLeftTile = self.super.levelMap.tileData[bottomRow][leftColumn] local botRightTile = self.super.levelMap.tileData[bottomRow][rightColumn] if (botLeftTile ~= 1) or (botRightTile ~= 1) then -- one or more bottom corners will be on a NOT AIR tile, so there is a collision, -- and the hero is stopped and placed on top of the tile audio.play(landSound) self.yVel = 0 self.yPos = (bottomRow-1)\*94 self.jumpState = "grounded" print("hero grounded") self.platform = false else -- both bottom corners are on an air tile, so the hero is not stopped by a tile, -- BUT!!! if might be stopped by a platform!!! Let's check! for i = 1, #self.super.objectsManager.platformList do local vDistance = self.yPosNext - self.super.objectsManager.platformList[i].mainGroup.y local hDistance = math.abs(self.xPosNext - self.super.objectsManager.platformList[i].mainGroup.x) if (vDistance \> 0) and (vDistance \< 64) then -- 64 for platform height if self.super.objectsManager.platformList[i].type == "platformSlow3" then print("platform type = platformSlow3") if (hDistance \< 236) then print("platform in hDistance") self.jumpState = "grounded" self.platform = i self.yPosNext = self.super.objectsManager.platformList[i].mainGroup.y if self.super.objectsManager.platformList[i].dropform == true then self.super.objectsManager.platformList[i]:startTimer() end end elseif self.super.objectsManager.platformList[i].type == 2 then if (hDistance \< 156) then self.jumpState = "grounded" self.platform = i self.yPosNext = self.super.objectsManager.platformList[i].mainGroup.y if self.super.objectsManager.platformList[i].dropform == true then self.super.objectsManager.platformList[i]:startTimer() end end end end -- if end -- for platformList self.yPos = self.yPosNext end elseif self.yVel \< 0 then -- moving up local topLeftTile = self.super.levelMap.tileData[topRow][leftColumn] local topRightTile = self.super.levelMap.tileData[topRow][rightColumn] if (topLeftTile == 2) or (topRightTile == 2) then -- one or more top corners will be on a solid tile self.yVel = 0 -- WRONG!!! self.yPos = (math.floor(self.yPosNext/94)\*94)+64 else self.yPos = self.yPosNext end elseif self.yVel == 0 then -- no vertical movement self.yPos = self.yPosNext end -- if (self.jumpState == "falling" or platform is true) else -- if self == "grounded" end -- then correct the horizontal movement, based on corrected Y and next X position !!! bottomRow = math.floor((self.yPos-1)/self.super.tileWorld.tileHeight)+1 -- first row = row 0 which is good for row multiplier 0\*mapwidth for Arraypos's local midRow = math.floor((self.yPos-self.super.tileWorld.tileWidth)/self.super.tileWorld.tileHeight)+1 -- first row = row 0 which is good for row multiplier 0\*mapwidth for Arraypos's topRow = math.floor((self.yPos-self.collisionHeight)/self.super.tileWorld.tileHeight)+1 -- first row = row 0 which is good for row multiplier 0\*mapwidth for Arraypos's leftColumn = math.floor((self.xPosNext-self.collisionWidth/2)/self.super.tileWorld.tileWidth)+1 -- first column = column 0 so add 1 for Arraypos's rightColumn = math.floor((self.xPosNext+self.collisionWidth/2)/self.super.tileWorld.tileWidth)+1 -- first column = column 0 so add 1 for Arraypos's if self.xVel \> 0 then -- moving right local topRightTile = self.super.levelMap.tileData[topRow][rightColumn] local midRightTile = self.super.levelMap.tileData[midRow][rightColumn] local botRightTile = self.super.levelMap.tileData[bottomRow][rightColumn] if (topRightTile == 2) or (midRightTile == 2) or (botRightTile == 2) then -- one or more right corners will be on a solid tile -- check wether on platform and only bottom point colliding if (self.platform ~= false) and (topRightTile ~= 2) and (midRightTile ~= 2) then -- hitting only bottom point to get off platform! self.xPos = self.xPosNext else self.xVel = 0 self.xPos = (math.floor(self.xPosNext/126)\*126)+63 end else self.xPos = self.xPosNext end elseif self.xVel \< 0 then -- moving left local topLeftTile = self.super.levelMap.tileData[topRow][leftColumn] local midLeftTile = self.super.levelMap.tileData[midRow][leftColumn] local botLeftTile = self.super.levelMap.tileData[bottomRow][leftColumn] if (topLeftTile == 2) or (midLeftTile == 2) or (botLeftTile == 2) then -- one or more top corners will be on a solid tile -- check wether on platform and only bottom point colliding if (self.platform ~= false) and (topLeftTile ~= 2) and (midLeftTile ~= 2) then -- hitting only bottom point to get off platform! self.xPos = self.xPosNext else self.xVel = 0 self.xPos = (math.floor(self.xPosNext/126)\*126)+64 end else self.xPos = self.xPosNext end elseif self.xVel == 0 then -- no horizontal movement self.xPos = self.xPosNext end -- gate check here !!! for i = 1, #self.super.objectsManager.gateList do local gate = self.super.objectsManager.gateList[i] if gate.gateState == "closed" then -- start checking stuff -- if gate is OPEN we do nothing at all! :-) -- CHECK wether self is IN gate rect if math.abs(gate.mainGroup.x - self.xPos) \< 128 then -- in horizontal area so check V overlap if (gate.mainGroup.y - self.yPos) \< 192 and (gate.mainGroup.y - self.yPos) \> - 444 then -- Y also within gate if self.holdingKey == false then -- self is blocked because gate is closed and self is not holding a key -- BLOCK self!!! if self.xVel \< 0 then -- moving left self.xVel = 0 self.xPos = gate.mainGroup.x + 128 elseif self.xVel \> 0 then -- moving right self.xVel = 0 self.xPos = gate.mainGroup.x - 128 end else -- self is HOLDING A KEY! -- So check if it is the RIGHT KEY index for this gate if self.holdingKey.index == gate.index then -- self opens door and moves through print("opened gate!") gate.open() self.holdingKey.mainGroup.y = -1000 self.holdingKey.keyState = "used" self.holdingKey = false else -- self is holding a key with the wrong index -- BLOCK self!!! if self.xVel \< 0 then -- moving left self.xVel = 0 self.xPos = gate.mainGroup.x + 128 elseif self.xVel \> 0 then -- moving right self.xVel = 0 self.xPos = gate.mainGroup.x - 128 end end -- check if holding the right key end -- self holding key or not check ends end -- if self is in vertical rect check ends end -- if self is in horizontal rect check ends end -- if gate.gateState == CLOSED or OPEN check ends end -- for loop over all gates ENDS -- finally, check to see if self is still standing on solid tiles or on finish -- if not on solid or cloud, set jumpState to "falling" -- if on finish tile, level is completed! bottomRow = math.floor(((self.yPos)/self.super.tileWorld.tileHeight)+1) -- first row = row 0 which is good for row multiplier 0\*mapwidth for Arraypos's leftColumn = math.floor((self.xPos-self.collisionWidth/2)/self.super.tileWorld.tileWidth)+1 -- first column = column 0 so add 1 for Arraypos's rightColumn = math.floor((self.xPos+self.collisionWidth/2)/self.super.tileWorld.tileWidth)+1 -- first column = column 0 so add 1 for Arraypos's local botLeftTile = self.super.levelMap.tileData[bottomRow][leftColumn] local botRightTile = self.super.levelMap.tileData[bottomRow][rightColumn] if self.platform ~= false then -- self is standing on a platform local hDistance = math.abs(self.xPosNext - self.super.objectsManager.platformList[self.platform].mainGroup.x) if self.super.objectsManager.platformList[self.platform].platformWidth == 3 then if hDistance \> 236 then self.platform = false self.jumpState = "falling" self.yVel = self.littleJumpSpeed end elseif self.super.objectsManager.platformList[self.platform].platformWidth == 2 then if hDistance \> 156 then self.platform = false self.jumpState = "falling" self.yVel = self.littleJumpSpeed end end else -- self is not on a platform if (botLeftTile == 1) and (botRightTile == 1) and (self.jumpState == "grounded") then -- both bottom corners will be on an air tile self.yVel = -10 self.jumpState = "falling" print("both corners on air") elseif (botLeftTile == 8) and (botRightTile == 8) and (self.jumpState == "grounded") then if self.finished == false then --print("level finished! Victory!") self.super.super.audioManager.playVictorySound() -- write unlocked and score into levelTable data and SAVE! -- set current level unlocked from PLAYABLE to PLAYED --levelTable.data[levelMap.number][3]=2 -- set next level unlocked from LOCKED to PLAYABLE --levelTable.data[levelMap.number+1][3]=1 -- set scores (temp to Gold Silver Bronze!) --levelTable.data[levelMap.number][7]=3 --levelTable.data[levelMap.number][8]=2 --levelTable.data[levelMap.number][9]=1 -- then SAVE the levelTable --levelTable.save() self.finished = true Runtime:removeEventListener("enterFrame", self.frameLoop) if (system.getInfo("environment") == "device") then -- disable touch controls for the hero character -- but this is done in the levelViewClass code! else -- disable key controls for the hero character Runtime:removeEventListener("key", self.keyListener) end self.super.gameInterface.popLevelEndScreen() end elseif (botLeftTile == 4) or (botRightTile == 4) and (self.jumpState == "grounded") then -- self is on death tile with one or two corners if self.dead == false then self.die() end else -- self is still standing on solid, cloud, ice or jump with one or both bottom corners end end -- draw self within it's own group (in turn within the maingroup) -- animate self running motion if self.dead == false then if self.jumpState == "grounded" then if self.xVel \> 4 then self.standingStill = false self.mainGroup.xScale = 1 self.runPhase = self.runPhase + self.xVel self.bodyGroup.y = 10 + math.abs(math.sin(self.runPhase/100))\*-32 self.leftArmGroup.rotation = math.sin(self.runPhase/100)\*60 self.leftForeArm.rotation = -40+math.sin(self.runPhase/100-2)\*30 self.rightArmGroup.rotation = math.sin(self.runPhase/100)\*-60 self.rightForeArm.rotation = -40+math.sin(self.runPhase/100-2)\*-30 self.leftLegGroup.rotation = math.sin(self.runPhase/100)\*-60 self.leftShinGroup.rotation = 60+math.sin(self.runPhase/100-1.5)\*-60 self.rightLegGroup.rotation = math.sin(self.runPhase/100)\*60 self.rightShinGroup.rotation = 60+math.sin(self.runPhase/100-1.5)\*60 self.head.rotation = 10+math.abs(math.sin(self.runPhase/100))\*-16 elseif self.xVel \< -4 then self.standingStill = false self.mainGroup.xScale = -1 self.runPhase = self.runPhase + self.xVel self.bodyGroup.y = 10 + math.abs(math.sin(self.runPhase/100))\*-32 self.leftArmGroup.rotation = math.sin(self.runPhase/100)\*60 self.leftForeArm.rotation = -40+math.sin(self.runPhase/100+2)\*30 self.rightArmGroup.rotation = math.sin(self.runPhase/100)\*-60 self.rightForeArm.rotation = -40+math.sin(self.runPhase/100+2)\*-30 self.leftLegGroup.rotation = math.sin(self.runPhase/100)\*-60 self.leftShinGroup.rotation = 60+math.sin(self.runPhase/100+1.5)\*-60 self.rightLegGroup.rotation = math.sin(self.runPhase/100)\*60 self.rightShinGroup.rotation = 60+math.sin(self.runPhase/100+1.5)\*60 self.head.rotation = 10+math.abs(math.sin(self.runPhase/100))\*-16 else if self.standingStill == false then --print("stand still") self.standingStill = true transition.to(self.bodyGroup, {time = 200, y = 15}) transition.to(self.head, {time = 150, rotation = 15}) transition.to(self.rightLegGroup, {time = 100, rotation = -35}) -- front leg forward transition.to(self.rightShinGroup, {time = 200, rotation = 45}) -- front shin straight transition.to(self.rightFoot, {time = 200, rotation = -10}) -- front shin straight transition.to(self.leftLegGroup, {time = 100, rotation = 5}) -- back leg backward transition.to(self.leftShinGroup, {time = 200, rotation = 25}) -- front shin straight transition.to(self.leftFoot, {time = 200, rotation = -30}) -- front shin straight transition.to(self.leftArmGroup, {time = 200, rotation = 10})-- back arm forward transition.to(self.leftForeArm, {time = 200, rotation = -60})-- back forearm forwar d transition.to(self.rightArmGroup, {time = 200, rotation = 15})-- right arm forward transition.to(self.rightForeArm, {time = 200, rotation = -85})-- right forearm forward else -- stand still motion -- used for testing limbs and joints positions --self.mainGroup.xScale = 4 --self.mainGroup.yScale = 4 --self.rightForeArm.rotation = self.rightForeArm.rotation + 1 --self.rightArmGroup.rotation = self.rightArmGroup.rotation + 1 --self.leftForeArm.rotation = self.leftForeArm.rotation - 1 --self.leftArmGroup.rotation = self.leftArmGroup.rotation - 1 --self.rightFoot.rotation = self.rightFoot.rotation + 1 --self.rightShinGroup.rotation = self.rightShinGroup.rotation + 1 --self.rightLegGroup.rotation = self.rightLegGroup.rotation + 1 --self.leftFoot.rotation = self.leftFoot.rotation - 1 --self.leftShinGroup.rotation = self.leftShinGroup.rotation - 1 --self.leftLegGroup.rotation = self.leftLegGroup.rotation - 100 --self.head.rotation = self.head.rotation + 1 --self.torso.rotation = self.torso.rotation - 1 end end end end end -- self.frameLoop

Wow. The Corona forum doesn’t allow you to upload .lua files!!

That makes sense :wacko:

This is probably handier for you to read and pick apart…

Thomas6 I have to thank you from the bottom of my heart for reaching out and helping me out. I needed to learn how to do this. Thanks a million.

You’re very welcome. Is it making any sense at all? The comments are very sparse, because it’s just code for my own use.