《游戏系统设计八》原来游戏的每日次数重置是这么做的?看看有没有bug

689 阅读4分钟

小知识,大挑战!本文正在参与“   程序员必备小知识

本文同时参与 「掘力星计划」   ,赢取创作大礼包,挑战创作激励金

在游戏开发中经常会有每天限制次数的需求,这样的功能几乎在每个功能都可能出现,这种功能每个模块自己写又是重复的,因此需要统一处理。

比如:每日抽奖的免费次数,比如每天可打的副本次数等等功能。

今天这篇写一下这个每日次数限制的功能的实现。

1、设计需求

需要任何模块都可以调用,在查询的时候可以自动重置,并且自动入库,并且支持周,月等周期。

2、数据库设计

image.png

n_roleId : 玩家id,作为主键

n_reset_type : 枚举,不同的重置类型定义不同的id,作为主键

n_count :当前周期已经使用的数量,每次重置为0

s_extend :扩展字段,每种类型可以存一些自定义的数据到里面

d_update : 更新时间

d_create : 数据创建的时间

3、代码实现

3.1 枚举类型定义

package org.pdool.dayLimit;
 
/**
* 重置次数限制类型
*/
public enum ResetLimitType {
   /**
    * 每日抽奖次数
    */
   LOTTERY_COUNT(1),
   /**
    * 每周收礼次数
    */
   RECEIVE_GIFT_COUNT(2,2),
  ;
   /**
    * 限制类型
    */
   private int limitType;
   /**
    * 重置类型 1 每天 2 每周 3 每月
    */
   private int resetType = 1;
 
   ResetLimitType(int limitType) {
       this.limitType = limitType;
  }
 
   ResetLimitType(int limitType, int resetType) {
       this.limitType = limitType;
       this.resetType = resetType;
  }
 
   public int getLimitType() {
       return limitType;
  }
 
   public int getResetType() {
       return resetType;
  }
 
   public static ResetLimitType valueOf(int limitType) {
       for (ResetLimitType resetLimitType : values()) {
           if (limitType == resetLimitType.getLimitType()) {
               return resetLimitType;
          }
      }
       return null;
  }
}

说明:枚举定义了2个构造函数。

默认的构造函数重置类型是1,也就是每日重置。

2个参数的构造函数,可以自定义重置类型,重置类型 1 每天 2 每周 3 每月

3.2 日期函数util

package org.pdool.dayLimit;
 
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
 
public class DateUtil {
   /**
    * 判断两个日期是否同一天
    *
    * @param beginDate
    * @param endDate
    * @return
    */
   public static boolean isSameDay(Date beginDate, Date endDate) {
       return getDiffDays(beginDate, endDate) == 0;
  }
   /**
    * 判断两个日期是否同一周
    *
    * @param beginDate
    * @param endDate
    * @return
    */
   public static boolean isSameWeek(Date beginDate, Date endDate) {
       Calendar begin = Calendar.getInstance();
       begin.setTime(beginDate);
       Calendar end = Calendar.getInstance();
       end.setTime(endDate);
 
       //换算beginDate的周一时间
       int beginDayOfWeek = begin.get(Calendar.DAY_OF_WEEK);
       if (beginDayOfWeek == 1) {
           begin.add(Calendar.DAY_OF_YEAR, -6);
      } else if (beginDayOfWeek > 2) {
           begin.add(Calendar.DAY_OF_YEAR, 2 - beginDayOfWeek);
      }
 
       //换算endDate的周一时间
       int endDayOfWeek = end.get(Calendar.DAY_OF_WEEK);
       if (endDayOfWeek == 1) {
           end.add(Calendar.DAY_OF_YEAR, -6);
      } else if (endDayOfWeek > 2) {
           end.add(Calendar.DAY_OF_YEAR, 2 - endDayOfWeek);
      }
       return ((end.get(Calendar.YEAR) == begin.get(Calendar.YEAR)) && (end.get(Calendar.DAY_OF_YEAR) == begin.get(Calendar.DAY_OF_YEAR)));
  }
 
   /**
    * 两个时间相差天数
    *
    * @param beginDate
    * @param endDate
    * @return
    */
   public static int getDiffDays(Date beginDate, Date endDate) {
       Calendar begin = Calendar.getInstance();
       begin.setTime(beginDate);
       begin.set(Calendar.HOUR_OF_DAY, 0);
       begin.set(Calendar.MINUTE, 0);
       begin.set(Calendar.SECOND, 0);
       begin.set(Calendar.MILLISECOND, 0);
       Calendar end = Calendar.getInstance();
       end.setTime(endDate);
       end.set(Calendar.HOUR_OF_DAY, 0);
       end.set(Calendar.MINUTE, 0);
       end.set(Calendar.SECOND, 0);
       end.set(Calendar.MILLISECOND, 0);
       return (int) ((end.getTimeInMillis() - begin.getTimeInMillis()) / (24 * 60 * 60 *1000));
  }
 
 
   /**
    * 判断2个时间
    *@param date
    *@param date2
    *@param state 1:比较是否是同一个月,2:比较是否是同一天,3:比较是否是同一年
    *@return boolean
    */
   public static boolean isSameMonth(Date date, Date date2)
  {
       Calendar begin = Calendar.getInstance();
       begin.setTime(date);
       Calendar end = Calendar.getInstance();
       end.setTime(date2);
       return (end.get(Calendar.YEAR) == begin.get(Calendar.YEAR)) && (end.get(Calendar.MONTH) == begin.get(Calendar.MONTH));
  }
}

