Quick Links: Download Gideros Studio | Gideros Documentation | Gideros community chat | DONATE
Tween a body - Need b2.Body:getX/Y(), b2.Body:setX/Y()? — Gideros Forum

Tween a body - Need b2.Body:getX/Y(), b2.Body:setX/Y()?

MellsMells Guru
edited April 2013 in General questions
Hi,

I wanted to play with bodies but noticed that b2.Body:getX/Y(), b2.Body:setX/Y() are not available.
I thought this would be handy.

Specifically in the following case :
local tween = GTween.new(myBody, 1, {x = myBody:getX()}, {repeatCount = 0, setLoop = true, reflect = true})
Does it make sense and do you know of an alternative?

Likes: Yan

twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
+1 -1 (+1 / -0 )Share on Facebook

Comments

  • ar2rsawseenar2rsawseen Maintainer
    @Mells yes I think you are right, since sprites are usually bind to bodies it would make sense to use tweening on body and not sprite. :)

    On the other hand it might not make sense to tween the bodies at all, as in, they are there for a physics purpose, and tweening might only disrupt it. But of course there can always be specific cases.

    But it's actually not getX/Y needed for tweening, but just general get/set methods

    I'll talk about it with @atilim, but in the mean time, you can probably do something like this:
    function b2.Body:set(param, val)
        local x, y = self:getPosition()
        if param == "x" then
            x = val
            self:setPosition(x, y)
        elseif param == "y" then
            y = val
            self:setPosition(x, y)
        elseif param == "rotation" then
            self:setAngle(math.rad(val))
        end
    end
     
    function b2.Body:get(param)
        local x, y = self:getPosition()
        if param == "x" then
            return x
        elseif param == "y" then
            return y
        elseif param == "rotation" then
            return math.deg(self:getAngle())
        end
    end
     
    function b2.Body:getX()
        return self:get("x")
    end
     
    function b2.Body:getY()
        return self:get("y")
    end
     
    function b2.Body:setX(x)
        self:set("x", x)
    end
     
    function b2.Body:setY(y)
        self:set("y", y)
    end

    Likes: Mells

    +1 -1 (+1 / -0 )Share on Facebook
  • atilimatilim Maintainer
    @ar2rsawseen's solution is totally correct :) On the other hand, changing the position of a physical body at each frame is not physically correct :)
  • MellsMells Guru
    edited April 2013
    @ar2rsawseen
    thank you!

    By the way what is the correct way to require the libs in init.lua?
     
    -- -----------------------
    -- REQUIRE
    -- -----------------------
    require "box2d" --Required in Box2dEasy?
    require "Box2dEasy"
    require "GiderosCodingEasy"
     
    -- -----------------------
    -- SET BOX2D WORLD
    -- -----------------------
    world = b2.World.new(0, 9.8, true)
     
    function createWorld()    	
    	local worldb2Draw = world:getDebug() -- ######## attempt to call method 'getDebug' (a nil value) ########
    	return worldb2Draw
    end
    worldb2Draw = createWorld()
    @atilim I was looking for a way to move my bodies on loop (ex moving platforms that go up and down, gravityScale = 0, etc), maybe this method to tween anything could be used to tween the linear velocity?

    I will have to search more.

    Edit : I required "Box2dEasy" before "box2d" and it worked. Should I remove the require("box2d") line?
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
  • MellsMells Guru
    edited April 2013
    So it works :
     
    function b2.Body:set(param, val)
    		local x, y = self:getPosition()
                    local lvx, lvy = self:getLinearVelocity()
    		if param == "x" then
    			x = val
    			self:setPosition(x, y)
    		elseif param == "y" then
    			y = val
    			self:setPosition(x, y)
    		elseif param == "rotation" then
    			self:setAngle(math.rad(val))		
     
                    -- #####
    		elseif param == "linearVelocity" then
    			self:setLinearVelocity(val)
    		elseif param == "linearVelocityX" then			
    			self:setLinearVelocity(val, lvy)
    		elseif param == "linearVelocityY" then
    			self:setLinearVelocity(lvx, val)
                    elseif param == "gravityScale" then
    			self:setGravityScale(val)
                    -- #####
     
    		end
    	end
     
    	function b2.Body:get(param)
    		local x, y = self:getPosition()
                    local lvx, lvy = self:getLinearVelocity()
    		if param == "x" then
    			return x
    		elseif param == "y" then
    			return y
    		elseif param == "rotation" then
    			return math.deg(self:getAngle())		
     
                    -- #####
    		elseif param == "linearVelocity" then
    			return lvx, lvy
    		elseif param == "linearVelocityX" then
    			return lvx
    		elseif param == "linearVelocityY" then
    			return lvy
                    elseif param == "gravityScale" then
    			return self:getGravityScale()
                    -- #####
     
    		end
    	end
    If you want a body to move/loop from left to right without messinging up physics :
           -- How to use
           local value = 10
           myBody:set("linearVelocityX", -value)
           local tween = GTween.new(myBody, .8, {linearVelocityX = myBody:get("linearVelocityX") + value * 2}, {reflect = true, setLoop = true, repeatCount = 0})
    thanks to @bowerandy for creating this discussion in the first place.


    Btw @atilim I see in the docs :

    Sprite:set(param, value)

    Sets the specified property of this sprite instance by its name. These names are supported:
    • "x"
    • "y"
    • "rotation"
    • "scaleX"
    • "scaleY"
    • "scale"
    • "alpha"
    • "redMultiplier"
    • "greenMultiplier"
    • "blueMultiplier"
    • "alphaMultiplier"
    I see that we have a getWidth/setWidth method available.
    Maybe it's worth adding "width" and "height" to the list of properties above?
    • "width"
    • "height"
    @ar2rsawseen
    yes if everything (gravityScale, linearDamping, localCenter, mass) was accessible through body:set(param, value) we could for example tween the gravityScale for all objects on screen for a nice slow motion effect, etc...
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
  • ar2rsawseenar2rsawseen Maintainer
    @Mells yes tweening velocity probably makes much more sense :)

    BTW there is no setWidth method, only getWidth as the width of Sprite is determined by its contents. Thus you can't adjust nor tween it. Same for height.
    But adjusting/tweening scale should provide similar effect.

    And yes to your last comment, I'm adding this to GCE ;)

    Also I will fix so Box2dEasy would work if required after box2d. So it won't matter where you require box2d completely ;)
  • MellsMells Guru
    edited April 2013
    @ar2rsawseen
    And yes to your last comment, I'm adding this to GCE
    Great, about that I have a few questions :
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
  • ar2rsawseenar2rsawseen Maintainer
    @Mells yes basically for now there is only this repo, but I had no chance to update it yet, cause I also need to test it :)
  • ar2rsawseenar2rsawseen Maintainer
    I think I've found a way to provide any setter/getter (current or future) method through set/get function dynamically. And it even seems to work, I've updated the repo.

    BTW @Mells your provided tween gives an awesome dancing box effect ;)
  • MellsMells Guru
    edited May 2013
    @ar2rsawseen wow that was fast and it works great, thank you :)

    What do you think about the following :
    elseif param == "worldVector" then
         local x, y = -- 0, 0?
         return self:getWorldVector(x, y)
    elseif param == "localPoint" then
         local x, y = -- 0, 0?
         return self:getLocalPoint(x, y)
    elseif param == "localVector" then
         local x, y = -- 0, 0?
         return self:getLocalVector(x, y)
    I don't see how useful it would be (I've never used it and have no idea of a specific case where it is relevant) but that would make everything accessible from the get/set methods?

    For those who want to see how to use it :
    print ("->", 	"position", body:get("position"))																	
    print ("->", 	"angle", body:get("angle"))						
    print ("->", 	"linearVelocity", body:get("linearVelocity"))
    print ("->", 	"angularVelocity", body:get("angularVelocity"))
    print ("->", 	"angularDamping", body:get("angularDamping"))
    print ("->", 	"worldCenter", body:get("worldCenter"))
    print ("->", 	"localCenter", body:get("localCenter"))
    print ("->", 	"mass", body:get("mass"))
    print ("->", 	"inertia", body:get("inertia"))
    print ("->", 	"gravityScale", body:get("gravityScale"))
    Now you can tween all those values if you want.


    A modified version of the physics example provided with Gideros.
    --[[
     
    This code is MIT licensed, see <a href="http://www.opensource.org/licenses/mit-license.php" rel="nofollow">http://www.opensource.org/licenses/mit-license.php</a>
    (C) 2010 - 2011 Gideros Mobile 
     
    ]]
     
    require "box2d"
    require "Box2dEasy"
    require "GiderosCodingEasy"
    require "easing"
     
    b2.setScale(20)
     
    local sky = Bitmap.new(Texture.new("sky.png"))
    stage:addChild(sky)
     
    local grass = Bitmap.new(Texture.new("grass.png"))
    grass:setY(400)
    stage:addChild(grass)
     
    -- this table holds the dynamic bodies and their sprites
    local actors = {}
     
    -- create world
    local world = b2.World.new(0, 9.8)
     
    -- this function creates a 80x80 physical box and adds it to the stage
    local function createBox(x, y, name)
    	local body = world:createBody{type = b2.DYNAMIC_BODY, position = {x = x, y = y}}
     
    	body.name = name
     
    	local shape = b2.PolygonShape.new()
    	shape:setAsBox(40, 40)
    	body:createFixture{shape = shape, density = 1, restitution = 0.2, friction = 0.3}
     
    	local sprite = Bitmap.new(Texture.new("box.png"))
    	sprite:setAnchorPoint(0.5, 0.5)
    	stage:addChild(sprite)
     
    	actors[body] = sprite
     
    	-- ------------------------------
    	-- Tween Body Properties
    	-- ------------------------------
    	local tween1 = GTween.new(body, 1, {gravityScale = 0}, {autoPlay = false})
    	local tween2 = GTween.new(body, 1, {linearVelocityY = 0}, {ease = easing.outQuintic, autoPlay = false})
    	local tween3 = GTween.new(body, .3, {gravityScale = 1}, {ease = easing.inExponential, autoPlay = false})
    	tween1.nextTween = tween2
    	tween2.nextTween = tween3
    	tween3.nextTween = tween1	
    	tween1:setPaused(false)
     
    end
     
    -- create ground body
    local ground = world:createBody({})
     
    ground.name = "ground"
     
    local shape = b2.EdgeShape.new(-200, 400, 520, 400)
    ground:createFixture({shape = shape, density = 0})
     
    -- create two boxes
    createBox(140, -30, "box1")
    createBox(180, 300, "box2")
     
    local function onBeginContact(event)
    	-- you can get the fixtures and bodies in this contact like:
    	local fixtureA = event.fixtureA
    	local fixtureB = event.fixtureB
    	local bodyA = fixtureA:getBody()
    	local bodyB = fixtureB:getBody()
     
    	print("begin contact: "..bodyA.name.."<->"..bodyB.name)
     
    	if 	bodyA.name == "box1" and bodyB.name == "box2" or
    		bodyB.name == "box1" and bodyA.name == "box2" then
    			GTween.pauseAll = true
    			bodyA:set("gravityScale", 1)
    			bodyB:set("gravityScale", 1)
    			print ("All tweens paused")
    	end
    end
     
    local function onEndContact(event)
    	-- you can get the fixtures and bodies in this contact like:
    	local fixtureA = event.fixtureA
    	local fixtureB = event.fixtureB
    	local bodyA = fixtureA:getBody()
    	local bodyB = fixtureB:getBody()
     
    	--print("end contact: "..bodyA.name.."<->"..bodyB.name)
    end
     
    local function onPreSolve(event)
    	-- you can get the fixtures and bodies in this contact like:
    	local fixtureA = event.fixtureA
    	local fixtureB = event.fixtureB
    	local bodyA = fixtureA:getBody()
    	local bodyB = fixtureB:getBody()
     
    	--print("pre solve: "..bodyA.name.."<->"..bodyB.name)
    end
     
    local function onPostSolve(event)
    	-- you can get the fixtures and bodies in this contact like:
    	local fixtureA = event.fixtureA
    	local fixtureB = event.fixtureB
    	local bodyA = fixtureA:getBody()
    	local bodyB = fixtureB:getBody()
     
    	--print("post solve: "..bodyA.name.."<->"..bodyB.name)
    end
     
     
    -- register 4 physics events with the world object
    world:addEventListener(Event.BEGIN_CONTACT, onBeginContact)
    world:addEventListener(Event.END_CONTACT, onEndContact)
    world:addEventListener(Event.PRE_SOLVE, onPreSolve)
    world:addEventListener(Event.POST_SOLVE, onPostSolve)
     
    -- step the world and then update the position and rotation of sprites
    local function onEnterFrame()
    	world:step(1/60, 8, 3)
     
    	for body,sprite in pairs(actors) do
    		sprite:setPosition(body:getPosition())
    		sprite:setRotation(body:getAngle() * 180 / math.pi)
    	end
    end
     
    stage:addEventListener(Event.ENTER_FRAME, onEnterFrame)
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
  • ar2rsawseenar2rsawseen Maintainer
    Those actually quite awesome effects.

    About the other options, well both getter and setter is needed to tween something, and I actually don't know why most of them has only getters, maybe its a box2d limitation.
  • @ar2rsawseen
    Those actually quite awesome effects.
    And it's even more powerful than I thought.

    Btw how to scale a body? I have found that fixtures need to be destroyed and recreated.
    Are there alternatives? I would like to tween the scaling.

    For example in the collision sample provided with Gideros (and modified above), how would you scale both the crate and it's body?
    That sounds trivial but I have no idea how to do it.
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
  • ar2rsawseenar2rsawseen Maintainer
    edited May 2013
    Well I don't know how efficient that is, but I've implemented a scaleBody property.
    Unfortunately you can't really scale x or y separate in most cases, thus there is only one scaleBody property.

    Updated the repo ;)
  • Cliff76Cliff76 Member
    I tried running @Mells example code above in a new GIderos project. I copied in the image assets from CollisionDetection. I downloaded GCE just now and added all of it's lua files to my new project. I then copied the code from above into a new main.lua file and ran. I get a stack overflow on Texture.__new(...). After looking at @ar2rsawseen code (line 1161 in GidereosCodingEasy.lua) I don't understand the intention. I totally understand the S/O error but I'm not follwoing why the recursive call to Texture.__new(...). Is it intended to call into a different module or namespace of some sort? (I'm aware that this could totally be a configuration error on my part but brand new to GCE and Gideros so bear with me.)
  • Cliff76Cliff76 Member
    Ah wait, I see now. It's not a recursive call as there is only 1 "_" in the name caling to the original implementation I guess. I still don't understand why I get a S/O... :(
  • ar2rsawseenar2rsawseen Maintainer
    @Cliff76 you need to exclude GCE and B2DE from execution and require them in init.lua ;)
  • Cliff76Cliff76 Member
    @ar2rsawseen Wha? Ok, I think I see it now. I'm bouncing between ZeroBrane and Gideros Studio and I didn't see anyway to change project properties in ZB but I am able to open the gproj file directly and add excludeFromExecution="1" to the file tag for GCE. I guess this is the way to do what you said. Here goes nothing...
  • Cliff76Cliff76 Member
    @ar2rsawseen Yup that worked! I'll need to revisit the Gideros docs and see if I missed the part that describes excluding files from execution.

  • Well I don't know how efficient that is, but I've implemented a scaleBody property.
    @ar2rsawseen It works like a charm. Thanks a lot :D
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
Sign In or Register to comment.