JS面向对象

该文章展示了一个使用HTML和JavaScript编写的简易飞机大战游戏的代码结构。游戏中包括了英雄飞机、敌人飞机、子弹、碰撞检测、得分系统和游戏状态管理等功能。通过引入多个JS文件分别处理不同的游戏元素,实现了游戏的逻辑和交互。
摘要由CSDN通过智能技术生成

简易版飞机大战

html页面

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
   canvas{
    display: block;
    margin: 100px auto;
    border: 1px solid #ccc;
    /* 鼠标变为手掌形状 */
    cursor: pointer;
   }
  </style>
  <!-- 引入js文件 -->
  <script src="js/util.js"></script>
  <script src="js/config.js"></script>
  <script src="js/game.js"></script>
  <script src="js/background.js"></script>
  <script src="js/hero.js"></script>
  <script src="js/bullet.js"></script>
  <script src="js/enemy.js"></script>
  <script src="js/bomb.js"></script>
  <script src="js/food.js"></script>
</head>
<body>
  <canvas width="512px" height="718px"></canvas>
  <script>
    // 在这个项目中创建和使用类的步骤: 1:创建一个JS文件在文件中创建一个类  2: 在html文件中引入这个类  3: 创建类的对象
    // 创建画布和画笔
   let canvas=document.querySelector("canvas")
   let cxt=canvas.getContext("2d")
    // 创建一个游戏的对象
   let game=new Game(canvas,cxt)
   //调用游戏的开始方法开始游戏
   game.start()
  </script>
</body>
</html>

Js页面

1.工具函数

//工具函数
function createImage(url){
  //创建图片对象
  let img=new Image();
  img.src=url;
  return img;
}

//判断任意两个组件是否发生碰撞
function collideCompent(com1,com2){
  if(com1.x<=com2.x+com2.width&&com1.x+com1.width>=com2.x&&com1.y<=com2.y+com2.height&&com1.y+com1.height>=com2.y){
    return true;
  }
  return false;
}

2.配置(定义、声明、赋值)

const WIDTH = 512
const HEIGHT = 768

// 定义游戏的状态
const STATE_START = 1;  /// 开始状态
const STATE_RUNNING = 2;  // 运行状态
const STATE_PAUSED = 3;   // 暂停状态
const STATE_END = 4;    // 结束状态

// 定义英雄的初始血量(生命值)
const HERO_HP = 3

// 定义一个子弹的默认间隔
const INTERVAL = 15;  //子弹间隔次数, 值越小,越密集




// 英雄数组,可以切换英雄图片
var HEROS = [{
	width: 116,
	height: 92,
	imgX: 393,
	imgY: 102,
	background: createImage("img/img_plane_main.png")
}, {
	width: 118,
	height: 100,
	imgX: 393,
	imgY: 0,
	background: createImage("img/img_plane_main.png")
}, {
	width: 116,
	height: 94,
	imgX: 127,
	imgY: 107,
	background: createImage("img/img_plane_main.png")
}, {
	width: 130,
	height: 106,
	imgX: 137,
	imgY: 0,
	background: createImage("img/img_plane_main.png")
}];

// 定义子弹对象
var BULLET = {
	width: 14,
	height: 32,
	speed: [4, 10],
	imgX: 335,
	imgY: 171,
	background: createImage("img/img_bullet.png")
}


// 定义敌机数组
var ENEMYS = [{
	width: 98,
	height: 76,
	speed: 3,
	score: 100,
	imgX: 267,
	imgY: 474,
	background: createImage("img/img_plane_enemy.png"),
	hp: 1,
  interval: 200
}, {
	width: 104,
	height: 76,
	speed: 3,
	score: 100,
	imgX: 162,
	imgY: 474,
	background: createImage("img/img_plane_enemy.png"),
	hp: 1,
  interval: 300
}, {
	width: 114,
	height: 82,
	speed: 8,
	score: 1000,
	imgX: 367,
	imgY: 440,
	background: createImage("img/img_plane_enemy.png"),
	hp: 2,
  interval: 450
}, {
	width: 104,
	height: 76,
	speed: 3,
	score: 200,
	imgX: 162,
	imgY: 474,
	background: createImage("img/img_plane_enemy.png"),
	hp: 2,
  interval: 1100
}, {
	width: 175,
	height: 133,
	speed: 2,
	score: 500,
	imgX: 190,
	imgY: 340,
	background: createImage("img/img_plane_enemy.png"),
	hp: 5,
  interval: 2300
}, {
	width: 260,
	height: 196,
	speed: 2,
	score: 2000,
	imgX: 0,
	imgY: 2,
	background: createImage("img/img_plane_enemy.png"),
	hp: 20,
  interval: 4700
}];

