Найти в Дзене
КвадроKot

12. Мини игра "Лопни Шарик" на Kotlin?

Давайте напишем простую мини игру "Лопни шарик" для Android на Kotlin. Мы создадим приложение, где шарики появляются на экране, двигаются вверх, и их можно лопать касанием. - Name: Balloonpop - Package name: com.example.balloonpop - Language: Kotlin - Minimum SDK: API 21 (Android 5.0) //---------------------------------------------- <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.balloonpop">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Balloonpop">
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:exported="true"
android:scr
Оглавление

Давайте напишем простую мини игру "Лопни шарик" для Android на Kotlin. Мы создадим приложение, где шарики появляются на экране, двигаются вверх, и их можно лопать касанием.

1. Создание проекта в Android Studio.

  1. Запусти Android Studio.
  2. Выбери New ProjectEmpty Activity.
  3. Укажи:

- Name: Balloonpop

- Package name: com.example.balloonpop

- Language: Kotlin

- Minimum SDK: API 21 (Android 5.0)

2. Файл манифеста (AndroidManifest.xml):

//----------------------------------------------

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.balloonpop">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Balloonpop">
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:exported="true"
android:screenOrientation="fullSensor"
tools:ignore="DiscouragedApi">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>

//----------------------------------------------

3. activity_main.xml:

//----------------------------------------------

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<com.example.balloonpop.BalloonGame
android:id="@+id/gameView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>

//----------------------------------------------

4. Balloon.kt (Класс шарика).

//----------------------------------------------

package com.example.balloonpop
class Balloon(
var x: Float,
var y: Float,
var radius: Float,
val speed: Float,
val color: Int,
var isPopped: Boolean = false
)

//----------------------------------------------

5. BalloonGame.kt (Основная логика игры).

//----------------------------------------------

package com.example.balloonpop
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.SurfaceView
import kotlin.math.pow
import kotlin.math.sqrt
import kotlin.random.Random

class BalloonGame(context: Context, attrs: AttributeSet?) : SurfaceView(context, attrs), Runnable {
private var gameThread: Thread? = null
private var isPlaying = false

private val balloons =
mutableListOf<Balloon>()
private val paint = Paint()
private var score = 0

// Настройки игры
private val maxBalloons = 10
private val spawnRate = 2000L
// 2 секунды
private var lastSpawnTime = 0L

override fun run() {
while (isPlaying) {
update()
draw()
controlFPS()
}
}

private fun update() {
// Обновление позиций шариков
balloons.forEach { balloon ->
if (!balloon.isPopped) {
balloon.y -= balloon.speed
}
}

// Удаление вышедших за пределы экрана
balloons.removeAll { it.y + it.radius < 0 || it.isPopped }

// Создание новых шариков
if (System.currentTimeMillis() - lastSpawnTime > spawnRate && balloons.size < maxBalloons) {
spawnBalloon()
lastSpawnTime = System.currentTimeMillis()
}
}

private fun draw() {
if (
holder.surface.isValid) {
val canvas: Canvas =
holder.lockCanvas()
canvas.drawColor(Color.
WHITE)

balloons.
forEach { balloon ->
if (!balloon.isPopped) {
paint.
color = balloon.color
canvas.drawCircle(balloon.x, balloon.y, balloon.radius, paint)
}
}

// Отрисовка счета
paint.color = Color.BLACK
paint.textSize = 50f
canvas.drawText("Score: $score", 50f, 50f, paint)

holder.unlockCanvasAndPost(canvas)
}
}

private fun controlFPS() {
try {
Thread.sleep(16)
// ~60 FPS
} catch (e: InterruptedException) {
e.printStackTrace()
}
}

private fun spawnBalloon() {
val radius = Random.nextFloat() * 50 + 50
val x = Random.nextFloat() * (
width - radius * 2) + radius
val speed = Random.nextFloat() * 5 + 2
val color = Color.argb(
255,
Random.nextInt(256),
Random.nextInt(256),
Random.nextInt(256)
)

balloons.add(Balloon(x,
height.toFloat() + radius, radius, speed, color))
}

@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
if (event.
action == MotionEvent.ACTION_DOWN) {
val x = event.
x
val y = event.y

balloons.forEach { balloon ->
val dx = x - balloon.x
val dy = y - balloon.y
val distance =
sqrt(dx.pow(2) + dy.pow(2))

if (!balloon.isPopped && distance < balloon.radius) {
balloon.isPopped = true
score += 10
}
}
}
return true
}

fun resume() {
isPlaying = true
gameThread = Thread(this)
gameThread?.start()
}

fun pause() {
isPlaying = false
gameThread?.join()
}
}

//----------------------------------------------

6. MainActivity.kt

//----------------------------------------------

package com.example.balloonpop
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {
private lateinit var gameView: BalloonGame

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
gameView = BalloonGame(this, null)
setContentView(gameView)
}

override fun onResume() {
super.onResume()
gameView.resume()
}

override fun onPause() {
super.onPause()
gameView.pause()
}
}

//----------------------------------------------

Пояснение кода:

  1. SurfaceView используется для рисования в отдельном потоке, что важно для плавной анимации.
  2. Игровой цикл реализован в методе run():
    - update() - обновление позиций шариков.
    - draw() - отрисовка всех объектов.
    - controlFPS() - контроль частоты кадров.
  3. Обработка касаний:
    - При касании проверяем попадание в шарик с помощью формулы расстояния между точками.
    - Лопнувшие шарики помечаются флагом isPopped.
  4. Генерация шариков:
    - Случайный размер, цвет и скорость.
    - Появление снизу экрана.
  5. Управление жизненным циклом:
    - Запуск и остановка игрового потока при паузе/возобновлении активности.

Советы для новичков:

  1. SurfaceView требует управления потоком вручную.
  2. Все операции с холстом должны быть внутри lockCanvas() и unlockCanvasAndPost().
  3. Для сложных игр рассмотрите использование игровых движков (LibGDX, Unity).
  4. Можно добавить:
    - Звуки при лопании.
    - Анимацию взрыва.
    - Уровни сложности.
    - Сохранение рекордов.

Этот код дает базовую реализацию игры. Вы можете модифицировать параметры шариков, добавить новые функции и улучшить графику по своему усмотрению.