Kuina-chan

くいなちゃん2018年12月11日


プログラミング言語Kuin」の逆引き辞典10、グラフィックスの描画の方法についてです。

目次

1ドローコントロールを扱う

1.1ドローコントロールを作成する



Kuinにおける2Dや3Dの高度なグラフィックスはすべて、ドローコントロールを使用して描画します。
ドローコントロールを作成するには「wnd@makeDraw(親ウインドウ,X座標,Y座標,,高さ,アンカーX,アンカーY,ドットバイドットにするかどうか)」とします(図1-1)。
  1. var wndMain: wnd@Wnd
  2. var drawMain: wnd@Draw
  3.  
  4. func main()
  5.   do @wndMain :: wnd@makeWnd(null, %aspect, 1600, 900, "Title")
  6.   do @drawMain :: wnd@makeDraw(@wndMain, 0, 0, 1600, 900, %scale, %scale, false)
  7.  
  8.   while(wnd@act())
  9.     do draw@render(60)
  10.   end while
  11. end func
図1-1: ドローコントロールの作成
第6、7引数のアンカーは、親ウインドウのサイズが変わったときに、サイズを固定したままにするには「%fix」、一緒に拡縮するには「%scale」、右下にくっつくように移動するには「%move」とします。
第8引数は、コントロールのサイズが変わったときに描画内容を、ドットバイドット(等倍を維持)にするには「true」、コントロールのサイズに合わせて拡縮するには「false」とします。
ドローコントロールは、描画途中の結果が見えないように、一度バックバッファと呼ばれる見えないバッファに描画しておいてから最終的な画面に転送します。 9行目の「draw@render」は、バックバッファから最終的な画面に転送する関数で、いろいろな図形を描画した後にdraw@renderで転送する流れにします。
draw@renderの引数に0以外の値を渡すと、1秒間に指定回数だけ画面が更新(FPS)されるように、待機して時間を調整します。 例えば引数に60を渡すと、1秒間に60回画面が更新されるようになります。 ゲームを作る場合は60か30を指定することが多いです。 0を渡すと待機せずに描画結果の転送だけを行います。
実行すると図1-2のようになります。
ドローコントロール
図1-2: ドローコントロール

1.2背景色を変える



draw@render関数で画面を転送したときに、バックバッファは自動でクリアされます。 このクリア時の背景色は変更でき、またクリアを自動ではせずに手動で行うようにもできます。
クリア時の背景色はデフォルトでは黒ですが、これを変更するには「draw@clearColor()」とします(図1-3)。
  1. var wndMain: wnd@Wnd
  2. var drawMain: wnd@Draw
  3.  
  4. func main()
  5.   do @wndMain :: wnd@makeWnd(null, %aspect, 1600, 900, "Title")
  6.   do @drawMain :: wnd@makeDraw(@wndMain, 0, 0, 1600, 900, %scale, %scale, false)
  7.  
  8.   do draw@clearColor(0xFFCC6666)
  9.  
  10.   while(wnd@act())
  11.     do draw@render(60)
  12.   end while
  13. end func
図1-3: クリアカラーの設定
色は16進数で2桁ずつ「0x不透明度」と指定します。 例えば青は「0xFF0000FF」になります。 また白と黒は定数が定義されており、白(0xFFFFFFFF)は「draw@white」と書け、黒(0xFF000000)は「draw@black」と書けます。
実行すると図1-4のようになります。
クリアカラー
図1-4: クリアカラー
draw@render関数の呼び出し時に自動でクリアをしないようにするには、事前に「autoClear(false)」を呼び出しておきます。 この場合「draw@clear()」を呼び出すことで手動でクリアできます。

1.3描画先のドローコントロールを切り替える



