相信学技术的你一定想把技术用于实际
(小编我就是从小玩游戏,然后就想自己做游戏了)
就酱紫一个小游戏,鼠标点击移动空白方格(别问为啥不用照片,用了图片都找不到北了)
还原成1 2 3 4 5 6 7 8就胜利了,(文章前面介绍如何做这样个游戏,文章后面将介绍解决八数码的算法)
我们可以先考虑游戏基本功能:新游戏,存档,继续游戏(读档),难度选择
(由于小编的游戏算法设计问题(后面提及),没设难度选项)
接下来就开正题了:
1.画出游戏基本窗体
LIKE THIS:
这里用个双重循环就可以解决,不过要搞清楚坐标,JFrame窗体的坐标从左上开始计算,那么就说明标题也占位置(大概20行像素点)。日常上测试代码:
public void paint(Graphics g)//重写paint,画的就不会消失{
super.paint(g);
initUI(g);
}
private Image bf;//用次画布,防止游戏闪屏private Graphics2D bfg;
private void initUI(Graphics g)
{
bf = this.createImage(getWidth(), getHeight());
bfg = (Graphics2D) bf.getGraphics();
bfg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);//开启抗锯齿Graphics g2 = bf.getGraphics();
g2.setColor(Color.white);
bfg.setColor(Color.DARK_GRAY);
//这里的size distance x0 y0都被我定义在一个接口里,这样可以方便理解//我想修改起始坐标(x0 y0)或间距distance或方格大小size只需要修改接口里数值bfg.fillRoundRect(x0, y0, 4 * distance + 3*size, 4 * distance + 3*size, 20, 20);
bfg.setColor(Color.white);
for(int i=0; i<3; i++)
{
for(int j=0; j<3; j++)
{
bfg.fillRect(x0+distance+(distance+size)*i, y0+distance+(distance+size)*j, size, size);
}
}
g.drawImage(bf, 0, 0, this);
}
2.产生数值并画到游戏方框中
生成随机排序有很多方法,小编用的随机交换数值的方法(即一开始定义它为123456780的规矩排序,然后随机交换之中的数值)(您也可以用您自己的方法,我的方法其实不好理解)
产生了之后就完事了???Nein!我们还得判断是否可以胜利
这个游戏就没法胜利是吧?所以还要用个算逆序数的算法来求是否可以胜利
public boolean AbleToWin(int[][] now) {//嘿嘿网上抄的,也不太懂其中奥妙int judge = 0;
for(int i=0; i<9; i++)
{
if(now[i/3][i%3]==0)continue;
for(int j=0; j
{
if(now[j/3][j%3]==0)continue;
if(now[i/3][i%3]
}
}
return judge%2==0;
}
然后咱设个循环,不能胜利就再求一次,直到可以胜利,再开始游戏
就把初始化界面完整代码附上了:
private void initUI(Graphics g)
{
bf = this.createImage(getWidth(), getHeight());
bfg = (Graphics2D) bf.getGraphics();
bfg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Graphics g2 = bf.getGraphics();
g2.setColor(Color.white);
bfg.setColor(Color.DARK_GRAY);
bfg.fillRoundRect(x0, y0, 4 * distance + 3*size, 4 * distance + 3*size, 20, 20);
bfg.setColor(Color.white);
for(int i=0; i<3; i++)
{
for(int j=0; j<3; j++)
{
bfg.setColor(new Color(250-start[j][i]*10,240-start[j][i]*7,190-start[j][i]*8));//每个框颜色不同,才丰富嘛//您也可以用自己的方法设bfg.fillRect(x0+distance+(distance+size)*i, y0+distance+(distance+size)*j, size, size);
}
}
bfg.setColor(Color.black);
bfg.setFont(new Font("微软雅黑", Font.BOLD,size/2));//字体for(int i=0; i
for(int j=0; j
{//这里注意下,数组的x y坐标轴和窗体的正好相反 //反正多调调就出来了if(start[j][i]==0)
{//0就不画了,用空白代替,因为0是我们要移动的框g2.fillRect(x0+distance+(distance+size)*i, y0+distance+(distance+size)*j, size, size);
continue;
}
bfg.drawString(start[j][i]+"", x0+distance+(distance+size)*i+size/2-16, y0+distance+(distance+size)*j+size/2+17);
}
//testArray(start);多测试输出,是个好习惯g.drawImage(bf, 0, 0, this);
}
到时候效果是大概这样:
(玩游戏开了抗锯齿,小编玩游戏从来不敢开,电脑太次了,233)
3.给窗体加监听
(如何加监听就不说了哈)
不过我们要把鼠标点击位置映射到游戏方格,如下(下标从0开始)
得到相应的值之后还要判断值是否有效,比如上图中空白在(2,2)的位置,那么有效值就只有(1,2)和(2,1)(即空白的上下左右才有效)
得到有效值之后就可以移动(交换数值,并让窗体重绘一遍),然后每次移动都判断一次是否到达终点。这样之后,恭喜,您的小游戏就做完啦!您还可以加很多,比如难度选项
测试代码
public void mousePressed(MouseEvent e) {
x = e.getX();
y = e.getY();
x-=x0+distance;
y-=y0+distance;
x/=distance+size;
y/=distance+size;
//这里访问数组时x和y要反过来if(isNotValue(map, y, x));
else move(y, x);
}
private boolean isNotValue(int[][] now,int x, int y)
{//判断是否有效,要求在0的上下左右return x<0||x>=now[0].length||y<0||y>=now.length||!arround0(now, x, y);
}
private boolean arround0(int[][] now, int x, int y)
{//判断是否在空白周围int x0 = get0(now).getX();
int y0 = get0(now).getY();
for(int k=0; k<4; k++)
if((x0+moveX[k])==x && (y0+moveY[k])==y)return true;
return false;
}
void move(int x, int y)
{
steps++;//步数swap(map, get0(map).getX(),get0(map).getY(),x,y);//交换frame.repaint();
if(isWin(map)){
JOptionPane.showMessageDialog(frame, "You Win You've used "+steps+" steps!");
steps=0;
while(!frame.AbleToWin(map=getinitNumber()));
frame.setMap(map);
frame.repaint();
}
}
boolean isWin(int[][] now)
{
for(int i=0; i<9; i++)
if(now[i/3][i%3]!=win[i/3][i%3])return false;
return true;
}
void swap(int[][] target,int x1,int y1,int x2,int y2)
{
int t = target[x1][y1];
target[x1][y1] = target[x2][y2];
target[x2][y2] = t;
}
(怎么样,玩得开心不?有没有想过让他自动还原呢?那岂不是很酷?)
像这样:
这样多么吸引人啊,那怎么弄呢?
EXTRA:八数码问题
八数码问题的解有很多,因为是找最短路,就考虑广度优先搜索()
用康托展开来判重复(如果不是acm就用单广搜+康托展开就够了,速度还可以)
我把我的打包好了的模板:
import java.util.ArrayList;
public class AutoBot_V3 {
static final int fac[] = {1,1,2,6,24,120,720,5040,40320,362880};
static final int moveX[] = {-1, 1, 0, 0};
static final int moveY[] = { 0, 0,-1, 1};//上下左右static final char[] indexs = {'↑','↓','←', '→'};
private String path;
private int steps;
int[] startmap;
public AutoBot_V3(int[] tmpmap) {//把目前游戏的数字排成一维数组再传入startmap = tmpmap;
init();
}
class States{
int[] state;
int loc;
int step;
int hash;
String path;
public States(int[] state, int loc, int step, int hash, String path) {
this.state = state;
this.loc = loc;
this.step = step;
this.hash = hash;
this.path = path;
}
}
int cantor(int[] s)//康拖展开求该序列的hash值{
int sum=0;
for(int i=0;i<9;i++)
{
int num=0;
for(int j=i+1;j<9;j++)
if(s[j]
sum+=(num*fac[9-i-1]);
}
return sum+1;
}
void init()
{
boolean[] vis = new boolean[1000000];;
ArrayList q = new ArrayList();
int[] start = startmap;
int[] end = {1,2,3,4,5,6,7,8,0};
int aim = cantor(end);
vis[cantor(start)] = true;
int pos0=0;
for(int i=0; i<9;i++)
if(start[i]==0)pos0 = i;
States first = new States(start, pos0, 0, cantor(start), "");
q.add(first);
while(!q.isEmpty())
{
States cur = q.get(0);
q.remove(0);
int x = cur.loc/3;
int y = cur.loc%3;
for(int k=0; k<4; k++)
{
int tx = x + moveX[k];
int ty = y + moveY[k];
if(tx<0||tx>2||ty<0||ty>2)continue;
int[] tmp = new int[9];
for(int i=0; i<9; i++)tmp[i] = cur.state[i];
int nextpos = tx*3 + ty;
tmp[cur.loc] = tmp[nextpos];
tmp[nextpos] = 0;
int nexthash = cantor(tmp);
if(vis[nexthash])continue;
vis[nexthash] = true;
if(nexthash == aim)
{
path = cur.path+indexs[k];
steps = cur.step + 1;
return;
}
q.add(new States(tmp, nextpos, cur.step+1, nexthash, cur.path+indexs[k]));
}
}
}
public String getPath()
{
return path;
}
public int getStep()
{
return steps;
}
}
能看懂就看,看不懂会用就好
由于有了前面是否可以胜利的判断,这里不用担心搜不到了(吐槽下,java写算法比c++慢好多,而且没c++那么灵活)
这里可以得到路径和步数,然后怎么解决就取决于您啦!遍历一遍path就出来了
好了小游戏到此也做的差不多了,如果您有什么新的脑洞也可以加上去,有什么想法一定要在下面评论哟!
感谢您看完这么多东西,记得点个赞哟