0
2

More than 1 year has passed since last update.

【2022年】AndroidStduio + Kotlinでゲームの土台作ってみる

Posted at

前置き

AndroidStudio+KotlinでAndroidで動くゲームの土台作る(2D限定)

iOS版も作りたいよ

UnityとかUnrealEngine4使えばいいと思うよ
あれならAndroidやiOSの他にも、理論上NintendoSwitchとかPS4とかに吐き出せるっぽいし
(Nintendoは、Nintendoに開発者登録して専用のUnityを落とす必要があった気がする)

iOSは開発者登録とか色々めんどくさいから
正直個人で楽しみながら作るにはおすすめできない

対象読者

Androidだけで軽くゲームっぽいのを作りたい人や
当たり判定とかを自前で実装してみたい人向け
ただゲーム作りたいだけならUnityとかUnrealEngine4使(略)

ゲームとして成立するのに必要な条件

以下の4つを満たす必要がある

ループ

ゲームはユーザーが止めるまで処理を実行し続ける必要がある

入力処理

ユーザーからの入力を受け取る必要がある

逐次処理

入力処理や状態変化に基づいて、各オブジェクトの情報を変更する処理

描画処理

逐次処理の情報を元にオブジェクトを描画する処理

とりあえずプロジェクトを作る

「Empty Activity」でプロジェクトを作る

View に描画を行う

最初に悪い例として、Viewクラスをそのまま使い土台を作ろうとしたが
理想通りに動かなかったため、割愛する

SurfaceView に描画を行う

main_activity.xml
<?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.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="1.0" />
    
</androidx.constraintlayout.widget.ConstraintLayout>

SurfaceView を 画面全体を覆うように定義する

MainActivity.kt
package com.game.gameapplicaton

import android.content.Context
import android.graphics.*
import android.os.Bundle
import android.view.SurfaceHolder
import android.view.SurfaceView
import androidx.appcompat.app.AppCompatActivity
import kotlin.concurrent.thread

class MainActivity : AppCompatActivity()  {

    class CustomSurfaceView : SurfaceView , SurfaceHolder.Callback
    {
        private var xPos = 0.0f
        private var surfaceHolder: SurfaceHolder? = null
        var color: Int? = null
        private var canvas: Canvas? = null

        constructor(context: Context, surfaceView: SurfaceView) : super(context) {
            surfaceHolder = surfaceView.holder

            /// 背景を透過させ、一番上に表示
            surfaceHolder!!.setFormat(PixelFormat.TRANSPARENT)
            surfaceView.setZOrderOnTop(true)
            /// コールバック
            surfaceHolder!!.addCallback(this)

        }

        // surfaceViewが作られたとき
        override fun surfaceCreated(holder: SurfaceHolder) {

            val fps : Long = 30
            thread {
                while(true)
                {
                    xPos += 1.0f
                    draw()
                    Thread.sleep( 1000 / fps )
                }
            }.start()

        }

        override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {

        }

        override fun surfaceDestroyed(holder: SurfaceHolder) {
        }


        private fun draw() {
            canvas = Canvas()

            /// ロックしてキャンバスを取得
            canvas = surfaceHolder!!.lockCanvas()

            //// キャンバスのクリア
            canvas!!.drawColor(0, PorterDuff.Mode.CLEAR)

            // とりあえず円を描く
            val paint = Paint()
            color = Color.BLACK
            canvas!!.drawCircle( 200.0f + xPos  , 200.0f , 30.0f , paint)

            /// ロックを解除
            surfaceHolder!!.unlockCanvasAndPost(canvas)
        }

    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val surfaceView : SurfaceView = findViewById(R.id.surfaceView)
        /// CustomSurfaceViewのインスタンスを生成
        CustomSurfaceView(this, surfaceView)
    }
    
}

基本コピペでいいが、重要な部分だけ説明

MainActitiby.kt
// surfaceViewが作られたとき
        override fun surfaceCreated(holder: SurfaceHolder) {

            val fps : Long = 30
            thread {
                while(true)
                {
                    xPos += 1.0f
                    draw()
                    Thread.sleep( 1000 / fps )
                }
            }.start()

        }

while(true)により、ゲームとして成立する条件である「ループ」を行う
何も考えずにwhile(true)をすると、アプリそのものが固まってしまうため、
Threadでアプリそのものの動作と同時並行で実行することで、アプリのフリーズを回避する

処理としては、draw関数で、単純な円を描画しており、描画前に円のx座標を加算することで
毎ループ時に円が移動する

その後、(1秒 ÷ 30 ) 秒待つ事で、xPosが高速で加算されて一瞬で円が移動するのを防いでいる
一応、いろんなゲームの基準であるfps30を基準にしているが、
この待ち処理は簡略なものであり、完璧なものではない

ビルドしてみよう

円が右方向に移動するだけのActivityが起動するはずだ

今後の展望

この処理は、必要最小の実装しかしてないため、
次の記事でほんの少し実用を想定した土台へ近づいていく(予定)

参考

おまけ

ゴリゴリの3Dでやりたい狂人は「GLSurfaceView」使うといいかもしれないね

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2