精华内容
下载资源
问答
  • 多线程游戏

    2020-05-20 20:54:30
    线程可以说是进程的一个小任务,一个进程可以有线程 游戏——王者荣耀 :代码,图片素材,运行:图片需要在屏幕上绘制出来,技能引起数据变化 2.java如何实现线程 Thread类:继承于Runnalble类 ①继承 ...

    1.程序、进程、线程

    ①程序

    存在硬盘里面的文件,固定文件,包含(代码,素材,配置文件……)

    ②进程 一个程序从载入内存,进程启动,运行,到结束

    运行/使用中的程序

    ③线程

    进程>线程 CPU 双核四线程 六核六线程

    线程可以说是进程的一个小任务,一个进程可以有多个线程

    游戏——王者荣耀 :代码,图片素材,运行:图片需要在屏幕上绘制出来,技能引起数据变化

     

    2.java如何实现线程

    Thread类:继承于Runnalble类

    ①继承

    ②重写run方法

    ③使用线程 -启动方法 start

    runnable接口:

    ①实现接口

    ②重写run方法

    ③实例化Thread的对象 调用构造方法(包含了实现了Runnable接口类的对象)

    ④使用Thread类的对象启动 start

    3.例子(运动小球)

    界面,画布

     

    展开全文
  • 这个回答非常形象,系统思维的模式。------------------------------------------------------作者:...主要是任务可分解性和结果可预期性。数据类的工作是很容易做到这点的。游戏并不是这种类型。多核、多线程、多g...

    这个回答非常形象,系统思维的模式。

    ------------------------------------------------------

    作者:既让
    链接:https://www.zhihu.com/question/52957429/answer/272333887
    来源:知乎
    多核肯定是难的。

    主要是任务可分解性和结果可预期性。数据类的工作是很容易做到这点的。游戏并不是这种类型。

    多核、多线程、多gpu也好。多并不能解决一切。甚至根本没用。多线操作良好与否主要还是软件的问题。或者说是逻辑设计的问题。机器可不知道你下面要干什么。


    我觉得用交通来比拟是合理的。一个城市堵不堵,看什么?一是路面质量;二是道路宽度;三是路线分布;四是道路数量。你会发现堵的路总是那么几条。你额外增加其他道路并不能加速运转。这时候反而是道路宽度和质量更能直接体现作用了。相比而言就是频率和缓存。不合理的架构使得本来可以走另外可能存在路线的车也走了已经拥挤的路,这样也不会很快。

    此外,为什么不是分得越多越好呢?用最简单的例子来说明。假设只有a,b两个点,那么他们来往就是2个线路。如果是a,b,c,那么是6条。如果是a,b,c,d,那么是12条。想想这个增长会有什么结果。任务分布给更多的点,就需要更多的交互。

    交互越多,额外的事务就越多。就像网络一样,经过节点越多,延迟越高。

    对于不可分解的任务。其他的都要等待。就算有能力也没用。

    而且任务可能存在互锁的情况。你等别人,别人等你(或者其他人),然后最后别人又等你。这种东西是逻辑性的。不可预期。

    还有就是万一不是核数问题呢?cpu和gpu任务不同。如果一部分任务本来是另一部分要做的,你怎么抬也没用。


    我认为模糊性的,数量有大的复杂性肯定是很高的。首先模糊的,你难以确定问题到底是什么样。就像球队组建一样。不就是几个人吗?但是交易之后结果基本上没多少会和预期一样的。人就是这样,这种隐藏的交互效果的逻辑并不能看出来。量大,这种更加难以估计了。


    不过我觉得,这也不是问题。主要是这方面经验太少了。游戏硬件、软件引擎、游戏本身都在变化。根本就没足够时间来寻找固定的模式。针对特定的硬件,设计高效引擎,应该能大幅提高性能。另外玩家都是从单核,简陋图形处理部件过来。设计思路当然也是跟着过去来的。如果多核、多gpu的玩家多,那么问题可能会集中处理,因而会有所改进。

    好像图形技术并不是很久远的科技。可能和这个不成熟也有关系。cpu是科学计算相联系的。这些的基础研究早在几十年前就完成了。图形,尤其是虚拟现实都还是新东西。


    还有就是最关键的一点。多核设计本身是反人类的。因为人的脑子是单核的。我们玩游戏都是操作一个逻辑主体在运行----我们都是单线程生物。设计者不也是这么想的吗?这种情况下,多核就是把原来是一件事硬生生分成几件事。有人以计算为例,说明如何多线程。不过你仔细想想,如果人没有刻意那么想,会那样操作吗?肯定是直接加减乘除的。


    展开全文
  • 511遇见易语言多线程大漠多线程 模块源码 .子程序 线程_等待, 逻辑型, 公开, 等待一个线程,返回真表示线程运行结束,返回假表示已超时 .参数 线程句柄, 整数型 .参数 等待时间, 整数型, 可空, 单位毫秒,默认为...

    API-线程等待CPU优化

    用到的API

    1、MsgWaitForMultipleObjects
    2、WaitForSingleObject
    3、SetThreadAffinityMask
    4、SetThreadIdealProcessor

    511遇见易语言多线程大漠多线程

    模块源码

    .子程序 线程_等待, 逻辑型, 公开, 等待一个线程,返回真表示线程运行结束,返回假表示已超时
    .参数 线程句柄, 整数型
    .参数 等待时间, 整数型, 可空, 单位毫秒,默认为5秒,无限等待--4294967295
    .局部变量 ret, 整数型
    .局部变量 时间, 整数型
    
    .如果真 (等待时间 = 0)
    等待时间 = -1
    .如果真结束
    时间 = 取启动时间 ()
    .循环判断首 ()
    ret = MsgWaitForMultipleObjects (1, 线程句柄, 假, 等待时间, 1215)
    .如果真 (等待时间 ≠ -1)
    .如果真 (取启动时间 () - 时间 ≥ 等待时间)
    ret = 258
    .如果真结束
    
    .如果真结束
    处理事件 ()
    .循环判断尾 (ret ≠ 0 且 ret ≠ 258)
    返回 (ret = 0)

     

    展开全文
  • Java多线程游戏仿真实例分享

    万次阅读 多人点赞 2021-02-02 18:20:57
    这是一篇学习分享博客,这篇博客将会介绍以下几项内容: 1、如何让一个程序同时做多件事?(多线程的创建、多线程的应用、多线程的特点以及...3、多线程游戏仿真实例分享(飞机大战、接豆人、双线挑战三个游戏实例)

    这是一篇学习分享博客,这篇博客将会介绍以下几项内容:

    1、如何让一个程序同时做多件事?(多线程的创建、多线程的应用)
    2、如何让小球在画面中真实地动起来?(赋予小球匀速直线、自由落体、上抛等向量运动)
    3、多线程游戏仿真实例分享(飞机大战、接豆人、双线挑战三个游戏实例)

    • 涉及的知识点有:多线程的应用、双缓冲绘图、小球的向量运动、游戏的逻辑判断、键盘监听器的使用、二维数组的使用、添加音乐效果等

    游戏效果:
    在这里插入图片描述
    在这里插入图片描述
    怎么样?如果觉得还不错的话就请继续看下去吧!

    热身

    第一步:创建画布

    • 心急吃不了热豆腐,我们先从最简单的创建画布开始。
      首先我们创建一个窗体,然后设置一些参数,从窗体中取得画笔,尝试在画布中心画一个图形,以下是参考代码:
    import java.awt.FlowLayout;
    import java.awt.Graphics;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    
    public class Frame {
    	//声明画布对象
    	public Graphics g;
    	
    	//主函数
    	public static void main(String[] args) {
    		//创建Frame类,然后运行showFrame函数
    		Frame fr=new Frame();
    		fr.showFrame();
    	}
    	
    	//编写窗体显示的函数
    	public void showFrame(){
    		//创建窗体
    		JFrame jf=new JFrame();
    		jf.setTitle("小球演示");//设置窗体标题
    		jf.setSize(900,900);//设置窗体大小
    		jf.setDefaultCloseOperation(3);//设置点击窗体右上角的叉叉后做什么操作,这里的3代表点击叉叉后关闭程序
    		jf.setLocationRelativeTo(null);//设置窗体居中显示
    		FlowLayout flow=new FlowLayout();//设置窗体布局为流式布局
    		jf.setLayout(flow);
    		
    		Mouse mou=new Mouse();//创建监听器对象
    		
    		JButton jbu=new JButton("START");//创建按钮,按下按钮后可以在画布中间画一个圆
    		jbu.addActionListener(mou);//为按钮添加事件监听器
    		jf.add(jbu);
    		
    		//设置窗体可见
    		jf.setVisible(true);
    		
    		//从窗体获取画布
    		g=jf.getGraphics();
    	}
    	
    	//创建内部类监听器(也可以重新创建一个文件编写该类)
    	class Mouse implements ActionListener{
    		//重写按钮监听方法
    		public void actionPerformed(ActionEvent e){
    			//按下按钮后会执行这里的代码,下面这条代码指的是在画布中心画一个圆
    			g.fillOval(300,300,300,300);
    		}
    	}
    }
    
    • 我们可以试着运行一下,出现以下图片所示效果第一步就成功了。

    第二步:让小球动起来

    • 用一段循环代码重复地画小球,每次循环让小球偏移一点距离
      我们在上述代码中的监听器类Mouse的按钮监听器方法actionPerformed(ActionEvent e)下加这样一段代码
    //重复画100次小球,每次横纵坐标分别加1
    for(int i=0;i<100;i++){
    	g.fillOval(300+i,300+i,30,30);
    	/*下面这段代码的意思是每执行一次循环,系统暂停30毫秒,否则画的
    	太快我们就观察不到小球在动了*/
    	try{
    		Thread.sleep(30);
    	}catch(Exception ef){
    	}
    }
    
    • 运行程序并点击START按键后,我们可以看到一个圆往右下角方向缓缓移动了一段距离,并且留下了痕迹。同时我们还可以发现,每次点击START键后,START键会保持被按下的状态,直至整个绘制小球的循环代码执行结束后才会弹起。这是因为我们现在写的程序只有一个线程在运行,所以只有当前任务执行完后按钮才能重新接收响应。想要解决这一点,可以利用下面将要讲到的多线程的原理。
      在这里插入图片描述

    那么,热身结束,下面让我们一起进入多线程的世界吧!

    一、如何让一个程序同时做多件事情?

    创建线程对象

    • 创建线程对象我们需要用到Thread类,该类是java.lang包下的一个类,所以调用时不需要导入包。下面我们先创建一个新的子类来继承Thread类,然后通过重写run()方法(将需要同时进行的任务写进run()方法内),来达到让程序同时做多件事情的目的。
    import java.awt.Graphics;
    import java.util.Random;
    
    public class ThreadClass extends Thread{
    	public Graphics g;
    	//用构造器传参的办法将画布传入ThreadClass类中
    	public ThreadClass(Graphics g){
    		this.g=g;
    	}
    	public void run(){
    		//获取随机的x,y坐标作为小球的坐标
    		Random ran=new Random();
    		int x=ran.nextInt(900);
    		int y=ran.nextInt(900);
    		for(int i=0;i<100;i++){
    			g.fillOval(x+i,y+i,30,30);
    			try{
    				Thread.sleep(30);
    			}catch(Exception ef){
    			}
    		}
    	}
    }
    
    • 然后我们在主类的按钮事件监听器这边插入这样一段代码,即每按一次按钮则生成一个ThreadClass对象
    public void actionPerformed(ActionEvent e){
    	ThreadClass thc=new ThreadClass(g);
    	thc.start();
    }
    
    • 在这里我们生成ThreadClass对象并调用start()函数后,线程被创建并进入准备状态,每个线程对象都可以同时独立执行run()方法中的函数,当run()方法中的代码执行完毕时线程自动停止。

    接下来我们试着运行一下吧!
    在这里插入图片描述

    加入清屏功能,让小球真正的动起来

    • 从上面的画图示范我们可以看出,小球在移动过程中是留下了轨迹的,那如果我只想看到小球的运动,不想看到小球的轨迹怎么办?
    • 很简单,我们只需要每次画新的小球之前先给整个画布画上一个大的背景色矩形,把原来的图案覆盖即可。

    让我们试着把run()方法中的代码改为下面这样:

    public void run(){
    	//获取一个随机数对象
    	Random ran=new Random();
    	//生成一对随机的x,y坐标设为小球的坐标,范围都在0-399
    	int x=ran.nextInt(400);
    	int y=ran.nextInt(400);
    	for(int i=0;i<100;i++){
    		//画一个能够覆盖画面中一块区域的白色矩形来清屏(把原来的笔迹都覆盖掉)
    		g.setColor(Color.white);
    		g.fillRect(300,300,300,300);
    		g.setColor(Color.black);
    		g.fillOval(200+x+i,200+y+i,30,30);
    		try{
    			Thread.sleep(30);
    		}catch(Exception ef){
    		}
    	}
    }
    

    让我们试着运行一下
    在这里插入图片描述

    • 我们运行后发现,小球确实在中间的白色矩形区域内实现了不留轨迹的运动,但是小球闪烁的非常厉害。这其中的原因有两个,一个是多个线程对象同时运行会产生冲突,另一个是IO设备使用频率过高。后者我们在稍后的部分讲到用双缓冲绘图去解决,前者则通过下面的方法解决。
    • 我们只在主类中创建一次ThreadClass对象。然后再创建一个列表,每次按按钮时将一组坐标存到这个列表中,最后通过run()方法中依次读出这个列表中的每一项并画出。
      在主类中创建ThreadClass对象并运行(主类的showFrame方法中插入以下代码)

    先创建坐标类

    public class Location {
    	public int x;
    	public int y;
    	public Location(int x,int y){
    		this.x=x;
    		this.y=y;
    	}
    }
    

    然后在主类和ThreadClass类中创建列表

    
    	public ArrayList<Location> locs=new ArrayList<Location>();
    

    然后在按钮监听器的方法下写入这段代码

    public void actionPerformed(ActionEvent e){
    	Random ran=new Random();
    	int x=ran.nextInt(400);
    	int y=ran.nextInt(400);
    	Location loc=new Location(x,y);
    	locs.add(loc);
    	System.out.println(locs.size());
    }
    

    然后将画布g和列表locs传入创建的线程对象中,在主类的showFrame方法插入以下代码。

    ThreadClass thc=new ThreadClass(g,locs);
    thc.start();
    

    重载Thread Class的run()方法

    public void run(){
    	while(true){
    		g.setColor(Color.white);
    		g.fillRect(300,300,300,300);
    		for(int i=0;i<locs.size();i++){
    			g.setColor(Color.black);
    			//每次给小球坐标偏移一下
    			int x=locs.get(i).x++;
    			int y=locs.get(i).y++;
    			g.fillOval(200+x,200+y,30,30);
    		}
    		try{
    			Thread.sleep(30);
    		}catch(Exception ef){
    		}
    	}
    }
    

    让我们再来试一下!
    在这里插入图片描述
    这下小球就没有闪烁的那么厉害了。

    二、如何让小球在画面中真实地动起来?

    • 众所周知,要想描述物体的运动状态,需要知道物体的三个物理量——位置、速度和加速度。我们只需要找到方法描述这三个物理量,便可以很好的模拟真实小球的运动。

    在这里我们可以创建一个Vector类来描述位置、速度和加速度这三个物理量

    public class Vector {
    	public int x;
    	public int y;
    	public Vector(int x,int y){
    		this.x=x;
    		this.y=y;
    	}
    	//向量的加和运算
    	public void add(Vector vec){
    		this.x+=vec.x;
    		this.y+=vec.y;
    	}
    }
    

    然后我们再创建一个Ball类来代表小球(move函数是本部分的关键)

    public class Ball {
    	public Vector location;//位置
    	public Vector speed;//速度
    	public Vector acce;//加速度
    	
    	//构造器传参,设定小球的基本参数
    	public Ball(Vector location,Vector speed,Vector acce){
    		this.location=location;
    		this.speed=speed;
    		this.acce=acce;
    	}
    	
    	//小球移动,这是整个部分的关键!!!每画完一次小球就调用一次move函数,让小球依据速度和加速度来改变一次位置
    	public void move(){
    		this.speed.x+=acce.x;//每调用一次move函数小球的速度就和加速度做一次加法
    		this.speed.y+=acce.y;
    		this.location.x+=speed.x;//每调用一次move函数小球的位置坐标就和速度做一次加法
    		this.location.y+=speed.y;
    	}
    }
    

    有了这两个类,我们就可以表示任意二维的向量运动了

    • 比如说从原点出发,向右速度为5,向下加速度为10的平抛运动可以表示为
    Vector location=new Vector(0,0);
    Vector speed=new Vector(5,0);
    Vector acce=new Vector(10,0);
    
    • 从原点出发,向右速度为5,向上速度为10,向下加速度为10的上抛运动可以表示为
    Vector location=new Vector(0,0);
    Vector speed=new Vector(5,10);
    Vector acce=new Vector(10,0);
    
    • 利用这个原理,我们已经可以做出一点好玩的东西了!
      试想一下,我们可以先给窗体添加一个鼠标监听器,然后获取鼠标按下松开的点的坐标,然后沿着按下和松开的点连成的直线方向丢出一个小球,这样是不是就可以做一个投篮游戏了呢。
    • 具体操作:给窗体加上鼠标监听器👉在mousePress函数下获取鼠标按下的点的坐标x1,y1👉在mouseRelease函数下获取鼠标松开的点的坐标x2,y2👉生成一个小球对象,以(x2,y2)作为小球坐标,(x2-x1)作为x方向上的速度,(y2-y1)作为y方向上的速度,y方向上加速度为1。然后把这个小球放入到传入ThreadClass的列表中,让线程将这个小球画出。

    需要改变的是主类中Mouse类的代码和ThreadClass类中run方法的代码

    • Mouse类
    //创建内部类监听器(也可以重新创建一个文件编写该类)
    	class Mouse implements ActionListener,MouseListener{
    		int prx=0;
    		int pry=0;//记录按下鼠标的点的坐标
    		
    		//重写按钮监听方法
    		public void actionPerformed(ActionEvent e){}
    
    		public void mouseClicked(MouseEvent e) {}
    
    		public void mousePressed(MouseEvent e) {
    			prx=e.getX();
    			pry=e.getY();//获取按下鼠标的点的坐标
    		}
    
    		public void mouseReleased(MouseEvent e) {
    			int speedx=(int)((e.getX()-prx)/10);
    			int speedy=(int)((e.getY()-pry)/10);
    			
    			Vector location=new Vector(e.getX(),e.getY());
    			Vector speed=new Vector(speedx,speedy);
    			Vector acce=new Vector(0,1);
    			Ball ball=new Ball(location,speed,acce);
    			balls.add(ball);
    		}
    		public void mouseEntered(MouseEvent e) {}
    		public void mouseExited(MouseEvent e) {}
    	}
    
    • ThreadClass下的run方法
    public void run(){
    		while(true){
    			g.setColor(Color.white);
    			g.fillRect(300,0,600,900);
    			for(int i=0;i<balls.size();i++){
    				g.setColor(Color.black);
    				g.fillOval(balls.get(i).location.x,balls.get(i).location.y,30,30);
    				balls.get(i).move();
    			}
    			try{
    				Thread.sleep(30);
    			}catch(Exception ef){
    			}
    		}
    	}
    

    执行效果如图所示(为了显示小球向量运动的效果,这里省去了清屏操作)
    ![](https://img-blog.csdnimg.cn/20210129145320124.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDcxMDQxMg==,size_16,color_FFFFFF,t_7
    完整代码放到这里:
    https://pan.baidu.com/s/10HcOSvuov14moes1jPe9JQ
    提取码:z8ii

    三、多线程游戏仿真实例分享

    (三个游戏的源代码、图片素材链接在下文中获取)

    游戏一:飞机大战

    游戏演示:

    Java游戏制作

    游戏说明:
    在这里插入图片描述

    飞机大战简介:

    • 飞机大战整个程序一共用了五个类GameUIFrame(游戏界面窗体显示及主程序)、ThreadClass(线程类,进行图片绘制、生成怪物、判断碰撞、刷新分数等一系列功能,是程序的主体部分)、FlyObject(创建所有飞行物对象的类,可以定义飞行物的位置、速度、加速度、显示图片等)、Vector(上文中介绍过的向量类)以及Listener(鼠标监听器,负责获取鼠标在屏幕上的点绘制我方飞船和生成飞船发出的子弹)。

    要实现飞机大战主要完成这几件事:

    1. 绘制我方飞机、不断发射子弹
    2. 不断随机生成怪物、宝箱
    3. 判断子弹与怪物、怪物与我方飞机之间是否碰撞
    4. 爆炸动效、刷新分数

    由于时间关系,目前博主所制作的游戏暂时只具有以上这些功能,有兴趣的伙伴还可以试着增加关卡、Boss、新的怪物(比如会发射子弹的怪物)、剧情等等。
    我们在游戏进行的过程中,难免会生成大量的图片对象。前面我们讲到,当我们需要在屏幕上绘制的图像过多时会出现卡顿闪屏现象,第二个解决方法就是双缓冲绘图,下面我来简单的介绍一下。

    双缓冲绘图解决闪屏

    • 我们正常画图的时候,是从窗体对象中直接获取Graphics类对象来绘画,然后每一次把需要画的图形传输到我们的屏幕上时,都需要占用一定的输入输出(IO)设备通道。所以当我们需要绘制的图像过多时将导致IO设备使用频率过高,屏幕就会出现闪屏现象。
    • 双缓冲绘图就是一次性把所有要画的对象先画到内存中,最后再把内存中的图片用窗体对象中直接获取Graphics类对象画出来。
    • 打个比方,比如说我要把地上的落叶全部扫进垃圾桶里,我把地上的树叶一片一片直接捡到垃圾桶里,这就是不用双缓冲绘图的情况;如果说我有一个垃圾铲,先把树叶捡到垃圾铲里,然后再一次性倒进垃圾桶,效率是不是高多了?这就有点类似双缓冲绘图的原理。

    要实现双缓冲绘图,首先我们要创建BufferedImage对象,然后从这个缓存对象中获取画布:

    //创建缓存
    BufferedImage bufImg=new BufferedImage(1200,1200,BufferedImage.TYPE_INT_ARGB);
    //最后的TYPE_INT_ARGB代表创建的是具有合成整数像素的 8 位 RGBA 颜色分量的图像,也可以选择其他类型,详见Java的API文件
    //获取缓存上的画布
    Graphics bufg=bufImg.getGraphics();
    

    获取了Graphics bufg后,我们所有的绘图操作先在bufg上完成,等一轮图像画完之后,再把bufg上的图像画到原本的Graphics对象中

    g.drawImage(bufImg,0,0,null);
    

    飞机大战制作Step1:飞行物

    • 游戏中真正的飞行物一共只有三种,我方飞机、怪物和子弹(均需要在创建FlyObject对象时设定位置、速度、加速度、图片、血量)。但是因为宝箱、爆炸特效需要定义的参数比真正的飞行物要少(只需要位置和图片),所以宝箱和爆炸特效也可以使用FlyObject类创建。
    • 我们可以为上面五类飞行物各创建一个列表,用于存放其对象;每次需要生成一架飞机或者一个怪物时,就往对应的列表中放入一个对象。然后在线程的run方法中依次将每个列表中的所有对象全部画出
    • 在FlyObject类中,最重要的是FlyObject的构造方法、move方法(负责计算飞行物的下一个坐标)和drawFO方法(传入画布,将飞行物图片画到飞行物的坐标上)。
    //有图片、有血量的飞行物
    public FlyObject(Vector location,Vector speed,Vector acce,String imgName,int HP){
    	this.location=location;//位置
    	this.speed=speed;//速度
    	this.acce=acce;//加速度
    	this.HP=HP;//血量
    	
    	this.imgName=fileAddress+imgName;//图片地址
    	ImageIcon imgicon=new ImageIcon(this.imgName);//如果我们想要在画布上画一张图片,可以先用图片地址创建一个ImageIcon对象,然后再从这个对象中获取Image对象
    	img=imgicon.getImage();
    }
    //前面介绍过的move方法
    public void move(){
    		speed.add(acce);
    		location.add(speed);
    }
    //将飞行物的图片画到画布上
    public void drawFO(Graphics g){
    //如果被绘制的对象有图片就画图片,没图片就画一个圆
    	if(imgName!=null){
    //			System.out.println(imgName);
    		g.drawImage(img,location.x, location.y,null);
    	}else{
    		g.fillOval(location.x, location.y,10,10);
    	}
    }
    
    • 游戏过程中,我方飞船是始终跟随着鼠标共同移动的。要实现这一点,我们需要在Listener类中实现MouseMotionListener,然后重写鼠标移动mouseMoved方法(记得最后要给窗体添加MouseMotionListener监听器)。当鼠标在窗体中进行移动时,该方法会不断地获取鼠标在窗体中的坐标,参考下面这段代码重写mouseMoved方法:
     public void mouseMoved(MouseEvent e){
        	Vector location=new Vector(e.getX(),e.getY());
        	FlyObject mp=new FlyObject(location,null,null,"我机.png");
        	mps.add(mp);//mps是存放我方飞机对象的列表ArrayList<FlyObject> mps
        }
    
    • 这样一来,在移动鼠标的过程该方法会被不停的调用,并且不停的往mps列表中存放我放飞机对象。在线程类的run方法中,我们每一次只需要获取该列表的最后一项(我方飞机的最新坐标)将其画出即可。
    • 因为子弹是我方飞机发射出来的,所以子弹生成坐标只需要取我方飞机的坐标即可。
    //不断发射子弹
    	public void generateBullet(){
    	//隔一段时间就生成一些子弹(int len是一个计数器,它记录的是run方法中的运行次数,所有代码跑完一次就加一)
    			if(len%5==0){
    		    	for(int i=0;i<4;i++){
    		    	//设定子弹坐标
    		    		Vector location_fo=new Vector(mps.get(mps.size()-1).location.x,mps.get(mps.size()-1).location.y+20*i);
    		        	Vector speed_fo=new Vector(100,0);//设定子弹速度
    		        	Vector acce_fo=new Vector(0,0);//设定子弹加速度(这里把加速度设为0意思就是让子弹做匀速运动)
    		        	FlyObject fo=new FlyObject(location_fo,speed_fo,acce_fo,"子弹.png",1);
    		        	fos.add(fo);
    		    	}
    			}
    	}
    
    • 怪物的生成就更简单了,在游戏设定中,怪物会从界面的最右边被生成,一直往窗体的最左边走,纵坐标和速度是随机的。
    public void generateEnemy(){
    	if(len%20==0){
    		Random ran=new Random();
    		//怪物的横坐标是固定的(窗体的最右边),纵坐标是随机的
    		int loc_y=ran.nextInt(900)+100;
    		//怪物只在x方向有速度
    		int spd_x=-ran.nextInt(10)-10;
    		Vector location=new Vector(1200,loc_y);
    		Vector speed=new Vector(spd_x,0);
    		Vector acce=new Vector(0,0);
    		FlyObject enemy=new FlyObject(location,speed,acce,"怪物.png",5);
    		enemys.add(enemy);
    	}
    }
    

    飞机大战制作Step2:判断碰撞

    • 判断两个物体是否碰撞,我这里用到的原理是判断两个飞行物坐标的距离是否小于一定的值(比如说图片宽度)。而且我们每一轮都要判断每一个子弹和每一个怪物的距离,都要判断我方飞机和怪物的距离等等。我们的所有飞行物都被放入了列表中,所以我们需要建立循环拆解列表,将其中的元素逐个取出,逐个比较
    //判断子弹是否击中怪物、怪物是否触碰我机、是否拾得宝箱
    public void judgeAttack(Graphics bufg_judgeAttack){
    	//判断子弹是否击中怪物
    	for(int i=0;i<enemys.size();i++){
    		//取出怪物对象
    		FlyObject en=enemys.get(i);
    		for(int j=0;j<fos.size();j++){
    			//取出子弹对象
    			FlyObject fo=fos.get(j);
    			//获取子弹和怪物的坐标位置
    			int fo_x=fo.location.x;
    			int fo_y=fo.location.y;
    			int en_x2=en.location.x;
    			int en_y2=en.location.y;
    			
    			//计算怪物和子弹之间的距离(也可以采用if(横坐标的差值<某数&纵坐标的差值<某数)
    			int distance_fo_en=(int)Math.sqrt(Math.pow((fo_x-en_x2),2)+Math.pow((fo_y-en_y2),2));
    			if(distance_fo_en<=50){
    				//这里en(怪物)的HP是血量,fo(子弹)的HP是伤害值。
    				en.HP-=fo.HP;
    				//在该子弹位置添加一个子弹爆炸效果,后面会介绍
    				explosion(fos.get(j));
    				//将该子弹从列表中移除
    				fos.remove(j);
    				if(en.HP<=0){
    					//怪物爆炸效果
    					explosion(enemys.get(i));
    					//这里如果直接用enemys.remove(i)会导致循坏for(int j=0;j<fos.size();j++)继续执行,误删其他元素
    					enemys.get(i).img=null;
    					//把怪物图片去除(每次画图就不画该怪物了),然后把它移出屏幕
    					enemys.get(i).location=new Vector(-1000,0);
    					if(en.imgName.equals(fileAddress+"怪物.png")){
    						score+=10;
    					}else if(en.imgName.equals(fileAddress+"怪物2.png")){
    						score+=50;
    					}
    				}
    				
    			}
    			
    		}
    	}
    }
    

    飞机大战制作Step3:爆炸动效

    • 前面说到过爆炸动效也可以放进FlyObject类列表中,完成爆炸动效需要写两个方法,一个方法生成爆炸动效对象,一个方法绘制爆炸动效。因为爆炸动效一般都是在子弹或者怪物消失的时候才会生成,所以只生成一次;但是绘制爆炸动效需要多次绘制,所以生成爆炸动效对象和绘制爆炸动效需要分成两个方法来写。

    • 生成爆炸动效的方法传入的是飞行物的对象,因为绘制爆炸动效至少需要两个元素:爆炸发生在哪里,生成什么爆炸效果(怪物的爆炸效果和子弹的爆炸效果不同)。所以首先我们对该飞行物的图片名称进行一个判断(判断是什么东西爆炸),然后取出它的坐标。最后生成一个对应的爆炸效果对象放入列表中。
      在这里插入图片描述

    • 爆炸效果是一种动态效果,所以还涉及到切换图片的操作。我们可以将预先准备好的几张图片同意文件名格式并编好序号,方便每画完一次图片就切换一张。

    	//爆炸动效
    	public void explosion(FlyObject flo){
    	//判断是什么对象爆炸
    		if(flo.imgName.equals(fileAddress+"怪物.png")|flo.imgName.equals(fileAddress+"怪物2.png")){
    		//获取爆炸对象的坐标
    			int x_explo=flo.location.x;
    			int y_explo=flo.location.y;
    			Vector location=new Vector(x_explo,y_explo);
    			//生成爆炸动效对象
    			FlyObject explo=new FlyObject(location,null,null,"爆炸_1.png",10);
    			//将爆炸动效对象添加到列表中
    			explotions.add(explo);
    		}
    	}
    	
    	//绘制爆炸动效
    	public void drawExplo(Graphics bufg_explotion){
    	//依次将列表中的每个爆炸图像画出
    		for(int i=0;i<explotions.size();i++){
    			explotions.get(i).drawFO(bufg_explotion);
    			//这里的HP表示的是这个爆炸效果持续的时间,每画一次效果HP减一,当HP等于0时停止绘制该爆炸效果
    			explotions.get(i).HP--;
    			
    			if(explotions.get(i).imgName.equals(fileAddress+"爆炸_1.png")){
    				//下面这条代码的作用是每画完一次图像就更换一次图片,以此达到动态变化的效果
    				ImageIcon imgicon=new ImageIcon(fileAddress+"爆炸_"+((explotions.get(i).HP%3)+1)+".png");//因为我绘制的爆炸效果图片一共有三张,所以这里取除以三的余数来设定图片的文件名
    				explotions.get(i).img=imgicon.getImage();
    			}
    			//当爆炸动效对象的HP等于0时移除该对象
    			if(explotions.get(i).HP==0){
    				explotions.remove(i);
    			}
    		}
    	}
    

    飞机大战制作Step4:游戏暂停/继续,判定游戏结束

    • 我们想要的效果:在游戏画面的左下角有一个暂停键,我们点击暂停键时游戏会进入暂停状态,再点击开始游戏会恢复到暂停之前的状态。在这里插入图片描述

    • 我们需要做的操作:我们可以创建一个布尔值对象gameRest(布尔值只有true和false两种状态),初始值设定为false,每一轮线程运行时都需要先判断一下gameRest值是否为false,如果为true,则跳过绘制飞行物、判断碰撞等操作;如果为false,则继续正常运行。

    • 接着我们写一个方法来改变gameRest的值,这样我们每调用一次方法就切换一次gameRest的值

    //游戏暂停/开始
    public void on_off(){
    	gameRest=!gameRest;
    }
    
    • 在鼠标监听器中添加一个监听事件,在mouseReleased方法下我们可以判断一下鼠标松开的坐标是否落在画面左下角这块区域,如果是就调用on_off方法来改变gameRest的值。
    • 当我们的飞船与怪物相碰时,飞船坠落,游戏结束,这里同样用到了一个布尔值gameOver
    //判断游戏是否结束
    	public void judgeGameOver(Graphics g_judgeGameOver){
    		for(int i=0;i<enemys.size();i++){
    			FlyObject en=enemys.get(i);
    			
    			FlyObject mp=mps.get(mps.size()-1);
    			
    			int mp_x=mp.location.x;
    			int mp_y=mp.location.y;
    			int en_x=en.location.x;
    			int en_y=en.location.y;
    			
    			int distance_mp_en=(int)Math.sqrt(Math.pow((mp_x-en_x),2)+Math.pow((mp_y-en_y),2));
    			if(distance_mp_en<=60){
    				//绘制gameOver图片
    				ImageIcon imgicon_gamover=new ImageIcon(fileAddress+"gameover.png");
    				Image img_gamover=imgicon_gamover.getImage();
    				g_judgeGameOver.drawImage(img_gamover,0,0,null);
    				gameOver=true;
    			}
    		}
    	}
    
    • 最后在run方法中插一段判断gameOver的代码
    if(gameOver==true){break;}
    

    飞机大战制作Step5:刷新分数

    • 将游戏分数的万位、千位、百位和十位和个位分别取出,然后每个数字对应显示一张图片,将刷新分数的方法写入run方法中,每一轮刷新一次分数。(Java中的符号“/”代表整除)
    //获取万位
    int number_5=score/10000;
    //获取千位
    int number_4=(score-number_5*10000)/1000;
    //获取百位
    int number_3=(score-number_5*10000-number_4*1000)/100;
    //获取十位
    int number_2=(score-number_5*10000-number_4*1000-number_3*100)/10;
    //获取个位
    int number_1=score-number_5*10000-number_4*1000-number_3*100-number_2*10;
    
    • 同样的,将每个数字的图片素材同一文件名格式并编号

    在这里插入图片描述

    • 这里的fileAddress是我存放图片素材的目录,这样当我更换图片目录时只需要更改这一个值就可以了。
    //生成图片对象
    ImageIcon imgicon_score=new ImageIcon(fileAddress+"Score.png");
    Image img_score=imgicon_score.getImage();
    ImageIcon imgicon5=new ImageIcon(fileAddress+number_5+".png");
    Image img5=imgicon5.getImage();
    ImageIcon imgicon4=new ImageIcon(fileAddress+number_4+".png");
    Image img4=imgicon4.getImage();
    ImageIcon imgicon3=new ImageIcon(fileAddress+number_3+".png");
    Image img3=imgicon3.getImage();
    ImageIcon imgicon2=new ImageIcon(fileAddress+number_2+".png");
    Image img2=imgicon2.getImage();
    ImageIcon imgicon1=new ImageIcon(fileAddress+number_1+".png");
    Image img1=imgicon1.getImage();
    
    //bufg_score是该方法导入的Graphics类画布
    bufg_score.drawImage(img_score, 340,50,null);
    bufg_score.drawImage(img5, 590,50,null);
    bufg_score.drawImage(img4, 650,50,null);
    bufg_score.drawImage(img3, 710,50,null);
    bufg_score.drawImage(img2, 770,50,null);
    bufg_score.drawImage(img1, 830,50,null);
    
    • 第一个游戏案例的分享差不多就到这里,如果有什么描述不够清楚的地方欢迎大家在评论区留言。也可以点击下方链接,下载我这三个游戏的全部源代码和游戏素材进行参考

    游戏源代码及游戏素材链接——提取码:hjzd

    • 这个游戏目前来说做得还非常粗糙,还有一些小漏洞和可以优化的地方,如果有小伙伴下载了我的代码,发现有什么好的建议欢迎私信或在评论区中指出。欢迎交流,您的评论将给我的学习之路带来巨大帮助。

    可以优化的地方:

    1. 有的子弹会穿过怪物,或者有时候碰到怪物没有死,说明判断碰撞和物体移动的方法还有缺陷。
    2. 怪物血量减少到一定程度时出现破损效果,这样看起来对怪物剩余血量更直观
    3. 游戏玩法比较单一,可以给飞机适当增加新的技能,增加关卡和Boss,丰富玩法
    4. 缺少游戏开始界面、背景音乐、音效等
    5. 游戏玩到后期比较卡顿,因为飞出窗体的子弹、怪物等仍然存在列表中,每次绘制图片时都要将这些看不见的对象重新再画一遍,十分消耗性能。

    游戏二:接豆人

    游戏演示:

    Java原创游戏分享

    游戏介绍:
    在这里插入图片描述

    • 接豆人游戏和飞机大战玩法虽然差异比较大,但是用到的代码原理其实是类似的。

    • 黄色的吃豆人的移动,同样是依靠鼠标监听器的mouseMoved方法不断获取鼠标的坐标然后绘制接豆人的图像。只不过这次我们只获取鼠标的横坐标,纵坐标设定为一个定值,这样就可以实现我们的接豆人只做水平方向的运动了。
      在这里插入图片描述

    • 接豆人吃到宝石和道具的判断,和飞机大战中的判断碰撞是类似的;接豆人中随机掉落的宝石、炸弹和道具,与飞机大战中刷新怪物是类似的。

    • 两个游戏比较不同的地方是,在接豆人游戏中如果吃到了蜘蛛或者金币礼包是会触发新事件的。而且在接豆人中也增加了玩家的生命值。

    • 大致总结一下,实现接豆人需要完成这几件事:让接豆人在水平方向跟随鼠标移动👉随机生成宝石、炸弹蜘蛛和道具,并且赋予下落物体一个垂直方向的加速度,增加真实感👉判断接豆人是否接到了掉落物👉给金币礼包和蜘蛛添加触发效果(下金币雨和接豆人进入眩晕)👉游戏暂停、游戏结束后重新开始👉间隔一段时间清理一下飞行物列表,提高游戏流畅度

    • 和飞机大战类似的地方就不再赘述,这里介绍一些不同的地方

    接豆人制作Step1:金币礼包和蜘蛛的触发效果

    • 首先我们需要定义四个变量
    	public int rewardTime;//奖励时间
    	public Boolean pause=false;//是否进入眩晕状态
    	public int pauseTime;//眩晕时间
    	public FlyObject mp_pause;//用于绘制眩晕时接豆人的图片
    
    • 在判断碰撞的方法下面,如果接豆人碰到的是礼物,则给rewardTime加上200,如果是蜘蛛,则给pauseTime加上200,且将pause的值改为true。

    在这里插入图片描述

    • 在生成下落物的方法中,我们先对rewardTime进行一个判断,如果rewardTime大于0,就下金币,如果小于等于0,就生成其他掉落物。
    //生成下落物
    public void generateDrop(){
    	if(rewardTime>0){
    		if(len%1==0){
    			rewardTime--;//每次rewardTime递减
    			Random ran=new Random();
    			Vector location=new Vector(ran.nextInt(750)+50,50);
    			Vector speed=new Vector(0,ran.nextInt(1)+10);
    			Vector acce=new Vector(0,2);
    			
    			FlyObject fo=new FlyObject(location,speed,acce,"金币1.png");
    			fos.add(fo);
    		}
    	}else{//生成其他掉落物
    	}
    
    • 当接豆人进入眩晕状态时,身边的掉落物还是正常掉落的,但是接豆人在眩晕状态下不能移动、不能接取掉落物。所以我们需要在绘制接豆人和判断碰撞的方法下分别先对pause的值进行一个判断,如果pause为false则正常运行。
    //绘制我机
    public void draw_mp(){
    	if(pause==false){
    		if(mps.size()-5>=0){
    			FlyObject mp=mps.get(mps.size()-5);
    			mp.drawFO(bufg);
    		}
    	}else{
    		//当pause为true时执行
    		pauseTime--;//pauseTime递减
    		mp_pause.drawFO(bufg);//绘制接豆人眩晕时的图片
    		if(pauseTime==0){
    			pause=false;//当pauseTime减少到0时将pause改回为false
    		}
    	}
    }
    

    接豆人制作Step2:游戏重新开始

    • 这个功能的实现和飞机大战中说过的暂停功能非常类似,我们需要创建一个布尔值gameOver,然后当生命值减少到0时将gameOver改为true。然后屏幕上显示gameOver的图像
      在这里插入图片描述
    • 当我们点击该区域时,将gameOver的值改回为false,并且将所有的飞行物列表、分数、生命值等全部恢复到游戏刚开始的状态。
    if(thc.gameOver){
    	if(e.getX()>340&e.getX()<540&e.getY()>630&e.getY()<710){
    		thc.life=3;
    		thc.fos.removeAll(fos);
    		thc.mps.removeAll(mps);
    		thc.score=0;
    		thc.gameOver=false;
    	}
    }
    

    接豆人制作Step3:清理列表数据,提升流畅度

    • 我们每间隔一段时间就把超出窗体可见范围的飞行物都从列表中删去,防止游戏后期需要画的飞行物太多导致卡顿。
    //清理缓存(在run方法中调用该方法)
    public void clear(){
    	//每500轮清理一次
    	if(len%500==0){
    		System.out.println("清理前:");
    		System.out.println("fos size is"+fos.size());
    		System.out.println("mps size is"+mps.size());
    		System.out.println("exps size is"+explotions.size());
    		clearList(fos,0);
    		clearList(mps,1);
    		clearList(explotions,0);
    		System.out.println("清理后:");
    		System.out.println("fos size is"+fos.size());
    		System.out.println("mps size is"+mps.size());
    		System.out.println("exps size is"+explotions.size());
    	}
    }
    	
    //清理列表(传入需要清理的列表,并传入清理类型)
    public void clearList(ArrayList<FlyObject> fos,int flag){
    	int fos_size=fos.size();
    	//其他飞行物类型的清理
    	if(flag==0){
    		for(int i=fos.size()-10;i>-1;i--){
    			//判断一下从哪个飞行物开始超出窗体可见范围(在它之前的飞行物一定是超过了)
    			if(fos.get(i).location.y>1000){
    				for(int j=0;j<i;j++){
    					fos.remove(0);//不断删除列表的第一个元素,直到删到开始超出窗体范围的那一个
    				}
    				break;
    			}
    		}
    	//接豆人的列表的清理
    	}else if(flag==1){
    		//只保留列表中最后一百个元素,前面的全部删除
    		for(int i=0;i<fos_size-100;i++){
    			fos.remove(0);
    		}
    	}
    }
    

    游戏源代码及游戏素材链接——提取码:hjzd

    游戏三:双线挑战(双人游戏)

    游戏截图:
    在这里插入图片描述
    游戏介绍:

    • 这个游戏和上面两个游戏不太一样,它是一个使用键盘操控的双人小游戏。操作方法有点类似贪吃蛇,两个人分别操控一条线,当触碰到游戏边界或者自身及对手的线时,游戏结束。所以在游戏过程中,双方可以尽可能地把对方包围在一个比较小的空间里,使自己成为最后的赢家。
    • 虽然这个游戏的画面设计比较粗糙,但是这次增加了游戏开始界面、游戏背景音乐的播放功能,仍然非常有意思。
    • 这个游戏一共做了三个版本
      在这里插入图片描述

    游戏皮肤:在这里插入图片描述

    双线挑战制作Step1:键盘监听器的使用

    • 键盘监听器的接口是KeyListener,我们主要用到keyPress和keyReleased两个方法,他们分别在键盘按下和键盘松开时被调用。
    class Listener implements KeyListener{
    	public void keyTyped(KeyEvent e) {
    	}
    	public void keyPressed(KeyEvent e) {
    	//获取按下键的keycode
    		int keyc=e.getKeyCode();
    		System.out.println(keyc+" is pressed!");
    		//也可以使用String press=e.getKeyChar()+"";这样获取到的就是键盘的字符
    	}
    	public void keyReleased(KeyEvent e) {
    	//获取松开键的keycode
    		int keyc=e.getKeyCode();
    		System.out.println(keyc+" is released!");
    	}
    }
    
    • 然后一定要记得给窗体加上监听器对象!而且键盘的监听相较于鼠标监听器还有一个特殊的地方,键盘的监听器需要焦点,键盘监听器需要获取焦点发生的动作事件。比如说我们的qq登陆界面上有两个输入框,如果我们直接敲击键盘,此时电脑是不知道我们需要输入的是账号还是密码。只有我们点击账号文本框后,才能让账号文本框得到焦点,从而顺利输入我们的账号。

    在这里插入图片描述

    • 同时,窗体获取焦点的代码也必须放在窗体可见之后,否则无法正常监听键盘事件
    //设置窗体可见,jf是创建的JFrame对象
    jf.setVisible(true);
    jf.addKeyListener(mou);//为窗体添加键盘监听器
    jf.requestFocusInWindow();//窗体获得焦点,记得要放在窗体可见之后
    
    • 现在让我们一起来做几个小实验,对键盘监听器的原理进一步了解(记得在实验前将键盘调为英文输入模式,否则无法正常监听英文键的输入):连续敲击F键;长按F键;慢速交替敲击F和D键;同时按下F和D键;快速交替敲击F和D键。
    • 下面是博主测出的结果(F键的keycode为70,D键的keycode为68):
      在这里插入图片描述
    • 看出来这几种按键方式的特点了吗?我们使用键盘与程序交互时,这些按键方式反馈的差异会给我们带来很大帮助。比如说有的游戏中同时按下W和D键是向右上跳,只按W键是向上跳,只按D键是向右走。我们就可以在键盘监听器中先判断用户是同时按了W,D键还是先按了D键再按W键,从而决定让角色向右上跳,还是先向右走再向垂直上跳。

    双线挑战制作Step2:用键盘控制线条的走向

    • 因为在双线挑战游戏中,我们是需要线条留下轨迹的,所以我们的游戏背景图片只需要画一次(否则就会把轨迹覆盖了)。那怎么让图片只画一次呢?
    //我们可以先定义一个整数flag1
    public int flag1=0;
    
    • 在画图之前先判断一下这个值是不是0,是0的话说明没有被画过;在画图的代码中,记得将flag1改为除0以外的数,表示这个图已经被画过一次了
    	//只画一次图片
    public void draw_just_once(int type){
    //如果说flag1为0,则开始画图
    	if(flag1==0){
    		ImageIcon imgic=new ImageIcon(fileAddress+"游戏背景_2.png");
    		Image img=imgic.getImage();
    		g.drawImage(img, 0,0,null);
    		flag1++;//更改flag1的值,表示图已画过
    	}
    }
    
    • 当我们的背景只画一次,而小方块又在不停的移动时,小方块自然就留下了轨迹;对于实现小方块移动的方法,和飞机大战中的FlyObject类、Vector类差不多。
    • 我们先创建一个从窗体的左上角出发的小方块,速度向右为1(这里的LineBall类和FlyObject原理及代码基本相同(move方法、Vector类的使用等),不太清楚的小伙伴可以回到飞机大战Step1看一看)
    lb_blue=new LineBall(new Vector(0,0),new Vector(1,0));
    
    • LineBall的drawLB方法和FlyObject的drawFO方法稍有不同。因为我这里用的小方块的图片是5个像素,所以说小方块的location每加1,我就让小方块的坐标向右移5个像素。
    public void drawLB(Graphics g){
    	if(imgName==null){
    		g.fillRect(location.x*10+50, location.y*10+50, 10,10);
    	}else{
    		ImageIcon imgic=new ImageIcon(fileAddress+imgName);
    		Image img=imgic.getImage();
    		g.drawImage(img,location.x*10+50, location.y*10+70, null);
    	}
    }
    
    • 然后在线程中运行这一段代码
    	lb_blue.imgName="蓝_4.png";
    	lb_blue.drawLB(g);//画完以后让小方块move移动一次
    	lb_blue.move();
     try{
    	 Thread.sleep(50);
     }catch(Exception ef){}
    
    • 运行效果大概是这样的
      在这里插入图片描述
    • 现在如果我们想让小方块改变它的移动方向,只需要改变对象lb_blue的speed值即可(比如说想让小方块往下走,那speed就改成(0,1);想往左走,那就改成(-1,0)。
    • 因为我们要使用键盘操控,所以我们必须获取WASD和上下左右键的keycode(可以使用双线挑战制作Step1中的方法自己试验一下,把这8个按键都按一遍就知道它们的keycode了,需要知道其他按键的keycode也可用此方法)。
    • 在键盘监听器的keyReleased方法下去判断按键及做出响应
    public void keyReleased(KeyEvent e) {
    	int keyc=e.getKeyCode();
    	System.out.println(keyc+" is released!");
    	int speed=1;
    	if(lb_blue.len!=0){
    		if(lb_blue.speed.y==0){
    			if(keyc==87){//w
    				lb_blue.len=0;
    				lb_blue.speed=new Vector(0,-speed);
    			}
    			if(keyc==83){//s
    				lb_blue.len=0;
    				lb_blue.speed=new Vector(0,speed);
    			}
    		}
    		if(lb_blue.speed.x==0){
    			if(keyc==65){//a
    				lb_blue.len=0;
    				lb_blue.speed=new Vector(-speed,0);
    			}
    			if(keyc==68){//d
    				lb_blue.len=0;
    				lb_blue.speed=new Vector(speed,0);
    			}
    		}
    	}
    }
    
    • 博主这里还用到了几个判断,在这里我给大家解释一下。
    • if(lb_blue.speed.y==0)/if(lb_blue.speed.x==0):这里的判断是,假如小方块目前正在往左走或者往右走(即y方向速度为0)时,才可以向上或者向下拐(不然就会出现本来在往上走,按了向下键后突然原地掉头,在这个游戏设定中是不符合规则的)后面的判断x方向速度同理。在这里插入图片描述
    • if(lb_blue.len!=0):这里的len代表的是小方块在当前方向行走的距离,每更改一次方向len就清零一次。它的意思是小方块更改方向后,必须往更改后的方向至少前进一格才能再次更改方向,否则仍然有可能出现“原地掉头”的操作。

    双线挑战制作Step3:利用二维数组设定“棋盘”

    • 回顾一下双线挑战最重要的游戏规则:玩家线条不能够触碰到边界、不能触碰对方和自身的线条。

    在这里插入图片描述

    • 大家觉得这种判定方法是不是像在下棋?两个玩家就像顺着小方块的移动方向不停的摆棋子(小方块),当下一个要摆的棋子超出了边界,或者摆在了原来有棋子的格子上时,游戏结束。
    • 二维数组的特点就十分符合我们的需求,比如说我们创建了一个大小为70*70的棋盘chessBoard。(横纵坐标范围均为0-69)
    public static int[][] chessBoard=new int [70][70];
    
    • 我们可以用chessBoard[x][y]=?来表示棋盘上坐标为(x,y)的格子里面装的是什么,这里我们用0代表空1代表这里有棋子。那么chessBoard[8][9]=0就代表坐标(8,9)的格子里没有棋子;chessBoard[7][6]=1就代表(7,6)的格子里已经放有棋子了。(二维数组在创建的时候默认每个位置的值都是0,也就是没有棋子)
    //判断游戏是否结束
    public Boolean judge_gameover(){
    	//判断棋子是否超出边界
    	if(location.x>69|location.y>69|location.x<0|location.y<0){
    		gameOver=true;
    		return true;
    	//判断棋子要放下的位置上原本有没有棋子
    	}else if(chessBoard[location.x][location.y]==1){
    		gameOver=true;
    		return true;
    	//如果上面两种情况都不是,则返回false
    	}else{
    		gameOver=false;
    		return false;
    	}
    }
    
    • 同时,我们需要修改一下drawLB的方法
    public void drawLB(Graphics g){
    	if(imgName==null){
    	//当棋子走到某一格时,将棋盘的这一格状态改为“有棋子”
    		chessBoard[location.x][location.y]=1;
    		g.fillRect(location.x*10+50, location.y*10+50, 10,10);
    	}else{
    	//当棋子走到某一格时,将棋盘的这一格状态改为“有棋子”
    		chessBoard[location.x][location.y]=1;
    		ImageIcon imgic=new ImageIcon(fileAddress+imgName);
    		Image img=imgic.getImage();
    		g.drawImage(img,location.x*10+50, location.y*10+70, null);
    	}
    }
    
    • 最后在run方法中:在这里插入图片描述

    双线挑战制作Step4:给游戏添加背景音乐

    • 首先我们需要创建一个PlayMusic类来载入音乐文件,准备播放
    import java.applet.AudioClip;
    import java.net.MalformedURLException;
    import java.net.URL;
    import javax.swing.JApplet;
     
    public class PlayMusic {
    	public AudioClip music = loadSound("此处输入需要播放的音乐文件路径(文件格式必须为WAV格式)");
    	
    	public static AudioClip loadSound(String filename) {
    		URL url = null;
    		try {
    			url = new URL("file:" + filename);
    		} 
    		catch (MalformedURLException e) {;}
    		return JApplet.newAudioClip(url);
    	}
    	//音乐播放
    	public void play() {
    		//音乐播放
    		music.play();
    		//循环播放
    		music.loop();
    	}
    }
    
    • 然后在需要播放音乐和音效的地方,插入这一段代码
    PlayMusic p=new PlayMusic();
    p.play();
    
    • 就这么简单!

    游戏源代码及游戏素材链接——提取码:hjzd

    一点点总结心得:

    实现一个程序的步骤——

    1. 我想要实现什么效果?
    2. 为了实现这样的效果我要怎么做?
      (开干!)
    3. 做好的效果和我的预期符合吗?如果不符合我要怎么修改?
    4. 搜集资料,撰写博客,和同学交流,对程序进一步优化

    写在最后:

    java给了我一种前所未有的体验,或者说一种前所未有的快感。只需要敲击键盘,就可以像在广阔的平原,凭空升起一座城堡。

    复杂纷繁的代码,从我的手中获得了意义,获得了生气。在这个世界里,犹如掌握了“生杀大权”,游戏的一切都由我来定义。

    飞机长什么样子,怪物又长什么样子;飞机一次打多少发子弹,怪物吃多少子弹会被杀死;怪物以什么姿态出生,又以什么姿态死去……

    每一个程序的活泼生动,都是用一条条朴实无华的代码堆砌的。手握代码,我们就是这个世界的造物主!

    展开全文
  • 高并发下的Nginx性能优化实战

    万人学习 2019-12-24 14:44:52
    【超实用课程内容】 本课程内容包含讲解解读Nginx的基础知识,解读Nginx的核心知识、带领学员进行高并发环境下的Nginx性能优化实战,让学生能够快速将所学融合到企业应用中。 【课程如何观看?】 PC端:...
  • 增加辅助的体验和灵活性,这是本套多线程模板的结束课程,进行游戏实战测试,优化脚本,封装了大漠移动点击功能, 移动点击源码: .版本 2 .子程序 移动点击 .参数 x .参数 y .参数 索引 超级延时 (200, ) 线程...
  • 511遇见易语言多线程大漠多线程-1进程线程多线程511遇见易语言多线程大漠多线程-2中转子程序传多参511遇见易语言多线程大漠多线程-3线程传参数据变量地址511遇见易语言多线程大漠多线程-4线程传参指针地址511遇见...
  • 多线程在随着cpu发展应用的是越来越多,游戏场景因为其在服务器所占的数据量与逻辑复杂度的原因必须依赖于它。为什么场景要采用多线程?场景的线程是怎样的?场景的线程又是如何创建的?场景的线程又是怎样管理的?...
  • 游戏引擎多线程

    千次阅读 2015-07-13 00:14:56
     最 近一直在做项目优化,可是由于项目引擎历史原因,不能去砍掉某些功能而去优化项目,项目开发到这种程度,只能在这个基础上去提升整个引擎的效率,常规的 CPU和GPU上的优化(美术资源上的缩减,CPU上耗费地方和...
  • 游戏脚本多线程与中控台完整详解
  • 易大漠多线程高级之中控台游戏隐藏与显示 第二章 1.易大漠多线程高级之全自动中控台简单介绍 2.易大漠多线程高级之中控台账号库制作 3.易大漠多线程高级之中控台账号保存与读取 4.易大漠多线程高级之中控台单线...
  • 多线程的实现和使用场景

    万次阅读 2021-06-09 22:06:20
    多线程小案列2.1 多线程计算2.2 多线程实现卖票小程序2.3多线程卖票小程序优化2.4多线程卖票小程序优化升级总结 一、多线程实现方式 1.1 Thread实现 继承Thread类并重写它的run方法。之后创建这个子类的对象并调用...
  • Java面试Offer直通车

    万人学习 2019-12-18 15:19:52
    课程采取录播模式,课程永久有效,可无限次观看 课件、课程案例代码完全开放给你,你可以根据所学知识,自行修改、优化 如何开始学习? PC端:报名成功后可以直接进入课程学习 移动端:CSDN 学院APP(注意不是CSDN ...
  • C语言基础

    万人学习 2020-01-08 14:13:00
    C语言是目前世界上流行、使用最广泛的高级程序设计语言 6、很编译器,几乎所有操作系统Windows,Linux,Unix的大部分代码都是C,C在背后做了很东西的,也许开发游戏用C++,安卓用XX更为合适,图形界面的用其他...
  • 目录 一.概述 二."标准"多线程 三.AsyncTask系统 3.1 FQueuedThreadPool线程池 3.2 Asyntask与IQueuedWork 3.3 其他相关技术细节 四.TaskGraph系统 4.1 从Tick函数谈起 ...多线程优化项...
  • 会耗费大量的系统资源,特别是多线程中每个线程都操作共同的窗口组件,会造成线程的堵塞,甚至崩溃,大漠foobar做为一个透明播放器,可以方便的创建在游戏窗口上, 并且我们能把所有的窗口运行情况记录写到本地,...
  • Java:多线程:到底什么时候该用多线程

    万次阅读 多人点赞 2018-09-30 16:27:29
    系统接受实现多用户多请求的高并发时,通过多线程来实现。   二、线程后台处理大任务 一个程序是线性执行的。如果程序执行到要花大量时间处理的任务时,那主程序就得等待其执行完才能继续执行下面的...
  • 系统接受实现多用户多请求的高并发时,通过多线程来实现。 二、线程后台处理大任务 一个程序是线性执行的。如果程序执行到要花大量时间处理的任务时,那主程序就得等待其执行完才能继续执行下面的。那用户就不得...
  • 多线程批量处理数据

    千次阅读 2020-04-22 16:32:20
    在项目开发中有时会遇到批量操作,如果操作时间较长,我们可能会使用多线程来处理。下面举一个我在实际项目开发过程中使用多线程优化的一个过程。
  • J2ME优化压缩PNG文件 4个目标文件 内容索引:JAVA源码,综合应用,J2me游戏,PNG,图形处理 这是个J2ME控制台程序,它能剔除PNG文件中的非关键数据段,减少文件大小从而达到压缩图片的目的。而图片的质量并不会受到损失...
  • 多个账号循环登录,而本套模板会自动根据你开启的游戏窗口多少自动循环自动登录自动切换账号,来完成任务,你也可以开启单个窗口,实现当个账号的循环,有可以开启任意个窗口完成多线程的循环,教程从时钟对切换账号...
  • UE4_多线程、异步执行任务

    千次阅读 2020-05-25 19:53:32
    为什么要使用多线程,如果你是想找UE4的多线程资料,肯定不是一个小白了,所以多线程的好处就不用我告诉你了。 如果你想去感受一些多线程的好处,或者想去学习一个多线程的案例。建议,去看看这个博主的文章。 ...
  • OSG 高效渲染 之 多线程模型与渲染

    千次阅读 2019-10-28 09:27:54
    这里以OSG多线程渲染为例,谈谈OSG渲染引擎多线程渲染模式,再说说哪里有点“过时”了需要改进。 先谈点题外话,曾经看到知乎上一篇关于游戏引擎的设计讨论的文章,有位“大大”提出游戏引擎的设计关键在于架构设计...
  • 单线程与多线程的区别

    万次阅读 多人点赞 2017-11-27 11:03:18
    什么是进程? 当一个程序开始运行时,它就是一个进程,进程包括...而一个进程又是由线程所组成的。 什么是线程线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区
  • 什么是进程? 当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源。...多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务, 也...
  • 项目中Java的多线程一般用在哪些场景?多线程使用的主要目的在于举个简单的例子伪代码多线程的常见应用场景 多线程使用的主要目的在于 1、吞吐量:你做WEB,容器帮你做了多线程,但是他只能帮你做请求层面的。简单...
  • 但是在实际项目应用的过程中,这个 demo 的实现方式显得异常简陋,而且对应个业务同时发起 Socket 通信请求的处理能力也是有限,总不能每个请求都创建一个线程去监听返回结果,所以有必要进一步优化一番,例如加入...
  • 多线程常见问题整理

    千次阅读 2019-04-21 14:18:16
    二.Java中如何实现多线程? 三.start()和run()的区别? 四.volatile是什么? 五.线程的生命周期? 六.wait() 七.notify()和notifyAll() 八.生产者消费者模型 九.什么是线程池?为什么要使用线程池? 十.Java...
  • 爬取量还是很大的,游戏应用有2000个左右,其他像实用工具,聊天软件讲道理要少很,没想到这些app的数量都在2000个左右。  最大页码数在67页,没有超过这个页数的,小米这个应用商店属实做的不咋地呀。不过页数...
  • Java基础知识面试题(2020最新版)

    万次阅读 多人点赞 2020-02-19 12:11:27
    支持多线程多线程机制使应用程序在同一时间并行执行多项任) 健壮性(Java语言的强类型机制、异常处理、垃圾的自动收集等) 安全性 什么是字节码?采用字节码的最大好处是什么 字节码:Java源代码经过虚拟机编译器...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 43,906
精华内容 17,562
关键字:

多线程优化的游戏