Android Studio制作简单拼图小游戏

火影  金牌会员 | 2025-1-20 03:34:04 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 987|帖子 987|积分 2961

制作的这个小游戏我称之为速度真夫君
这是一个九宫格3x3的还原图片的小游戏,当你运行程序时,计时器显示为0,当你点击第一个碎片开始计时,并且记载你的点击次数,相邻两个碎片可以互换,凌驾120秒游戏失败

1.class PuzzleView

代码如下:
  1. package com.example.gameapplication.Activity;
  2. import android.content.Context;
  3. import android.graphics.Bitmap;
  4. import android.graphics.Canvas;
  5. import android.graphics.Paint;
  6. import android.os.Handler;
  7. import android.util.AttributeSet;
  8. import android.view.MotionEvent;
  9. import android.view.View;
  10. import android.widget.Toast;
  11. import java.util.ArrayList;
  12. import java.util.Collections;
  13. import java.util.List;
  14. public class PuzzleView extends View {
  15.     private List<PuzzlePiece> pieces; // 存储拼图块的列表
  16.     private int numColumns = 3; // 拼图的列数(3x3)
  17.     private int pieceSize; // 原始拼图块大小
  18.     private int scaledPieceSize; // 缩放后的拼图块大小
  19.     private Paint paint; // 用于绘制拼图块的画笔
  20.     private Paint textPaint; // 用于绘制文本的画笔
  21.     private Integer firstSelectedIndex = null; // 记录第一个选中的拼图块索引
  22.     private float scaleFactor = 1.5f; // 拼图块的缩放因子
  23.     private int swapCount = 0; // 记录交换次数的计数器
  24.     private long startTime ; // 计时开始时间,初始化为0
  25.     private Handler handler = new Handler(); // 用于更新计时器的处理器
  26.     private boolean isRunning = false; // 计时器是否在运行
  27.     // 定义一个Runnable来定期更新计时器
  28.     private Runnable timerRunnable = new Runnable() {
  29.         @Override
  30.         public void run() {
  31.             if (isRunning) {
  32.                 long elapsedTime = (System.currentTimeMillis() - startTime) / 1000; // 计算经过的时间(秒)
  33.                 if (elapsedTime >= 120) {
  34.                     endGame("时间到,游戏结束!"); // 超过120秒,结束游戏
  35.                     return;
  36.                 }
  37.                 invalidate(); // 刷新视图以更新计时器
  38.                 handler.postDelayed(this, 1000); // 每秒更新一次
  39.             }
  40.         }
  41.     };
  42.     public PuzzleView(Context context, AttributeSet attrs) {
  43.         super(context, attrs);
  44.         paint = new Paint(); // 初始化绘图画笔
  45.         textPaint = new Paint(); // 初始化文本画笔
  46.         textPaint.setColor(0xFF000000); // 设置文本颜色为黑色
  47.         textPaint.setTextSize(50); // 设置文本大小
  48.         pieces = new ArrayList<>(); // 初始化拼图块列表
  49.     }
  50.     // 初始化拼图,分割图像为拼图块
  51.     public void initializePuzzle(Bitmap bitmap) {
  52.         pieceSize = bitmap.getWidth() / numColumns; // 计算原始拼图块大小
  53.         scaledPieceSize = (int) (pieceSize * scaleFactor); // 计算缩放后的拼图块大小
  54.         // 将图像分割为拼图块
  55.         for (int i = 0; i < numColumns; i++) {
  56.             for (int j = 0; j < numColumns; j++) {
  57.                 // 创建拼图块的Bitmap
  58.                 Bitmap pieceBitmap = Bitmap.createBitmap(bitmap, j * pieceSize, i * pieceSize, pieceSize, pieceSize);
  59.                 // 缩放拼图块
  60.                 pieceBitmap = Bitmap.createScaledBitmap(pieceBitmap, scaledPieceSize, scaledPieceSize, true);
  61.                 pieces.add(new PuzzlePiece(pieceBitmap, i * numColumns + j)); // 添加拼图块到列表
  62.             }
  63.         }
  64.         shufflePuzzle(); // 打乱拼图
  65.         invalidate(); // 刷新视图
  66.     }
  67.     // 打乱拼图块的顺序
  68.     private void shufflePuzzle() {
  69.         Collections.shuffle(pieces); // 随机打乱拼图块
  70.     }
  71.     // 启动计时器
  72.     private void startTimer() {
  73.         startTime = System.currentTimeMillis(); // 记录开始时间
  74.         isRunning = true; // 设置计时器为运行状态
  75.         handler.postDelayed(timerRunnable, 1000); // 每秒启动一次计时器
  76.     }
  77.     // 结束游戏并显示提示
  78.     private void endGame(String message) {
  79.         isRunning = false; // 停止计时器
  80.         handler.removeCallbacks(timerRunnable); // 移除计时器回调
  81.         // 计算总用时(秒),确保在游戏开始后才计算
  82.         long totalTime = (isRunning ? (System.currentTimeMillis() - startTime) / 1000 : 0);
  83.         Toast.makeText(getContext(), message + " 用时: " + totalTime + "秒", Toast.LENGTH_SHORT).show(); // 显示结束提示
  84.     }
  85.     @Override
  86.     protected void onDraw(Canvas canvas) {
  87.         super.onDraw(canvas);
  88.         // 计算拼图整体绘制的起始位置,使其居中
  89.         int totalWidth = scaledPieceSize * numColumns;
  90.         int totalHeight = scaledPieceSize * numColumns;
  91.         int startX = (getWidth() - totalWidth) / 2; // 水平居中
  92.         int startY = (getHeight() - totalHeight) / 2; // 垂直居中
  93.         // 绘制拼图块
  94.         for (int i = 0; i < pieces.size(); i++) {
  95.             PuzzlePiece piece = pieces.get(i);
  96.             int x = startX + (i % numColumns) * scaledPieceSize; // 计算水平坐标
  97.             int y = startY + (i / numColumns) * scaledPieceSize; // 计算垂直坐标
  98.             canvas.drawBitmap(piece.getBitmap(), x, y, paint); // 绘制拼图块
  99.         }
  100.         // 绘制交换次数文本
  101.         canvas.drawText("交换次数: " + swapCount, startX, startY - 20, textPaint);
  102.         // 计算经过的时间(秒)
  103.         long elapsedTime = (isRunning ? (System.currentTimeMillis() - startTime) / 1000 : 0);
  104.         // 将时间格式化为“mm:ss”
  105.         String timeFormatted = String.format("%02d:%02d", elapsedTime / 60, elapsedTime % 60);
  106.         canvas.drawText("时间: " + timeFormatted, startX, startY - 70, textPaint);
  107.     }
  108.     @Override
  109.     public boolean onTouchEvent(MotionEvent event) {
  110.         if (event.getAction() == MotionEvent.ACTION_DOWN) {
  111.             // 计算点击的列和行
  112.             int startX = (getWidth() - (scaledPieceSize * numColumns)) / 2; // 计算拼图的起始X坐标
  113.             int startY = (getHeight() - (scaledPieceSize * numColumns)) / 2; // 计算拼图的起始Y坐标
  114.             int x = (int) (event.getX() - startX) / scaledPieceSize; // 计算点击的列
  115.             int y = (int) (event.getY() - startY) / scaledPieceSize; // 计算点击的行
  116.             int clickedIndex = y * numColumns + x; // 计算点击的拼图块索引
  117.             // 处理选中逻辑
  118.             if (clickedIndex >= 0 && clickedIndex < pieces.size()) {
  119.                 if (!isRunning) {
  120.                     startTimer(); // 如果计时器未运行,则开始计时
  121.                 }
  122.                 handleSelection(clickedIndex); // 处理选中拼图块
  123.             }
  124.             invalidate(); // 刷新视图
  125.         }
  126.         return true; // 事件处理完成
  127.     }
  128.     private void handleSelection(int clickedIndex) {
  129.         if (firstSelectedIndex == null) {
  130.             // 第一次点击,记录索引
  131.             firstSelectedIndex = clickedIndex;
  132.         } else {
  133.             // 第二次点击,检查是否可以交换
  134.             if (isAdjacent(firstSelectedIndex, clickedIndex)) {
  135.                 swapPieces(firstSelectedIndex, clickedIndex); // 交换拼图块
  136.                 swapCount++; // 增加交换次数计数
  137.             }
  138.             firstSelectedIndex = null; // 重置选中状态
  139.         }
  140.     }
  141.     // 交换拼图块
  142.     private void swapPieces(int index1, int index2) {
  143.         Collections.swap(pieces, index1, index2); // 交换拼图块
  144.         checkCompletion(); // 检查拼图是否完成
  145.     }
  146.     // 检查两个拼图块是否相邻
  147.     private boolean isAdjacent(int index1, int index2) {
  148.         int row1 = index1 / numColumns;
  149.         int col1 = index1 % numColumns;
  150.         int row2 = index2 / numColumns;
  151.         int col2 = index2 % numColumns;
  152.         // 检查是否相邻(上下或左右)
  153.         return (Math.abs(row1 - row2) + Math.abs(col1 - col2) == 1);
  154.     }
  155.     // 检查拼图是否完成
  156.     private void checkCompletion() {
  157.         boolean completed = true;
  158.         for (int i = 0; i < pieces.size(); i++) {
  159.             if (pieces.get(i).getPosition() != i) {
  160.                 completed = false; // 拼图尚未完成
  161.                 break;
  162.             }
  163.         }
  164.         if (completed) {
  165.             long totalTime = (System.currentTimeMillis() - startTime) / 1000; // 计算总用时(秒)
  166.             endGame("拼图完成! 总交换次数: " + swapCount + ", 用时: " + totalTime + "秒"); // 游戏完成提示
  167.         }
  168.     }
  169. }