// 爆炸图片
var BOMBIMGS = [
	createImage("img/wsparticle_01.png"), 
	createImage("img/wsparticle_02.png"), 
	createImage("img/wsparticle_03.png"), 
	createImage("img/wsparticle_04.png"), 
	createImage("img/wsparticle_05.png"), 
	createImage("img/wsparticle_06.png"), 
	createImage("img/wsparticle_07.png"), 
	createImage("img/wsparticle_08.png"), 
	createImage("img/wsparticle_09.png"), 
	createImage("img/wsparticle_10.png")
];

const FOODS_HP = 1
const FOODS_SPEED = 2
const FOODS_BULLET = 3

// 食物
var FOODS = [
	{
		background: createImage("img/add_hp.png"),
		type: FOODS_HP,
		width: 60,
		height: 35
	}, {
		background: createImage("img/drop_0.png"),
		type: FOODS_SPEED,
		width: 60,
		height: 35
	}
	, {
		background: createImage("img/drop_1.png"),
		type: FOODS_BULLET,
		width: 60,
		height: 35
	}
]
//得分图片数组,数字0-9
var scoreImgs = [
	createImage("img/number_0.png"),
	createImage("img/number_1.png"),
	createImage("img/number_2.png"),
	createImage("img/number_3.png"),
	createImage("img/number_4.png"),
	createImage("img/number_5.png"),
	createImage("img/number_6.png"),
	createImage("img/number_7.png"),
	createImage("img/number_8.png"),
	createImage("img/number_9.png"),
]

3.游戏类(总类)

// 创建一个游戏类
class Game {
  //传入画布和画笔
  constructor(canvas,cxt){
    this.canvas=canvas;
    this.cxt=cxt;
    //创建属性
    this.init()
  }
  init(){
     //背景属性,也就是需要一个背景对象
    this.bg=new Background(cxt);
    //开始游戏的图片
    this.startBgImg=createImage("img/start.png");
    //结束游戏的图片
    this.restartBgImg=createImage("img/restart.png");
    //hp图片
    this.hpImg=createImage("img/hp_0.png");
    this.hpWidth=100;
    this.hpHeight=10;
    //创建英雄对象
    this.hero=new Hero(cxt)
    //敌机数组
    this.enemyList=[];
    //调用创建敌机方法的计数器
    this.enemyIndex=0;
    //爆炸对象数组
    this.bombList=[];
    //食物数组
    this.foodList=[]
    //16秒创建一个食物
    this.foodInterval=800
    //计数器
    this.foodIndex=0
    //游戏状态的属性
    this.state=STATE_START;
    //总分
    this.totalScore=0;
  }
  //开始游戏的方法
  start(){
    //给画布添加点击事件
    this.canvas.onclick=()=>{
      //如果游戏是开始状态
      if(this.state==STATE_START){
        //游戏状态修改为运行状态
        this.state=STATE_RUNNING
      }else if(this.state==STATE_PAUSED){
        //游戏状态修改为运行状态
        this.state=STATE_RUNNING
        //如果游戏是结束状态
      }else if(this.state==STATE_END){
        //重新创建属性,重新开始游戏
        this.init()
        //游戏状态修改为运行状态
        this.state=STATE_RUNNING
      }
    }
    //给画布添加一个鼠标移动事件,从e中可以获取到鼠标坐标
    this.canvas.onmousemove = e => {
      this.hero.move(e)
    }
    //添加监听键盘事件
    window.onkeyup=(e)=>{
      if(e.keyCode==32){//按下的是空格键
        //修改游戏的状态为暂停状态
        this.state=STATE_PAUSED;
      }
    }
    window.setInterval(() => {
      // 根据不同的游戏状态,画的界面也不相同
      // 如果游戏的状态是开始状态,则背景只画出,不移动
      if(this.state == STATE_START) {   // 游戏开始状态
        // 画出背景
        this.bg.draw()
        //画出hp图片
        this.drawHp()
        // 还要画一个开始游戏的图片
        this.cxt.drawImage(this.startBgImg, (WIDTH - this.startBgImg.width)/ 2, HEIGHT - 200)
      } else if(this.state == STATE_RUNNING) { // 游戏运行状态
        // 画出背景
        this.bg.update()
        this.drawHp()
        //游戏得分
        this.drawScore()
        // 画出英雄
        this.hero.update()
        // 调用创建敌机的方法
        this.createEnemy()
        //批量画出所有组件方法
        this.batDrawCompents()
        //各种组件碰撞检测
        this.collideCompents()
        //调用创建食物的方法
        this.createFood()
        //英雄死掉之后,游戏结束
        if(!this.hero.live){
          this.state=STATE_END
        }
      } else if(this.state == STATE_PAUSED) { // 游戏暂停状态
          // 暂停状态什么都不要做,保持原来的界面状态就可以
      }else if(this.state == STATE_END) {  // 游戏结束状态
          this.drawEnd()
      }
    }, 20)
  }

