洗澡的时候突发奇想,2D游戏里像素是比较简单的绘画风格,但是经常除了人物外,还有一堆乱七八糟的方块比如泥土啊,墙壁啊之类的方块,这些方块如果手画,不仅不具备随机性,而且画起来也很繁琐。因此想到,能不能写一个像素方块的生成器,来生成简单的泥土,墙壁之类像素图,从而减少美工的负担呢。于是就有这篇博客了,先写一下整体思路,然后再写具体的实现。


前言:本文前部分是本人的开发流程,基本以时间顺序描述开发过程,后半部分附有效果图和程序源码,前半部分可直接跳过。部分代码受本人能力限制,显得多余累赘,别吐槽谢谢。

程序介绍:本程序使用java编写,是一个像素方块的生成器,可用来生成像素面,同时具有扩展性,可通过编写代码扩展本程序功能。


正文

这是一个简单的软件,首先使用java搭配天下第一swing框架开发,图形界面大概分为绘画区和参数区,绘画区负责生成图像,和提供预览功能,参数区顾名思义即输入各项参数。初步界面设计如下。

此部分的部分代码如下

        public void CreateJFrame(){//声明jf窗口,设置属性JFrame jf=new JFrame();setLayout(null);Container c=jf.getContentPane();JPanel DrawArea=new JPanel(null);DrawArea.setBounds(5,0,800,600);DrawArea.setBorder(BorderFactory.createTitledBorder("绘画区"));JPanel PramArea=new JPanel(null);PramArea.setBounds(810,0,200,600);PramArea.setBorder(BorderFactory.createTitledBorder("参数区"));add(DrawArea);add(PramArea);setBounds(0,0,1040,650);setTitle("Pixel Generator::CSDN Blog:Zhidai_");setVisible(true);setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);}

接下来,分两部分,一是绘画区,一是参数区,两部分分开实现后再合成一体。这里先分析参数区的需求。

参数区需要输入参数,同时将参数传输给绘画区,因此需要一个文本框实现输入功能,一个保存按钮,保存目前输入的参数,一个清空按钮,清空目前填入的参数。输入的参数大概包括,图片大小(width,length),像素大小(pixelsize),基本颜色(R,G,B),图片存放位置,新图片名称,随机数种子。(同时基本颜色提供一个取色器,随机数种子可输入或不输入)

后期还要排版,先用流布局随便排一下大概的顺序。代码就不贴了,简单的复制黏贴而已。

然后还需要获取到输入框输入的数据。写个监听事件,监听鼠标点击按钮的时候,保存下当前时刻的参数。emm也很简单的东西,代码如下

            JB_SavePramButton.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {String temp;temp=JF_width.getText();imageWidth=StringToInt(temp);temp=JF_length.getText();imageLength=StringToInt(temp);temp=JF_pixelsize.getText();pixelSize=StringToInt(temp);temp=JF_color_R.getText();color_R=StringToInt(temp);temp=JF_color_G.getText();color_G=StringToInt(temp);temp=JF_color_B.getText();color_B=StringToInt(temp);temp=JF_RandomNumber.getText();randomNumber=StringToInt(temp);}});

清空参数也是一样的,简单的代码,如下。

            JB_ClearPramButton.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {JF_width.setText("");JF_length.setText("");JF_pixelsize.setText("");JF_color_B.setText("");JF_color_G.setText("");JF_color_R.setText("");JF_RandomNumber.setText("");}});

到此,参数区基本上框架搭好了。


在我试图分析绘画区的需求的时候,发现了一个严重的问题,就是一开始绘画区是固定的,我本来希望可以在右边参数区改完立刻预览到成品,但是如果图片过大的时候可能会有问题,就是图片超出了边界,这个时候就不能很好的预览了。因此我视图通过两个窗口来解决这个问题,打开程序后,首先是参数区,填好参数之后,点击生成图片,会打开另一个窗口,展示的就是绘画区的内容,这样设计可以灵活的调整窗口的大小,但是不利于查看。

调整后的窗口发生了较大的改变,主要缩小了参数区的大小,改善了数据结构,重新写了一个类来控制绘画区的生成,同时绘画区也可以动态生成多个,方便对比。

同时将原本窗口的关闭属性修改了,这样原先关闭一个会把参数区的也关了,现在不会发生这种问题。

setDefaultCloseOperation(DISPOSE_ON_CLOSE);