「draw@」ライブラリでの描画や操作はすべて、最後に作成したドローコントロールに対して行われます。 この操作対象のドローコントロールを切り替えるには「draw@target(ドローコントロール)」とします(図1-5)。
  1. var wndMain: wnd@Wnd
  2. var draw1: wnd@Draw
  3. var draw2: wnd@Draw
  4.  
  5. func main()
  6.   do @wndMain :: wnd@makeWnd(null, %aspect, 1600, 900, "Title")
  7.   do @draw1 :: wnd@makeDraw(@wndMain, 0, 0, 797, 900, %fix, %fix, false)
  8.   do @draw2 :: wnd@makeDraw(@wndMain, 803, 0, 797, 900, %fix, %fix, false)
  9.  
  10.   while(wnd@act())
  11.     do draw@target(@draw1) {操作対象を@draw1に切り替え}
  12.     do draw@clearColor(0xFFCC6666) {背景色を赤にする}
  13.     do draw@render(0) {@draw1のバックバッファを転送してクリア}
  14.     do draw@target(@draw2) {操作対象を@draw2に切り替え}
  15.     do draw@clearColor(0xFF6666CC) {背景色を青にする}
  16.     do draw@render(60) {@draw2のバックバッファを転送してクリア}
  17.   end while
  18. end func
図1-5: 操作対象の切り替えの処理
実行すると図1-6のようになります。
操作対象の切り替え
図1-6: 操作対象の切り替え

2単純な図形を描画する

2.1線分を描画する



線分を描画するには「draw@line(始点X,始点Y,終点X,終点Y,)」または「draw2d@line(始点X,始点Y,終点X,終点Y,線の太さ)」とします(図2-1)。
  1. var wndMain: wnd@Wnd
  2. var drawMain: wnd@Draw
  3.  
  4. func main()
  5.   do @wndMain :: wnd@makeWnd(null, %aspect, 1600, 900, "Title")
  6.   do @drawMain :: wnd@makeDraw(@wndMain, 0, 0, 1600, 900, %scale, %scale, false)
  7.  
  8.   while(wnd@act())
  9.     do draw@line(1500.0, 100.0, 100.0, 800.0, 0xFFFFFF00)
  10.     do draw2d@line(100.0, 100.0, 1500.0, 800.0, 5.0, 0xFFFFFF00)
  11.     do draw@render(60)
  12.   end while
  13. end func
図2-1: 線分の描画
draw@line関数は高速に描画できますが、アンチエイリアス処理は行われず、線の太さも指定できません。 単純な描画を行いたいとき以外はdraw2d@line関数を使ったほうが綺麗です。
実行すると図2-2のようになります。
線分
図2-2: 線分

2.2四角形を描画する



塗りつぶしの四角形を描画するには「draw@rect(始点X,始点Y,,高さ,)」または「draw2d@rect(始点X,始点Y,,高さ,線の太さ,)」とし、塗りつぶさない四角形を描画するには「draw@rectLine(始点X,始点Y,,高さ,)」または「draw2d@rectLine(始点X,始点Y,,高さ,)」とします(図2-3)。
  1. var wndMain: wnd@Wnd
  2. var drawMain: wnd@Draw
  3.  
  4. func main()
  5.   do @wndMain :: wnd@makeWnd(null, %aspect, 1600, 900, "Title")
  6.   do @drawMain :: wnd@makeDraw(@wndMain, 0, 0, 1600, 900, %scale, %scale, false)
  7.  
  8.   while(wnd@act())
  9.     do draw@rect(100.0, 100.0, 100.0, 200.0, 0xFF00FFFF)
  10.     do draw@rectLine(300.0, 100.0, 100.0, 200.0, 0xFF00FF00)
  11.     do draw2d@rect(500.0, 100.0, 100.0, 200.0, 0xFF00FFFF)
  12.     do draw2d@rectLine(700.0, 100.0, 100.0, 200.0, 5.0, 0xFF00FF00)
  13.     do draw@render(60)
  14.   end while
  15. end func
図2-3: 四角形の描画
draw@rect関数やdraw@rectLine関数は高速に描画できますが、アンチエイリアス処理は行われず、線の太さも指定できません。 単純な描画を行いたいとき以外はdraw2d@rect関数やdraw2d@rectLine関数を使ったほうが綺麗です。
実行すると図2-4のようになります。
四角形
図2-4: 四角形

