プログラミング言語kotlinで、ボールとブロックの簡単な衝突ゲームを作っていきたいと思います。落ちゲーの基礎を学ぼう、的な位置づけです。
スタートボタン、それからライトボタン、レフトボタン、3つボタンを用意しています。
スタートボタンを押すと、ボールがランダムに落ちてきて、ブロックにあたるとボールは消える。
当たらなければボールは画面の下まで落ちていくっていう、簡単な落ちゲーみたいなものを作って
それらがどういう仕組みで動いているかっていうのを勉強していきましょう。
※5回終了すると、ゲームオーバーの画面に遷移するintentについては次回
動画
▼動画内で使用した画像です。ご自由にお使いください。
コード
▼activity_main.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">
<Button
android:id="@+id/btnLeft"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="Left"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/btnStart"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="@+id/btnRight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="Right"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/btnStart" />
<Button
android:id="@+id/btnStart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="Start"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/btnRight"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/btnLeft" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toTopOf="@+id/btnStart"
app:layout_constraintTop_toTopOf="parent"
tools:layout_editor_absoluteX="1dp">
<ImageView
android:id="@+id/ivBall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ball" />
<ImageView
android:id="@+id/ivBlock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="@drawable/block" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
▼MainActivity.kt
package com.example.ballblockgame
import android.animation.Animator
import android.animation.ValueAnimator
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.animation.AccelerateInterpolator
import android.widget.Button
import android.widget.ImageView
import androidx.constraintlayout.widget.ConstraintLayout
class MainActivity : AppCompatActivity() {
//5)制限回数
private var remainingFalls = 5
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//1)viewの取得+ボールを画面の外
val btnLeft:Button = findViewById(R.id.btnLeft)
val btnRight:Button = findViewById(R.id.btnRight)
val btnStart:Button = findViewById(R.id.btnStart)
val ivBall :ImageView = findViewById(R.id.ivBall)
val ivBlock :ImageView = findViewById(R.id.ivBlock)
ivBall.translationY = -200f //起動直後は画面の外
//2)左ボタンを押したら
btnLeft.setOnClickListener {
val params = ivBlock.layoutParams as ConstraintLayout.LayoutParams
params.leftMargin -=50 //左の余白を減らす
ivBlock.layoutParams = params
}
//3)右ボタンを押したら
btnRight.setOnClickListener {
val params = ivBlock.layoutParams as ConstraintLayout.LayoutParams
params.rightMargin -= 50 //右の余白を減らす
ivBlock.layoutParams = params
}
//8)スタートボタン
btnStart.setOnClickListener {
//4)アニメーション
val screenHeight =resources.displayMetrics.heightPixels.toFloat() //画面の高さ
val anim = fallingAnimator(ivBall,ivBlock,screenHeight)//中身は「5」へ
anim.start()
//btnStart.isEnabled = false
}
}
//5)「4」の中身
private fun fallingAnimator(ball:ImageView,block:ImageView,sHeight:Float):ValueAnimator{
// [5-1]X軸(ヨコ幅)の、ランダムな初期位置を設定
val screenWidth = resources.displayMetrics.widthPixels.toFloat()-ball.width //画面幅 -ボール幅
ball.translationX = (Math.random() * screenWidth).toFloat()
val animator = ValueAnimator.ofFloat(0f,sHeight)
animator.duration = 3000 // アニメーションの時間(ミリ秒)
animator.interpolator = AccelerateInterpolator() //アニメーションの加速度
//[5-2]フレームの更新
animator.addUpdateListener { valueAnimator ->
val value = valueAnimator.animatedValue as Float
ball.translationY = value
//6)衝突検出 ⇒条件の中身は「7」へ
if(isColliding(ball,block)){
//衝突したらアニメーションをキャンセル
animator.cancel()
//ボールを枠の外に出す
ball.translationY = -200f//画面の枠のもっと上
}
}
//[5-3]アニメーションの開始、終了、キャンセル、または繰り返しのイベント
animator.addListener(object :Animator.AnimatorListener{
override fun onAnimationStart(animation: Animator) {}
//[5-4]アニメーションが終わったら新しく生成(※回数に達したら終了)
override fun onAnimationEnd(animation: Animator) {
//制限回数を1減らす
remainingFalls--
//制限回数が残っている場合、新しいアニメーションを作成して再生
if(remainingFalls>0){
//アニメーションが終わったら、新しいアニメーションを作成して再生
val newAnimator = fallingAnimator(ball, block, sHeight)
newAnimator.start()
}
}
override fun onAnimationCancel(animation: Animator) {}
override fun onAnimationRepeat(animation: Animator) {}
})
//[5-5]値を返す
return animator
}
//7)衝突の条件
private fun isColliding(ball:ImageView,block:ImageView):Boolean{
val ballX = ball.x
val ballY = ball.y
val ballWidth = ball.width
val ballHeight = ball.height
val blockX = block.x
val blockY = block.y
val blockWidth = block.width
//val blockHeight = block.height
return ballX<blockX+blockWidth &&
ballX + ballWidth>blockX &&
ballY+ballHeight > blockY
//ballY < blockY + blockHeight
}
}