How to move 2d environment around character and keep physics engine happy?

Here is another platformer/side-scrolling game issue:

In many of these types of games the character does not actually move much at all, but the environment moves around the character (to keep the hero central in the display obviously).

I can’t seem to find the best way to do this. Say you have a character (dynamic), an object like a crate (dynamic), and the ground (static)… I can write a block of code that sets ground.x = ground.x - 3 (move ground backwards when touching the screen or something…same thing for crate) to move the environment backwards, but then when my character hits the crate, the display gets “jerky”. It seems like since i’m physically changing the x-position of the objects, the objects temporarily interfere with each other until the physics engine catches up and sorts the collision out.

Whereas if you apply a force to the crate and it bumps into the character, there is no “jerkiness” … the crates bounces off without interference with the character like you want.

If I wanted to move the character I would investigate applying a force to move it around the screen, but since I want to move the environment this won’t work (can’t apply force to static objects (ground) and with different sized objects in the environment the force would move them different amounts)

So I guess to break-down my long-winded post:

What is the correct way to move the environment around your character so that the physics collisions work smoothly/properly?

Thanks,
Chris [import]uid: 146966 topic_id: 34642 reply_id: 334642[/import]

Go and take a look at Caleb’s Perspective.lua module.

It works great for me, I’ve got a platformer in the works with lots of physics objects.

Perspective has quite a few useful features, like layer integration, remove and destroy…

Perspective in Community Code

I was able to set damping = 10 and the screen movement is “buttery” smooth, IMO.

Hope this helps,

Nail [import]uid: 106779 topic_id: 34642 reply_id: 137687[/import]

Put your whole world in a group and move the group the opposite of the way your main object is moving.

something like this:

function moveworld()  
 world.x = -mainBody.x + \_halfWidth;  
 world.y = -mainBody.y + \_halfHeight;  
end   
  
Runtime:addEventListener("enterFrame",moveworld)  

The physics engine will be covered extensively in the next issue of CoronaPaper. [import]uid: 173835 topic_id: 34642 reply_id: 137712[/import]

@xnailbender: You really seem to like it :wink:

@Stian Staunes: Actually, that’s exactly what Perspective does, but it’s much easier for you. It does all of that inside of the library, to completely eliminate any need for you to do any extra camera coding like your example. And it also works perfectly with the physics engine.

Caleb [import]uid: 147322 topic_id: 34642 reply_id: 137751[/import]

Thanks guys, I hadn’t heard of the Perspective module before. I’m excited to play around and incorporate it into my code. I’ll post back after I get a chance to try it out.

Chris [import]uid: 146966 topic_id: 34642 reply_id: 137791[/import]

@Caleb wrote:

@xnailbender: You really seem to like it :wink:

Now I’m wondering if parallax scrolling can be integrated into the module.

It would be a complete platfrom scrolling solution in a single module.

I’m just starting to look into parallax scrolling, it will be a big visual improvement for my platformer’s Ver 1.2.

Just a little more testing and I’ll submit Ver 1.0

I’ve been making little improvements, like allowing users to drag/ place the “Left”, “Right” and “Up” controller buttons where ever they like them best.

My boys, 7 & 12, were complaining they didn’t the controller positions, but I liked them where they were, so to stop the bickering I coded in the moveable buttons. :). All my beta testers are happy now. :slight_smile:

Hopefully if my app gets any users, they will use/like the feature.

Nail

[import]uid: 106779 topic_id: 34642 reply_id: 137809[/import]

Hmm… I’m wondering if I ought to say this, thinking of the previous time I said it…

Parallax scrolling is simplicity in it’s utmost to do.

(that means I’ll add it sometime in the future)

EDIT:
After much labor (almost 45 minutes!) I’ve come out with Perspective 1.2, which supports parallax tracking :slight_smile:

Caleb [import]uid: 147322 topic_id: 34642 reply_id: 137864[/import]

@Caleb wrote:

Parallax scrolling is simplicity in it’s utmost to do.

Crack’n me up there…

Nail [import]uid: 106779 topic_id: 34642 reply_id: 137968[/import]

Ok, I’ve had a chance to play around with Perspective a bit. First off, great stuff … thanks Caleb!

Below is a test case I made (plug n play code):
[lua]display.setStatusBar( display.HiddenStatusBar )
local physics = require “physics”
physics.start()
physics.setDrawMode( “hybrid” )