2.3円を描画する



塗りつぶしの円を描画するには「draw2d@circle(中心X,中心Y,半径X,半径Y,)」とし、塗りつぶさない円を描画するには「draw2d@circleLine(中心X,中心Y,半径X,半径Y,)」とします(図2-5)。
  1. var wndMain: wnd@Wnd
  2. var drawMain: wnd@Draw
  3.  
  4. func main()
  5.   do @wndMain :: wnd@makeWnd(null, %aspect, 1600, 900, "Title")
  6.   do @drawMain :: wnd@makeDraw(@wndMain, 0, 0, 1600, 900, %scale, %scale, false)
  7.  
  8.   while(wnd@act())
  9.     do draw2d@circle(200.0, 200.0, 100.0, 100.0, 0xFFFF00FF)
  10.     do draw2d@circleLine(500.0, 200.0, 100.0, 100.0, 5.0, 0xFFFF00FF)
  11.     do draw@render(60)
  12.   end while
  13. end func
図2-5: 円の描画
実行すると図2-6のようになります。
円
図2-6: 円

3画像を描画する

3.1画像ファイルを描画する



画像ファイルを描画するには「draw@makeTex(ファイルパス)」で読み込んでから、「draw@Tex.draw(転送先X,転送先Y,転送元X,転送元Y,転送元幅,転送元高さ,)」で描画します(図3-1)。
  1. var wndMain: wnd@Wnd
  2. var drawMain: wnd@Draw
  3.  
  4. func main()
  5.   do @wndMain :: wnd@makeWnd(null, %aspect, 1600, 900, "Title")
  6.   do @drawMain :: wnd@makeDraw(@wndMain, 0, 0, 1600, 900, %scale, %scale, false)
  7.  
  8.   var tex: draw@Tex :: draw@makeTex("res/dot_back_side.png")
  9.  
  10.   while(wnd@act())
  11.     do tex.draw(0.0, 0.0, 0.0, 0.0, 800.0, 450.0, draw@white)
  12.     do draw@render(60)
  13.   end while
  14. end func
図3-1: 画像の描画
読み込める画像ファイルの形式は、「.png」「.jpg」「.dds」です。
「res/dot_back_side.png」に画像ファイルを用意して実行すると図3-2のようになります。
画像
図3-2: 画像

3.2画像を拡縮して描画する



画像を拡縮して描画するには「draw@Tex.drawScale(転送先X,転送先Y,転送先幅,転送先高さ,転送元X,転送元Y,転送元幅,転送元高さ,)」とします(図3-3)。
  1. var wndMain: wnd@Wnd
  2. var drawMain: wnd@Draw
  3.  
  4. func main()
  5.   do @wndMain :: wnd@makeWnd(null, %aspect, 1600, 900, "Title")
  6.   do @drawMain :: wnd@makeDraw(@wndMain, 0, 0, 1600, 900, %scale, %scale, false)
  7.  
  8.   var tex: draw@Tex :: draw@makeTex("res/dot_back_side.png")
  9.  
  10.   while(wnd@act())
  11.     do tex.drawScale(0.0, 0.0, 1600.0, 900.0, 0.0, 0.0, 800.0, 450.0, draw@white)
  12.     do draw@render(60)
  13.   end while
  14. end func
図3-3: 画像の拡縮描画
実行すると図3-4のようになります。
画像の拡縮
図3-4: 画像の拡縮

3.3画像を回転して描画する



