java oop经典案例开发与源码 -java swing 羊了*羊 简易版本开发 (1) 地图模块编辑器开发(附github源码地址)

简介: java oop经典案例开发与源码 -java swing 羊了*羊 简易版本开发 (1) 地图模块编辑器开发(附github源码地址)

背景

最近爆火的消消游戏 大家应该都玩过,

有没有想过用java自己开发一个呢,我们玩的小程序是客户端,

首先加载地图才能玩,地图是在服务端编辑的,

所以地图是第一步,本篇我们就是开发一个简易版的地图编辑器

地图编辑器效果图

目录结构以及模块

关键模块以及代码分析

MainWindow.java


主窗口,也是程序入口,这里因为只写了地图编辑器代码,所以目前只负责启动地图编辑器,主要是用spring application runner  启动,目的是等所有comment 都被spring 管理之后在启动

@Component
public class MainWindowStartRunner implements ApplicationRunner {
    @Autowired
    private MainWindow mainWindow;
    @Override
    public void run(ApplicationArguments args) throws Exception {
        mainWindow.init();
    }
}

DesignPanel.java

地图设计器主入口,将地图编辑器面板,按钮面板和图片选项卡面板放入:

public class DesignPanel extends JPanel {
    private final DesignMapPanel designMapPanel;
    private final DesignSpriteGroupPanel spriteGroupPanel;
    private final DesignButtonPanel buttonPanel;
    public void init() throws IOException {
        this.setLayout(new BorderLayout());
        this.add(spriteGroupPanel,BorderLayout.WEST);
        this.add(designMapPanel,BorderLayout.CENTER);
        this.add(buttonPanel,BorderLayout.NORTH);
    }
}

DesignMapPanel.java 是地图编辑器核心类,负责绘制网格,以及添加图像到地图中,关键代码图下

 public  DesignMapPanel() throws IOException {
        //获取总窗体宽高
        GraphicsEnvironment ge=GraphicsEnvironment.getLocalGraphicsEnvironment();
        Rectangle rect=ge.getMaximumWindowBounds();
        int w=rect.width -230;
        int h=rect.height -104 ;
        this.setLayout(null);
        //设置滚动条
        jLayeredPane = new JLayeredPane();
        jLayeredPane.setBounds(0,0,w,h);
        jLayeredPane.setLayout(null);
        this.add(jLayeredPane);
        //设置初始化时的背景图片大小
        windowWidth = jLayeredPane.getBounds().width;
        windowHeight = jLayeredPane.getBounds().height;
        bgImg = ImageIO.read(this.getClass().getResourceAsStream("/images/sprites/bg.jpeg"));
        bgLabel = new JLabel(new ImageIcon(bgImg.getScaledInstance(windowWidth,windowHeight,Image.SCALE_DEFAULT)));
        bgLabel.setBounds(0,0,windowWidth,windowHeight);
        this.jLayeredPane.add(bgLabel,JLayeredPane.DEFAULT_LAYER);
        //设置鼠标点击事件
        this.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                addHeros(e.getX(), e.getY());
            }
        });
    }
@Override
    public void paint(Graphics g) {
        super.paint(g);
        Graphics2D graphics2D = (Graphics2D) g;
        graphics2D.setStroke(new BasicStroke(2.0f));
        graphics2D.setColor(Color.black);
        //获取当前窗口宽高大小
        windowWidth = this.getBounds().width;
        windowHeight = this.getBounds().height;
        //计算宽高间距
        cpix = windowWidth / columns - 1;
        hpix = windowHeight / rows - 1;
        //绘制列线条
        for (int i = 0; i < columns; i++) {
            graphics2D.drawLine(i * cpix, 0, i * cpix, windowHeight);
        }
        //绘制行线条
        for (int i = 0; i < rows; i++) {
            graphics2D.drawLine(0, i * hpix, windowWidth, i * hpix);
        }
    }
private void addHeros(int x, int y) {
        //循环宽高,找到鼠标点击点最靠近的横竖交界点
        for (int i = 1; i <= columns; i++) {
            for (int j = 1; j <= rows; j++) {
                int pointX = i * cpix;
                int pointY = j * hpix;
                //碰撞检测,判断鼠标点击点与哪行哪列最接近,并记录
                Rectangle clickPoint = new Rectangle(pointX - cpix / 2, pointY - hpix / 2, cpix, hpix);
                boolean isIn = clickPoint.contains(x, y);
                if (isIn) {
                    //找到了最近交界点行号和列号,创建对象并报存
                    HeroSprite heroSprite = new HeroSprite(i, j, HeroSelectContextHolder.currentHeroType());
                    HeroView heroView = new HeroView(heroSprite);
                    Border blackline = BorderFactory.createRaisedBevelBorder();
                    heroView.setBorder(blackline);
                    heroView.setIcon(new ImageIcon(heroView.getImage().getScaledInstance(cpix ,hpix*2,Image.SCALE_DEFAULT)));
                    heroView.setBounds(i * cpix - cpix / 2, j * hpix - hpix, cpix, hpix * 2);
                    heroViews.add(heroView);
                    //将选择的图像放到找到的中心点位置
                    jLayeredPane.add(heroView,JLayeredPane.DRAG_LAYER);
                    System.out.println("c:" + i + ",r:" + j);
                    this.updateUI();
                }
            }
        }
    }