说明:实现了对天,月 和 周的时间比较,可以判断两个时间是否在同一天,同一周,同一个月。

3.3 重置对象的定义

package org.pdool.dayLimit;
 
import java.util.Date;
 
/**
* 重置次数
*/
public class ResetCountVO {
   // 角色id
   private int roleId;
   // 重置的类型
   private ResetLimitType resetType;
   //已经消耗次数
   private int num;
   //最后更新时间
   private Date updateTime;
   //扩展信息
   private String extend;
 
   public int getRoleId() {
       return roleId;
  }
 
   public void setRoleId(int roleId) {
       this.roleId = roleId;
  }
 
   public int getNum() {
       return num;
  }
 
   public void setNum(int num) {
       this.num = num;
  }
 
   public Date getUpdateTime() {
       return updateTime;
  }
 
   public void setUpdateTime(Date updateTime) {
       this.updateTime = updateTime;
  }
 
   public String getExtend() {
       return extend;
  }
 
   public void setExtend(String extend) {
       this.extend = extend;
  }
 
   public ResetLimitType getResetType() {
       return resetType;
  }
 
   public void setResetType(ResetLimitType resetType) {
       this.resetType = resetType;
  }
 
   /**
    * 获得次数
    *
    * @return
    */
   public int getCount() {
       int resetType = this.resetType.getResetType();
       Date now = new Date();
       if (resetType == 1) {
           // 每日重置
           if (DateUtil.isSameDay(now, updateTime)) {
               return this.num;
          }
      } else if (resetType == 2) {
           // 每周重置
           if (DateUtil.isSameWeek(now, updateTime)) {
               return this.num;
          }
      } else if (resetType == 3) {
           // 每月重置
           if (DateUtil.isSameMonth(now, updateTime)) {
               return this.num;
          }
      }
       this.num = 0;
       updateTime = now;
       return 0;
  }
 
   /**
    * 增加次数
    *
    * @param addCountArg
    * @return
    */
   public int addCount(int... addCountArg) {
       int count = getCount();
       int addCount = 1;
       if (addCountArg.length != 0) {
           addCount = addCountArg[0];
      }
       count += addCount;
       num = count;
       updateTime = new Date();
       return count;
  }
 
 
}

说明:重置对象自带的方法比如查和增加次数。

addCount 函数,在不传参数的时候,默认增加次数 1,在当前的基础上增加1 次

传递参数的时候,则使用传递的参数,比如 addCount(50),则在当前的基础上增加50次。

3.4 使用方法

package org.pdool.dayLimit;
 
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
 
public class Aain {
   public static void main(String[] args) {
       Map<ResetLimitType,ResetCountVO> playerResetMap = new HashMap<>();
       ResetCountVO resetCountVO1 = new ResetCountVO();
       resetCountVO1.setResetType(ResetLimitType.LOTTERY_COUNT);
       resetCountVO1.setUpdateTime(new Date(1621870616000L));
       resetCountVO1.setNum(1);
       playerResetMap.put(ResetLimitType.LOTTERY_COUNT,resetCountVO1 );
 
       ResetCountVO resetCountVO2 = new ResetCountVO();
       resetCountVO2.setResetType(ResetLimitType.RECEIVE_GIFT_COUNT);
       resetCountVO2.setUpdateTime(new Date(1621870616000L));
       resetCountVO2.setNum(1000);
       playerResetMap.put(ResetLimitType.RECEIVE_GIFT_COUNT,resetCountVO2 );
 
       int count = resetCountVO1.getCount();
       System.out.println(count);
 
       int count2 = resetCountVO2.getCount();
       System.out.println(count2);
  }
}

3.5 注意事项

1、没有实现数据的入库操作,这个在使用代码的时候,需要根据自己的项目进行更改。

2、没有封装在枚举类型对应的对象不存在时自动创建操作,可以根据自己的项目代码规则自己实现

在上面的我定义了一个map ,可以使用 下面的代码进行封装实现

ResetCountVO resetCountVO = playerResetMap.computeIfAbsent(ResetLimitType.LOTTERY_COUNT, k -> new ResetCountVO());
       if (resetCountVO.getUpdateTime() == null){
           resetCountVO.setResetType(ResetLimitType.LOTTERY_COUNT);
           resetCountVO.setUpdateTime(new Date(1621870616000L));
           resetCountVO.setNum(1);
      }

4、总结

周期性的限制次数的思想很简单,就是任何需要访问数量的地方进行次数重置检测。

我们要做的就是怎么实现的的简单方便,将逻辑进行封装的更顺手。

好了,今天就写到这吧,睡觉。