JAVA简单实现坦克对战(只有坦克和子弹)

超级简单JAVA实现坦克大战游戏

这个游戏我企图用我仅有的一点的java知识和面对对象的知识来编程,里面不合理的地方请不要介意,如果能留下您宝贵的意见,感激不尽。

文件目录结构

在这里插入图片描述
游戏主要分成:
实体类:子弹(Bullet)类、坦克(Tank)类
线程类:子弹移动类(BulletMoveThread)、英雄坦克移动类(TankMoveThread)、敌方坦克自动射击移动类(EnemyTankMoveThread)

效果图

在这里插入图片描述

游戏主体
package game;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;


/*
 * 游戏主体
 * */
public class Game {
	private JFrame jf = new JFrame("坦克大战");    //创建一个JFrame对象,存放整个窗体
	JPanel topMenu = new JPanel(); // 头部菜单
	private int enemyNum; // 敌机数量
	private Tank[] enemy;// 敌机
	private EnemyTankMoveThread[] enemyTank; // 敌机的移动线程
	// 以下变量被我用static修饰,是为了方便我在isGameOver(判断游戏结束)方法中能使用到
	// 因为静态方法得使用静态变量
	// isGameOver在Tank类中的reduceLifeVaule(扣除生命值)方法中被调用
	private static JPanel jp = new JPanel(); // 游戏主体
	private static int heroLifeNum; // 我方生命
	private static JLabel heroLifeNumLabel = new JLabel(); // 提示英雄方剩余生命的标签
	private static JLabel enemyNumLabel = new JLabel(); // 提示敌机数量的标签
	private static Tank hero; // 英雄机(我方)	
	private static TankMoveThread heroTank; // 英雄机的移动线程
	public Game() {
		// 游戏初始化
		jf.setLayout(new BorderLayout()); //设置边框布局
		jf.setBounds(300, 100, 600, 650); //设置窗口大小和位置
		jf.setVisible(true); // 设置窗体可见,不写看不见
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		// 头部区域
		gameHeader();
		// 游戏区域
		init();
	}
	void gameHeader() {
		// 游戏头部
		topMenu.removeAll();
		JLabel enemyNumTips = new JLabel("敌机数量"); // 提示标签
		JLabel heroLifeNumTips = new JLabel("我方生命"); // 提示标签
		JButton resetButton = new JButton("新对局"); // 重置按钮
		resetButton.addActionListener(new ResetGame());
		topMenu.add(enemyNumTips);  
		topMenu.add(enemyNumLabel);
		topMenu.add(heroLifeNumTips);
		topMenu.add(heroLifeNumLabel);
		topMenu.add(resetButton);
		jf.add(topMenu, BorderLayout.NORTH);
	}
	void init() {
		jp.removeAll(); // 清空游戏主体的内容
		jp.setLayout(null);
		// 初始化(顶部栏)数据
		enemyNum = 10; // 敌人数量
		heroLifeNum = 3; // 我方生命
		enemyNumLabel.setText(String.valueOf(enemyNum)); 
		heroLifeNumLabel.setText(String.valueOf(heroLifeNum));
		// 添加敌机
		enemy = new Tank[enemyNum]; // 敌机组
		enemyTank = new EnemyTankMoveThread[enemyNum]; // 敌机线程组
		for (int i=0;i<enemyNum;i++) {
			enemy[i] = new Tank(10,10); // 新建敌机
			enemy[i].setBelongTo("enemy"); // 标识为敌机
			enemy[i].setBackground(Color.red); // 设置颜色
			enemyTank[i] = new EnemyTankMoveThread(); // 新建对应线程
			enemyTank[i].setTank(enemy[i]); // 将坦克传入到线程中
			enemyTank[i].start(); // 开启线程
			enemyTank[i].resumeThread(); // 唤醒线程
			jp.add(enemy[i]); // 添加JPanel容器中
		}
		// 设置游戏主题内容
		
		hero = new Tank(jf.getSize().width-80,jf.getSize().height-140);
		hero.setBelongTo("hero"); // 表示为英雄机
		jp.add(hero);
		heroTank = new TankMoveThread();
		heroTank.setTank(hero);
		heroTank.start();
		jp.setBackground(Color.black);//设置背景色
		jp.addKeyListener(new keyCodeListener());
		jp.enableInputMethods (false); // 切换输入法状态,默认窗口打开时输入法是中文的,导致(子弹)键盘按键不可用
		jf.add(jp, BorderLayout.CENTER);
		// (如果没有这句(下面的这句代码),swing默认jframe和jpanel和jlabel是没有焦点的,
		// swing自动把焦点给了窗体中第一个可以获取焦点的组件,
		// 因为我把键盘监听事件(上一句)添加到jp,所以jp要获取焦点)
		jp.requestFocus();  // jp获取焦点,才能执行键盘的监听事件
	}
	public static void main(String[] args) {
		new Game();
	}
	public static void isGameOver() {
		int enemys = 0; // 敌机数量
		int heros = 0; // 英雄机
		for (int i = 0; i < jp.getComponentCount(); i++) {
			Object obj = jp.getComponent(i);
		    if (obj instanceof Tank) {
		    	// 先找出坦克
		    	if (((Tank)obj).getBelongTo() == "enemy") {
		    		// 敌机
		    		enemys++;
		    	}
		    	if (((Tank)obj).getBelongTo() == "hero") {
		    		// 英雄机
		    		heros++;
		    	}
		    }
		}
		if (enemys == 0) {
			// 敌机数量为0,以消灭玩所有的敌机,赢了
			// 停止英雄机的移动线程
			Game.hero.setExist(false); // 因为坦克移动线程的终止条件是坦克不存在,所以这里之间设置了坦克不存在。
			// 提示信息
			JOptionPane.showMessageDialog(Game.jp, "成功消灭所有敌机,你赢得了战争", "游戏结束", JOptionPane.ERROR_MESSAGE);
		}
		if (Game.heroLifeNum == 0) {
			// 停止敌机的移动线程
			for (int i = 0; i < jp.getComponentCount(); i++) {
				Object obj = jp.getComponent(i);
			    if (obj instanceof Tank) {
			    	((Tank)obj).setExist(false);
			    }
			}
			// 提示信息
			JOptionPane.showMessageDialog(Game.jp, "噢不,你输掉了战争,失去了一切", "游戏结束", JOptionPane.ERROR_MESSAGE);
		}
		if (heros == 0 && Game.heroLifeNum != 0) {
			// 初始化英雄坦克
			hero = new Tank(jp.getSize().width-70,jp.getSize().height-70);
			hero.setBelongTo("hero"); // 表示为英雄机
			// 初始化英雄坦克的移动线程
			heroTank = new TankMoveThread();
			heroTank.setTank(hero);
			heroTank.start();
			// 添加到JPanel中
			jp.add(hero);
			Game.heroLifeNum--;
		} 
		// 设置页面上的对应的英雄生命数量
		Game.heroLifeNumLabel.setText(String.valueOf(Game.heroLifeNum));
		Game.enemyNumLabel.setText(String.valueOf(enemys));
		
	}
	public void reset() {
		// 停止敌机和子弹的线程
		for (int i = 0; i < jp.getComponentCount(); i++) {
			Object obj = jp.getComponent(i);
		    if (obj instanceof Tank) {
		    	((Tank)obj).setExist(false);
		    }
		    if (obj instanceof Bullet) {
		    	((Bullet)obj).setExist(false);	    	
		    }
		}
		// 重绘窗体
    	jf.repaint();
    	// 头部区域
		gameHeader();
		// 游戏区域
		init();
	}
	class keyCodeListener implements KeyListener{
		@Override
		public void keyPressed(KeyEvent e) {
			// 按键按下未松开
			// 1号玩家坦克
			if (e.getKeyCode() == KeyEvent.VK_LEFT || e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_RIGHT || e.getKeyCode() == KeyEvent.VK_DOWN) {
				switch(e.getKeyCode()) {
					case  KeyEvent.VK_LEFT :
						heroTank.setDirection("left");
						break;
					case  KeyEvent.VK_UP :
						heroTank.setDirection("up"); 
						break;
					case  KeyEvent.VK_DOWN :
						heroTank.setDirection("down"); 
						break;
					case  KeyEvent.VK_RIGHT : 
						heroTank.setDirection("right"); 
						break;
				}
				// 唤醒1号玩家坦克移动线程
				heroTank.resumeThread();
			}
			// 1号玩家射击
			if (e.getKeyCode() == KeyEvent.VK_NUMPAD1) {
				// 创建一个子弹线程,并传入一个子弹,字段类所需参数是坦克的方向
				Bullet bullet = new Bullet("hero",hero.getDirection(),hero.getPositionX(),hero.getPositionY());
				jp.add(bullet);
				new BulletMoveThread(bullet).start();
			}
		}
		@Override
	    public void keyReleased(KeyEvent e) {
			// 按键松开时
			// 1号玩家坦克
			if (e.getKeyCode() == KeyEvent.VK_LEFT || e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_RIGHT || e.getKeyCode() == KeyEvent.VK_DOWN) {	
				//停止1号玩家坦克移动线程
				heroTank.pauseThread();
			}
	    }
		@Override
	    public void keyTyped(KeyEvent e) {
			// 按键输入时
	    }
	}
	class ResetGame implements ActionListener{
    	// 重置游戏
    	public void actionPerformed(ActionEvent event){
    		reset();
    	}
    }
}

