JAVA记忆翻牌游戏制作

  • 游戏功能需求说明
  • 代码编写
    • 1 框架搭建
    • 2 主要技术难点
      • 21 图片面板对应的图片索引获取
      • 22 图片面板
    • 3 完整代码
  • 游戏截图
    • 1启动后界面
    • 2开始游戏界面
    • 3游戏结束界面

1 游戏功能需求说明

  该游戏主要模拟常见的翻牌游戏,即找到所有两两相同的牌认为游戏成功,主要需求包含:

  • 初始化面板后显示所有图片网格,图片默认显示为背景色;
  • 点击图片后显示图片,再次点击后显示背景;
  • 点击另一张图片,如果与前一张图片相同,则两张图片一直显示,再次点击不会再显示为背景,表示配对成功;如果与前一张图片不同,则两张图片显示50ms后显示为背景;
  • 重复上面两步,直到图片网格全部显示为图片,即所有图片配对成功;
  • 游戏开始后显示游戏用时,并在全部配对成功后提示游戏用时。

2 代码编写

2.1 框架搭建

  主面板当然是JFrame了,显示图片使用的是JLabel,考虑到点击事件的处理以及能够显示出图片的轮廓来,将JLabel放到了一个自己写的一个JPanel子类PicPanel中。

  实现过程中主要的问题就是PicPanel的实现问题,主要是为该类增加了一个MouseListener,需要处理好鼠标点击的事件。因为需要跟主面板进行交互,因此需要将主面板作为构造参数传入到该类中。同时该类是用于显示图片的,自然少不了传入的图片路径了。

2.2 主要技术难点

  当然了这里的技术难点是针对个人来讲的,就是比较耗时的功能点,对于大神来讲就不是什么难点了。

2.2.1 图片面板对应的图片索引获取

  因为需要在构造每个图片面板的时候传入每个面板对应的图片路径,因此首先需要初始化每个图片面板对应的图片路径,这里假设所有图片都存放在本地一个固定目录下,根据游戏设计,图片面板个数作为已知量使用(本程序使用了两个全局静态变量ROWS和COLUMNS表示图片面板网格的行数和列数,自然两者的乘积必须为偶数了),而初始化的图片索引指的是图片路径下所有图片按文件名从小到大排序后,随机生成的一个由int组成的数组,该数组必须满足以下条件:

  • 数组长度与图片面板个数相同;
  • 数组中每个值表示图片路径下的图片顺序;
  • 数组中的值介于0-picNums之间,不包含picNums(图片总数量);
  • 数组中的每个值出现的次数必须为偶数;
  • 数组中每个值所在的位置是随机的;
  • 图片总数小于图片网格个数一半时必须使用所有图片。

      这里针对图片数量和图片面板个数之间的关系使用了两种方法:

  • 图片数量大于等于图片面板个数一半时,因为图片数量多,因此只要随机从图片中选取不重复的图片索引即可,使用的是List;
  • 图片数量小于图片面板个数一半时,因为图片数量少,需要保证所有的图片都要使用上,因此Map来进行下标存储,并记录每个下标出现的次数;

2.2.2 图片面板

  由于需要将图片显示在面板上,这里采用了传统的图片显示方案,即使用JLabel对象,并使用其setIcon()方法为其设置背景图片,为保证图片能够完全铺满整个面板,因此对获取的图像进行了拉伸(有变形的可能)。

  

2.3 完整代码

  这里没有进行详细的拆分,所有代码都写到了一个类中,代码使用的图片放到了D:\pics文件夹下,使用的图片是从网上下载的三个车标,在后面的截图中可以看到,理论上将代码拷贝走,并在D:\pics文件夹下放入图片即可运行。

  

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.border.CompoundBorder;
import javax.swing.border.LineBorder;

/**
 * @author jqs 主要实现记忆翻牌功能
 */
public class RememberCard extends JFrame {

