太空入侵者 -- 《电玩游戏Java实战DIY》

飞机大战

这个让我想起,许多培训机构都会有一个项目“飞机大战”,读书那会儿也浏览了该代码;下载图片资源,发现这个资源与我读书时看得不一样,但可以使用,资源重整了一下

ShootGame射击游戏

在这里插入图片描述

1.给6个对象类添加图片属性,共有的读取图片属性在FlyingObject父类里面提取。

- 分别打开子弹、天空、英雄机、小敌机、大敌机、蜜蜂6个子类。
- Bullet、Sky类里面最上面添加子弹图片的属性,设为静态的,以及给image赋值放在静态块中,但是静态块中先不赋值。
- Hero类里面最上面添加子弹图片的属性,设为静态的,以及给image赋值放在静态块中,但是静态块中也先不赋值。
- 打开FlyingObject父类,读图片的行为子类共有的,放在父类里面,完成loadImage()方法。
- Bullet、Sky、Hero里面完成静态块里面图片的赋值。
- Airplane、BigAirplane、Bee类里面最上面添加子弹图片的属性,设为静态的,以及在静态块中给image。

2.ShootGame中—窗体的宽(512)、高(768)做成常量(适合)。

- World类里声明窗口宽、高为常量,主方法里面窗体大小修改为WIDTH,HEIGHT。
- Sky修改构造方法里面super(参数)改为World.WIDTH,World.HEIGHT、y1的坐标-this.height
- FlyingObject修改构造方法的x坐标World.WIDTH-this.width

3.画对象

3.1、获取对象图片
想画对象得先得到对象的图片,而我们分析每个对象都能得到图片,意味着得图片的行为,为所有派生类所共有的。
所以将得图片的行为设计在超类FlyingObject中,每个对象得到图片的行为都是不一样的(在派生类中需要重写getImage()),所以设计为抽象方法。
---行为不一样原因?看步骤3.3。 
3.2、需要在派生类分别进行一一重写getImage()。
- 射击游戏分别找6个对象列,进行重写。
- 重写之前先分析对象的状态。步骤3.3
3.2.1、天空Sky,直接返回image
3.2.2、子弹Bullet
 (1)若活着,直接返回image
 (2)若死了,则删除
 ---3.3.1步骤看对象的三种状态
3.2.3、英雄机Hero:
 (1)若活着呢,返回images[0]和images[1]切换
 (2)若死了呢,游戏结束了,不需要考虑图片
3.2.4、小敌机Airplane:
 (1)若活着呢,直接返回images[0]
 (2)若死了呢,images[1]到images[4]轮换,4后删除
3.2.5、大敌机BigAirplane:
(1)若活着呢,直接返回images[0]
(2)若死了呢,images[1]到images[4]轮换,4后删除
3.2.6、小蜜蜂Bee:
     (1)若活着呢,直接返回images[0]
     (2)若死了呢,images[1]到images[4]轮换,4后删除
//所以每个对象得到图片的行为都不一样,回到步骤3.1、FlyingObject中设计
//抽象方法getImage()。

//所有子类重写getImage()方法完成后到步骤3.4继续操作。    	     
3.3、对象状态
3.3.1、分为三种
---活着的
---死了的(还没有删除,有爆破效果)
---删除的
    回到3.2.3步骤。。。
3.3.2、获取图片时需要考虑对象的状态
因为需要在不同状态下得到不同的图片,所以需要设计对象的状态。
而每个对象都有状态,所以将状态设计在超类FlyingObject中,状态在实际项目中一般都设计为常量,后面不需要修改。
---在FlyingObject中设计LIFE、DEAD、REMOVE,state表示当前状态,四种属性。
因为每个对象都需要去判断它当前所属的状态,所以判断状态的行为为共有的, 需要设计在超类中。
又因每个对象判断状态的行为都是一样的,所以设计为普通方法。
---在FlyingObject中设计isLife(),isDead(),isRemove()三个方法。
//继续进行步骤3.2分别6个对象进行重写getImage()方法。

3.4、开始绘制

