Кукловод - маленький гайд по созданию Ragdoll'ов

Создание Куколок.
В этом Уроке я покажу, как я создаю ragdoll'ы
для своих игр С множественным числом я погорячился — для своей игры
Необходимо:
Adobe Flash Studio (у меня CS4) — Для рисования.
Flash Develop + haxe — Для программирования.
Nape — физический двиг.

Как настраивать проект и где брать — не скажу.
Ибо я показываю метод, а не детальное руководство.


Хочется верить, что я программист (
Хотя иногда приходиться открывать штуки для рисования.
ИДЕ у меня появилось не давно — осваивать его решил.
Идея такая не задавать кодом структуру
человечка, а определить функцию, которая считывает
клип с его частями.

Этап 1: Рисование куколок
К сожалению, что во мне спит художник — я точно не верю)
Разбудить мне его не удалось, поэтому не буду
показывать как я рисовал, только результат.
Рисуем части тела по отдельности.
Вот что у меня вышло — ужас):

Теперь конвертируем части тела в клипы:
Я сделал так:

Также, как видно, я добавил еще один символ
— маленький круг — joint
это крепления для частей.

Создаем новый символ ragdoll
и распологаем по местам все наши части,
там где должны быть соединения ставим joint:

И важно, не забыть, прописать всем символам
имена(instance Name).Я написал такие же, как и названия символов.
А Соединениям нужно писать, если голова- join_head, рука — joint_hand_left и тп.
Теперь в свойствах Символа разрешаем ему Экспортироваться
С именем CRagdoll (С большой буквы, ибо Хакс не понимает классы с маленькой)

Все теперь можно нажать Shift+F12 или Publish
Рисование кончалось)
Начинаем прогать.

Этап 2: Куколка живи
Создаем новый хакс проект во ФлещДевелоп

В проекте создаем папку lib
в нее добавим наш ragdoll.swf
Надеюсь нап вы сами скачаете.
У Луки какие-то проблемы с версией под хакс, так что качать лучше код.
Теперь к проекту подключаем библиотеку
Нажать на проект — добавить (Add) — Libraly Asset выбираем наш файл ragdoll.swf
Утомился я писать)
Исходники прикреплю.
Описывать весь процесс не стану — слишком долго, кода вышло много)
опишу методы:
createBoxBySprite — Создает Тело с прикрепленным к нему спрайтом.
createBoxByChild — создает тело по имени ребенка у спрайта
createPivotJoint — создает точечное соединение
createPivotJointByChild — по имени чайлда
createRotaryLimitJoint — создает соединение, ограничивающее углы
createRagdollBySprite — главная наша функция создает регдольчик
Да, много функций для урока) но они потом пригодятся, к примеру для
объектов уровня и тп

Плюс этого метода.
Что теперь художник может нарисовать персонажа
— любого роста и телосложения, а нам — программерам — пофиг)

Что вышло — смотреть ниже (флешку сюда вставить нельзя?, сейчас залью куда-нибудь)
кликаем на экран появляется куколка)
мой нет-бук держит порядка 20 тел, но злоупотреблять не стоит…
Архив с исходником и медией:СКАЧАТЬ или ifolder
Смотреть:СМОТРЕТЬ

Не уверер, что кто-то смотрел код на haxe), а я старался. Так, что показываю здесь

package ;

import flash.Lib;
import flash.display.Sprite;
import flash.display.DisplayObject;
import flash.geom.Point;
import flash.events.Event;
import flash.events.MouseEvent;

import nape.geom.Vec2;
import nape.geom.AABB;
import nape.space.Space;
import nape.space.UniformSleepSpace;
import nape.phys.Body;
import nape.phys.PhysObj;
import nape.phys.Material;
import nape.util.Tools;
import nape.constraint.PivotJoint;
import nape.constraint.RotaryLimitJoint;

class Main extends Sprite 
{       
        static function main() 
        {
                var main:Main = new Main();
                Lib.current.addChild(main);             
        }
        
        static private var GRAVITY:Vec2 = new Vec2(0, 250); 
        static private var DELTA:Float = 0.016;
        static private var WIDTH:Int = 640;
        static private var HEIGHT:Int = 480;
        static private var WIDTH_H2:Int =320;
        static private var HEIGHT_H2:Int = 240;
        
        private var space:Space;
        public function new()
        {
                super();
                
                space = new UniformSleepSpace(new AABB(0, 0, WIDTH, HEIGHT), 30, GRAVITY); //Физика
                createBorder(); //Границы мира
                
                this.graphics.beginFill(0x901010);
                this.graphics.drawRect(0, 0, WIDTH, HEIGHT);
                this.graphics.endFill();
                addEventListener(Event.ENTER_FRAME, update); // Обновить мир
                addEventListener(MouseEvent.CLICK, click);
                
        }
        private function click(e:Event):Void
        {
                //куколки
                createRagdollBySprite(space, this, new Vec2(mouseX, mouseY), new CRagdoll(),(Math.random()>0.9 ? true : false));
        }
        private function update(e:Event):Void
        {
                space.step(DELTA);
        }
        private function createBorder():Void
        {
                var p:PhysObj; 
                space.addObject(p = Tools.createBox(-20, HEIGHT_H2, 50, HEIGHT, 0, 0, 0, true, Material.Wood)); 
                addChild(p.graphic); 
                space.addObject(p = Tools.createBox(WIDTH +20, HEIGHT_H2, 50, HEIGHT, 0, 0, 0, true, Material.Wood)); 
                addChild(p.graphic); 
                space.addObject(p = Tools.createBox(WIDTH_H2, -20, WIDTH, 50, 0, 0, 0, true, Material.Wood)); 
                addChild(p.graphic); 
                space.addObject(p = Tools.createBox(WIDTH_H2, HEIGHT + 20, WIDTH, 50, 0, 0, 0, true, Material.Wood)); 
                addChild(p.graphic);
        }
        
