可扩展java游戏框架实践之java飞机大战

前言

在Java awt frame下利用画笔工具实现一个游戏框架。
该框架支持:

  • 游戏动画实现
  • 动图实现
  • 移动实现
  • 碰撞检测
  • 游戏流程控制
  • 游戏音乐控制

功能设计实现

①游戏动画实现

JFrame下实现画图功能

public static JFrame frame = new JFrame();
		frame.setTitle("PlaneWar");    //设置窗口标题
        frame.setSize(width, height);    //设置窗口大小
        frame.setLocationRelativeTo(null);//设置窗口居中显示
        frame.setResizable(false);//设置不可调整大小
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);//设置窗口关闭时程序结束
        frame.setLayout(null);
        frame.setVisible(true);//设置窗口可见
        frame.getGraphics().drawImage(图片,0,0,null);//把图片画出
frame.getGraphics().drawImage(图片,0,0,null);//把图片画出

上述一行代码可以把Image类在frame中显示出来。

在这里插入图片描述

如果采用上图方式显示图片,即

frame.getGraphics().drawImage(图片1,图片1X坐标,图片1Y坐标,null);//把图片1画出
frame.getGraphics().drawImage(图片2,图片2X坐标,图片2Y坐标,null);//把图片2画出
frame.getGraphics().drawImage(图片3,图片3X坐标,图片3Y坐标,null);//把图片3画出

这种方式画出的图片会出现频闪问题。

所以推荐下述方法②实现游戏动画实现。
在这里插入图片描述

将图片画在BufferImage中,然后把BufferImage画到frame中,解决游戏画面频闪问题

    public  BufferedImage bufferedImage = new BufferedImage(800,800,BufferedImage.TYPE_INT_RGB);
    public  Graphics2D bufferedImageGraphics=bufferedImage.createGraphics();
    ...
    bufferedImageGraphics.drawImage(图片1,图片1X坐标,图片1Y坐标,null);//把图片1画出
    bufferedImageGraphics.drawImage(图片2,图片2X坐标,图片2Y坐标,null);//把图片2画出
    bufferedImageGraphics.drawImage(图片3,图片3X坐标,图片3Y坐标,null);//把图片3画出
    ...
    frame.getGraphics().drawImage(bufferedImage,0,0,null);//把缓存图片画出

②动图实现

一张动图是由多张静态图片组成
将一个需要显示的类以动态图片的效果显示出来的解决方案:

  • 在该类中维护一个Image容器,容器中存放组成动态图片的所有静态图片。
  • 显示此类,就从容器中取一张静态图片显示
  • 下一次显示此类,就从容器中取下一章静态图片显示 即可
