精华内容
下载资源
问答
  • java游戏开发

    2011-09-13 16:53:44
    java游戏开发的PPT,对java游戏开发感兴趣的朋友可以看看,值得学习。
  • JAVA游戏开发之超级马里奥教程JAVA游戏开发之超级马里奥教程JAVA游戏开发之超级马里奥教程
  • java游戏开发规则

    2009-05-29 23:58:38
    java游戏开发规则java游戏开发规则java游戏开发规则
  • Java游戏开发实践

    2010-01-23 17:30:25
    Java游戏开发实践 Java游戏开发实践
  • Java游戏开发入门源码合集

    热门讨论 2013-01-02 23:19:30
    Java游戏开发入门源码合集,15款游戏,非常不错,推荐给大家
  • java游戏开发实例

    2010-01-22 18:31:54
    本人JAVA游戏开发实训时老师给做的JAVA游戏实例,很经典的啊!
  • Java游戏开发前景

    千次阅读 2017-03-13 15:37:59
    但是现在随着诺基亚的地位一去不复返,Java游戏开发行业的曾经的火热也一去不复返,很多从事游戏开发的业内人士都有这么疑惑,Java游戏开发还能王者归来吗,Java游戏开发的前景在哪里?想学习java游戏开发该怎么学?去...

      Java游戏开发前景如何?Java游戏开发曾经伴随着诺基亚的辉煌,Java游戏开发行业也是如火如荼,如日中天。但是现在随着诺基亚的地位一去不复返,Java游戏开发行业的曾经的火热也一去不复返,很多从事游戏开发的业内人士都有这么疑惑,Java游戏开发还能王者归来吗,Java游戏开发的前景在哪里?想学习java游戏开发该怎么学?去哪学呢?

      1、Java SE 6 Update N (之前被叫做 "Consumer JRE")的发行

      在这个新的版本中,sun号称其将让Java升级更方便,部署更简单(方便的探测客户端的Java版本,并安装程序需要的版本),用户将能够只安装jre中他们需要的部分,对于暂时用不到的部分可以等到用的时候再安装,同时加速Java虚拟机的启动速度(实际上就是在电脑刚开机的时候就启动Java虚拟机,而不是在第一次运行Java程序的时候),增加对swing的硬件加速支持。而新的Java plug-in将大幅增强applet的稳定性,可靠性,将允许applet更改自己可以使用的内存大小,选择使用哪个特定版本的jre执行。

      实际上一旦applet变得稳定,Java面对flash将体现出巨大的性能优势,因此Java SE 6 Update N的发布将是Java游戏开发界未来一段时间内最大的契机。

      2、Sony或是任天堂决定在家用游戏机上支持Java   这个的希望非常渺茫的,但是考虑到微软的XNA做到了跨windows和xbox360平台,也许sony在ps3屡遭败仗的情况下会考虑一下支持Java。实际上现在的家用游戏机就是电脑,很多发烧友已经可以在ps3和wii上运行Java程序了,只是目前Java无法使用这些机器的硬件加速功能。

      3、手机进一步发展

      目前看来移动设备的功能已经越来越强大,手机的性能也许在不久的将来可以达到电脑的水准,手机游戏也将占据游戏市场越来越大的份额,那么由于手机的操作系统种类繁多,跨平台的Java自然也会越来越受青睐。

      去哪学java开发好呢?

      千锋2017年2月就业榜出平均月薪10827元,Java工程师平均月薪12153元;深圳千锋JavaEE首期班毕业第一周就业率61.5%,平均薪资13156元;毕业两周100%就业,平均薪资12729元。就业薪资震惊整个java培训届。千锋JavaEE手把手带领学员进行项目实战,走出千锋就已是身经百战的职场精英;过硬的教师团队,敢讲别人不敢讲的前沿技术,只为让学员在面试与工作时,能力更胜一筹;严格的管理制度,周周考试检验学习进度,天天默写代码,让代码成为生活中的一部分;以天为单位,有效的细化保证学员的学习效果;全方位教学体系,深入的课程规划,课程内容涉及大数据与云计算,有力加分项,让学员更受企业与用人单位青睐。

      千锋在培养学员成为程序员的过程中,最注重编程思想,我们不仅要教会学员写代码,还要教好学员如何运用代码去应战实际工作中的需要,更迭迅速的时代,JavaEE作为一个异类身处其中,从问世起便立于不败之地,JavaEE在行业中规范市场,为整个行业规范大方向,无论行业哪个环节哪个方向快速发展,都将成为Java开发进步的最佳助力!

    展开全文
  • java 游戏开发教程j2me教程java java 高手毕竟之路java 游戏开发教程j2me教程java java 高手毕竟之路java 游戏开发教程j2me教程java java 高手毕竟之路java 游戏开发教程j2me教程java java 高手毕竟之路java 游戏...
  • Java游戏 开发源 代码 大集合 打包
  • Java游戏开发——拼图游戏经典版

    万次阅读 多人点赞 2018-12-09 11:38:16
    拼图游戏是一款经典的益智游戏游戏难度分为 简单、正常、困难 三种难度,分别对应3*3,4*4,5*5布局,游戏开始前图片被随机打乱,空块位于最右下角,玩家通过点击空块周围图片或者按键方式对图片和空块进行相互交换...

    游戏介绍:

    拼图游戏是一款经典的益智游戏,游戏难度分为 简单、正常、困难 三种难度,分别对应3*3,4*4,5*5布局,游戏开始前图片被随机打乱,空块位于最右下角,玩家通过点击空块周围图片或者按键方式对图片和空块进行相互交换,直到所有图片都回到原位即为游戏胜利。

    本次制作的拼图游戏运行界面如下:

     

     

    使用素材文件夹:

    素材和完整源码链接:https://pan.baidu.com/s/1ws1S4rYVK5U95TU5FMA5UQ 提取码:kvtg

     

    游戏设计的思路

    对拼图界面的图像信息可以采用二维数组map进行存储,数组存储的是图片ID,拼图完成的map数组存储的内容应该为从左到右,从上到下,图片ID顺序为1~8,最右下角的数 组元素存储的图片ID为-1(BLANK_STATE)。所有的移动操作可以简化为对map的移动操作,每次移动完成调用repaint()对图片按ID进行绘画即可。使用文本存储历史记录,每次过关对当前步数和历史记录进行比较和更新。考虑到数组map要求打乱后可以通过移动被还原,所以对数组的打乱必须有所讲究,这里我们采用系统对原有图片执行10000次上下左右按键事件来对图片进行打乱,最后再将空块移动到最右下角,这样图片就顺利地做到了随机打乱。

     

    图片的获取及显示:

    先根据关卡level来获取关卡图片,不同的关卡将分成不同数量的小块,假如是简单难度3*3地图,小图片的宽w是大图片的宽的三分之一,小图片的高h是大图片的高的三分之一,第i张图片的左上角起点的x值为图片所在列数*小图片宽(i%3*w),y值为图片所在行数*小图片高(i/3*h):

    
    private void getPics() {
    
    		BufferedImage bufferedImage = null;
    		int width = 0,height = 0;
    	       try {
    	            bufferedImage = ImageIO.read(new File("D:/Game/JigsawGame/pic"+level+".png"));
    	            width = bufferedImage.getWidth();
    	            height = bufferedImage.getHeight();
    	        } catch (IOException e) {
    	            e.printStackTrace();
    	        }
    	    
    	    if(level==EASY){
    	    	
    	    	pics = new Image[9];
    
    	    	int w = width/3;
    	    	int h = height/3;
    	    	
    	    	for(int i=0;i<8;i++){
    	    		int x = i%3*w;
    	    		int y = i/3*h;
    	    		pics[i] = bufferedImage.getSubimage(x, y, w, h);	    		
    	    	}
    	    	
    	    }else if(level==NORMAL){
    
    	    	pics = new Image[16];
    
    	    	int w = width/4;
    	    	int h = height/4;
    	    	
    	    	for(int i=0;i<15;i++){
    	    		int x = i%4*w;
    	    		int y = i/4*h;
    	    		pics[i] = bufferedImage.getSubimage(x, y, w, h);	    		
    	    	}
    	    	
    	    }else{
    	    	
    	    	pics = new Image[25];
    	    	
    	    	int w = width/5;
    	    	int h = height/5;
    	    	
    	    	for(int i=0;i<24;i++){
    	    		int x = i%5*w;
    	    		int y = i/5*h;
    	    		pics[i] = bufferedImage.getSubimage(x, y, w, h);	    		
    	    	}
    	    	
    	    }
    	    
    	}
    
    
    public void paint(Graphics g){
    		
    		g.clearRect(0, 0, this.getWidth(), this.getHeight());
    		
    		for(int i=0;i<map.length;i++)
    			for(int j=0;j<map.length;j++){	
    				
    				if(map[i][j]!=BLANK_STATE){
    					//画图
    					g.drawImage(pics[map[i][j]-1],leftX+j*W,leftY+i*W,W,W,this);
    				}
    				
    			}		
    		}

     

    游戏逻辑

    对玩家执行不同的操作进行分别处理:

    ①当玩家鼠标点击

    当玩家鼠标点击的小图片位于空块上下左右方时,点击的小图片与空块交换位置,否则点击无效。

    ②当玩家按键移动

    当空块位于第一行时,moveDown()无效;

    当空块位于最后一行时,moveUp()无效;

    当空块位于第一列时,moveRight()无效;

    当空块位于最后一列时,moveLeft()无效;

    此处的moveLeft()指的是小图片移动的方向,也就是空块移动的反方向。鼠标点击事件的移动可以复用按键事件的移动方法,只需要将点击的小图片与数组下标进行转换,再判断下调用哪个方法即可。

    public void mousePressed(MouseEvent e) {
    		// TODO Auto-generated method stub
    		
    		int x = e.getX()-leftX;
    		int y = e.getY()-leftY;
    		
    		if(x<0||y<0||x>600||y>600){//超出地图范围
    			return ;
    		}
    		
    		//存储数组行列下标
    		int i = y/W;
    		int j = x/W;
    				
    		if(blank_x == i-1&&blank_y == j){//空块在当前点击块的上侧
    			moveUp();
    		}else if(blank_x == i+1&&blank_y == j){//空块在当前点击块的下侧
    			moveDown();
    		}else if(blank_x == i&&blank_y == j-1){//空块在当前点击块的左侧
    			moveLeft();
    		}else if(blank_x == i&&blank_y == j+1){//空块在当前点击块的右侧
    			moveRight();
    		}else{
    			return ;
    		}
    		
    		repaint();
    
    		isWin();		
    		
    	}
    
    
    
    
    public void keyPressed(KeyEvent e) {
    		// TODO Auto-generated method stub
    		if(e.getKeyCode() == KeyEvent.VK_UP&&blank_x!=level+1){//上
    			moveUp();
    		}else if(e.getKeyCode() == KeyEvent.VK_DOWN&&blank_x!=0){//下
    			moveDown();
    		}else if(e.getKeyCode() == KeyEvent.VK_LEFT&&blank_y!=level+1){//左			
    			moveLeft();
    		}else if(e.getKeyCode() == KeyEvent.VK_RIGHT&&blank_y!=0){//右
    			moveRight();
    		}else{
    			return ;
    		}
    			
    		repaint();
    	    isWin();
    		
    	}

    小图片的移动

    交换小图片与空块的值,并对空块的数组下标重新赋值:

    	//空块与上一格交换	
        private void moveDown(){
    				
    		map[blank_x][blank_y] = map[blank_x-1][blank_y];
    		map[blank_x-1][blank_y] = BLANK_STATE;
    		blank_x = blank_x-1;
    		step++;
    		HelpPanel.nowStep.setText(step+"");
    	}
    	
    	//空块与下一格交换
    	private void moveUp(){
    				
    		map[blank_x][blank_y] = map[blank_x+1][blank_y];
    		map[blank_x+1][blank_y] = BLANK_STATE;
    		blank_x = blank_x+1;		
    		step++;
    		HelpPanel.nowStep.setText(step+"");
    	}
    
    	//空块与左一格交换
    	private void moveRight(){
    		
    		map[blank_x][blank_y] = map[blank_x][blank_y-1];
    		map[blank_x][blank_y-1] = BLANK_STATE;
    		blank_y = blank_y-1;
    		step++;
    		HelpPanel.nowStep.setText(step+"");
    	}
    	
    	//空块与右一格交换
    	private void moveLeft(){
    		
    		map[blank_x][blank_y] = map[blank_x][blank_y+1];
    		map[blank_x][blank_y+1] = BLANK_STATE;
    		blank_y = blank_y+1;		
    		step++;
    		HelpPanel.nowStep.setText(step+"");
    	}

    打乱地图信息

    使用Math.Random()获取随机值,各个方向按键事件的概率是1/4,当符合条件时执行移动方法并且移动次数+1,移动10000次实现随机打乱地图。

    	
    	private void RandomTheMap() {
    
    		int i =0;
    
    		//尝试移动一万次	
    		while(i<10000){
    			int rate = (int) (Math.random()*100);
    			if(rate<25&&blank_x!=level+1){
    				moveUp();
    				i++;
    			}else if(rate<50&&blank_x!=0){
    				moveDown();
    				i++;
    			}else if(rate<75&&blank_y!=level+1){
    				moveLeft();
    				i++;
    			}else if(rate<100&&blank_y!=0){
    				moveRight();
    				i++;
    			}		
    		}
    		
    		
    		//再将空块移动到最右下角
    		
    		while(blank_x<level+1){
    			moveUp();
    		}
    		
    		while(blank_y<level+1){
    			moveLeft();
    		}
    		
    		
    		step=0;
    	}
    

    判断输赢

    将现有map信息与正确的map信息一一对比,如果都相等,过关,比较当前记录和历史记录,如果刷新历史记录进行记录的更新,然后询问是否开始下一关。如果开始下一关,进行level++并对地图及面板信息进行初始化:

    private void isWin() {
    		
    		boolean flag = true;
    		
    		for(int i=0;i<successMap.length;i++){
    			for(int j=0;j<successMap.length;j++){
    				if(map[i][j] != successMap[i][j]){
    					flag = false;
    					break;
    				}
    			}
    		}
    		
    		if(flag){
    			String msg;
    			int type;
    			int choice;
    			int record;
    			String title;
    			switch(level){//通关,是否进入下一关
    			case EASY:
    					this.removeKeyListener(this);
    					this.removeMouseListener(this);
    					record = mapUtil.getRecord(level);//历史记录
    					if(step<record){
    						msg = "恭喜你通过简单关卡并刷新历史记录,完成步数是"+step+"\n历史记录是"+record+",是否要进入下一关?";
    						mapUtil.writeRecord(level, step);//更新历史记录
    					}else{
    						msg = "恭喜你通过简单关卡,完成步数是"+step+"\n是否要进入下一关?";
    					}
    					type = JOptionPane.YES_NO_OPTION;
    					title = "过关";
    					choice = 0;
    					choice = JOptionPane.showConfirmDialog(null, msg,title,type);
    					if(choice==1){
    						System.exit(0);
    					}else if(choice == 0){
    						level++;
    						map = mapUtil.getMap(level);
    						successMap = mapUtil.getMap(level);
    						record = mapUtil.getRecord(level);//获取正常难度的历史记录
    						initializeMap();//初始化地图相关变量信息
    						RandomTheMap();//打乱图案顺序
    						step = 0;
    						HelpPanel.histotyStep.setText(record+"");
    						HelpPanel.panelCenter.setSuccessPic(level);
    						HelpPanel.nowLevel.setText("正常");
    						HelpPanel.nowStep.setText(step+"");
    						getPics();
    						this.addKeyListener(this);
    						this.addMouseListener(this);
    						repaint();
    					}
    				;break;
    			case NORMAL:
    				this.removeKeyListener(this);
    				this.removeMouseListener(this);
    				record = mapUtil.getRecord(level);//历史记录
    				if(step<record){
    					msg = "恭喜你通过正常关卡并刷新历史记录,完成步数是"+step+"\n历史记录是"+record+",是否要进入下一关?";
    					mapUtil.writeRecord(level, step);//更新历史记录
    				}else{
    					msg = "恭喜你通过正常关卡,完成步数是"+step+"\n是否要进入下一关?";
    				}
    					type = JOptionPane.YES_NO_OPTION;
    					title = "过关";
    					choice = 0;
    					choice = JOptionPane.showConfirmDialog(null, msg,title,type);
    					if(choice==1){
    						System.exit(0);
    					}else if(choice == 0){
    						level++;
    						map = mapUtil.getMap(level);
    						successMap = mapUtil.getMap(level);
    						initializeMap();//初始化地图相关变量信息
    						RandomTheMap();//打乱图案顺序
    						getPics();
    						step = 0;
    						record = mapUtil.getRecord(level);//获取困难难度的历史记录。
    						HelpPanel.histotyStep.setText(record+"");
    						HelpPanel.panelCenter.setSuccessPic(level);
    						HelpPanel.nowStep.setText(step+"");
    						HelpPanel.nowLevel.setText("困难");
    						this.addKeyListener(this);
    						this.addMouseListener(this);
    						repaint();
    					}
    				;break;
    			case HARD:
    				this.removeKeyListener(this);
    				this.removeMouseListener(this);
    				record = mapUtil.getRecord(level);//历史记录
    				if(step<record){
    					msg = "恭喜你通过困难关卡并刷新历史记录,完成步数是"+step+"\n历史记录是"+record;
    					JOptionPane.showMessageDialog(this, msg);
    					mapUtil.writeRecord(level, step);//更新历史记录
    				}else{
    					msg = "恭喜通过困难关卡,完成步数是"+step;
    					JOptionPane.showMessageDialog(this, msg);
    				}
    				;break;
    			}
    			
    		}
    		
    		
    	}

    历史记录的读写

    采用文本形式对记录进行保存,初始情况下,读取记录的时候如果没有记录文件进行文件的新建并初始所有历史记录为9999步;记录的保存采用两个逗号对三个关卡的历史记录进行分割,读取后进行array.split(",")分割操作;写入时,先清空原有文本信息,再将记录合成字符串写入文件。

    	//获取关卡历史记录
    	public int getRecord(int level){
    		File file = new File("D://GameRecordAboutSwing");
    		
    		if(!file.exists()){
    			file.mkdirs();
    		}
    		File record = new File("D://GameRecordAboutSwing/recordJigsawGame.txt");
    
    		try{
    		if(!record.exists()){//如果不存在,新建文本
    			record.createNewFile();
    			fos = new FileOutputStream(record);
    			dos = new DataOutputStream(fos);
    			String s = "9999,9999,9999";
    			dos.writeBytes(s);
    			System.out.println(record.isFile());;
    		}
    		//读取记录
    		fis = new FileInputStream(record);
    		dis = new DataInputStream(fis);
    		String str = dis.readLine();
    		array = str.split(",");
    		
    		}catch(Exception e){
    			e.printStackTrace();
    		}finally{
    			 try {
    					if(fis!=null)
    					 fis.close();
    					if(dis!=null)
    					 dis.close();			
    					if(fos!=null)
    			    	 fos.close();
    					if(dos!=null)
    					 dos.close();				
    			     } catch (IOException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    		}
    			
    		return Integer.parseInt(array[level-1]);
    	}
    	
    	//更新关卡历史记录
    	public void writeRecord(int level,int step){
    		File record = new File("D://GameRecordAboutSwing/recordJigsawGame.txt");
    		
    		try {
    			//清空原有记录
    			FileWriter fileWriter =new FileWriter(record);
    	        fileWriter.write("");
    			fileWriter.flush();
    			fileWriter.close();
    	        //重新写入文本
    			fos = new FileOutputStream(record);
    			dos = new DataOutputStream(fos);
    			array[level-1] = step+"";
    			String s = array[0]+","+array[1]+","+array[2];
    			dos.writeBytes(s);
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}finally{
    		     try {
    				if(fos!=null)
    		    	 fos.close();
    				if(dos!=null)
    				 dos.close();				
    		     } catch (IOException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		       
    		}
            
    
    		
    	}

    到这里核心的游戏逻辑及游戏面板的相关内容已经说明完了,涉及辅助面板类的东西会有些错综复杂,这里就不详细介绍了。

    完整源码如下:

    MapUtil类:

    package 人物拼图;
    
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.FileWriter;
    import java.io.IOException;
    
    public class MapUtil {
    
    	int[][] map;
    	FileInputStream fis = null;
    	FileOutputStream fos = null;
    	DataInputStream dis = null;
    	DataOutputStream dos = null;
    	String array[] = null;
    	
    	int level = 1;
    	
    	public static final int EASY = 1,NORMAL = 2,HARD = 3,BLANK_STATE = -1;
    	
    	
    	public int[][] getMap(int level){
    		switch(level){
    		case EASY:
    			map = new int[3][3];
    			for(int i=0;i<3;i++)
    				for(int j=0;j<3;j++){
    					map[i][j] = 3*i+j+1;
    				}
    			map[2][2] = BLANK_STATE;
    		;break;
    		case NORMAL:
    			map=new int[4][4];
    			for(int i=0;i<4;i++)
    				for(int j=0;j<4;j++){
    					map[i][j] = 4*i+j+1;
    				}
    			map[3][3] = BLANK_STATE;
    		break;
    		case HARD:
    			map=new int[5][5];
    			for(int i=0;i<5;i++)
    				for(int j=0;j<5;j++){
    					map[i][j] = 5*i+j+1;
    				}
    			map[4][4] = BLANK_STATE;
    			break;
    		}
    		return map;
    	}
    	
    	//获取关卡历史记录
    	public int getRecord(int level){
    		File file = new File("D://GameRecordAboutSwing");
    		
    		if(!file.exists()){
    			file.mkdirs();
    		}
    		File record = new File("D://GameRecordAboutSwing/recordJigsawGame.txt");
    
    		try{
    		if(!record.exists()){//如果不存在,新建文本
    			record.createNewFile();
    			fos = new FileOutputStream(record);
    			dos = new DataOutputStream(fos);
    			String s = "9999,9999,9999";
    			dos.writeBytes(s);
    			System.out.println(record.isFile());;
    		}
    		//读取记录
    		fis = new FileInputStream(record);
    		dis = new DataInputStream(fis);
    		String str = dis.readLine();
    		array = str.split(",");
    		
    		}catch(Exception e){
    			e.printStackTrace();
    		}finally{
    			 try {
    					if(fis!=null)
    					 fis.close();
    					if(dis!=null)
    					 dis.close();			
    					if(fos!=null)
    			    	 fos.close();
    					if(dos!=null)
    					 dos.close();				
    			     } catch (IOException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    		}
    			
    		return Integer.parseInt(array[level-1]);
    	}
    	
    	//更新关卡历史记录
    	public void writeRecord(int level,int step){
    		File record = new File("D://GameRecordAboutSwing/recordJigsawGame.txt");
    		
    		try {
    			//清空原有记录
    			FileWriter fileWriter =new FileWriter(record);
    	        fileWriter.write("");
    			fileWriter.flush();
    			fileWriter.close();
    	        //重新写入文本
    			fos = new FileOutputStream(record);
    			dos = new DataOutputStream(fos);
    			array[level-1] = step+"";
    			String s = array[0]+","+array[1]+","+array[2];
    			dos.writeBytes(s);
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}finally{
    		     try {
    				if(fos!=null)
    		    	 fos.close();
    				if(dos!=null)
    				 dos.close();				
    		     } catch (IOException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		       
    		}
            
    
    		
    	}
    	
    }
    

    MapUtil类主要用做对地图数组信息的初始化以及历史记录的读写。

    GamePanel类

    package 人物拼图;
    
    import java.awt.Graphics;
    import java.awt.Image;
    import java.awt.Toolkit;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.KeyEvent;
    import java.awt.event.KeyListener;
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseListener;
    import java.awt.image.BufferedImage;
    import java.awt.image.ImageObserver;
    import java.io.File;
    import java.io.IOException;
    
    import javax.imageio.ImageIO;
    import javax.swing.ImageIcon;
    import javax.swing.JOptionPane;
    import javax.swing.JPanel;
    
    public class GamePanel extends JPanel implements ActionListener ,MouseListener,KeyListener{
    
    	Image pics[];
    	int leftX=50,leftY=50;
    	int level,W;
    	int[][] map;
    	int[][] successMap;//最后结果
    	int blank_x,blank_y;//记录空白块的数组下标
    	int step = 0;
    	MapUtil mapUtil = new MapUtil();
    	int record;//存储当前关卡记录
    	public static final int EASY = 1,NORMAL = 2,HARD = 3,BLANK_STATE = -1;
    	
    	
    	public GamePanel(int level){
    		setSize(600, 600);
    		this.level = level;
    		map = mapUtil.getMap(level);
    		successMap = mapUtil.getMap(level);
    		initializeMap();//初始化地图相关变量信息
    		RandomTheMap();//打乱图案顺序
    		this.setVisible(true); 
    		this.addKeyListener(this);
    		this.addMouseListener(this);
    		this.setFocusable(true);
    		getPics();
    		record = mapUtil.getRecord(level);
    		HelpPanel.histotyStep.setText(record+"");
    		HelpPanel.nowStep.setText(step+"");
    		repaint();
    	}
    
    	public void paint(Graphics g){
    		
    		g.clearRect(0, 0, this.getWidth(), this.getHeight());
    		
    		for(int i=0;i<map.length;i++)
    			for(int j=0;j<map.length;j++){	
    				
    				if(map[i][j]!=BLANK_STATE){
    					//画图
    					g.drawImage(pics[map[i][j]-1],leftX+j*W,leftY+i*W,W,W,this);
    				}
    				
    			}		
    		}
    	
    	private void initializeMap(){
    		switch(level){
    		case EASY:
    			blank_x=2;blank_y=2;
    			W = 200;
    			;break;
    		case NORMAL:
    			blank_x=3;blank_y=3;
    			W = 150;
    			;break;
    		case HARD:
    			blank_x=4;blank_y=4;
    			W = 120;
    			;break;
    		}
    	}
    	
    	private void RandomTheMap() {
    
    		int i =0;
    
    		//尝试移动一万次	
    		while(i<10000){
    			int rate = (int) (Math.random()*100);
    			if(rate<25&&blank_x!=level+1){
    				moveUp();
    				i++;
    			}else if(rate<50&&blank_x!=0){
    				moveDown();
    				i++;
    			}else if(rate<75&&blank_y!=level+1){
    				moveLeft();
    				i++;
    			}else if(rate<100&&blank_y!=0){
    				moveRight();
    				i++;
    			}		
    		}
    		
    		
    		//再将空块移动到最右下角
    		
    		while(blank_x<level+1){
    			moveUp();
    		}
    		
    		while(blank_y<level+1){
    			moveLeft();
    		}
    		
    		
    		step=0;
    	}
    
    	//空块与上一格交换
    	private void moveDown(){
    				
    		map[blank_x][blank_y] = map[blank_x-1][blank_y];
    		map[blank_x-1][blank_y] = BLANK_STATE;
    		blank_x = blank_x-1;
    		step++;
    		HelpPanel.nowStep.setText(step+"");
    	}
    	
    	//空块与下一格交换
    	private void moveUp(){
    				
    		map[blank_x][blank_y] = map[blank_x+1][blank_y];
    		map[blank_x+1][blank_y] = BLANK_STATE;
    		blank_x = blank_x+1;		
    		step++;
    		HelpPanel.nowStep.setText(step+"");
    	}
    
    	//空块与左一格交换
    	private void moveRight(){
    		
    		map[blank_x][blank_y] = map[blank_x][blank_y-1];
    		map[blank_x][blank_y-1] = BLANK_STATE;
    		blank_y = blank_y-1;
    		step++;
    		HelpPanel.nowStep.setText(step+"");
    	}
    	
    	//空块与右一格交换
    	private void moveLeft(){
    		
    		map[blank_x][blank_y] = map[blank_x][blank_y+1];
    		map[blank_x][blank_y+1] = BLANK_STATE;
    		blank_y = blank_y+1;		
    		step++;
    		HelpPanel.nowStep.setText(step+"");
    	}
    	
    	
    
    	private void getPics() {
    
    		BufferedImage bufferedImage = null;
    		int width = 0,height = 0;
    	       try {
    	            bufferedImage = ImageIO.read(new File("D:/Game/JigsawGame/pic"+level+".png"));
    	            width = bufferedImage.getWidth();
    	            height = bufferedImage.getHeight();
    	        } catch (IOException e) {
    	            e.printStackTrace();
    	        }
    	    
    	    if(level==EASY){
    	    	
    	    	pics = new Image[9];
    
    	    	int w = width/3;
    	    	int h = height/3;
    	    	
    	    	for(int i=0;i<8;i++){
    	    		int x = i%3*w;
    	    		int y = i/3*h;
    	    		pics[i] = bufferedImage.getSubimage(x, y, w, h);	    		
    	    	}
    	    	
    	    }else if(level==NORMAL){
    
    	    	pics = new Image[16];
    
    	    	int w = width/4;
    	    	int h = height/4;
    	    	
    	    	for(int i=0;i<15;i++){
    	    		int x = i%4*w;
    	    		int y = i/4*h;
    	    		pics[i] = bufferedImage.getSubimage(x, y, w, h);	    		
    	    	}
    	    	
    	    }else{
    	    	
    	    	pics = new Image[25];
    	    	
    	    	int w = width/5;
    	    	int h = height/5;
    	    	
    	    	for(int i=0;i<24;i++){
    	    		int x = i%5*w;
    	    		int y = i/5*h;
    	    		pics[i] = bufferedImage.getSubimage(x, y, w, h);	    		
    	    	}
    	    	
    	    }
    	    
    	}
    
    
    	@Override
    	public void keyTyped(KeyEvent e) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void keyPressed(KeyEvent e) {
    		// TODO Auto-generated method stub
    		if(e.getKeyCode() == KeyEvent.VK_UP&&blank_x!=level+1){//上
    			moveUp();
    		}else if(e.getKeyCode() == KeyEvent.VK_DOWN&&blank_x!=0){//下
    			moveDown();
    		}else if(e.getKeyCode() == KeyEvent.VK_LEFT&&blank_y!=level+1){//左			
    			moveLeft();
    		}else if(e.getKeyCode() == KeyEvent.VK_RIGHT&&blank_y!=0){//右
    			moveRight();
    		}else{
    			return ;
    		}
    			
    		repaint();
    		isWin();
    		
    	}
    
    	@Override
    	public void keyReleased(KeyEvent e) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void mouseClicked(MouseEvent e) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void mousePressed(MouseEvent e) {
    		// TODO Auto-generated method stub
    		
    		int x = e.getX()-leftX;
    		int y = e.getY()-leftY;
    		
    		if(x<0||y<0||x>600||y>600){//超出地图范围
    			return ;
    		}
    		
    		//存储数组行列下标
    		int i = y/W;
    		int j = x/W;
    				
    		if(blank_x == i-1&&blank_y == j){//空块在当前点击块的上侧
    			moveUp();
    		}else if(blank_x == i+1&&blank_y == j){//空块在当前点击块的下侧
    			moveDown();
    		}else if(blank_x == i&&blank_y == j-1){//空块在当前点击块的左侧
    			moveLeft();
    		}else if(blank_x == i&&blank_y == j+1){//空块在当前点击块的右侧
    			moveRight();
    		}else{
    			return ;
    		}
    		
    		repaint();
    
    		isWin();		
    		
    	}
    
    	private void isWin() {
    		
    		boolean flag = true;
    		
    		for(int i=0;i<successMap.length;i++){
    			for(int j=0;j<successMap.length;j++){
    				if(map[i][j] != successMap[i][j]){
    					flag = false;
    					break;
    				}
    			}
    		}
    		
    		if(flag){
    			String msg;
    			int type;
    			int choice;
    			int record;
    			String title;
    			switch(level){//通关,是否进入下一关
    			case EASY:
    					this.removeKeyListener(this);
    					this.removeMouseListener(this);
    					record = mapUtil.getRecord(level);//历史记录
    					if(step<record){
    						msg = "恭喜你通过简单关卡并刷新历史记录,完成步数是"+step+"\n历史记录是"+record+",是否要进入下一关?";
    						mapUtil.writeRecord(level, step);//更新历史记录
    					}else{
    						msg = "恭喜你通过简单关卡,完成步数是"+step+"\n是否要进入下一关?";
    					}
    					type = JOptionPane.YES_NO_OPTION;
    					title = "过关";
    					choice = 0;
    					choice = JOptionPane.showConfirmDialog(null, msg,title,type);
    					if(choice==1){
    						System.exit(0);
    					}else if(choice == 0){
    						level++;
    						map = mapUtil.getMap(level);
    						successMap = mapUtil.getMap(level);
    						record = mapUtil.getRecord(level);//获取正常难度的历史记录
    						initializeMap();//初始化地图相关变量信息
    						RandomTheMap();//打乱图案顺序
    						step = 0;
    						HelpPanel.histotyStep.setText(record+"");
    						HelpPanel.panelCenter.setSuccessPic(level);
    						HelpPanel.nowLevel.setText("正常");
    						HelpPanel.nowStep.setText(step+"");
    						getPics();
    						this.addKeyListener(this);
    						this.addMouseListener(this);
    						repaint();
    					}
    				;break;
    			case NORMAL:
    				this.removeKeyListener(this);
    				this.removeMouseListener(this);
    				record = mapUtil.getRecord(level);//历史记录
    				if(step<record){
    					msg = "恭喜你通过正常关卡并刷新历史记录,完成步数是"+step+"\n历史记录是"+record+",是否要进入下一关?";
    					mapUtil.writeRecord(level, step);//更新历史记录
    				}else{
    					msg = "恭喜你通过正常关卡,完成步数是"+step+"\n是否要进入下一关?";
    				}
    					type = JOptionPane.YES_NO_OPTION;
    					title = "过关";
    					choice = 0;
    					choice = JOptionPane.showConfirmDialog(null, msg,title,type);
    					if(choice==1){
    						System.exit(0);
    					}else if(choice == 0){
    						level++;
    						map = mapUtil.getMap(level);
    						successMap = mapUtil.getMap(level);
    						initializeMap();//初始化地图相关变量信息
    						RandomTheMap();//打乱图案顺序
    						getPics();
    						step = 0;
    						record = mapUtil.getRecord(level);//获取困难难度的历史记录。
    						HelpPanel.histotyStep.setText(record+"");
    						HelpPanel.panelCenter.setSuccessPic(level);
    						HelpPanel.nowStep.setText(step+"");
    						HelpPanel.nowLevel.setText("困难");
    						this.addKeyListener(this);
    						this.addMouseListener(this);
    						repaint();
    					}
    				;break;
    			case HARD:
    				this.removeKeyListener(this);
    				this.removeMouseListener(this);
    				record = mapUtil.getRecord(level);//历史记录
    				if(step<record){
    					msg = "恭喜你通过困难关卡并刷新历史记录,完成步数是"+step+"\n历史记录是"+record;
    					JOptionPane.showMessageDialog(this, msg);
    					mapUtil.writeRecord(level, step);//更新历史记录
    				}else{
    					msg = "恭喜通过困难关卡,完成步数是"+step;
    					JOptionPane.showMessageDialog(this, msg);
    				}
    				;break;
    			}
    			
    		}
    		
    		
    	}
    
    	@Override
    	public void mouseReleased(MouseEvent e) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void mouseEntered(MouseEvent e) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void mouseExited(MouseEvent e) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void actionPerformed(ActionEvent e) {
    		// TODO Auto-generated method stub
    		
    	}
    
    }
    

    GamePanel类主要涉及游戏面板的小图片的读取和显示,以及全部游戏的逻辑。

    SuccessPanel类:

    package 人物拼图;
    
    import java.awt.Color;
    import java.awt.Graphics;
    import java.awt.Image;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;
    
    import javax.imageio.ImageIO;
    import javax.swing.JPanel;
    
    public class SuccessPanel extends JPanel{
    
    	int level;
    	Image[] pics;
    	int[][] map;
    	int W ;	
    	MapUtil mapUtil;
    	private int leftX=10;
    	private int leftY=150;
    	
    	public static final int EASY=1,NORMAL=2,HARD=3,BLANK_STATE=-1;
    	
    	public SuccessPanel(int level){
    		setSuccessPic(level);
    	}
    
    	public void setSuccessPic(int level) {
    		
    		mapUtil = new MapUtil();
    		this.level = level;
    		
    		BufferedImage bufferedImage = null;
    		int width = 0,height = 0;
    	       try {
    	            bufferedImage = ImageIO.read(new File("D:/Game/JigsawGame/pic"+level+".png"));
    	            width = bufferedImage.getWidth();
    	            height = bufferedImage.getHeight();
    	        } catch (IOException e) {
    	            e.printStackTrace();
    	        }
    	       
    		if(level == EASY){
    			pics = new Image[9];
    			map = mapUtil.getMap(level);
    			W = 40;
    			
    	    	int w = width/3;
    	    	int h = height/3;
    	    	
    	    	for(int i=0;i<8;i++){
    	    		int x = i%3*w;
    	    		int y = i/3*h;
    	    		pics[i] = bufferedImage.getSubimage(x, y, w, h);	    		
    	    	}
    		}else if(level == NORMAL){
    			pics = new Image[16];
    			map = mapUtil.getMap(level);
    			W = 30;
    			
    	    	int w = width/4;
    	    	int h = height/4;
    	    	
    	    	for(int i=0;i<15;i++){
    	    		int x = i%4*w;
    	    		int y = i/4*h;
    	    		pics[i] = bufferedImage.getSubimage(x, y, w, h);	    		
    	    	}
    		}else{
    			pics = new Image[25];
    			map = mapUtil.getMap(level);
    			W = 24;
    			
    			int w = width/5;
    	    	int h = height/5;
    	    	
    	    	for(int i=0;i<24;i++){
    	    		int x = i%5*w;
    	    		int y = i/5*h;
    	    		pics[i] = bufferedImage.getSubimage(x, y, w, h);	    		
    	    	}
    		}
    		
    		repaint();
    	}
    	
    	
    	public void paint(Graphics g){
    		
    		for(int i=0;i<map.length;i++)
    			for(int j=0;j<map.length;j++){	
    				
    				if(map[i][j]!=BLANK_STATE){
    					//画图
    					g.drawImage(pics[map[i][j]-1],leftX+j*W,leftY+i*W,W,W,this);
    				}
    				
    			}
    	}
    	
    }
    

    SuccessPanel类负责游戏运行界面右下角完整拼图顺序的小图片显示;

    HelpPanel类:

    package 人物拼图;
    
    import java.awt.BorderLayout;
    import java.awt.GridLayout;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseListener;
    
    import javax.swing.JButton;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    
    public class HelpPanel extends JPanel {
    
    	private int level = 1;
    	public static final int EASY = 1,NORMAL = 2,HARD = 3,BLANK_STATE = -1;
    	static JLabel nowStep = new JLabel("0");
    	static JLabel histotyStep = new JLabel("9999");
    	static JLabel nowLevel = new JLabel();
    	JLabel levelLabel = new JLabel("当前难度:");
    	JLabel nowStepLabel = new JLabel("当前步数:");
    	JLabel historyStepLabel = new JLabel("历史记录:");
    	JButton button1 = new JButton("退出");
    	BorderLayout panel1 = new BorderLayout();//右侧
    	JPanel panelNorth = new JPanel(new GridLayout(4,2,10,30));
    	static SuccessPanel panelCenter = new SuccessPanel(1);
    	
    	public HelpPanel(int level){
    		setLevel(level);
    		this.setVisible(true);
    		this.setLayout(new BorderLayout());//当前面板
    
    		panelNorth.add(levelLabel);
    		panelNorth.add(nowLevel);
    		panelNorth.add(nowStepLabel);
    		panelNorth.add(nowStep);
    		panelNorth.add(historyStepLabel);
    		panelNorth.add(histotyStep);//历史记录
    		panelNorth.add(button1);
    
    		this.add(panelNorth,BorderLayout.NORTH);
    		this.add(panelCenter,BorderLayout.CENTER);
    		
    		button1.addMouseListener(new MouseAdapter(){
    			public void mouseClicked(MouseEvent e){
    				System.exit(1);
    			}
    		});;
    	}
    	
    	
    	public void setLevel(int level){
    		this.level = level;
    		panelCenter.setSuccessPic(level);
    		if(level == EASY){
    		 	nowLevel.setText("简单");
    		}else if(level == NORMAL){
    		 	nowLevel.setText("正常");
    		}else{
    		 	nowLevel.setText("困难");
    		}
    		
    	}
    	
    	
    	
    
    	
    
    }
    

    HelpPanel类负责游戏运行界面右边辅助面板的显示,其中装载了SuccessPanel;

    GameClient类:

    package 人物拼图;
    
    import java.awt.BorderLayout;
    import java.awt.FlowLayout;
    import java.awt.GridLayout;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JTextField;
    
    public class GameClient extends JFrame{
    
    	GamePanel panel2;
    
    	
    	public GameClient(){
    
    		HelpPanel panel = new HelpPanel(1);
    		panel2 = new GamePanel(1);//难度简单
    		panel2.setLayout(new BorderLayout());
    		
    		this.getContentPane().setLayout(new BorderLayout());
    		this.getContentPane().add(panel,BorderLayout.EAST);
    		this.getContentPane().add(panel2,BorderLayout.CENTER);
    		this.setSize(850,750);
    		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		this.setTitle("人物拼图游戏");
    		this.setVisible(true);
    	
    	}
    	
    	
    	public static void main(String[] args) {
    		new GameClient();
    	}
    
    
    }
    

    GameClient类是游戏窗口类,装载了游戏面板和辅助面板,用来开始游戏。

    源码已经粘贴完毕,最后再展示两张游戏运行截图:

     

    展开全文
  • Java游戏开发——开心农场

    万次阅读 多人点赞 2018-12-13 21:34:07
    本次开发了一个“开心农场”游戏,运行程序,效果如下图所示。鼠标先选定指定土地(默认选择第一块土地),点击“播种”按钮,可以播种种子;点击“生长”按钮,可以让作物处于生长阶段;点击“开花”按钮,可以让...

    游戏介绍:

    “开心农场”是一款以种植为主的社交游戏。用户可以扮演一个农场的农场主,在自己的农场里开垦土地,种植各种水果蔬菜。本次开发了一个“开心农场”游戏,运行程序,效果如下图所示。鼠标先选定指定土地(默认选择第一块土地),点击“播种”按钮,可以播种种子;点击“生长”按钮,可以让作物处于生长阶段;点击“开花”按钮,可以让作物处于开花阶段;点击“结果”按钮,可以让作物结果;点击“收获”按钮,可以收获果实到仓库。默认生长期为1分钟,开花期为2分钟,落果期为3分钟,支持作物离线生长。

    使用素材文件夹:

    素材以及完整源码链接:https://pan.baidu.com/s/1p1rXX1VlB21RnLfHEAStCg 提取码: gszw

    游戏设计的思路:

    使用一个带背景的面板作为游戏面板,图片里有九块可种植的土地,每块土地对应的Farm对象存储当前土地状态和最后一次操作时间,先判断鼠标点击的哪块土地,再对选定的土地进行后续操作。面板使用“播种”、“生长”、“开花”、“结果”、“收获”五个按钮和9个用于表示九块土地上的作物的Crop对象。5个按钮的点击事件,会改变土地的状态和最后操作时间以及Crop对象的图片,通过改变Crop对象的图片可以达到农作物各种状态的转变。使用文本存储当前游戏数据,使用线程每秒自动刷新游戏页面并更新文件中的游戏数据,在游戏初始化时先读取历史数据,再做状态的换算,用以达到作物离线生长的作用。

    设计步骤:

    1.1设计农作物Crop类:

    农作物Crop类实现作物状态的显示,通过继承JLabel组件和设置JLabel组件的Icon实现。

    package farmGame;
    
    import javax.swing.Icon;
    import javax.swing.ImageIcon;
    import javax.swing.JLabel;
    
    public class Crop extends JLabel{
    
    	
    	Icon icon = null;
    	public Crop(){
    		super();
    	}
    
    	public void setIcon(String picture){
    		icon = new ImageIcon(picture);//获取图片
    		setIcon(icon);
    	}
    	
    }
    

    1.2设计游戏面板BackgroundPanel类:

    BackgroundPanel类主要用来显示游戏背景图。

    package farmGame;
    
    import java.awt.Graphics;
    import java.awt.Image;
    
    import javax.swing.JPanel;
    
    public class BackgroundPanel extends JPanel{
    
    	private Image image;
    	
    	public BackgroundPanel(){
    		super();
    		setOpaque(false);//当设置为false时,组件并未不会显示其中的某些像素,允许控件下面的像素显现出来。
    		setLayout(null);
    	}
    	
    	public void setImage(Image image){
    		this.image = image;
    	}
    	
    	protected void paintComponent(Graphics g){//重写绘制组件外观方法
    		if(image!=null){
    			int width = getWidth();
    			int height = getHeight();
    			g.drawImage(image, 0, 0, width, height, this);
    		}
    		super.paintComponent(g);
    	}
    		
    }
    

    1.3农场土地Farm类:

    Farm类对象用来存储一块土地的状态和最后一次操作时间(例如播种),包含该块土地相关的播种、生长、开花、结果、收获操作;state==0时未播种,state==1时已播种,state==2时生长中,state==3时开花中,state==4已结果;每次操作如果符合条件修改state值和最后一次操作时间,否则设置提示信息为不能播种。

    package farmGame;
    
    import java.io.Serializable;
    import java.util.Date;
    
    public class Farm implements Serializable {
    	private static final long serialVersionUID = 1L;
    	public int state = 0;
    	public Date lastTime = null;
    	
    	public int getState() {
    		return state;
    	}
    
    	public void setState(int state) {
    		this.state = state;
    	}
    
    	public Date getLastTime() {
    		return lastTime;
    	}
    
    	public void setLastTime(Date lastTime) {
    		this.lastTime = lastTime;
    	}
    
    	public String seed(Crop crop,String picture){
    		String returnValue = "";
    		if(state == 0){
    			crop.setIcon(picture);
    			state = 1;
    			lastTime = new Date();
    		}else{
    			returnValue = getMessage()+",不能播种";
    		}
    		
    		return returnValue;
    	}
    	
    	public String grow(Crop crop,String picture){
    		String returnValue = "";
    		if(state == 1){
    			crop.setIcon(picture);
    			state = 2;
    			lastTime = new Date();
    		}else{
    			returnValue = getMessage()+",不能生长";
    		}
    		
    		return returnValue;
    	}
    	
    	public String bloom(Crop crop,String picture){
    		String returnValue = "";
    		if(state == 2){
    			crop.setIcon(picture);
    			state = 3;
    			lastTime = new Date();
    		}else{
    			returnValue = getMessage()+",不能开花";
    		}
    		
    		return returnValue;	
    	}
    	
    
    	public String fruit(Crop crop,String picture){
    		String returnValue = "";
    		if(state == 3){
    			crop.setIcon(picture);
    			state = 4;
    			lastTime = new Date();
    		}else{
    			returnValue = getMessage()+",不能结果";
    		}
    		
    		return returnValue;
    	}
    	
    	public String harvest(Crop crop,String picture){
    		String returnValue="";
    		if(state==4){
    			crop.setIcon(picture);
    			state = 0;
    			lastTime = null;
    		}else{
    			returnValue = getMessage()+",不能收获!";
    		}
    		
    		return returnValue;
    	}
    	
    	
    	public String getMessage() {
    	
    		String message = "";
    		switch(state){
    		case 0:
    			message = "作物还没有播种";
    			break;
    		case 1:
    			message = "作物刚刚播种";
    			break;
    		case 2:
    			message = "作物正在生长";
    			break;
    		case 3:
    			message = "作物正处于开花期";
    			break;
    		case 4:
    			message = "作物已经结果";
    			break;
    		}
    		
    		return message;
    	}
    	
    }
    

    1.4设计游戏窗口MainFrame类:

    编写一个继承自JFrame类的MainFrame窗体类,用于完成播种、生长、开花、结果、收获等操作。

    GROWTIME常量表示1分钟的生长期毫秒数,BLOOMTIME和FRUITTIME表示2分钟的开花期毫秒数和3分钟的落果期毫秒数,fruitNumber记录仓库果实数量,farms对应9块土地,crops对应9块土地上的作物图片,isInDiamond()方法用于判断鼠标点击的是哪块土地,saveNowState()方法用于存储当前游戏数据,readNowState()方法用于读取历史游戏数据并转换成当前时间对应的游戏数据,线程每秒执行一次存储游戏和读取游戏的操作。

    详细说明:

    判断鼠标选定的是哪块土地

    在这里土地是菱形土地,所以需要判断点在哪块菱形土地上,经测量,菱形土地宽width160px,高height65px;先让鼠标点击点依次尝试位移九块土地的中心点(点击处坐标-每个菱形中心坐标=点击处相对于每个菱形中心的坐标x,y),

    如果|x*width|+|y*height|<=width*height*0.5,说明点击处位于该块菱形土地内,否则位于该块土地外。

    详细证明过程参考:https://blog.csdn.net/hany3000/article/details/11265499

    如果每块土地都判断过一次相对点是否在菱形上,并且点击处都不在其上,说明没有选中可种植土地。

    ②种植信息的保存

    这里使用文本IO的形式进行保存,存储格式是

    第一块土地上的种植状态,第一块土地最后一次操作时间;第二块土地种植状态,第二块土地最后一次操作时间;......第九块土地种植状态,第九块土地最后一次操作时间&当前仓库果实数

    ③种植信息的读取

    先读取文本字符串str,使用str.split("&")将文本分割成两段,第一段array1[0]表示9块土地相关信息,第二段array1[1]表示当前仓库果实数量;对array1[0]进行细分,array1[0].split(";")得到每块土地的种植信息和最后一次操作时间拼接得到的字符串array2数组;再对array2[i]进行细分,array2[i].split(",")得到help数组,help[0]表示土地种植状态,help[1]表示土地最后一次操作时间。

    ④作物状态的转换

          每块土地的最后一次操作时间是使用Date对象存储的,上面获取到了每块土地的种植状态和最后一次操作时间,那么可以先使用new Date()获取当前时间,根据当前时间的长整型的毫秒数和最后一次操作时间长整型的毫秒数相减,得到中间相差了subTime毫秒。

    创建temp变量暂存变化时间;

    如果种植状态是已播种,判断subTime是否大于生长期GROWTIME,如果大于说明现在可以进入到下一阶段,subTime-=GROWTIME,emp+=GROWTIME,并且种植状态改为已生长。

    接着如果当前种植状态是已生长,判断subTime是否大于开花期BLOOMTIME,如果大于说明现在可以进入到下一阶段,subTime-=BLOOMTIME,temp+=BLOOMTIME,并且种植时间改为已开花。

    再如果当前种植状态是已开花,判断subTime是否大于落果期FRUITTIME,如果大于说明现在可以进入到下一阶段,subTime-=FRUITTIME,temp+=FRUITTIME,并且种植时间改为已结果。

          最后判断当前种植状态是什么,将最后一次操作时间+=temp得到新的最后一次操作时间,然后将当前土地对应的作物的图片进行修改,便得到了最新的农场状态。

    ⑤自动更新游戏数据

    创建线程,线程开始时先休眠1000毫秒,再保存游戏数据,读取游戏数据(读取历史游戏数据和更新游戏状态是在同一个方法里做到的);休眠1000毫秒......依次循环

    这样可以确保每秒都能及时保存游戏数据,并且保证当前农场状态的正确性。

    MainFrame类完整代码:

    package farmGame;
    
    import java.awt.EventQueue;
    import java.awt.Image;
    import java.awt.Toolkit;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseListener;
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    import javax.swing.ImageIcon;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JOptionPane;
    import javax.swing.SwingConstants;
    
    public class MainFrame extends JFrame implements MouseListener,Runnable{
    
    	int fruitNumber = 0;
    	Farm[] farms = new Farm[9];
    	Crop[] crops = new Crop[9];
    	Farm farm = new Farm();
    	Crop crop = new Crop();
    	JLabel storage;	
    	SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    	FileInputStream fis = null;
    	FileOutputStream fos = null;
    	DataInputStream dis = null;
    	DataOutputStream dos = null;
    
    	//生长期1小时,开花期2小时,结果期3小时
    	public static final long GROWTIME = 1000*60,BLOOMTIME = 1000*60*2,FRUITTIME = 1000*60*3;
    
    	public MainFrame(){
    		super();
    		setTitle("打造自己的开心农场");
    		setBounds(500,200,900,600);//设置窗口位置和大小
    		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		addMouseListener(this);
    		final BackgroundPanel backgroundPanel = new BackgroundPanel();
    		Image bk = Toolkit.getDefaultToolkit().getImage("D:/Game/FarmGame/farmBackground.png");
    		backgroundPanel.setImage(bk);
    		backgroundPanel.setBounds(0,0,855,553);//设置游戏面板位置和大小
    		getContentPane().add(backgroundPanel);
    		storage = new JLabel();
    		storage.setHorizontalAlignment(SwingConstants.CENTER);
    		storage.setText("您的仓库没有任何果实,快快播种吧!");
    		storage.setBounds(200,70,253,28);//标签位置
    		backgroundPanel.add(storage);
    
    		initlize();
    		for(Crop temp:crops){
    			backgroundPanel.add(temp);
    		}		
    
    		final JButton button_1 = new JButton();//播种
    		button_1.setRolloverIcon(new ImageIcon("D:/Game/FarmGame/播种1.png"));//移动到图标上显示的图片
    		button_1.setBorderPainted(false);
    		button_1.setContentAreaFilled(false);
    		button_1.setIcon(new ImageIcon("D:/Game/FarmGame/播种.png"));//正常显示图片
    		button_1.addActionListener(new ActionListener(){
    			@Override
    			public void actionPerformed(ActionEvent e) {
    				if(farm == null)//如果没有先选中土地
    					return ;
    
    				String message = farm.seed(crop, "D:/Game/FarmGame/seed.png");
    				if(!message.equals("")){
    					JOptionPane.showMessageDialog(null, message);
    				}
    			}			
    		});		
    		button_1.setBounds(140,477,56,56);//29, 185, 56, 56
    		backgroundPanel.add(button_1);
    
    
    		final JButton button_2 = new JButton();//生长
    		button_2.setContentAreaFilled(false);
    		button_2.setBorderPainted(false);
    		button_2.setRolloverIcon(new ImageIcon("D:/Game/FarmGame/生长1.png"));
    		button_2.setIcon(new ImageIcon("D:/Game/FarmGame/生长.png"));
    		button_2.addActionListener(new ActionListener() {
    
    			@Override
    			public void actionPerformed(ActionEvent e) {
    				if(farm == null)//如果没有先选中土地
    					return ;
    
    				String message = farm.grow(crop, "D:/Game/FarmGame/grow.png");
    				if(!message.equals("")){
    					JOptionPane.showMessageDialog(null, message);
    				}
    			}
    		});
    		backgroundPanel.add(button_2);
    		button_2.setBounds(280,477,56,56);//114,185,56,56
    
    
    		final JButton button_3 = new JButton();
    		button_3.setContentAreaFilled(false);
    		button_3.setBorderPainted(false);
    		button_3.setRolloverIcon(new ImageIcon("D:/Game/FarmGame/开花1.png"));
    		button_3.setIcon(new ImageIcon("D:/Game/FarmGame/开花.png"));
    		button_3.addActionListener(new ActionListener() {
    
    			@Override
    			public void actionPerformed(ActionEvent e) {
    
    				if(farm == null)//如果没有先选中土地
    					return ;
    
    				String message = farm.bloom(crop, "D:/Game/FarmGame/bloom.png");
    				if(!message.equals("")){
    					JOptionPane.showMessageDialog(null, message);
    				}
    			}
    		});
    		backgroundPanel.add(button_3);
    		button_3.setBounds(420,477,56,56);//199,185,56,56;
    
    
    		final JButton button_4 = new JButton();
    		button_4.setContentAreaFilled(false);
    		button_4.setBorderPainted(false);
    		button_4.setRolloverIcon(new ImageIcon("D:/Game/FarmGame/结果1.png"));
    		button_4.setIcon(new ImageIcon("D:/Game/FarmGame/结果.png"));
    		button_4.addActionListener(new ActionListener() {
    
    			@Override
    			public void actionPerformed(ActionEvent e) {
    				if(farm == null)//如果没有先选中土地
    					return ;
    
    				String message = farm.fruit(crop,"D:/Game/FarmGame/fruit.png");
    				if(!message.equals("")){
    					JOptionPane.showMessageDialog(null, message);
    				}
    			}
    		});
    		backgroundPanel.add(button_4);
    		button_4.setBounds(560,477,56,56);
    
    
    		final JButton button_5 = new JButton();
    		button_5.setContentAreaFilled(false);
    		button_5.setBorderPainted(false);
    		button_5.setRolloverIcon(new ImageIcon("D:/Game/FarmGame/收获1.png"));
    		button_5.setIcon(new ImageIcon("D:/Game/FarmGame/收获.png"));
    		button_5.addActionListener(new ActionListener() {
    
    			@Override
    			public void actionPerformed(ActionEvent e) {
    				if(farm == null)//如果没有先选中土地
    					return ;
    
    				String message = farm.harvest(crop, "");
    				if(!message.equals("")){
    					JOptionPane.showMessageDialog(null, message);
    				}else{
    					fruitNumber++;
    					storage.setText("您的仓库现在有"+fruitNumber+"个果实.");
    				}
    			}
    		});
    		backgroundPanel.add(button_5);
    		button_5.setBounds(700,477,56,56);//369,185,56,56
    
    		new Thread(this).start();
    
    	}
    
    
    	public void initlize(){
    
    		for(int i=0;i<9;i++){
    			farms[i] = new Farm();
    			crops[i] = new Crop();
    		}
    
    		crops[0].setBounds(269, 153, 106, 96);
    		crops[1].setBounds(165, 195, 106, 96);
    		crops[2].setBounds(59, 238, 106, 96);
    		crops[3].setBounds(378, 199, 106, 96);
    		crops[4].setBounds(278, 236, 106, 96);
    		crops[5].setBounds(169, 284, 106, 96);
    		crops[6].setBounds(501, 247, 106, 96);
    		crops[7].setBounds(393, 281, 106, 96);
    		crops[8].setBounds(286, 333, 106, 96);
    
    
    		farm = farms[0];//默认选中第一块土地
    		crop = crops[0];//默认第一株植物
    		readNowState();//更新现在状态
    	}
    
    	@Override
    	public void mouseClicked(MouseEvent e) {
    		// TODO Auto-generated method stub
    		int x = e.getX();
    		int y = e.getY();
    
    		System.out.println("x is "+x+", y is "+y);
    
    		if(isInDiamond(x-324,y-258,160,65)){//第一块土地
    			System.out.println("第一块土地");
    			farm = farms[0];
    			crop = crops[0];
    		}else if(isInDiamond(x-220,y-300,160,65)){
    			System.out.println("第二块土地");			
    			farm = farms[1];
    			crop = crops[1];
    		}else if(isInDiamond(x-114,y-343,160,65)){
    			System.out.println("第三块土地");
    			farm = farms[2];
    			crop = crops[2];
    		}else if(isInDiamond(x-433,y-304,160,65)){
    			System.out.println("第四块土地");
    			farm = farms[3];
    			crop = crops[3];
    		}else if(isInDiamond(x-333,y-341,160,65)){
    			System.out.println("第五块土地");
    			farm = farms[4];
    			crop = crops[4];
    		}else if(isInDiamond(x-224,y-389,160,65)){
    			System.out.println("第六块土地");
    			farm = farms[5];
    			crop = crops[5];
    		}else if(isInDiamond(x-556,y-352,160,65)){
    			System.out.println("第七块土地");
    			farm = farms[6];
    			crop = crops[6];
    		}else if(isInDiamond(x-448,y-386,160,65)){
    			System.out.println("第八块土地");
    			farm = farms[7];
    			crop = crops[7];
    		}else if(isInDiamond(x-341,y-438,160,65)){//第九块土地
    			System.out.println("第九块土地");
    			farm = farms[8];
    			crop = crops[8];
    		}else{
    			farm = null;
    			crop = null;
    			System.out.println("没有选中任何土地");
    		}
    
    	}
    
    	//width菱形宽,height菱形高
    	public boolean isInDiamond(int x,int y,int width,int height){
    		if(Math.abs(x*height)+Math.abs(y*width)<=width*height*0.5){
    			return true;
    		}
    
    		return false;
    	}
    
    	public void readNowState(){
    
    		File file = new File("D://GameRecordAboutSwing");
    
    		if(!file.exists()){
    			file.mkdirs();
    		}
    
    		File record = new File("D://GameRecordAboutSwing/recordFarmGame.txt");
    
    		try{
    			if(!record.exists()){//如果不存在,新建文本
    				record.createNewFile();
    				fos = new FileOutputStream(record);
    				dos = new DataOutputStream(fos);
    				String s = "0,null;0,null;0,null;0,null;0,null;0,null;0,null;0,null;0,null&0";
    				dos.writeBytes(s);
    				System.out.println(record.isFile());;
    			}
    
    			//读取记录
    			fis = new FileInputStream(record);
    			dis = new DataInputStream(fis);
    			String str = dis.readLine();
    			String[] array1 = str.split("&");//作物相关
    			String[] array2 = array1[0].split(";");//土地相关
    			fruitNumber = Integer.parseInt(array1[1]);//仓库果实
    			if(fruitNumber == 0){
    				storage.setText("您的仓库没有任何果实,快快播种吧!");
    			}else{
    				storage.setText("您的仓库现在有"+fruitNumber+"个果实.");
    			}
    
    			for(int i=0;i<9;i++){
    				String[] help = array2[i].split(",");
    				int state = Integer.parseInt(help[0]);
    				if(help[1].equals("null")){//未播种,直接跳过
    					continue;
    				}
    				Date lastTime = sdf.parse(help[1]);
    				Date nowTime = new Date();
    				long subTime = nowTime.getTime() - lastTime.getTime();//得到相差毫秒數
    				long temp = 0;//存储farm[i]最后操作的时间到当前时间的时间差(毫秒表示)
    				if(state == 1){//已播种
    					if(subTime>=GROWTIME){
    						subTime -= GROWTIME;
    						temp += GROWTIME;
    						state = 2;
    					}
    				}
    				if(state == 2){//已生长
    					if(subTime>=BLOOMTIME){
    						subTime -= BLOOMTIME;
    						temp += BLOOMTIME;
    						state = 3;
    					}
    				}
    				if(state == 3){//已开花
    					if(subTime>=FRUITTIME){
    						subTime -= FRUITTIME;
    						temp += FRUITTIME;
    						state = 4;
    					}
    				}
    
    				switch(state){
    				case 1:
    					farms[i].state = 1;
    					farms[i].lastTime = lastTime;
    					crops[i].setIcon("D:/Game/FarmGame/seed.png");
    					break;
    				case 2:
    					farms[i].state = 2;
    					farms[i].lastTime = new Date(lastTime.getTime()+temp);
    					crops[i].setIcon("D:/Game/FarmGame/grow.png");
    					break;
    				case 3:
    					farms[i].state = 3;
    					farms[i].lastTime = new Date(lastTime.getTime()+temp);
    					crops[i].setIcon("D:/Game/FarmGame/bloom.png");
    					break;
    				case 4:
    					farms[i].state = 4;
    					farms[i].lastTime = new Date(lastTime.getTime()+temp);
    					crops[i].setIcon("D:/Game/FarmGame/fruit.png");
    					break;
    				}
    
    			}
    
    		}catch(Exception e){
    			e.printStackTrace();
    		}finally{
    			try {
    				if(fis!=null)
    					fis.close();
    				if(dis!=null)
    					dis.close();			
    				if(fos!=null)
    					fos.close();
    				if(dos!=null)
    					dos.close();				
    			} catch (IOException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    
    	}
    
    
    
    	public void saveNowState(){
    
    		StringBuilder sb = new StringBuilder();
    		String[] strs = new String[9];
    
    		for(int i=0;i<9;i++){
    			strs[i] = farms[i].lastTime == null?"null":sdf.format(farms[i].lastTime);//土地最后一次操作时间
    			if(i!=8)
    				sb.append(farms[i].state+","+strs[i]+";");
    			else
    				sb.append(farms[i].state+","+strs[i]);				
    		}
    
    		sb.append("&"+fruitNumber);
    //		System.out.println(sb);	
    
    		File record = new File("D://GameRecordAboutSwing/recordFarmGame.txt");
    
    		try {
    			//清空原有记录
    			FileWriter fileWriter =new FileWriter(record);
    			fileWriter.write("");
    			fileWriter.flush();
    			fileWriter.close();
    			//重新写入文本
    			fos = new FileOutputStream(record);
    			dos = new DataOutputStream(fos);
    			String s = sb.toString();
    			dos.writeBytes(s);
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}finally{
    			try {
    				if(fos!=null)
    					fos.close();
    				if(dos!=null)
    					dos.close();				
    			} catch (IOException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    
    		}
    
    	}
    
    	@Override
    	public void run() {
    		try {
    			while(true){
    				Thread.sleep(1000);
    				saveNowState();
    				readNowState();
    			}
    		} catch (InterruptedException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    
    	}
    
    
    	@Override
    	public void mouseEntered(MouseEvent arg0) {
    		// TODO Auto-generated method stub
    
    	}
    
    	@Override
    	public void mouseExited(MouseEvent arg0) {
    		// TODO Auto-generated method stub
    
    	}
    
    	@Override
    	public void mousePressed(MouseEvent arg0) {
    		// TODO Auto-generated method stub
    
    	}
    
    	@Override
    	public void mouseReleased(MouseEvent arg0) {
    		// TODO Auto-generated method stub
    
    	}
    
    	public static void main(String[] args) {
    		EventQueue.invokeLater(new Runnable(){
    			public void run(){
    				try{
    					MainFrame frame = new MainFrame();
    					frame.setVisible(true);
    				}catch(Exception e){
    					e.printStackTrace();
    				}
    			}
    		});
    	}
    
    
    
    }
    

    游戏代码已经贴完了,虽说游戏功能较弱,但是基本的作物定时生长,种植信息的保存和读取,已经实现了。

    后续可能会通过微信小程序的形式对该游戏进行完整的制作(如果毕设老师同意该命题的话^_^),敬请期待(#^.^#)。

    展开全文
  • Java游戏开发起步

    千次阅读 2004-11-04 20:59:00
    Java游戏开发Step by Step系列之三Java游戏开发起步湖南 周凤英 文惺 前面我们已经介绍了Java游戏开发所要掌握的基础知识,分析了当今游戏开发的常规知识。想必大家已经有点儿迫不急待的想亲自动手打造一个属于自己...

    Java游戏开发Step by Step系列之三

    Java游戏开发起步

    湖南 周凤英 文惺

      前面我们已经介绍了Java游戏开发所要掌握的基础知识,分析了当今游戏开发的常规知识。想必大家已经有点儿迫不急待的想亲自动手打造一个属于自己的Java游戏了。好吧好吧,先让我们从Java游戏开发的各个技术环节简单处理,来全面体验一下Java开发游戏的乐趣吧!
      在开始之前我们还得确认您已经看完了我们系列的“Java 游戏开发基础”和“游戏开发常规”,并已经安装了Java JDK、浏览器软件如IE。本章是以Internet为开发对象,一步一步教大家认识Java的Thread、Applets以及游戏编程中的动画技术、图像处理技术、声音技术以及事件处理。在每一小部分我们都附上了相应的源代码以供大家参考。在文章中我们还穿插了很多建设性的问题,让读者参与到我们的开发中来。但是由于本章指在带领大家进入这个门槛并对Java游戏开发有个大概的认识,大部分知识并不会很详细说明。可能有些技术细节你还是不明白其原理、其运作。没关系,后面系列文章中会分步详细的对每个技术环节进行全面剖析。

    一、Applet

      Applets是一种Web浏览器上的小程序,由于applet对系统而言绝对安全,所以它做的事比aplication有限,但是对于客户端的程序,applets仍然是个很强大的工具。为了浏览和运行方便,我们就以applet为开发对象。
      开发Applets程序,我们得继承Applet类,并覆写必要的函数,下面几个函数控制了Web页面上的applet生成与执行。

    函数作用
    Init()这个函数会被自动调用,执行applet的初始化动作—包括组件在版面上的分配,你一定得覆写它
    Start()每当浏览器显示applet内容时,都会调用它,让applet开启其正规工作(尤其是那些被stop()关闭的工作),调用init()之后也会调用这个函数
    Stop()每当浏览器不显示内容时,都会调用它。让applet关闭某些耗资源的工作,调用destory()之后也会调用这个函数
    Destroy()浏览器将applet自网页移除之际,便会调用它,以执行”applet不再被使用”应该做的最后释放资源等动作
    Paint()让你在Applet界面上进行相应的绘画动作,每次刷新时都会重画

      所有的applet文件源文件名和java应用程序一样都是.java为扩展名,编译后的执行文件扩展名为.class,由于在applet中已经没有了main()函数,它是和html自动集成,所以我们要执行applet,要在html源文件中放入一特定的标签(tag),才能告诉网页如何装载并执行这个applet,这里有一点要注意,我们执行的网页必须能执行java程序。

    <HTML>
    <APPLET CODE="HelloWorld.class" WIDTH=300 HEIGHT=500>
    </APPLET>
    </HTML>

      <applet code ="HelloWorld.class" width=300 height=500>这行即applet的执行处。applet执行文档为 ="HelloWorld.class",告诉网页“applet”扩展文件为HelloWorld.class。width 和 height 告诉浏览器这个显示的applet的大小。有关标签(tag)的说明,大家可在网上找到很多相关的说明文档。

     

    二、线程

      由于apllet,Java应用程序的执行都和线程有关。我们来大概了解一下线程的概念。
      线程也称为轻型进程(LWP)。每个线程只能在单个进程的作用域内活动、协作和数据交换,并且在计算资源方面非常廉价。线程需要操作系统的支持,因此不是所有的机器都提供线程。Java编程语言,作为相当新的一种语言,已将线程支持与语言本身合为一体,这样就对线程提供了强健的支持。
      Thread类是一个具体的类,即不是抽象类,该类封装了线程的行为。要创建一个线程,程序员必须创建一个从Thread类导出的新类。程序员必须覆盖Thread的run()函数来完成有用的工作。用户并不直接调用此函数,而是必须调用Thread的start()函数,该函数再调用run()。
      但是使用Thread类实现线程,增加了程序的类层次,所以一般程序员都由另一个java线程接口Runnable接口来实现,Runnable接口只有一个函数run(),此函数必须由实现了此接口的类实现。
      线程中有几个重要的方法是我们得了解:

    Thread.start(): 启动一个线程
    Thread.stop(): 停止一个线程
    Thread.sleep(time in milliseconds): 暂停线程在一个等待时间内。

     

    三、动画技术

    自由降落动画

      好了,了解了一些基本概念后。下面我们就开始我们的实质性的工作。我们设计一个球从屏幕顶上降落到屏幕下面,程序实现比较简单,但是这是游戏动画中不可少的一部分。在开始之前我们来看看我们的applet开始语句。

    import java.awt.*;
    import java.applet.*;
    public class Ball extends Applet implements Runnable { public void init() {} public void start() {} public void stop() {} public void destroy() {} public void run() {} public void paint(Graphics g) {} }

      在开始函数中我们要新建程序的主线程,并启动这个线程。一旦做好这些准备工作以后,当applet第一次被显示时,就会创建线程对象的一个实例,并把this对象作为建构方法的参数,之后就可以启动动画了:

    public void start() {
      // 定义一个新的线程
      Thread th = new Thread(this);
      // 启动线程
      th.start();
    }

      现在我们来看看线程的run方法,它在循环while(),中每隔20毫秒重画动画场景。sleep这个方法很重要,如果在run循环中没有这部分,圆的重画动作将执行得很快,其他方法将得不到有效执行,也即我们在屏幕上将看不到球的显示。

    public void run ()
    {
      while (true) {
        // 重画applet画面
        repaint();
    try { // 暂停线程20毫秒 Thread.sleep (20); } catch (InterruptedException ex) { } } }

      我们接着读下去之前,有几个问题需要回答。你也许会问,浏览器调用Java小程序的start和stop方法吗?run 方法是如何被调用的?情况是这样的,当浏览器启动了一个内部线程时,就相应地启动了applet的运行。当网页显示时,就启动了applet的start方法。Start方法创建一个线程对象,并把applet自身传送给线程,以实现run方法。
      此时,两个线程在运行:由浏览器启动的初始线程,以及处理动画的线程。快速查看applet的start方法,可以知道它创建了线程,并启动了它。类似地,当网页被隐藏后,applet的stop方法就调用了线程的stop方法。
      需要注意的是,在Applet和Thread两个类中都有start和stop方法,但它们的功能不同。一旦Applet显示时,就调用applet的start方法,一旦applet隐藏时,就调用applet的stop方法。相反,线程的start方法将调用run方法,线程的stop方法将停止正在执行的线程。

    public void paint(Graphics g);

    paint()方法所传入的参数——java.awt.Graphics对象将是一个经裁剪的相关显示区的图像代表(而不会是整个显示区)。我们对圆球图形元素的绘制就是在通过重写paint()方法,在其中对传入的Graphics对象g进行操作完成的。
      当我们应用程序的逻辑要对系统界面进行更新时,调用repaint()方法来通知AWT线程进行刷新操作。repaint()方法实际会让AWT线程去调用另外一个方法,update。update方法在默认情况下会做两件事,一是清除当前区域内容,二是调用其paint()方法完成实际绘制工作。paint、repaint、update 三个方法关系如图所示:

      但是如何让我们的圆运动呢?这里我们利用函数Graphics类的fillOval函数来设置了圆的起始位置x,y。现在我们只要在线程run方法中每单位时间增大y的值,线程将在每一个单位时间内重画圆的位置。每单位时间y值越大,下降的速度就会越快。在屏幕上我们就将看到这个圆球做自由降落运动。 如下代码所示:

    while (true) { 
      // 设置动画移动速度 
      y += 1;
    }
    
    public void paint (Graphics g) { //设置球的颜色 g.setColor (Color.blue);
    // 从x,y位置处画一个实心的圆 g.fillOval (x , y, 2 * r, 2 * r); }

      在这之前我们需要在开始处设置一些变量,定义好x,y的默认位置值。r 在此处是我们画的圆的半径大小。

    int x = 100;
    int y = 20;
    int r = 10;

      好了,我们的自由降落的动画就完了。是不是很简单,如果还有地方不明白,大家可在 这里 下载源代码例子及演示效果。如果大家有兴趣,可改变y的值及x的值,你会得到不同的下降效果。

    双缓冲,消除闪烁

      大家可能注意到了上面例子中的我们下降的圆看起来不是很清晰,带着很严重的闪烁。这种现象在写游戏程序中是普遍存在的现象。这是由于我们的repaint()函数导致的结果,由于它在调用paint()函数前会自动清除屏幕,所以在一个毫秒内我们会看到一个空白的屏幕,在快速的变换操作中就出现了闪烁现象。
      解决这种闪烁现象有几种方法,下面是两种方法的列举说明,其他的方式大家可以自己尝试。
      第一种:我们始终不清除屏幕显示。但是这个方法会带来个附作用,我们下降的圆不在是一个圆了,而是一条直线,因为它的下降过程中没有了断点,保留了所有的圆球的影象。我们只要在Ball.java内加上如下代码update(Graphics g) {paint(g)},你就会看到一条很长的线拉出来。有兴趣的朋友可以试试。
      第二种:使用双缓冲机制(Double buffering)。现在大部分的游戏都是采用双缓冲机制来解决屏幕的闪烁现象,我们就以此为例来进行说明,有关缓冲区及相关缓冲机制的概念,大家可参考附录的缓冲说明。
      而我们的程序中简单的说就是在显示我们想要的图画之前,把所有的图画先在后台绘制好并存放到相应的图像变量中去。当需要显示时直接复制到前台屏幕就可以了。
      具体实现如下:

    1. 我们用createImage方法新建一后台图像类变量
    2. 使用getGraphics()方法得到当前图像的图形关联
    3. 在后台处理所有相关的处理,如清除屏幕,后台绘画等等。当完成所有的后台工作后,复制已经绘制好的图像到前台,并覆盖前台的存在图像。这样我们的所有操作都是在后台前行,在屏幕显示新的图像前,这些内容都已经存在于后台了。所以你也将在任何时刻都看不到空屏幕的存在。也即代表闪烁消除了。

      下面我们来看看相关的代码说明。在开始之前我们得先在程序的开始部分声明两个实例变量用来存储后台图画。如下:

    private Image bgImage;
    private Graphics bg; 

    然后我们利用update()方法来实现双缓冲机制。Update()方法要实现下面三个步骤:

    1. 清除屏幕上的组件
    2. 设置相关联组件的前景色
    3. 调用paint方法重画屏幕
    public void update (Graphics g)
    {
      // 初始化buffer
      if (bgImage == null) {
        bgImage = createImage (this.getSize().width, this.getSize().height);
        bg = bgImage.getGraphics ();
      }
    // 后台清屏,即设置圆球组件和后台一样的颜色和大小 bg.setColor (getBackground ()); bg.fillRect (0, 0, this.getSize().width, this.getSize().height);
    // 绘制相应的元素组件 bg.setColor (getForeground()); paint (bg);
    // 在屏幕上重画已经绘制好的圆 g.drawImage (bgImage, 0, 0, this); }

    此处g为屏幕图形,bg为g的后台关联。而bgimage包含了bg图形。请在 这里 下载源代码例子及演示效果。

    改变运动方向

      是的,我们已经解决了动画的两个很重要的问题,移动动画和闪烁消除。但是我们很快会发现一个问题,球从屏幕顶上落下来后,就不见了。这可不是我们所需要的。我们要的是一个生动的画面。如何让我们的球不穿过屏幕而始终在屏幕上活动呢?在开始之前,我建议大家自己想办法解决,如果你能自己处理好了。你的水平将会有一个很大的提高。如果没有想出好办法,没关系,下面我们将很详细的说明球的方向改变的技术。
      不知道大家注意了没有,在上面我们说到球的移动时,我们是通过增加y的值,让线程重画新的圆位置和图形。如果改变y的值的大小球的下降速度也会改变。不错,这就是我们的解决方法,我们只要用一个变量来存储这个速度的大小而不用固定的值。在线程执行也即run方法处我们用代码改变速度的方向,球的方向也会改变。即设置这个变量“speed”为“-1”。当然在设置值前我们要进行判断,你是想让球穿过屏幕从别一边开始显示,还是来回反弹呢!如果想来回反弹,我们只要不让球的半径值超过applet屏幕显示区域就可以了。此处我们用r/2来表示球的半径。

    //反弹下落球
    if (y > appletsize_y – r/2) {
      // 改变方向
      x_speed = -1;
    }
    // 反弹上升球
    else if (y < r/2)
    {
      // 改变方向
      x_speed = +1;
    }

      至于如何让球穿过从屏幕顶上重新下降,我们在此没有说明,也不会说明了。留给大家自己去想想,已经很简单了。在 这里 我们附上了两种方式的源代码和执行文件。如果大家运行程序,大家可能会发现,我们的球的大小和速度有一些改变。这里是为了更好的反应演示效果。



    四、多媒体

    使用多媒体声音

      多媒体功能在游戏中是必不少的一部分,优美的音乐,漂亮的界面往往是一个成功游戏必需具备的条件。在开始之前我们先了解一下主要的小型声音文件类型:

    • AU - (扩展名为AU或SND)适用于短的声音文件,为Solaris和下一代机器的通用文件格式,也是JAVA平台的标准的音频格式。AU类型文件使用的三种典型音频格式为:8位μ-law类型(通常采样频率为8kHz),8位线性类型,以及16位线性类型。
    • WAV - (扩展名为WAV)由Microsoft和IBM共同开发,对WAV的支持已经被加进Windows 95并且被延伸到Windows 98。WAV文件能存储各种格式包括μ-law,a-law和PCM(线性)数据。他们几乎能被所有支持声音的Windows应用程序播放。
    • AIFF - (扩展名为AIF或IEF)音频互换文件格式是为Macintosh计算机和Silicon Graphics (SGI)计算机所共用的标准音频文件格式。AIFF和 AIFF-C几乎是相同的,除了后者支持例如μ-law和IMA ADPCM类型的压缩。
    • MIDI - (扩展名为MID)乐器数字接口MIDI是为音乐制造业所认可的标准,主要用于控制诸如合成器和声卡之类的设备。

      在JDK1.0上,java只支持*.au格式的声音文件,但是java2的API以及声音包提供了很强大的对声音技术的支持。而此部分为了让大家快速掌握游戏编程的基本知识,我们仅使用了AudioClip接口类来实现播放“*.wav”。如果大家有兴趣可参考sun java网站的声音sapmle,上面提供了完备的实例和教程说明。
      使用AudioClip接口比较简单,我们只要实例对象,加载声音文件后,再在任何地方播放即可。恢复和播放声音最简单的方法是通过Applet类的play()方法。

    AudioClip接口

    1. 播放 play
    2. 循环 loop
    3. 停止 stop

      启动和停止声音文件,或循环播放,你必须用applet的getAudioClip方法把它装载进入AudioClip对象,getAudioClip方法要用一个或两个参数,当作播放的指示。第一个或唯一的一个参数是URL参数,用来指示声音文件的位置,第二参数是文件夹路径指针。
       下列代码行举例说明加载声音文件进入剪贴对象:下面的“gun.wav”是指当前目录下的声音文件。我们也可用*.au格式的文件代替。

    AudioClip co = getAudioClip(getCodeBase(), "gun.wav");

    getAudioClip()方法仅仅能被applet内调用。随着JAVA2的引入,应用程序也能用Applet类的newAudioClip方法装入声音文件。前一例子可以改写如下以用于Java应用程序:

    AudioClip co = newAudioClip("gun.wav")

      我们现在可在任何地方使用方法play()播放我们的声音了。play()一旦被调用立刻开始恢复和播放声音。但这有一点要注意:如果声音文件不能被查找,将不会有出错信息,仅仅是沉默。源代码及应用程序请于 此处 下载。


    五、图片处理技术

      图片的处理和声音的处理在一样简单。设置图片变量,得到图形,最后绘制图形。我们就直接从代码来分析。在此我们绘制一幅applet的背景图。开始绘制前,我们先要声明图形变量,用来存放图形文件。

    Image backImage; 
    // 加载图片文件
    backImgage = getImage (getCodeBase (), "black.gif");

    下面在我们的paint()方法中利用函数drawImage绘制我们图形。

    g.drawImage (backImage, 0, 0, this); 

    DrawImage参数中的blackImage即我们得到的图形,而后面的0,0分别代表图形的x坐标和y坐标。this为图形代表的类,这里指的即picture类。在这里建议大家使用*.gif格式的图片文件。因为如果是internet网上,文件的大小也决定了你的applet加载时的快慢,没有人很愿意等很长时间来玩你的游戏,即使你的游戏比较出色。源代码及演示程序可以从 这里 下载。
      大家在玩游戏时是不是见过人物图像行走,动物来回跑动的动画?这些都是基于图形技术来实现的。我们只要把上面的代码稍微修改,用数组变量来存储我们得到的图形文件组,再利用drawImage()方法播放出来就可实现动画图片的播放:

    Image[] backImage; 
    // 加载图片文件
    for (int i=4,i<backImage.length,i++) {
      backImgage[i] = getImage (getCodeBase (), "t1"+i+".gif");
    }

    大家可参考JDK包中的Animation例子,它就是一个很好的播放一组图片文件的例子。

    六、事件处理

    鼠标监听技术

      玩游戏时,不管是小型的扑克牌和大型的RPG游戏,都要参与者溶入到游戏的角色当中。不错,交互,游戏有了交互的功能才可以说是一个完整的游戏。即使是编程游戏如机器人足球,Robocode都要程序员参与编写代码,观察比赛。有两种主流方法可实现游戏的交互:鼠标和键盘。当然还包括手操杆等,但现在大部分Pc机上使用的还是鼠标和键盘。我们就以这两项为基础来说明游戏中事件的响应过程。
      要判断相应的鼠标所进行的动作:是点击,还是移动。我们必须对我们鼠标进行监听。要监听鼠标事件就必须调用这些接口之一,或扩展一个鼠标适配器(mouse adapters)类。AWT 提供了两种监听接口(listener interface):java.awt.event.MouseListener和java.awt.event.MouseMotionListener。
      现在我设计一个鼠标事件,当点击applet屏幕时,下降的球向反方向运动。以实现了对游戏的简单控制。
      MouseListener一共有5个方法,主要用来实现鼠标的点击事件。这里要注意一点:由于MouseListener是接口我们要在实现的类中重载它的所有方法。
      Mouse点击事件有:

    • mousePressed() 当用户按下鼠标按钮时发生
    • mouseReleased() 当用户松开鼠标按钮时发生
    • mouseClicked() 当用户按下并松开鼠标按钮时发生。用户在选择或双击图标的时候通常会点击鼠标按钮。用户如果在松开鼠标之前移动鼠标,点击不会导致鼠标相应事件出现
    • 因为点击鼠标是按下鼠标和松开鼠标的结合,在事件分配给 mouseClicked() 方法之前,mousePressed() 和 mouseReleased() 方法已同时被调用

      鼠标状态处理有:

    • mouseEntered() 当鼠标离开当前组件并进入你所监听的组件时激活事件
    • mouseExited() 当鼠标离开你所监听的组件时发生

      Mouse移动事件主要通过接口MouseMotionListener来实现:

    • mouseDragged() 当用户按下鼠标按钮并在松开之前进行移动时发生。在mouseDragged()后松开鼠标不会导致mouseClicked()
    • mouseMoved() 当鼠标在组件上移动而不时拖动时发生

      依据我们的游戏设计,我们在这要使用到MouseListener接口。实现接口后。我们要在init()函数加入监听器addMouseLisener(),来监听对applet的响应事件。
      知道了鼠标事件的处理,我们再来回顾一下上面提到的球反弹设计,现在我们要如何处理了球的控制呢?让我们想一想,不错,可能你已经发现了,我们照样可通过改变speed方向来实现回弹控制操作。在mousePressed(){}事件中加入下面的代码,我们的回弹控制就设计完成。

    speed = -4

    记得在释放applet资源时,我们要释放mouseListener资源。在destory()中加入

    removeMouseListener(this);

    可能有些朋友会使用mouseDown()方法,mouseDown()在此我建议大家不要再使用这个方法了,它已经是被淘汰的产品,是为了兼容JDK1.0而带到JDK1.4中来的。源代码请于 这里 下载。

    键盘监听技术

      知道了鼠标的操作处理,键盘的操作处理就很简单了。我们只要实现keyListener接口,并在相应的事件中加入我们要实现的代码。

    • KeyPressed 当按键时发生
    • KeyReleased 当翻译键时发生
    • KeyTyped 当打击键时发生

    由于在后面我们设计的游戏中我们不会使用到键盘操作,键盘事件处理我们就交给大家自己去实现。
      好了,现在我们来回顾一下我们能做什么了?移动一个物体,加载声音和图片,用鼠标对游戏进行一定的控制。哦,我的天,我们已经可以做自己的很简单的游戏了。是的,你可以了,我认为在此,大家可以放下教程,把自己小时候一直想玩的游戏,把自己学程序时一直想做的游戏自己进行设计实现,这对你的帮助将是非常大的。对你的编程水平也是一个很大的提高。
      当然如果你仍然认为自己认识还不是很深,下面让我们来设计一个完整的游戏。这将是一个很有意思的过程。

     

    七、第一个游戏——“保卫者”

      真正做自己的游戏是总是很兴奋。在开始任何事情之前,我们都要有个好的设计,游戏更不例外。下面我们就以上面的例子为本。设计一个“保卫者”的游戏。游戏思路本身很简单,从屏幕的顶端不断的有炸弹落下来,而我们这些“保卫者”要在它们着地之前,用鼠标点击让它反弹回去,不让它落到地面上来,但是球在上升过程中我们也要注意不让它撞到顶上。如果撞到顶上或地画,你的生命点数都会减少。每点中一个炸弹你的分数就会增加。当你的生命点数为零时便“Game Over”。

    设计结构

    1、模块设计

      游戏的结构很简单,由三个模块组成:

    • Denfen类:Denfen类控制整个游戏主线程,初始化炸弹类,并绘制屏幕上的炸弹数量及处理炸弹的移动,并监听鼠标事件
    • Bomb类:主要是判断炸弹的速度,方向,是否撞到地面和点击事件
    • Denfense类:主要用来处理游戏者的记分和生命点数

    2、方法实现

    Denfen

    • init():初始化所有对象,包括声音文件的加载,Bomb类的生成
    • run():处理炸弹的下降运动
    • paint(...):绘制炸弹及相关的数据记录显示
    • update(...):实现屏幕图像的双缓冲,消除闪烁
    • mouseProcess (...):利用mouseEvent事件监听来处理鼠标按下事件,并根据鼠标当时的x坐标和y坐标判断是否点中炸弹。
    • addBomb():利用默认值来动态实现bomb的生成,这里我们利用了数组来记录的。默认值是3,大家可依据自己的爱好增加或减少记录。

    Denfenser

    • Score:积分
    • Life:生命点
    • AddScore():增加游戏者的积分
    • Death():减少游戏者的生命点数
    • getScore():获得当前的积分数
    • getLife():获得当前的生命点数

    Bomb

    • Bomb(...):构造函数,初始化炸弹的位置,声音,颜色等相关变量的值
    • down():处理bomb的下降
    • isRebound():反向回弹炸弹的方向,并根据积分来加快炸弹的下降速度
    • userHit(int x, int y):游戏者是否点中炸弹
    • wasHitEarth():判断炸弹是否撞击到地面或顶面,如果是生命点将减少
    • DrawBomb(Graphics g):绘制Bomb图象。

    3、工作原理

      首先我们在init()方法中加载所有游戏必要的资源,包括声音,鼠标事件的监听、背景等相关设置。利用addBomb()方法增加bomb的数量、初始位置及初始化颜色。再利用start()启动线程。线程调用run()方法,处理炸弹下降运动down()。Repaint()会在每一个单位时间调用paint()方法不断的刷新屏幕,paint()调用Bomb.addBomb()绘制炸弹。当游戏者按下鼠标,mousePress()事件激活,判断是否点中了炸弹。如果点中addScore()自动加1分。如果没有点中炸弹,炸弹继续下降,当撞到屏幕wasHitEarth()方法激活,其内调用death()方法,减少Denfenser.life生命点,同时audio.play()处理声音的播放,用以提示游戏者。当你的生命点数小于0时“Game Over”。
      这个游戏并不是很完善。下面提到一些改进方法,大家可以动手试试。做出适合自己的游戏风格来。具体的源代码及实现过程请大家从 这里 下载。

    4、游戏的改进

      背景的替换,本例的背景用的是函数setBackground(),大家可用相应的图形来代替。
      炸弹数量的增加,为了减少复杂度,例子用到的炸弹数量是固定值3,我们可根据积分的多少,在游戏中动态的增加炸弹的数量。
      等级的设置,本游戏中没有等级的功能。如果大家在游戏中加入等级,依据不同的等级不断的变换游戏的模式,这将是很有意思的过程。
      模式改变。我们可以在游戏中实现自己的模式。如消灭炸弹。点一个炸弹,就让炸弹从屏幕上消灭。
      我们还可以增加一个游戏者,加大游戏的可玩性。增加键盘的处理功能。加大游戏的灵活性。
      还有很多很多的处理和玩法,这都等着你去发掘。相信java 游戏编程将会是一个很有意思的学习过程。

     

    八、附录——缓冲机制

      缓冲区是在视频内存中用来储存着色的像素(影像)的区域。缓冲区的大小由解析度和色深决定,例如800x600,16bit色的缓冲区就占用800x600x2(16bit=2bytes)的内存区域。

      (1) 前置Buffer是当前显示在萤幕上的缓冲区,后置Buffer是尚未显示在萤幕上的缓冲区。
      (2) Single Buffering使用一个前置缓冲区,在着色的同时影像立即显示在萤幕上。因此当萤幕更新影像时会出现闪烁的现象。Single Buffering在目前的程序中已很少使用。
      (3) Double Buffering则使用两个缓冲区,一个前置Buffer,一个后置Buffer。所谓前置和后置是相对而言的。前置缓存的像素在屏幕上显示的同时,显卡正在紧张地着色后置缓存中的像素。后置缓存的像素上色完毕后是以Vsync信号的形式等待。在前置缓存和后置缓存交换后,新一轮的着色工作又重新开始。这正如舞台话剧中前台和后台的演员一般。在前台演员表演时,后台的演员仍在进行最后的排练。前台的演员下场时正是后台演员登场的时间。唯一不同的是前置和后置缓存是循环轮番上阵,而演员表演完毕一般都不再出场。目前大多数游戏内定都使用Double Buffering。
      (4) Triple Buffering使用一个前置缓存和两个后置缓存。在着色完第一个后置缓冲区的数据后,立即开始处理第二个后置缓冲区。今天,不少新游戏都采用的是Triple Buffering,Trible Buffering正逐渐成为发展的趋势,因为它没有Vsync(萤幕的垂直刷新频率)等待的时间,游戏也将更加流畅。Triple Buffering也是3Dmark2000测试的内定值设定。

    展开全文
  • java游戏开发杂谈 - 画布和画笔

    千次阅读 2019-04-02 12:22:22
    在Eclipse里,编写如下两个类: package game2; import java.awt.Color; import java.awt.Graphics;... * java游戏开发杂谈 * ---demo2:画布和画笔 * * @author 台哥 * @date 2019-04-02 */ public ...
  • java游戏开发 地图图块属性

    千次阅读 2015-10-19 11:34:14
    继续挖坑,java游戏开发,继续在图块属性上困扰,参考df《矮人要塞》的开发一款模拟经营类游戏,地图大小将达到 256*256*256 大小(这只是世界地图的一小小小块)。。因为这是个平面 三维游戏 = = 而每个点上面,有...
  • JAVA游戏开发-超炫酷贪吃蛇游戏源码及教程

    万次阅读 多人点赞 2019-08-29 17:28:49
    自己刚好正在学习JAVA编程,也想实现一个类似功能的游戏Demo练手,在网上查看了不少源码案例,全都是很古老的方块式贪吃蛇游戏案例,没有想要的实现,因此自己动手实现一个JAVA版的贪吃蛇游戏。 我在这个Dome完成...
  • Java游戏开发——飞行射击类游戏

    千次阅读 多人点赞 2018-11-28 10:42:31
    import java.awt.image.ImageObserver; import javax.swing.JPanel; public class Bullet { static final int BULLET_STEP_X = 3; static final int BULLET_STEP_Y = 15; static final int BULLET_WIDTH = 30;...
  • Java游戏开发编写源码

    千次阅读 2019-05-09 13:50:14
    本文来源公众号:千问千知(qwqz1000),更多游戏开发源码可关注公众号获取。 关注千问千知,获取Java项目视频。 线程,让游戏拥有了动态变化的能力。 java的图形界面,在启动的时候,就开始了一个线程。 这个线程...
  • Java 游戏开发Notes

    千次阅读 2005-08-10 13:23:00
    一、社区: 2005年Sun专门新成立了 Java游戏开发社区 以示重视:http://community.java.net/games/ 上面有Projects,Wiki和Blog的连接,不过和整个java.net一样,架子大,内容少。二、主要参考书籍: 1. David ...
  • java游戏开发杂谈 - java是什么

    千次阅读 2019-03-27 15:58:55
    java是一门编程语言, 它有三个开发平台:j2ee、j2se、 j2me。(其实android也算一个了) j2ee, 也就是web开发,比如网站、各类管理系统,主要是围绕数据库、网页等进行的开发。j2ee涵盖的技术比较多,除了java...
  • JAVA游戏开发之FPS精准控制

    千次阅读 2014-03-02 23:23:46
    目录 JAVA游戏开发之FPS精准控制... 1 1 概述... 2 1.1 编写目的... 3 2 FPS精准控制... 3 2.1 FPS描述... 3 2.2 大众版FPS控制... 3 2.3 大众版效果展示... 4 2.4 精准FPS控制... 5 2.5 精准
  • 本人做了一年四个月的java web开发。是在一个创业型的公司里面就职的! 从大四实习开始就一直在改...并且使用c++,coco2d-x,和java独自写了一个小游戏,希望可以借此找到游戏开发的工作! 但是实现往往是不如我所愿的
  • java游戏开发杂谈 - 实现游戏主菜单

    千次阅读 2019-04-12 16:11:24
    经常玩游戏的同学,大家都知道,游戏都会有个主菜单,里面有多个菜单选项:开始游戏、游戏设置、关于游戏、退出游戏等等,这个菜单是怎么实现的呢。 有一定桌面软件开发基础的同学...我们既然是学习游戏开发,那么...
  • java游戏开发杂谈 - 游戏编程浅析

    千次阅读 2019-03-27 16:16:51
    每个游戏,你所看到的它的一切,都是计算机画出来的! 地图是画出来,人物是画出来的,树木建筑是画出来的,菜单按钮是画出来的,滚动的文字、闪烁的图标、云雾烟火,都是画出来的。 游戏编程,所要做的,就是控制...
  • java游戏开发杂谈 - 事件处理

    千次阅读 2019-04-09 15:17:56
    大家先不用深究太详细的,只需要知道,当我们按下键盘时,操作系统会接收到指令,然后把指令传递给java虚拟机,java虚拟机再把指令转发给当前的游戏程序。 java定义了一些类来封装事件,比如KeyEvent对应按键事件....
  • Java游戏开发框架LGame-0.2.8版发布(含PC及Android版,已有文档)
  • Java游戏开发之一

    千次阅读 2007-05-19 11:46:00
    Java开发MMOPG游戏是一个挑战, 接下来的这个系列我将结合最近正在开发的一款网络游戏,写一些游戏开发方面的技巧。使用Java开发游戏除了了解基本的类库,还要了解供游戏使用的多种API: 首当其冲的是全屏切换API:...
  • JAVA游戏开发之FPS精准控制                         2013年7月18日                       目录 JAVA游戏开发之FPS精准控制... 1 1 概述... 2 1.1 编写...
  • Java游戏开发框架LGame-0.2.6发布(含PC及Android版)

    万次阅读 热门讨论 2010-07-31 20:10:00
    Java游戏开发框架LGame-0.2.6发布(含PC及Android版,对于图形渲染部分功能有显著强化)
  • JAVA游戏开发计算显示FPS

    千次阅读 2014-03-03 13:37:22
    今天写游戏需要计算FPS,就在网上搜到了Loon的一个java 游戏计算FPS的类,修改了一下如下(版权属原作者): package com.px.FPS; import java.text.DecimalFormat; /** * */ /** * Title: LoonFramework * ...
  • 终于找到java游戏开发工作

    千次阅读 2014-03-13 19:31:09
    三个星期了!终于接到offer,到公司上班了!虽然工资有点低!但是算是转游戏开发成功你了! 终结一句:尝试—分析失败原因.....................=成功

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 165,054
精华内容 66,021
关键字:

java游戏开发

java 订阅