        //вспомогательные функции для работы с физикой
        static private var G2R:Float = 0.017453292519;  // Math.PI / 180;
        static private var R2G:Float = 57.295779514719; // 180 / Math.PI;
        static public inline function createBoxBySprite(space:Space,render:Sprite, pos:Vec2, sprite:Sprite,material:Material, isStatic:Bool, ?data:Dynamic,?collision=0xffffffff):PhysObj
        {
                if (sprite!=null)
                {                       
                        var pobj:PhysObj;       
                        pobj = Tools.createBox(0, 0, sprite.width , sprite.height , 0, 0, 0, isStatic, material,collision);
                        pobj.setPos(pos.px + sprite.x, pos.py + sprite.y, G2R * sprite.rotation);                       
                        pobj.assignGraphic(sprite);             
                        pobj.data = data;                                       
                        if(isStatic) pobj.body.stopAll();               
                        pobj.body.update();                             
                        space.addObject(pobj);
                        render.addChild(pobj.graphic);          
                        return pobj;
                }       
                else return null;
        }       
        static public  inline function createBoxByChild(space:Space, render:Sprite, pos:Vec2, parent:Sprite, name:String, material:Material, isStatic:Bool, ?data:Dynamic,?collision=0xffffffff):PhysObj
        {               
                var child:DisplayObject =  parent.getChildByName(name);                         
                if (child!=null)
                {               
                        var point:Point = child.localToGlobal(new Point());
                //      parent.removeChild(point);                      
                        child.x = point.x-parent.x;
                        child.y = point.y-parent.y;
                        child.rotation = parent.rotation;                       
                        return createBoxBySprite(space, render, pos, cast(child,Sprite), material, isStatic, data,collision);
                }       
                else return null;
        }       
        static public inline function createPivotJointByChild(space:Space,body1:PhysObj,body2:PhysObj, pos:Vec2,breakable:Bool,maxForce:Float, parent:Sprite,name:String):PivotJoint
        {
                var child:DisplayObject =  parent.getChildByName(name);                         
                if (child!=null)
                {
                        var sumPos:Vec2 = pos.clone();
                        sumPos.px += child.x;
                        sumPos.py += child.y;                                   
                        return createPivotJoint(space, body1, body2, sumPos,breakable,maxForce);
                }       
                else return null;
        }       
        static public inline function createPivotJoint(space:Space, obj1:PhysObj, obj2:PhysObj, pos:Vec2, ?breakable:Bool=false, ?maxForce:Float=1600000):PivotJoint
        {
                if (obj1 == null || obj2 == null) return null;
                else 
                {
                        var p:PivotJoint = new PivotJoint(obj1, obj2, pos);
                        p.breakable = breakable;                        
                        if (breakable) p.maxForce = maxForce;
                        p.biasCoef = 0.5;
                        p.ignore = true;
                        space.addConstraint(p); 
                        return p;
                }                       
        }
        static public inline function createRotaryLimitJoint(space:Space, obj1:PhysObj, obj2:PhysObj, min:Float, max:Float,?breakable:Bool=false,?maxForce:Float=2000000):RotaryLimitJoint
        {
                if (obj1 == null || obj2 == null) return null;
                else 
                {
                        var p:RotaryLimitJoint = new RotaryLimitJoint(obj1, obj2, min, max);
                        p.breakable = breakable;                
                        if (breakable) p.maxForce = maxForce;
                        p.biasCoef = 0.5;
                        p.ignore = true;
                        space.addConstraint(p);
                        return p;
                }
        }
        static public inline function createRagdollBySprite(space:Space, render:Sprite,pos:Vec2, sprite:Sprite, ?isbreak:Bool=false):Void
        {
                var head:PhysObj;                       
                var body:PhysObj;       
                var leftfoot:PhysObj;   
                var rightfoot:PhysObj;  
                var lefthand:PhysObj;           
                var righthand:PhysObj;                          
                var material:Material = Material.Wood;
                
                // # bodyes                             
                leftfoot        = createBoxByChild(space, render, pos, sprite, 'foot_left', material,false,null,1);                     
                rightfoot       = createBoxByChild(space, render, pos, sprite, 'foot_right',material,false,null,2);                     
                body            = createBoxByChild(space, render, pos, sprite, 'body',          material,false,null,4);         
                head            = createBoxByChild(space, render, pos, sprite, 'head',          material,false,null,8);
                lefthand        = createBoxByChild(space, render, pos, sprite, 'hand_left', material,false,null,16);
                righthand       = createBoxByChild(space, render, pos, sprite, 'hand_right',material,false,null,32);            

                // # pivot joints               
                createPivotJointByChild(space, head,      body, pos,  isbreak, 1600000, sprite, 'joint_head');                          
                createPivotJointByChild(space, lefthand,  body, pos,  isbreak, 1600000, sprite, 'joint_hand_left');     
                createPivotJointByChild(space, righthand, body, pos,  isbreak, 1600000, sprite, 'joint_hand_right');    
                createPivotJointByChild(space, leftfoot,  body, pos,  isbreak, 1600000, sprite, 'joint_foot_left');     
                createPivotJointByChild(space, rightfoot, body, pos,  isbreak, 1600000, sprite, 'joint_foot_right');
        
                // # rotary limits
                createRotaryLimitJoint(space, lefthand, body, -0.45, 0.45,  isbreak);                   
                createRotaryLimitJoint(space, head,     body, -0.45, 0.45,  isbreak);
                createRotaryLimitJoint(space, righthand,body, -0.45, 0.45,  isbreak);   
                createRotaryLimitJoint(space, leftfoot, body, -0.25, 0.25,  isbreak);   
                createRotaryLimitJoint(space, rightfoot,body, -0.25, 0.25,  isbreak);
        }
}