public class BaseImage  {
	public int index=0;
	...
	public ArrayList<Image> imageList = new ArrayList<>();
	...
	public Image getImg(){
        if(imageList.size()==0){
            return null;
        }else{
            index=(index+1)%size;
            return imageList.get(index);
        }
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

③移动实现

游戏中的移动包含:

  • 标题文字的移动
  • 游戏敌人的移动
  • 子弹射击的移动

移动的方向多种多样:

  • 从方向a到方向b的直线移动
  • 某种形状的规则移动
  • 不规则移动 等等

移动作用的对象就是坐标。
用一个类记录item的移动规则,每次移动告诉这个类告诉如何修改坐标就行

public interface IMoveStrategy {
    void Move(BaseImage baseImage);
}

public class BaseImage  {
    public int positionX;
    public int positionY;
    ...
    IMoveStrategy iMoveStrategy=null;
    public void setiMoveStrategy(IMoveStrategy iMoveStrategy){this.iMoveStrategy=iMoveStrategy;}
    public Image getImg(){
    	if(iMoveStrategy!=null)
            iMoveStrategy.Move(this);
        ...
    }
 }

标题文字从下往上运动:
在这里插入图片描述
子弹以正方形的形状从下往上运动:
在这里插入图片描述

④碰撞检测

有的游戏中需要包含碰撞检测。比如子弹碰到敌人,敌人碰到游戏角色。
游戏功能设计时:
如果碰撞,则运行什么逻辑
这个时候只关心比较高层次的逻辑设计,具体如何实现碰撞并不关心。
2个物体是否碰撞与物体的具体形状有关,不同形状之间的碰撞算法是不一样的。
所以碰撞检测需要支持多种形状。

interface HitDetectionFactory{
    boolean isHit(IShape shape1,IShape shape2);
}
public class HitDetection {
    public boolean isHit(IShape shape1, IShape shape2){
        HitDetectionFactory hitDetectionFactory =null;
        if((shape1 instanceof ShapeCircle)&&(shape2 instanceof ShapeCircle)){
            //圆形与圆形的碰撞检测
            hitDetectionFactory=new HitDetectionCircleCircle();
        }else if(((shape1 instanceof ShapeCircle)&&(shape2 instanceof ShapeRectangle))||
                ((shape2 instanceof ShapeCircle)&&(shape1 instanceof ShapeRectangle))){
            //圆形与矩形碰撞检测
            hitDetectionFactory=new HitDetectionCircleRectangel();
        }//这里增加else if 可以支持多种不同形状的碰撞算法
        else{//默认碰撞检测
            hitDetectionFactory=new HitDetectionCircleCircle();
        }
        return hitDetectionFactory.isHit(shape1,shape2);
    }
}

...
	HitDetection ht = new HitDetection();//声明一个碰撞检测类
	ht.isHit(物体1,物体2)//这里只关心物体是否碰撞,具体如何实现碰撞并不关心。

圆形与圆形的碰撞检测:

圆形与圆形的碰撞算法比较简单
比较圆形距离与2个圆形半价之和的大小即可。
对应碰撞检测的算法如下:

class HitDetectionCircleCircle implements HitDetectionFactory{
    @Override
    public boolean isHit(IShape shape1,IShape shape2) {
        ShapeCircle s1=(ShapeCircle)shape1;
        ShapeCircle s2=(ShapeCircle)shape2;
        int len1=(s2.x-s1.x)*(s2.x-s1.x)+(s2.y-s1.y)*(s2.y-s1.y);
        int len2=(s1.r+s2.r)*(s1.r+s2.r);
        if(len1<=len2){//相撞
                return true;
        }
        return false;
    }
}

圆形与矩形碰撞检测:

圆形与矩形是否碰撞包含3种情况:
在这里插入图片描述

  • 情况1:矩形的边角点在圆内
  • 情况2:圆心到矩形中心的距离小于等于半径+矩形宽度的一半
  • 情况3:圆心到矩形中心的距离小于等于半径+矩形长度的一半

对应碰撞检测的算法如下:

class HitDetectionCircleRectangel implements  HitDetectionFactory{
    @Override
    public boolean isHit(IShape shape1, IShape shape2) {
        ShapeCircle sc=null;
        ShapeRectangle sr=null;
        if(shape1 instanceof ShapeCircle){
                sc=(ShapeCircle)shape1;
                sr=(ShapeRectangle) shape2;
        }else{
            sc=(ShapeCircle)shape2;
            sr=(ShapeRectangle) shape1;
        }
        if(isInTheCircle(sr.x,sr.y,sc)) return true;
        if(isInTheCircle(sr.x+sr.width,sr.y,sc)) return true;
        if(isInTheCircle(sr.x+sr.width,sr.y+sr.height,sc)) return true;
        if(isInTheCircle(sr.x,sr.y+sr.height,sc)) return true;
        int mx=sr.x+sr.width/2;
        int my=sr.y+sr.height/2;
        if(Math.abs(mx-sc.x)<=sc.r+sr.width/2) return true;
        if(Math.abs(my-sc.y)<=sc.r+sr.height/2) return true;

        return false;
    }
    private boolean isInTheCircle(int x,int y,ShapeCircle circle){
        int len1=(circle.x-x)*(circle.x-x)+(circle.y-y)*(circle.y-y);
        if(len1<=circle.r* circle.r) return true;
        return false;
    }
}

⑤游戏流程控制

游戏流程控制的实现比较简单:通过系统状态控制即可。

系统状态类

public class MyStation {
    public static final MyStation MAIN = new MyStation();
    public static final MyStation CHOICE = new MyStation();
    public static final MyStation TOP = new MyStation();
    public static final MyStation GAMING = new MyStation();
    public static final MyStation EXIT= new MyStation();
    public static boolean isReady=true;
    public static MyStation curStation = MAIN;
    public static void setStation(MyStation s) throws UnsupportedAudioFileException, LineUnavailableException, IOException {
        curStation=s;
    }
}

main函数

    public static void main(String[] args) throws Exception {
        frame.setTitle("PlaneWar");    //设置窗口标题
        frame.setSize(width, height);    //设置窗口大小
        frame.setLocationRelativeTo(null);//设置窗口居中显示
        frame.setResizable(false);//设置不可调整大小
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);//设置窗口关闭时程序结束
        frame.setLayout(null);
        frame.setVisible(true);//设置窗口可见

        while(MyStation.curStation!=MyStation.EXIT){
           if(MyStation.curStation==MyStation.MAIN)
               new ScreenMain(frame).RunScreen();
           if(MyStation.curStation==MyStation.CHOICE)
               new ScreenChoice(frame).RunScreen();
            if(MyStation.curStation==MyStation.TOP)
                new ScreenTop(frame).RunScreen();
            if(MyStation.curStation==MyStation.GAMING)
                new ScreenGaming(frame).RunScreen();
        }
        System.exit(0);

    }

⑥游戏音乐控制

java sound api(JDK)原生支 .wav .au .aiff 这些格式的音频文件,当然 PCM(Pulse Code Modulation----脉冲编码调制)文件也是可以直接播放的,如果是 mp3,ogg,ape,flac 则需要第三方 jar 。