local perspective=require(“perspective”)
local camera=perspective.createView()

local ground = display.newRect( 0, 300, 480, 50 )
ground:setFillColor( 0, 255, 0 )
camera:add(ground, 3, false)
physics.addBody(ground, “static”, { density=1.0, friction=0.4, bounce=0})

local hero = display.newRect( 150, 300, 15, 50 )
hero:setFillColor( 255, 0, 0 )
camera:add(hero, 1, false)
physics.addBody(hero, “dynamic”, { density=1.0, friction=0.4, bounce=0})
hero.isFixedRotation = true

local crate = display.newRect( 200, 300, 15, 15 )
crate:setFillColor( 0, 0, 255 )
camera:add(crate, 2, false)
physics.addBody(crate, “dynamic”, { density=1.0, friction=0.4, bounce=0.3})

local forwardBtnPressed = false
local backBtnPressed = false

local function moveCharacter(event)
if forwardBtnPressed then
hero.x = hero.x + 3
elseif backBtnPressed then
hero.x = hero.x - 3
end
end

function forwardBtnPressed(event)
forwardBtnPresssed = false
backBtnPressed = false
if event.phase == “began” then
forwardBtnPressed = true
Runtime:addEventListener( “enterFrame”, moveCharacter )
elseif event.phase == “ended” then
Runtime:removeEventListener( “enterFrame”, moveCharacter )
forwardBtnPressed = false
end
print(hero.x)
return true
end

function backBtnPressed(event)
forwardBtnPresssed = false
backBtnPressed = false
if event.phase == “began” then
backBtnPressed = true
Runtime:addEventListener( “enterFrame”, moveCharacter )
elseif event.phase == “ended” then
Runtime:removeEventListener( “enterFrame”, moveCharacter )
backBtnPressed = false
end
return true
end

local forwardBtn = display.newRect(display.contentWidth/2,display.contentHeight/2,display.contentWidth/2,display.contentHeight)
forwardBtn:setFillColor(0, 0)
local backBtn = display.newRect(0,display.contentHeight/2,display.contentWidth/2,display.contentHeight)
backBtn:setFillColor(0, 0)

camera:setFocus(hero)
camera:track()
camera.damping=30
camera:setBounds(0, display.contentWidth*4, 0, display.contentHeight)

forwardBtn:addEventListener( “touch”, forwardBtnPressed )
backBtn:addEventListener(“touch”, backBtnPressed )[/lua]
You can touch the left or right side of the screen to move the “hero” (red block) around.

My issue is that you can see when the red block moves into the crate, they interfere or overlap each other before being pushed apart. I know this is happening because of how I have the code setup to move the red block (x = x+3 on enterFrame)… so when the red block is next to the crate, it is instantaneously being overlapped by 3 pixels on the crate … then the physics engine detects the collision and pushes them apart.

This becomes less apparent the smaller you increment the chance (x = x+.5 for example), but this makes the character movement unacceptably slow.

I’m wondering if either of you know of a better way that I can have the character move (during a continuous touch event) that doesn’t cause this problem? [import]uid: 146966 topic_id: 34642 reply_id: 138001[/import]

My second issue is harder to demonstrate here so I thought I’d make another post.

In a chunk of my ‘real’ code I have tried to do essentially the same thing that I did in my test case except that I’m using images for the objects. On the character (spaceman2) and ground I have used the Physics Editor application to define the physical boundaries for the more complicated ground (rolling terrain) and character. That data is stored within “shapedefs.lua” … I’m not sure how familiar or not you are with Physics Editor? The crate object is the same as before, just a vector object.

When I fire up the simulator, all of the physics boundaries (that I can see through the “hybrid” draw mode) are present and work properly, but my images do not show on screen. Even the crate which isn’t an image object does not show… just the orange physics boundary.

Have you ever come across this issue before? I’ve been troubleshooting a lot trying to slowly reduce the complexity in this code to make it closer to the test code from my other post, but so far I haven’t been able to make any progress. As soon as I comment out the camera:add(object) lines, my images appear again.

I’ll post my code below even though it will be a bit harder to follow (and is not plug and play) than the last chunk I posted:
[lua]display.setStatusBar( display.HiddenStatusBar )
local physics = require “physics”
physics.start()
physics.setDrawMode( “hybrid” )

