Difference between revisions of "Tutorial:PhysicsDrawing (日本語)"

(beginning translation.)
m (All draft translations is completed. Happy holyday!)
 
(One intermediate revision by the same user not shown)
Line 3: Line 3:
 
== 物理演算による衝突の描画 ==
 
== 物理演算による衝突の描画 ==
  
When developing with Box2D (aka love.physics) you will likely get to a point where the collisions don't quite match up with what you expect them to be due to some minor mistake.
+
Box2D (aka love.physics) で開発中に、ちょっとした間違いで想定していた衝突地点と全く当たらない (一致しない) ことがあると思います。
  
At the end of this guide there's a complete snippet that will draw all collisions at the correct location. If you just want the code feel free to jump down and just copy & paste it. But we will go over what the code does bit by bit.
+
このガイドの最後に、正しい位置で衝突処理を全て描画する完成版のコードスニペットを掲載してあります。コードだけが必要でした遠慮せずにガイドの最後へ移動してコピーと貼り付けをしてください。
  
love.physics works via a few different sets of objects. Most notably. "World", "Body", "Shape" and "Fixture".
+
はい、注目。 love.physics には動作の違うオブジェクトが数種類あります ー "World" (世界、ワールド), "Body" (物体), "Shape" (形状) および"Fixture" (取付具)。
  
* "World" is the physics world. You can have multiple ones but only objects within the same world will collide.
+
* "World" は物理演算 (剛体力学) が作用する世界、あるいは舞台です。オブジェクトを多数配置できますが、同じ World にあるオブジェクト同士に限り衝突できます。
  
* "Body" is the location of your object within the physics world. It defines which world this object belongs to, if it can move and the mass of the object (which will be calculated from the size of your shape by default). It also holds a reference to the fixture.
+
* "Body" (物体) は物理演算 (剛体力学) が作用する世界に配置されたオブジェクトです。オブジェクトは移動可能であり質量がある場合は (デフォルトでは Shape の寸法から計算します)、このオブジェクトの所属先となる世界を定義します。
  
* "Shape" is the collision itself. If two shapes collide, the physics system will handle it. It also holds a reference to the fixture.
+
* "Shape" は自己衝突します。二個の Shape が衝突する場合は、物理演算システムで処理します。また Fixture の参照を維持します。
  
* "Fixture" is how we attach a shape to a body. It defines a few additional properties how collisions will be handled (such as friction, collision channels and a few more). It also holds a reference to the body and the shape.
+
* "Fixture" は Body へ Shape を接続する方法を指定します。衝突の処理方法に関するプロパティを定義します (摩擦、衝突チャンネルなど)。また Body と Shape の参照を維持します。
  
In order to draw all shapes we need to start at the world. It handles all collisions. So it's gotta contain a list of things. Specifically. It contains a list of bodies which we can get in the following way:
+
Shape をすべて描画には、ワールドを創造する必要があります。これで衝突をすべて処理します。オブジェクトは必ずリストに収容してください。具体的には、この方法により Body をリストへ格納できます。
  
 
<source lang="lua">
 
<source lang="lua">
Line 23: Line 23:
 
</source>
 
</source>
  
We don't need a list of bodies though. We just wanna do something for every body. So we make a for loop out of it:
+
けれども Body のリストは不要です。何らかの処理を Body に施したいだけです。処理をするにはfor ループで取り出します。
  
 
<source lang="lua">
 
<source lang="lua">
Line 31: Line 31:
 
</source>
 
</source>
  
A body can have multiple shapes which are attached with multiple fixtures. So we have to iterate over the list of fixtures as well:
+
Body には複数の Fixture を取り付けた複数 Shape を構成できます。そのため Fixture リストもイテレートを行う必要があります。
 
 
 
<source lang="lua">
 
<source lang="lua">
 
for _, body in pairs(World:getBodyList()) do
 
for _, body in pairs(World:getBodyList()) do
Line 41: Line 40:
 
</source>
 
</source>
  
A fixture however can only have one shape. So now we already have a reference to all collision fixtures (and therefore shapes) there are within this loop!
+
しかしながら Fixture に取り付け可能な Shape は一つだけです。さて、このループには全ての Fixture の衝突に関する基準地点が既にあります!
  
We can simply store a reference to the shape by doing
+
このようにすることで Shape への参照を格納できます。
  
 
<source lang="lua">
 
<source lang="lua">
Line 55: Line 54:
 
</source>
 
</source>
  
さて、描画してみましょう。様々な Shape があります。詳しく説明すると、
+
さて、描画してみましょう。様々な Shape があります。詳しく説明すると、このようになります。
  
 
* CircleShape
 
* CircleShape
Line 74: Line 73:
 
</source>
 
</source>
  
cx & cy stand for "CircleX" and "CircleY". The position we want to draw it at.
+
cx & cy "CircleX" "CircleY" に対応します。つまり描画したい位置を指定します。
  
body:getWorldPoints() will take any amount of points and convert them into world space (a point being two numbers. One for x and one for y). In other words. It will do "x + body:getX()", "y + body:getY()" for all points provided.
+
body:getWorldPoints() は点の個数とワールド空間の変換処理をします (x y の二種類の数値を指定します)。換言すれば、 "x + body:getX()", "y + body:getY()" はすべて指定された点となります。
  
