羊了个羊-JAVA小游戏2

三. 数据模型3 - Map类

1. Map类 - 地图

注意: 一个地图下面有多个图层, 层层遮盖
重要属性:
floorHeight: 层高 Integer (有几张图层, 与游戏难度相关)
list: 存放图层数据 List<Layer>
生成 get 和 set 方法

/**
 * 地图的构建
 * 一个地图下面有多个图层, 层层遮盖
 */
public class Map {
    private Integer floorHeight; //层高, 有几张图层, 游戏难度是相关的
    private List<Layer> list = new ArrayList<>();
// get和set方法
}

2. 测试地图 - TestBuildMap.class

/**
 *  测试创建一个地图实例  层高3
 */
public class TestBuildMap {
    public static void main(String[] args) {
        Map map = new Map();
        map.setFloorHeight(3); // 设置层高为3层
		// 创建第一层图层
        Layer layer1 = LayerUtil.build(3, 3);
        // 创建第二层图层
        Layer layer2 = LayerUtil.build(6, 6);
        // 创建第三层图层
        Layer layer3 = LayerUtil.build(9, 9);
		
		// 将创建好的图层加入到地图中
        map.getList().add(layer1);
        map.getList().add(layer2);
        map.getList().add(layer3);

        // 因为List是集合, 所以可以遍历
        // 通过map.getList() 获取到list集合
        List<Layer> list = map.getList();
        for (int i=0;i<list.size();i++){
            // 打印当前图层
            System.out.println("第"+i+"个图层");

            // list.get(i) : 获取到当前图层
            // 当前图层有 showCells()方法, 查看每一个图层的内容
            list.get(i).showCells();
        }
    }
}

3. 代码重构 - MapUtil.class

public class MapUtil {
    // 有一个build方法
    public static Map build(Integer floorHeight){
        Map map = new Map();
        map.setFloorHeight(3);

        Layer layer1 = LayerUtil.build(6, 6);
        Layer layer2 = LayerUtil.build(6, 6);
        Layer layer3 = LayerUtil.build(6, 6);

        map.getList().add(layer1);  // 结论 在绝对布局中  同样位置 先加入的组件展示最上层
        map.getList().add(layer2);  // 和现实当中 后面加入的会再上层是不同的
        map.getList().add(layer3);
		// 把 map 创建好返回
        return map;
    }

4. 测试类,渲染map - TestRenderMap.class

public class TestRenderMap extends JFrame{
	// 通过MapUtil的build方法 拿到一个Map
	// 然后 拿到图层的List , 进行遍历
    public static Map map = MapUtil.build(3);
    public TestRenderMap() throws FileNotFoundException, JavaLayerException {
        // 初始化窗口的基本信息
        init();
        // 2.渲染图层
        List<Layer> list = map.getList();
        for (int i=0;i<list.size();i++){
        	// 调用renderLayer方法, 将当前的图层对象传入
            renderLayer(list.get(i));
        }
        // 3.自动刷新
        autoRefresh();
    }

	// 定义renderLayer方法,传入图层对象
    private void renderLayer(Layer layer){
    	//通过图层获取到二维数组
        Cell[][] cells = layer.getCells();
        // 通过遍历将一个一个图层刷新到界面上
        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 * 59;
                int y = row * 66;
                brands1.setBounds(x,y,59,66);
                this.getContentPane().add(brands1);
            }
            System.out.println();
        }
    }
    public static void main(String[] args) throws FileNotFoundException, JavaLayerException {
        new TestRenderMap();

    }
}

注意

测试时:创建地图都创建成 3*3 - MapUtil.java中修改代码
在这里插入图片描述

**5. 修改bug: 图层的偏移 **

绘制 3*3 的图层, 但是完全被覆盖住了
而游戏当中会有 部分遮盖的效果, 可以看到下一层牌的一部分, 从而猜测是什么牌
这样增加了游戏的可玩性 - 设置图层偏移量


以下为测试代码

Layer.class - 先测试