П.С. постарался исправить все ошибки и опечатки.
  • +12

Комментарии (22)

0
«Внимание! Посещение этого сайта может нанести вред вашему компьютеру.»
0
странно (
по ссылки с исходниками?
может с фолдора ifolder.ru/19844036
0
у половины голова отлетает(
0
0
Изверг ;)))
0
Так и должно)
у одного из 10…
эта я показать, что в напе легко сделать разрушаемые лжойтны,
пс тоя я и не поню может в боксе также (
0
В Боксе очень сложно. (:
world.DestroyJoint(joint);
0
Не, там не так)
Там если джойн сильно натянуть (в зависимости от параметров)
— он порвется.
А в боксе не помню, есть ли такое)
0
Ну х3, я с Nape'ом не работал никогда. Но мой вариант только убъёт джоинт в боксе. (:
0
угу и я про то) в напе его тоже удальть можно))
Я же писал про.
к примеру, регдол сильно ударить или упал с большой высоты — конечность то сама оторвется — это удобненько)
0
На самом деле я с джоинтами ещё ни разу не работал (не считая тестов). Собственно сейчас работаю первый раз, но рвать мне их не нужно. (:
0
Nape — монстр!
Я заполнил весь экран рэгдоллами, а он даже подтормаживать не начал!
0
Угу, шустрый.
Там сейчас, к примеру, несколько куколок коллизится так
голова с голвой
туловище с туловищем
нога с ного и тп)
Я так сделал в свойе игре… забыл убрать
0
полезный тьютор и всегда приятно посмотреть кто как использует haxe и nape
спасибо и +1!
0
Ну вот… только хотел статью про регдол на Nape накатать… ))
Хотя вот моя старая статья… может пригодится комунибудь…
habrahabr.ru/blogs/Flash_Platform/104176/
0
Привет, интересно.
Тоже публикуй.
Я же, не сравнивал с боксом.
Я хотел показать, как я беру части из нарисованного sprit'а
и у меня код на хаксе.

а зачем ты спрайт, каждый раз двигаешь, ведь в Напе
нужно просто ФизическиеОбъект.assignGraphics… — это удобней.
ну и так есть замечания) публикуй в комментах напишу.
0
[offtop]

ТОварищи, а что хакс вот прям такой весь удобный, ьыстрый и крутой по-сравнению с ас3?
0
Не думаю, но плюсы, действительно есть.
Хотя я вот уже планирую вернуться или на ас3 или попробовать, что-то еще.
Хакс — хорош, но нет никаких движух в коммуните, да и коммунити очень маленькое.
Сейчас он как морская свинка — и плавает плохо, и скушать мало) имхо.
0
Разницу в скорости при обычном использовании ты вряд ли заметишь. Ну, генерит он чуть более оптимизированный байткод, ну и что. В итоге всё равно тот же флеш. Но как ЯП для меня хакс более удобный, чем as3.
Ему скорее нехватает актуальной IDE (в том числе под OSX), способ без хитрых манипуляций подключать AS3-код, проектов, написанных на нём и комьюннити, активно его юзающего.
Как минимум стоит его попробовать. Мне понравилось.
0
вопрос времени и денег
fdt.powerflasher.com/docs/Intro_To_haXe_Development_With_FDT
+2
Любые IDE на базе Эклипса — полное УГ.
0
Если УГ это то что я думаю)
то полностью согласен…
какое-то время сидел в линуксе с эклипсом и флешбилдером.
привыкнуть смог)
и вроде все есть и побольшей части работает, но не так как-то оно — не то…

кстати, да, было бы круто, если можно было бы нормально вставлять ас3-код...))
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.