    /**
     * 初始化游戏的行列数,行列数成绩必须为偶数
     */
    private static final int ROWS = 3;
    private static final int COLUMNS = 4;
    private static final long serialVersionUID = -8908268719780973221L;
    private JTextField txt_Time;
    private boolean isRunning = false;
    /**
     * 存放图片的目录,简单起见,存放图片的目录中图片个数为初始化的行列数乘积的一半
     */
    private String picDir = "D:\\pics";
    private String[] picture;
    protected boolean isStart;
    private PicPanel preOne = null;
    /**
     * 用于标示已找到的对数
     */
    private int count;
    private JPanel panel_Pic;

    public RememberCard() {
        setTitle("\u5BFB\u627E\u76F8\u540C\u5361\u724C");

        JPanel panel_Time = new JPanel();
        getContentPane().add(panel_Time, BorderLayout.NORTH);

        JLabel lbl_Time = new JLabel("\u7528\u65F6\uFF1A");
        panel_Time.add(lbl_Time);

        txt_Time = new JTextField();
        txt_Time.setEditable(false);
        panel_Time.add(txt_Time);
        txt_Time.setColumns(10);

        JLabel lbl_Unit = new JLabel("\u79D2");
        panel_Time.add(lbl_Unit);

        JButton btn_Start = new JButton("\u5F00\u59CB");
        panel_Time.add(btn_Start);
        panel_Pic = new JPanel();
        getContentPane().add(panel_Pic, BorderLayout.CENTER);
        btn_Start.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                if (isRunning) {
                    return;
                }
                setRunning(true);
                startGame();

            }
        });
        initPicPanels();
    }

    /**
     * 初始化图片面板
     */
    private void initPicPanels() {
        panel_Pic.setLayout(new GridLayout(ROWS, COLUMNS, 5, 5));
        initPictureIndex();
        for (int i = 0; i < ROWS * COLUMNS; i++) {
            PicPanel panel_1 = new PicPanel(this, picture[i]);
            panel_Pic.add(panel_1);
        }
    }

    /**
     * 开始游戏
     */
    protected void startGame() {
        new Thread() {
            @Override
            public void run() {
                long startTime = System.currentTimeMillis();
                while (count < ROWS * COLUMNS / 2) {
                    txt_Time.setText(((System.currentTimeMillis() - startTime) / 1000)
                            + "");
                }
                JOptionPane.showMessageDialog(null,
                        "成功!共耗时" + txt_Time.getText() + "秒。");
                // 结束后重新初始化一下面板以便于下一次的运行
                count = 0;
                panel_Pic.removeAll();
                initPicPanels();
                txt_Time.setText(null);
                panel_Pic.validate();
                isRunning = false;
            }
        }.start();
    }

    /**
     * 初始化图片的索引并赋值每个图片的路径
     */
    private void initPictureIndex() {
        picture = new String[ROWS * COLUMNS];

        // 这里没有检测图片目录中文件的有效性,需要保证都是图片类型。
        File file = new File(picDir);
        File[] pics = file.listFiles();

        // 初始化一个ROWS*COLUMNS的int数组,里面存放每个图片的索引
        int[] indexs = getIndexs(picture.length, pics.length);
        for (int i = 0; i < indexs.length; i++) {
            picture[i] = pics[indexs[i]].getAbsolutePath();
        }
    }

    /**
     * 根据提供的图片总数目(假设图片都是互不相同的)得到一个长度为sum的数组用来表示每个图片的索引
     *
     * @param sum
     *            游戏的行列数乘积
     * @param picNums
     *            给定目录下图片的总数目
     * @return
     */
    private int[] getIndexs(int sum, int picNums) {
        int half = sum / 2;

        if (picNums < half) {
            return getIndexsByMap(sum, picNums);
        }
        int[] tmpResult = new int[sum];
        Random random = new Random(System.currentTimeMillis());
        int temp = 0;
        LinkedList<Integer> list = new LinkedList<Integer>();
        while (list.size() != half) {
            temp = random.nextInt(picNums);
            if (!list.contains(temp)) {
                list.add(temp);
            } else if (picNums < half) {
                list.add(temp);
            }
        }

        for (int i = 0; i < tmpResult.length; i++) {
            tmpResult[i] = list.get(i >= half ? i % half : i);
        }
        // 将顺序打乱,否则会出现前半部分和后半部分是完全分开的情况
        LinkedList<Integer> _result = new LinkedList<Integer>();
        while (_result.size() != sum) {
            temp = random.nextInt(sum);
            if (!_result.contains(temp)) {
                _result.add(temp);
            }
        }
        int[] result = new int[sum];
        for (int i = 0; i < result.length; i++) {
            result[i] = tmpResult[_result.get(i)];
        }
        return result;
    }

    /**
     * 当图片数量小于总格子数一半时需要使用下面的方法获取,保证所有的图片都能使用上
     *
     * @param sum
     * @param picNums
     * @return
     */
    private int[] getIndexsByMap(int sum, int picNums) {
        int half = sum / 2;
        int[] tmpResult = new int[sum];
        Random random = new Random(System.currentTimeMillis());
        int temp = 0;
        HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
        // 因为图片的数量小于sum的一半,因此先按顺序将图片索引都添加到map中以保证得到的结果中每个图片都被使用到
        for (int i = 0; i < picNums; i++) {
            map.put(i, 1);
        }
        int size = picNums;
        while (size != half) {
            temp = random.nextInt(picNums);
            if (!map.containsKey(temp)) {
                map.put(temp, 1);
            } else {
                map.put(temp, map.get(temp) + 1);
            }
            size++;
        }

        List<Integer> list = mapKeyToList(map);
        for (int i = 0; i < tmpResult.length; i++) {
            tmpResult[i] = list.get(i >= half ? i % half : i);
        }
        // 将顺序打乱,否则会出现前半部分和后半部分是完全分开的情况
        LinkedList<Integer> _result = new LinkedList<Integer>();
        while (_result.size() != sum) {
            temp = random.nextInt(sum);
            if (!_result.contains(temp)) {
                _result.add(temp);
            }
        }
        int[] result = new int[sum];
        for (int i = 0; i < result.length; i++) {
            result[i] = tmpResult[_result.get(i)];
        }
        return result;
    }

    /**
     * 将map中的key转换成一个list,其中每个key的value表示该key出现的次数,转换中如果次数多于1需要重复添加key到list中
     *
     * @param map
     * @return
     */
    private List<Integer> mapKeyToList(HashMap<Integer, Integer> map) {
        List<Integer> list = new ArrayList<Integer>();
        Iterator<Integer> keyIt = map.keySet().iterator();
        Integer key = 0;
        while (keyIt.hasNext()) {
            key = keyIt.next();
            if (map.get(key) == 1) {
                list.add(key);
            } else {
                for (int i = 0; i < map.get(key); i++) {
                    list.add(key);
                }
            }
        }
        return list;
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                RememberCard remCard = new RememberCard();
                remCard.setSize(400, 300);
                remCard.setLocationRelativeTo(null);
                remCard.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                remCard.setVisible(true);
            }
        });
    }

    public PicPanel getPreOne() {
        return preOne;
    }

    public void setPreOne(PicPanel preOne) {
        this.preOne = preOne;
    }

    public void addCount() {
        count++;
    }

    public boolean isRunning() {
        return isRunning;
    }

    public void setRunning(boolean isRunning) {
        this.isRunning = isRunning;
    }
}