准备好图片之后可以开画了,每个对象都有画的功能,所以将画的行为
设计在超类FlyingObject中,每个对象画的行为都是一样的,所以设计为普通方法。
---在FlyingObject中设计paintObject()画笔方法。

3.5、是否需要重画

画对象的图片,除了天空都是一张图(敌人虽然不是一个但是每次画出来一个都一样),所以不需要重写。
天空Sky需要画两张图,y和y1两个坐标,需要重写超类的paintObject()。
----在Sky中重写paintObject(),图片画两次。
//完成后继续操作步骤3.6

3.6、画的行为写好之后,则需要在窗口上调用画笔方法。

想调用画笔方法,我们需要重写面板里面的画笔方法paint(),paint()方法并不需要
在主方法里调用,就因为它是自带的方法。
调用之前action()方法里面代码全部删除。
----在World类中重写paint()。
//所有完成,运行测试,背景图片、英雄机、子弹可以正常处理,3种敌人没出现,因为
//父类3种敌人构造方法里面,设置敌人坐标为负的,这样在窗体外,因此改为正的测试,测试完
//再调回负的。

太空入侵者

《电玩游戏Java实战DIY》中 "项目30 太空入侵者——太空\项目31 太空入侵者——外星人\项目32太空入侵者——复仇\项目33太空入侵者——生死较量"选中这个看一下,基本和“飞机大战”一样,可以复用

30 太空入侵者——太空

创 建 太 空 ,并 制 造 太 空 船 。

30.1 绘制天空Space

绘制一个黑色纯色背景
public class Space extends FlyingObject{

    public Space(int width, int height) {
        super(WIDTH, HEIGHT,0,0);
    }

    /**
     * 重写 paintObject() 画对象
     */
    public void paintObject(Graphics g) {
        g.drawImage(getImage(), x, y, null);
    }

    @Override
    public BufferedImage getImage() {
        return new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    }
}

30.2 绘制太空船Ship

获取hero0、hero1图片绘制,使用两个看起来有变化
public class Ship extends FlyingObject {
    //添加图片属性,英雄机图片两张
    private static BufferedImage[] heros;

    public Ship() {
        super(48, 69, 226, 562);

        heros = new BufferedImage[2];
        for (int i = 0; i < heros.length; i++) {
            heros[i] = ReadResource.loadImage("hero" + i + ".png");
        }
    }

    @Override
    public BufferedImage getImage() {
        return heros[0];
    }

    /**
     * 英雄机移动
     *
     * @param x 坐标x
     * @param y 坐标y
     */
    public void move(int x, int y) {
        //英雄机的x = 鼠标的x - this.width/2;
        this.x = x - this.width / 2;
        this.y = y - this.height / 2;
    }
}

30.3 抽象共有对象和共有方法

共有对象
public abstract class FlyingObject {

    protected int width;  //宽
    protected int height; //高
    protected int x;      //x坐标
    protected int y;      //y坐标

    public FlyingObject(int width, int height) {
        this.width = width;
        this.height = height;
    }

    public FlyingObject(int width, int height, int x, int y) {
        this.width = width;
        this.height = height;
        this.x = x;
        this.y = y;
    }

    /**
     * 画对象  g:画笔
     */
    public void paintObject(Graphics g) {
        /*
         * Graphics绘制图像的一个类,理解为一个画笔
         * Graphics对象封装了Java支持的基本呈现操作所需的状态信息。
         */
        //参数1代表img传图片,谁调就是谁的图片,x、y为对象的坐标,null用不到,不用问。
        g.drawImage(getImage(), x, y, null);
    }

    /**
     * 获取图片
     */
    public abstract BufferedImage getImage();
}
共有方法
public class ReadResource {
    private static final String PATH = "/res/img/";

    /**
     * 调整bufferedimage大小
     *
     * @param fileName BufferedImage 图片名称
     * @return BufferedImage  返回新image
     */
    public static BufferedImage loadImage(String fileName) {
        try {
            BufferedImage img =
                    ImageIO.read(ReadResource.class.getResource(PATH + fileName));
            return img;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException();
        }
    }

