java做飞机大战3篇
java做飞机大战3篇
java做飞机大战篇1
线程:进程(process)就是一块包含了某些资源的内存区域。操作系统利用进程把它的工作划分为一些功能单元。线程:进程中所包含的一个或多个执行单元称为线程(thread)。进程还拥有一个私有的虚拟地址空间,该空间仅能被它所包含的线程访问。 线程和进程的区别如下:1)一个进程至少有一个线程。线程的划分尺度小于进程,使得多线程程序的并发性高。另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。2)线程在执行过程中与进程的区别在于每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。3)从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用来实现进程的调度和管理以及资源分配。
2 简述线程的状态及其转换 1)New,创建一个线程,但是线程并没有进行任何的操作。2)Runnable,新线程从New状态,调用start方法转换到Runnable状态。线程调用start方法向线程调度程序(JVM或者是操作系统)注册一个线程,这个时候一切就绪只等CPU的时间。3)Running,从Runnable状态到Running状态,线程调度根据调度策略的不同调度不同的线程,被调度执行的线程进入Running状态,执行run方法。4)Dead状态,从Running状态到Runnable,run方法运行完毕后,线程就会被抛弃,线程就进入Dead状态。5)Block状态,从Running状态到Block状态,如果线程在运行的状态中因为I/O阻塞、调用了线程的sleep方法以及调用对象的wait方法则线程将进入阻塞状态,直到这些阻塞原因被结束,线程进入到Runnable状态。
3 简述线程的两种创建方式以及它们的区别 创建线程的两种方式:1)使用Thread创建线程。Thread类是线程类,其每一个实例表示一个可以并发运行的线程。我们可以通过继承该类并重写run方法来定义一个具体的线程。其中重写run方法的目的是定义该线程要执行的逻辑。启动线程时调用线程的start()方法而非直接调用run()方法。start()方法会将当前线程纳入线程调度,使当前线程可以开始并发运行。当线程获取时间片段后会自动开始执行run方法中的逻辑。2)使用Runnable创建线程。实现Runnable接口并重写run方法来定义线程体,然后在创建线程的时候将Runnable的实例传入并启动线程。两种创建线程方式的区别: 使用Thread创建线程,编写简单,可以直接操纵线程,无需使用Thread.currentThread(),但是不能够再继承其他类。使用Runnable创建线程可以将线程与线程要执行的任务分离开减少耦合,同时Java是单继承的,定义一个类实现Runnable接口,这样该类还可以继承自其他类。
多线程实现方法
使用Thread创建线并启动线程
java.lang.Thread类是线程类,其每一个实例表示一个可以并发运行的线程。我们可以通过继承该类并重写run方法来定义一个具体的线程。其中重写run方法的目的是定义该线程要执行的逻辑。启动线程时调用线程的start()方法而非直接调用run()方法。start()方法会将当前线程纳入线程调度,使当前线程可以开始并发运行。当线程获取时间片段后会自动开始执行run方法中的逻辑。
public class TestThread extends Thread{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println("我是线程");
}
}
}
创建预启动线程
…
Thread thread = new TestThread();//实例化线程
thread.start();//启动线程
…
使用Runnable创建并启动线程
实现Runnable接口并重写run方法来定义线程体,然后在创建线程的时候将Runnable的实例传入并启动线程。这样做的好处在于可以将线程与线程要执行的任务分离开减少耦合,同时java是单继承的,定义一个类实现Runnable接口这样的做法可以更好的去实现其他父类或接口。因为接口是多继承关系。
public class TestRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println("我是线程");
}
}
}
启动线程的方法:
…
Runnable runnable = new TestRunnable();
Thread thread = new Thread(runnable);//实例化线程并传入线程体
thread.start();//启动线程
…
使用内部类创建线程
通常我们可以通过匿名内部类的方式创建线程,使用该方式可以简化编写代码的复杂度,当一个线程仅需要一个实例时我们通常使用这种方式来 创建。 例如:继承Thread方式:
Thread thread = new Thread(){ //匿名类方式创建线程
public void run(){
//线程体
}
};
thread.start();//启动线程
Runnable方式:
Runnable runnable = new Runnable(){ //匿名类方式创建线程
public void run(){
}
};
Thread thread = new Thread(runnable);
thread.start();//启动线程
线程的方法
currentThread:方法可以用于获取运行当前代码片段的线程
Thread current = Thread.currentThread();
获取线程信息 Thread提供了 获取线程信息的相关方法: long getId():返回该线程的标识符 StringgetName():返回该线程的名称 int getPriority():返回线程的优先级 Thread.state getState():获取线程的状态boolean isAlive():测试线程是否处于活动状态 boolean isDaemon():测试线程是否为守护线程 booleanisInterrupted():测试线程是否已经中断
线程优先级
线程的切换是由线程调度控制的,我们无法通过代码来干涉,但是我们可以通过提高线程的优先级来最大程度的改善线程获取时间片的几率。线程的优先级被划分为10级,值分别为1-10,其中1最低,10最高。线程提供了3个常量来表示最低,最高,以及默认优先级:Thread.MIN_PRIORITY, Thread.MAX_PRIORITY, Thread.NORM_PRIORITY 设置优先级的方法为:
void setPriority(int priority)
守护线程
守护线程与普通线程在表现上没有什么区别,我们只需要通过Thread提供的方法来设定即可: __void setDaemon(boolean )__当参数为true时该线程为守护线程。 守护线程的特点是,当进程中只剩下守护线程时,所有守护线程强制终止。 GC就是运行在一个守护线程上的。需要注意的是,设置线程为后台线程要在该线程启动前设置。
Thread daemonThread = new Thread();
daemonThread.setDaemon(true);
daemonThread.start();
sleep方法
Thread的静态方法sleep用于使当前线程进入阻塞状态: __static void sleep(long ms)__该方法会使当前线程进入阻塞状态指定毫秒,当指定毫秒阻塞后,当前线程会重新进入Runnable状态,等待分配时间片。该方法声明抛出一个InterruptException。所以在使用该方法时需要捕获这个异常 改程序可能会出现"跳秒"现象,因为阻塞一秒后线程并非是立刻回到running状态,而是出于runnable状态,等待获取时间片。那么这段等待时间就是"误差"。所以以上程序并非严格意义上的每隔一秒钟执行一次输出。
yield方法:
Thread的静态方法yield: __static void yield()__该方法用于使当前线程主动让出当次CPU时间片回到Runnable状态,等待分配时间片。
join方法
__void join()__ 该方法用于等待当前线程结束。此方法是一个阻塞方法。 该方法声明抛出InterruptException。
线程同步
synchronized关键字 多个线程并发读写同一个临界资源时候会发生"线程并发安全问题“ 常见的临界资源: 多线程共享实例变量多线程共享静态公共变量 若想解决线程安全问题,需要将异步的操作变为同步操作。 所谓异步操作是指多线程并发的操作,相当于各干各的。所谓同步操作是指有先后顺序的操作,相当于你干完我再干。
同步代码块(synchronized 关键字 ),同步代码块包含两部分:一个作为锁的对象的引用,一个作为由这个锁保护的代码块这个比较难理解故写了下面代码帮助理解
/__
_ 多线程并发安全问题
_ 当多个线程同时操作同一资源时,由于
_ 线程切换时机不确定,导致出现逻辑混乱。
_ 严重时可能导致系统崩溃。
_ @author ylg
_
_/
public class SyncDemo1 {
public static void main(String[] args) {
/_
_ 当一个方法中的局部内部类想引用该方法
_ 的其他局部变量时,这个变量必须被声明
_ 为final的
_/
final Table table = new Table();
Thread t1 = new Thread(){
public void run(){
while(true){
int bean = table.getBean();
Thread.yield();//模拟线程切换
System.out.println(
getName()+":"+bean
);
}
}
};
Thread t2 = new Thread(){
public void run(){
while(true){
int bean = table.getBean();
Thread.yield();//模拟线程切换
System.out.println(
getName()+":"+bean
);
}
}
};
t1.start();
t2.start();
}
}
class Table{
//20个豆子
private int beans = 20;
/__
_ 当一个方法被synchronized修饰后,该方法
_ 成为"同步方法"。多个线程不能同时进入到
_ 方法内部。
_ @return
_/
public synchronized int getBean(){
if(beans==0){
throw new RuntimeException("没有豆子了!");
}
Thread.yield();//模拟线程切换
return beans--;
}
}
/__
_ 有效的缩小同步范围可以保证在
_ 安全的前提下提高了并发的效率
_ @author ylg
_
_/
public class SyncDemo2 {
public static void main(String[] args) {
final Shop shop = new Shop();
Thread t1 = new Thread(){
public void run(){
shop.buy();
}
};
Thread t2 = new Thread(){
public void run(){
shop.buy();
}
};
t1.start();
t2.start();
}
}
class Shop{
/_
_ 在方法上使用synchroinzed,同步监视器对象即当前方法所属对象:this
_/
// public synchronized void buy(){
public void buy(){
try{
Thread t = Thread.currentThread();
System.out.println(t+"正在挑选衣服..");
Thread.sleep(5000);
/_
_ 同步块可以缩小同步范围。
_ 但是必须保证"同步监视器"即:"上锁对象"是同一个才可以。
_ 通常,在一个方法中使用this所谓同步监视器对象即可。
_/
synchronized (this) {
System.out.println(t+"正在试衣服..");
Thread.sleep(5000);
}
System.out.println(t+"结账离开");
}catch(Exception e){
}
}
}
/__
_ synchronized也成为"互斥锁"
_ 当synchronzed修饰的是两段代码,但是"锁对象"相同时,这两段代码就是互斥的。
_ @author ylg
_
_/
public class SyncDemo4 {
public static void main(String[] args) {
final Boo b = new Boo();
Thread t1 = new Thread(){
public void run(){
b.methodA();
}
};
Thread t2 = new Thread(){
public void run(){
b.methodB();
}
};
t1.start();
t2.start();
}
}
class Boo{
public synchronized void methodA(){
Thread t = Thread.currentThread();
System.out.println(t+"正在调用方法A");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
System.out.println(t+"调用方法A完毕");
}
public synchronized void methodB(){
Thread t = Thread.currentThread();
System.out.println(t+"正在调用方法B");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
System.out.println(t+"调用方法B完毕");
java做飞机大战篇2
用Java制作游戏之前,一定要做到方向明确,思路清晰。首先确定自己需要用到几个类,类里的内容大致是什么,用脑图进行表达展现。
Java语言是一种跨平台、适合于分布式计算环境的面向对象编程语言,具有简单性、面向对象、分布式,多线程性等,其中面向对象有封装,继承,多态三大特性。
1)封装:数据和基于数据的操作封装
2) 继承:一个对象直接使用另一个对象的属性和方法,子类可以继承父类
3) 多态:一个程序中同名的多个不同方法,实现多态的常见方法 子类对父类的覆盖,利用重载在同一个类中定义多个同名的不同方法;
Java游戏制作中必须有 public static void main(String[]args)的主方法,main()方法是程序执行的入口。例如我的飞机大战main(),便是游戏开始执行的入口,写在GameFace类中。
游戏框架的制作,其大部分的代码都是固定形式,可以通用。使用setVisible(true)的方法,改变默认的不可见状态,setLayout(null)取消默认的管理布局,然后setLocation(),setSize(),setBound()等方法设置其大小。将按钮添加监听,实现监听。在对应方法中编写代码,,为监听者创建对象,完成注册,即在接口名字前添加“add”。
游戏版面要继承JPanel,其原因是JPanel带有双缓冲技术,可以解决闪烁的问题,需要加入到JFrame窗体中,JPanel默认的布局管理器是FlowLayout。调用画笔,将所要显示的图片画出来。然后再根据自己的思路,在游戏版面内添加敌机,实现矩形碰撞,生命值判定,游戏结束对话框。要熟练掌握调用其他的类,画出分数,画出生命值。制作容器,装子弹,飞机,炸弹。
制作游戏思路一定要清晰,对于共有属性,可以建立一个父类,例如在创建一个FlyingObject类,这样可以避免代码的反复编写。对于子类myplane、enemies可以直接继承父类,子类可以根据自己的需求再增加新的变量。在子类myplane中,画出我的飞机,控制飞机飞行的边界,调用子弹容器,画出容器中子弹,与敌机进行碰撞检测。飞机大战还应该实现键盘监听,例如Enter键实现暂停,上下左右键实现移动等。在敌机Enemies类中,定义敌机随机出现坐标,敌机图片的相对路径,敌机速度,敌机子弹与我的plane碰撞出现的结果以及敌机子弹生成的时间等。
游戏制作时需要注意的事项:1.JLabel的位置一定要放对,应当先添加背景JLabel,再添加其它控件。否则其它控件将被JLabel所遮挡。
2.对于多次重复编写的代码,尽可能的用for循环,简单方便。但是也要注意跳出语句的使用。
3.调用函数时要注意顺序
4.类名,方法名要遵循命名规范性,尽量做到见名知义,也要注意大小写。
java做飞机大战篇3
1.首先有玩家类,窗口类,主函数类和图片文件(.jpg)
2.然后是先行知识,创建窗口(JFrame),设置窗口属性;窗口上不能直接添加组件(键盘监听等),所以先在窗口上添加容器(Jpanel),将组件(KeyAdapter)添加到容器;
3.画出玩家:重写窗口类中的paintComponent方法,创建Graphics对象,调用drawImage方法可画图,调用drawString方法可标注其名字
4.移动:在窗口类中创建键盘监听抽象类KeyAdapter(实现了接口的部分方法但无具体操作),需要重写该类的Keypressed方法和KeyRleased方法,赋给按键变量真值,随后将该对象添加到窗口
5.随机生成初始坐标:在开始游戏后随机给定玩家的x、y坐标,创建Random对象,调用Random.nextInt(n)方法,n代表从[0,n)区间的随机值。
6.最后通过一个Timer.schedule(匿名内部类对象,指定延迟后开始,周期)方法来实现移动效果。匿名内部类【TimerTask的子类,重写了run方法,包括repaint方法(实则调用paintComponent)和yidong方法】来重画、监听键盘的指令()并作出相应动作
下面是源代码(有注释):
容器类
package a;
import javax.swing._;
import java.awt._;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import static a.Newgame.frame;
//新建面板
public class War extends JPanel {
private Timer timer;
private boolean sUp, sDown, sRight, sLeft;//右飞机按键变量
private boolean sW, sD, sS, sA;//左飞机按键变量
private Player1 player1 = new Player1();
private Player2 player2 = new Player2();
private ImageIcon img11 = player1.img1;
private ImageIcon img22 = player2.img2;
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);//此句调用父类方法进行界面重绘,恢复到某个界面,下面的步骤再画玩家;如果注释掉会有重影
//在面板上画出玩家
g.drawImage(img11.getImage(), player1.x, player1.y, 100, 100, this);
g.drawString("P1", player1.x, player1.y);
g.drawImage(img22.getImage(), player2.x, player2.y, 100, 100, this);
g.drawString("P2", player2.x, player2.y);
}
public void startGame() {
timer = new Timer();
timer.schedule(new TimerTask() {//匿名内部类(TimerTask的子类)
@Override
public void run() {//重写run()函数
repaint();//调用重写的paintComponent来画两飞机
yidong();//并每次判断按下哪个键,然后移动
}
}, 0, 50);//安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。50毫秒执行一次
}
public void yidong() {
//因为每打印一次可能用户按下一个飞机的几个键或者两个飞机的几个键,这些是都要检测到的,改成elseif后只能检测到一个键,无法实现两架飞机同时多方向移动
if (sW && player1.y > 0) {
player1.y -= player1.speed;
}
if (sA && player1.x > 0) {
player1.x -= player1.speed;
}
if (sS && player1.y < 700) {
player1.y += player1.speed;
}
if (sD && player1.x < 900) {
player1.x += player1.speed;
}
if (sUp && player2.y > 0) {
player2.y -= player2.speed;
}
if (sDown && player2.y < 700) {
player2.y += player2.speed;
}
if (sLeft && player2.x > 0) {
player2.x -= player2.speed;
}
if (sRight && player2.x < 900) {
player2.x += player2.speed;
}
}
public void act() {
//随机生成两飞机的初始坐标
Random rd = new Random();
player1.x = rd.nextInt(900);
player1.y = rd.nextInt(700);
player2.x = rd.nextInt(900);
player2.y = rd.nextInt(700);
//开始游戏后获得计时器开始监听并重画
startGame();
//KeyAdapter是KeyListener的实现类,重写了所有方法但没有具体操作
KeyAdapter keyAdapter = new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
super.keyPressed(e);
int key = e.getKeyCode();
switch (key) {
case KeyEvent.VK_W:
sW = true;
break;
case KeyEvent.VK_A:
sA = true;
break;
case KeyEvent.VK_D:
sD = true;
break;
case KeyEvent.VK_S:
sS = true;
break;
case KeyEvent.VK_RIGHT:
sRight = true;
break;
case KeyEvent.VK_LEFT:
sLeft = true;
break;
case KeyEvent.VK_DOWN:
sDown = true;
break;
case KeyEvent.VK_UP:
sUp = true;
break;
}
}
@Override
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
switch (key) {
case KeyEvent.VK_W:
sW = false;
break;
case KeyEvent.VK_A:
sA = false;
break;
case KeyEvent.VK_D:
sD = false;
break;
case KeyEvent.VK_S:
sS = false;
break;
case KeyEvent.VK_RIGHT:
sRight = false;
break;
case KeyEvent.VK_LEFT:
sLeft = false;
break;
case KeyEvent.VK_DOWN:
sDown = false;
break;
case KeyEvent.VK_UP:
sUp = false;
break;
}
}
};
frame.addKeyListener(keyAdapter);
}
}
主函数类:
package a;
import javax.swing._;
public class Newgame {
public static JFrame frame;
public static void main(String[] args) {
frame = new JFrame("逃出生天");
frame.setSize(1000,800);
//绝对布局组件位置和形状不会随窗体改变,不设置布局管理器就可以使用setBounds()来控制位置
frame.setLayout(null);
//设置窗体关闭程序自动关闭
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
//窗体居中显示
frame.setLocationRelativeTo(null);
//添加主面板
War war = new War();
frame.add(war);
//设置面板大小
war.setBounds(0,0,1000,800);
//设置主面板可见
frame.add(war);
frame.setVisible(true);
war.act();
}
}
1234567891011121314151617181920212223242526
玩家类:
玩家1
package a;
import javax.swing._;
public class Player1 {
public int x;
public int y;
public int speed = 50;
public ImageIcon img1 = newImageIcon("D:\\Program_Files\\Jetbrains\\workspace_idea\\src\\a\\plane.jpg");
}
12345678910
玩家2
package a;
import javax.swing._;
public class Player2 {
public int x;
public int y;
public int speed = 50;
public ImageIcon img2 = newImageIcon("D:\\Program_Files\\Jetbrains\\workspace_idea\\src\\a\\plane2.jpg");
}
做成一个桌面小游戏:
点“+”号
选好主函数类后点ok
然后点Build中的Build Artificts
选择jar包点Build即可
然后jar包就在你的workspace生成了,找到它发送桌面快捷方式,更改图标名字即可
图片转换成图标的网站附上
上一篇:独立与依靠作文5篇
下一篇:2020上学期班主任工作总结5篇