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

14. Мини игра "Пинг Понг" на Kotlin?

Оглавление

Давайте напишем мини игру "Пинг Понг" на Kotlin. Мы создадим упрощенную версию с базовой механикой.

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

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

- 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 или выше.