    /**
     * 调整bufferedimage大小
     *
     * @param source  BufferedImage 原始image
     * @param targetW int  目标宽
     * @param targetH int  目标高
     * @param flag    boolean 是否同比例调整
     * @return BufferedImage  返回新image
     */
    public static BufferedImage resizeBufferedImage(BufferedImage source, int targetW, int targetH, boolean flag) {
        int type = source.getType();
        BufferedImage target = null;
        double sx = (double) targetW / source.getWidth();
        double sy = (double) targetH / source.getHeight();
        if (flag && sx > sy) {
            sx = sy;
            targetW = (int) (sx * source.getWidth());
        } else if (flag && sx <= sy) {
            sy = sx;
            targetH = (int) (sy * source.getHeight());
        }
        if (type == BufferedImage.TYPE_CUSTOM) { // handmade
            ColorModel cm = source.getColorModel();
            WritableRaster raster = cm.createCompatibleWritableRaster(targetW, targetH);
            boolean alphaPremultiplied = cm.isAlphaPremultiplied();
            target = new BufferedImage(cm, raster, alphaPremultiplied, null);
        } else {
            target = new BufferedImage(targetW, targetH, type);
        }
        Graphics2D g = target.createGraphics();
        g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g.drawRenderedImage(source, AffineTransform.getScaleInstance(sx, sy));
        g.dispose();
        return target;
    }

    /**
     * 调整bufferedimage大小
     *
     * @param source BufferedImage 原始image
     * @return BufferedImage  返回新image
     */
    public static BufferedImage resizeBufferedImageByHalf(BufferedImage source) {
        return resizeBufferedImage(source, source.getWidth() / 2, source.getHeight() / 2, true);
    }
}

30.4 添加到战场SpaceDestroyers

public class SpaceDestroyers extends JPanel implements MouseMotionListener, MouseListener {
    public static final int WIDTH = 500;  //窗口的宽
    public static final int HEIGHT = 700; //窗口的高

    private Space sky = new Space(); //太空背景
    private Ship ship = new Ship(); //飞船

    public SpaceDestroyers() {
        addMouseMotionListener(this);
        addMouseListener(this);
    }

    @Override
    public void mouseDragged(MouseEvent event) {
    }

    @Override
    public void mouseMoved(MouseEvent event) {
        int x = event.getX();
        int y = event.getY();
        ship.move(x, y);
        repaint();
    }

    @Override
    public void mouseClicked(MouseEvent e) {
    }

    @Override
    public void mousePressed(MouseEvent e) {
    }

    @Override
    public void mouseReleased(MouseEvent e) {
    }

    @Override
    public void mouseEntered(MouseEvent e) { //鼠标移入事件
    }

    @Override
    public void mouseExited(MouseEvent e) { //鼠标移出事件
    }
}

30.5 运行效果

游戏入口RunGame 
public class RunGame {
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        SpaceDestroyers space = new SpaceDestroyers();;
        frame.add(space);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(space.WIDTH, space.HEIGHT);
        frame.setLocationRelativeTo(null);
        frame.setResizable(false);
        frame.setVisible(true);
    }
}

在这里插入图片描述

31 太空入侵者——外星人

31.1 生成敌机Bee

1》父类添加“生、死”状态,和绘制坐标变化step()
2》被子弹击中爆炸图片播放
public class Bee extends FlyingObject {

    private static BufferedImage[] bees;

    static {
        bees = new BufferedImage[5];
        bees[0] = ReadResource.loadImage("bee1.png");
        for (int i = 1; i < bees.length; i++) {
            bees[i] = ReadResource.loadImage("bom" + i + ".png");
        }
    }

    private int xSpeed; //x坐标移动速度
    private int ySpeed; //y坐标移动速度

    private int deadIndex = 1; // 死了,爆炸开始的下标

    public Bee() {
        super(48, 41);

        xSpeed = 1;
        ySpeed = 2;
    }

    @Override
    public void step() {
        x += xSpeed; //x坐标移动速度(或左或又)
        y += ySpeed; //y坐标移动速度

        if (x <= 0 || x >= WIDTH) {
            xSpeed *= -1; //切换方向
        }
    }

