Android Studio:我的第一個小遊戲 APP「猜數字」

2019/12/28 9,049 3 軟體應用 , 行動平台 , 程式設計 , Android , JAVA , APP開發

這次因為學校課程期末專案的關係,我們組經過我的建議要做一個 🎮 純手打遊樂場 APP,使用 Android Studio 這個工具進行開發,每個人負責一個小遊戲,最後請比較會整合的組員進行最後的整合,加上文書就可以交出一個作業與報告了!利用這個機會我就運用前段時間學會的一些小技巧寫了這個「猜數字(guessTheNumber)」小遊戲 APP,採用 Java 語言撰寫。萌芽站長的開發時間約 3 小時內。


▲ activity_main.xml 設計模式截圖,這是主要遊戲介面。

▲ menu_main.xml 設計模式截圖,這是右上角的選單。

遊戲規則簡介

程式會隨機出一個 1 ~ 99 數字,使用者要從 1 ~ 99 中尋找這個數字,每次輸入並送出後 APP 會自動檢查是否猜中數字,沒猜中的話會幫忙縮減尋找範圍,每次送出算一次,最少次數內找到這個隨機數字則代表使用者的運氣或技巧特好,這是採用一般人猜數字邏輯所撰寫的小型遊戲。程式還會幫忙存最佳歷史記錄,就是最少次猜中的記錄,可以隨時清空,遊戲中可以放棄或重新遊戲。


▲ 打開後即開始遊戲,輸入猜測數字後按下送出按鈕。(鍵盤已經鎖定只能輸入整數數字)

▲ 送出後會自動清空輸入框,接著一直猜直到猜中。遊戲結束按鈕會變得不可按。

遊戲介面簡介

使用者打開「猜數字」APP 後會看到一個遊戲介面,由上至下為遊戲標題、提示文字、輸入框與送出按鈕、猜測次數、歷史最佳記錄、重新開始提示,右上角點開有功能列表,分別是刪除記錄、放棄遊戲、重新開始與結束,詳細說明如下:

遊戲標題:為「猜數字」三個大字。
提示文字:遊戲過程必須注意的文字,會告知輸入是否正確以及猜測範圍,猜中或中途放棄會在此顯示答案(隨機數字)。
輸入框與送出按鈕:用來輸入您猜測的數字並送出,遊戲放棄或結束時按鈕不可使用。
猜測次數:用來顯示遊戲過程中的猜測總次數。
歷史最佳記錄:顯示最少次數猜中的記錄。
重新開始提示:提示「重新開始?往下拉!」

刪除記錄:刪除歷史最佳記錄。
放棄遊戲:放棄正在進行中的遊戲,將顯示隨機數字(答案),也就是本來要猜的數字。
重新開始:遊戲直接重新啟動。
結束:離開遊戲、關閉程式。


▲ 往下拉可以重新開始遊戲,右上角我們先點「放棄遊戲」,再點「刪除記錄」測試。

▲ 放棄遊戲會顯示本來要猜的數字,猜測次數會歸 0。刪除記錄會顯示「記錄已刪除」訊息,並且將記錄徹底移除。

遊戲程式碼

這邊直接在文章中放上比較重要的三個檔案程式碼,其他的可至文末 GitHub 內查看。

📁 res → 📁 layout → 📄 activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/refresh_layout"
    tools:context=".MainActivity">

    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingLeft="5dp"
        android:paddingRight="5dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center_horizontal|center_vertical"
            android:orientation="vertical"
            android:paddingTop="10dp"
            android:paddingBottom="10dp">

            <Space
                android:layout_width="match_parent"
                android:layout_height="50dp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:layout_marginBottom="10dp"
                android:text="猜數字"
                android:textSize="36sp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/hint"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:layout_marginBottom="10dp"
                android:text="提示訊息:請輸入 1~99 的數字"
                android:textSize="16sp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center_horizontal|center_vertical"
                android:orientation="horizontal"
                android:paddingTop="10dp"
                android:paddingBottom="10dp">

                <EditText
                    android:id="@+id/input"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="30dp"
                    android:layout_marginRight="5dp"
                    android:layout_weight="1"
                    android:digits="0123456789"
                    android:gravity="center"
                    android:inputType="number"
                    android:textSize="24sp" />

                <Button
                    android:id="@+id/submit"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="5dp"
                    android:layout_marginRight="30dp"
                    android:layout_weight="0"
                    android:text="送出"
                    android:textSize="18sp"
                    app:cornerRadius="15dp"/>

            </LinearLayout>

            <TextView
                android:id="@+id/times"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:layout_marginBottom="5dp"
                android:text="猜測次數:0"
                android:textColor="#E24D4D"
                android:textSize="16sp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/record"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="5dp"
                android:layout_marginBottom="5dp"
                android:text="歷史最佳記錄:無"
                android:textColor="#1F2192"
                android:textSize="16sp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/hint2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="5dp"
                android:layout_marginBottom="5dp"
                android:text="重新開始?請下拉!"
                android:textColor="#188EAC"
                android:textSize="16sp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

        </LinearLayout>

    </androidx.core.widget.NestedScrollView>

</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

📁 res → 📁 menu → 📄 menu_main.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item android:id="@+id/record_delete" android:title="刪除記錄" app:showAsAction="never"/>
    <item android:id="@+id/give_up" android:title="放棄遊戲" app:showAsAction="never"/>
    <item android:id="@+id/restart" android:title="重新開始" app:showAsAction="never"/>
    <item android:id="@+id/exit" android:title="結束" app:showAsAction="never"/>