画像を回転して描画するには「draw@Tex.drawRot(転送先X,転送先Y,転送先幅,転送先高さ,転送元X,転送元Y,転送元幅,転送元高さ,回転中心座標X,回転中心座標Y,回転角度(ラジアン),)」とします(図3-5)。
  1. var wndMain: wnd@Wnd
  2. var drawMain: wnd@Draw
  3.  
  4. func main()
  5.   do @wndMain :: wnd@makeWnd(null, %aspect, 1600, 900, "Title")
  6.   do @drawMain :: wnd@makeDraw(@wndMain, 0, 0, 1600, 900, %scale, %scale, false)
  7.  
  8.   var tex: draw@Tex :: draw@makeTex("res/dot_back_side.png")
  9.  
  10.   while(wnd@act())
  11.     do tex.drawRot(0.0, 0.0, 800.0, 450.0, 0.0, 0.0, 800.0, 450.0, 0.0, 0.0, lib@pi / 4.0, draw@white)
  12.     do draw@render(60)
  13.   end while
  14. end func
図3-5: 画像の回転描画
実行すると図3-6のようになります。
画像の回転
図3-6: 画像の回転

4テキストを描画する

4.1テキストを描画する



テキストを描画するには「draw@makeFont(フォント名,サイズ,太字かどうか,斜体かどうか,プロポーショナルかどうか,文字の間隔)」でフォントを読み込んでから、「draw@Font.draw(描画先X,描画先Y,テキスト,)」で描画します(図4-1)。
  1. var wndMain: wnd@Wnd
  2. var drawMain: wnd@Draw
  3.  
  4. func main()
  5.   do @wndMain :: wnd@makeWnd(null, %aspect, 1600, 900, "Title")
  6.   do @drawMain :: wnd@makeDraw(@wndMain, 0, 0, 1600, 900, %scale, %scale, false)
  7.  
  8.   var font1: draw@Font :: draw@makeFont(null, 60, false, false, false, 60.0)
  9.   var font2: draw@Font :: draw@makeFont(null, 60, false, false, true, 0.0)
  10.  
  11.   while(wnd@act())
  12.     do font1.draw(0.0, 0.0, "Hello, world!", 0xFFFF9999)
  13.     do font2.draw(0.0, 120.0, "Hello, world!", 0xFF9999FF)
  14.     do draw@render(60)
  15.   end while
  16. end func
図4-1: テキストの描画
draw@makeFontの第1引数のフォント名を「null」にすると、Windowsに標準でインストールされている「"Meiryo UI"」になります。
draw@makeFontの第5引数を「false(等幅)」にしたときは、文字間隔は第6引数で指定した幅になります。 「true(プロポーショナル)」にしたときは、文字間隔は自然になるように自動調整されますが、さらに第6引数で指定した幅が加算されるため幅を増減させることができます。
実行すると図4-2のようになります。
テキスト
図4-2: テキスト

4.2テキストを中央揃えや右揃えで描画する



テキストを中央揃えや右揃えで描画するには「draw@Font.calcWidth(テキスト)」で幅を取得してから描画します(図4-3)。
  1. var wndMain: wnd@Wnd
  2. var drawMain: wnd@Draw
  3.  
  4. func main()
  5.   do @wndMain :: wnd@makeWnd(null, %aspect, 1600, 900, "Title")
  6.   do @drawMain :: wnd@makeDraw(@wndMain, 0, 0, 1600, 900, %scale, %scale, false)
  7.  
  8.   var font: draw@Font :: draw@makeFont(null, 60, false, false, true, 0.0)
  9.  
  10.   while(wnd@act())
  11.     const text: []char :: "Hello, world!"
  12.     var width: float :: font.calcWidth(text)
  13.     do draw@line(800.0, 0.0, 800.0, 900.0, 0xFFFFFF99)
  14.     do font.draw(800.0, 0.0, text, 0xFF9999FF)
  15.     do font.draw(800.0 - width / 2.0, 120.0, text, 0xFF9999FF)
  16.     do font.draw(800.0 - width, 240.0, text, 0xFF9999FF)
  17.     do draw@render(60)
  18.   end while
  19. end func
図4-3: テキストの文字揃え描画
実行すると図4-4のようになります。
テキストの文字揃え
図4-4: テキストの文字揃え

53Dオブジェクトを描画する

(この記事は執筆中です!)
1544538522jaf