实现用java做一个简易版《羊了个羊》小游戏(附源代码)

该项目是跟着这个b站视频一步一步写出来的,初学java有些地方我看不是很明白,但是讲解很仔细,大家可以看原视频,我没有添加背景音乐和背景图片,做出来的效果也勉勉强强。

代码已经上传到github上了,大家可以去github上直接下载代码,附上链接:点击进入github源码链接

嫌麻烦进不去github的,我直接附上HTTPS链接和SSH,自己git clone就行

HTTPS: git clone https://github.com/19138060480/Genshin-elimination-game.git

SSH: git clone [email protected]:19138060480/Genshin-elimination-game.git

先上效果图:

 因为懒得找原图,手边又刚好有原神的七元素图片,所以就用七元素做成了简易版的《原了个原》

 当元素在放满七个后会判断游戏失败,每次图片是随机的(但是可以保证能消除完)后面灰色的图片是被遮盖的图片,无法点击(指点击后不会消失),当前面覆盖的图片被点击消失后才会恢复彩色图片(即可点击状态)。

效果图就先到这里,在上源码之前先看一下我创建的包和类,不想修改乱七八糟的东西的宝子们可以跟我设置相同的包名和类名,省的麻烦。(亲测,修改后可能会出现近百个错误);

 

特别注意的是,imgs里面的图片只要命名正确就可以了,不需要原图(指可以百度),后面加Gray的图片是灰色的,我用ps一个一个把图片拖进去变成灰色你知道有多累吗?(其实也可以通过代码.......)

源码来咯:

首先是BeginGame包里面的Play

package BeginGame;

import Model.Brand;
import Model.Cell;
import Model.Layer;
import Model.Map;
import Tool.MapUtil;

import javax.swing.*;
import java.util.List;

//测试渲染一个地图
public class Play extends JFrame {

    public static Map map= MapUtil.build(3);

    public Play(){
        /*初始化窗口基本信息*/
        inti();

        /*渲染图层*/
        List<Layer> list=map.getList();
        for (int i = 0; i < list.size(); i++) {
            renderLayer(list.get(i));
        }
        map.compareAll();   //判定游戏开始时,所有牌是灰色还是彩色
        /*自动刷新*/
        autoRefresh();          //自动刷新线程
    }

