Test12 - Pool Demo
From Flare3D - WIKI
package { import flare.basic.*; import flare.core.Lines3D; import flare.core.Pivot3D; import flare.physics.collision.*; import flare.physics.core.PhysicsMesh; import flare.physics.core.PhysicsSphere; import flare.physics.core.PhysicsSystemManager; import flare.physics.core.RigidBody; import flare.physics.tools.Math3D; import flare.primitives.*; import flare.system.Input3D; import flare.utils.Vector3DUtils; import flash.display.*; import flash.events.*; import flash.geom.*; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; public class Test12_poolDemo extends Sprite { private var scene: Scene3D; private var physics: PhysicsSystemManager; private var whiteBallPhysics: RigidBody; private var resetPosition:Vector3D; private var resetPositionOthersBall:Vector.<Vector3D>; private var dirIndicator:Lines3D; private var focusedShow:MovieClip; private var stick:Pivot3D; [SWF(frameRate = 25, backgroundColor = 0xFFFFFF, width = 800, height = 600 )] public function Test12_poolDemo() { //Create the scene... //Load the visual representation of the Scene scene = new Viewer3D(this, "../resources/pool.f3d"); //Load the physical representation of the table scene.addChildFromFile("../resources/pool_geom.f3d"); //load the pool stick scene.addChildFromFile("../resources/poolStick.f3d"); //Define load complete event scene.addEventListener( Scene3D.COMPLETE_EVENT, completeEvent ); //Set the expected frame rate scene.frameRate = 30; //Stop playback of the scene scene.pause(); //Initialize the physics manager //Set the world dimensions. It's used for collisions classification physics = PhysicsSystemManager.getInstance(new Vector3D(400, 400, 400), 20); //Set the gravity acceleration //By default, it is set to 9.81 mts. As this demo use centimeters, so gravity is set to 981 cms. physics.gravity = new Vector3D(0, -981, 0); // Set Physics Solver Accuracy. The greater the accuracy, the slower the system physics.accuracy = PhysicsSystemManager.HIGH_ACCURACY; //Create a label to show instructions var controls: TextField = new TextField(); controls.autoSize = TextFieldAutoSize.LEFT; controls.defaultTextFormat = new TextFormat( "Helvetica" , 14, 0xffffff); addChild(controls); controls.htmlText = "ARROWS: Set shot direction & power (+SHIFT move faster, +CTRL move slower)" + String.fromCharCode(13) + "SPACE: Hit" + String.fromCharCode(13) + "R: Reset white ball" + String.fromCharCode(13) + "T: Reset all"; controls.x = 5; controls.y = 5; //Create the direction indicator dirIndicator = new Lines3D(); dirIndicator.lineStyle( 1, 0xFFFFFF ); scene.addChild(dirIndicator); //Variable to indicate when Scene has focus focusedShow = new MovieClip(); } private function completeEvent(e:Event):void { //Remove the complete listener scene.removeEventListener(Scene3D.COMPLETE_EVENT, completeEvent); //Hide the physics geometry of the table scene.getChildByName("cloth").visible = false; scene.getChildByName("rails").visible = false; scene.getChildByName("table").visible = false; //Get the reference to the stick stick = scene.getChildByName("poolStick.f3d"); //Get parts of the 3D Table and balls, and assign Restitution/Friction real properties: // // The restitution value indicate how much velocity is lost when two objects collide: // value equals to 0 represents a plastic collision (all velocity is lost, the object not bounce) and // value equals to 1 represents a elastic collision (all velocity is conservated, the object has max bounce) // NOTE: the final restitution is 0.5 * (restitution_object1 + restitution_object2) // The friction property indicates how this object displace over other objects // The greater the friction then less the displacement // NOTE: the final friction is 0.5 * (friction_object1 + friction_object2) // According this article (http://billiards.colostate.edu/threads/physics.html) we define the material properties var cloth:Pivot3D = scene.getChildByName("cloth"); //Create a physical representation (a TriangleMesh) for the cloth cloth.addComponent( new PhysicsMesh() ); (cloth.components[0] as RigidBody).restitution = 0.05; (cloth.components[0] as RigidBody).friction = 0.55; var rails:Pivot3D = scene.getChildByName("rails"); //Create a physical representation (a TriangleMesh) for the rails of the table rails.addComponent( new PhysicsMesh() ); (rails.components[0] as RigidBody).restitution = 0.45; (rails.components[0] as RigidBody).friction = 0.95; var holes:Pivot3D = scene.getChildByName("table"); //Create a physical representation (a TriangleMesh) for the rest of table (as holes) holes.addComponent( new PhysicsMesh() ); (holes.components[0] as RigidBody).restitution = 0.05; (holes.components[0] as RigidBody).friction = 0.5; //Get the white ball reference var whiteBall:Pivot3D = scene.getChildByName("poolballq"); //Create a physical representation (a Sphere) for the white ball whiteBall.addComponent(new PhysicsSphere()); whiteBallPhysics = whiteBall.components[0] as RigidBody; whiteBallPhysics.mass = 194; //Set weight of the ball whiteBallPhysics.restitution = 0.95; //Set restitution property of the ball whiteBallPhysics.friction = 0.05; //Set friction property of the ball //Set max velocity to prevents penetrations whiteBallPhysics.maxLinVelocities.setTo(500, 500, 500); //Save the initial position for white ball for reset resetPosition = whiteBall.getPosition(); //Create the other balls physical representation resetPositionOthersBall = new Vector.<Vector3D>(15); for (var i:int = 1; i <= 15; i++ ) { var tmp:Pivot3D = scene.getChildByName("poolball" + i); tmp.addComponent(new PhysicsSphere()); (tmp.components[0] as RigidBody).mass = 160; //Set weight of the ball (tmp.components[0] as RigidBody).restitution = 0.95; //Set restitution property of the ball (tmp.components[0] as RigidBody).friction = 0.05; //Set friction property of the ball //Save its initial position for reset resetPositionOthersBall[i - 1] = tmp.getPosition(); } // Canvas color and size focusedShow.graphics.beginFill(0x000000, 0.6); focusedShow.graphics.drawRect(0,0,scene.viewPort.width,scene.viewPort.height); focusedShow.graphics.endFill(); stage.addChild(focusedShow); //Define an update event scene.addEventListener( Scene3D.UPDATE_EVENT, updateEvent ); //Start playback of the scene scene.resume(); } private function updateEvent(e:Event):void { // Reset white ball positions (event if it's moving) if ( Input3D.keyDown( Input3D.R ) ) resetWhiteBall(); // Reset position for all balls(event if it's moving) if ( Input3D.keyDown( Input3D.T ) ) { //reset others balls for (var i:int = 1; i <= resetPositionOthersBall.length; i++ ) { var tmp:Pivot3D = scene.getChildByName("poolball" + i); //get ball reference (tmp.components[0] as RigidBody).setPosition(resetPositionOthersBall[i - 1].x, resetPositionOthersBall[i - 1].y, resetPositionOthersBall[i - 1].z); //copy position from reset position (tmp.components[0] as RigidBody).setLinearVelocity(Vector3DUtils.ZERO); //Set linear velocity to zero (tmp.components[0] as RigidBody).setAngularVelocity(Vector3DUtils.ZERO); //Set angle velocity to zero (tmp.components[0] as RigidBody).setActive(true); //Set active for next physics step } //Reset white ball resetWhiteBall(); } //If the application does not has focus, pause the physics update focusedShow.visible = physics.paused; //Handle shot //Check if I can hit white ball if (canHitBall()) { configureShot(); } //Update physics engine physics.step(); } //Put white ball on the start point private function resetWhiteBall(): void { whiteBallPhysics.setPosition(resetPosition.x, resetPosition.y, resetPosition.z); whiteBallPhysics.setLinearVelocity(Vector3DUtils.ZERO); whiteBallPhysics.setAngularVelocity(Vector3DUtils.ZERO); whiteBallPhysics.setActive(true); } //Check if it's posible hit the white ball. //If the ball is still moving, I can't hit private function canHitBall():Boolean { //If white ball speed is lower than 0.15 cm/s, then it is stopped (to avoid rounding errors); or is into a hole or outside the table if (Math.abs(whiteBallPhysics.getVelocity().length) < 0.15 || whiteBallPhysics.y < 75) { //reset others balls for (var i:int = 1; i <= resetPositionOthersBall.length; i++ ) { var tmp:Pivot3D = scene.getChildByName("poolball" + i); //Again, if speed is lower than 0.15 cm/s, then it is stopped; or is into a hole or outside the table if (Math.abs((tmp.components[0] as RigidBody).getVelocity().length) > 0.15 && (tmp.components[0] as RigidBody).y >= 75) { return false; } } } else return false; return true; } private var angleShot:Number = 0; private var powerShot:Number = 0.5; private var multiplier:Number; private var directionVector:Vector3D = new Vector3D(); private var posStick:Vector3D = new Vector3D(); private function configureShot():void { //Check if the white ball goes into a hole or outside the table (height is lower than cloth height). If it's true, I must reset its position. if (whiteBallPhysics.y < 75) { resetWhiteBall(); angleShot = 0; powerShot = 0.5; } //Show the pool stick stick.visible = true; //Process these key events... //Change shot indicator speed (faster or slower) if ( Input3D.keyDown( Input3D.SHIFT ) ) multiplier = 10; else if ( Input3D.keyDown( Input3D.CONTROL ) ) multiplier = 0.2; else multiplier = 1; //Set shot direction if ( Input3D.keyDown( Input3D.RIGHT ) ) angleShot += 0.4 * multiplier; if ( Input3D.keyDown( Input3D.LEFT ) ) angleShot -= 0.4 * multiplier; //Set shot power if ( Input3D.keyDown( Input3D.DOWN ) ) powerShot = Math.min(powerShot + (0.005) * multiplier, 1.0); if ( Input3D.keyDown( Input3D.UP ) ) powerShot = Math.max(powerShot - (0.005) * multiplier, 0.1); //Calculate the shot direction directionVector.setTo( -1, 0, 0); directionVector.normalize(); directionVector = Math3D.getRotationMatrix(0, 1, 0, angleShot).transformVector(directionVector); //Render the shot Indicator (GUI) dirIndicator.clear(); dirIndicator.moveTo(whiteBallPhysics.x, whiteBallPhysics.y, whiteBallPhysics.z); dirIndicator.lineTo(whiteBallPhysics.x + directionVector.x * powerShot * 100, whiteBallPhysics.y, whiteBallPhysics.z + directionVector.z * powerShot * 100); dirIndicator.draw(); //Set stick position and orientation posStick.setTo(0, 0, 0); posStick.x += -directionVector.x * powerShot * 20; posStick.y += -directionVector.y * powerShot * 20; posStick.z += -directionVector.z * powerShot * 20; stick.setRotation(0, angleShot, 0); stick.rotateZ(7.5); stick.setPosition(whiteBallPhysics.x + posStick.x, whiteBallPhysics.y + posStick.y, whiteBallPhysics.z + posStick.z); //Do the SHOT. Apply a linear velocity to white ball (0~500 cm/s) if ( Input3D.keyHit( Input3D.SPACE ) ) { //Calculate the magnitude of velocity directionVector.scaleBy(powerShot * 500); //Apply the velocity to white ball whiteBallPhysics.setLinearVelocity(directionVector); //Clear direction indicator dirIndicator.clear(); //Hide the pool stick stick.visible = false; } } } }