/**
 * @author jqs
 *
 *         图片面板,主要实现了图片的显示与图片相同判断
 */
class PicPanel extends JPanel {
    private static final long serialVersionUID = 2172162568449349737L;
    private String picPath;
    private JLabel lbl_Pic = new JLabel();
    private ImageIcon bgIcon = null;
    private boolean isShow = false;
    private RememberCard parent;
    private boolean finished = false;

    public PicPanel(RememberCard rememberCard, String picPath) {
        this.picPath = picPath;
        this.parent = rememberCard;
        this.setBorder(new CompoundBorder(null, new LineBorder(new Color(0, 0,
                0), 2)));
        this.setLayout(new BorderLayout());
        this.add(lbl_Pic, BorderLayout.CENTER);
        this.addMouseListener(mouseAdapter);
    }

    public String getPicPath() {
        return picPath;
    }

    public void setPicPath(String picPath) {
        this.picPath = picPath;
    }

    /**
     * 图片面板的鼠标事件监听,配对过程在此完成
     */
    private MouseAdapter mouseAdapter = new MouseAdapter() {
        @Override
        public void mouseClicked(MouseEvent e) {
            new Thread() {
                public void run() {
                    if (!parent.isRunning() || finished) {
                        return;
                    }
                    isShow = !isShow;
                    if (isShow) {
                        if (bgIcon == null) {
                            initLabelImage();
                        }
                        PicPanel curOne = (PicPanel) lbl_Pic.getParent();
                        PicPanel preOne = parent.getPreOne();
                        if (preOne == null) {
                            parent.setPreOne(curOne);
                        } else {
                            boolean right = checkRight(curOne, preOne);
                            if (right) {
                                parent.setPreOne(null);
                                curOne.setFinished(true);
                                preOne.setFinished(true);
                                parent.addCount();
                            } else {
                                lbl_Pic.setIcon(bgIcon);
                                repaint();
                                try {
                                    Thread.sleep(50);
                                } catch (InterruptedException e1) {
                                    e1.printStackTrace();
                                }
                                lbl_Pic.setIcon(null);
                                isShow = !isShow;
                                repaint();
                                preOne.getMouseListeners()[0]
                                        .mouseClicked(null);
                                parent.setPreOne(null);
                                return;
                            }
                        }
                        lbl_Pic.setIcon(bgIcon);
                    } else {
                        lbl_Pic.setIcon(null);
                    }
                    repaint();

                };
            }.start();
        }

        /**
         * 检查两个面板显示的图片是否一致,根据图片的路径来判断,同时要保证两个面板不是同一个面板
         *
         * @param curOne
         * @param preOne
         * @return
         */
        private boolean checkRight(PicPanel curOne, PicPanel preOne) {
            return curOne.getPicPath().equals(preOne.getPicPath())
                    && !curOne.equals(preOne);
        }
    };

