В физическом движке Nape можно создавать произвольные тела на основании графического рисунка.
Графику для физики игр я подготавливаю в Paint.NET, который позволяет указывать так называемую маску рисунка, где прозрачный задний фон alpha=0, там отсутствует физические элементы тела.
Попробуем улучшить отображение нашей машинки из предыдущих примеров. Для начала нарисуем кузов размером 200 на 60 пикселей (при создании в Paint.NET нового спрайта по умолчанию он заливается непрозрачным белым цветом, необходимо его выделить и удалить, прозрачность отображается шахматной доской):
Сохраняем кузов машины в папку assets проекта. Для преобразования рисунков в физические тела в Nape можно использовать следующие файлы (копируем их в папку src проекта):
Файл IsoBody.as
Файл DisplayObjectIso.as
Файл BitmapDataIso.as
Модернизируем наш проект движения машинки, с учетом новых знаний:
Пример работы можно посмотреть тут.
Графику для физики игр я подготавливаю в Paint.NET, который позволяет указывать так называемую маску рисунка, где прозрачный задний фон alpha=0, там отсутствует физические элементы тела.
Попробуем улучшить отображение нашей машинки из предыдущих примеров. Для начала нарисуем кузов размером 200 на 60 пикселей (при создании в Paint.NET нового спрайта по умолчанию он заливается непрозрачным белым цветом, необходимо его выделить и удалить, прозрачность отображается шахматной доской):
Сохраняем кузов машины в папку assets проекта. Для преобразования рисунков в физические тела в Nape можно использовать следующие файлы (копируем их в папку src проекта):
Файл IsoBody.as
package { import nape.geom.AABB; import nape.geom.GeomPoly; import nape.geom.GeomPolyList; import nape.geom.IsoFunction; import nape.geom.MarchingSquares; import nape.geom.Vec2; import nape.phys.Body; import nape.shape.Polygon; public class IsoBody { public static function run(iso:IsoFunction, bounds:AABB, granularity:Vec2 = null, quality:int = 2, simplification:Number = 1.5):Body { var body:Body = new Body(); if (granularity == null) granularity = Vec2.weak(8, 8); var polys:GeomPolyList = MarchingSquares.run(iso, bounds, granularity, quality); for (var i:int = 0; i < polys.length; i++) { var p:GeomPoly = polys.at(i); var qolys:GeomPolyList = p.simplify(simplification).convexDecomposition(true); for (var j:int = 0; j < qolys.length; j++) { var q:GeomPoly = qolys.at(j); body.shapes.add(new Polygon(q)); // Recycle GeomPoly and its vertices q.dispose(); } // Recycle list nodes qolys.clear(); // Recycle GeomPoly and its vertices p.dispose(); } // Recycle list nodes polys.clear(); // Align body with its centre of mass. // Keeping track of our required graphic offset. var pivot:Vec2 = body.localCOM.mul(-1); body.translateShapes(pivot); body.userData.graphicOffset = pivot; return body; } } }
Файл DisplayObjectIso.as
package { import nape.geom.AABB; import flash.display.DisplayObject; public class DisplayObjectIso implements IsoFunction { public var displayObject:DisplayObject; public var bounds:AABB; public function DisplayObjectIso(displayObject:DisplayObject):void { this.displayObject = displayObject; this.bounds = AABB.fromRect(displayObject.getBounds(displayObject)); } public function iso(x:Number, y:Number):Number { // Best we can really do with a generic DisplayObject // is to return a binary value {-1, 1} depending on // if the sample point is in or out side. return (displayObject.hitTestPoint(x, y, true) ? -1.0 : 1.0); } } }
Файл BitmapDataIso.as
package { import nape.geom.AABB; import nape.geom.IsoFunction; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.DisplayObject; public class BitmapDataIso implements IsoFunction { public var bitmap:BitmapData; public var alphaThreshold:Number; public var bounds:AABB; public function BitmapDataIso(bitmap:BitmapData, alphaThreshold:Number = 0x80):void { this.bitmap = bitmap; this.alphaThreshold = alphaThreshold; bounds = new AABB(0, 0, bitmap.width, bitmap.height); } public function graphic():DisplayObject { return new Bitmap(bitmap); } public function iso(x:Number, y:Number):Number { // Take 4 nearest pixels to interpolate linearly. // This gives us a smooth iso-function for which // we can use a lower quality in MarchingSquares for // the root finding. var ix:int = int(x); var iy:int = int(y); //clamp in-case of numerical inaccuracies if(ix<0 data-blogger-escaped-if="if" data-blogger-escaped-ix="ix" data-blogger-escaped-iy="0;">=bitmap.width) ix = bitmap.width-1; if(iy>=bitmap.height) iy = bitmap.height-1; // iso-function values at each pixel centre. var a11:Number = alphaThreshold - (bitmap.getPixel32(ix,iy)>>>24); var a12:Number = alphaThreshold - (bitmap.getPixel32(ix+1,iy)>>>24); var a21:Number = alphaThreshold - (bitmap.getPixel32(ix,iy+1)>>>24); var a22:Number = alphaThreshold - (bitmap.getPixel32(ix+1,iy+1)>>>24); // Bilinear interpolation for sample point (x,y) var fx:Number = x - ix; var fy:Number = y - iy; return a11*(1-fx)*(1-fy) + a12*fx*(1-fy) + a21*(1-fx)*fy + a22*fx*fy; } } }
Модернизируем наш проект движения машинки, с учетом новых знаний:
package { import flash.display.Sprite; import flash.display.DisplayObject; import flash.events.Event; import nape.callbacks.BodyCallback; import nape.constraint.DistanceJoint; import nape.constraint.WeldJoint; import nape.phys.Compound; import nape.phys.Material; import nape.callbacks.CbType; // типы обратных вызовов import nape.callbacks.CbEvent; // события обратных вызовов import nape.callbacks.BodyListener; //слушатель для колеса import nape.geom.Vec2; import nape.phys.Body; import nape.phys.BodyType; import nape.shape.Circle; import nape.shape.Polygon; import nape.space.Space; import nape.util.BitmapDebug; import nape.util.Debug; import nape.shape.Shape; public class Main extends Sprite { private var space:Space; private var debug:Debug; private var wheel_front:Body; private var wheel_rear:Body; private var cabin:Body; //Ограничение для колес private var wheel_f_joint:DistanceJoint; //Держит расстояние между колес private var wheel_r_joint:DistanceJoint; private var cabin_front_joint:DistanceJoint; //Амортизатор спереди машины на сварку private var cabin_rear_joint:DistanceJoint; //Амортизатор сзади машины на сварку // Объединения private var car:Compound; // Обратные вызовы private var cbCarType:CbType; //Кузов [Embed(source="../assets/cabin.png")] private var Cabin:Class; //Колеса [Embed(source="../assets/wheel.png")] private var Wheel:Class; public function Main():void { super(); if (stage != null) { initialise(null); } else { addEventListener(Event.ADDED_TO_STAGE, initialise); } } private function initialise(ev:Event):void { if (ev != null) { removeEventListener(Event.ADDED_TO_STAGE, initialise); } var gravity:Vec2 = Vec2.weak(0, 900); space = new Space(gravity); debug = new BitmapDebug(stage.stageWidth, stage.stageHeight, stage.color); //Рисуем Ограничения debug.drawConstraints = true; addChild(debug.display); setUp(); stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler); } private function setUp():void { var w:int = stage.stageWidth; var h:int = stage.stageHeight; // Пол var floor:Body = new Body(BodyType.STATIC); floor.shapes.add(new Polygon(Polygon.regular(320, 100, 6, 0, false), Material.wood())); floor.position.setxy(320, 300); floor.space = space; // Объединение - машина car = new Compound(); // Колесо переднее wheel_front = new Body(BodyType.DYNAMIC); wheel_front.position.setxy(290, 106); var wheel_frontShape:Shape = new Circle(12, null, Material.rubber()); wheel_frontShape.body = wheel_front; //wheel_front.angularVel = 20; wheel_front.compound = car; // Колесо заднее wheel_rear = new Body(BodyType.DYNAMIC); wheel_rear.position.setxy(200, 106); var wheel_rearShape:Shape = new Circle(12, null, Material.rubber()); wheel_rearShape.body = wheel_rear; //wheel_rear.angularVel = 20; wheel_rear.compound = car; // Кузов var cabinIso:BitmapDataIso = new BitmapDataIso((new Cabin()).bitmapData, 0x80); cabin = IsoBody.run(cabinIso, cabinIso.bounds); cabin.mass = 10; cabin.position.setxy(245, 70); cabin.compound = car; //Ограничение для колес (держит межколесное растояние) wheel_f_joint = new DistanceJoint(wheel_front, cabin, wheel_front.localCOM, cabin.localCOM, 78, 78); wheel_f_joint.compound = car; wheel_r_joint = new DistanceJoint(wheel_rear, cabin, wheel_rear.localCOM, cabin.localCOM, 70, 70); wheel_r_joint.compound = car; //Передний амортизатор cabin_front_joint = new DistanceJoint(wheel_front, cabin, wheel_front.localCOM, cabin.worldPointToLocal(Vec2.weak(cabin.position.x + 70, cabin.position.y)), 32, 35); cabin_front_joint.compound = car; //Задний амортизатор cabin_rear_joint = new DistanceJoint(wheel_rear, cabin, wheel_rear.localCOM, cabin.worldPointToLocal(Vec2.weak(cabin.position.x - 62, cabin.position.y)), 32, 35); cabin_rear_joint.compound = car; //Выводим машинку со всеми ограничениями car.space = space; //Обратный вызов для переднего колеса cbCarType = new CbType(); wheel_front.cbTypes.add(cbCarType); wheel_front.space.listeners.add(new BodyListener(CbEvent.SLEEP, cbCarType, carSleepHandler)); } private function carSleepHandler(cb:BodyCallback):void { //Как только засыпает - едем! var impulse:Vec2 = Vec2.get(10, 0); impulse.length = 3200; cabin.applyImpulse(impulse); } private function enterFrameHandler(ev:Event):void { space.step(1 / stage.frameRate); debug.clear(); debug.draw(space); debug.flush(); } } }
Пример работы можно посмотреть тут.
похоже по nape на русском ваша инфа самая свежая. странно что блог непопулярен, совсем нет комментов. совсем чтоли as уже никто не учит :)
ОтветитьУдалитьне выводиться рисунок машинки. контуры есть а png нету. в чём дело подскажите?
а не,
ОтветитьУдалитьпоспешил. такое же барахло как и всё остальное.
но спсбо всеравно(если вообще тут ктото есть и заглядываешь), наводит на мысли, дополняет