坦克类
package game;

import javax.swing.JLabel;
import javax.swing.JPanel;

import java.awt.Color;
import java.awt.Dimension;
/*
 坦克类
*/
public class Tank extends JLabel{
	/*
	  坦克的属性:颜色,运动方向,移动速度,状态(生存、死亡),位置(x,y),阵营(敌机还是英雄)
	 * */
	private Color tankColor = Color.yellow; // 坦克的颜色
	private int movingSpeed = 10; // 移动速度
	private int positionX = 10; // X坐标
	private int positionY = 10; // Y坐标
	private String belongTo; // 是英雄(hero)还是敌机(enemy)
	private String direction = "up"; // 方向,子弹类会需要这个值
	private int lifeValue = 3;// 生命值
	private boolean exist = true; // 判断是否存在的标志
	public Tank(int x, int y) {
		this.setBackground(tankColor);
		this.setOpaque(true); // 是否不透明,不设没颜色。。。。
		//this.setPreferredSize(new Dimension(50, 50)); 
		this.positionX = x;
		this.positionY = y;
		this.setBounds(positionX, positionY, 50, 50);
		this.setText("↑");
		this.setHorizontalAlignment(JLabel.CENTER);
		this.setFont(new java.awt.Font("Dialog", 1, 30));
	}
	public void setTankColor(Color color) {
		// 设置背景颜色
		this.tankColor = color;
	}
	public void reduceLifeVaule() {
		// 扣除生命值
		this.lifeValue--;
		if (this.lifeValue == 0) {
			// 生命值扣完,对应坦克死亡,消除存在
			this.exist = false;
			// 从JPanel中删除
			JPanel jp = (JPanel)this.getParent();
			for (int i = 0; i < jp.getComponentCount(); i++) {
			    Object obj = jp.getComponent(i);
			    if (obj instanceof Tank && (Tank)obj == this) {
			    	jp.remove((Tank)obj);
			    	jp.repaint();
			    	Game.isGameOver();
			    }
			}
			
		}
	}
	public void setName(String name) {
		this.setText(name);
	}
	public void setMovingSpeed(int speed) {
		// 设置移动速度
		this.movingSpeed = speed;
	}
	public void setPositionX(int x) {
		// 设置PositionX
		this.positionX = x;
	}
	public void setPositionY(int y) {
		// 设置PositionY
		this.positionY = y;
	}
	public void setExist(boolean exist) {
		this.exist = exist;
	}
	public void setBelongTo(String belongTo) {
		// 设置阵营
		this.belongTo = belongTo;
	}
	public String getBelongTo() {
		// 返回
		return this.belongTo;
	}
	public int getPositionX() {
		// 返回X轴坐标
		return this.positionX;
	}
	public int getPositionY() {
		// 返回Y轴坐标
		return this.positionY;
	}
	public boolean getExist() {
		return this.exist;
	}
	public void setPosition (String direction) {
		// direction 方向
		if (this.exist == false) return;
		switch(direction) {
			case "up":
				this.setText("↑");
				this.direction = "up";
				if (this.positionY > this.movingSpeed ) this.positionY -= this.movingSpeed;
				break;
			case "right":
				this.setText("→");
				this.direction = "right";
				// 当父容器宽度大于 当前X轴坐标+当前坦克宽度+移动速度时,目的是为了让坦克不碰到窗体边界
				if(this.getParent().getWidth() > this.positionX+this.getSize().width+this.movingSpeed*2) this.positionX += this.movingSpeed;
				break;
			case "down":
				this.setText("↓");
				this.direction = "down";
				if(this.getParent().getHeight() > this.positionY+this.getSize().height+this.movingSpeed*2) this.positionY += this.movingSpeed;
				break;
			case "left":
				this.setText("←");
				this.direction = "left";
				if (this.positionX > 10 ) this.positionX -= this.movingSpeed;
				break;
		}
		this.setBounds(positionX, positionY, 50, 50);
	}
	public String getDirection() {
		return this.direction;
	}
	public static void main(String[] args) {
	}
}