Then we simply draw the circle at the position we just calculated with the radius of the shape. I choose "fill" as first parameter. You can also use "line". "fill" means it'll be drawn as circle. "fill" will draw it as a ring.
+
そのあとに、 Shape の半径を算出して出た位置へ円を描画します。第一引数には輪を描画する "fill" を指定します。また、円を描画する "line" も使えます。
  
 
== PolygonShape の描画 ==
 
== PolygonShape の描画 ==
  
Next up is the PolygonShape. A polygon is a list of points with up to 8 vertices (aka 16 numbers representing x and y. e.g. (x1, y1, x2, y1, ..., xn, yn) )
+
お次は PolygonShape です。PolygonShape は最大 8 頂点から成るリストです (x y を16 の個数で表したものとして知られています。つまり、(x1, y1, x2, y1, ..., xn, yn) です)
  
We can abuse a little bit of functionality of lua to make this a super small snippet of code. We can get all the points by calling "shape:getPoints()" (once we verified it's a polygon shape).
+
このコードを超小型スペットにするために、ちょっと Lua の機能を悪用します。つまり、 "shape:getPoints()" を呼び出すと全地点を取得できます (PolygonShape であることを確認後に)
  
This will return up to 16 individual numbers. However. If we put this directly into another function without following parameter. It will simply put the next number as next parameter.
+
これは最大16までの数値を返します。しかしながら、これを引数の後ではなく別の関数へ直接指定しますと、この引数は下記の数値を出力します。
  
 
<source lang="lua">
 
<source lang="lua">
Line 99: Line 98:
 
add(2, 2) --> 4 の表示。
 
add(2, 2) --> 4 の表示。
  
add(f()) --> 3 の表示。 We returned multiple parameters which are simply considered as following parameters. So a -> 1, b -> 2.
+
add(f()) --> 3 の表示。 引数の指定後に複数の引数が返されるものとします。つまり、 a -> 1, b -> 2 です。
  
add(f(), 0) --> 1 の表示。 We returned multiple parameters. However those are directly followed by another value. So only the first return value is used and the others will be discarded. a -> 1, b -> 0
+
add(f(), 0) --> 1 の表示。 複数の引数が返されますが、その後にも値があります。最初の返値のみ使用され、他の値は無視されます。つまり a -> 1, b -> 0 です。
 
</source>
 
</source>
  
Knowing this we can do the following:
+
これを理解すると、このようなことができます。
  
 
<source lang="lua">
 
<source lang="lua">
Line 110: Line 109:
 
</source>
 
</source>
  
Since there is no following parameter it will simply put as many points in the function as the shape has. We don't have to handle anything else!
+
この引数がないため、 Shape の複数地点を関数で簡単に扱えます。それ以外の処理は不要です!
  
 
== EdgeShape と ChainShape の描画 ==
 
== EdgeShape と ChainShape の描画 ==
  
Lastly we have edge and chain shapes. They are essentially the same. Internally they are handled a bit differently. But for our case we can assume an EdgeShape to be the same as a ChainShape with two points. A ChainShape can have two or more points (this is the only shape without a limit).
+
最後は EdgeShape ChainShape です。これは本質的には同じものですが、内部処理では少し違うものとして扱われます。けれども、今回は EdgeShape が 2 点から成るChainShape として考えることにしましょう。 ChainShapeは 2 点以上から構成できます (これは唯一制限のない Shape です)
  
However. Both are made up of lines that may or may not loop around. And both have the function "shape:getPoints()" as well! So we do not have to treat them separately.
+
しかしながら、互いにループする、またはループしない線で構成されています。そして互いに同じ関数である "shape:getPoints()" が使えます! よって、個別に扱わなくても問題ありません。
  
We pretty much do the same as we did for the polygon. But instead of drawing a polygon we draw a line.
+
けれども、多角形の描画ではなく、線の描画となります。
  
 
<source lang="lua">
 
<source lang="lua">

Latest revision as of 14:05, 21 December 2019

これはチュートリアルではなく物理演算プログラムのデバッグで便利な説明付きのコードスニペットです。

物理演算による衝突の描画

Box2D (aka love.physics) で開発中に、ちょっとした間違いで想定していた衝突地点と全く当たらない (一致しない) ことがあると思います。

このガイドの最後に、正しい位置で衝突処理を全て描画する完成版のコードスニペットを掲載してあります。コードだけが必要でした遠慮せずにガイドの最後へ移動してコピーと貼り付けをしてください。

はい、注目。 love.physics には動作の違うオブジェクトが数種類あります ー "World" (世界、ワールド), "Body" (物体), "Shape" (形状) および"Fixture" (取付具)。

  • "World" は物理演算 (剛体力学) が作用する世界、あるいは舞台です。オブジェクトを多数配置できますが、同じ World にあるオブジェクト同士に限り衝突できます。
  • "Body" (物体) は物理演算 (剛体力学) が作用する世界に配置されたオブジェクトです。オブジェクトは移動可能であり質量がある場合は (デフォルトでは Shape の寸法から計算します)、このオブジェクトの所属先となる世界を定義します。
  • "Shape" は自己衝突します。二個の Shape が衝突する場合は、物理演算システムで処理します。また Fixture の参照を維持します。
  • "Fixture" は Body へ Shape を接続する方法を指定します。衝突の処理方法に関するプロパティを定義します (摩擦、衝突チャンネルなど)。また Body と Shape の参照を維持します。

Shape をすべて描画には、ワールドを創造する必要があります。これで衝突をすべて処理します。オブジェクトは必ずリストに収容してください。具体的には、この方法により Body をリストへ格納できます。

listOfBodies = World:getBodyList()

けれども Body のリストは不要です。何らかの処理を Body に施したいだけです。処理をするにはfor ループで取り出します。

for _, body in pairs(World:getBodyList()) do
    -- 執筆予定
end

Body には複数の Fixture を取り付けた複数 Shape を構成できます。そのため Fixture リストもイテレートを行う必要があります。

for _, body in pairs(World:getBodyList()) do
    for _, fixture in pairs(body:getFixtureList()) do
        -- 執筆予定
    end
end

しかしながら Fixture に取り付け可能な Shape は一つだけです。さて、このループには全ての Fixture の衝突に関する基準地点が既にあります!

このようにすることで Shape への参照を格納できます。

for _, body in pairs(World:getBodyList()) do
    for _, fixture in pairs(body:getFixtureList()) do
        local shape = fixture:getShape()

        -- 衝突処理の描画 (執筆予定)
    end
end

さて、描画してみましょう。様々な Shape があります。詳しく説明すると、このようになります。

  • CircleShape
  • PolygonShape
  • EdgeShape
  • ChainShape

CircleShape の描画

まず、 CircleShape から説明しましょう。このコードで描画します。

local cx, cy = body:getWorldPoints(shape:getPoint())
love.graphics.circle("fill", cx, cy, shape:getRadius())

cx & cy は "CircleX" と "CircleY" に対応します。つまり描画したい位置を指定します。

body:getWorldPoints() は点の個数とワールド空間の変換処理をします (x と y の二種類の数値を指定します)。換言すれば、 "x + body:getX()", "y + body:getY()" はすべて指定された点となります。

そのあとに、 Shape の半径を算出して出た位置へ円を描画します。第一引数には輪を描画する "fill" を指定します。また、円を描画する "line" も使えます。

PolygonShape の描画

お次は PolygonShape です。PolygonShape は最大 8 頂点から成るリストです (x と y を16 の個数で表したものとして知られています。つまり、(x1, y1, x2, y1, ..., xn, yn) です)。

このコードを超小型スペットにするために、ちょっと Lua の機能を悪用します。つまり、 "shape:getPoints()" を呼び出すと全地点を取得できます (PolygonShape であることを確認後に)。

これは最大16までの数値を返します。しかしながら、これを引数の後ではなく別の関数へ直接指定しますと、この引数は下記の数値を出力します。

add = function(a, b)
    print( a + b )
end

f = function()
    return 1, 2
end

add(2, 2) --> 4 の表示。

add(f()) --> 3 の表示。 引数の指定後に複数の引数が返されるものとします。つまり、 a -> 1, b -> 2 です。

add(f(), 0) --> 1 の表示。 複数の引数が返されますが、その後にも値があります。最初の返値のみ使用され、他の値は無視されます。つまり a -> 1, b -> 0 です。

これを理解すると、このようなことができます。

love.graphics.polygon("fill", body:getWorldPoints(shape:getPoints()))

この引数がないため、 Shape の複数地点を関数で簡単に扱えます。それ以外の処理は不要です!

EdgeShape と ChainShape の描画

最後は EdgeShape と ChainShape です。これは本質的には同じものですが、内部処理では少し違うものとして扱われます。けれども、今回は EdgeShape が 2 点から成るChainShape として考えることにしましょう。 ChainShapeは 2 点以上から構成できます (これは唯一制限のない Shape です)。

しかしながら、互いにループする、またはループしない線で構成されています。そして互いに同じ関数である "shape:getPoints()" が使えます! よって、個別に扱わなくても問題ありません。

けれども、多角形の描画ではなく、線の描画となります。

love.graphics.line(body:getWorldPoints(shape:getPoints()))

完成版のコード

画面上の正しい位置で衝突処理を全て描画する完成版のコードスニペットを掲載しておきます!

Happy debugging! (ハッピー・デバッギング!)

for _, body in pairs(world:getBodyList()) do
    for _, fixture in pairs(body:getFixtureList()) do
        local shape = fixture:getShape()

        if shape:typeOf("CircleShape") then
            local cx, cy = body:getWorldPoints(shape:getPoint())
            love.graphics.circle("fill", cx, cy, shape:getRadius())
        elseif shape:typeOf("PolygonShape") then
            love.graphics.polygon("fill", body:getWorldPoints(shape:getPoints()))
        else
            love.graphics.line(body:getWorldPoints(shape:getPoints()))
        end
    end
end

そのほかの言語