Box2D - Klouby a jednoduchá motorka - 4. díl

8.2.2010· Autor: Ivan Kuckir· Počet komentářů: 6

Po kratší přestávce zde opět máme článek o Box2D. Dnes se podíváme na další užitečnou věc při programování fyzikálních simulací, a to klouby, a nakonec si vytvoříme jedoduchou motorku s ovládáním.

Slovo na úvod

Před tím, než budete pokračovat dále, byste měli být seznámeni s látkou předchozích článků, a to:

Box2D - fyzikální svět - 1. díl

Box2D - první kroky - 2. díl

Box2D - Basketball - 3. díl

Pokud si na ně přesně nevzpomínáte nebo jste si je vlastnoručně neotestovali, doporučuji tak učinit.

Co jsou klouby

Ve fyzikálních enginech se slovem kloub myslí objekt, který má za úkol udržovat vzájemnou polohu dvou těles, a to podle nějakého vztahu. Pěknou demonstraci kloubů můžete vidět na stránkách autora - http://box2dflash.boristhebrave.com/(šipkami přepínáte ukázky). Máme tyto druhy kloubů:

Vzdálenostní kloub (Distance Joint) - b2DistanceJoint

Distance joint

- jeho úkolem je udržovat stanovenou vzdálenost mezi dvěma body ("anchor1" a "anchor2") na tělesech. Představte si ho jako neviditelnou kovovou tyč, která je v místech puntíků připevněná k telesům a může se tam i protáčet.

Rotační kloub (Revolute Joint) - b2RevoluteJoint

Revolute joint

- je to vlastně vzdálenostní kloub pro nulovou vzdálenost. Představte si ho tak, že si vystřihnete dva tělesa z papíru, napichnete je na jehlu a můžete s nimi rotovat.

Kladkový kloub (Pulley Joint) - b2PulleyJoint

Pulley joint

- v podstatě se jedná o kladku, kterou představují ty dvě čáry na obrázku. Součet jejich délek je buď vždy stejný, nebo můžete stanovit přesný poměr, tj. když jde jedno těleso dolů - druhé jde nahoru. Body "ground1" a "ground2" jsou statické.

Ke všem kloubům

Body "anchor1" a "anchor2" se mohou nacházet i mimo plochu tělesa, budou však stále ve stejné pozici vůči tělesu. Vazby těles ke koubu nejsou "absolutní" a při použití větší síly se dají vychýlit - podle hustoty tělesa. Další klouby a jejich používání můžete nalézt v dokumentaci k Box2D (v angličtině) - http://box2dflash.boristhebrave.com/docs/2.0.2/manual#Joints. V tomto článku si ukážeme použití vzdálenostního kloubu.

Vytváření motorky

Naším plánem je vytvořit motorku ve stylu terénních 2D závodů, kde s motorkou skáčete přes překážky, děláte salta apod. Jako tělo motorky nám poslouží čtyřúhelník a jako kola použijeme dvě kružnice. Mezi nimi vytvoříme vzdálenostní klouby (červené čáry, viz obrázek níže):

Motorka

Základní AS soubor

Vytvoříme si následující AS soubor. Řádky, které nejsou okomentovány, by měly být jasné z předchozích článků.

package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.KeyboardEvent;

import Box2D.Dynamics.*;
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Common.Math.*;
import Box2D.Dynamics.Joints.*;


public class Main extends Sprite
{ 
  public var world:b2World;

  public var ground:b2Body;     // těleso země

  public var wheelF:b2Body;     // přední kolečko - "front"
  public var wheelB:b2Body;     // zadní kolečko - "back"
  public var motorBody:b2Body;  // tělo motocyklu

  public var jointB1:b2Joint;   // první zadní kloub
  public var jointB2:b2Joint;   // druhý zadní kloub
  public var jointF1:b2Joint;   // první přední koub
  public var jointF2:b2Joint;   // druhý přední kloub

  public var up:int = 0;        // signalizátor nahoru/dolů
  public var right:int = 0;     // signalizátor doprava/doleva
  public var rightSide:Boolean = true;      // zda je motocykl otočený doprava

  public function Main()
  {
    addEventListener(Event.ENTER_FRAME, Update, false, 0, true);
    addEventListener(Event.ADDED_TO_STAGE, addListeners);

    var worldAABB:b2AABB = new b2AABB();
    worldAABB.lowerBound.Set(-100.0, -100.0);
    worldAABB.upperBound.Set(100.0, 100.0);

    var gravity:b2Vec2 = new b2Vec2 (0.0, 10.0);
    var doSleep:Boolean = true;

    world = new b2World(worldAABB, gravity, doSleep);

    createGround();               // funkce pro vytvoření terénu

    //////////////////////////////////////

  }