复制代码
2.class PuzzlePiece

代码如下:
  1. package com.example.gameapplication.Activity;
  2. import android.graphics.Bitmap;
  3. public class PuzzlePiece {
  4.     private Bitmap bitmap;
  5.     private int position; // 在拼图中的位置
  6.     public PuzzlePiece(Bitmap bitmap, int position) {
  7.         this.bitmap = bitmap;
  8.         this.position = position;
  9.     }
  10.     public Bitmap getBitmap() {
  11.         return bitmap;
  12.     }
  13.     public int getPosition() {
  14.         return position;
  15.     }
  16.     public void setPosition(int position) {
  17.         this.position = position;
  18.     }
  19. }
复制代码
3.class MainActivity

  1. package com.example.gameapplication;
  2. import androidx.appcompat.app.AppCompatActivity;
  3. import android.graphics.Bitmap;
  4. import android.graphics.BitmapFactory;
  5. import android.os.Bundle;
  6. import com.example.gameapplication.Activity.PuzzleView;
  7. public class MainActivity extends AppCompatActivity {
  8.     private PuzzleView puzzleView;
  9.     @Override
  10.     protected void onCreate(Bundle savedInstanceState) {
  11.         super.onCreate(savedInstanceState);
  12.         puzzleView = new PuzzleView(this, null);
  13.         setContentView(puzzleView);
  14.         Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.puzzle); // 替换为你的图片
  15.         puzzleView.initializePuzzle(bitmap);
  16.     }
  17. }
复制代码
4.activity_main.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.     android:layout_width="match_parent"
  4.     android:layout_height="match_parent">
  5.     <!-- PuzzleView will be set programmatically in MainActivity -->
  6. </FrameLayout>
复制代码
最后自己找一张喜欢的图片放进去就可以了

 总结,这个游戏没什么难度 但是重要在于图片的起始位置,判断点击的是哪个碎片,两个碎片是否相邻,时间的盘算。结束!!!!!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

火影

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表