addHeros 比较关键,使用碰撞检测找到对应最近的行号和列表,并将选择的图像放到他们的交界点处

HeroSelectContextHolder.java 主要负责左边图像选择卡和右边地图编辑器关联起来,主要是保存当前选择的是哪个图像

public class HeroSelectContextHolder {
    private static HeroType heroType = HeroType.HERO_TYPE_1;
    public static void selectHeroType(HeroType heroType){
        HeroSelectContextHolder.heroType = heroType;
    }
    public static HeroType currentHeroType(){
        return heroType;
    }
}

HeroView.java 图像对象,主要是用于对绘制等一些功能提供方法

public class HeroView extends JLabel{
    private static Image s1Img;
    private static Image s2Img;
    private static Image s3Img;
    private static Image s4Img;
    private static Image s5Img;
    private HeroSprite heroSprite;
    public HeroView(HeroSprite heroSprite) {
        this.heroSprite = heroSprite;
        initImages();
    }
    private void initImages() {
        try {
            if (Objects.isNull(s1Img) || Objects.isNull(s2Img) || Objects.isNull(s3Img) || Objects.isNull(s4Img) ) {
                s1Img = ImageIO.read(this.getClass().getResourceAsStream("/images/sprites/1.png"));
                s2Img = ImageIO.read(this.getClass().getResourceAsStream("/images/sprites/2.png"));
                s3Img = ImageIO.read(this.getClass().getResourceAsStream("/images/sprites/3.png"));
                s4Img = ImageIO.read(this.getClass().getResourceAsStream("/images/sprites/4.png"));
                s5Img = ImageIO.read(this.getClass().getResourceAsStream("/images/sprites/5.png"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public HeroSprite getHeroSprite() {
        return heroSprite;
    }
    public Image getImage() {
        if (heroSprite.getHeroType() == HeroType.HERO_TYPE_1) {
            return s1Img;
        }
        if (heroSprite.getHeroType() == HeroType.HERO_TYPE_2) {
            return s2Img;
        }
        if (heroSprite.getHeroType() == HeroType.HERO_TYPE_3) {
            return s3Img;
        }
        if (heroSprite.getHeroType() == HeroType.HERO_TYPE_4) {
            return s4Img;
        }
        if (heroSprite.getHeroType() == HeroType.HERO_TYPE_5) {
            return s5Img;
        }
        return null;
    }
}

HeroSprite.java 图像内部逻辑对象,主要负责保存逻辑内容

@AllArgsConstructor
@Getter
public class HeroSprite {
    private int col = 0;
    private int row = 0;
    private HeroType heroType;
    public boolean isIn(int x,int y){
        return this.col==x&& row==y;
    }
}

HeroType.java 图像类型,这里具体对应左边某个选择的图像

public enum HeroType {
    HERO_TYPE_1(1),
    HERO_TYPE_2(2),
    HERO_TYPE_3(3),
    HERO_TYPE_4(4),
    HERO_TYPE_5(5);
    private int type ;
    HeroType(int type ){
        this.type = type;
    }
    public int getType() {
        return type;
    }
}

DesignButtonPanel.java 负责上面的按钮操作面板,目前地图选择页面里只有选择某个关卡并保存,将数据和管卡保存起来并生成json文件

this.saveButton.addActionListener(e -> {
            //选择哪个关卡
            Integer selectLevel = Integer.parseInt(this.levels.getSelectedItem().toString());
            //获取地图数据
            HeroMapData heroMapData = HeroMapData.builder().heroSprites(designMapPanel.heroSprites())
                    .level(selectLevel).build();
            String mapJson = JSONUtil.toJsonStr(heroMapData);
            //获取当前项目目录
            String usrDir = System.getProperty("user.dir");
            //按照关卡将地图数据保存
            File mapFile = new File(usrDir+File.separator+"map"+File.separator+selectLevel+".json");
            FileUtil.writeBytes(mapJson.getBytes(),mapFile);
        });

点击保存后可以到当前项目目录下找到map/#{选择的管卡}.json文件,如下:


bf19db127b1e44658cffe31b9f76ee4d.png



907d8f7a419145fcb1f473197dc9a511.png


4edf770900f14f5c85673d27fcbd7ded.png

我地图编辑后选择了管卡1,所有可以看见1.json 内容如上

注意事项

第一次运行或者 reources 下增删改图片记得都要maven clean ,然后compile ,不然会遇见图片空指针问题

由于开发时间较短,大概用了不到一上午时间,所以还有很多需要优化重构的地方,也当成练习题大家可以继续扩展优化,本篇主要也是抛砖引玉,有问题可以多留言交流

github源码地址 这里


相关文章
|
2月前
|
Java
Java开发实现图片URL地址检验,如何编码?
【10月更文挑战第14天】Java开发实现图片URL地址检验,如何编码?
93 4
|
2月前
|
Web App开发 Java
使用java操作浏览器的工具selenium-java和webdriver下载地址
【10月更文挑战第12天】Selenium-java依赖包用于自动化Web测试,版本为3.141.59。ChromeDriver和EdgeDriver分别用于控制Chrome和Edge浏览器,需确保版本与浏览器匹配。示例代码展示了如何使用Selenium-java模拟登录CSDN,包括设置驱动路径、添加Cookies和获取页面源码。
134 6
|
1月前
|
Java Spring
JAVA获取重定向地址URL的两种方法
【10月更文挑战第17天】本文介绍了两种在Java中获取HTTP响应头中的Location字段的方法:一种是使用HttpURLConnection,另一种是使用Spring的RestTemplate。通过设置连接超时和禁用自动重定向,确保请求按预期执行。此外,还提供了一个自定义的`NoRedirectSimpleClientHttpRequestFactory`类,用于禁用RestTemplate的自动重定向功能。
|
2月前
|
JSON JavaScript 前端开发
《进阶篇第7章》学习vue中的ajax之后,练习vue案例-github用户搜索案例
《进阶篇第7章》学习vue中的ajax之后,练习vue案例-github用户搜索案例
18 0
|
2月前
|
存储 IDE 开发工具
来咯,他来咯 看GitHub Codespaces 如何帮助缩短开发设置时间
来咯,他来咯 看GitHub Codespaces 如何帮助缩短开发设置时间
34 0
|
4月前
|
存储 API 网络架构
GitHub——通过接口获取仓库下的最新版本
GitHub——通过接口获取仓库下的最新版本
42 1
|
4月前
|
开发者 存储 API
Xamarin 开发者的社区资源概览:从官方文档到GitHub示例,全面探索提升开发技能与解决问题的多元化渠道与实用工具
【8月更文挑战第31天】Xamarin 开发者社区资源概览旨在提升开发效率与解决问题,涵盖官方文档、社区论坛、GitHub 项目等。官方文档详尽,涵盖 Xamarin.Forms 使用、性能优化等;社区论坛供交流心得;GitHub 提供示例代码。此外,第三方博客、视频教程及 Xamarin University 等资源也丰富多样,适合各阶段开发者学习与提升。通过综合利用这些资源,开发者可不断进步,应对技术挑战。
56 0
爆赞!GitHub首本Python开发实战背记手册,标星果然百万名不虚传
Python (发音:[ 'paiθ(ə) n; (US) 'paiθɔn ] n. 蟒蛇,巨蛇 ),是一种面向对象的解释性的计算机程序设计语言,也是一种功能强大而完善的通用型语言,已经具有十多年的发展历史,成熟且稳定。Python 具有脚本语言中最丰富和强大的类库,足以支持绝大多数日常应用。 Python 语言的特点:
|
5月前
|
人工智能 数据挖掘 大数据
爆赞!GitHub首本标星120K的Python程序设计人工智能案例手册
为什么要学习Python? Python简单易学,且提供了丰富的第三方库,可以用较少的代码完成较多的工作,使开发者能够专注于如何解决问题而只花较少的时间去考虑如何编程。此外,Python还具有免费开源、跨平台、面向对象、胶水语言等优点,在系统编程、图形界面开发、科学计算、Web开发、数据分析、人工智能等方面有广泛应用。尤其是在数据分析和人工智能方面,Python已成为最受开发者欢迎的编程语言之一,不仅大量计算机专业人员选择使用Python进行快速开发,许多非计算机专业人员也纷纷选择Python语言来解决专业问题。 由于Python应用广泛,关于Python的参考书目前已经有很多,但将Pytho
|
6月前
|
Python 容器
GitHub狂揽6700 Star,Python进阶必备的案例、技巧与工程实践
当下是 Python 急剧发展的时代,越来越多的人开始学习和使用Pyhon,而大家也遇到了各种问题。这份手册清晰、细致地介绍了 Python 代码应该遵循的编程风格,并解释了背后的原理和机制。