public class Layer {
    private Integer offsetX; // 偏移量
    private Integer offsetY; // 偏移量
	// 给到 get 和 set 方法
	// 在构造方法赋值
	public Layer(Integer rowNum, Integer colNum) throws Exception {
		this.offsetX = 0;
        this.offsetY = 0;
	}
}

MapUtil.class

public class MapUtil {
    // 有一个build方法
    public static Map build(Integer floorHeight){
        Map map = new Map();
        map.setFloorHeight(3);

		layer1.setOffsetX(60);
        layer2.setOffsetX(30);
        layer3.setOffsetX(20);
        
        Layer layer1 = LayerUtil.build(6, 6);
        Layer layer2 = LayerUtil.build(6, 6);
        Layer layer3 = LayerUtil.build(6, 6);
        

TestBuildMap.class

private void renderLayer(Layer layer){
        Cell[][] cells = layer.getCells();
        // 打印图层测试
        layer.showCells();
        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 * 59 + layer.getOffsetX();
                int y = row * 66;
                brands1.setBounds(x,y,59,66);

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

x轴和y轴都设置
Layer.java

public class Layer {
    private Integer offsetX; // 偏移量
    private Integer offsetY; // 偏移量
    // 设置对应的get 和 set 方法
	// 构造方法中赋随机数
	public Layer(Integer rowNum, Integer colNum) throws Exception {
        this.offsetX = new Random().nextInt(100);
        this.offsetY = new Random().nextInt(100);  // 设置0-100的随机数
	}
}

TestRenderMap.java
在这里插入图片描述

四. 图层的遮盖判定算法思路

问题1: 假定我们有 9层 图层, 最顶层称为1层, 最下层我们称为9层, 那么此时第六层当中有一张牌, 他应该是正常还是 显示灰色状态呢?
那么 第一层 的牌呢? 最后一层第9层的 牌呢?
思路:

  • 判定 6层的某一张牌, 和他上层的 5层的所有牌进行一个比较, 是否有交集
    – 如果有: 则说明盖住了, 显示灰色; 不用再判断6层的这张牌 和上层其他牌的遮盖问题
    – 如果没有, 则说明5层的牌并没有盖住它(6层的这张牌); 则继续跟4层比较,如果还没有, 继续比较更高层的牌, 直到 最高层 1层, 全部比较完毕
  • 特殊情况1: 第一层的牌 没有遮盖问题, 都是显示正常
  • 特殊情况2: 消除的游戏, 所以牌在减少, 某一层牌为0了, 或者某个单元格为空 - 消除过程中进行考虑判断
    具有优化的可能, 目前先暂时实现基本功能, 在考虑优化

1.图层遮盖算法的实现-1

图层建立时, 同时也建立链式结构

  • Layer当中增加parentLayer属性 也就是当前图层的上层
    实现compare函数, 比较一张牌和一个图层的遮盖问题
  • 递归实现: 如果和当前图层没有遮盖, 则比较上上层 , 直到顶层

Layer.class

public class Layer {
    private Layer parent;  // 保存上一层图层对象
	// 提供get  set 方法
}

在MapUtil中构建图层的链式关系

public class MapUtil {
    // 有一个build方法
    public static Map build(Integer floorHeight){
        Map map = new Map();
        map.setFloorHeight(3);

        Layer layer1 = LayerUtil.build(6, 6);
        Layer layer2 = LayerUtil.build(6, 6);
        Layer layer3 = LayerUtil.build(6, 6);

        // 构建图层的 链式关系
        layer3.setParent(layer2);
        layer2.setParent(layer1);
        layer1.setParent(null);  
        // 强调: parent=null  说明当前这一层已经是顶层, 
        // 这是循环 或者递归结束的的重要条件

		map.getList().add(layer1);
        map.getList().add(layer2);
        map.getList().add(layer3);
		return map;
   }
   // 定义compare方法 判断是否遮盖
   // 函数的作用: 判断当前牌和某一图层内的所有牌是否有 矩阵交集
   // true : 有交集, 当前牌显示灰色
   // false: 没有交集, 当前牌显示亮色
	public static boolean compare(Brand brand, Layer layer){
        // 获得当前层的所有 cell 单元格
        Cell[][] cells = layer.getCells();
        // 遍历cells
        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();// 获取单元格当中牌对应的矩阵
                    // 获取传入进来的牌的 矩阵
                    // 目标显示对象.getBounds(参照显示对象)-返回一个矩形显示对象区域A(x,y,width,height)-jdk提供
                    Rectangle rect = brand.getBounds();
                    // 比较矩阵和矩阵之间是否有交集的方法 - intersects
                    boolean result = rect.intersects(temp);
                    if (result){  // true
                        //有交集  说明被上面的 牌被盖住了   判断结束
                        return result;
                    }
                }

            }
        }
        
        // 与当前图层的所有牌都比较完毕 没有交集
        // 此时需要和更上层的牌进行比较判断
        
        //getParent()  获取到当前图层的上一层
        // 假设当前有9层, 正在比较的是6层当中的某个牌 正在和5层进行比较
        // layer.getParent()  是获取到4层的所有数据
        if (layer.getParent()!=null){
            return compare(brand, layer.getParent());
        }else{
            //如果 parent==null.说明已经到顶层了
            return false;
        }

    }
}