  //创建敌机,每隔20毫秒执行一次
  createEnemy(){
    //计数器
    this.enemyIndex++;
    //循环敌机数组
    ENEMYS.forEach((enemyObj,index)=>{
      //每隔不同敌机的间隔时间(enemyObj.interval)创建敌机
      if(this.enemyIndex%enemyObj.interval==0){
        //拿到循环到的敌机的索引,创建敌机对象
        let enemy=new Enemy(this.cxt,index)
        //将创建好的敌机对象push到敌机数组中
        this.enemyList.push(enemy)
      }
    })
  }
   // 批量画出所有的组件 :敌机, 食物道具,爆炸效果
   batDrawCompents() {
    //批量画出敌机
    this.enemyList.forEach((enemy,index)=>{
      //死掉的和出界的敌机需要执行不同的操作,出界的敌机直接移除就可以,死掉的敌机(先产生爆炸效果,游戏得分后)移除
      if(enemy.outOfBounds()){//出界
        //移除
        this.enemyList.splice(index,1)
      }
      if(!enemy.live){//死掉
        //先爆炸再移除,产生爆炸
        //创建爆炸对象
        let bomb=new Bomb(enemy);
        this.bombList.push(bomb);
        //得分
        this.totalScore+=enemy.score;
        this.enemyList.splice(index,1)
      }
      //调用敌机的画出和移动方法
      enemy.update()
    })
    // 批量画出食物
    this.foodList.forEach((food,index)=>{
      //移除出界的和死掉的食物
      if(food.outOfBounds()||!food.live){
        this.foodList.splice(index,1)
      }
      //调用食物的画出和移动方法
      food.update()
    })
    // 批量画出爆炸效果
    this.bombList.forEach((bomb,index)=>{
      //先将死掉的爆炸从列表中移除
      if(!bomb.live){
        this.bombList.splice(index,1)
      }
      //调用爆炸的画出方法
      bomb.draw()
    })
  }
  //各种组件的碰撞检测方法 子弹和敌机,英雄和敌机,英雄和食物
  collideCompents(){
    //子弹和敌机的碰撞检测,画布上有子弹和敌机列表也就是有很多子弹和敌机
    this.enemyList.forEach((enemy,enemyIndex)=>{
      //和子弹列表中每个子弹判断是否碰撞
      this.hero.bulletList.forEach((bullet,bulletIndex)=>{
        //双方都存活的状态下发生碰撞
        if(collideCompent(enemy,bullet)&&enemy.live&&bullet.live){//敌机和子弹发生碰撞
          //发生碰撞后
          bullet.collided()
          enemy.collided()
        }
      })
      //英雄和敌机碰撞
      if(collideCompent(enemy,this.hero)&&enemy.live&&this.hero.live){
        enemy.collided()
        this.hero.collided()
      }
    })
    //英雄和食物数组中食物的碰撞检测
    this.foodList.forEach((food,index)=>{
      if(collideCompent(food,this.hero)&&this.hero.live&&food.live){
        //英雄吃掉食物(食物活着)
        this.hero.eat(food)
        //食物碰撞之后(死掉)
        food.collided()
      }
    })
  }
  drawEnd(){
  // 先画出背景
  this.bg.draw()
  //                            透明度
  this.cxt.fillStyle="rgba(0,0,0,0.5)"
  //填充矩形
  this.cxt.fillRect(0,0,WIDTH,HEIGHT)
  //重新开始图片
  this.cxt.drawImage(this.restartBgImg, (WIDTH - this.restartBgImg.width)/ 2, 300)
  }
  //创建食物
  createFood(){
    //计数器
    this.foodIndex++
    //间隔时间
    if(this.foodIndex%this.foodInterval==0){
      //创建食物对象
      let food=new Food(this.cxt)
      //将食物push进食物数组中
      this.foodList.push(food)
    }
  }
  //画出hp
  drawHp(){
    //hp图片
    this.cxt.drawImage(this.hpImg,10,10,30,20)
    //填充矩形和描边矩形
    this.cxt.strokeStyle='red'
    this.cxt.fillStyle='red'
    this.cxt.strokeRect(50,15,this.hpWidth,this.hpHeight)
    this.cxt.fillRect(50,15,this.hpWidth*this.hero.hp/HERO_HP,this.hpHeight)
  }
  //画出游戏得分
  drawScore(){
    //转为字符串格式,便于循环拿到每个值
    let scoreStr=String(this.totalScore)//2700 "2700"
    for(let i=0;i<scoreStr.length;i++){
      //获取指定索引的字符
      let c=scoreStr.charAt(i);
      //将c作为图片数组的索引
      let img=scoreImgs[c];
      //画出得分
      this.cxt.drawImage(img,400+15*i,15,15,25)
    }
  }
}

  

