Getting stuck in the wall

Hey all,

I’m playing around with the physics engine, but I came upon an issue I cannot seem to resolve; I spawn a bunch of circles (15), add a dynamic body to it, and give them a random velocity : (simplified code)

globe = display.newCircle( 
	math.random(30, FIELD.WIDTH-30),
	math.random(30, FIELD.HEIGHT-30), 
	5 )
physics.addBody( globe, "dynamic", { radius=5, bounce=0.99 } )
globe.setLinearVelocity(Xv, Yv)

I have my “playfield” bordered by rectangles that are “static” body;

bot = rect(0, FIELD.HEIGHT, FIELD.WIDTH, 15, green)
right = rect(FIELD.WIDTH-5, 0,  15, FIELD.HEIGHT, purple)

top = rect(0, 0, FIELD.WIDTH, 15, green)
left = rect(0, 0, 5, FIELD.HEIGHT, red)

physics.addBody( top, "static" )
physics.addBody( bot, "static" )
physics.addBody( left, "static" )
physics.addBody( right, "static" )

function rect(x, y, w, h, color)
	local new_x = x+(w/2)
	local new_y = y+(h/2)
	local r = display.newRect(new_x, new_y, w, h)
	r.fill = color
	return r
end

After running the simulation for a few seconds, some globes get stuck to the wall; I’m not exactly sure what happens, but I observe that Xv or Yv drops to 0 in this state, my attempt to fix this was to run check regularly for this case and reset Xv or Yv to the initial state (which I store); while this sometimes fixes it, mostly it does not; (simplified)

if (vx == 0 or vy == 0) then 
	new_vx = vx
	new_vy = vy

	if (vx == 0) then
		new_vx = globe[id].init_Xv
	end
	if (vy == 0) then
		new_vy = globe[id].init_Yv
	end

	globe[id]:setLinearVelocity(new_vx, new_vy)
end

I also attempted to make the border not straight by adding, but again no luck;

r.path.x4 = -1
r.path.x3 = -2
r.path.x2 = -3
r.path.x1 = -4

I then tried to round the x and y position because I believe it might be related to floating calculations; so I round the numbers and reset Xv and Yv; (depending on what is 0) Sadly this does not resolve it either; The last thing I tried is to teleport them away from the wall while resetting :

globe[id].x = round(globe[id].x) + (math.random(20,30)/10)
globe[id].y = round(globe[id].y) + (math.random(20,30)/10)
globe[id]:setLinearVelocity(new_vx, new_vy)

While this helps somewhat, its clearly visible, and not a full solution; Does anyone have an idea how to resolve this ?

This is what I want to recreate : https://upload.wikimedia.org/wikipedia/commons/6/6d/Translational_motion.gif

Thanks for the help / idea’s !

Very hard to understand how your code works from these fragments. I would expect your globes to just drop down thanks to gravity since they are dynamic bodies.

Have you configured any collision detection for the globes and walls?

Hey Markus,

I set physics.setGravity( 0, 0 ) ; and globes are dynamic, walls are static;

The marked globe will only move horizontally against (/in) the wall;

This is the code (warning : its not clean :wink: )

-----------------------------------------------------------------------------------------
--
-- main.lua
--
-----------------------------------------------------------------------------------------

local physics = require( "physics" )
physics.start()
physics.setGravity( 0, 0 )
physics.setDrawMode( "hybrid" )

-- Hide status bar
display.setStatusBar( display.HiddenStatusBar )
 
-- Seed the random number generator
-- math.randomseed( os.time() )
math.randomseed(os.clock()*1000) -- os.time() is to easy

-- game constants
local FIELD = {WIDTH = 320, HEIGHT = 480}
local LEVEL = {START = 1, VERIFY = 2, REQUIREMENT = {}, ATOMS = {}, ENTROPY = {}, TEXT = {}}

local background

-- color definitions
local white 	= {1}
local black 	= {0}

local yellow 	= {255, 255, 0}
local red 		= {255, 0, 0 , 0.6}
local green 	= {0, 255, 0}
local blue 		= {0, 0, 255}

local pink 		= {255, 204, 204}
local orange	= {255, 128, 0}
local seablue	= {0, 255, 255}
local purple	= {255, 0, 255}

-- vars
local atoms = {} 
local atom_count = 35