</menu>

📁 java → 📄 MainActivity.java

package tw.mnya.guessthenumber;

import androidx.appcompat.app.AppCompatActivity;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;

import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.text.InputType;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

/* 作者:萌芽站長 */

public class MainActivity extends AppCompatActivity {

    private SwipeRefreshLayout mSwipeRefreshLayout;

    private TextView hint;
    private EditText input;
    private Button submit;
    private TextView times;
    private TextView record;
    private int rec = 9999999;
    private int ranNum = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        hint = (TextView) findViewById(R.id.hint);
        input = (EditText) findViewById(R.id.input);
        submit = (Button) findViewById(R.id.submit);
        times = (TextView) findViewById(R.id.times);
        record = (TextView) findViewById(R.id.record);

        class Game implements Runnable {

            // 變數區
            int in = 0; // 輸入值
            int min = 1; // 最小值
            int max = 99; // 最大值
            int time = 0; // 猜測次數

            @Override
            public void run() {
                new Thread(new Runnable() {
                    @Override
                    public void run() {

                        // 初始值
                        in = 0; // 輸入值
                        min = 1; // 最小值
                        max = 99; // 最大值
                        time = 0; // 猜測次數
                        input.setText(""); // 清空輸入

                        runOnUiThread(new Runnable() {

                            @Override
                            public void run() {

                                hint.setText("提示訊息:請輸入 " + min + "~" + max + " 的數字");
                                times.setText("猜測次數:" + time);
                                input.setInputType(InputType.TYPE_CLASS_NUMBER);
                                submit.setEnabled(true); // 啟用按鈕

                            }
                        });

                        // 讀取歷史最佳記錄
                        String getRecord = getSharedPreferences("record", MODE_PRIVATE)
                                .getString("times", "");
                        if (getRecord.equals("")) {
                            record.setText("歷史最佳記錄:無");
                        } else {
                            record.setText("歷史最佳記錄:" + getRecord + " 次");
                            rec = Integer.parseInt(getRecord);
                        }

                        // 隨機生成 1~99 數字作為遊戲目標猜測值
                        ranNum = (int) (Math.random() * 99 + 1);
                        Log.v("ANS", "答案:" + ranNum);

                        // 送出按鈕監聽
                        submit.setOnClickListener(
                                new View.OnClickListener() {
                                    @Override
                                    public void onClick(View v) {

                                        if (input.getText().toString().matches("")) {

                                            hint.setText("提示訊息:不要空白!請輸入 " + min + "~" + max + " 的數字");

                                        } else {

                                            // 輸入值載入
                                            in = Integer.parseInt(input.getText().toString());

                                            // 清空輸入
                                            input.setText("");

                                            // 判斷區
                                            if (in <= max && in >= min) { // 輸入值介於最大至最小可能值內
                                                if (in > ranNum) {
                                                    max = in;
                                                    hint.setText("提示訊息:請輸入 " + min + "~" + max + " 的數字");
                                                    time++;
                                                } else if (in < ranNum) {
                                                    min = in;
                                                    hint.setText("提示訊息:請輸入 " + min + "~" + max + " 的數字");
                                                    time++;
                                                } else {
                                                    time++;
                                                    hint.setText("恭喜猜中數字「" + ranNum + "」!您只花了 " + time + " 次就完成了!");
                                                    submit.setEnabled(false);
                                                    // 判斷是否寫入歷史最佳記錄
                                                    if (time < rec) {
                                                        rec = time;
                                                        SharedPreferences editRecord = getSharedPreferences("record", MODE_PRIVATE);
                                                        editRecord.edit()
                                                                .putString("times", String.valueOf(rec))
                                                                .apply();
                                                        record.setText("歷史最佳記錄:" + rec + " 次");
                                                    }
                                                }
                                            } else {
                                                hint.setText("提示訊息:請輸入 " + min + "~" + max + " 的數字,不要亂輸入啦!");
                                                time++;
                                            }

                                            // 寫入次數
                                            times.setText("猜測次數:" + time);

                                        }

                                    }
                                }
                        );

                        //


                    }
                }).start();
            }
        }

        Game game = new Game();
        game.run();

        // 下拉重新載入
        mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.refresh_layout);
        mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                mSwipeRefreshLayout.setRefreshing(false);
                Game game = new Game();
                game.run();
            }
        });

    }

    // 右上選單實作

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        // 按下「刪除記錄」後的動作
        if (id == R.id.record_delete) {
            // 更新畫面顯示記錄
            record.setText("歷史最佳記錄:無");
            // 清空記錄值
            SharedPreferences editRecord = getSharedPreferences("record", MODE_PRIVATE);
            editRecord.edit()
                    .putString("times", "")
                    .apply();
            // 建立提示訊息
            Toast.makeText(this, "記錄已刪除", Toast.LENGTH_SHORT).show();
        } else if (id == R.id.give_up) {
            hint.setText("放棄遊戲!答案是「"+ ranNum +"」");
            submit.setEnabled(false);
            times.setText("猜測次數:0");
        } else if (id == R.id.restart) {
            Intent intent = getIntent();
            finish();
            startActivity(intent);
        } else if (id == R.id.exit) {
            finish();
        }
        return super.onOptionsItemSelected(item);
    }

}

⌨️ GitHub 存放庫:https://github.com/qwe987299/guessTheNumber (可下載自由使用!不過務必標註使用來源:作者「萌芽站長」以及本文連結,感謝配合!)

贊助廣告 ‧ Sponsor advertisements

留言區 / Comments

萌芽論壇