  public function addListeners(e:Event):void
  {
    stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
    stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
  }

  public function createGround():void
  {

  }
  public function onKeyDown(e:KeyboardEvent):void
  {

  }

  public function onKeyUp(e:KeyboardEvent):void
  {

  }

  public function Update(e:Event):void
  {
    world.Step(1.0/60.0, 10);
    for (var bb:b2Body = world.m_bodyList; bb; bb = bb.m_next)
    {
      if (bb.m_userData != null && !bb.IsSleeping())
      {
        bb.m_userData.x = bb.GetPosition().x * 100;
        bb.m_userData.y = bb.GetPosition().y * 100;
        bb.m_userData.rotation = (bb.GetAngle() * (180/Math.PI))%360;
      }
    }
  }

}
}

Jako jednoduché cvičení si zkuste do funkce createGround dopsat vytvoření libovolného terénu - viz minulé články. Můj terén, který vidíte v ukázce a můžete si ho i stáhnout, je dělaný přes vkládání trojúhelníků podle souřadnic vrcholů uložených v poli. Vy si můžete vymyslet vlastní systémy a s nimi vytvářet dokonale propracované terény.

Motorka

Další kód budeme psát do konstruktoru Main na konec (tam, kde jsou lomítka). Vytvoříme si kolečka:

var wheelSD = new b2CircleDef();           // definice tvaru kolečka
 wheelSD.radius = 0.25;                    // poloměr kolečka
 wheelSD.density = 15;                     // hustota
 wheelSD.friction = 1.5;                   // tření
 wheelSD.restitution = 0.5;                // elastičnost ("odráživost")
 
 var wheelBD:b2BodyDef = new b2BodyDef();  // definice těla kola
 wheelBD.angularDamping = 2;               // tlumení rotace 
 
 wheelBD.position.Set(1.02,0.48);          // pozice prvního kolečka
 wheelBD.userData = new Wheel();           // vytvoření herce (připraven v knihovně - linkage)
 addChild(wheelBD.userData);               // přidání herce na scénu
 wheelF = world.CreateBody(wheelBD);       // vytvoření tělesa ve světě podle definice
 wheelF.CreateShape(wheelSD);              // vytvoření jeho tvaru
 wheelF.SetMassFromShapes();               // nastavení hmotnosti podle tvaru
 
 // to same se zadním kolem

 wheelBD.position.Set(-0.12,0.47);
 wheelBD.userData=new Wheel();
 addChild(wheelBD.userData);
 wheelB = world.CreateBody(wheelBD);
 wheelB.CreateShape(wheelSD);
 wheelB.SetMassFromShapes();

Kolečka už máme, teď si vytvoříme tělo motocyklu (čtyřúhelník)

var motorBodyBD:b2BodyDef = new b2BodyDef();   // definice tělesa 
 motorBodyBD.position.Set(0.43, 0.18);         // pozice ve světě
 motorBodyBD.linearDamping = 0.2;              // tlumení pohybu
 motorBodyBD.userData = new MotorBody();       // vytvoření herce (linkage MC v knihovně)
 addChild(motorBodyBD.userData);               // přidání herce na scénu
 
 var motorBodySD = new b2PolygonDef();         // definice tvaru (mnohoúhelník)
 motorBodySD.density = 55;                     // hustota tělesa
 motorBodySD.vertexCount = 4;                  // počet vrcholů
 motorBodySD.vertices[0].Set(-0.45, -0.12);    // nastavení pozic vrhcolů
 motorBodySD.vertices[1].Set( 0.42, -0.18);    // ....
 motorBodySD.vertices[2].Set( 0.14, 0.23);
 motorBodySD.vertices[3].Set( -0.09, 0.25);
 motorBody = world.CreateBody(motorBodyBD);    // vytvoření tělesa ve světě podle definice
 motorBody.CreateShape(motorBodySD);           // vytvoření jeho tvaru
 motorBody.SetMassFromShapes();                // nastavení hmotnosti podle tvaru

Už teď to můžeme spustit. Uvidíme tělo a kolečka, jak padají dolů. Zatím nejsou nijak spojeny. Proto přidáme 4 klouby (červené čáry - viz obrázek výše).

Vytvoření kloubů se skládá ze 3 částí:

  • vytvoření definice kloubu - jednoduše se vytvoří nová instance třídy - new b2xxxxJointDef();
  • inicializace - zavolá se metoda, ve které se předají 2 tělesa, mezi kterými má být kloub + globální souřadnice bodů na tělesech, ke kterým se má koub připojit
  • vytvoření kloubu do světa - zavolá se metoda světa CreateJoint (podobně jako CreateBody()).