    @Override
    public BufferedImage getImage() { //每10毫秒走一次
        if (isLife()) { //若活着呢
            return bees[0]; //返回images[0]
        } else if (isDead()) {
            BufferedImage img = bees[deadIndex++]; //从第2张开始轮转
            if (deadIndex == bees.length) { //若到最后一张了
                state = REMOVE; //则修改当前状态为删除状态
            }
            return img;
        }
        return null;
    }

    @Override
    public boolean outOfBounds() {
        //蜜蜂越界 = 蜜蜂的y >= 窗体的高
        return this.y >= HEIGHT;
    }
}

31.2 将敌机添加到战场SpaceDestroyers

    private FlyingObject[] enemies = {}; //敌机(小蜜蜂)数组
    private int enterindex = 0; //敌人数组角标

    // 游戏状态
    public static final int START = 0; //开始状态
    public static final int RUNNING = 1; //运行状态
    public static final int PAUSE = 2; //暂停状态
    public static final int GAME_OVER = 3; //结束状态
    public int state = START;// 默认状态
    
    //生成敌人进行储存
    public void populateEneraies() {//每10毫秒
        enterindex++;
        if (enterindex % 40 == 0) {//限制敌人的出场速率,每400毫秒走一次
            //获取敌人对象
            FlyingObject obj = generateEnemy();
            //将敌人数组进行扩容
            enemies = Arrays.copyOf(enemies, enemies.length + 1);
            //将敌人对象添加到数组中
            enemies[enemies.length - 1] = obj;
            //System.out.println("敌人数量:"+enemies.length);
        }
    }

    //生成敌人(蜜蜂)对象;可以增加敌人种类如Boss等
    public FlyingObject generateEnemy() {
        return new Bee();
    }
    
    @Override
    public void paint(Graphics g) {
        sky.paintObject(g); // 绘制太空背景
        ship.paintObject(g); // 绘制飞船
        for (int i = 0; i < enemies.length; i++) { //遍历所有敌人
            enemies[i].paintObject(g); //绘制敌人
        }
    }

    //定时器
    public void action() {
        java.util.Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                if (state == RUNNING) {
                    stepAction(); //飞行物移动
                    repaint(); //重绘
                }
            }
        }, 10, 10);
    }
    
    //飞行物移动
    public void stepAction() {
        populateEneraies(); //生成敌机

        sky.step();
        for (int i = 0; i < enemies.length; i++) {
            enemies[i].step();
        }
    }
  • java.util.Timer定时器自动生成敌机
  • repaint() 绘制放到定时器,并添加游戏状态
    在这里插入图片描述

31.2 添加子弹Bullet

public class Bullet extends FlyingObject {

    private static BufferedImage bullet; //添加子弹图片

    static {
        bullet = ReadResource.loadImage("bullet.png");
    }

    private int speed; //移动速度

    public Bullet(int x, int y) {
        super(8, 20, x, y);
        speed = 3;
    }

    @Override
    public void step() {
        y -= speed;
    }

    @Override
    public BufferedImage getImage() {
        if (isLife()) { //活着的
            return bullet; // 直接返回image
        } else if (isDead()) { // 死了的
            state = REMOVE; // 将状态修改为删除状态
        }
        return null; // 死了返回空
    }

    @Override
    public boolean outOfBounds() {
        //子弹越界 = 子弹的y <= 负的自身的高度
        return this.y <= -this.height;
    }
}

31.3 为Ship添加子弹Bullet的射击shoot()

    //生成子弹对象,因为子弹是由英雄机发射出来
    public Bullet[] shoot() {//1发 2发  或 4发
        //判断子弹的火力,是单倍火力还是双倍火力
        if (fire > 0) {//双倍火力
            Bullet[] bs = new Bullet[2];
            //在英雄机1/4的位置
            bs[0] = new Bullet(this.x + this.width * 1 / 4, this.y - 20);
            //在英雄机2/4的位置
            bs[1] = new Bullet(this.x + this.width * 3 / 4, this.y - 20);
            fire -= 2;//每生成一次双别火力,火力值减2
            return bs;
        } else {//单倍火力
            Bullet[] bs = new Bullet[1];
            /*
             * 单倍火力,一颗子弹,子弹位置在英雄机的正中间
             * 子弹的x = 1/2的英雄机的宽 + 英雄机的x;
             * 子弹的y = 英雄机的y - 子弹的高度;
             */
            bs[0] = new Bullet(this.x + this.width / 2, this.y - 20);
            return bs;
        }
    }

