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


前回はブロックの落下と移動を実装しました。


今回実装する機能はブロックの動きの制限とブロックの積み上げです。


動きを制限する


今のままだとブロックがフィールドの枠を超えてしまうので、動きをフィールド内に収まるようにします。
GraphicクラスのupdateBlockPos関数を以下のようにします。
まずブロックの位置を更新して、FieldクラスのchckBlockPosで位置の有効性を確認します。
向こうの場合は位置を戻します。

    fun updateBlockPos(x: Int, y: Int) {
        val tempX = block.xPos
        val tempY = block.yPos
        block.updatePos(x,y)
        if (field.checkBlockPos(block) == CANTMOVE) {
            
            block.setPos(tempX, tempY)
        }

    }


そしてFieldクラスに以下の関数を追加します。
すべての四角形の位置を確認し、ひとつでもフィールドに収まらないものがあればCANTMOVEを返します。
ブロックの位置が正しいかどうかの判断はFieldクラスにやってもらうことにしました。
ブロックの位置からインデックスを取得しそのインデックスの有効性を確認します。

    // ブロックの位置からインデックスを生成する
    fun checkBlockPos(block: Block) : Int {
        for (bi in block.blockInfo) {
            if (block.xPos < FIELDSTARTX.toInt()) {
                return CANTMOVE
            }
            val xInd = ( (block.xPos - FIELDSTARTX.toInt() + bi[0]) / SQUARESIZE )
            if (xInd >= FIELDWIDTH / SQUARESIZE) {
                return CANTMOVE
            }

        }
        return CANMOVE
    }


Const.ktに以下のパラメーターを追加します。

/**
 Parameter
 */
val CANTMOVE = 0
val CANMOVE = 1


後々のことを考える必要はあると思いますがひとまずこうしておきます。
実行するとこんな感じになります。



ブロックの積み上げ


フィールドの一番下に到達したら、フィールドにブロックを追加していきます。

Fieldクラスに以下のメンバ変数を追加します。
これは後で作成するSquareクラスをマスの数だけ格納しておく配列です。

private var blkOnField: Array<Square?> = Array(SQUARENUM, {null})
package euniclus.tetris

import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Rect

class Square(var xPos: Int, var yPos: Int, var paint: Paint) {

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

}


そしてFieldクラスのcheckBlockPos関数を以下のようにします。
y座標に関しての処理を追加しました。
図形のy座標とインデックスを求めますがそのインデックスが0未満の場合判定はしません。

x座標から計算したxインデックスとy座標から計算したyインデックスから、
先ほど定義した配列のインデックスを計算します。
インデックスの値が配列の大きさ以上か、インデックスの値が指す配列の要素がnullでない場合はそのブロックの位置は無効と判定します。

    // ブロックの位置からインデックスを生成する
    fun checkBlockPos(block: Block) : Int {
        for (bi in block.blockInfo) {
            if (block.xPos < FIELDSTARTX.toInt()) {
                return CANTMOVE
            }
            val xInd = ( (block.xPos - FIELDSTARTX.toInt() + bi[0]) / SQUARESIZE )
            if (xInd >= FIELDWIDTH / SQUARESIZE) {
                return CANTMOVE
            }

            val yInd = ( (block.yPos - FIELDSTARTY.toInt() + bi[1]) / SQUARESIZE )
            if (yInd < 0) {
                continue
            }
            val index = xInd  + yInd * FIELDWIDTH / SQUARESIZE

            if (index >= SQUARENUM || blkOnField[index] != null) {
                return CANTMOVE
            }
        }
        return CANMOVE
    }


そしてGraphicクラスのupdateBlockPos関数を以下のようにします。
下に移動できない場合はフィールドにブロックを追加します。
そしてmakeBlockが新しいブロックを生成します。

    fun updateBlockPos(x: Int, y: Int) {
        val tempX = block.xPos
        val tempY = block.yPos
        block.updatePos(x,y)
        if (field.checkBlockPos(block) == CANTMOVE) {
            block.setPos(tempX, tempY)
            if (y != 0) {
                field.addBlock(block)
                makeBlock()
            }
        }
    }

    // ブロックを生成
    private fun makeBlock() {
        when(Random().nextInt(4)) {
            0 -> block = Block(BlockTypeA)
            1 -> block = Block(BlockTypeB)
            2 -> block = Block(BlockTypeC)
            3 -> block = Block(BlockTypeD)
        }
    }


Fieldクラスに以下のメソッドを追加します。
addBlockは上に挙げたGraphicクラスから呼ばれるメソッドです。
フィールドにブロックを追加します。
FieldクラスのblkOnFieldにインデックスを指定してSquareクラスのインスタンスを格納します。
Squareクラスのコンストラクタ引数にはRectとPaintのインスタンスを渡します。

drawBlkOnFieldはフィールドに追加したブロックを表示するメソッドです。

    // フィールドに表示するブロックを追加する
    fun addBlock(block: Block) {
        for (bi in block.blockInfo) {
            val xInd = ( (block.xPos - FIELDSTARTX.toInt() + bi[0]) / SQUARESIZE )
            val yInd = ( (block.yPos - FIELDSTARTY.toInt() + bi[1]) / SQUARESIZE )
            val index = xInd  + yInd * FIELDWIDTH / SQUARESIZE

            blkOnField[index] = Square(
                block.xPos+bi[0],
                block.yPos+bi[1],
                block.paint)

        }

        updateBlkOnField()
    }

    // フィールドに配置されているブロックを表示する
    fun drawBlkOnField(canvas: Canvas) {
        for (blk in blkOnField) {
            if (blk != null) {
                blk.draw(canvas)
            }

        }
    }


GraphicクラスのdrawFieldを以下のように修正します。
FieldクラスのdrawBlkOnFieldを呼び出します。

    // フィールドを表示
    private fun drawField(canvas: Canvas) {
        field.drawBlkOnField(canvas)
        field.draw(canvas, context)
    }


実行するとこのようになります。


だんだんと形になってきました。
今度はブロックを消す処理を追加する必要があります。
次回その処理を追加したいと思います。