这是一篇学习分享博客,这篇博客将会介绍以下几项内容:
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);
}
}
}
//重复画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){
}
}
那么,热身结束,下面让我们一起进入多线程的世界吧!
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){
}
}
}
}
public void actionPerformed(ActionEvent e){
ThreadClass thc=new ThreadClass(g);
thc.start();
}
让我们试着把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){
}
}
}
先创建坐标类
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;
}
}
有了这两个类,我们就可以表示任意二维的向量运动了
Vector location=new Vector(0,0);
Vector speed=new Vector(5,0);
Vector acce=new Vector(10,0);
Vector location=new Vector(0,0);
Vector speed=new Vector(5,10);
Vector acce=new Vector(10,0);
需要改变的是主类中Mouse类的代码和ThreadClass类中run方法的代码
//创建内部类监听器(也可以重新创建一个文件编写该类)
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) {
}
}
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://pan.baidu.com/s/10HcOSvuov14moes1jPe9JQ
提取码:z8ii
(三个游戏的源代码、图片素材链接在下文中获取)
游戏演示:
Java游戏制作
要实现飞机大战主要完成这几件事:
由于时间关系,目前博主所制作的游戏暂时只具有以上这些功能,有兴趣的伙伴还可以试着增加关卡、Boss、新的怪物(比如会发射子弹的怪物)、剧情等等。
我们在游戏进行的过程中,难免会生成大量的图片对象。前面我们讲到,当我们需要在屏幕上绘制的图像过多时会出现卡顿闪屏现象,第二个解决方法就是双缓冲绘图,下面我来简单的介绍一下。
要实现双缓冲绘图,首先我们要创建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);
//有图片、有血量的飞行物
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);
}
}
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 mps
}
//不断发射子弹
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);
}
}
//判断子弹是否击中怪物、怪物是否触碰我机、是否拾得宝箱
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
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;
}
}
}
}
}
}
前面说到过爆炸动效也可以放进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);
}
}
}
我们想要的效果:在游戏画面的左下角有一个暂停键,我们点击暂停键时游戏会进入暂停状态,再点击开始游戏会恢复到暂停之前的状态。
我们需要做的操作:我们可以创建一个布尔值对象gameRest(布尔值只有true和false两种状态),初始值设定为false,每一轮线程运行时都需要先判断一下gameRest值是否为false,如果为true,则跳过绘制飞行物、判断碰撞等操作;如果为false,则继续正常运行。
接着我们写一个方法来改变gameRest的值,这样我们每调用一次方法就切换一次gameRest的值
//游戏暂停/开始
public void on_off(){
gameRest=!gameRest;
}
//判断游戏是否结束
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;
}
}
}
if(gameOver==true){
break;}
//获取万位
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;
//生成图片对象
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
可以优化的地方:
游戏演示:
Java原创游戏分享
接豆人游戏和飞机大战玩法虽然差异比较大,但是用到的代码原理其实是类似的。
黄色的吃豆人的移动,同样是依靠鼠标监听器的mouseMoved方法不断获取鼠标的坐标然后绘制接豆人的图像。只不过这次我们只获取鼠标的横坐标,纵坐标设定为一个定值,这样就可以实现我们的接豆人只做水平方向的运动了。
接豆人吃到宝石和道具的判断,和飞机大战中的判断碰撞是类似的;接豆人中随机掉落的宝石、炸弹和道具,与飞机大战中刷新怪物是类似的。
两个游戏比较不同的地方是,在接豆人游戏中如果吃到了蜘蛛或者金币礼包是会触发新事件的。而且在接豆人中也增加了玩家的生命值。
大致总结一下,实现接豆人需要完成这几件事:让接豆人在水平方向跟随鼠标移动随机生成宝石、炸弹蜘蛛和道具,并且赋予下落物体一个垂直方向的加速度,增加真实感判断接豆人是否接到了掉落物给金币礼包和蜘蛛添加触发效果(下金币雨和接豆人进入眩晕)游戏暂停、游戏结束后重新开始间隔一段时间清理一下飞行物列表,提高游戏流畅度
和飞机大战类似的地方就不再赘述,这里介绍一些不同的地方
public int rewardTime;//奖励时间
public Boolean pause=false;//是否进入眩晕状态
public int pauseTime;//眩晕时间
public FlyObject mp_pause;//用于绘制眩晕时接豆人的图片
//生成下落物
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{
//生成其他掉落物
}
//绘制我机
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
}
}
}
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;
}
}
//清理缓存(在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
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!");
}
}
//设置窗体可见,jf是创建的JFrame对象
jf.setVisible(true);
jf.addKeyListener(mou);//为窗体添加键盘监听器
jf.requestFocusInWindow();//窗体获得焦点,记得要放在窗体可见之后
//我们可以先定义一个整数flag1
public int 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的值,表示图已画过
}
}
lb_blue=new LineBall(new Vector(0,0),new Vector(1,0));
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){
}
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就清零一次。它的意思是小方块更改方向后,必须往更改后的方向至少前进一格才能再次更改方向,否则仍然有可能出现“原地掉头”的操作。public static int[][] chessBoard=new int [70][70];
//判断游戏是否结束
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;
}
}
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);
}
}
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
实现一个程序的步骤——
写在最后:
java给了我一种前所未有的体验,或者说一种前所未有的快感。只需要敲击键盘,就可以像在广阔的平原,凭空升起一座城堡。
复杂纷繁的代码,从我的手中获得了意义,获得了生气。在这个世界里,犹如掌握了“生杀大权”,游戏的一切都由我来定义。
飞机长什么样子,怪物又长什么样子;飞机一次打多少发子弹,怪物吃多少子弹会被杀死;怪物以什么姿态出生,又以什么姿态死去……
每一个程序的活泼生动,都是用一条条朴实无华的代码堆砌的。手握代码,我们就是这个世界的造物主!