31.4 将子弹添加到战场SpaceDestroyers

    //生成子弹对象进行储存
    public void shootAction() {
        //限制子弹的出场速率,该速率要比敌人稍快
        shootindex++;
        if (shootindex % 30 == 0) {
            //获取子弹对象
            Bullet[] bs = ship.shoot();
            //将子弹数组进行扩容
            bullets = Arrays.copyOf(bullets, bullets.length + bs.length);
            //将子弹对象添加到数组中
            System.arraycopy(bs, 0, bullets, bullets.length - bs.length, bs.length);

            //System.out.println(bullets.length);
        }
    }

    @Override
    public void paint(Graphics g) {
        sky.paintObject(g); // 绘制太空背景
        ship.paintObject(g); // 绘制飞船
        for (int i = 0; i < enemies.length; i++) { //遍历所有敌人
            enemies[i].paintObject(g); //绘制敌人
        }
        for (int i = 0; i < bullets.length; i++) { //遍历所有子弹
            bullets[i].paintObject(g); //画子弹
        }
    }

    //定时器
    public void action() {
        java.util.Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                if (state == RUNNING) {
                    stepAction(); //飞行物移动
                    repaint(); //重绘
                }
            }
        }, 10, 10);
    }

    //飞行物移动
    public void stepAction() {
        populateEneraies(); //生成敌机
        shootAction(); //存储子弹,ship射击子弹
        sky.step();
        for (int i = 0; i < enemies.length; i++) {
            enemies[i].step();
        }
        for (int i = 0; i < bullets.length; i++) {
            bullets[i].step();
        }
    }

在这里插入图片描述

31.5 子弹与敌机撞上、飞船与敌机撞上

父类FlyingObject添加碰撞逻辑判断
    /**
     * 碰撞逻辑判断
     */
    //other---代表子弹、(或者英雄机)
    //this---表示敌人
    public boolean hit(FlyingObject other) {
        int x1 = this.x - other.width;
        int x2 = this.x + this.width;
        int y1 = this.y - other.height;
        int y2 = this.y + this.height;

        int x = other.x;
        int y = other.y;

        return x >= x1 && x <= x2 && y >= y1 && y <= y2;
    }
    
    /**
     * 飞行物去死
     */
    public void goDead() {
        state = DEAD;
    }
在战场SpaceDestroyers判断撞击,设为Dead状态
    //子弹与敌人碰撞
    public void bulletBangAction() {
        //遍历获取所有子弹
        for (int i = 0; i < bullets.length; i++) {
            Bullet b = bullets[i];
            //遍历获取所有的敌人
            for (int j = 0; j < enemies.length; j++) {
                FlyingObject f = enemies[j];
                //具体碰撞条件
                if (b.isLife() && f.isLife() && f.hit(b)) {//条件满足,代表撞上了
                    //敌人去死
                    f.goDead();
                    //子弹去死
                    b.goDead();
                }
            }
        }
    }

    //飞船与敌人碰撞
    public void shipBangAction() {
        //遍历所有敌人
        for (int j = 0; j < enemies.length; j++) {
            //获取所有敌人
            FlyingObject f = enemies[j];

            if (ship.isLife() && f.isLife() && f.hit(ship)) {
                //敌人去死
                f.goDead();
                //英雄机减命
                ship.substractLife();
                //英雄机火力值归0
                ship.clearFire();
            }
        }
    }
子弹与敌机goDead()后,在获取绘制图片getImage()时走到移除REMOVE状态;在定时器java.util.Timer添加清除
    @Override
    public BufferedImage getImage() {
        if (isLife()) {
            return bullet;
        } else if (isDead()) {
            state = REMOVE; // 将状态修改为删除状态
        }
        return null;
    }
    @Override
    public BufferedImage getImage() { //每10毫秒走一次
        if (isLife()) {
            return bees[0]; //返回images[0]
        } else if (isDead()) {
            BufferedImage img = bees[deadIndex++]; //从第2张开始轮转
            if (deadIndex == bees.length) { //若到最后一张了
                state = REMOVE; //则修改当前状态为删除状态
            }
            return img;
        }
        return null;
    }

