Kotlinのサンプルを紹介します ①


Kotlinでのプログラミングを学習するためテトリスのようなものを作っていきます。
Kotlin習得のためなるべくプログラムだけで実装したいと思います。

一から冷静に始めていきたいと思います。


プロジェクト作成


プロジェクトを作成します。
LanguageにKotlinを選択します。

図形表示させる


テトリスで使う図形は四角形と線ですかね。
この二つで何とか表現できるかと思います。
これらを表示するための処理はひとまずMainActivity.ktに記述していきます。

四角形を表示させます。
図形を表示させる場合、PaintクラスとCanvasクラスを使用するようです。

package euniclus.tetris

import android.content.Context
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.view.View

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(Graphic(this))
    }

    inner class Graphic(context: Context): View(context) {
        private var paint: Paint = Paint()

        override fun onDraw(canvas: Canvas) {
            // 四角形を表示
            paint.style=Paint.Style.FILL
            paint.color=Color.argb(255,255,0,255) 
            canvas.drawRect(Rect(110,210,190,290),paint)

        }
    }
}


次は線を表示します。
線でフィールド(フィールドという言い方が正しいかはわかりませんが)を表現します。
表示する場所は真ん中周辺にしておきます。
onDrawメソッドに以下の記述を追加します。


// ラインを表示
val wm: WindowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
val disp = wm.getDefaultDisplay()
val size = Point()
disp.getSize(size)

val startX = 200f
val startY = 300f
val height = 1000
val width = 700
val sqSize = 100
for (i in 0..height/sqSize ) {
  paint.style = Paint.Style.STROKE
  paint.color = Color.argb(255, 190,200,255)
  paint.strokeWidth = 10f
  canvas.drawLine(startX, startY+i*sqSize ,startX+width,startY+i*sqSize , paint)
}

for (i in 0..width/sqSize ) {
  paint.style = Paint.Style.STROKE
  paint.color = Color.argb(255, 190,200,255)
  paint.strokeWidth = 10f
  canvas.drawLine(startX+i*sqSize , startY,startX+i*sqSize ,startY+height, paint)
}


これでテトリス作成に当たり必要なものを表示することができました。
しかしこのまま進めていくとすべての処理をMainActivityに書くことになってしまうため、ソースコードの整備をしま
以下のファイルを追加します

  • 図形を表示するクラス
  • ブロックに関する処理を行うクラス
  • フィールドに関する処理を行うクラス
  • パラメーターを記述するファイル(Const.kt)
package euniclus.tetris

import android.support.v7.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(Graphic(this))
    }
}
package euniclus.tetris

import android.content.Context
import android.graphics.*
import android.view.View

// 図形を表示するクラス
class Graphic(context: Context): View(context) {
    private var paint: Paint = Paint()

    override fun onDraw(canvas: Canvas) {

        // ブロックを表示
        drawBlock(canvas)

        // フィールドを表示
        drawField(canvas)

    }

    private fun drawBlock(canvas: Canvas) {
        val block = Block()
        block.draw(canvas)
    }

    private fun drawField(canvas: Canvas) {
        val field = Field()
        field.draw(canvas, context)
    }
}
package euniclus.tetris

import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import java.util.*

// ブロックに関する処理を行うクラス
class Block {

    private var paint: Paint = Paint()

    init {
        paint.style= Paint.Style.FILL
        paint.color= Color.argb(
            255,
            Random().nextInt(200),
            Random().nextInt(200),
            Random().nextInt(200))
    }

    fun draw(canvas: Canvas) {
        canvas.drawRect(Rect(
            BLOCKSTARTX,
            BLOCKSTARTY,
            BLOCKSTARTX+BLOCKSIZE,
            BLOCKSTARTY+BLOCKSIZE),paint)
    }

}
package euniclus.tetris

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Point
import android.view.WindowManager

