掌握數(shù)字合成的藝術(shù):使用Android原生控件打造2048小游戲
2048游戲是一款經(jīng)典的數(shù)字合成益智游戲,2048游戲以其簡單的規(guī)則、易于上手的操作和富有挑戰(zhàn)性的玩法而受到廣大玩家的喜愛。不僅鍛煉了玩家的邏輯思維和策略規(guī)劃能力,還提供了放松和娛樂的休閑方式。
游戲說明
- 「游戲目標(biāo)」:玩家需要通過上下左右滑動屏幕,使得相同數(shù)字的方塊在碰撞時相加,目標(biāo)是組合出數(shù)字2048。在游戲中,每次滑動后,系統(tǒng)會在空白格子隨機生成一個新數(shù)字,玩家需要策略性地進行滑動,以合并方塊并逐步接近目標(biāo)數(shù)字。
- 「操作方法」
向上、下、左、右滑動屏幕來移動所有方塊。
相同數(shù)字的方塊相撞時會合并成為它們的和。
- 「游戲規(guī)則」
每次滑動后,會在空白處隨機出現(xiàn)一個2或4的方塊。
當(dāng)無法移動時游戲結(jié)束。
- 「得分規(guī)則」
每次合并方塊時,合并后的數(shù)字會被加到總分中。
盡可能創(chuàng)造更大的數(shù)字來獲得高分。
- 「游戲策略」
保持大數(shù)字在角落。
盡量保持棋盤整潔,給自己更多合并的機會。
????下面我們使用Android原生控件來實現(xiàn)這個小游戲(PS:不包含自定義View的方式)
實現(xiàn)思路
1.使用2維數(shù)組表示游戲棋盤,在界面中形成一個4*4的棋盤,在布局中添加GridLayout顯示棋盤,添加TextView用來顯示游戲分?jǐn)?shù)
private val board = Array(4) { IntArray(4) }
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FAF8EF">
<TextView
android:id="@+id/titleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="2048"
android:textColor="#776E65"
android:textSize="48sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/scoreTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="Score: 0"
android:textColor="#776E65"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/titleTextView" />
<GridLayout
android:id="@+id/gridLayout"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="16dp"
android:background="#BBADA0"
android:columnCount="4"
android:padding="4dp"
android:rowCount="4"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/titleTextView" />
</androidx.constraintlayout.widget.ConstraintLayout>
界面效果
2.通過手勢檢測來觸發(fā)移動操作
private lateinit var gestureDetector: GestureDetector
gestureDetector用于檢測手勢的GestureDetector,攔截觸摸事件并計算出手勢的方向,調(diào)用相應(yīng)的移動函數(shù) (moveLeft, moveRight, moveUp, moveDown)進行游戲控制
override fun onTouchEvent(event: MotionEvent): Boolean {
return gestureDetector.onTouchEvent(event) || super.onTouchEvent(event)
}
override fun onDown(e: MotionEvent): Boolean = true
override fun onFling(
e1: MotionEvent?,
e2: MotionEvent,
velocityX: Float,
velocityY: Float
): Boolean {
val diffX = e2.x - (e1?.x ?: 0f)
val diffY = e2.y - (e1?.y ?: 0f)
if (Math.abs(diffX) > Math.abs(diffY)) {
if (diffX > 0) {
moveRight()
} else {
moveLeft()
}
} else {
if (diffY > 0) {
moveDown()
} else {
moveUp()
}
}
addNewTile()
updateUI()
return true
}
3.每次移動后,檢查并合并相同的數(shù)字
以moveLeft為例,對每一行進行操作,過濾掉0,將非0數(shù)字靠左排列,合并相鄰的相同數(shù)字
private fun moveLeft() {
var changed = false
for (i in 0 until 4) {
val row = board[i].filter { it != 0 }.toMutableList()
var j = 0
while (j < row.size - 1) {
if (row[j] == row[j + 1]) {
row[j] *= 2
score += row[j]
row.removeAt(j + 1)
changed = true
}
j++
}
while (row.size < 4) {
row.add(0)
}
if (board[i] != row.toIntArray()) {
changed = true
}
board[i] = row.toIntArray()
}
if (changed) {
addNewTile()
}
checkGameOver()
}
填充0到4個格子,如果檢測到有變化,添加新方塊格子
4.每次有效移動后,在空白位置隨機添加新的數(shù)字(2或4)
private fun addNewTile() {
val emptyTiles = mutableListOf<Pair<Int, Int>>()
for (i in 0 until 4) {
for (j in 0 until 4) {
if (board[i][j] == 0) {
emptyTiles.add(Pair(i, j))
}
}
}
if (emptyTiles.isNotEmpty()) {
val (i, j) = emptyTiles[Random.nextInt(emptyTiles.size)]
board[i][j] = if (Random.nextFloat() < 0.9f) 2 else 4
}
}
90%概率放置2,10%概率放置4
圖片
5.實時更新UI以反映游戲狀態(tài)
private fun updateUI() {
gridLayout.removeAllViews()
for (i in 0 until 4) {
for (j in 0 until 4) {
val tileView = TextView(this)
tileView.text = if (board[i][j] != 0) board[i][j].toString() else ""
tileView.setBackgroundResource(getTileBackground(board[i][j]))
tileView.setTextColor(ContextCompat.getColor(this, getTileTextColor(board[i][j])))
tileView.textSize = 24f
tileView.gravity = android.view.Gravity.CENTER
val params = GridLayout.LayoutParams()
params.width = 0
params.height = 0
params.columnSpec = GridLayout.spec(j, 1f)
params.rowSpec = GridLayout.spec(i, 1f)
params.setMargins(4, 4, 4, 4)
tileView.layoutParams = params
gridLayout.addView(tileView)
}
}
scoreTextView.text = "分?jǐn)?shù): $score"
}
清空gridLayout,根據(jù)board數(shù)組重新生成所有方塊,設(shè)置方塊的背景顏色和文字顏色,最后更新分?jǐn)?shù)顯示
6.持續(xù)檢查游戲是否結(jié)束
private fun isGameOver(): Boolean {
for (i in 0 until 4) {
for (j in 0 until 4) {
if (board[i][j] == 0) {
return false
}
}
}
for (i in 0 until 4) {
for (j in 0 until 4) {
if (i < 3 && board[i][j] == board[i + 1][j]) {
return false
}
if (j < 3 && board[i][j] == board[i][j + 1]) {
return false
}
}
}
return true
}
檢查是否還有空格,檢查是否有相鄰的相同數(shù)字,如果兩者都沒有游戲結(jié)束
圖片
完整代碼
游戲畫面
https://github.com/Reathin/Sample-Android/tree/master/module_2048