flipside双人旋转赛车物理原理解析
flipside双人旋转赛车物理原理解析
从源码看,flipside的物理技术由box2d开源物理引擎提供

目前重点怀疑,游戏的赛车物理控制代码在以下包中

怀疑com.nitrome.flipside.Car是赛车的类
public function getExtrapolatedPosition(param1:Number)
获得外推力位置
package com.nitrome.flipside { import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Common.Math.*; import Box2D.Dynamics.*; import Box2D.Dynamics.Contacts.*; import Box2D.Dynamics.Joints.*; import com.nitrome.game.Score; import com.nitrome.util.Global; import com.nitrome.util.Key; import flash.geom.ColorTransform; import flash.ui.Keyboard; public class Car extends ActiveObject { public static const DESTROYED_ANIM_DURATION:Number = 40; public static const RESTORED_ANIM_DURATION:Number = 40; public static const MAG_TIME:Number = 32; public var index:Number; public var wheel1:b2Body; public var wheel2:b2Body; public var axle:b2Body; public var pin1:b2RevoluteJoint; public var pin2:b2RevoluteJoint; public var lightTrail:LightTrail; public var lightTrail2:LightTrail; private const wheelRadius:Number = 0.515625; private const axleLength:Number = 1.90625; private const axleThickness:Number = 0.2; public var wheelSpin:Number = 0; public var playerTorque:Number = 0.15; public var playerMaxSpin:Number = 32; public var playerDownforceCutoff:Number = 12; public var boostTime:Number = 0; public var destroyedTime:Number = 0; public var restoreTime:Number = 0; public var touchPoint1:b2Vec2 = null; public var touchPoint2:b2Vec2 = null; public var touchBody1:b2Body = null; public var touchBody2:b2Body = null; public var touching1:Boolean = false; public var touching2:Boolean = false; public var touchTime1:Number = 0; public var touchTime2:Number = 0; public var tileTouchLastFrame:Number = 0; public var aiInstruction:String = "r"; public var aiInstructionTime:Number = -1; public var aiRubberbandEnabled:Boolean = true; public var centerCellX:Number = -1; public var centerCellY:Number = -1; public var riderFlip:Boolean = false; public var riderFrame:Number = 1; public var riderAngle:Number = 0; public var riderAngleTarget:Number = 0; public var downforceDisabled:Number = 0; public var linearPosition:Number = 0; public var nextGate:Gate = null; public var lap:Number = 0; public var maxLap:Number = 0; public var rank:Number; public var finished:Boolean = false; public var finishFrameNumber:Number = 0; public var lastFrameNumber:Number = 0; public var ignoreUpKey:Boolean = false; public var instructionsVisibility:Number = 0; public function Car(param1:Number, param2:Number) { var _loc10_:Number = NaN; super(); Controller.cars.push(this); index = Controller.cars.length - 1; var _loc3_:b2BodyDef = new b2BodyDef(); _loc3_.userData = { "car":this, "activeObject":this, "contactMade":true, "contactHeld":true, "contactLost":true }; var _loc4_:Number = (param1 + 0.5) * 64 / 32; var _loc5_:Number = param2 * 64 / 32; _loc5_ = _loc5_ + (2 - wheelRadius); _loc3_.position.Set(_loc4_ - 0.75,_loc5_); wheel1 = makeBody(_loc3_); _loc3_.position.Set(_loc4_ + 0.75,_loc5_); wheel2 = makeBody(_loc3_); _loc3_.position.Set(_loc4_,_loc5_); axle = makeBody(_loc3_); var _loc6_:b2CircleDef = new b2CircleDef(); _loc6_.radius = wheelRadius; _loc6_.density = 1; _loc6_.friction = 1; _loc6_.restitution = 0.3; _loc6_.filter.categoryBits = 2; wheel1.CreateShape(_loc6_); wheel2.CreateShape(_loc6_); wheel1.SetMassFromShapes(); wheel2.SetMassFromShapes(); var _loc7_:b2PolygonDef = new b2PolygonDef(); _loc7_.SetAsBox(axleLength / 2,axleThickness / 2); _loc7_.density = 1; _loc7_.friction = 0.8; _loc7_.restitution = 0.3; _loc7_.filter.categoryBits = 2; axle.CreateShape(_loc7_); axle.SetMassFromShapes(); var _loc8_:b2RevoluteJointDef = new b2RevoluteJointDef(); _loc8_.body1 = wheel1; _loc8_.body2 = axle; _loc8_.localAnchor1 = new b2Vec2(0,0); _loc8_.localAnchor2 = new b2Vec2(-axleLength / 2,0); _loc8_.collideConnected = false; pin1 = makeJoint(_loc8_) as b2RevoluteJoint; var _loc9_:b2RevoluteJointDef = new b2RevoluteJointDef(); _loc9_.body1 = wheel2; _loc9_.body2 = axle; _loc9_.localAnchor1 = new b2Vec2(0,0); _loc9_.localAnchor2 = new b2Vec2(axleLength / 2,0); _loc9_.collideConnected = false; pin2 = makeJoint(_loc9_) as b2RevoluteJoint; Viewport.prerender("bike" + index + "Wheel"); Viewport.prerender("bike" + index + "WheelLines"); Viewport.prerender("bike" + index + "Rider"); Viewport.prerender("bike" + index + "Chassis"); Viewport.prerender("bike" + index + "ChassisNumbers"); Viewport.prerender("bike" + index + "ChassisShine"); Viewport.prerender("bikeArrows"); nextGate = Controller.level.firstGate; lightTrail = new LightTrail(); lightTrail2 = new LightTrail(); switch(index) { case 0: _loc10_ = 65535; break; case 1: _loc10_ = 14869218; break; case 2: _loc10_ = 3407743; break; case 3: _loc10_ = 13526514; } lightTrail.setColor(_loc10_); lightTrail2.setColor(_loc10_); } override public function advance() : void { if(destroyedTime > 0) { beingDestroyed(); } else if(restoreTime > 0) { beingRestored(); } controlling(); rubberbanding(); gatetracking(); motion(); animation(); lastFrameNumber = Controller.frameNumber; } public function beingDestroyed() : void { var _loc1_:b2Vec2 = null; var _loc2_:Number = NaN; var _loc3_:Number = NaN; var _loc4_:Car = null; var _loc5_:Number = NaN; var _loc6_:Number = NaN; var _loc7_:Number = NaN; axle.SetLinearVelocity(new b2Vec2(0,0)); wheel1.SetLinearVelocity(new b2Vec2(0,0)); wheel2.SetLinearVelocity(new b2Vec2(0,0)); axle.SetAngularVelocity((Math.random() - 0.5) * 100); wheel1.SetAngularVelocity(0); wheel2.SetAngularVelocity(0); if(destroyedTime < 2) { _loc1_ = nextGate.lastGate.centerPoint; _loc2_ = (axleLength + wheelRadius) * 0.7; _loc3_ = 0; while(_loc3_ < Controller.cars.length) { _loc4_ = Controller.cars[_loc3_]; if(_loc4_ != this) { _loc5_ = _loc4_.getCenterX() - _loc1_.x; _loc6_ = _loc4_.getCenterY() - _loc1_.y; _loc7_ = _loc5_ * _loc5_ + _loc6_ * _loc6_; if(_loc7_ < _loc2_ * _loc2_) { return; } } _loc3_++; } destroyedTime = 0; axle.SetXForm(_loc1_.Copy(),0); wheel1.SetXForm(new b2Vec2(_loc1_.x - axleLength / 2,_loc1_.y),0); wheel2.SetXForm(new b2Vec2(_loc1_.x + axleLength / 2,_loc1_.y),0); axle.SetAngularVelocity(0); } else { --destroyedTime; } } public function beingRestored() : void { --restoreTime; } public function controlling() : void { var _loc5_:Number = NaN; var _loc6_:Number = NaN; var _loc7_:String = null; var _loc9_:Number = NaN; var _loc10_:ActiveObject = null; var _loc11_:b2Vec2 = null; var _loc1_:* = false; var _loc2_:* = false; var _loc3_:Boolean = false; if(index == 0 && !finished && !Controller.demoGame) { _loc1_ = Key.isDown(Keyboard.LEFT); _loc2_ = Key.isDown(Keyboard.RIGHT); _loc3_ = Key.isDown(Keyboard.UP); if(!Controller.multiplayerGame) { _loc1_ ||= Key.isDown("A".charCodeAt(0)); _loc2_ ||= Key.isDown("D".charCodeAt(0)); _loc3_ ||= Key.isDown("W".charCodeAt(0)); } } else if(index == 1 && Controller.multiplayerGame && !finished && !Controller.demoGame) { _loc1_ = Key.isDown("A".charCodeAt(0)); _loc2_ = Key.isDown("D".charCodeAt(0)); _loc3_ = Key.isDown("W".charCodeAt(0)); } else { _loc5_ = Math.floor(axle.GetPosition().x * 32 / 64); _loc6_ = Math.floor(axle.GetPosition().y * 32 / 64); if(_loc5_ != centerCellX || _loc6_ != centerCellY) { if(Controller.level.instructionGrid[_loc5_][_loc6_]) { var _loc8_:String = Controller.level.instructionGrid[_loc5_][_loc6_]; if(_loc8_ == "be" || null == "bd") { aiRubberbandEnabled = null == "be"; } else if(null != aiInstruction) { aiInstruction = null; aiInstructionTime = Controller.frameNumber; } } } _loc7_ = aiInstruction; if(_loc7_.substr(0,1) == "i") { aiInstruction = "i r"; _loc9_ = 0; while(_loc9_ < Controller.level.activeList.length) { _loc10_ = Controller.level.activeList[_loc9_]; if(_loc10_.transmitsAI) { if(_loc10_.inheritedAI(this)) { break; } } _loc9_++; } _loc7_ = aiInstruction.substr(2); } if(_loc7_ == "r") { _loc1_ = false; _loc2_ = true; _loc3_ = false; } else if(_loc7_ == "l") { _loc1_ = true; _loc2_ = false; _loc3_ = false; } else if(_loc7_ == "rf") { _loc2_ = forwardSpeed() > playerDownforceCutoff / 2; _loc1_ = !_loc2_; _loc3_ = false; } else if(_loc7_ == "lf") { _loc1_ = forwardSpeed() < -playerDownforceCutoff / 2; _loc2_ = !_loc1_; _loc3_ = false; } else if(_loc7_ == "ls") { _loc1_ = wheelSpin > -8; _loc2_ = !_loc1_; _loc3_ = false; } else if(_loc7_ == "rs") { _loc2_ = wheelSpin < 8; _loc1_ = !_loc2_; _loc3_ = false; } else if(_loc7_ == "n") { _loc1_ = _loc2_ = _loc3_ = false; } else if(_loc7_ == "j") { _loc1_ = _loc2_ = false; _loc3_ = true; } else if(_loc7_ == "jl") { if(forwardSpeed() < 0) { _loc3_ = true; } else { _loc2_ = true; } } else if(_loc7_ == "jr") { if(forwardSpeed() > 0) { _loc3_ = true; } else { _loc1_ = true; } } else { trace("UNRECOGNISED INSTRUCTION: [" + aiInstruction + "]"); } } if(Controller.introTime < Controller.INTRO_DURATION) { _loc1_ = _loc2_ = _loc3_ = false; } if(_loc3_ && !ignoreUpKey) { if(touchTime1 <= 2 && touchTime2 <= 2) { _loc11_ = axle.GetWorldVector(new b2Vec2(0,isInverted() ? 350 * 3 : -350 * 3)); wheel1.ApplyForce(_loc11_,wheel1.GetWorldCenter()); wheel2.ApplyForce(_loc11_,wheel2.GetWorldCenter()); touchTime1 = MAG_TIME + 2; touchTime2 = MAG_TIME + 2; downforceDisabled = 20; ignoreUpKey = true; } } else if(!_loc3_) { ignoreUpKey = false; } if(boostTime > 0) { --boostTime; } var _loc4_:Number = 0; if(_loc2_) { _loc4_ = playerMaxSpin; wheelSpin = Math.max(wheelSpin,wheel1.m_angularVelocity); } else if(_loc1_) { _loc4_ = -playerMaxSpin; wheelSpin = Math.min(wheelSpin,wheel1.m_angularVelocity); } wheelSpin = Global.slide(wheelSpin,_loc4_,playerTorque); if(_loc4_ != 0) { wheel1.SetAngularVelocity(wheelSpin); wheel2.SetAngularVelocity(wheelSpin); } else { if(wheel1.IsSleeping() && wheel2.IsSleeping() && axle.IsSleeping()) { return; } wheel1.m_angularVelocity = wheel2.m_angularVelocity = Global.slide(wheel1.m_angularVelocity,0,0.1); } wheel2.SetXForm(wheel2.GetPosition(),wheel1.GetAngle()); axle.ApplyTorque(_loc4_ / 5); } public function rubberbanding() : void { var _loc1_:Number = NaN; linearPosition = nextGate.fromLast(axle.GetPosition()); linearPosition = (linearPosition + nextGate.lastGate.index) / Controller.level.gates.length; linearPosition += lap - 1; playerTorque = 0.15; playerMaxSpin = 32; playerDownforceCutoff = 12; if(index != 0 && Controller.cars[0].rank && !Controller.multiplayerGame && aiRubberbandEnabled) { _loc1_ = linearPosition - Controller.cars[0].linearPosition; if(_loc1_ > 0.2) { if(_loc1_ > 1) { _loc1_ = 1; } playerTorque = 0.15 - _loc1_ * 0.1; playerMaxSpin = 32 - _loc1_ * 10; playerDownforceCutoff = 12 + _loc1_ * 2; } else if(_loc1_ < -0.2) { if(_loc1_ < -1) { _loc1_ = -1; } playerTorque = 0.15 + -_loc1_ * 0.15; playerMaxSpin = 32 + -_loc1_ * 10; playerDownforceCutoff = 12 - -_loc1_ * 4; } } if(boostTime > 0) { playerMaxSpin += 4; playerTorque += 0.05; } } public function gatetracking() : void { var _loc1_:b2Vec2 = null; var _loc2_:String = null; if(nextGate) { _loc1_ = axle.GetPosition(); if(nextGate.after(_loc1_) && nextGate.aligned(_loc1_)) { if(nextGate.isFirst && !finished && !Controller.demoGame) { ++lap; if(lap == Controller.level.totalLaps + 1) { if(index == 0) { Controller.overlay.doOverlay("finish"); } else if(index == 1 && Controller.multiplayerGame) { Controller.overlay2.doOverlay("finish"); } justFinished(); } else if(lap == maxLap + 1 && lap > 1) { _loc2_ = "lap" + lap.toString(); if(lap == Controller.level.totalLaps) { _loc2_ = "finallap"; } if(index == 0) { Controller.overlay.doOverlay(_loc2_); } else if(index == 1 && Controller.multiplayerGame) { Controller.overlay2.doOverlay(_loc2_); } } if(lap > maxLap) { maxLap = lap; } } nextGate = nextGate.nextGate; } if(!nextGate.lastGate.after(_loc1_) && nextGate.lastGate.aligned(_loc1_)) { if(nextGate.lastGate.isFirst && !finished) { --lap; } nextGate = nextGate.lastGate; } } } public function justFinished() : void { if(finished) { return; } finished = true; finishFrameNumber = Controller.frameNumber; if(Controller.multiplayerGame) { if(rank == 2) { if(index == 1) { ++Controller.player1Score; } else { ++Controller.player2Score; } } } else if(index == 0) { if(rank == 1) { Score.value += Controller.levelScore(); NitromeGame.setLevelUnlocked(Controller.level.number + 1); } } } public function motion() : void { var _loc4_:b2Vec2 = null; var _loc5_:Array = null; var _loc6_:Number = NaN; var _loc7_:b2Body = null; var _loc8_:b2Vec2 = null; var _loc9_:b2Vec2 = null; if(touching1) { touchTime1 = 0; } else { ++touchTime1; } if(touching2) { touchTime2 = 0; } else { ++touchTime2; } if(wheel1.IsSleeping() && wheel2.IsSleeping() && axle.IsSleeping()) { return; } var _loc1_:b2Vec2 = wheel2.GetWorldCenter().Copy(); _loc1_.Subtract(wheel1.GetWorldCenter()); _loc1_.Normalize(); var _loc2_:b2Vec2 = axle.GetLinearVelocity(); var _loc3_:Number = b2Math.b2Dot(_loc1_,_loc2_); --downforceDisabled; if(Math.abs(_loc3_) > playerDownforceCutoff && downforceDisabled < 0 && touchPoint1 && Boolean(touchPoint2)) { _loc4_ = new b2Vec2(-_loc1_.y,_loc1_.x); if(b2Math.b2Dot(_loc4_,touchPoint1) < b2Math.b2Dot(_loc4_,wheel1.GetPosition())) { _loc4_.Multiply(-1); } _loc5_ = [wheel1,wheel2]; _loc6_ = 33 * 0.5 / 32; for each(_loc7_ in _loc5_) { _loc8_ = _loc4_.Copy(); _loc8_.Multiply(_loc6_ + 0.5); _loc8_.Add(_loc7_.GetPosition()); if(surfaceAtPoint(_loc8_)) { _loc9_ = _loc4_.Copy(); _loc9_.Multiply(20); applyGravity(_loc7_,_loc9_); } } } applyGravity(wheel1); applyGravity(wheel2); applyGravity(axle); } public function animation() : void { var _loc1_:Number = forwardSpeed(); if(_loc1_ < playerDownforceCutoff || touchTime1 > MAG_TIME || touchTime2 > MAG_TIME) { if(Math.abs(axle.GetLinearVelocity().x) > 2 && Math.abs(_loc1_) > 2) { riderFlip = forwardSpeed() < 0; } if(isInverted()) { riderAngleTarget = 180; } else { riderAngleTarget = 0; } } riderAngle = (riderAngle - riderAngleTarget) * 0.95 + riderAngleTarget; if(Controller.frameNumber != lastFrameNumber) { riderFrame = Global.slide(riderFrame,riderFlip ? 5 : 1,1); } var _loc2_:Boolean = isInverted(); var _loc3_:b2Vec2 = (_loc1_ > 0 != _loc2_ ? wheel1 : wheel2).GetPosition().Copy(); _loc3_.Add(axle.GetWorldVector(new b2Vec2(0,_loc2_ ? 0.2 : -0.2))); lightTrail.pushPosition(_loc3_,Math.abs(_loc1_ / 16)); lightTrail.length = Global.slide(lightTrail.length,Math.abs(60 * _loc1_ / 16),2); _loc3_ = (_loc1_ > 0 != _loc2_ ? wheel1 : wheel2).GetPosition().Copy(); _loc3_.Add(axle.GetWorldVector(new b2Vec2(0,_loc2_ ? -0.2 : 0.2))); lightTrail2.pushPosition(_loc3_,Math.abs(_loc1_ / 16)); lightTrail2.length = Global.slide(lightTrail2.length,Math.abs(32 * _loc1_ / 16),2); if(Controller.introTime > 50 && Controller.introTime < 110 && index == 0 && !Controller.demoGame && !Controller.multiplayerGame) { instructionsVisibility = 2; } else if(instructionsVisibility > 0) { instructionsVisibility -= 0.02; } } public function surfaceAtPoint(param1:b2Vec2) : b2Shape { var _loc4_:b2Shape = null; var _loc2_:b2AABB = new b2AABB(); _loc2_.lowerBound.Set(param1.x - 0.01,param1.y - 0.01); _loc2_.upperBound.Set(param1.x + 0.01,param1.y + 0.01); var _loc3_:Array = new Array(); if(Controller.world.Query(_loc2_,_loc3_,1000) == 0) { return null; } for each(_loc4_ in _loc3_) { if(_loc4_.TestPoint(_loc4_.m_body.GetXForm(),param1)) { return _loc4_; } } return null; } public function isInverted() : Boolean { if(!touchPoint1 || !touchPoint2) { return false; } var _loc1_:b2Vec2 = wheel2.GetPosition().Copy(); _loc1_.Subtract(wheel1.GetPosition()); _loc1_.Normalize(); var _loc2_:b2Vec2 = touchPoint1.Copy(); _loc2_.Add(touchPoint2); _loc2_.Multiply(0.5); var _loc3_:b2Vec2 = new b2Vec2(-_loc1_.y,_loc1_.x); return b2Math.b2Dot(_loc3_,wheel1.GetPosition()) > b2Math.b2Dot(_loc3_,_loc2_); } public function forwardSpeed() : Number { var _loc1_:b2Vec2 = wheel2.GetPosition().Copy(); _loc1_.Subtract(wheel1.GetPosition()); _loc1_.Normalize(); var _loc2_:b2Vec2 = new b2Vec2(-_loc1_.y,_loc1_.x); var _loc3_:Boolean = touchPoint1 ? b2Math.b2Dot(_loc2_,wheel1.GetPosition()) > b2Math.b2Dot(_loc2_,touchPoint1) : false; var _loc4_:Number = b2Math.b2Dot(axle.GetLinearVelocity(),_loc1_); return isInverted() ? -_loc4_ : _loc4_; } public function getCenterX() : Number { return axle.GetWorldCenter().x; } public function getCenterY() : Number { return axle.GetWorldCenter().y; } public function getPosition() : b2Vec2 { return axle.GetPosition(); } public function getVelocity() : b2Vec2 { return axle.GetLinearVelocity(); } public function getNearbyCars(param1:Number) : Array { var _loc3_:Car = null; var _loc4_:b2Vec2 = null; var _loc2_:Array = []; for each(_loc3_ in Controller.cars) { if(_loc3_ != this) { _loc4_ = axle.GetPosition().Copy(); _loc4_.Subtract(_loc3_.axle.GetPosition()); if(_loc4_.LengthSquared() < param1 * param1) { _loc2_.push(_loc3_); } } } return _loc2_; } public function getExtrapolatedPosition(param1:Number) : b2Vec2 { var _loc2_:b2Vec2 = axle.GetPosition().Copy(); var _loc3_:b2Vec2 = axle.GetLinearVelocity().Copy(); _loc3_.Multiply(param1); _loc2_.Add(_loc3_); return _loc2_; } override public function contactMade(param1:b2ContactPoint) : void { var _loc2_:b2Shape = null; var _loc3_:b2Shape = null; var _loc4_:Car = null; if(param1.shape1.m_body == wheel1 || param1.shape1.m_body == wheel2) { _loc2_ = param1.shape1; _loc3_ = param1.shape2; } else { if(!(param1.shape2.m_body == wheel1 || param1.shape2.m_body == wheel2)) { return; } _loc2_ = param1.shape2; _loc3_ = param1.shape1; } if(_loc2_.m_body == wheel1) { touchPoint1 = param1.position; touchBody1 = _loc3_.m_body; touching1 = true; } else { touchPoint2 = param1.position; touchBody2 = _loc3_.m_body; touching2 = true; } if(_loc3_.m_body.m_userData && Boolean(_loc3_.m_body.m_userData.tile)) { tileTouchLastFrame = Controller.frameNumber; } if(_loc3_.m_body.m_userData && Boolean(_loc3_.m_body.m_userData.car)) { _loc4_ = _loc3_.m_body.m_userData.car as Car; if(aiInstructionTime < _loc4_.aiInstructionTime) { aiInstruction = _loc4_.aiInstruction; aiInstructionTime = _loc4_.aiInstructionTime; } else { _loc4_.aiInstruction = aiInstruction; _loc4_.aiInstructionTime = aiInstructionTime; } } } override public function contactHeld(param1:b2ContactPoint) : void { var _loc2_:b2Shape = null; var _loc3_:b2Shape = null; if(param1.shape1.m_body == wheel1 || param1.shape1.m_body == wheel2) { _loc2_ = param1.shape1; _loc3_ = param1.shape2; } else { if(!(param1.shape2.m_body == wheel1 || param1.shape2.m_body == wheel2)) { return; } _loc2_ = param1.shape2; _loc3_ = param1.shape1; } if(_loc2_.m_body == wheel1) { touchPoint1 = param1.position; touchBody1 = _loc3_.m_body; touching1 = true; } else { touchPoint2 = param1.position; touchBody2 = _loc3_.m_body; touching2 = true; } if(_loc3_.m_body.m_userData && Boolean(_loc3_.m_body.m_userData.tile)) { tileTouchLastFrame = Controller.frameNumber; } } override public function contactLost(param1:b2ContactPoint) : void { var _loc2_:b2Shape = null; var _loc3_:b2Shape = null; if(param1.shape1.m_body == wheel1 || param1.shape1.m_body == wheel2) { _loc2_ = param1.shape1; _loc3_ = param1.shape2; } else { if(!(param1.shape2.m_body == wheel1 || param1.shape2.m_body == wheel2)) { return; } _loc2_ = param1.shape2; _loc3_ = param1.shape1; } if(_loc2_.m_body == wheel1) { if(_loc3_.m_body == touchBody1) { touching1 = false; } } else if(_loc3_.m_body == touchBody2) { touching2 = false; } } public function hitLaser() : void { if(destroyedTime == 0) { destroyedTime = DESTROYED_ANIM_DURATION; restoreTime = RESTORED_ANIM_DURATION; if(Controller.onScreen(axle.GetPosition().x * 32,axle.GetPosition().y * 32)) { NitromeGame.sound_manager.playSound("laser"); } } } override public function render(param1:Viewport) : void { var _loc11_:b2Body = null; var _loc12_:b2Vec2 = null; var _loc13_:Number = NaN; var _loc14_:Number = NaN; var _loc2_:Number = Math.floor(axle.GetPosition().x * 32) - param1.scrollX; var _loc3_:Number = Math.floor(axle.GetPosition().y * 32) - param1.scrollY; if(_loc2_ < -100) { return; } if(_loc2_ > 650) { return; } if(_loc3_ < -100) { return; } if(_loc3_ > 500) { return; } var _loc4_:Number = axle.GetAngle(); var _loc5_:ColorTransform = null; if(destroyedTime > 0) { if(destroyedTime == 1) { _loc5_ = Global.whiteOut(0); } else { _loc5_ = Global.whiteOut(destroyedTime / DESTROYED_ANIM_DURATION); } } else if(restoreTime > 0) { _loc5_ = Global.whiteOut(1 - restoreTime / RESTORED_ANIM_DURATION); } var _loc6_:Number = 0; while(_loc6_ < 2) { _loc11_ = _loc6_ == 0 ? wheel1 : wheel2; _loc12_ = axle.GetWorldPoint(new b2Vec2((_loc6_ == 0 ? 31 : -32) / 32,0)); _loc13_ = Math.floor(_loc12_.x * 32) - param1.scrollX; _loc14_ = Math.floor(_loc12_.y * 32) - param1.scrollY; param1.drawRotated("bike" + index + "Wheel",_loc13_,_loc14_,0,_loc5_); param1.drawRotated("bike" + index + "WheelLines",_loc13_,_loc14_,_loc11_.GetAngle(),_loc5_); _loc6_++; } var _loc7_:Number = axle.GetAngle() % (Math.PI * 2); if(_loc7_ < -Math.PI) { _loc7_ += Math.PI * 2; } if(_loc7_ > Math.PI) { _loc7_ -= Math.PI * 2; } var _loc8_:Number = 1 - Math.abs(_loc7_ / Math.PI); var _loc9_:ColorTransform = new ColorTransform(1,1,1,_loc8_); if(_loc5_) { _loc9_.concat(_loc5_); } param1.drawRotated("bike" + index + "Chassis",_loc2_,_loc3_,_loc4_ + Math.PI,_loc5_); param1.drawRotated("bike" + index + "Chassis",_loc2_,_loc3_,_loc4_,_loc9_); param1.drawRotated("bike" + index + "ChassisNumbers",_loc2_,_loc3_,_loc4_,_loc5_); param1.drawRotated("bike" + index + "Rider-" + riderFrame.toString(),_loc2_,_loc3_,_loc4_ + riderAngle * Math.PI / 180,_loc5_); param1.drawRotated("bike" + index + "ChassisShine",_loc2_,_loc3_,0,_loc5_,"lighten"); var _loc10_:Number = instructionsVisibility; if(_loc10_ > 1) { _loc10_ = 1; } if(_loc10_ > 0) { param1.drawRotated("bikeArrows",_loc2_,_loc3_,0,new ColorTransform(1,1,1,instructionsVisibility)); } } } }
通过分析源码,可以思考得出,双人旋转赛车的赛车对象由3个部分组成,2个轮子(物理体)+一个轴(物理体)
关于旋转物理关节的实现
var _loc8_:b2RevoluteJointDef = new b2RevoluteJointDef(); _loc8_.body1 = wheel1; _loc8_.body2 = axle; _loc8_.localAnchor1 = new b2Vec2(0,0); _loc8_.localAnchor2 = new b2Vec2(-axleLength / 2,0); _loc8_.collideConnected = false; pin1 = makeJoint(_loc8_) as b2RevoluteJoint; var _loc9_:b2RevoluteJointDef = new b2RevoluteJointDef(); _loc9_.body1 = wheel2; _loc9_.body2 = axle; _loc9_.localAnchor1 = new b2Vec2(0,0); _loc9_.localAnchor2 = new b2Vec2(axleLength / 2,0); _loc9_.collideConnected = false; pin2 = makeJoint(_loc9_) as b2RevoluteJoint;
浙公网安备 33010602011771号