子弹类
package game;

import java.awt.Color;
import java.awt.Rectangle;

import javax.swing.JLabel;
import javax.swing.JPanel;

/*
  子弹类
  */
public class Bullet extends JLabel{
	/*
	  属性:速度, 方向, 大小,状态(消失、出现)
	  方法:
	 * */
	private Color bulletColor = Color.yellow; // 子弹的颜色
	private int movingSpeed = 5; // 移动速度
	private int positionX = 0; // X坐标
	private int positionY = 0; // Y坐标
	private String direction; // 移动方向
	private boolean exist = true; // // 判断是否存在的标志
	private String belongTo; // 是英雄(hero)的子弹还是敌机(enemy)的子弹
	public Bullet(String belongTo,String direction, int x, int y) {
		// 传入三个参数,方向,坦克的XY轴坐标
		this.setBackground(bulletColor);
		this.setOpaque(true); // 是否不透明,不设没颜色。。。。
		this.belongTo = belongTo; // 是英雄的子弹还是敌机的子弹
		this.positionX = x+20; // 加上20是为了让子弹到坦克的中间
		this.positionY = y+20;
		this.setBounds(positionX, positionY, 10, 10);
		this.direction = direction;
	}
	public void setExist(boolean exist) {
		this.exist = exist;
	}
	public void setPosition () {
		// direction 方向
		// switch 内部的if判断是为了不让子弹超出边界
		if (this.exist == false) return;
		switch(direction) {
			case "up":
				if (this.positionY > this.movingSpeed ) this.positionY -= this.movingSpeed; else this.exist = false;
				break;
			case "right":
				if(this.getParent().getParent().getWidth() > this.positionX+this.getSize().width+this.movingSpeed*2) this.positionX += this.movingSpeed;  else this.exist = false;
				break;
			case "down":
				if(this.getParent().getParent().getHeight() > this.positionY+this.getSize().height*5+this.movingSpeed*2) this.positionY += this.movingSpeed;  else this.exist = false;
				break;
			case "left":
				if (this.positionX > 10 ) this.positionX -= this.movingSpeed; else this.exist = false;
				break;
		}
		this.setBounds(positionX, positionY, 10, 10);
		isRam(); // 判断子弹是否碰撞其他单位,如果撞击会修改exist的值
		if (!this.exist){/*如果子弹撞到边界或是撞击了其他单位*/clearBullet(); /*执行清除方法*/}
	}
	private void isRam() {
		// 判断子弹是否碰撞其他单位
		JPanel jp = (JPanel)this.getParent();
		for (int i = 0; i < jp.getComponentCount(); i++) {
			if (jp.getComponent(i) != null) {
				Object obj = jp.getComponent(i);
			    if (obj instanceof Tank && ((Tank) obj).getBelongTo() != this.belongTo) {
			    	// 找出所有的坦克并且不是跟子弹的发出阵营相同的坦克
			    	Tank tank = (Tank)obj;
			    	Rectangle currentBullet = new Rectangle(positionX,positionY,5,5); // x轴坐标,y轴坐标,宽和高,均为int类型
			    	Rectangle currentTank = new Rectangle(tank.getPositionX(),tank.getPositionY(),50,50); 
			    	if (currentBullet.intersects(currentTank)) {
			    		// 发生了碰撞,修改子弹的存在状态
			    		this.exist = false;
			    		// 被撞击的坦克,生命值减1
			    		tank.reduceLifeVaule();
			    	}
			    }
			}
		}
	}
	private void clearBullet() {
		// 找出JPanel中的子弹,如果是当前子弹就将此子弹从JPnael中清除掉
		JPanel jp = (JPanel)this.getParent();
		for (int i = 0; i < jp.getComponentCount(); i++) {
			if (jp.getComponent(i) != null ) {
				Object obj = jp.getComponent(i);
			    if (obj instanceof Bullet) {
			    	// 如果类型是子弹
			    	if ((Bullet)obj == this) {
			    		// 如果是当前子弹
			    		//this.setVisible(false);
			    		jp.remove((Bullet)obj); // 移除子弹
					    jp.repaint(); // 重绘JPanel
			    	}
			    }
			}
		}
	}
	public boolean getExist() {
		// 返回exist值
		return this.exist;
	}
	public static void main(String[] args) {
	}
}

