从零开始的2D游戏开发 —— 像素方块生成器,


 

        洗澡的时候突发奇想,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() {
                @Override
                public 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() {
                @Override
                public 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() {
                @Override
                public 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() {
                @Override
                public 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() {
                @Override
                public 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转int
        public int StringToInt(String t)
        {
            int n=0;
            for(int i=0;i<t.length();i++)
            {
                n=n*10+t.charAt(i)-'0';
            }
            return n;
        }


        @Override
        public void mouseClicked(MouseEvent e) {

        }

        @Override
        public void mousePressed(MouseEvent e) {

        }

        @Override
        public void mouseReleased(MouseEvent e) {

        }

        @Override
        public void mouseEntered(MouseEvent e) {

        }

        @Override
        public 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() {
                @Override
                public 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);
        }

        @Override
        public 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);
        }

        @Override
        public void mouseClicked(MouseEvent e) {

        }

        @Override
        public void mousePressed(MouseEvent e) {

        }

        @Override
        public void mouseReleased(MouseEvent e) {

        }

        @Override
        public void mouseEntered(MouseEvent e) {

        }

        @Override
        public void mouseExited(MouseEvent e) {

        }
    }

    public static void main(String[] args) {

        new createMenu().CreateJFrame();

    }
}

 

 

 

 

 

相关内容

    暂无相关文章