盲盒功能的简单实现

在墨刀上找了一个盲盒的项目练练手,有个开盲盒的功能,效果图如下

点击开盒后端处理后返回给前端一个盲盒奖品,这个功能一开始的时候我觉得挺简单的,直到我动手的时候,感觉没那么简单,也可能是我太笨了。。。

下面是最终的判断逻辑,这是 node 中的代码,其实就是一个 if...else if... 判断

function getRandomInt(min, max) {
      min = Math.ceil(min);
      max = Math.floor(max);
      return Math.floor(Math.random() * (max - min)) + min;
    }
    
    const randomNum=getRandomInt(1,101)
    const data=await openBoxItemModel.find()
    var list=[]

    if(randomNum<=100 && randomNum>65){  //有35%的概率为流量卡
      list.push(data[0].title)
    }else if(randomNum<=65 && randomNum>35){  //有30%的概率为洗发水
      list.push(data[3].title)
    }else if(randomNum<=35 && randomNum>10){  //有25%的概率为抽纸
      list.push(data[1].title)
    }else if(randomNum<=10 && randomNum>0){   //有10%的概率为沙发
      list.push(data[2].title)
    } 

    console.log(list,'list');

刚开始的想法

我想的是双重for循环或者for..in循环,就像下面这样

 const data=await openBoxItemModel.find()

 function getRandomInt(min, max) {
     min = Math.ceil(min);
     max = Math.floor(max);
     return Math.floor(Math.random() * (max - min)) + min;
 }
 
const randomNum=getRandomInt(1,36)

//算出总概率  就是100
const totalWeight = data.reduce((acc, prize) => acc + prize.probability, 0);
//获得奖品的列表
var list=[]  
const sort=[10,25,30,35] //对应着物品中奖概率,比如沙发为10、抽纸为25...

 function drawPrize(){
      const random=Math.random()*totalWeight;
      let sum=0
      for(let prize of data){
        sum+=prize.probability
       
        for(let i=0;i<sort.length;i++){
          
          if(random<sort[i]){
            list.push(prize)
          }
        }
      }
    }
    drawPrize()

遇到的问题

这样做会把所有 random 小于 sort 中物品概率的物品加入到 list 中,这个随机数一开始的想法就是错的,考虑到假如随机数为15,那么list数组里就会有 抽纸、流量卡、洗发水  那应该将哪个奖品作为最终的返回前端。如果按照最接近某个概率的奖品并且是小于的关系来说,15这个随机数最接近25,也就是抽纸这个奖品,但是15又小于35(流量卡),所以每次的随机数如果为10以上到35以下,都会将抽纸作为最终的奖品给予用户,但随机数是25以上35以下时,奖品应该是流量卡,不应该是抽纸。而且还有个问题,11到25(包括25)有15个随机数是抽纸的奖品,26到35(包括35)只有10个随机数,这样一来 抽纸 :流量卡 的比例是 3 :2,但设定的奖品概率是流量卡大于抽纸的,所以这显然不对

改变随机数和判断思路

考虑的是将每个中奖概率除以总概率(100)得出每个物品概率的百分比,然后从上一个和下一个概率中间抽取随机数。比如  10/100  为 0.1 百分比为10% 那么随机数在大于0小于等于10时认为抽到单人沙发  25/100 为 0.25 百分比为25% 那么随机数在大于10小于等于25时认为抽到抽纸,以此类推,随机数设定为1到100。然后从100开始到65,中间有35个随机数,包括100的,这样流量卡的概率为35%,然后从65到35,中间有30个随机数,包括65,这样洗发水的概率为30%......

这是我的数据

num是库存,probability是中奖概率  可以看出 流量卡 > 洗发水 > 抽纸 > 沙发

经过上面的判断,最终实验了100次抽奖,结果如下

这样看来确实是  流量卡 > 洗发水 > 抽纸 > 沙发     但还是不确定这样判断是否正确,如果不对的话,希望帮忙指正。

全部代码

接口部分

router.post('/openBox',async (req,res)=>{
  const data1=await boxSituationModel.find({user_id:req.body.user_id})
  if(data1[0].no_open_box<=0){
    res.send({code:400,msg:'没有未开启的盲盒啦,快去购买吧~'})
  }else{
    await boxSituationModel.updateOne({user_id:req.body.user_id},{
      $inc:{no_open_box:-1}
    })
    await boxSituationModel.updateOne({user_id:req.body.user_id},{
      $inc:{open_box_count:1}
    })
    
    const data=await openBoxItemModel.find()

    function getRandomInt(min, max) {
      min = Math.ceil(min);
      max = Math.floor(max);
      return Math.floor(Math.random() * (max - min)) + min;
    }
    
    const randomNum=getRandomInt(1,101)

    if(randomNum<=100 && randomNum>65){  //有35%的概率为流量卡
      await openBoxItemModel.updateOne({_id:'65eafe12871fbae51bb9c47c'},{
        $inc:{num:-1}
      })
     
      res.send({code:200,msg:'恭喜你获得流量卡',data:data[0]})

    }else if(randomNum<=65 && randomNum>35){  //有30%的概率为洗发水
      await openBoxItemModel.updateOne({_id:'65eafe4b3763eeca074a8717'},{
        $inc:{num:-1}
      })
    
      res.send({code:200,msg:'恭喜你获得洗发水',data:data[3]})

    }else if(randomNum<=35 && randomNum>10){  //有25%的概率为抽纸
      await openBoxItemModel.updateOne({_id:'65eafe2b81ddcb7c727d53a6'},{
        $inc:{num:-1}
      })
 
      res.send({code:200,msg:'恭喜你获得抽纸',data:data[1]})

    }else if(randomNum<=10 && randomNum>0){   //有10%的概率为沙发
      await openBoxItemModel.updateOne({_id:'65eafe3b4df8513729d0edef'},{
        $inc:{num:-1}
      })
    
      res.send({code:200,msg:'恭喜你获得沙发',data:data[2]})
    } 
    
  }

})

前端部分

const openBox=async ()=>{
   const {data} = await axios.post('/welfare/openBox',{user_id:'65db28959400000014001e74',open,noOpen})
    if(data.code==400){
        showToast(data.msg)
    }else if(data.code==200){
        goodsImg.value='data:image/png;base64,'+data.data.img
        goodsImg.value=data.data.img
        goodsTitle.value=data.data.title
        getBox() //刷新累计开盒数量及代开盲盒数量
    }
}

效果图

user_id 我暂时先写固定的了,后面再改,你们换一下

大概的流程就是,通过用户id,如果开盒一次,减去相应的待开盲盒数量,然后判断抽到了哪个奖品,将奖品库存数量-1,但是没有做库存不足的判断,因为我只是练习的,就不写了。

推荐组件

我觉得这个组件库开盒效果还挺酷的,后面我再添上

相关阅读

在mongodb数据库中存本地图片要转base64格式

const fs=require('fs')
const path=require('path')

const image = fs.readFileSync(path.join(__dirname,'../','./imgs/image.jpg'));
const imageBase64 = image.toString('base64');

这样做在存大量图片的时候特别麻烦,这也是我在网上找的,暂时没去看更好的方法,如果有希望能私信或评论区告知。图片路径是与 public 同级的 imgs 下面的,感觉不如放到 public 下。图片多的话也不知道会不会增加服务器压力。此时保存的是没有 data URL 前缀的 base64 ,所以在渲染时要加上,当然也可以在存储的时候处理

goodsImg.value='data:image/png;base64,'+data.data.img

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值