// フィールドに関する処理を行うクラス
class Field {
    private var paint: Paint = Paint()
    fun draw(canvas: Canvas, context: Context) {

        val wm: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
        val disp = wm.defaultDisplay
        val size = Point()
        disp.getSize(size)

        val startX = FIELDSTARTX
        val startY = FIELDSTARTY
        val height = FIELDHEIGHT
        val width = FIELDWIDTH
        val sqSize = SQUARESIZE
        for (i in 0..height/sqSize) {
            paint.style = Paint.Style.STROKE
            paint.color = Color.argb(255, 190,200,255)
            paint.strokeWidth = 10f
            canvas.drawLine(startX, startY+i*sqSize,startX+width,startY+i*sqSize, paint)

        }

        for (i in 0..width/sqSize) {
            paint.style = Paint.Style.STROKE
            paint.color = Color.argb(255, 190,200,255)
            paint.strokeWidth = 10f
            canvas.drawLine(startX+i*sqSize, startY,startX+i*sqSize,startY+height, paint)
        }
    }
}


図形の位置や大きさを定義するファイルです。
ここにパラメーターを追加していきたいと思います。

package euniclus.tetris

/**
 * Block
 */
// 四角形のサイズ
val BLOCKSIZE = 80
// 間隔
val BLOCKSPACE = 20+BLOCKSIZE
// ブロックの表示開始位置 x
val BLOCKSTARTX = 210
// ブロックの表示開始位置 y
val BLOCKSTARTY = 210

/**
 * Field
 */
// 表示開始位置
val FIELDSTARTX = 200f
// 表示開始位置
val FIELDSTARTY = 300f
// フィールドの高さ
val FIELDHEIGHT = 1000
// フィールドの幅
val FIELDWIDTH = 700
//  フィールドのマスの大きさ
val SQUARESIZE = 100

ブロックの体裁を整える


今は四角形一つだけが表示されている状態ですので、
テトリスらしく複数の四角形を組み合わせてブロックを数種類作成します。

Const.ktに以下の記述を追加します。
ひとつの四角形を中心にそこからオフセットを指定してブロックを作成します。
その値を配列で定義します。
今はとりあえず4種類ですね。

// 表示開始位置からのオフセットを配列で持つ
val BlockTypeA = arrayOf(
    intArrayOf(0,0),
    intArrayOf(BLOCKSPACE,0),
    intArrayOf(BLOCKSPACE*2,0),
    intArrayOf(0,-BLOCKSPACE)
)

val BlockTypeB = arrayOf(
    intArrayOf(0,0),
    intArrayOf(BLOCKSPACE,0),
    intArrayOf(BLOCKSPACE*2,0),
    intArrayOf(BLOCKSPACE*3,0)
)

val BlockTypeC = arrayOf(
    intArrayOf(0,0),
    intArrayOf(BLOCKSPACE,0),
    intArrayOf(BLOCKSPACE*2,0),
    intArrayOf(BLOCKSPACE,-BLOCKSPACE)
)

val BlockTypeD = arrayOf(
    intArrayOf(0,0),
    intArrayOf(BLOCKSPACE,0),
    intArrayOf(BLOCKSPACE,-BLOCKSPACE),
    intArrayOf(0,-BLOCKSPACE)
)


上で定義した配列をブロッククラスが受け取るようにします。
そしてその配列をループで回しブロックを表示する処理を追加します。

class Block(val blockInfo: Array<IntArray>) {

    private var paint: Paint = Paint()

    fun draw(canvas: Canvas) {
        paint.style= Paint.Style.FILL
        paint.color= Color.argb(255,255,0,255)

        for (i in 0..blockInfo.size-1) {
            canvas.drawRect(Rect(
                BLOCKSTARTX+blockInfo[i][0],
                BLOCKSTARTY+blockInfo[i][1],
                BLOCKSTARTX+BLOCKSIZE+blockInfo[i][0],
                BLOCKSTARTY+BLOCKSIZE+blockInfo[i][1]),
                paint)
        }
    }
}


そしてGraphicクラスから以下のようにして呼び出します。

val block = Block(BlockTypeC)
block.draw(canvas)


これを実行するとこのようになりました。


これで体裁は整ったかと思います。
あとはこれに機能を追加していくだけですね。
次回から機能の実装に入りたいと思います。