local perspective=require(“perspective”)
local camera=perspective.createView()

local background = display.newImage(“background_outdoor.png”)

local physicsData = ( require “shapedefs”).physicsData()
ground_lev_test = display.newImage( “ground_lev_test.png”, true )
ground_lev_test:setReferencePoint( display.TopLeftReferencePoint )
ground_lev_test.x = 0; ground_lev_test.y = 0
camera:add(ground_lev_test, 2, false)
ground_lev_test.myName = “ground_lev_test”
physics.addBody( ground_lev_test, “static”, physicsData:get(“ground_lev_test”) )

spaceman2 = display.newImage( “spaceman2.png” )
spaceman2.x = 240
spaceman2.y = 150
spaceman2.alpha=1
camera:add(spaceman2, 1, false)
spaceman2.myName = “spaceman2”
physics.addBody( spaceman2, “dynamic”, physicsData:get(“spaceman2”) )
spaceman2.isFixedRotation = true

local crate = display.newRect( 250, 100, 15, 15 )
crate:setFillColor( 0, 0, 0 )
crate.alpha = 1
camera:add(crate, 3, false)
physics.addBody(crate, “dynamic”, { density=1.0, friction=0.4, bounce=0.3, filter={categoryBits = 8, maskBits = 7} })

local forwardBtnPressed = false
local backBtnPressed = false
local jumpBtnPressed = false

local function moveCharacter(event)
if forwardBtnPressed then
spaceman2.x = spaceman2.x + 3
elseif backBtnPressed then
spaceman2.x = spaceman2.x - 3
end
end

function forwardBtnPressed(event)
forwardBtnPresssed = false
backBtnPressed = false
if event.phase == “began” then
forwardBtnPressed = true
Runtime:addEventListener( “enterFrame”, moveCharacter )
elseif event.phase == “ended” then
Runtime:removeEventListener( “enterFrame”, moveCharacter )
forwardBtnPressed = false
end
print(spaceman2.x)
return true
end

function backBtnPressed(event)
forwardBtnPresssed = false
backBtnPressed = false
if event.phase == “began” then
backBtnPressed = true
Runtime:addEventListener( “enterFrame”, moveCharacter )
elseif event.phase == “ended” then
Runtime:removeEventListener( “enterFrame”, moveCharacter )
backBtnPressed = false
end
return true
end

local forwardBtn = display.newRect(display.contentWidth/2,display.contentHeight/2,display.contentWidth/2,display.contentHeight/2)
forwardBtn:setFillColor(0, 0)
local backBtn = display.newRect(0,display.contentHeight/2,display.contentWidth/2,display.contentHeight/2)
backBtn:setFillColor(0, 0)

camera:setFocus(spaceman2)
camera:track()
camera.damping=30
camera:setBounds(0, display.contentWidth*4, 0, display.contentHeight)

forwardBtn:addEventListener( “touch”, forwardBtnPressed )
backBtn:addEventListener(“touch”, backBtnPressed )[/lua] [import]uid: 146966 topic_id: 34642 reply_id: 138003[/import]

Problem # 2 has been solved :slight_smile:

The culprit was the line:
[lua]local background = display.newImage(“background_outdoor.png”)[/lua]

It wasn’t in a layer and was sitting in front of all of the other objects that had been put in Perspective layers.

Embarrassed it took me 2 nights to figure that out!

Thanks,
Chris [import]uid: 146966 topic_id: 34642 reply_id: 138005[/import]

@maher, I don’t know how to eliminate the “overlap” issue using the “pixel” method, but instead you can play with seLlinearVelocity(x,y) of the hero.

Try this and see if it’s what you are after…

local function moveCharacter(event)  
  
 if forwardBtnPressed then  
 --hero.x = hero.x + 2  
 hero:setLinearVelocity(150, 0)  
 elseif backBtnPressed then  
 --hero.x = hero.x - 2  
 hero:setLinearVelocity(-150, 0)  
 end  
end  
   
function forwardBtnPressed(event)  
 forwardBtnPresssed = false  
 backBtnPressed = false  
 if event.phase == "began" then  
 forwardBtnPressed = true  
 Runtime:addEventListener( "enterFrame", moveCharacter )  
 elseif event.phase == "ended" then  
 Runtime:removeEventListener( "enterFrame", moveCharacter )  
 forwardBtnPressed = false  
 hero:setLinearVelocity( 0, 0 )  
 end  
 print(hero.x)  
 return true  