为绘画区加了个菜单栏,进行保存操作,可预见的参数区的图片名次和地址可能也会改成弹窗保存了。

public void createJFrame(Parameter para){JFrame jf=new JFrame();setLayout(null);Container c=jf.getContentPane();JMenuBar JMB_menuBar=new JMenuBar();JMenu M_operation=new JMenu("操作");JMenuItem MI_save=new JMenuItem("保存图片");M_operation.add(MI_save);JMB_menuBar.add(M_operation);setJMenuBar(JMB_menuBar);setBounds(200,200,para.imageWidth+50,para.imageLength+50);setTitle("Pixel Generator::CSDN Blog:Zhidai_");setVisible(true);setDefaultCloseOperation(DISPOSE_ON_CLOSE);}

接下来是绘画区的绘图功能,在参数里加了一块调色板,本来还以为需要自己写,后来发现awt里有JColorChooser这个东西,而且封装的相当好,一行代码就可以解决,当然如果要更好的调色板可以自己编写,不过目前这个已经可以满足需求了。下面放修改后的参数区和绘画区。

para.color=JColorChooser.showDialog(jf,"调色板",para.color);

这个showDialog返回的是个Color类,因此声明一个Color来获取到调色板选择的颜色,三个参数分别是:父窗口,窗口标题,默认颜色


接下来是写本工具的核心功能,也就是生成像素图。

像素图的生成先确定一种基本颜色,然后在基本颜色上面random出其他相近的颜色,然后组合起来变成像素图。因此需要一个随机算法来生成。

            Random random=new Random(para.randomNumber);int widcnt=para.imageWidth/para.pixelSize;int lengcnt=para.imageLength/para.pixelSize;Color color_t;for(int i=0;i<widcnt;i++){for(int j=0;j<lengcnt;j++){int color_r,color_g,color_b;int offset=para.offset;color_r=Math.max(Math.min(para.color_R+(random.nextInt()%offset-offset/2),255),0);color_g=Math.max(Math.min(para.color_G+(random.nextInt()%offset-offset/2),255),0);color_b=Math.max(Math.min(para.color_B+(random.nextInt()%offset-offset/2),255),0);color_t=new Color(color_r,color_g,color_b);g2.setColor(color_t);g2.fillRect(i*para.pixelSize,j*para.pixelSize,para.pixelSize,para.pixelSize);}}

随机像素生成算法,用一个偏移量,在基本颜色上进行偏移,同时保证偏移后的数值满足RGB的要求,即属于[0,255],之后进行绘画,计算好位置和大小之后就可以画了。一个简单的随机算法。以下是不同偏移量的效果图。


程序到这里基本完成,剩下都是修饰工作。

总结:本博客主要是用java swing编写一个像素方块生成器的过程,通过简单的搭建分为两个窗口,一个是参数,一个是绘画,在参数窗口写好参数后生成绘画窗口,绘画窗口是像素的预览模式,点击保存按钮可以保存图片到指定路径。下面会放本程序效果图和完整代码。

提醒:

像素大小应是图片宽度长度的公约数;

RGB可通过调色板获取;

偏移量为像素与基本颜色的偏移量,偏移量越大,像素颜色越偏离基本颜色;

随机数种子可填可不填;

图片保存地址为地址+图片命名+格式,如F:/a.jpg为保存在F盘根目录下文件名为a,格式是jpg的图片文件。

参数限制:

图片大小:整数,是像素大小的公倍数

RGB与偏移量:整数,[0,255],大于等于0,小于等于255。

随机数种子:整数