 Stream.of(AudioSystem.getAudioFileTypes()).forEach(e -> {System.out.println(e.toString());});
 输出:
 	WAVE
	AU
	AIFF

单个音乐文件播放
Clip.start()方法不是阻塞操作,这意味着它不会等待,而是启动一个新的守护程序线程来播放声音,该守护程序线程在程序退出main方法后即被Kill

 		File musicPath = new File("路径");
        AudioInputStream  audioInput = AudioSystem.getAudioInputStream(musicPath);
        Clip clip = AudioSystem.getClip();
        clip.open(audioInput);
        clip.start();                    //播放音乐
        clip.loop(Clip.LOOP_CONTINUOUSLY);        //设置循环播放
        System.out.println("播放5s");
        Thread.sleep(5000);
        int position=clip.getFramePosition();
        clip.stop();
        System.out.println("暂停2s");
        Thread.sleep(2000);
        clip.setFramePosition(position);
        clip.start();
        System.out.println("继续播放5s");
        Thread.sleep(5000);
        clip.close();
        System.out.println("关闭");

窗体实现音乐切换
在这里插入图片描述

class UI{
    public void initUI() throws LineUnavailableException, UnsupportedAudioFileException, IOException, InterruptedException {
        JFrame jf = new JFrame("测试切换音乐");
        jf.setSize(100,200);
        jf.setLocationRelativeTo(null);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //设置窗体背景色
        jf.getContentPane().setBackground(Color.WHITE);

        jf.setLayout(new FlowLayout());

        Clip clip1 = AudioSystem.getClip();
        clip1.open(AudioSystem.getAudioInputStream(new File("路径1")));
        clip1.loop(Clip.LOOP_CONTINUOUSLY);        //设置循环播放
        clip1.stop();

        Clip clip2 = AudioSystem.getClip();
        clip2.open(AudioSystem.getAudioInputStream(new File("路径2")));
        clip2.loop(Clip.LOOP_CONTINUOUSLY);        //设置循环播放
        clip2.stop();

        Clip clip3 = AudioSystem.getClip();
        clip3.open(AudioSystem.getAudioInputStream(new File("路径3")));
        clip3.loop(Clip.LOOP_CONTINUOUSLY);        //设置循环播放
        clip3.stop();

        JButton jbu1 = new JButton("音乐1");
        JButton jbu2 = new JButton("音乐2");
        JButton jbu3 = new JButton("音乐3");
        jbu1.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                clip1.start();
                clip2.stop();
                clip3.stop();
            }
        });
        jbu2.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                clip1.stop();
                clip2.start();
                clip3.stop();
            }
        });
        jbu3.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                clip1.stop();
                clip2.stop();
                clip3.start();
            }
        });
        jf.add(jbu1);
        jf.add(jbu2);
        jf.add(jbu3);
        jf.setVisible(true);

    }
}