end   
  
function backBtnPressed(event)  
 forwardBtnPresssed = false  
 backBtnPressed = false   
 if event.phase == "began" then  
 backBtnPressed = true  
 Runtime:addEventListener( "enterFrame", moveCharacter )   
 elseif event.phase == "ended" then  
 Runtime:removeEventListener( "enterFrame", moveCharacter )  
 backBtnPressed = false  
 hero:setLinearVelocity( 0, 0 )  
 end  
 return true  
end  

Hope this helps,

Nail [import]uid: 106779 topic_id: 34642 reply_id: 138023[/import]

xnailbender is correct, in my opinion. If you’re using the physics library, you should use a physics approach to moving your character, instead of incrementing the X like that.

The code xnailbender posted should work, too.

Caleb [import]uid: 147322 topic_id: 34642 reply_id: 138055[/import]

Go and take a look at Caleb’s Perspective.lua module.

It works great for me, I’ve got a platformer in the works with lots of physics objects.

Perspective has quite a few useful features, like layer integration, remove and destroy…

Perspective in Community Code

I was able to set damping = 10 and the screen movement is “buttery” smooth, IMO.

Hope this helps,

Nail [import]uid: 106779 topic_id: 34642 reply_id: 137687[/import]

Thanks for the recommendations. the setLinearVelocity works well, but then I noticed it kind of messes with a jump button I added. The jump works through “applyForce” in vertical direction. If I jump stationary it looks natural. If I jump and touch forward, the character glides through the air (the gravity doesn’t appear to work the same way).

I think I can get everything to look right if I use applyForce to move forward and back … just might need to add some extra controls to ramp the speed up to some max limit for if the touch is held continuously.

Thanks,
Chris [import]uid: 146966 topic_id: 34642 reply_id: 138222[/import]

I’ve done a retro style platformer (http://www.youtube.com/watch?v=_5oPGbcAfeQ) and from my experience it’s much harder to achieve such performance with physic engines, your mileage may vary. [import]uid: 206803 topic_id: 34642 reply_id: 138224[/import]

@Aidin wrote:

from my experience it’s much harder to achieve such performance with physic engines, your mileage may vary.

I agree Aidiin, I use the “pixel” method in my platformer for both run and jump. I found using physics very tricky to get right and consistent.

I don’t mind a slight “overlay” on collisions, I just make my physics body a pixel or 2 bigger and I build for 480x320 landscape @ 60 FPS for best results and let Corona scale everything.

I may not totally eliminate the “overlay”, but it’s acceptable to me.

Nail

[import]uid: 106779 topic_id: 34642 reply_id: 138226[/import]

Put your whole world in a group and move the group the opposite of the way your main object is moving.

something like this:

function moveworld()  
 world.x = -mainBody.x + \_halfWidth;  
 world.y = -mainBody.y + \_halfHeight;  
end   
  
Runtime:addEventListener("enterFrame",moveworld)  

The physics engine will be covered extensively in the next issue of CoronaPaper. [import]uid: 173835 topic_id: 34642 reply_id: 137712[/import]

You need to keep his velocity and set the desired velocity when you jump/move:

[lua]–In your jump function, this is the part that makes him go up:
local vx, vy=hero:getLinearVelocity()
hero:setLinearVelocity(vx, -50) – or however much Y-velocity you want to use



–And in your move left-right function:
local vx, vy=hero:getLinearVelocity()
if forwardButtonIsPressed then
hero:setLinearVelocity(150, vy)
else
hero:setLinearVelocity(-150, vy)
end[/lua]

Oh, and notice the “forwardButtonIsPressed”. You can’t make forwardButtonPressed a boolean and a function - same as backButtonPressed.

Caleb [import]uid: 147322 topic_id: 34642 reply_id: 138268[/import]

@xnailbender: You really seem to like it :wink:

@Stian Staunes: Actually, that’s exactly what Perspective does, but it’s much easier for you. It does all of that inside of the library, to completely eliminate any need for you to do any extra camera coding like your example. And it also works perfectly with the physics engine.

Caleb [import]uid: 147322 topic_id: 34642 reply_id: 137751[/import]