前置き
AndroidStudio+KotlinでAndroidで動くゲームの土台作る(2D限定)
iOS版も作りたいよ
UnityとかUnrealEngine4使えばいいと思うよ
あれならAndroidやiOSの他にも、理論上NintendoSwitchとかPS4とかに吐き出せるっぽいし
(Nintendoは、Nintendoに開発者登録して専用のUnityを落とす必要があった気がする)
iOSは開発者登録とか色々めんどくさいから
正直個人で楽しみながら作るにはおすすめできない
対象読者
Androidだけで軽くゲームっぽいのを作りたい人や
当たり判定とかを自前で実装してみたい人向け
ただゲーム作りたいだけならUnityとかUnrealEngine4使(略)
ゲームとして成立するのに必要な条件
以下の4つを満たす必要がある
ループ
ゲームはユーザーが止めるまで処理を実行し続ける必要がある
入力処理
ユーザーからの入力を受け取る必要がある
逐次処理
入力処理や状態変化に基づいて、各オブジェクトの情報を変更する処理
描画処理
逐次処理の情報を元にオブジェクトを描画する処理
とりあえずプロジェクトを作る
「Empty Activity」でプロジェクトを作る
View に描画を行う
最初に悪い例として、Viewクラスをそのまま使い土台を作ろうとしたが
理想通りに動かなかったため、割愛する
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.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0" />
</androidx.constraintlayout.widget.ConstraintLayout>
SurfaceView を 画面全体を覆うように定義する
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)
}
}
基本コピペでいいが、重要な部分だけ説明
// 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」使うといいかもしれないね