티스토리 뷰
물리엔진라이브러리를 만들어 보자..
Box2D는 2006년 게임개발자 회의에서 에린 카토(Erin Catto)가 물리 관련 튜토리얼로 발표한 C++ 코드에서 출발했습니다.(http://box2d.org/about/)
Java로 구현된 JBox2D 라이브러리를 설치 필요(http://www.jbox2d.org/)
추가로 PBox2D(프로세싱 Box2D) 설치 필요
아래 사이트 : https://github.com/shiffman/Box2D-for-Processing
그러나 프로세싱에서 사용하기 위해서는
Processing > Sketch 메뉴에서 >라이브러리 > 라이브러리 추가하기 메뉴에서
jbox2D를 선택하고 shiffman의 라이브러리를 설치해 주어야 한다.
위와 같이 설치가 불가할 경우 manual로 설치해 주어야 한다.
https://github.com/processing/processing/wiki/How-to-Install-a-Contributed-Library
//------------------------------------------
// Box 2D를 사용한 예제
import shiffman.box2d.*;
Box2DProcessing box2d;
void setup(){
box2d = new Box2DProcessing(this);
box2d.createWorld();
box2d.setGravity(0,-10);
}
//----------------------------------------------
// Box2D 라이브러리를 사용하여
// Pixel 좌표계(Processing 세계)를 World 좌표계(Box2D) 바꾸는 예제
// 마우스를 찍는 곳에서 원인이 생긴다.
import shiffman.box2d.*;
import org.jbox2d.collision.shapes.*;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.*;
Box2DProcessing box2d;
void setup(){
size(320,320);
background(255);
box2d = new Box2DProcessing(this);
box2d.createWorld();
box2d.setGravity(0,-10);
}
void draw(){
// background(255);
}
void mousePressed(){
Vec2 pixelPos = new Vec2(mouseX, mouseY);
Vec2 worldPos = box2d.coordPixelsToWorld(pixelPos);
ellipse(pixelPos.x, pixelPos.y, 16, 16);
}
//---------------------------------------------------
// Box2d 객체를 생성하고 활용하기
import shiffman.box2d.*;
import org.jbox2d.collision.shapes.*;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.*;
size(320, 320);
background(255);
//0. create world
Box2DProcessing box2d = new Box2DProcessing(this);
box2d.createWorld();
// 1. create bodyDef
BodyDef bd = new BodyDef();
bd.position.set(box2d.coordPixelsToWorld(width/2, height/2));
// 2. define body
Body body = box2d.createBody(bd);
// 3. create Shape
PolygonShape ps = new PolygonShape();
float box2Dw = box2d.scalarPixelsToWorld(150);
float box2Dh = box2d.scalarPixelsToWorld(100);
ps.setAsBox(box2Dw, box2Dh);
//4. create FixtureDef
FixtureDef fd = new FixtureDef();
fd.shape = ps;
fd.friction=0.3;
fd.restitution=0.5;
fd.density=1.0; // same as water(1kg/m3)
// 5. combinate body, fixture, shape
fill(175);
stroke(0);
body.createFixture(fd);
//body.createFixture(ps, 1);
//---------------------------------------
// 프로젝트 하기
// 5.7 Box2D와 프로세싱
// Boxes란 폴더를 만들고 3개의 파일 생성(Boxes.pde, Boundary.pde, Box.pde)
// Box.pde
class Box{
float x, y; // point to draw box.
float w, h; //weight, height
Box(float x_, float y_){
x = x_;
y = y_;
w = 16;
h = 16;
}
void display(){
fill(175);
stroke(0);
rectMode(CENTER);
rect(x, y, w, h);
}
}
//---------------------------------------
// Boxes.pde
import java.util.ArrayList;
ArrayList<Box> boxes;
void setup(){
size(640, 480);
boxes = new ArrayList<Box>();
}
void draw(){
background(255);
//when pressed mouse, box is created
if(mousePressed){
Box p = new Box(mouseX, mouseY);
boxes.add(p);
}
// Display ArrayList
for(Box b:boxes){
b.display();
}
}
//----------------------------------------------
// 위의 예제에 대하여 Box2D 라이브러리를 사용하여 수정하기
// Box.pde
class Box{
Body body; // Box2D Object(World, Body, Fixture, Shape
float w, h; //weight, height
Box(){
w = 16;
h = 16;
// 1. create bodyDef
BodyDef bd = new BodyDef();
bd.type = BodyType.DYNAMIC;
bd.position.set(box2d.coordPixelsToWorld(mouseX, mouseY));
// 2. define body
body = box2d.createBody(bd);
// 3. create Shape
PolygonShape ps = new PolygonShape();
float box2DW = box2d.scalarPixelsToWorld(w/2);
float box2DH = box2d.scalarPixelsToWorld(h/2);
ps.setAsBox(box2DW, box2DH);
//4. create FixtureDef
FixtureDef fd = new FixtureDef();
fd.shape = ps;
fd.friction=0.3; // 물리 속성(마찰)
fd.restitution=0.5; // 물리속성(탄성)
fd.density=1.0; // same as water(1kg/m3)
// 5. combinate body, fixture, shape
body.createFixture(fd);
}
void display(){
Vec2 pos = box2d.getBodyPixelCoord(body); // 위치를 가져온다.
float a = body.getAngle(); //각도를 가져온다.
pushMatrix();
translate(pos.x, pos.y); // body가 있는 위치로 좌표를 변환함
rotate(-a);
fill(175);
stroke(0);
rectMode(CENTER);
rect(0, 0, w, h);
popMatrix();
}
void killBody(){
box2d.destroyBody(body);
}
}
//---------------------------------------
// Boxes.pde
import shiffman.box2d.*;
import org.jbox2d.collision.shapes.*;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.*;
import java.util.ArrayList;
Box2DProcessing box2d;
ArrayList<Box> boxes;
void setup(){
size(640, 480);
box2d = new Box2DProcessing(this);
box2d.createWorld();
boxes = new ArrayList<Box>();
}
void draw(){
background(255);
box2d.step();
//when pressed mouse, box is created
if(mousePressed){
Box p = new Box();
boxes.add(p);
}
// Display ArrayList
for(Box b:boxes){
b.display();
}
}
//----------------------------------------------
// 경계면을 만들어셔 쌓이도록 하기
// Box.pde
class Box{
Body body; // Box2D Object(World, Body, Fixture, Shape
float w, h; //weight, height
Box(){
w = 16;
h = 16;
// 1. create bodyDef
BodyDef bd = new BodyDef();
bd.type = BodyType.DYNAMIC;
bd.position.set(box2d.coordPixelsToWorld(mouseX, mouseY));
// 2. define body
body = box2d.createBody(bd);
// 3. create Shape
PolygonShape ps = new PolygonShape();
float box2DW = box2d.scalarPixelsToWorld(w/2);
float box2DH = box2d.scalarPixelsToWorld(h/2);
ps.setAsBox(box2DW, box2DH);
//4. create FixtureDef
FixtureDef fd = new FixtureDef();
fd.shape = ps;
fd.friction=0.3; // 물리 속성(마찰)
fd.restitution=0.5; // 물리속성(탄성)
fd.density=1.0; // same as water(1kg/m3)
// 5. combinate body, fixture, shape
body.createFixture(fd);
}
void display(){
Vec2 pos = box2d.getBodyPixelCoord(body); // 위치를 가져온다.
float a = body.getAngle(); //각도를 가져온다.
pushMatrix();
translate(pos.x, pos.y); // body가 있는 위치로 좌표를 변환함
rotate(-a);
fill(175);
stroke(0);
rectMode(CENTER);
rect(0, 0, w, h);
popMatrix();
}
void killBody(){
box2d.destroyBody(body);
}
}
//--------------------------------------
// Boundary.pde
class Boundary{
Body b; // Box2D Object(World, Body, Fixture, Shape
float x, y;
float w, h; //weight, height
Boundary(float x_, float y_, float w_, float h_){
x = x_;
y = y_;
w = w_;
h = h_;
// 1. create bodyDef
BodyDef bd = new BodyDef();
bd.type = BodyType.STATIC;
bd.position.set(box2d.coordPixelsToWorld(x,y));
// 2. define body
b = box2d.createBody(bd);
// 3. create Shape
float box2DW = box2d.scalarPixelsToWorld(w/2);
float box2DH = box2d.scalarPixelsToWorld(h/2);
PolygonShape ps = new PolygonShape();
ps.setAsBox(box2DW, box2DH);
// 5. combinate body, fixture, shape
b.createFixture(ps, 1); // 밀도가 1(kg/m3)
}
void display(){
fill(0);
stroke(0);
rectMode(CENTER);
rect(x, y, w, h);
}
}
//--------------------------------------
// Boxes.pde
import shiffman.box2d.*;
import org.jbox2d.collision.shapes.*;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.*;
import java.util.ArrayList;
Box2DProcessing box2d;
ArrayList<Box> boxes;
ArrayList<Boundary> boundaries;
void setup(){
size(640, 480);
box2d = new Box2DProcessing(this);
box2d.createWorld();
boxes = new ArrayList<Box>();
boundaries = new ArrayList<Boundary>();
boundaries.add(new Boundary(width/4,height-5,width/2-50,10));
boundaries.add(new Boundary(3*width/4,height-50,width/2-50,10));
}
void draw(){
background(255);
box2d.step();
// Display ArrayList
for(Boundary wall:boundaries){
wall.display();
}
//when pressed mouse, box is created
if(mousePressed){
Box p = new Box();
boxes.add(p);
}
// Display ArrayList
for(Box b:boxes){
b.display();
}
}
//-----------------------------------
// 10. 복잡한 모양을 만들어 보자
// NATURE OF CODE-5 : Boxes 복잡한 모양(Polygon) 객체를 만들어 보자
간단한 사각형 및 원에서 다각형(Polygon)을 만들어서 사용하자.
주요 변경 사항은 Box 객체 생성자에서 다각형을 꼭지점(vertex)로 만든다.
Processing Pixcel 좌표계에서 반시계 방향으로 정의한다.
// 3. create Shape
Vec2[] vertices = new Vec2[4]; // make array
vertices[0] = box2d.vectorPixelsToWorld(new Vec2(-15, 25));
vertices[1] = box2d.vectorPixelsToWorld(new Vec2(15, 0));
vertices[2] = box2d.vectorPixelsToWorld(new Vec2(20, -15));
vertices[3] = box2d.vectorPixelsToWorld(new Vec2(-10, -10));
PolygonShape ps = new PolygonShape();
ps.set(vertices, vertices.length);
그리고 Box의 Display() 함수에서 beginShape()와 EndShape()를 통하여 꼭지점(vertex) 연결한다.
void display(){
Vec2 pos = box2d.getBodyPixelCoord(body); // 위치를 가져온다.
float a = body.getAngle(); //각도를 가져온다.
Fixture f = body.getFixtureList();
PolygonShape ps = (PolygonShape)f.getShape();
rectMode(CENTER);
pushMatrix();
translate(pos.x, pos.y); // body가 있는 위치로 좌표를 변환함
rotate(-a);
fill(175);
stroke(0);
beginShape();
for(int i = 0; i < ps.getVertexCount(); i++){
Vec2 v = box2d.vectorWorldToPixels(ps.getVertex(i));
vertex(v.x, v.y);
}
endShape(CLOSE);
popMatrix();
}
//---------------------------------------
// NATURE OF CODE-5 : Boxes 복잡한 모양(Polygon) 객체를 2개 이상 붙여보자
특정 객체(예:Box)에 대한 Shape(Ploygon)을 만들고 Body에 붙이는 과정을 되풀이 하면 된다.
Box() 생성자에서 두개의 객체 만들기
Box(){
w = 16;
h = 16;
r = TWO_PI;
// 1. create bodyDef
BodyDef bd = new BodyDef();
bd.type = BodyType.DYNAMIC;
bd.position.set(box2d.coordPixelsToWorld(mouseX, mouseY));
// 2. define body
body = box2d.createBody(bd);
// 3. create
// 첫번째 객체만 들기
PolygonShape ps = new PolygonShape();
float box2dW = box2d.scalarPixelsToWorld(w/4); // Box의 폭을 작게 만들기 위해 4로 나눔
float box2dH = box2d.scalarPixelsToWorld(h/2);
ps.setAsBox(box2dW, box2dH);
// 두번째 객체만 들기
CircleShape cs = new CircleShape();
cs.m_radius = box2d.scalarPixelsToWorld(r);
Vec2 offset = new Vec2(0, -h/2); // 원을 사각형 위로 올리기 위하여 Offset으로 보정
offset = box2d.vectorPixelsToWorld(offset);
cs.m_p.set(offset.x, offset.y); // 보정을 하지 않을 경우 화면상에서 충돌이 먹히지 않는다.
//4. create FixtureDef
FixtureDef fd = new FixtureDef();
fd.shape = ps;
fd.friction=0.1; // 물리 속성(마찰)
fd.restitution=0.5; // 물리속성(탄성)
fd.density=1.0; // same as water(1kg/m3)
// 5. combinate body, fixture, shape
body.createFixture(fd);
body.createFixture(ps, 1.0);
body.createFixture(cs, 1.0);
}
Box의 Disply()에서 두개의 객체 그려주면, 생성자 만든 Shape에 매핑된다.
주의할 점은 생성자에서 정의한 shape와 processing의 객체가 일치하지 않을 경우 충돌 처리가 제대로 되지 않는다.
void display(){
Vec2 pos = box2d.getBodyPixelCoord(body); // 위치를 가져온다.
float a = body.getAngle(); //각도를 가져온다.
pushMatrix();
rectMode(CENTER);
translate(pos.x, pos.y); // body가 있는 위치로 좌표를 변환함
rotate(-a);
fill(175);
stroke(0);
rect(0, 0, w/2, h);
ellipse(0, -h/2, r*2, r*2);
popMatrix();
}
//---------------------------------------
// NATURE OF CODE-5 : 5.11 Box2D Joint 객체
Joint 객체는 Body 객체와 다른 Body 객체와 연결할 수 있게 만들어 주는 객체임.
본 객체를 이용하면 흔들리는 추나, 목각 인형등 축으로 움직이는 다양한 것들을 만들 수 있다.(예 : 자동차)
먼저 DistanceJoint를 만든다.(두개의 객체 연결)
1. 2개의 객체 준비(생성자)
2. JointDef 객체 생성
3. JointDef 객체의 속성 정의(객체의 어디에 위치하며, 길이등)
4. Joint 객체 생성
기존에 정의한 Particle 객체를 Box를 응용하여 아래와 같이 변경한다.
class Particle{
Body body; // Box2D Object(World, Body, Fixture, Shape
float r; //반지름 정의
Particle(float x, float y){
r = 8; // 반지름
// 1. create bodyDef
BodyDef bd = new BodyDef();
bd.type = BodyType.DYNAMIC;
bd.position.set(box2d.coordPixelsToWorld(x, y));
// 2. define body
body = box2d.world.createBody(bd);
// 3. 객체 만들기
CircleShape cs = new CircleShape();
cs.m_radius = box2d.scalarPixelsToWorld(r);
//4. create FixtureDef
FixtureDef fd = new FixtureDef();
fd.shape = cs;
fd.friction=0.01; // 물리 속성(마찰)
fd.restitution=0.3; // 물리속성(탄성)
fd.density=1.0; // same as water(1kg/m3)
// 5. combinate body, fixture, shape
body.createFixture(fd);
//body.setLinearVelocity(new Vec2(random(-5, 5), random(2, 5)));
}
void display(){
Vec2 pos = box2d.getBodyPixelCoord(body); // 위치를 가져온다.
float a = body.getAngle(); //각도를 가져온다.
pushMatrix();
rectMode(CENTER);
translate(pos.x, pos.y); // body가 있는 위치로 좌표를 변환함
rotate(-a);
fill(127);
stroke(0);
strokeWeight(2);
ellipse(0,0, r*2, r*2);
line(0, 0, r, 0); // 원에 반지름을 표시해서 회전 여부 확인
popMatrix();
}
void killBody(){
box2d.destroyBody(body);
}
}
그리고 Pair 객체를 생성하여 Particle의 body를 Joint 객체를 통해 묶어 주도록 한다.
(교재에서는 DistanceJoint 객체를 사용하라고 하는데, 사용하면 Error 발생한다.
Cannot cast joint to DistanceJoint)
class Pair{
Particle p1; // Body를 가지고 있는 객체
Particle p2; // Body를 가지고 있는 객체
float len; // DistanceJoint의 길이 정의
Pair(float x, float y){
len = 32;
//1. 2개의 객체 준비(생성자)
p1 = new Particle(x, y);
p2 = new Particle(x + random(-1, 1), y+random(-1, 1)); // 같은 위치에 존재시 문제 가능
// 2. JointDef 객체 생성
DistanceJointDef djd = new DistanceJointDef();
//3. JointDef 객체의 속성 정의(객체의 어디에 위치하며, 길이등)
djd.bodyA = p1.body;
djd.bodyB = p2.body;
djd.length = box2d.scalarPixelsToWorld(len);
djd.frequencyHz = 0; //회전수를 입력(1~5)
djd.dampingRatio = 0; //감세 정도(0~1)
//4. Joint 객체 생성
Joint dj =(Joint) box2d.world.createJoint(djd);
}
void display(){
Vec2 pos1 = box2d.getBodyPixelCoord(p1.body);
Vec2 pos2 = box2d.getBodyPixelCoord(p2.body);
stroke(0);
line(pos1.x, pos1.y, pos2.x, pos2.y);
p1.display();
p2.display();
}
}
Static으로 고정된 벽을 만들어 Joint 객체가 잠시 머물도록 한다.
class Boundary{
Body b; // Box2D Object(World, Body, Fixture, Shape
float x, y;
float w, h; //weight, height
Boundary(float x_, float y_, float w_, float h_){
x = x_;
y = y_;
w = w_;
h = h_;
// 1. create bodyDef
BodyDef bd = new BodyDef();
bd.type = BodyType.STATIC;
bd.position.set(box2d.coordPixelsToWorld(x,y));
// 2. define body
b = box2d.createBody(bd);
// 3. create Shape
PolygonShape ps = new PolygonShape();
float box2dW = box2d.scalarPixelsToWorld(w/2);
float box2dH = box2d.scalarPixelsToWorld(h/2);
ps.setAsBox(box2dW, box2dH);
// 5. combinate body, fixture, shape
b.createFixture(ps, 1); // 밀도가 1(kg/m3)
}
void display(){
fill(0);
stroke(0);
rectMode(CENTER);
rect(x, y, w, h);
}
}
자 이제 실행할 수 있도록 프로그램들을 묶어 준다.
import shiffman.box2d.*;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.joints.*;
import org.jbox2d.collision.shapes.*;
import org.jbox2d.collision.shapes.Shape;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.*;
import org.jbox2d.dynamics.contacts.*;
Box2DProcessing box2d;
ArrayList<Boundary> boundaries; // 경개면을 표시함
ArrayList<Pair> pairs;
void setup(){
size(640, 480);
box2d = new Box2DProcessing(this);
box2d.createWorld();
pairs = new ArrayList<Pair>();
boundaries = new ArrayList<Boundary>();
// 경계면을 표시함(2개)
boundaries.add(new Boundary(width/4, height-5, width/2-50, 10));
boundaries.add(new Boundary(3*width/4, height-50, width/2-50, 10));
}
void draw(){
background(255);
fill(0);
box2d.step();
// Pair 객체를 표시함
for(Pair p:pairs){
p.display();
}
// 경계면을 표시함
for (Boundary wall: boundaries) {
wall.display();
}
}
// 마우스를 누르는 경우 객체를 생성함
void mousePressed() {
Pair p = new Pair(mouseX,mouseY);
pairs.add(p);
}
//---------------------------------------
// NATURE OF CODE-5 : Box2D Box 객체(Dynamic)를 Boundary 객체(Static) 쌓기
1. Box 객체를 만든다.(Body, Fixture, Shpae 그리고 조합)
Box는 생성 시점에 마우스가 위치가 지점의 좌표를 가져와 생성된다.
Box는 개별적으로 Body를 가지고 Shape가 정해지면 물리적 속성을 가진 Fixture에 고정되고
display()함수를 통해 표현되는 객체와 연결되어 움직인다.
class Box{
Body body; // Box2D Object(World, Body, Fixture, Shape
float w, h; //weight, height
Box(){
w = 16;
h = 16;
// 1. create bodyDef
BodyDef bd = new BodyDef();
bd.type = BodyType.DYNAMIC;
bd.position.set(box2d.coordPixelsToWorld(mouseX, mouseY));
// 2. define body
body = box2d.createBody(bd);
// 3. create Shape
PolygonShape ps = new PolygonShape();
float box2dW = box2d.scalarPixelsToWorld(w/2);
float box2dH = box2d.scalarPixelsToWorld(h/2);
ps.setAsBox(box2dW, box2dH);
//4. create FixtureDef
FixtureDef fd = new FixtureDef();
fd.shape = ps;
fd.friction=0.3; // 물리 속성(마찰)
fd.restitution=0.5; // 물리속성(탄성)
fd.density=1.0; // same as water(1kg/m3)
// 5. combinate body, fixture, shape
body.createFixture(fd);
}
void display(){
Vec2 pos = box2d.getBodyPixelCoord(body); // 위치를 가져온다.
float a = body.getAngle(); //각도를 가져온다.
pushMatrix();
translate(pos.x, pos.y); // body가 있는 위치로 좌표를 변환함
rotate(-a);
fill(175);
stroke(0);
rectMode(CENTER);
rect(0, 0, w, h);
popMatrix();
}
void killBody(){
box2d.destroyBody(body);
}
}
2. Boundary 객체를 만든다.(Body, Fixture, Shpae 그리고 조합)
고정된 벽 역활을 수행하는 객체이다.
class Boundary{
Body b; // Box2D Object(World, Body, Fixture, Shape
float x, y;
float w, h; //weight, height
Boundary(float x_, float y_, float w_, float h_){
x = x_;
y = y_;
w = w_;
h = h_;
// 1. create bodyDef
BodyDef bd = new BodyDef();
bd.type = BodyType.STATIC;
bd.position.set(box2d.coordPixelsToWorld(x,y));
// 2. define body
b = box2d.createBody(bd);
// 3. create Shape
PolygonShape ps = new PolygonShape();
float box2dW = box2d.scalarPixelsToWorld(w/2);
float box2dH = box2d.scalarPixelsToWorld(h/2);
ps.setAsBox(box2dW, box2dH);
// 5. combinate body, fixture, shape
b.createFixture(ps, 1); // 밀도가 1(kg/m3)
}
void display(){
fill(0);
stroke(0);
rectMode(CENTER);
rect(x, y, w, h);
}
}
3. Box와 Boundary가 놀 수 있는 홈구장을 만들어 준다.
import shiffman.box2d.*;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.joints.*;
import org.jbox2d.collision.shapes.*;
import org.jbox2d.collision.shapes.Shape;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.*;
import org.jbox2d.dynamics.contacts.*;
Box2DProcessing box2d;
ArrayList<Boundary> boundaries; // 경개면을 표시함
ArrayList<Box> boxes;
void setup(){
size(640, 480);
box2d = new Box2DProcessing(this);
box2d.createWorld();
boxes = new ArrayList<Box>();
boundaries = new ArrayList<Boundary>();
// 경계면을 표시함(2개)
boundaries.add(new Boundary(width/4, height-5, width/2-50, 10));
boundaries.add(new Boundary(3*width/4, height-50, width/2-50, 10));
}
void draw(){
background(255);
fill(0);
box2d.step();
// Pair 객체를 표시함
for(Box b:boxes){
b.display();
}
// 경계면을 표시함
for (Boundary wall: boundaries) {
wall.display();
}
}
// 마우스를 누르는 경우 객체를 생성함
void mousePressed() {
Box p = new Box();
boxes.add(p);
}
//---------------------------------------
// NATURE OF CODE-5 : 5.11 Box2D Revolute Joint를 통한 객체의 연결
Revolute Joint 객체는 Body 객체와 다른 Body 객체와 고정점으로 연결하여 회전케함
예제는 마우스를 클릭할때 마다. 풍차를 하나씩 만들어 주고 회전하도록 함
1. Box.pde
기존에 만든 Box에서 생성자에 고정(Static)과 동적(Dynamic)을 만들어 주고 박스의 생성 위치와 넒이와 높이를 지정하도록 변경함
class Box{
Body body; // Box2D Object(World, Body, Fixture, Shape
float w, h; //weight, height
Box(float x_, float y_, float w_, float h_, boolean lock){
w = w_;
h = h_;
// 1. create bodyDef
BodyDef bd = new BodyDef();
// 파라미터로 전달되는 boolean값에 따라 객체를 동적, 혹은 정적으로 정의함
if (lock)
bd.type = BodyType.STATIC;
else
bd.type = BodyType.DYNAMIC;
bd.position.set(box2d.coordPixelsToWorld(x_, y_));
// 2. define body
body = box2d.createBody(bd);
// 3. create Shape
PolygonShape ps = new PolygonShape();
float box2dW = box2d.scalarPixelsToWorld(w/2);
float box2dH = box2d.scalarPixelsToWorld(h/2);
ps.setAsBox(box2dW, box2dH);
//4. create FixtureDef
FixtureDef fd = new FixtureDef();
fd.shape = ps;
fd.friction=0.3; // 물리 속성(마찰)
fd.restitution=0.5; // 물리속성(탄성)
fd.density=1.0; // same as water(1kg/m3)
// 5. combinate body, fixture, shape
body.createFixture(fd);
}
void display(){
Vec2 pos = box2d.getBodyPixelCoord(body); // 위치를 가져온다.
float a = body.getAngle(); //각도를 가져온다.
pushMatrix();
translate(pos.x, pos.y); // body가 있는 위치로 좌표를 변환함
rotate(-a);
fill(175);
stroke(0);
rectMode(CENTER);
rect(0, 0, w, h);
popMatrix();
}
void killBody(){
box2d.destroyBody(body);
}
}
2. Windmill.pde
2개의 객체(Box)를 통하여 한개의 Box는 고정(Static) 다른 한개는 동적(Dynamic)으로 만들어 RevoluteJoint 객체를 통하여 풍차를 만든다.
class Windmill{
RevoluteJoint joint;
Box box1; // Body를 가지고 있는 객체
Box box2; // Body를 가지고 있는 객체
Windmill(float x, float y){
//1. 2개의 객체 준비(생성자)
box1 = new Box(x, y, 120, 10, false);
box2 = new Box(x, y, 10, 40, true);
// 2. JointDef 객체 생성
RevoluteJointDef rjd = new RevoluteJointDef();
//3. JointDef 객체의 속성 정의(객체의 어디에 위치하며, 길이등)
rjd.initialize(box1.body, box2.body, box1.body.getWorldCenter());
rjd.enableMotor = true; //모터의 작동 상태
rjd.motorSpeed = PI*2; // 회전 정도
rjd.maxMotorTorque = 1000.0; //회전 강도
//4. Joint 객체 생성
joint = (RevoluteJoint) box2d.world.createJoint(rjd);
}
// 모타를 켜고 끕니다.
void toggleMotor(){
boolean motorstatus = joint.isMotorEnabled();
joint.enableMotor(!motorstatus);
}
void display(){
box1.display();
box2.display();
}
}
3. main_RevoluteJoint
실제 화면상에서 풍차를 돌린다.
마우스를 클릭할때 마다 풍차가 생성된다.
class Windmill{
RevoluteJoint joint;
Box box1; // Body를 가지고 있는 객체
Box box2; // Body를 가지고 있는 객체
Windmill(float x, float y){
//1. 2개의 객체 준비(생성자)
box1 = new Box(x, y, 120, 10, false);
box2 = new Box(x, y, 10, 40, true);
// 2. JointDef 객체 생성
RevoluteJointDef rjd = new RevoluteJointDef();
//3. JointDef 객체의 속성 정의(객체의 어디에 위치하며, 길이등)
rjd.initialize(box1.body, box2.body, box1.body.getWorldCenter());
rjd.enableMotor = true; //모터의 작동 상태
rjd.motorSpeed = PI*2; // 회전 정도
rjd.maxMotorTorque = 1000.0; //회전 강도
//4. Joint 객체 생성
joint = (RevoluteJoint) box2d.world.createJoint(rjd);
}
// 모타를 켜고 끕니다.
void toggleMotor(){
boolean motorstatus = joint.isMotorEnabled();
joint.enableMotor(!motorstatus);
}
void display(){
box1.display();
box2.display();
}
}
//---------------------------------------
// NATURE OF CODE-5 : 5.11 Box2D Revolute Joint(풍차)가 외부와 연계
고정된 풍차의 모터 동작(회전)을 마우스로 켜기나 끄도록 하며
외부 객체(Particle)이 풍차의 날개에 영향을 주는 현상을 관찰하자.
1. Particle
기존에는 반지름이 8로 고정했으나 입력을 받아서 변경할 수 있도록 수정하고
객체가 더이상 화면에 존재하지 않을 경우 삭제하는 done() 함수 추가한다.
class Particle{
Body body; // Box2D Object(World, Body, Fixture, Shape
float r; //반지름 정의
Particle(float x, float y, float r_){
r = r_; // 반지름
// 1. create bodyDef
BodyDef bd = new BodyDef();
bd.type = BodyType.DYNAMIC;
bd.position.set(box2d.coordPixelsToWorld(x, y));
// 2. define body
body = box2d.world.createBody(bd);
// 3. 객체 만들기
CircleShape cs = new CircleShape();
cs.m_radius = box2d.scalarPixelsToWorld(r);
//4. create FixtureDef
FixtureDef fd = new FixtureDef();
fd.shape = cs;
fd.friction=0.01; // 물리 속성(마찰)
fd.restitution=0.3; // 물리속성(탄성)
fd.density=1.0; // same as water(1kg/m3)
// 5. combinate body, fixture, shape
body.createFixture(fd);
body.setAngularVelocity(random(-10, 10)); // 각 속도를 지정함
}
void display(){
Vec2 pos = box2d.getBodyPixelCoord(body); // 위치를 가져온다.
float a = body.getAngle(); //각도를 가져온다.
pushMatrix();
translate(pos.x, pos.y); // body가 있는 위치로 좌표를 변환함
rotate(-a);
fill(127);
stroke(0);
strokeWeight(2);
ellipse(0, 0, r*2, r*2);
line(0, 0, r, 0); // 원에 반지름을 표시해서 회전 여부 확인
popMatrix();
}
void killBody(){
box2d.destroyBody(body);
}
// 화면상에 더더이상 객체가 존재하지 않는지를 확인하고 삭제함
boolean done(){
Vec2 pos = box2d.getBodyPixelCoord(body);
if(pos.y > height+r*2) { // 화면 공간을 벋어남
killBody();
return true;
}
return false;
}
}
2. Box.pde
기존에 만든 Box에서 생성자에 고정(Static)과 동적(Dynamic)을 만들어 주고 박스의 생성 위치와 넒이와 높이를 지정하도록 변경함
class Box{
Body body; // Box2D Object(World, Body, Fixture, Shape
float w, h; //weight, height
Box(float x_, float y_, float w_, float h_, boolean lock){
w = w_;
h = h_;
// 1. create bodyDef
BodyDef bd = new BodyDef();
// 파라미터로 전달되는 boolean값에 따라 객체를 동적, 혹은 정적으로 정의함
if (lock)
bd.type = BodyType.STATIC;
else
bd.type = BodyType.DYNAMIC;
bd.position.set(box2d.coordPixelsToWorld(x_, y_));
// 2. define body
body = box2d.createBody(bd);
// 3. create Shape
PolygonShape ps = new PolygonShape();
float box2dW = box2d.scalarPixelsToWorld(w/2);
float box2dH = box2d.scalarPixelsToWorld(h/2);
ps.setAsBox(box2dW, box2dH);
//4. create FixtureDef
FixtureDef fd = new FixtureDef();
fd.shape = ps;
fd.friction=0.3; // 물리 속성(마찰)
fd.restitution=0.5; // 물리속성(탄성)
fd.density=1.0; // same as water(1kg/m3)
// 5. combinate body, fixture, shape
body.createFixture(fd);
}
void display(){
Vec2 pos = box2d.getBodyPixelCoord(body); // 위치를 가져온다.
float a = body.getAngle(); //각도를 가져온다.
pushMatrix();
translate(pos.x, pos.y); // body가 있는 위치로 좌표를 변환함
rotate(-a);
fill(175);
stroke(0);
rectMode(CENTER);
rect(0, 0, w, h);
popMatrix();
}
void killBody(){
box2d.destroyBody(body);
}
}
3. Windmill.pde
2개의 객체(Box)를 통하여 한개의 Box는 고정(Static) 다른 한개는 동적(Dynamic)으로 만들어 RevoluteJoint 객체를 통하여 풍차를 만든다.
class Windmill{
RevoluteJoint joint;
Box box1; // Body를 가지고 있는 객체
Box box2; // Body를 가지고 있는 객체
Windmill(float x, float y){
//1. 2개의 객체 준비(생성자)
box1 = new Box(x, y, 120, 10, false);
box2 = new Box(x, y, 10, 40, true);
// 2. JointDef 객체 생성
RevoluteJointDef rjd = new RevoluteJointDef();
//3. JointDef 객체의 속성 정의(객체의 어디에 위치하며, 길이등)
rjd.initialize(box1.body, box2.body, box1.body.getWorldCenter());
rjd.enableMotor = true; //모터의 작동 상태
rjd.motorSpeed = PI*2; // 회전 정도
rjd.maxMotorTorque = 1000.0; //회전 강도
//4. Joint 객체 생성
joint = (RevoluteJoint) box2d.world.createJoint(rjd);
}
// 모타를 켜고 끕니다.
void toggleMotor(){
boolean motorstatus = joint.isMotorEnabled();
joint.enableMotor(!motorstatus);
}
// 모타의 상태를 읽어 옴
boolean motorOn(){
return joint.isMotorEnabled();
}
void display(){
box1.display();
box2.display();
// 풍차의 축을 그려줌
Vec2 anchor = box2d.coordWorldToPixels(box1.body.getWorldCenter());
fill(0);
noStroke();
ellipse(anchor.x, anchor.y, 8, 8);
}
}
4. main_RevoluteJoint
import shiffman.box2d.*;
import org.jbox2d.common.*;
import org.jbox2d.collision.shapes.*;
import org.jbox2d.collision.shapes.Shape;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.*;
import org.jbox2d.dynamics.joints.*;
import org.jbox2d.dynamics.contacts.*;
Box2DProcessing box2d;
Windmill windmill;
ArrayList<Particle> particles;
void setup(){
size(480, 320);
box2d = new Box2DProcessing(this);
box2d.createWorld();
windmill = new Windmill(width/2, 200);
particles = new ArrayList<Particle>();
}
void draw(){
background(255);
if(random(1) < 0.1) { // 기본 framecount는 60에서 10% 즉 초당 6개의 객체 생성
float sz = random(4,8); // 반지름음 4~8 사이에서 임의 생성
particles.add(new Particle(random(width/2-100, width/2+100), -20, sz));
}
box2d.step();
// Windmill을 표시함
windmill.display();
// 하늘에서 별이 떨어지듯 객체(Particle)이 떨어짐
for (int i = particles.size()-1; i>=0; i--){
Particle p = particles.get(i);
p.display();
// 더이상 화면상에 객체가 없으면 삭제함
if(p.done()){
particles.remove(i);
}
}
// 모타의 동작 상태를 화면에 보여줌
String status = "OFF";
if(windmill.motorOn())
status = "ON";
else
status = "OFF";
fill(0);
text("마우스를 클릭하면 모타의 상태가 변경됨.\n 모타 : " + status, 10, height-50);
}
// 마우스를 누르는 경우 풍차의 모타를 켜거나 끔
void mousePressed() {
windmill.toggleMotor();
}
//----------------------------------------------------------
// NATURE OF CODE-5 : 5.11 Box2D MouseJoint
객체의 Body를 마우스로 옮길때 사용하는 객체임(마우스의 좌표에 따라 움직임)
'processing' 카테고리의 다른 글
NATURE OF CODE-0.INTRODUCTION (0) | 2016.05.07 |
---|---|
PROCESSING 언어 (0) | 2016.05.07 |
NATURE OF CODE-5 : Boxes 예제(원을 만들어 경사면을 구르게 하기) (0) | 2016.05.06 |
NATURE OF CODE-5 : Boxes 예제(경계면을 고정으로 만들기) (0) | 2016.05.06 |
NATURE OF CODE-5 : Boxes 예제(Box2D 라이브러리 사용) (0) | 2016.05.06 |