⑦动态显示数字图片

输入:整型数字+起始坐标
输出:(数字图片+坐标)的序列

public ArrayList<ImageAndMyposition> getImg(int number, int positionX, int positionY){
        ArrayList<ImageAndMyposition> res= new ArrayList<>();
        //System.out.println(number);
        boolean flag=false;
        if(number<0){
            flag=true;
            number=-number;
        }
        if(number==0){
            res.add(new ImageAndMyposition(getImageN(0),new MyPosition(positionX,positionY)));
            return res;
        }
        ArrayList<Image> numlist= new ArrayList<>();
        ArrayList<MyPosition> positionlist=new ArrayList<>();
        while(true){
            int index=number%10;
            number=number/10;
            numlist.add(getImageN(index));
            positionlist.add(new MyPosition(positionX,positionY));
            positionX+=50;
            if(number==0)break;
        }
        positionlist.add(new MyPosition(positionX,positionY));
        if(flag)numlist.add(getImageN(10));
        Collections.reverse(numlist);

        for(int index=0;index<numlist.size();index++){
            res.add(new ImageAndMyposition(numlist.get(index),positionlist.get(index)));
        }
        return res;
    }

在这里插入图片描述

设计流程

原型设计

游戏主界面
新的游戏界面
游戏中界面
游戏暂停界面
排行榜界面

类图设计

多音效类图实现

在这里插入图片描述
新增音效直接继承BaseMusic即可
示例:

public class MusicShoot extends BaseMusic{
    public MusicShoot() throws UnsupportedAudioFileException, LineUnavailableException, IOException {
        clip = AudioSystem.getClip();
        clip.open(AudioSystem.getAudioInputStream(new File("MusicFiles\\射击.wav")));
        clip.loop(Clip.LOOP_CONTINUOUSLY);        //设置循环播放
        clip.stop();
    }
}

图像基类实现

在这里插入图片描述
此项目中所有的显示效果都是通过DrawImage实现的,需要一个图像基类
imageList:ArrayList 是用来存储一张或多张Image的

素材及工具

图片素材

音乐素材

飞机大战0.5版本源码

效果
在这里插入图片描述
实现功能:

  • 动态背景
  • 角色移动
  • 发射不同伤害子弹
  • 随机生成敌人
  • 敌人被击中效果

知识点:

  • awt
  • swing
  • java容器
  • 多线程初步实践
package game;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.LinkedList;

public class ThreadDraw implements Runnable {


    BufferedImage bi=null;
    JFrame jf=null;
    public static  CMe me=null;

    public static LinkedList<CEnemy> listEnemy=new LinkedList<>();
    public static LinkedList<CBullet> listBullet=new LinkedList<>();
    ThreadDraw(BufferedImage bi,JFrame jf){
            this.bi=bi;
            this.jf=jf;

    }

