В физическом движке 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 нету. в чём дело подскажите?
а не,
ОтветитьУдалитьпоспешил. такое же барахло как и всё остальное.
но спсбо всеравно(если вообще тут ктото есть и заглядываешь), наводит на мысли, дополняет