4.背景类

class Background {
  //传入画笔
  constructor(cxt){
    this.cxt=cxt;
    this.bgImg=createImage("img/img_bg_level_1.jpg")
    //背景的y轴坐标
    this.y1=0;
    this.y2=-HEIGHT;
  }
  //画出背景
  draw(){
    //从画布的(0,0)位置开始画
    this.cxt.clearRect(0,0,WIDTH,HEIGHT)//橡皮擦,擦除一张画布的面积
    this.cxt.drawImage(this.bgImg,0,this.y1)
    this.cxt.drawImage(this.bgImg,0,this.y2)
  }
  //移动背景
  move(){
    this.y1++
    this.y2++;
    if(this.y2==0){
      this.y1=0;
      this.y2=-HEIGHT;
    }
  }
  update(){
    this.draw()
    this.move()
  }
}

5.英雄类(己方)

 // 创建一个英雄类
 class Hero {
  //构造函数只会调用一次
  constructor(cxt){
    this.cxt=cxt;
    // 定义英雄类别的属性 : 属性的值有四个:0: 1排子弹 1:2排子弹  2:3排 3: 4排子弹
    this.index=0;
    //调用英雄图片方法,画出不同索引的英雄
    this.init()
    // 英雄坐标
    this.x=(WIDTH-this.width)/2;
    this.y=HEIGHT-this.height-50;
    //英雄的存活状态
    this.live=true;
    //生命值
    this.hp=HERO_HP;
    //弹夹,实际上就是个子弹的数组,放子弹对象
    this.bulletList=[];
    // 创建子弹的间隔,也就是每调用多少次shoot方法,创建一个子弹
    this.interval=INTERVAL;
    // 需要一个递增的变量,没执行一次shoot方法,这个值就加1,相当于一个计数器,记录调用了多少次shoot方法
    this.intervalCount=0;
    //增加子弹速度
    this.bulletSpeed=0
  }
  //英雄图片
  init(){
    this.bg=HEROS[this.index].background
    this.imgX = HEROS[this.index].imgX
    this.imgY = HEROS[this.index].imgY
    this.width = HEROS[this.index].width
    this.height = HEROS[this.index].height
  }
  //画出自己
  draw(){
    if(this.live)this.cxt.drawImage(this.bg, this.imgX, this.imgY, this.width, this.height, this.x, this.y, this.width, this.height)
  }
  //移动
  move(e){
    if(this.live){
      // 通过修改英雄的坐标,只要英雄的坐标发生了修改,等下个20毫秒之后就会按照新的坐标进行绘制
      //鼠标在飞机中心点
      this.x=e.offsetX-this.width/2;
      this.y=e.offsetY-this.height/2;
    }
  }
  eat(food){
    //判断吃掉的是什么类型的食物,不同类型的食物英雄要做的操作不同
    if(food.type==FOODS_BULLET){
      //增加子弹行数
      if(this.index<3){
        this.index++;
        //画出索引增加后的英雄图片
        this.init()
      }
    }else if(food.type==FOODS_HP){//加生命
      if(this.hp<HERO_HP){
        this.hp++;
      }
    }else if(food.type==FOODS_SPEED){//加速度
      //子弹速度数组只有两个值0和1
      this.bulletSpeed=1;
    }
  }
   //发生碰撞后
   collided(){
    this.hp--;
    if(this.hp==0){
      this.live=false;
    }
  }
  //射击方法, 每隔20毫秒执行1次, 一秒钟会产生50个子弹   300毫秒
  shoot(){
    if(this.live){
      //下面创建子弹的代码,每执行15次shoot方法创建1次
      this.intervalCount++;
      if(this.intervalCount%this.interval==0){
        //通过this.index判断要发射几排子弹
        if(this.index==0){//1排子弹
          //创建一个子弹对象
          let bullet=new Bullet(this)
          //将子弹扔到弹夹中
          this.bulletList.push(bullet)
        }else if(this.index==1){//2排
          //创建两个子弹对象就行,这两个子弹对象的y轴坐标相同,x轴坐标不相同
          let b1=new Bullet(this)
          b1.x-=16;
          let b2=new Bullet(this)
          b2.x+=16;
          this.bulletList.push(b1)
          this.bulletList.push(b2)
        }else if(this.index == 2) {  // 3排

          let b1 = new Bullet(this)
          let b2 = new Bullet(this)
          b2.x -= 32
          let b3 = new Bullet(this)
          b3.x += 32

          this.bulletList.push(b1)
          this.bulletList.push(b2)
          this.bulletList.push(b3)

        }else if(this.index == 3) {  // 4排
          let b1 = new Bullet(this)
          b1.x -= 16
          let b2 = new Bullet(this)
          b2.x += 16
          let b3 = new Bullet(this)
          b3.x -= 48
          let b4 = new Bullet(this)
          b4.x += 48

          this.bulletList.push(b1)
          this.bulletList.push(b2)
          this.bulletList.push(b3)
          this.bulletList.push(b4)
        }
      }
    }
  }
  //画出弹夹中所有子弹的方法
  drawBulletList(){
    //英雄存活状态下
    if(this.live){
      //通过循环拿到弹夹中的所有子弹对象, 挨个调用draw和move方法
      this.bulletList.forEach((bullet,index)=>{
        if(bullet.outOfBounds()||!bullet.live){//出界和死掉的子弹都要移除
          //移除弹夹中的子弹
          this.bulletList.splice(index,1)
        }
        //画出和移动子弹
        bullet.update()
      })
    }
  }
  update(){
    //画出英雄
    this.draw()
    //射击方法
    this.shoot()
    //画出弹夹中所有子弹
    this.drawBulletList()
  }
}