    @Override
    public void run() {
        int j=0;
        Long curTime;
        while(true){
            try {

                curTime=System.currentTimeMillis();
                Thread.sleep(5);

                Image img = new ImageIcon("img\\bg2.jpg").getImage();
                j+=1;if(j==1000){j=0;}

                //画背景
                var g=bi.createGraphics();
                g.drawImage(img,0,j+0,null);
                g.drawImage(img,0,j-1000,null);


                //画me
                g.drawImage(me.getImg(),me.x,me.y,null);

                //画子弹
                for(int index=0;index<listBullet.size();index++){
                    var i =listBullet.get(index);
                    g.drawImage(i.getImg(),i.x,i.y,null);
                    i.moveYBy(-3);
                    if(i.y<0||i.used){
                        listBullet.remove(index);
                        index--;
                    }
                }

                //画敌人
                for(int index=0;index<listEnemy.size();index++){
                    var i =listEnemy.get(index);
                    if(i.health<=0){
                        listEnemy.remove(index);
                        index--;
                    }
                    g.drawImage(i.getImg(),i.x,i.y,null);
                    i.moveYBy(1);
                }

                //检查是否碰撞
                for(var e:listEnemy){
                    for(var b:listBullet){
                        e.checkHit(b);
                    }
                }

                //画出缓冲区
                jf.getGraphics().drawImage(bi,0,0,null);

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

//
//    @Override
//    public void run(){
//        Long curTime;
//        var g=bi.createGraphics();
//        int j=0;
//        int bgSpeed=-200;
//
//        while(true){
//            try {
//                Thread.sleep(5);
//                curTime=System.currentTimeMillis();
//                int bgposition= (int) (bgSpeed*(UI.UIStartTime-curTime)/1000) %1000;
//                Image img = new ImageIcon("img\\bg4.jpg").getImage();
//                j+=1;if(j==1000){j=0;}
//
//                //画背景
//                g.drawImage(img,0,bgposition,null);
//                g.drawImage(img,0,bgposition-1000,null);
//
//                //画子弹
//                for(int index=0;index<listBullet.size();index++){
//                    var i =listBullet.get(index);
//                    g.drawImage(i.getImg(),i.x,i.y,null);
//                    //i.moveYBy(-3);
//                    i.moveByTime(curTime);
//                    if(i.y<-50||i.used){
//                        listBullet.remove(index);
//                        index--;
//                    }
//                }
//
//                //画敌人
//                for(int index=0;index<listEnemy.size();index++){
//                    var i =listEnemy.get(index);
//                    if(i.health<=0){
//                        listEnemy.remove(index);
//                        index--;
//                        continue;
//                    }
//                   // g.drawImage(i.getImg(),i.x,i.y,null);
//                    g.drawImage(i.getImg(curTime),i.x,i.y,null);
//                    //i.moveYBy(1);
//                    i.moveByTime(curTime);
//                }
//                //检查是否碰撞
//                for(var e:listEnemy){
//                    for(var b:listBullet){
//                        e.checkHit(b);
//                    }
//                }
//
//                //画me
//                g.drawImage(me.getImg(),me.x,me.y,null);
//                //画缓冲区
//                jf.getGraphics().drawImage(bi,0,0,null);
//
//
//
//            } catch (InterruptedException e) {
//                throw new RuntimeException(e);
//            }
//        }
//    }

}

package game;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.Random;

public class UI {
    public static long UIStartTime;
    public static BufferedImage bi = new BufferedImage(1000,1000,BufferedImage.TYPE_INT_RGB);
    public static  JFrame jf =null;
    public static  CMe me= null;
    public static ThreadDraw td=null;
    public void initUI(){
        UIStartTime=System.currentTimeMillis();
        jf = new JFrame("弹球游戏");
        jf.setSize(1000,1000);
        jf.setResizable(false);
        jf.setLocationRelativeTo(null);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //jf.setLayout(new FlowLayout());

        jf.setVisible(true);
        td=new ThreadDraw(bi,jf);
        new Thread(td).start();
        me=new CMe(450,800);
        ThreadDraw.me=me;



        //===============监听器=====================

        jf.addKeyListener(new KeyAdapter() {

            @Override
            public void keyTyped(KeyEvent e) {
                switch (e.getKeyChar()){
                    case 'a':
                    case 'A':
                        me.move(me.x-me.speed,me.y);
                        break;
                    case 's':
                    case 'S':
                        me.move(me.x,me.y+me.speed);
                        break;
                    case 'd':
                    case 'D':
                        me.move(me.x+me.speed,me.y);
                        break;
                    case 'w':
                    case 'W':
                        me.move(me.x,me.y-me.speed);
                        break;
                    case 'q':
                    case 'Q':
                    case '1':
                        ThreadDraw.listBullet.add(new CBullet());
                        break;
                    case '2':
                        ThreadDraw.listBullet.add(new CBullet('t'));
                        break;
                    default:
                        System.out.println(e.getKeyChar());
                        break;
                }
                System.out.println(e.getKeyChar());

            }
        });

        //定时加敌人
        new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    try {
                        Thread.sleep(2000);
                        var ce=new CEnemy();
                        ce.x=new Random().nextInt(800)+100;
                        ThreadDraw.listEnemy.add(ce);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }).start();

    }


    public static void main(String[] args) {


//        long startime= System.currentTimeMillis();
//        new Thread(new Runnable() {
//            @Override
//            public void run() {
//                while (true){
//                    try {
//                        Thread.sleep(100);
//                        double len=System.currentTimeMillis()-startime;
//                        len=len/1000;
//                        System.out.println(len);
//                        //System.out.println(System.currentTimeMillis()-startime);
//                    } catch (InterruptedException e) {
//                        throw new RuntimeException(e);
//                    }
//                }
//
//            }
//        }).start();
        new UI().initUI();
    }


}

package game;

import javax.swing.*;

public class CBullet extends CItem{

    int speedX=0;
    int speedY=-200;
    int damage=40;
    boolean used=false;
    CBullet(){
        this.lastTime=System.currentTimeMillis();
        img=  new ImageIcon("img\\bullet.png").getImage();
        damage=40;
        x=UI.me.x+25;
        y=UI.me.y;
    }
    CBullet(char type){
        this.lastTime=System.currentTimeMillis();
        x=UI.me.x+40;
        y=UI.me.y;
        switch (type){
            case 't':
                img = new ImageIcon("img\\雷电.png").getImage();
                break;
            default:
                img = new ImageIcon("img\\bullet.png").getImage();
                damage=60;
                break;
        }
    }
    public void moveByTime(long CurTime){
        x+=speedX*(CurTime-lastTime)/1000;
        y+=speedY*(CurTime-lastTime)/1000;
        lastTime=CurTime;
    }

}

package game;

import javax.swing.*;
import java.awt.*;

public class CEnemy extends CItem{
    int health=120;
    int speedX=0;
    int speedY=150;
    int hitcount=0;
    static final int HITCOUNT=50;
    static final int HITTIME=500;
    long hittime=0;
    Image img2=  new ImageIcon("img\\enemy_hit.png").getImage();
    CEnemy(){
            lastTime=System.currentTimeMillis();
            img=  new ImageIcon("img\\enemy.png").getImage();
            x=500;
            y=0;
    }

   public void checkHit(CBullet b){
        if(b.y<this.y){
            if(b.x+b.img.getWidth(null)<this.x){//没中
                return;
            }
            if(b.x>this.x+img.getWidth(null)){//没中
                return;
            }

            //中了
            hitcount=HITCOUNT;
            hittime= (lastTime+HITTIME);
            b.used=true;
            health-=b.damage;

        }
    }


    public void moveByTime(long CurTime){
        x+=speedX*(CurTime-lastTime)/1000;
        y+=speedY*(CurTime-lastTime)/1000;
        lastTime=CurTime;
    }

    @Override
    public Image getImg() {
        if(hitcount>0){
            hitcount--;
            return img2;
        }
        return img;
    }

    public Image getImg(long time){
        if(time<hittime){
            return  img2;
        }
        return img;
    }
}

package game;

import java.awt.*;

public class CItem {
    Image img;
    int x;
    int y;
    CItem(){
    }
//    CItem(int x,int y,long lastTime){
//        this.x=x;
//        this.y=y;
//        this.lastTime=lastTime;
//    }
    int speedX=0;
    int speedY=1;
    long lastTime;
    public Image getImg(){
        return img;
    }


    public void moveByTime(long CurTime){
        x+=speedX*(CurTime-lastTime)/1000;
        y+=speedY*(CurTime-lastTime)/1000;
        lastTime=CurTime;
    }

    public void move(){
        x++;
        y++;
    }
    public void moveXBy(int len){
        x+=len;
    }
    public void moveYBy(int len){
        y+=len;
    }
    public void move(int x,int y){
        this.x=x;
        this.y=y;
    }
}

package game;

import javax.swing.*;
import java.awt.*;

public class CMe extends CItem{
    int speed=20;
    CMe(int x,int y){
        img=  new ImageIcon("img\\emoji3.png").getImage();
        this.x=x;
        this.y=y;
    }

}


  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值