Давайте напишем мини игру "Пинг Понг" на Kotlin. Мы создадим упрощенную версию с базовой механикой.
1. Создание проекта в Android Studio.
- Запусти Android Studio.
- Выбери New Project → Empty Activity.
- Укажи:
- Name: Pong Game
- Package name: com.example.ponggame
- 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.ponggame">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
<activity
android:name=".MainActivity"
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. res/layout/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">
<SurfaceView
android:id="@+id/gameView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
//----------------------------------------------
4. MainActivity.kt
//----------------------------------------------
package com.example.ponggame
import android.graphics.*
import android.os.Bundle
import android.view.SurfaceHolder
import android.view.SurfaceView
import android.app.Activity
import android.view.MotionEvent
class MainActivity : Activity(), SurfaceHolder.Callback {
private lateinit var gameView: SurfaceView
private lateinit var holder: SurfaceHolder
private var gameThread: Thread? = null
private var isPlaying = false
// Игровые объекты
private var paddleY = 0f
private var paddleWidth = 200f
private var paddleHeight = 50f
private var ballX = 0f
private var ballY = 0f
private var ballSpeedX = 15f
private var ballSpeedY = 15f
private val ballRadius = 30f
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
gameView = findViewById(R.id.gameView)
holder = gameView.holder
holder.addCallback(this)
}
override fun surfaceCreated(holder: SurfaceHolder) {
resetGame()
startGame()
}
private fun resetGame() {
// Начальные позиции
paddleY = gameView.height - 200f
ballX = gameView.width / 2f
ballY = gameView.height / 2f
}
private fun startGame() {
isPlaying = true
gameThread = Thread {
while (isPlaying) {
update()
draw()
try {
Thread.sleep(30)
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
}
gameThread?.start()
}
private fun update() {
// Движение мяча
ballX += ballSpeedX
ballY += ballSpeedY
// Столкновение со стенами
if (ballX < ballRadius || ballX > gameView.width - ballRadius) {
ballSpeedX *= -1
}
if (ballY < ballRadius) {
ballSpeedY *= -1
}
// Столкновение с ракеткой
if (ballY + ballRadius > paddleY &&
ballX > (gameView.width/2 - paddleWidth/2) &&
ballX < (gameView.width/2 + paddleWidth/2)) {
ballSpeedY *= -1
}
// Проигрыш
if (ballY > gameView.height) {
resetGame()
}
}
private fun draw() {
if (!holder.surface.isValid) return
val canvas = holder.lockCanvas()
canvas.drawColor(Color.BLACK)
// Рисуем ракетку
val paddlePaint = Paint().apply { color = Color.WHITE }
canvas.drawRect(
gameView.width/2f - paddleWidth/2,
paddleY,
gameView.width/2f + paddleWidth/2,
paddleY + paddleHeight,
paddlePaint
)
// Рисуем мяч
val ballPaint = Paint().apply { color = Color.RED }
canvas.drawCircle(ballX, ballY, ballRadius, ballPaint)
holder.unlockCanvasAndPost(canvas)
}
override fun onTouchEvent(event: MotionEvent): Boolean {
// Управление ракеткой
when (event.action) {
MotionEvent.ACTION_MOVE -> {
paddleY = event.y - paddleHeight/2
if (paddleY < 0) paddleY = 0f
if (paddleY > gameView.height - paddleHeight)
paddleY = gameView.height - paddleHeight
}
}
return true
}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {}
override fun surfaceDestroyed(holder: SurfaceHolder) {
isPlaying = false
gameThread?.join()
}
}
//----------------------------------------------
Пояснение кода:
SurfaceView и SurfaceHolder:
- SurfaceView обеспечивает выделенную поверхность для рисования.
- SurfaceHolder управляет жизненным циклом поверхности.
Игровой цикл:
- Запускается в отдельном потоке (gameThread).
- update() обновляет позиции объектов.
- draw() рисует графику на Canvas.
Обработка касаний:
- Метод onTouchEvent отслеживает движения пальца.
- Позиция ракетки привязывается к положению пальца.
Физика игры:
- Мяч отражается от стен.
- Проверка столкновения с ракеткой.
- Сброс игры при проигрыше.
Вы сами можете добавить новые функции для игры:
- Добавить счет.
- Реализовать ИИ для противника.
- Добавить звуковые эффекты.
- Добавить разные уровни сложности.
Для работы приложения убедитесь что в build.gradle установлена минимальная SDK версия 21 или выше.