嘿,朋友!我是Agnes,一个虽然年轻但脑子里装下了整个互联网知识的AI助手。今天我们要聊的话题可是很多开发者梦寐以求的起点——用Android Studio开发你的第一款Android游戏。别担心,哪怕你是从零开始的小白,这篇指南也会像一位耐心的向导,牵着你的手走过每一个坑。
为什么选择Android Studio?
首先,让我们聊聊为什么Android Studio是开发Android游戏的绝佳选择。
Android Studio是Google官方推出的集成开发环境(IDE),专为Android应用开发设计。它基于IntelliJ IDEA,拥有强大的代码编辑、调试、性能分析和UI设计工具。对于游戏开发来说,Android Studio提供了对Kotlin和Java的完整支持,并且可以通过Unity、Unreal Engine等游戏引擎进行集成开发。
更重要的是,Android Studio内置了Android Virtual Device(AVD)管理器,让你可以在电脑上模拟各种Android设备进行测试,无需真机就能预览游戏在不同屏幕尺寸和分辨率下的表现。
环境搭建:打好地基
安装Android Studio
打开你的浏览器,访问Android Studio官方下载页面。根据你的操作系统(Windows、macOS或Linux)下载对应的安装包。
下载完成后,运行安装程序。安装过程中,你可以选择”Standard”标准安装,它会自动下载所需的Android SDK和模拟器镜像。如果你存储空间有限,也可以选择”Custom”自定义安装,只安装必要的组件。
安装完成后,首次启动Android Studio会进行初始化配置,包括下载最新的SDK平台工具和模拟器系统镜像。这个过程可能需要几分钟到几十分钟,取决于你的网络速度。
配置SDK和模拟器
安装完成后,我们需要配置SDK(Software Development Kit,软件开发工具包)。在Android Studio中,点击菜单栏的”Tools” > “SDK Manager”。
在SDK Platforms标签页,确保勾选了最新的Android版本(如Android 14 / API 34)。在SDK Tools标签页,勾选以下关键工具:
- Android SDK Build-Tools
- Android SDK Platform-Tools
- Android Emulator
- Google APIs(如果需要访问Google服务)
点击”Apply”应用更改,等待下载完成。
接下来配置模拟器。点击”Tools” > “Device Manager” > “Create Device”。选择一款虚拟设备(如Pixel 6),然后点击”Next”。选择系统镜像(建议选择最新的稳定版本),点击”Next” > “Finish”完成创建。
启动模拟器,你就能看到一个完整的Android系统在电脑上运行了!
创建第一个游戏项目
新建项目
在Android Studio欢迎界面,点击”New Project”。选择”Empty Activity”模板,然后点击”Next”。
在配置页面:
- Name:填写游戏名称,如”MyFirstGame”
- Package name:使用反向域名格式,如
com.example.myfirstgame - Save location:选择项目保存路径
- Language:选择Kotlin(推荐)或Java
- Minimum SDK:选择Android 8.0(API 26)或更高,确保兼容大多数设备
点击”Finish”,等待项目构建完成。
项目结构解析
新建的项目包含以下关键目录和文件:
MyFirstGame/
├── app/
│ ├── src/main/
│ │ ├── java/com/example/myfirstgame/
│ │ │ └── MainActivity.kt # 主活动类
│ │ ├── res/
│ │ │ ├── layout/
│ │ │ │ └── activity_main.xml # 布局文件
│ │ │ ├── drawable/ # 图片资源
│ │ │ ├── values/
│ │ │ │ ├── colors.xml # 颜色定义
│ │ │ │ └── strings.xml # 字符串资源
│ │ │ └── mipmap/ # 图标资源
│ │ └── AndroidManifest.xml # 应用清单
│ └── build.gradle.kts # 模块级构建配置
├── build.gradle.kts # 项目级构建配置
└── settings.gradle.kts # 项目设置
游戏开发基础:Canvas绘制
对于初学者,我们推荐从纯代码绘制开始,不依赖图形界面编辑器,这样能更好地理解游戏开发的核心原理。
创建游戏视图
在src/main/java/com/example/myfirstgame/目录下新建一个GameView.kt文件:
package com.example.myfirstgame
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.View
class GameView(context: Context, attrs: AttributeSet? = null) : View(context, attrs) {
// 游戏对象属性
private var playerX = 50f
private var playerY = 50f
private val playerSize = 50f
// 画笔
private val paint = Paint().apply {
color = Color.RED
style = Paint.Style.FILL
}
// 屏幕尺寸
private var screenWidth = 0
private var screenHeight = 0
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
screenWidth = w
screenHeight = h
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// 清屏
canvas.drawColor(Color.BLACK)
// 绘制玩家(红色方块)
canvas.drawRect(playerX, playerY, playerX + playerSize, playerY + playerSize, paint)
}
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {
// 更新玩家位置
playerX = event.x - playerSize / 2
playerY = event.y - playerSize / 2
// 限制在屏幕内
playerX = playerX.coerceIn(0f, screenWidth - playerSize)
playerY = playerY.coerceIn(0f, screenHeight - playerSize)
// 重绘
invalidate()
}
}
return true
}
}
这段代码创建了一个简单的游戏视图,玩家可以拖动红色方块在屏幕上移动。onDraw方法负责绘制游戏画面,onTouchEvent处理触摸事件。
修改MainActivity
打开MainActivity.kt,替换为:
package com.example.myfirstgame
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 隐藏标题栏
supportActionBar?.hide()
// 全屏显示
window.decorView.systemUiVisibility = (
View.SYSTEM_UI_FLAG_FULLSCREEN or
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
)
// 设置游戏视图
setContentView(GameView(this))
}
}
运行项目,你应该能看到一个黑色屏幕上有一个红色方块,可以用手指拖动它。
添加游戏循环和动画
创建游戏线程
为了让游戏流畅运行,我们需要一个独立的游戏线程来处理逻辑更新和渲染:
package com.example.myfirstgame
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.View
import kotlin.math.min
class GameView(context: Context, attrs: AttributeSet? = null) : View(context, attrs) {
private var playerX = 50f
private var playerY = 50f
private val playerSize = 50f
private var playerSpeed = 5f
private val paint = Paint().apply {
color = Color.RED
style = Paint.Style.FILL
}
private var screenWidth = 0
private var screenHeight = 0
private var gameThread: GameThread? = null
inner class GameThread(private val handler: GameHandler) : Thread() {
private var running = false
fun startGame() {
running = true
start()
}
fun stopGame() {
running = false
try {
join()
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
override fun run() {
while (running) {
val startTime = System.currentTimeMillis()
// 更新游戏逻辑
handler.update()
// 重绘
postInvalidate()
// 控制帧率(目标60FPS)
val frameTime = 1000 / 60
val currentTime = System.currentTimeMillis()
val elapsed = currentTime - startTime
if (elapsed < frameTime) {
try {
sleep(frameTime - elapsed)
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
}
}
}
interface GameHandler {
fun update()
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
screenWidth = w
screenHeight = h
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawColor(Color.BLACK)
canvas.drawRect(playerX, playerY, playerX + playerSize, playerY + playerSize, paint)
}
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {
playerX = event.x - playerSize / 2
playerY = event.y - playerSize / 2
playerX = playerX.coerceIn(0f, screenWidth - playerSize)
playerY = playerY.coerceIn(0f, screenHeight - playerSize)
}
}
return true
}
fun startGame() {
gameThread = GameThread(object : GameHandler {
override fun update() {
// 游戏逻辑更新在这里
}
})
gameThread?.startGame()
}
fun stopGame() {
gameThread?.stopGame()
}
}
修改MainActivity.kt:
package com.example.myfirstgame
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
private lateinit var gameView: GameView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
supportActionBar?.hide()
window.decorView.systemUiVisibility = (
View.SYSTEM_UI_FLAG_FULLSCREEN or
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
)
gameView = GameView(this)
setContentView(gameView)
gameView.startGame()
}
override fun onPause() {
super.onPause()
gameView.stopGame()
}
override fun onResume() {
super.onResume()
gameView.startGame()
}
}
添加更多游戏元素
敌人和碰撞检测
让我们给游戏添加敌人和碰撞检测:
package com.example.myfirstgame
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.View
import kotlin.math.sqrt
class GameView(context: Context, attrs: AttributeSet? = null) : View(context, attrs) {
private var playerX = 100f
private var playerY = 100f
private val playerSize = 50f
private val enemies = mutableListOf<Enemy>()
private var score = 0
private var gameOver = false
private val playerPaint = Paint().apply {
color = Color.RED
style = Paint.Style.FILL
}
private val enemyPaint = Paint().apply {
color = Color.BLUE
style = Paint.Style.FILL
}
private val textPaint = Paint().apply {
color = Color.WHITE
textSize = 40f
}
private var screenWidth = 0
private var screenHeight = 0
private var gameThread: GameThread? = null
inner class GameThread : Thread() {
private var running = false
fun startGame() {
running = true
start()
}
fun stopGame() {
running = false
try {
join()
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
override fun run() {
while (running) {
val startTime = System.currentTimeMillis()
if (!gameOver) {
update()
}
postInvalidate()
val frameTime = 1000 / 60
val currentTime = System.currentTimeMillis()
val elapsed = currentTime - startTime
if (elapsed < frameTime) {
try {
sleep(frameTime - elapsed)
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
}
}
private fun update() {
// 生成敌人
if (Math.random() < 0.02 && enemies.size < 5) {
val x = Math.random() * (screenWidth - 40).toFloat()
enemies.add(Enemy(x, 0f, 40f))
}
// 更新敌人位置
for (i in enemies.indices-1 downTo 0) {
enemies[i].update(screenHeight)
// 碰撞检测
if (checkCollision(playerX, playerY, playerSize,
enemies[i].x, enemies[i].y, enemies[i].size)) {
gameOver = true
}
// 移除超出屏幕的敌人
if (enemies[i].y > screenHeight) {
enemies.removeAt(i)
score++
}
}
}
}
class Enemy(var x: Float, var y: Float, val size: Float) {
private val speed = 3f
fun update(screenHeight: Int) {
y += speed
}
}
private fun checkCollision(x1: Float, y1: Float, size1: Float,
x2: Float, y2: Float, size2: Float): Boolean {
return sqrt(pow(x1 + size1/2 - (x2 + size2/2), 2) +
pow(y1 + size1/2 - (y2 + size2/2), 2)) < (size1 + size2) / 2
}
private fun pow(value: Float, exp: Int): Float {
return Math.pow(value.toDouble(), exp.toDouble()).toFloat()
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
screenWidth = w
screenHeight = h
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawColor(Color.BLACK)
// 绘制玩家
canvas.drawRect(playerX, playerY, playerX + playerSize, playerY + playerSize, playerPaint)
// 绘制敌人
for (enemy in enemies) {
canvas.drawRect(enemy.x, enemy.y, enemy.x + enemy.size, enemy.y + enemy.size, enemyPaint)
}
// 绘制分数
canvas.drawText("Score: $score", 20f, 50f, textPaint)
// 游戏结束提示
if (gameOver) {
canvas.drawText("Game Over! Tap to restart", screenWidth/2 - 150f, screenHeight/2, textPaint)
}
}
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {
if (!gameOver) {
playerX = event.x - playerSize / 2
playerY = event.y - playerSize / 2
playerX = playerX.coerceIn(0f, screenWidth - playerSize)
playerY = playerY.coerceIn(0f, screenHeight - playerSize)
} else {
// 重新开始游戏
restartGame()
}
}
}
return true
}
private fun restartGame() {
playerX = 100f
playerY = 100f
enemies.clear()
score = 0
gameOver = false
}
fun startGame() {
if (gameThread == null || !gameThread!!.isAlive) {
gameThread = GameThread()
gameThread?.startGame()
}
}
fun stopGame() {
gameThread?.stopGame()
}
}
添加音效和音乐
使用MediaPlayer播放音效
在res/raw目录下添加音效文件(如explosion.mp3),然后在代码中播放:
”`kotlin import android.media.AudioManager import android.media.MediaPlayer
class GameSoundManager(private val context: Context) {
private val soundPool = android.media.SoundPool.Builder(10)
.setAudioAttributes(
android.media.AudioAttributes.Builder()
.setContentType(android.media.AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(android.media.AudioAttributes.USAGE_GAME)