31.6 在SpaceDestroyers战场清除越界和已移除的飞行物

    //定时器
    public void action() {
        java.util.Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                if (state == RUNNING) {
                    stepAction(); //飞行物移动
                    cleanFlyingAction();
                    repaint(); //重绘
                }
            }
        }, 10, 10);
    }

    //删除越界和死亡移除的飞行物(敌机、子弹)
    public void cleanFlyingAction() {
        bulletBangAction();//子弹与敌人碰撞
        shipBangAction();//英雄机与敌人碰撞

        int index = 0; //计数
        //1、删除越界和死亡移除的敌人
        FlyingObject[] enemiesLife =
                new FlyingObject[enemies.length];
        for (int i = 0; i < enemies.length; i++) {
            FlyingObject f = enemies[i];
            if (!f.isRemove() && !f.outOfBounds()) {
                //将不越界且没有死亡移除的敌人添加到数组中
                enemiesLife[index] = f;
                index++;
            }
        }
        enemies = Arrays.copyOf(enemiesLife, index);

        //2、删除越界和死亡移除的子弹
        index = 0;//计数
        Bullet[] bulletsLife = new Bullet[bullets.length];
        for (int i = 0; i < bullets.length; i++) {
            Bullet b = bullets[i];
            if (!b.isRemove() && !b.outOfBounds()) {
                bulletsLife[index] = b;
                index++;
            }
        }
        bullets = Arrays.copyOf(bulletsLife, index);
    }

在这里插入图片描述

显示游戏命条数和分数

添加得分接口
/*
 * 得分接口;可为不同敌机实现的不同得分
 */
public interface Enemey {
    public int getScore();
}
敌机继承接口public class Bee extends FlyingObject implements Enemey
子弹与敌人碰撞增加分数【score += ((Enemey) f).getScore()】和火力【ship.addFire(score)】,增加50分增加40发火力
paint(Graphics g)显示游戏命条数和分数
        //绘制分数和命
        //设置字体
        g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 18));
        //设置颜色
        g.setColor(new Color(255, 200, 0));
        g.drawString("分数:" + score, 10, 25);

        g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 18));
        g.setColor(new Color(255, 0, 0));
        g.drawString("命数:" + ship.getLife(), 10, 55);

在这里插入图片描述

最后游戏结束判断

检测飞船生命为零游戏结束
    //检测游戏结束
    public void checkGameOver() {
        if (ship.getLife() <= 0) {
            state = GAME_OVER;
        }
    }
paint(Graphics g)显示游戏状态
        //绘制游戏状态
        g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 36));
        String strState = "";
        switch (state) {
            case START:
                g.setColor(new Color(0, 13, 255));
                strState = "点击开始游戏";
                break;
            case PAUSE:
                g.setColor(new Color(255, 200, 0));
                strState = "暂停游戏";
                break;
            case GAME_OVER:
                g.setColor(new Color(255, 0, 0));
                strState = "GAME OVER";
                break;
        }
        g.drawString(strState, (WIDTH - (36 * strState.length())) / 2, HEIGHT / 2);
游戏结束状态转变处理
    @Override
    public void mouseClicked(MouseEvent event) {
        switch (state) {
            case START:
            case PAUSE:
                int x = event.getX();
                int y = event.getY();
                ship.move(x, y);
                state = RUNNING;
                break;
            case RUNNING:
                state = PAUSE;
                break;
            case GAME_OVER:
                state = START;
                score = 0;
                sky = new Space();
                ship = new Ship();
                enemies = new FlyingObject[0];
                bullets = new Bullet[0];
                break;
        }
    }

太空入侵者源码SpaceDestroyers.7z
在这里插入图片描述

自行查看一下"32 太空入侵者——复仇\33 太空入侵者——生死较量" ,就是敌机添加子弹和飞船放大招

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xhBruce

佛系随缘,共同探讨

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值