    /**
     * 初始化Label对象的image
     */
    private void initLabelImage() {
        try {
            Image image = ImageIO.read(new File(picPath));
            if (image != null) {
                int lblWidth = this.getWidth();
                int lblHeight = this.getHeight();
                bgIcon = new ImageIcon(image.getScaledInstance(lblWidth,
                        lblHeight, Image.SCALE_DEFAULT));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 当找到配对的图片面板后设置完成状态为true,此时点击图片面板已经无效了。
     *
     * @param b
     */
    protected void setFinished(boolean b) {
        finished = b;
    }
}

3 游戏截图

3.1启动后界面

3.2开始游戏界面

3.3游戏结束界面

时间: 2024-08-28 05:02:27

JAVA记忆翻牌游戏制作的相关文章

程序设计C语言二级考试教程 Java基础视频教程 安卓软件开发教程 Unity3D游戏制作入门教程

热门推荐电脑办公计算机基础知识教程 Excel2010基础教程 Word2010基础教程 PPT2010基础教程 五笔打字视频教程 Excel函数应用教程 Excel VBA基础教程 WPS2013表格教程 更多>平面设计PhotoshopCS5教程 CorelDRAW X5视频教程 Photoshop商业修图教程 Illustrator CS6视频教程 更多>室内设计3Dsmax2012教程 效果图实例提高教程 室内设计实战教程 欧式效果图制作实例教程 AutoCAD2014室内设计 Aut

js之翻牌游戏中的一个深刻感悟

先“上菜”: 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>记忆方块</title> 6 <script src="randomNum.js"></script> 7 <script src="Card.js"&g

游戏制作大致流程粗谈之二

上次讲到了游戏原画的制作,在原画师完成原画的创作后,负责建模的同学便需要通过建模工具对原画进行建模,包括游戏的人物模型,场景,物品,等等等等, 游戏建模大致流程如下:1.建立模型   2.UV展开 3.绘制贴图 4.骨骼动画,同时还需要进行编辑的有模型的碰撞体积等等,然后用贴图对模型进行渲染,同时修改 一些材质. 游戏的模型完成后便该有程序员同学来进行代码的编写了,目前比较流行的游戏编程语言有C++,JAVA等,游戏编程接口有DirectX,OpenGL等.编程是游戏制作 环节中耗时最长,工作量

1.cocos2dx记忆卡片游戏代码、并将游戏移植到“华为荣耀”手机上、移植中的问题总结

 1记忆卡片游戏代码 CardItem.h #pragmaonce #ifndef__CardItem_H__ #define__CardItem_H__ #include"cocos2d.h" USING_NS_CC; classCardItem :publicCCSprite { public: staticCardItem *create(intidx); boolinit(intidx); CCLabelTTF *ttf; CCSprite *bg; CC_SYNTHESI

简易2D横版RPG游戏制作

Unity学习笔记1 简易2D横版RPG游戏制作 http://m.blog.csdn.net/article/details?id=24601905

游戏制作流程【转】

作为创意产业最具发展潜力的行业——游戏行业,它的发展俨然超过所有人的预想.国家政策大力扶持,游戏市场发展势头迅猛,网游收入规模超越传统三大娱乐产业:电影票房.电视娱乐节目和音像制品. 与此同时,国内网游制作水准也在不断提升,<完美世界>.<梦幻西游>等众多高水平网游逐渐夺回被国外网游占据的市场份额.国产网游的逐步成熟,中国网 游市场的前景一片大好,让很多喜欢游戏的人都想进入游戏行业,但对大多数人来说这一领域还是陌生.空白的.这些高水准的网游是如何制作出来的?怎么才能成 为一名优秀的

我的项目 6 js实现翻牌游戏

在我的项目中需要在里面添加一个翻牌游戏,就研究了一下,在这里只实现了基本的效果.不多说,和大家分享一下. 说到翻牌游戏,大致分为以下几个步骤: 绘制正反面卡牌-------------->洗牌----------------------->翻牌------------------->翻牌判断 这里面应用的一些图片,,,,,,额额额额,,,大家就自己下两个试试,这里也提供不了...嘿嘿 1.绘制正反面卡牌 function make_deck()//生成卡组并绘制 { var i;// v

关于游戏制作的相关细节

网络是一个发展非常快的地方,其中很多东西的更新换代都是非常快的,之前流行的还是一个游戏,可能过一段时间以后,新的游戏出现了,大家的选择自然就不一样了.这一点对棋牌游戏制作来说是非常重要的,在这个过程中,效率是很重要的,先开发出来的平台就是很占优势的,而大雄游戏的成功就是因为它的棋牌游戏制作效率是极高的,玩家很喜欢. 不过大雄游戏认为在这个过程中只重视快是不行的,对于棋牌游戏制作来说,游戏的每一个细节都是要处理好的,这样才会得到大家的认可.很多平台认为一个游戏即使有一些小瑕疵也是没有问题的.而事实

张左峰的分享 网页游戏制作技术 加密的设计思路与手段

网页游戏制作技术 加密的设计思路与手段 必备工具:Doswf 好朋友Laan开发,请自行百度搜索 今天太晚了,明天再更新内容...咔咔咔