6.子弹类(随英雄产生)

//创建一个子弹类
class Bullet {
  //子弹在英雄中产生
  constructor(hero){
    //传入英雄的画笔
    this.cxt=hero.cxt;
    this.bg=BULLET.background;
    this.imgX = BULLET.imgX
    this.imgY = BULLET.imgY
    this.width = BULLET.width
    this.height = BULLET.height
    // 子弹的速度,得用变量(在英雄中)
    this.speed = BULLET.speed[hero.bulletSpeed]
    //子弹的坐标
    this.x=hero.x + hero.width / 2 - this.width / 2
    this.y=hero.y - this.height
    //子弹的存活状态
    this.live=true;
  }
  //画出子弹
  draw() {
    if(this.live) this.cxt.drawImage(this.bg, this.imgX, this.imgY, this.width, this.height, this.x, this.y, this.width, this.height)
  }
  //移动
  move() {
    // 修改子弹y轴的坐标
    if(this.live) this.y -= this.speed
  }
  //发生碰撞后
  collided(){
    this.live=false;
  }
  update(){
    this.draw()
    this.move()
  }
  // 判断子弹是否出界
  outOfBounds() {
    if(this.y <= -this.height) {
      return true
    }
    return false
  }
}

7.敌机类

//创建敌机类
class Enemy {
  //需要一个索引值确定画的是哪种敌机
  constructor(cxt, index) {
    this.cxt = cxt;
    this.imgX = ENEMYS[index].imgX
    this.imgY = ENEMYS[index].imgY
    this.width = ENEMYS[index].width
    this.height = ENEMYS[index].height
    this.bg = ENEMYS[index].background
    //敌机坐标,x轴坐标通过随机数获取
    this.x = parseInt(Math.random() * (WIDTH - this.width))
    this.y = -this.height
    //敌机的速度
    this.speed = ENEMYS[index].speed
    //间隔时间
    this.interval = ENEMYS[index].interval

    this.hp = ENEMYS[index].hp   // 敌机的初始生命值
    this.score = ENEMYS[index].score  // 敌机死亡后的分数
    //敌机存活状态
    this.live = true 
  }
  //画出敌机
  draw() {
    if(this.live) this.cxt.drawImage(this.bg, this.imgX, this.imgY, this.width, this.height, this.x, this.y, this.width, this.height)
  }
  //移动敌机
  move() {
    if(this.live) this.y += this.speed
  }
  //发生碰撞后
  collided(){
    this.hp--;
    if(this.hp==0){
      this.live=false;
    }
  }
  update() {
    this.draw()
    this.move()
  }