2.实现遮盖后不能点击且是灰色的

把map当中所有的牌都调用一次compare函数 , 从而判断是否置灰

Map.java

public class Map {
    /**
     * 判断当前map中所有的牌是否置灰
     *
     * 循环太多层, 牌越多 性能越差
     * 这个函数什么时候调用 - 除了以下两种情况, compare函数不执行
     * 1. 游戏开始的时候调用一次
     * 2. 牌点击之后需要调用
     */
    public void compareAll(){
//        测试代码 - 看方法是否执行
        System.out.println("Map.compareAll");
        // 先遍历当前 list
        // 索引号: i=0 是最顶层, 不需要判断 所以直接i=1开始判断
        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];
                    // 判断当前单元格是否等于1
                    if (cell.getState()==1){
                        // 如果有牌, 获取当前的单元格中的牌
                        Brand brand = cell.getBrand();
                        // 调用compare方法, 将当前牌 以及 当前牌的上一个图层传入进去
                        boolean result = MapUtil.compare(brand, layer.getParent());
                        // result: true 或 false;
                        brand.setGray(result);
                    }
                }
                System.out.println();
            }
        }
    }
}

Brand.java-修改
在这里插入图片描述

TestRenderMap.class
在这里插入图片描述
3.问题:点击完上面的牌之后, 下面的牌没有变为正常颜色 - Brand.class

compareAll() 函数在游戏开始时调用了一次, 但是在牌点击消失后没有调用

 this.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    System.out.println("Brand.mouseClicked");

                    Brand brand =(Brand) e.getSource();// 获取当前的组件
                    if (brand.getGray()){
                        // 灰色 不能被点击
                        return;
                    }else{
                    	// 每消除一个调用compareAll()方法
                        // 2. 但是通过 parent.remove() 只是在页面的UI树中删掉了 brand对象
                        // 但是 单元格 cell当中状态 state 和 brand对象 并没有删除
                        // 在我们自己构建的数据结构中还存在 : state并没有变为0 , 而且单元格中的Brand还在
                        // 解决: 把UI中的牌删除 同时 删除数据模型中的数据 和对应状态
                        //brand.getParent().remove(brand);  // 通过父容器删除自己 - 一般树形结构使用
                        cell.setState(0);
                        cell.setBrand(null);
                        // 以上cell 会报错, 因为在Brand对象中, 获取不到cell对象
                        // 解决办法: 一个图层有很多的单元格对象, 一个地图有更多 - 采用从上往下找的办法会特别麻烦
                        // 采用链式的办法 : 定义单元格对象的时候 , 把单元格对象传进来
                        // 在Brand中定义 cell对象   private Cell cell; 提供get和set 方法
                       // 在MapUtil中创建调用LayerUtil.build: 创建牌对象, 一个一个放到 cell中
                                //--将牌 给到单元格对象   cell.setBrand(brands1);
                                //--让牌 反向找到单元格对象 brands1.setCell(cell);
                        //-----------------------------------------------------

                        // 也需要判断 整个map地图 所有牌哪些是置灰
                        // 1. 需要调用 map.compareAll() - 当前Brand对象并没有Map对象,
                        //    问题: map对象的共享问题
                        // 在 TestRenderMap.java中, 将map对象设置为 static, 通过类名访问 -- 不是特别好
                        // 但是 调用map.compareAll() 还是没有显示为正常颜色 - 解决
                        // 原因: 与conpareAll的比较逻辑有关系
                        // 逻辑是: 找到map中的单元格, 判断单元格里面的状态是否为1,如果是1比较, 否则不比较
                        TestRenderMap.map.compareAll();
                    }