效果图如下:

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Random;import javax.imageio.ImageIO;
import javax.swing.*;public class Main {public static class Parameter{int imageWidth;int imageLength;int pixelSize;Color color=new Color(0,0,0);int color_R;int color_G;int color_B;int offset;int randomNumber;String imageFormat;String imageLocal;}public static class createMenu extends JFrame implements MouseListener{public Parameter para=new Parameter();public void CreateJFrame(){//声明jf窗口,设置属性JFrame jf=new JFrame();setLayout(null);Container c=jf.getContentPane();JPanel PramArea=new JPanel(new FlowLayout(FlowLayout.LEFT,0,0));JPanel JP_ImageProperties=new JPanel(new FlowLayout(FlowLayout.CENTER,10,10));JPanel JP_ColorProperties=new JPanel(new FlowLayout(FlowLayout.LEFT,10,10));JPanel JP_SaveProperties=new JPanel(new FlowLayout(FlowLayout.LEFT,10,10));JPanel JP_Others=new JPanel(new FlowLayout(FlowLayout.LEFT,10,10));PramArea.setBounds(0,0,475,250);PramArea.setBorder(BorderFactory.createTitledBorder("参数区"));JLabel JL_width=new JLabel("图片宽度:");JLabel JL_length=new JLabel("图片长度:");JLabel JL_pixelsize=new JLabel("像素大小:");JLabel JL_color_R=new JLabel("R:");JLabel JL_color_G=new JLabel("G:");JLabel JL_color_B=new JLabel("B:");JLabel JL_offset=new JLabel("偏移量:");JLabel JL_ImageLocal=new JLabel("图片保存地址:");JLabel JL_ImageFormat=new JLabel("图片格式:");JLabel JL_RandomNumber=new JLabel("随机数种子:");//JCB_ImageFormat.addItem("你想要添加的格式"); 可按照右边的格式来添加其他图片格式JComboBox JCB_ImageFormat=new JComboBox();JCB_ImageFormat.addItem("png");JCB_ImageFormat.addItem("jpg");JCB_ImageFormat.addItem("bmp");JTextField JF_width=new JTextField();JF_width.setText("800");JF_width.setColumns(5);JTextField JF_length=new JTextField();JF_length.setText("600");JF_length.setColumns(5);JTextField JF_pixelsize=new JTextField();JF_pixelsize.setText("10");JF_pixelsize.setColumns(5);JTextField JF_color_R=new JTextField();JF_color_R.setText("192");JF_color_R.setColumns(3);JTextField JF_color_G=new JTextField();JF_color_G.setText("192");JF_color_G.setColumns(3);JTextField JF_color_B=new JTextField();JF_color_B.setText("192");JF_color_B.setColumns(3);JTextField JF_offset=new JTextField();JF_offset.setText("10");JF_offset.setColumns(3);JTextField JF_ImageLocal=new JTextField();JF_ImageLocal.setText("F:/a.jpg");JF_ImageLocal.setColumns(10);JTextField JF_RandomNumber=new JTextField();JF_RandomNumber.setText("1");JF_RandomNumber.setColumns(3);JButton JB_ClearPramButton=new JButton("清空参数");JB_ClearPramButton.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {JF_width.setText("");JF_length.setText("");JF_pixelsize.setText("");JF_color_B.setText("");JF_color_G.setText("");JF_color_R.setText("");JF_RandomNumber.setText("");}});JButton JB_CreateImageButton=new JButton("生成图片");JB_CreateImageButton.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {String temp;temp=JF_length.getText();para.imageLength=StringToInt(temp);temp=JF_width.getText();para.imageWidth=StringToInt(temp);temp=JF_pixelsize.getText();para.pixelSize=StringToInt(temp);temp=JF_color_R.getText();para.color_R=StringToInt(temp);temp=JF_color_G.getText();para.color_G=StringToInt(temp);temp=JF_color_B.getText();para.color_B=StringToInt(temp);temp=JF_offset.getText();para.offset=StringToInt(temp);para.imageFormat= String.valueOf(JCB_ImageFormat.getSelectedItem());para.imageLocal=JF_ImageLocal.getText();para.color=new Color(para.color_R,para.color_G,para.color_B);temp=JF_RandomNumber.getText();para.randomNumber=StringToInt(temp);new createDrawWindow().createJFrame(para);}});JButton JB_GetColorButton=new JButton("调色板");JB_GetColorButton.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {para.color=JColorChooser.showDialog(jf,"调色板",para.color);JF_color_R.setText(String.valueOf(para.color.getRed()));JF_color_G.setText(String.valueOf(para.color.getGreen()));JF_color_B.setText(String.valueOf(para.color.getBlue()));para.color_R=para.color.getRed();para.color_G=para.color.getGreen();para.color_B=para.color.getBlue();}});JP_ImageProperties.add(JL_width);JP_ImageProperties.add(JF_width);JP_ImageProperties.add(JL_length);JP_ImageProperties.add(JF_length);JP_ImageProperties.add(JL_pixelsize);JP_ImageProperties.add(JF_pixelsize);PramArea.add(JP_ImageProperties);JP_ColorProperties.add(JL_color_R);JP_ColorProperties.add(JF_color_R);JP_ColorProperties.add(JL_color_G);JP_ColorProperties.add(JF_color_G);JP_ColorProperties.add(JL_color_B);JP_ColorProperties.add(JF_color_B);JP_ColorProperties.add(JB_GetColorButton);JP_ColorProperties.add(JL_offset);JP_ColorProperties.add(JF_offset);PramArea.add(JP_ColorProperties);JP_SaveProperties.add(JL_ImageFormat);JP_SaveProperties.add(JCB_ImageFormat);JP_SaveProperties.add(JL_ImageLocal);JP_SaveProperties.add(JF_ImageLocal);PramArea.add(JP_SaveProperties);JP_Others.add(JL_RandomNumber);JP_Others.add(JF_RandomNumber);JP_Others.add(JB_ClearPramButton);JP_Others.add(JB_CreateImageButton);PramArea.add(JP_Others);add(PramArea);setBounds(0,0,500,300);setTitle("Pixel Generator::CSDN Blog:Zhidai_");setVisible(true);setDefaultCloseOperation(DISPOSE_ON_CLOSE);}//String转intpublic int StringToInt(String t){int n=0;for(int i=0;i<t.length();i++){n=n*10+t.charAt(i)-'0';}return n;}@Overridepublic void mouseClicked(MouseEvent e) {}@Overridepublic void mousePressed(MouseEvent e) {}@Overridepublic void mouseReleased(MouseEvent e) {}@Overridepublic void mouseEntered(MouseEvent e) {}@Overridepublic void mouseExited(MouseEvent e) {}}public static class createDrawWindow extends JFrame implements MouseListener {Parameter para;BufferedImage bi;public void createJFrame(Parameter para_t){para=para_t;JFrame jf=new JFrame();setLayout(null);Container c=jf.getContentPane();JButton SaveButton=new JButton("<html>保存图片<br>");SaveButton.setBounds(0,0,90,26);SaveButton.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {try {ImageIO.write(bi,para.imageFormat,new FileOutputStream(para.imageLocal));} catch (IOException e1) {e1.printStackTrace();}}});add(SaveButton);setBounds(200,200,para.imageWidth+50,para.imageLength+100);setTitle("Pixel Generator::CSDN Blog:Zhidai_");setVisible(true);setDefaultCloseOperation(DISPOSE_ON_CLOSE);}@Overridepublic void paint(Graphics g) {super.paint(g);bi=new BufferedImage(para.imageWidth,para.imageLength,BufferedImage.TYPE_INT_RGB);Graphics2D g2=(Graphics2D) bi.getGraphics();//            --------------------------------------------Random random=new Random(para.randomNumber);int widcnt=para.imageWidth/para.pixelSize;int lengcnt=para.imageLength/para.pixelSize;Color color_t;for(int i=0;i<widcnt;i++){for(int j=0;j<lengcnt;j++){int color_r,color_g,color_b;int offset=para.offset;color_r=Math.max(Math.min(para.color_R+(random.nextInt()%offset-offset/2),255),0);color_g=Math.max(Math.min(para.color_G+(random.nextInt()%offset-offset/2),255),0);color_b=Math.max(Math.min(para.color_B+(random.nextInt()%offset-offset/2),255),0);color_t=new Color(color_r,color_g,color_b);g2.setColor(color_t);g2.fillRect(i*para.pixelSize,j*para.pixelSize,para.pixelSize,para.pixelSize);}}//            --------------------------------------------g.drawImage(bi,30,60,this);}@Overridepublic void mouseClicked(MouseEvent e) {}@Overridepublic void mousePressed(MouseEvent e) {}@Overridepublic void mouseReleased(MouseEvent e) {}@Overridepublic void mouseEntered(MouseEvent e) {}@Overridepublic void mouseExited(MouseEvent e) {}}public static void main(String[] args) {new createMenu().CreateJFrame();}
}

从零开始的2D游戏开发 —— 像素方块生成器相关推荐

  1. Unity 从零开始的2D游戏开发 —— 碰撞检测 和 用射线实现地面检测

    本文一部分为半年前所写,时间间隔较大,若有逻辑上的错误敬请见谅. 碰撞检测是2D游戏开发里经常要用到的东西,当我们做个游戏demo的时候,碰撞检测基本上无处不在,利用碰撞检测可以实现一些比较基础功能, ...

  2. Unity 从零开始的2D游戏开发 —— 角色移动脚本

    角色移动脚本是一个游戏最基础的脚本,通常也是改动最多的脚本,因此在后期会变得异常臃肿,不过这不是我们讨论的问题,本文主要说明角色移动脚本的设计和一些区别. 1.通过控制角色的Transform来移动角 ...

  3. Unity 从零开始的2D游戏开发 —— [Animator] 使用动画状态机制作简单动画

    动画是一个2D游戏里面必不可少的一部分,使用动画可以实现很多意想不到的功能,除了常规的人物动画,还可以实现一些周期性的运动.接下来会尝试实现一个非帧动画,一个帧动画. 一.非帧动画:通过改变位置或拉伸 ...

  4. Unity简单2D游戏开发

    Unity简单2D游戏开发 前言: 近日比较无聊,在b站找了一个up主,跟着他的教程来做游戏--开发一个简单的2D游戏 用 Tilemap 绘制场景 新建一个2D项目,在Unity Asset Sto ...

  5. Unity 2D 游戏开发解决方案大全

    Unity 2D 游戏开发解决方案大全 一些官方腔 这篇文章会是一个大纲模式,致力于,为刚入坑的小白,对于一些常见的 Unity 2D 开发问题给出解决方案(啊,尤其是我) 一些方案可能并非最优解,但 ...

  6. unity 2d 游戏开发教程(2d战棋)

    unity 2d 游戏开发教程(2d战棋) 类似的游戏有:火焰纹章,梦幻模拟战 先上效果 源码领取方式:私信发送 2D战棋资料领取 这是 unity3d 战棋游戏开发 专题的内容拓展 这个专题完整的讲 ...

  7. Unity 2D游戏开发教程之摄像头追踪功能

    Unity 2D游戏开发教程之摄像头追踪功能 上一章,我们创建了一个简单的2D游戏.此游戏中的精灵有3个状态:idle.left和right.这看起来确实很酷!但是仅有的3个状态却限制了精灵的能力,以 ...

  8. Unity 2D游戏开发教程之游戏中精灵的跳跃状态

    Unity 2D游戏开发教程之游戏中精灵的跳跃状态 精灵的跳跃状态 为了让游戏中的精灵有更大的活动范围,上一节为游戏场景添加了多个地面,于是精灵可以从高的地面移动到低的地面处,如图2-14所示.但是却 ...

  9. Unity 2D游戏开发教程之为游戏场景添加多个地面

    Unity 2D游戏开发教程之为游戏场景添加多个地面 为游戏场景添加多个地面 显然,只有一个地面的游戏场景太小了,根本不够精灵四处活动的.那么,本节就来介绍一种简单的方法,可以为游戏场景添加多个地面. ...

最新文章

  1. 怎么使一个浮点数删除小数部分C语言,如何得出一个浮点数的小数部分,要把各个位保存到一个数组里边。...
  2. VS2012/13本地发布网站详细步骤(可带数据库)
  3. Python的零基础超详细讲解(第五天)-Python的运算符
  4. 深度丨全球14家顶尖 AI 产业巨头深度学习实力及战略分析
  5. jQuery的.bind()、.live()和.delegate()之间区别
  6. Mockito测试void方法会引发异常
  7. Vue怎样新建并启动项目(图文教程详解)
  8. centos svn 的搭建
  9. 浅析HandlerThread
  10. Linux下Java安装与配置
  11. ibatis学习四---执行流程浅析
  12. lca_trajan
  13. 离散数学学习笔记 第二章 命题逻辑
  14. centos7 部署安装SRS流媒体服务器
  15. python 取余 韩信点兵_韩信点兵问题的神算法
  16. Docker 之 Docker Machine
  17. 2021钳工技能高考成绩查询,这里有2021钳工时间和报名费用以及流程
  18. 运维工程师都在做什么?
  19. Zabbix 5.0.12 异常:Zabbix unreachable poller processes more than 75% busy:
  20. layui laydate 渲染失效问题,lay-key导致动态生成代码二次渲染失败

热门文章

  1. 【chatgpt代码系列】激光定位激光与地图匹配置信度算法
  2. 哭得累了   矛盾心里总是强求   劝自己要放手   闭上眼让你走
  3. 讲清MVC、MVP、MVVM,看这一篇文章就够了
  4. nginx html 缓存及不缓存配置
  5. Nginx的模块与工作原理
  6. python学习第八天
  7. 详解IP分片与TCP分段的区别
  8. 将时分秒的时间转为2020-11-12T20:00:00.000+08:00格式
  9. C语言标准化输入、输出字符
  10. 关于安阳工学院ACM实验室纳新问题解答