日々是好日~every day is a good day~

日常の中の非日常の備忘録

【Android Studio】動く背景と動くUFO

以前 『背景が動く中で飛ぶ鳥』を作りました
everydayisagoodday.hatenadiary.com
これは横方向に背景が動いてそこに鳥のアニメーションを表示するものでした
今回はこれを応用して 縦方向に背景を動かしそこにタッチ入力によって動くUFOを表示したいと思います
1.res/drawableに背景とUFOの画像を準備

2.activity_main.xmlの編集
画面全体をSurfaceViewにします

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <SurfaceView
        android:id="@+id/surfaceview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="1.0" />

</androidx.constraintlayout.widget.ConstraintLayout>

3.MainActivity.ktの編集

import android.content.Context
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.MotionEvent
import android.view.SurfaceHolder
import android.view.SurfaceView
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.MotionEventCompat

var SCREEN_WIDTH = 0
var SCREEN_HEIGHT = 0
var isRunning = false
var flag_ufo = 0   //+:右 -:左 0:停止

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val surfaceView: SurfaceView = findViewById(R.id.surfaceview)
        PlayView(this, surfaceView)   //SurfaceViewのインスタンスを生成

        GetScreenSize(this)  //スクリーンサイズの取得
    }
    override fun onTouchEvent(event: MotionEvent): Boolean {

        val action: Int = MotionEventCompat.getActionMasked(event)

        return when (action) {
            MotionEvent.ACTION_DOWN -> {
                if(event.x > SCREEN_WIDTH/2){
                    flag_ufo = 10
                }
                else{
                    flag_ufo = -10
                }
                true
            }
            MotionEvent.ACTION_UP -> {
                flag_ufo = 0
                true
            }
            else -> super.onTouchEvent(event)
        }
    }

    //スクリーンサイズの取得
    fun GetScreenSize(context: Context) {
        val vm: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
        val display = vm.defaultDisplay
        val metric = DisplayMetrics()
        display.getMetrics(metric)
        SCREEN_WIDTH = metric.widthPixels
        SCREEN_HEIGHT = metric.heightPixels
    }
}

// SurfaceViewを継承したクラス
class PlayView: SurfaceView, SurfaceHolder.Callback {
    private var surfaceHolder: SurfaceHolder? = null

    constructor(context: Context, surfaceView: SurfaceView) : super(context) {
        surfaceHolder = surfaceView.holder
        // コールバック
        surfaceHolder!!.addCallback(this)
    }

    override fun surfaceCreated(p0: SurfaceHolder) {
        val playgame = PlayGame(surfaceHolder!!, resources)
            //ゲームスタート
            isRunning = true
            playgame.start()
    }

    override fun surfaceChanged(p0: SurfaceHolder, p1: Int, p2: Int, p3: Int) {

    }

    override fun surfaceDestroyed(p0: SurfaceHolder) {

    }
}

//ゲームのThreadクラス
class PlayGame:Thread{
    private var holder : SurfaceHolder
    private var resources : Resources

    private var backBitmap : Bitmap  //背景のビットマップ
    private var ufoBitmap : Bitmap  //UFOのビットマップ
    private var ufoPosx = 0   //UFOのx座標
    private var ufoPosy = 0   //UFOのy座標
    constructor(holder: SurfaceHolder, resources: Resources) {
        this.holder = holder
        this.resources = resources
        backBitmap = BitmapFactory.decodeResource(resources, R.drawable.sky)
        ufoBitmap = BitmapFactory.decodeResource(resources, R.drawable.ufo)
        ufoPosx = SCREEN_WIDTH/2-ufoBitmap.width/2
        ufoPosy = SCREEN_HEIGHT-ufoBitmap.height-100
    }
    private var backPosx = 0   //背景のx座標
    private var backPosy = 0   //背景のy座標
    
    override fun run() {
        while(isRunning){
            if(holder == null) return
            val canvas = holder.lockCanvas()
            if(canvas != null){
                try {
                    synchronized(holder){
                        BackGround(canvas)  //背景を表示
                        UfoMove(canvas)     //UFOを表示
                    }
                }
                finally {
                    holder.unlockCanvasAndPost(canvas)
                }
            }
        }
    }
    //UFO描画
    private fun UfoMove(canvas: Canvas) {
        val ufoVerocity = 20  //UFO移動速度
        val ufoMargin = 50    //UFOビットマップの余白
        if(flag_ufo > 0){   //右へ
            ufoPosx += ufoVerocity
        }else if(flag_ufo < 0){   //左へ
            ufoPosx -= ufoVerocity
        }else{   //停止
            ufoPosx = ufoPosx
        }
        if(ufoPosx > SCREEN_WIDTH-ufoBitmap.width+ufoMargin){
            ufoPosx = SCREEN_WIDTH-ufoBitmap.width+ufoMargin
        }
        if(ufoPosx < -ufoMargin){
            ufoPosx = -ufoMargin
        }
        canvas.drawBitmap(ufoBitmap,ufoPosx.toFloat(),ufoPosy.toFloat(),null)
    }
    //背景描画
    private fun BackGround(canvas: Canvas){
        val backVelocity = 15   //背景の移動速度

        backPosy = backPosy + backVelocity
        if(backPosy > backBitmap.height){
            backPosy = 0
        }
        canvas.drawBitmap(backBitmap,backPosx.toFloat(),backPosy.toFloat(),null)

        //背景をloop表示
        if(backPosy > 0) {
            canvas.drawBitmap(backBitmap,backPosx.toFloat(),
                (backPosy - backBitmap.height).toFloat(),null)
        }
    }
}

実行すると

画面右側をタッチするとUFOが右に 画面左側をタッチするとUFOが左に移動します
タッチをやめるとその場にとどまります
また新しいゲームができそうです