子弹移动线程类
package game;


/*
 * https://www.cnblogs.com/bieyaoxiguan/p/11493590.html 此线程类参考借鉴了这个博客
 * 
		    // 1.创建自己的线程
			MyThread myThread = new MyThread();
			
			// 2.在合适的地方启动线程(你需要在什么地方启动它)
			myThread.start();
			
			// 3.启动后线程的 阻塞/暂停
			myThread.pauseThread();
			
			// 4.以及 阻塞/暂停 线程后的 唤醒/继续
			myThread.resumeThread();
 * */
public class BulletMoveThread extends Thread {
//-------------------------------------------------------------
    //无意义
    private final Object lock = new Object();

    //标志线程阻塞情况
    private boolean pause = false;
    /**
     * 设置线程是否阻塞
     */
    public void pauseThread() {
        this.pause = true;
    }

    /**
     * 调用该方法实现恢复线程的运行
     */
    public void resumeThread() {
        this.pause = false;
        synchronized (lock) {
            //唤醒线程
            lock.notify();
        }
    }

    /**
     * 这个方法只能在run 方法中实现,不然会阻塞主线程,导致页面无响应
     */
    void onPause() {
        synchronized (lock) {
            try {
                //线程 等待/阻塞
                lock.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
//-------------------------------------------------以上内容不适合修改  
    private Bullet bullet; // 子弹
    public BulletMoveThread(Bullet bullet) {
    	// 构造子弹线程时,传入子弹
    	this.bullet = bullet;
    }
    @Override
    public void run() {
        super.run();
        //一直循环
        while (bullet.getExist()) {
        // 如果子弹已经达到边界,即返回值为false,此线程结束
            try {
            	//程序每20毫秒执行一次 值可更改
                Thread.sleep(20);
                // 子弹移动
                bullet.setPosition();
                // 判断子弹是否碰到其他坦克
            } catch (Exception e) {
                e.printStackTrace();
                break;
            }
        }
    }
}

英雄坦克移动线程类
package game;
/*
 * https://www.cnblogs.com/bieyaoxiguan/p/11493590.html
 * 
		    // 1.创建自己的线程
			MyThread myThread = new MyThread();
			
			// 2.在合适的地方启动线程(你需要在什么地方启动它)
			myThread.start();
			
			// 3.启动后线程的 阻塞/暂停
			myThread.pauseThread();
			
			// 4.以及 阻塞/暂停 线程后的 唤醒/继续
			myThread.resumeThread();
 * */
public class TankMoveThread extends Thread {
//-------------------------------------------------------------
    //无意义
    private final Object lock = new Object();

    //标志线程阻塞情况
    private boolean pause = false;
    /**
     * 设置线程是否阻塞
     */
    public void pauseThread() {
        this.pause = true;
    }

    /**
     * 调用该方法实现恢复线程的运行
     */
    public void resumeThread() {
        this.pause = false;
        synchronized (lock) {
            //唤醒线程
            lock.notify();
        }
    }

    /**
     * 这个方法只能在run 方法中实现,不然会阻塞主线程,导致页面无响应
     */
    void onPause() {
        synchronized (lock) {
            try {
                //线程 等待/阻塞
                lock.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
//-------------------------------------------------以上内容不适合修改  
    private String direction = ""; // 坦克移动方向
    private Tank tank; // 坦克
    public void setDirection(String direction) {
    	// 设置移动方向
    	this.direction = direction;
    }
    public void setTank(Tank tank) {
    	// 设置该线程对应的坦克
    	this.tank = tank;
    }
    @Override
    public void run() {
        super.run();
        int i = 0;
        //一直循环
        while (tank.getExist()) {
            if (pause || direction == "") {
                //线程 阻塞/等待
                onPause();
            }
            try {
            	//程序每50毫秒执行一次 值可更改
                Thread.sleep(50);
                // 坦克移动
                tank.setPosition(direction);
            } catch (Exception e) {
                e.printStackTrace();
                break;
            }
        }
    }
}

敌方坦克(自动行走及射击)线程类
package game;

import java.util.Random;

public class EnemyTankMoveThread extends Thread{
	//-------------------------------------------------------------
    //无意义
    private final Object lock = new Object();

    //标志线程阻塞情况
    private boolean pause = false;
    /**
     * 设置线程是否阻塞
     */
    public void pauseThread() {
        this.pause = true;
    }

    /**
     * 调用该方法实现恢复线程的运行
     */
    public void resumeThread() {
        this.pause = false;
        synchronized (lock) {
            //唤醒线程
            lock.notify();
        }
    }

    /**
     * 这个方法只能在run 方法中实现,不然会阻塞主线程,导致页面无响应
     */
    void onPause() {
        synchronized (lock) {
            try {
                //线程 等待/阻塞
                lock.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
//-------------------------------------------------以上内容不适合修改  
    private Tank tank; // 坦克
    private  Random r = new Random(1); // 随机数
    private int step = 0; // 坦克移动的步数,到了就换方向
    private int rate = 0; // 射击频率
    private String direction; // 移动方向
    public void setTank(Tank tank) {
    	// 设置该线程对应的坦克
    	this.tank = tank;
    }
    @Override
    public void run() {
        super.run();
        //一直循环
        while (tank.getExist()) {
            if (pause) {
                //线程 阻塞/等待
                onPause();
            }
            try {
            	// 敌机坦克射击(射击频率)
            	if (rate == 0) {
            		// 如果射击频率运行到了0,就射击
            		while(rate == 0) {/* 为0就循环,直到不为0*/rate = (int)(Math.random()*20);}
		        	Bullet bullet = new Bullet("enemy",tank.getDirection(),tank.getPositionX(),tank.getPositionY());
		        	if (tank.getParent()!= null ) {
		        		// 在点击新对局(重置)按钮的时候,由于tank.getParent()即JPanel容器未存在
		        		//就无法添加子弹到其中,所以加此判断
		        		tank.getParent().add(bullet);
		        		new BulletMoveThread(bullet).start();
		        	}
		        }
            	// 决定何时改变坦克的移动方向
            	Thread.sleep(200);
            	if (step == 0) {
            		// 如果坦克步骤运行到了0,就转向
            		while(step == 0) {
            			// 为0就循环,直到不为0
            			step = (int)(Math.random()*20);
            		}
            		// 随机决定坦克移动的方向
                    int number=(int)(Math.random()*4);
                    switch(number) {
                    	case 0:
                    		direction = "up";
    	                	break;
                    	case 1:
                    		direction = "right";
    	                	break;
                    	case 2:
                    		direction = "down";
    	                	break;
                    	case 3:
                    		direction = "left";
    	                	break;
                    }	
            	}
            	step--;
            	rate--;
            	tank.setPosition(direction); // 调用坦克的位置移动方法
            } catch (Exception e) {
                e.printStackTrace();
                break;
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值