  // 判断敌机是否出界
  outOfBounds() {
    if(this.y >= HEIGHT) {
      return true
    }
    return false
  }
}

8.食物(工具)类

class Food{
    constructor(cxt){
        this.cxt=cxt;
        //创建一个0--2的随机数,这个随机数可以从食物数组中获取一个食物对象
        this.index=parseInt(Math.random()*3)
        this.bg=FOODS[this.index].background
        this.width=FOODS[this.index].width
        this.height=FOODS[this.index].height
        this.type=FOODS[this.index].type
        //创建食物对象的坐标
        this.x=parseInt(Math.random()*(WIDTH-this.width))
        this.y=-this.height;
        //食物存活状态
        this.live=true;
        //x轴速度2--6的随机数
        this.speedX=2+parseInt(Math.random()*5)
        //y轴2--5的随机数
        this.speedY=2+parseInt(Math.random()*4)

    }
    //画出食物
    draw(){
        if(this.live)this.cxt.drawImage(this.bg,this.x,this.y,this.width,this.height)
    }
    //移动食物
    move(){
        //超出x轴速度取反
        if(this.x<=0||this.x>=WIDTH-this.width){
            this.speedX=-this.speedX;
        }
        this.x+=this.speedX;
        this.y+=this.speedY;
    }
    //碰撞之后
    collided(){
        this.live=false;
    }
    update(){
        this.draw()
        this.move()
      }
    //判断食物是否出界
    outOfBounds() {
        if(this.y >= HEIGHT) {
          return true
        }
        return false
      }
}

9.爆炸类(敌机与子弹碰撞处产生)

class Bomb{
    //爆炸在敌机与子弹碰撞的位置产生
    constructor(enemy){
        //传入敌机的画笔
        this.cxt=enemy.cxt;
        this.enemy=enemy;
        //获取爆炸图片对象数组
        this.imgs=BOMBIMGS;
        //计数器,表示第几次调用draw方法
        this.index=0;
        //爆炸的存活状态
        this.live=true;
    }
    //画出爆炸效果,需要以20毫秒为间隔画出十张图片,才算一次爆炸效果
    //画完10张图后爆炸效果结束
    draw(){
        //敌机碰撞位置不固定,所以爆炸的x轴y轴位置在方法中设置
        //爆炸存在     共10张图片    
        if(this.live&&this.index<this.imgs.length){
            //画出第index张爆炸图片
            //爆炸的坐标
            this.x=this.enemy.x+(this.enemy.width-this.imgs[this.index].width)/2;
            this.y=this.enemy.y+(this.enemy.height-this.imgs[this.index].height)/2;
            this.cxt.drawImage(this.imgs[this.index],this.x,this.y)
            this.index++;
        }else{
            this.live=false;
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值