Code: https://files.me.com/horacebury/dzd1ev
Swf Video: https://files.me.com/horacebury/cctx6z
I've been wondering about the issue surrounding Ragdoll physics in Corona and had always wanted to produce a direct comparison to illustrate the differences various developers are seeing, myself included.
Recently, I found some JavaScript ports of the Box2D Flash engine and thought that I would attempt a rag doll scenario in box JavaScript and Corona.
This is the result of my efforts. I have tried to make a rag doll (head, neck, body, 2-piece arms and legs with -45 to 45 degree rotation limits) in a scene with a static floor and two boxes (one statically positioned and one weld to the static box.) The limbs of the rag doll can also be grabbed with a touch/mouse joint to throw the doll around.
I am hoping that this illustrates both how to create this type of scene and the problem some have experienced with rag doll physics - that of joints coming apart. Unfortunately, the point of note here is that in the JavaScript version (being a direct port of the Flash Box2D engine) the joints do not come apart.
The javascript version requires the canvas element and therefore needs to run in an HTML5 browser. That means it should be fine on the iPad/iPhone/Android. I have left the job of showing the JavaScript example in a web overlay as an exercise for the reader, but it will run fine in Chrome, for example. The Corona rag doll runs just fine in the simulator, too.
I've tried to make the scene, rag doll, dimensions, joints and so on exactly the same in both and the code is laid out relatively similarly for easy comparison. (The JavaScript version has extra functions for handling things we take for granted in Corona.
I've included a 'references.txt' file indicating where I got my source and information. I would like to name the Box2dWeb (http://code.google.com/p/box2dweb/) port as my port of choice.
I hope you guys have fun and can make some use of this.
Code: https://files.me.com/horacebury/dzd1ev
Swf Video: https://files.me.com/horacebury/cctx6z
Great work. Thanks for sharing.
Amazing!
I wonder how could i achieve the effect where limbs would "bounc" back in the original position, rather than staying/floating where they want.? does anyone have any idea?
This is the point I was trying to illustrate. When all objects in the environment are dynamic and do not collide with any static objects, they should not pull apart. Even the touch joint (known in Box2D as a mouse joint) should not cause joints to come apart. In the javascript implementation they do not.
I mostly fixed that pull apart issue.....
ONE line of code will GREATLY improve it.
physics.setPositionIterations(128)
API is my friend :)
So before, test with the comment out of the above line, pull on the head, pull on the body - notice the separaion.
add the line back in, do the same thing - it's greatly reduced. :)
Now that is out the way, this is awesome. I always wanted to build a ragdoll. In my game, I'm using a brand new way to interact with levels via temporary touch joints, passing thos x and y parms and taking action based on them and this pull apart stuff kept RUINING IT.
The things you should know about this are the position iterations are cpu intensive as they go up (I MUST use 128 for my game, if I go lower it ruins the mechanics. I've had to do some HARDCORE optimizations, as I wrote my own particle engine as well....arrrgh!) I forget what the default is (I think 8? ) but for me since the action is FAST, I needed higher. I still get some rare issues, but for the most part this fixed the pull apart stuff I was getting.
There is also velocity iterations and some other clamping you can do, but this was the most simple direct approach to fixing it
:)
ng
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | -- environment local physics = require("physics") physics.start() physics.setGravity( 0, 10 ) physics.setDrawMode( 'hybrid' ) --+++++++++++++++++++++++++++++ --This fixes for mostly the pull apart crap -- :) physics.setPositionIterations(128) --+++++++++++++++++++++++++++++ -- values local props = { friction=1, density=1, bounce=0.1 } local scene = display.newGroup() local doll = display.newGroup() -- scene scene.static = display.newRect( scene, 100, 100, 50, 50 ) scene.weld = display.newRect( scene, 300, 100, 50, 50 ) scene.floor = display.newRect( scene, 0, 320, 600, 10 ) physics.addBody( scene.static, "static", props ) physics.addBody( scene.floor, "static", props ) physics.addBody( scene.weld, "dynamic", props ) scene.weldjoint = physics.newJoint( "weld", scene.static, scene.weld, scene.weld.x, scene.weld.y ) -- ragdoll doll.head = display.newCircle( doll, 200, 100, 20 ); doll.head.radius = 20 doll.neck = display.newRect( doll, 195, 120, 10, 10 ) doll.body = display.newRect( doll, 180, 130, 40, 80 ) doll.leftupperarm = display.newRect( doll, 170, 130, 10, 30 ) doll.rightupperarm = display.newRect( doll, 220, 130, 10, 30 ) doll.leftlowerarm = display.newRect( doll, 170, 160, 10, 30 ) doll.rightlowerarm = display.newRect( doll, 220, 160, 10, 30 ) doll.leftthigh = display.newRect( doll, 175, 210, 10, 30 ) doll.rightthigh = display.newRect( doll, 215, 210, 10, 30 ) doll.leftshin = display.newRect( doll, 175, 240, 10, 30 ) doll.rightshin = display.newRect( doll, 215, 240, 10, 30 ) function addBodies( groups, props ) for i=1, #groups do local dg = groups[i] for i=1, dg.numChildren do if (dg[i].radius) then props.radius = dg[i].radius else props.radius = nil end physics.addBody( dg[i], "dynamic", props ) end end end addBodies( { doll }, props ) function setColours( dg ) for i=1, dg.numChildren do local obj = dg[i] obj.strokeWidth = 1 obj:setStrokeColor( 255,255,255 ) obj:setFillColor( 150,150,150 ) end end setColours( doll ) setColours( scene ) doll.chin = physics.newJoint( "pivot", doll.head, doll.neck, doll.neck.x, doll.neck.y-doll.neck.height/2 ) doll.throat = physics.newJoint( "pivot", doll.body, doll.neck, doll.neck.x, doll.neck.y+doll.neck.height/2 ) doll.leftshoulder = physics.newJoint( "pivot", doll.body, doll.leftupperarm, doll.leftupperarm.x, doll.leftupperarm.y-doll.leftupperarm.height/2 ) doll.rightshoulder = physics.newJoint( "pivot", doll.body, doll.rightupperarm, doll.rightupperarm.x, doll.rightupperarm.y-doll.rightupperarm.height/2 ) doll.leftelbow = physics.newJoint( "pivot", doll.leftlowerarm, doll.leftupperarm, doll.leftupperarm.x, doll.leftupperarm.y+doll.leftupperarm.height/2 ) doll.rightelbow = physics.newJoint( "pivot", doll.rightlowerarm, doll.rightupperarm, doll.rightupperarm.x, doll.rightupperarm.y+doll.rightupperarm.height/2 ) doll.lefthip = physics.newJoint( "pivot", doll.body, doll.leftthigh, doll.leftthigh.x, doll.leftthigh.y-doll.leftthigh.height/2 ) doll.righthip = physics.newJoint( "pivot", doll.body, doll.rightthigh, doll.rightthigh.x, doll.rightthigh.y-doll.rightthigh.height/2 ) doll.leftknee = physics.newJoint( "pivot", doll.leftshin, doll.leftthigh, doll.leftthigh.x, doll.leftthigh.y+doll.leftthigh.height/2 ) doll.rightknee = physics.newJoint( "pivot", doll.rightshin, doll.rightthigh, doll.rightthigh.x, doll.rightthigh.y+doll.rightthigh.height/2 ) function addRotationLimits( joints, limit ) for i=1, #joints do local joint = joints[i] joint.isLimitEnabled = true joint:setRotationLimits( -limit, limit ) end end addRotationLimits( { doll.chin, doll.throat, doll.leftshoulder, doll.rightshoulder, doll.leftelbow, doll.rightelbow, doll.lefthip, doll.righthip, doll.leftknee, doll.rightknee }, 45 ) function touch( event ) if (event.phase == 'began') then doll.touch = physics.newJoint( "touch", event.target, event.x, event.y ) print(doll.touch.maxForce,doll.touch.frequency,doll.touch.dampingRatio) doll.touch:setTarget( event.x, event.y ) display.getCurrentStage():setFocus( event.target ) elseif (event.phase == 'moved' and doll.touch ~= nil) then doll.touch:setTarget( event.x, event.y ) elseif (doll.touch ~= nil) then doll.touch:removeSelf() doll.touch = nil display.getCurrentStage():setFocus( nil ) end return true end function addTouch( dg ) for i=1, dg.numChildren do dg[i]:addEventListener( "touch", touch ) end end addTouch( doll ) |
Yeah, from the default 8 to 128 is pretty heavy going. Ave you tried it on different devices?
In my game Tiltopolis I had to up the value a bit, but I only went to 12 I think.
Thanks for getting a solution though, I think a lot of people will be happy to see that.
Matt
I WISH I could use 12.
Basically, if I took 2 walls and a ball was going between them with some high # of bounce I don't have a problem.
It's when I do a drag body on the environment ...ie
Let's say I had a square and the square was big enough to have a ball INSIDE the square (meaning it's composed of 4 display.newRect, up down left and right)
_
|_|
^^ my square lol.
Then I put some drag code on the square (not gonna post here, that's easy at this point)
If I didn't put iterations high enough, you would drag the square around and the ball would go through the wall and go bye bye.
with 128, it GREATLY reduces this chance, but doesn't eliminate it. The problem I think is (just a guess, i'm too new to coding as of june 2011) box2d exposure.
I've read the box2d manual front to back over and over again, and there seems to be a few things that Corona doesn't expose in terms of API.
Even after doing things like isBullet on both objects ball, and square that doesn't seem to help.
I've even done some "trickery" to add a function to add some linear impulse to the ball when it hits a wall so it forces the ball back into the square. My testing seems to point to this works a LITTLE, but it's hard to do exact testing.
.....ok long message wrapping up :)
--I get 60FPS with around 5,000 to 20,000 vertices for the shape of my level + ball + enemies + a particle engine I wrote with 128 iterations. I do get a drop to low 40's on occasion and if I drop the iterations to 32 or 64 (I like to do *2 hehe) the FPS stays low/mid 50s to 60fps.
It's been the #1 thing thats bugging me, as my game HEAVILY relies on this mechanic of dragging, and this thing is just a pain in my butt :)
ng
The doll shakes when pressed and dragged up against the square blocks. It even still shakes for a minute when laying on the ground. Is this a problem with Corona simulator? The html5 example is incredibly smooth and natural with no shaking. I am in a win 7 environment. Does anyone else see this?
I have to admit, I've seen this too in XP pro and mac. Can't say why but my guess would be the ever present math approximation. Would love to hear from Ansca on this.
Thanks, trying to decide what what dev platform to use. Phonegap with HTML5 or Corona. I like the idea of using a framework with one code base for both Android and IOs so Objective-C is not high on the list for me.
Coming over from C# web apps.
I notice HTML5 doll movements are slower though.
Like it was moving through water not air. Does the doll need more density to drag it faster? Or is that the speed limitation for html5? My PC is blazing fast so it is not that.
Can HTML5 code be protected when run in the browser control on a phone? Is there a commonly used trick people use to view client code like we do on PCs?
IMHO, I'd go with Corona even if you do want to use HTML5, simply because you have access to more underlying hardware functions than with the other technologies - and for my money Corona is much more stable and easier to use.
The HTML code in the in-app browser control is only visible if you make it so, otherwise the user simply does not know that it is an HTML or Obj-C app. It's up to you to make that distinction, if you want to. Assuming the code doesn't crash and show an obvious browser error, or something - but AFAIK, HTML/Javascript errors are suppressed within in-app browsers (which is quite handy actually.)
I've done extensive work with in-app browser controls and HTML and I have to say that (for example) if you were to build a publishing app (like a digital newspaper) you would probably want to go with HTML within Corona. I've done this (though not released to the world) and it worked like a charm, once the web overlay nuances are understood. I've also heard from other developers that there is great power to be had when coding within HTML nested within an app, whether it's Corona or Obj-C that you're using. For the cross platform nature, I'd go with Corona.
I've actually been thinking about posting my code which handles HTML within a web overlay (popup) control. I may yet do it...
Thanks. I am kind of leaning that way. Is it possible for me to test on a real iPhone device first before buying the subscription? I am relying on the simulator. I have a Mac and Dev account already. Been testing PhoneGap code on my phone.
Bubble ball has touch issues for instance. I have played many levels of that on my iPhone 4s and it is does have intermittent touch issues. It could also be just bad UI design also. It happens when object get near one another. I would like to make sure that it is not with any Corona app before opening my wallet.
No, it's not with any Corona app - it would be an app specific thing. My game, Tiltopolis, does not have that issue, I believe. You might see different (if you do, please let me know.)
Yes, you can deploy to devices without a paid license, but you will get an alert on every app load informing the user that the app is for testing only and you won't be able to build for the store without a license.
Yes, that has a clean and professional looking UI on Tiltopolis.
Just one small thing, the cubes tend to jump out of their tracks for a brief second as you move the phone to left to right. I don't think it effects the game playing but just looks a little unnatural physic behavior. So not a biggy. But other than that is works great. Thanks for the help.
Thanks :)
Yes, as with so many things, the 'math approximation' was very, very hard to fine tune and the route I eventually took gave me the best result this way. I have increased the physic engine's sensitivity to absorb the abnormalities, but they are visible, unfortunately.
What else do I have to do to replace the boxes with images? Just replacing them doesn't seem to work.
From:
1 2 | doll.head = display.newCircle( doll, 200, 100, 20 ); doll.head.radius = 20 |
To:
1 | doll.head = display.newImage("face_blank.png",200,100,true) |
What's the result when you try that?
The head picture shows up, but it's not connected to the body. The joint line gets the error:
1 | doll.chin = physics.newJoint("pivot", doll.head, doll.body,200,100) |
ERROR: physics.newJoint() requires argument 2 to have a physics body attached to the display object.
Somehow, the physics isn't being attached to the image head. I just added
1 | physics.addBody(doll.head,"dynamic",props) |
and it seems to be working now. Not sure why it's not getting physics as part of doll.
In the original post you are only creating an image and not adding it to the 'doll' display group. That's what the first parameter is for.
If you do:
1 2 3 4 5 6 | doll.head = display.newCircle( doll, 200, 100, 20 ); doll.head.radius = 20 doll:insert(doll.head) <lua> You'll find it should work because the image object will be iterated over in the following loop. |
Very nice work. Well done