var jointDef:b2DistanceJointDef = new b2DistanceJointDef()
 jointDef.Initialize(wheelF, motorBody, wheelF.GetPosition(), new b2Vec2(.75, .05)); 
 jointF1 = world.CreateJoint(jointDef);
 
 jointDef.Initialize(wheelF, motorBody, wheelF.GetPosition(), new b2Vec2(.62, .18));
 jointF2 = world.CreateJoint(jointDef);
 
 jointDef.Initialize(wheelB, motorBody, wheelB.GetPosition(), new b2Vec2(.33, .28));
 jointB1 = world.CreateJoint(jointDef);
 
 jointDef.Initialize(wheelB, motorBody, wheelB.GetPosition(), new b2Vec2(.36, .35));
 jointB2 = world.CreateJoint(jointDef);

Určitě jste si všimli, že většina souřadnic jsou celkem konkrétní čísla. Tato čísla jsem si nevymyslel - jednoduše jsem si načetl obrázek výše do Flashe, umístil ho středem motocyklu do levého horního rohu a ostatní jsouřadnice jsem okoukal podle flashových pravítek.

Když to teď spustíme, dostaneme motorku, která má k sobě připojená kolečka a padá na zem (pokud jste si ji vytvořili). Naše první motorka je na světě!

Ovládání

Teď si dopíšeme listenery tlačítek.

public function onKeyDown(e:KeyboardEvent):void
{
  switch(e.keyCode)                     // zkoušíme hodnotu kódu tlačítka:
  {
    case 38: up = 1; break;             // šipka nahoru - nastavíme up kladné
    case 40: up =-1; break;             // šipka dolů - nastavíme up záporné
    case 39: right = 1; break;          // šipka doprava- nastavíme right kladné
    case 37: right =-1; break;          // šipka doleva - nastavíme right záporné
    case 32: rightSide = !rightSide;    // mezerník - změníme rightSide na opačnou hodnotu
             motorBody.m_userData.scaleX *= -1; break;  // a vertikálně převrátíme herce těla motorky
    case 74: motorBody.ApplyImpulse(new b2Vec2(0, -70), motorBody.GetWorldCenter()); break;   // "J" - skok do vzduchu
  }
}
 
public function onKeyUp(e:KeyboardEvent):void
{
  switch(e.keyCode)
  {
    case 38: up = 0; break;        // puštění šipky nahoru - vynulujeme up
    case 40: up = 0; break;        // pustění šipky dolů - to samé
    case 39: right = 0; break;     // puštění šipky vpravo - vynulujeme right
    case 37: right = 0; break;     // puštění šipky vlevo - to samé
  }
}

Také přidáme patřičnou akci do Update (pod for cyklus) a na závěr si můžeme trošku pohrát s grafikou (herců nebo prostředí), jako jsem si hrál já :-).

if(up!=0)           // pokud je zmáčknuté nahoru nebo dolů
{
  if(rightSide) wheelB.ApplyTorque(up*26);    // pokud je motorka natočená vpravo, nastavíme moment síly na zadní (levé) kolečko
  else wheelF.ApplyTorque(up*-26);             // jinak nastavíme na přední (pravé)
}
if(right!=0) motorBody.ApplyTorque(right*35);  // pokud je zmáčknutá šípka vpravo nebo vlevo, aplikujeme moment síly na tělo motorky
 
this.x = 400 - motorBody.m_userData.x;         // a nakonec přesuneme celý tento objekt podle pozice motorky, aby zůstala uprostřed

A můžeme jet!

Stáhnout zdrojový kód

Ivan Kuckir Tvůrce flash her a RIA aplikací, interaktivních webů, začlenění flashe do dalších webových technologií.

E-mail: ivan.kuckir(zavinac)gmail.com | Web: www.ivank.net | ICQ: 303646153 | Skype: van010

Motto: Programování sice může být zajímavé, ale jsou i lepší věci. Třeba nejlepší online hry

Seriál: Box2d - fyzikální svět

Popis fyzikálního engine Box2d pro Flash.

Komentáře k článku  
Vynikajuce al | 8.2.2010 22:09
vdaka richard7854 | 9.2.2010 15:00
Konecne krasny clanok TINYSOFT | 9.2.2010 19:11
Dopravníkový pás ivanK.net | 10.2.2010 10:49
Dopravnikovy pas al | 11.2.2010 11:06
animace kódem ivanK.net | 12.2.2010 12:29

Přihlášení uživatele