在这里插入图片描述

五. 消除区域实现-ElimiateBox.class

在这里插入图片描述
Brand.java

public class Brand extends Component{
    private String name; //牌的名称
    ElimiateBox elimiateBox = new ElimiateBox();
}

在这里插入图片描述

消除区域代码逻辑

/**
 * 消除区域
 */
public class ElimiateBox {
    // 1. 定义列表存放点击牌的数据
    private static List<Brand> slot = new ArrayList<>();
    //2. 定义方法: 消除牌的逻辑
    public void addSlot(Brand brand){
        slot.add(brand);
        // 牌的排序 - 调用sort方法
        slot.sort(Comparator.comparing(Brand::getName));
        // 根据牌的名称进行消除
        // 获取牌的名称
        // 快捷键:slot.stream().collect(Collectors.groupingBy(Brand::getName));
        Map<String, List<Brand>> map = slot.stream().collect(Collectors.groupingBy(Brand::getName));
        // 使用keySet() 方法拿到键
        Set<String> key = map.keySet();
        //快捷键: key.for
        for (String s:key){
            List<Brand> brands = map.get(s);
            if (brands.size()==3){
                // 我们使用的是增强for循环, 不能 一边添加  一边删除
                // 所以使用迭代器进行清空集合
                // 创建方法用来写迭代器清空集合方法 - deleteByBrandName()
                deleteByBrandName(s);
                break;
            }
        }
        // 4. 调用paint方法
        paint();
        // 5. 返回到Brand类中,将 //brand.getParent().remove(brand);
        //    改为  elimiateBox.addSlot(brand);
        over(brand);
    }
    // 3. 绘制到消除框
    void paint(){
        for (int i=0;i<slot.size();i++){
            Brand brand = slot.get(i);
            int x = i*brand.getWidth()+20;
            brand.setBounds(x,640,59,66);
        }
    }

    // 设置游戏失败的方法
    void over(Brand brand){
        if(slot.size()>=7){
            JOptionPane.showMessageDialog(brand, "游戏结束!");
            System.exit(0);
        }
    }

	// 迭代器清空集合
    void deleteByBrandName(String name){
        // 获取迭代器
        Iterator<Brand> iterator = slot.iterator();
        while(iterator.hasNext()){
            Brand next = iterator.next();
            if(next.getName().equals(name)){
                next.getParent().remove(next);
                iterator.remove();
            }
        }
    }
}

六. 添加音乐

  1. 创建根目录music, 引入音乐
  2. 创建lib目录,存放jar包
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

M\usic.class

/**
 * 添加音乐: 引入jar包
 */
public class Music {
    // 构造方法
    public void music() throws FileNotFoundException, JavaLayerException {
        // 通过缓冲流的方式, 获取音乐
        // System.getProperty("user.dir") -- 获取根目录
        String str = System.getProperty("user.dir")+"/music/sheep.mp3";
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(str));
        // 通过 获取播放器, 传入音乐地址
        Player player = new Player(bufferedInputStream);
        player.play();
    }
}

最后在 TestRenderMap中调用
在这里插入图片描述

七.添加背景图片

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值