function create_borders()
	bot = rect(0, FIELD.HEIGHT, FIELD.WIDTH, 15, green)
	right = rect(FIELD.WIDTH-5, 0,  15, FIELD.HEIGHT, purple)
	
	top = rect(0, 0, FIELD.WIDTH, 15, green)
	left = rect(0, 0, 5, FIELD.HEIGHT, red)
	-- playfield = rect(0, 0, FIELD.WIDTH, FIELD.HEIGHT, red)
	
	-- physics.addBody( playfield, "static" )
	physics.addBody( top, "static" )
	physics.addBody( bot, "static" )
	physics.addBody( left, "static" )
	physics.addBody( right, "static" )
end

function create_atom(n)
	
	-- we need a 1 or a -1
	local function random_direction()

		local result = math.random(0,1)
		if result == 0 then
			result = -1
		end
		return result
	end
	
	-- for n atoms
	local count = 0
	local atom_id = #atoms
	while count < (n+1) do
		
		-- determ type
		local selected_atom = math.random(0,1)
		-- local atom
		if selected_atom == 0 then
			atoms[atom_id] = display.newCircle( 
								math.random(30, FIELD.WIDTH-30),
								math.random(30, FIELD.HEIGHT-30), 
								3 )
			atoms[atom_id].fill = orange
			physics.addBody( atoms[atom_id], "dynamic", { radius=5, bounce=0.99 } )
		else			
			atoms[atom_id] = display.newCircle( 
								math.random(30, FIELD.WIDTH-30),
								math.random(30, FIELD.HEIGHT-30), 
								5 )
			atoms[atom_id].fill = purple
			physics.addBody( atoms[atom_id], "dynamic", { radius=7, bounce=0.99 } )
		end
		
		local Xv = math.random( 40,120 ) * random_direction()
		local Yv = math.random( 20,60 ) * random_direction()
		atoms[atom_id]:setLinearVelocity(Xv, Yv)
		atoms[atom_id].init_Xv = Xv
		atoms[atom_id].init_Yv = Yv
		
		atom_id = atom_id + 1 
		count = count + 1 
	end
end

-- changed from newRect that start with centered x,y
function rect(x, y, w, h, color)
	local new_x = x+(w/2)
	local new_y = y+(h/2)
	local r = display.newRect(new_x, new_y, w, h)
	r.fill = color
	r.path.x4 = -1
	r.path.x3 = -2
	r.path.x2 = -3
	r.path.x1 = -4
	return r
end

function printatoms()
	
local atom_id = 0
local n = #atoms
	while atom_id < (n+1) do

	local vx, vy = atoms[atom_id]:getLinearVelocity()
	
	local function round(number)
  return number - (number % 1)
end

	-- print ("vx:" .. round(vx) .. " vy:" .. round(vy))
	
		if (vx == 0 or vy == 0) then 
			new_vx = vx
			new_vy = vy
			
			if (vx == 0) then
				new_vx = atoms[atom_id].init_Xv
			end
			if (vy == 0) then
				new_vy = atoms[atom_id].init_Yv
			end
			
			-- atoms[atom_id].fill = red
			
			print ("x" .. atoms[atom_id].x .. " y:" .. atoms[atom_id].y)
			print ("vx ori : " .. atoms[atom_id].init_Xv .. " vy ori: " .. atoms[atom_id].init_Yv)
			print ("vx:" .. vx .. " vy:" .. vy .. " to vx:" .. new_vx .. " vy:" .. new_vy)
			atoms[atom_id].x = round(atoms[atom_id].x) + (math.random(20,30)/10)
			atoms[atom_id].y = round(atoms[atom_id].y) + (math.random(20,30)/10)
			atoms[atom_id]:setLinearVelocity(new_vx, new_vy)
		end
		atom_id = atom_id + 1 
	end
end
 
function add_atoms()
	atom_count = atom_count + 5;
	create_atom(5)
	print (#atoms)
	-- draw_debug()
end

-- main function
function main()
	
	create_borders()
	-- generate atoms
	create_atom(atom_count)
	updateLoopTimer = timer.performWithDelay( 400, printatoms, 0 )
end


-- run the code
main()

-- return to menu
-- state = MENU.MENU

I ran your code and now I see what you mean. Strange behavior indeed, and I wasn’t able to solve it either.

Did some googling and it seems to be a “feature” of the Box2D engine. Maybe this can help you:

And this:

I tried physics.setMKS("velocityThreshold", 0) but it didn’t seem to have any effect as the balls still got stuck around the edges when colliding with too small velocity.

1 Like

Hey Markus,

Thanks for having a look! It’s weird nobody seemed to have faced this issue before; I can kinda work around it by the reseting velocity; but its far from ideal.