    private void renderLayer(Layer layer){
        /*渲染图层
        默认情况下 brand牌的左上角坐标是0,0    需要改变牌的坐标
        设置布局方式,默认swing 添加组件 提供了多种布局方式  网格、流线、、、  绝对布局
         */

        Cell[][] cells=layer.getCells();
        layer.showCell();
        for (int row = 0; row < cells.length; row++) {
            for (int col = 0; col < cells[row].length; col++) {
                Brand brands1 = cells[row][col].getBrand();

                int x=col*50+layer.getOffsetx();        //加上了偏移量
                int y=row*50+layer.getOffsety();       //设置xy坐标
                brands1.setBounds(x,y,50,50);

                this.getContentPane().add(brands1);
            }
            System.out.println();
        }

    }
    private void autoRefresh(){     //自动刷新方法

        JFrame main=this;

        new Thread(new Runnable() {
            @Override
            public void run() {     //一直执行刷新页面
                while(true){

                    main.repaint();
                    try {
                        Thread.sleep(40);   //40毫秒刷新一次
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
    private void inti(){
        this.setTitle("羊了个羊");      //标题
        this.setSize(450,800);      //设置窗口大小




        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);    //关闭窗口的同时也关闭进程

        /*设置绝对布局*/
        this.setLayout(null);
        this.setBounds(0,0,450,800);
        this.setLocationRelativeTo(null);       //窗口居中
        this.setVisible(true);  //窗口显示(默认关闭)
    }

    public static void main(String[] args) {
        new Play();
    }



}

 然后是Model包

Brand:

package Model;
import BeginGame.Play;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
/*
游戏中的牌
*/
public class Brand extends Component {        //牌类
    /*牌基础信息*/
    private String name;    //名称
    private boolean isGray;    //判断当前的牌是否是在牌堆下
    private Image image;        //正常图片
    private Image grayimage;     //牌堆下灰色图片

    /*坐标*/
    private Integer x;      //代表在渲染的时候左上角的坐标
    private Integer y;

    /*宽高度*/
    private Integer width;
    private Integer height;
    /**/
    private Cell cell;
    
    EliminateBox eliminateBox =new EliminateBox();


    public Brand(String name){      //含name参数构造
        this.name=name;
        /*通过name的值 对应图片目录下的图片前缀*/
        this.image = Toolkit.getDefaultToolkit().getImage("imgs\"+name+".png");    //通过name找到图片
        this.grayimage=Toolkit.getDefaultToolkit().getImage("imgs\"+name+"Gray.png");      //通过name找到灰色图片

        this.isGray=false;       //默认不置灰

        /*设置宽高*/
        this.width=50;
        this.height=50;
        /*设置默认坐标*/
        this.x=0;
        this.y=0;

        this.addMouseListener(new MouseAdapter() {      //鼠标监听器
            @Override
            public void mouseClicked(MouseEvent e) {        //点击鼠标
                System.out.println("点击鼠标");

                Brand brand=(Brand) e.getSource();      //点击后获取当前组件     强制下转型(默认是object)

                if (brand.getGray()){
                    //灰色
                    return;
                }else {

                    /*只在页面中删除了brand对象,但是cell状态中的state和brand没有删除*/
                    //brand.getParent().remove(brand);        //调用上层容器删除自己    一般树形结构使用这样的方式
                    eliminateBox.addBox(brand);


                    /*解决问题关键是纪要删除UI当中的组件,还要删除数据模型中的数据和对应状态*/
                    cell.setState(0);
                    cell.setBrand(null);


                    Play.map.compareAll();          //涉及到map对象的共享  处理:把map对象设为静态变量
                }


            }
        });

    }

    @Override
    public void paint(Graphics g) {     //重写Component的paint方法(绘制函数)
        if(this.isGray==true){                  //通过控制isGray来控制图片的灰色和彩色
            //绘制灰色图片
            g.drawImage(this.grayimage,this.x,this.y,null);
        }else {
            //绘制正常图片
            g.drawImage(this.image,this.x,this.y,null);
        }
    }

    /*     set/get     */
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean isGray() {
        return isGray;
    }

    public void setGray(boolean gray) {
        isGray = gray;
    }
    public boolean getGray(){
        return isGray;
    }

    public Image getImage() {
        return image;
    }

    public void setImage(Image image) {
        this.image = image;
    }

    public Image getGrayimage() {
        return grayimage;
    }

    public void setGrayimage(Image grayimage) {
        this.grayimage = grayimage;
    }

    public Cell getCell() {
        return cell;
    }

    public void setCell(Cell cell) {
        this.cell = cell;
    }

Cell:

package Model;

/*
    单元格类
    有两种状态, 0:无牌,1:有牌;
 */
public class Cell {
    private Integer state;      //0、1
    private  Brand brand;


    /*   get/set方法   */
    public Integer getState() {
        return state;
    }

    public void setState(Integer state) {
        this.state = state;
    }

    public Brand getBrand() {
        return brand;
    }

    public void setBrand(Brand brand) {
        this.brand = brand;
    }
}
EliminateBox:
package Model;

import javax.swing.*;
import java.util.*;
import java.util.Map;
import java.util.stream.Collectors;

public class EliminateBox {         //消除区域
    private static List<Brand> Box=new ArrayList<>();   //存放消除牌的数据

    /*迭代器清空集合方法*/
    void deleteByBrandName(String name){
        Iterator<Brand> iterator = Box.iterator();
        while (iterator.hasNext()){     //如果有下一个值
            Brand next=iterator.next(); //获取下一个值
            if (next.getName().equals(name)){
                next.getParent().remove(next);
                iterator.remove();
            }

        }
    }

    public void addBox(Brand brand){        //添加到消除区方法
        Box.add(brand);
        /*牌的排序(根据名称)*/
        Box.sort(Comparator.comparing(Brand::getName));

        /*消除算法*/
        Map<String, List<Brand>> map= Box.stream().collect(Collectors.groupingBy(Brand::getName));//获取牌的名称
        Set<String> key=map.keySet();
        for (String s:key){
            List<Brand> brands=map.get(s);
            if (brands.size()==3){
                deleteByBrandName(s);       //调用迭代器消除方法
                break;
            }

        }


        paint();        //调用方法 绘制到消除区

        over(brand);//调用方法 判断游戏结束
    }


    void paint(){       //绘制到消除区
        for (int i = 0; i < Box.size(); i++) {
            Brand brand = Box.get(i);
            int x=i*brand.getWidth()+10;
            brand.setBounds(x,600,50,50);
        }
    }

    /*消除方法*/
    void over(Brand brand){
        if (Box.size()>=7){             //判断游戏结束
            JOptionPane.showMessageDialog(brand,"游戏失败");
            System.exit(0);
        }
    }

}

Layer:

package Model;
import java.util.Random;

/*
    图层类
    二维表格
 */
public class Layer {


    private Integer offsetx;    /*设置偏移量*/   //x轴
    private  Integer offsety;   //偏移量 y轴

    private Integer rowNum; //有多少行  (行数)
    private Integer colNum; //有多少列  (列数)

    private Integer capacity;   //当前图层能最多容纳的牌数量     最大容量

    private Integer size;       //图层 目前有多少牌     当牌添加的时候,需要改变值、当牌减少的时候,也需要改变值

    private Layer parent;       //上一层图层对象

    private Cell[][] cells =null;

    public Layer(Integer rowNum, Integer colNum) throws Exception{      //构造函数,传递参数为行号和列号(因为其他属性都可以通过行号列号知道)
        this.rowNum = rowNum;
        this.colNum = colNum;


        this.capacity= this.rowNum * this.colNum;       //容量为行数×列数

        if(this.capacity%3!=0){
            throw new Exception("容量不是3的倍数");
        }

        this.cells=new Cell[this.rowNum][this.colNum];      //因为传递的是行号和列号,所以可以通过行号和列号创建对象.
        this.size=0;        //默认为零

        this.offsetx=new Random().nextInt(100);
        this.offsety=new Random().nextInt(100); //用随机数设置偏移量
    }

    public void showCell(){
        for (int row = 0; row < cells.length; row++) {
            for (int col = 0; col < cells[row].length; col++) {
                Brand brands1= cells[row][col].getBrand();
                System.out.print(brands1.getName()+"-");
            }
            System.out.println();

        }
    }
    /*   get/set方法   */

    public Layer getParent() {
        return parent;
    }

    public void setParent(Layer parent) {
        this.parent = parent;
    }

    public Integer getOffsety() {
        return offsety;
    }

    public void setOffsety(Integer offsety) {
        this.offsety = offsety;
    }

    public Integer getOffsetx() {
        return offsetx;
    }

    public void setOffsetx(Integer offsetx) {
        this.offsetx = offsetx;
    }





    public Integer getRowNum() {
        return rowNum;
    }

    public void setRowNum(Integer rowNum) {
        this.rowNum = rowNum;
    }

    public Integer getColNum() {
        return colNum;
    }

    public void setColNum(Integer colNum) {
        this.colNum = colNum;
    }

    public Integer getCapacity() {
        return capacity;
    }

    public void setCapacity(Integer capacity) {
        this.capacity = capacity;
    }

    public Integer getSize() {
        return size;
    }

    public void setSize(Integer size) {
        this.size = size;
    }

    public Cell[][] getCells() {
        return cells;
    }

    public void setCells(Cell[][] cells) {
        this.cells = cells;
    }
}

Map:

package Model;

import Tool.MapUtil;

import java.util.ArrayList;
import java.util.List;

//图层
public class Map {

    private Integer floorHeight;    //层高        有几个图层
    private List<Layer> list=new ArrayList<>();     //存放图层数据



    /*     set/get     */
    public Integer getFloorHeight() {
        return floorHeight;
    }

    public void setFloorHeight(Integer floorHeight) {
        this.floorHeight = floorHeight;
    }

    public List<Layer> getList() {
        return list;
    }

    public void setList(List<Layer> list) {
        this.list = list;
    }


    /*判断当前map中所有牌是否置灰*/
    public void compareAll(){
        System.out.println("map.compareAll");
        //i=0是最顶层layer,不需要判断
        for (int i = 1; i < list.size(); i++) {
            Layer layer=list.get(i);
            Cell[][] cells=layer.getCells();
            for (int row = 0; row < cells.length; row++) {
                for (int col = 0; col < cells[row].length; col++) {
                    Cell cell=cells[row][col];      //先拿到单元格对象
                    if (cell.getState()==1){
                        Brand brand = cell.getBrand();      //有牌取牌

                        boolean result=MapUtil.compare(brand,layer.getParent());
                        brand.setGray(result);


                    }

                }


            }


        }
    }

}

然后是Tool工具包:

BrandUtil:

package Tool;

import Model.Brand;
import java.util.Random;

/*
    工具类
    提供 创建牌相关的一些公共方法


 */
public class BrandUtil {
    public static Random random =new Random();
    public  static  String[] brandNames= {"风","岩","雷","草","水","火","冰"};     //牌名数组

    public static String getBrandName(){        //随机获取一个牌的名称
        int a=random.nextInt(brandNames.length);
        return brandNames[a];
    }

    //创建随机牌
    public static Brand[] buildBrands(Integer capcity){      //需要参数(数组大小)

        Brand brands[]=new Brand[capcity];

        for (int i = 0; i < brands.length; i=i+3) {
            String randomBrandName=getBrandName();  //每次循环获取一个随机牌名称

            Brand brand1=new Brand(randomBrandName);     //名称传递,创建新的对象
            Brand brand2=new Brand(randomBrandName);
            Brand brand3=new Brand(randomBrandName);        //创建三个相同的对象,方便删除牌

            brands[i]=brand1;
            brands[i+1]=brand2;
            brands[i+2]=brand3;
        }


        for (int i = 0; i < brands.length; i++) {
            //当前位置A的变量拿到
            Brand brandA = brands[i];

            //随机交换位置
            int randomIndex=random.nextInt(brands.length);
            Brand brandB=brands[randomIndex];

            Brand temp=brandA;
            brands[i]= brandB;
            brands[randomIndex]=temp;       //交换

        }
        return brands;
    }
}

LayerUtil:

package Tool;

import Model.Brand;
import Model.Cell;
import Model.Layer;

public class LayerUtil {
    public static Layer build(Integer rowNum, Integer colNum) {
        Layer layer = null;
        try {
            layer = new Layer(rowNum, colNum);     //容量需要为3的倍数,不然下面for循环因为i=i+3时,数组越界会报错
        } catch (Exception e) {
            e.printStackTrace();
        }

        Brand[] brands=BrandUtil.buildBrands(layer.getCapacity());

        Cell cells[][] = layer.getCells();
        int flag=0;
        for (int row = 0; row < cells.length; row++) {
            for (int col = 0; col < cells[row].length; col++) {

                //        System.out.println(row+"-"+col);
                Brand brands1=brands[flag++];
                Cell cell = new Cell();       //初始化单元格对象
                cell.setState(1);
                cell.setBrand(brands1);     //单元格对象找到我们牌

                brands1.setCell(cell);      //牌反向找到单元格对象,       互相链式关系

                cells[row][col] = cell;       //把之前空的图层设置了值
            }
        }
        return layer;

    }
}

MapUtil:

package Tool;

import Model.Brand;
import Model.Cell;
import Model.Layer;
import Model.Map;

import java.awt.*;

public class MapUtil {
    public static Map build(Integer floorHeight){
        Map map=new Map();
        map.setFloorHeight(floorHeight);

        Layer layer1= LayerUtil.build(3,5);
        Layer layer2= LayerUtil.build(9,4);
        Layer layer3= LayerUtil.build(6,4);
        Layer layer4= LayerUtil.build(6,9);
        Layer layer5= LayerUtil.build(3,3);
        Layer layer6= LayerUtil.build(10,9);
        Layer layer7= LayerUtil.build(3,8);
        Layer layer8= LayerUtil.build(9,6);
        Layer layer9= LayerUtil.build(6,9);
        Layer layer10= LayerUtil.build(6,12);
        Layer layer11= LayerUtil.build(6,5);
        Layer layer12= LayerUtil.build(4,9);
        Layer layer13= LayerUtil.build(5,9);
        Layer layer14= LayerUtil.build(7,9);
        Layer layer15= LayerUtil.build(9,9);
        Layer layer16= LayerUtil.build(2,9);
        Layer layer17= LayerUtil.build(5,9);
        Layer layer18= LayerUtil.build(4,9);



        layer1.setParent(null);     //parent为null时已经是最后一层了 是循环递归结束的条件
        layer2.setParent(layer1);
        layer3.setParent(layer2);       //用链式关系把图层锁起来
        layer4.setParent(layer3);
        layer5.setParent(layer4);
        layer6.setParent(layer5);
        layer7.setParent(layer6);
        layer8.setParent(layer7);
        layer9.setParent(layer8);
        layer10.setParent(layer9);
        layer11.setParent(layer10);
        layer12.setParent(layer11);
        layer13.setParent(layer12);
        layer14.setParent(layer13);
        layer15.setParent(layer14);
        layer16.setParent(layer15);
        layer17.setParent(layer16);
        layer18.setParent(layer17);


        map.getList().add(layer1);
        map.getList().add(layer2);
        map.getList().add(layer3);
        map.getList().add(layer4);
        map.getList().add(layer5);
        map.getList().add(layer6);
        map.getList().add(layer7);
        map.getList().add(layer8);
        map.getList().add(layer9);
        map.getList().add(layer10);
        map.getList().add(layer11);
        map.getList().add(layer12);
        map.getList().add(layer13);
        map.getList().add(layer14);
        map.getList().add(layer15);
        map.getList().add(layer16);
        map.getList().add(layer17);
        map.getList().add(layer18);


        return map;
    }

    public static boolean compare(Brand brand, Layer layer){            //判断当前牌和某一图层所有牌是否有矩阵交集,ture表示有交集,显示灰色,false表示没有交集,显示正常牌
        Cell cells[][]=layer.getCells();

        for (int row = 0; row < cells.length; row++) {
            for (int col = 0; col < cells[row].length; col++) {
                //如果当前单元格为空,cell不用比较
                Cell cell=cells[row][col];
                if(cell.getState()==1){
                    //单元格有牌,可以比较
                    Rectangle temp=cell.getBrand().getBounds();

                    Rectangle rect=brand.getBounds();

                    boolean result=rect.intersects(temp);       //布尔类型判断是否有交集

                    if (result){
                        //有交集说明被上层牌盖住了
                        return result;      //判定结束,结束方法
                    }



                }
            }
            System.out.println();

        }

        /*如果跳出了上面的循环,说明都没有交集,需要和更上层进行对比*/

        if (layer.getParent()!=null){
            return compare(brand,layer.getParent());        //递归判定
        }else{      //如果getparent等于null,说明已经到最顶层了
            return false;

        }



    }
}

好了只用这些就能完整的运行啦,细心的人可能发现了我没有发Test包里的东西 ,因为那只是用来做测试的!!!不过如果想要的话私聊我,我看看能不能把整个压缩包发给你.......

我这上面注释写的应该还算清楚,应该不难理解吧,不过为了以防万一,我还是说一下一些关键可修改的地方

首先是MapUtil类里面的,这个包是不是看到很多重复的代码块(不是为了凑代码行数!!)

这是为了创建不同的图层,这个游戏可以理解为有很多图层叠在一起,然后这里就是设置图层的个数,每创建一组就多一层图层。

 这一行是设置图层的长rowNum和宽colNum的 (注意长和宽的乘积一定要是3的倍数,最简单的方法就是其中一个是3或者3的倍数,不然会报错,因为在LayerUtil类里设置了捕获异常,捕获的是BrandUtil类里,因为Brand对象是三个三个创建的,如果不是三的倍数会出现数组越界)

 这一行是为了让后一个图层把上一层图层联系起来,层与层之间互相关联,比如这个图就是layer3与layer2锁在一起,让layer3的父层为layer2,如果要新添加图层的话,新添加的图层也需要把上一层设置为父层。

 这一条代码是为了让图层显示出来,如果没有这一行,你的图层将不会渲染到游戏里,add括号内写哪一层就渲染哪一层。

这三条语句可以看为一个整体,如果要添加或者删除图层的话,只需要修改对应的这三条语句就可以了。

然后是BrandUtil类

 brandNames数组里是你的图片名称,你可以随意修改(前提是找得到图片),图片在imgs文件夹下,和Total_class是同级的,注意你也要创建一个imgs文件夹来存放图片,不然就进入brand类里面,把这个imgs改成你文件的名字

特别说一下,设置的图层偏移量是在Play类里面,写的有注释,可以自行查找也可以不改,还有就是每个图层的长宽比不要太大,不然会出现你不想看到的结果(指bug)。

 其他的有注释,能看懂的应该都能看懂,看不懂的那我也没办法,照抄就好了。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>