在开发小游戏项目时,有时需要服务端来操作用户的体力。如果使用数据库来保存略显冗余,性能也稍有不足,所有使用Redis的过期机制可以更好更快的快速实现需求。
示例中的Redis经过了多例的封装 参照 多例Redis类实现
<?php
namespace app\common;
use ryjoin\Redis;
/**
* 体力计算
* Class Live
* @package app\common
*/
class Live
{
/**
* redis的key
* @var string
*/
protected $key;
/**
* 体力配置
* @var array
*/
protected $config = [
'max' => 5, // 最大值
'interval' => 60 // 体力恢复间隔(秒)
];
/**
* 设置用户
* Energy constructor.
* @param $user_id
*/
public function __construct($user_id)
{
$this->key = "lives:$user_id";
}
/**
* 获取体力
* @return array
*/
public function get()
{
$lives = $this->config['max']; // 体力
$next_live_time= -1; // 恢复倒计时(秒)
if (Redis::db()->exists($this->key)) {
$ttl = (int)Redis::db()->ttl($this->key);
$reduce = ceil($ttl / $this->config['interval']);
$lives -= $reduce;
$next_live_time = $ttl % $this->config['interval'];
// 如果正好处于恢复时 否则会出现剩余0的情况
if ($next_live_time == 0) {
$next_live_time = $this->config['interval'] - 1;
}
}
return [
'lives' => (int)$lives, // 当前体力
'next_live_time' => $next_live_time // 下一次恢复时间(秒)
];
}
/**
* 扣除体力
* @return bool
*/
public function reduce()
{
$lives = $this->get();
if ($lives['lives'] == 0) { // 没有体力
return false;
}
if (Redis::db()->exists($this->key)) {
$expired_ttl = (int)Redis::db()->ttl($this->key);
Redis::db()->set($this->key, 1, $expired_ttl + $this->config['interval']);
} else {
Redis::db()->set($this->key, 1, $this->config['interval']);
}
return true;
}
/**
* 增加体力
* @return bool
*/
public function increase()
{
$lives = $this->get();
if ($lives['lives'] == $this->config['max']) { // 体力上限
return true;
}
if (Redis::db()->exists($this->key)) {
$expired_ttl = (int)Redis::db()->ttl($this->key);
if ($expired_ttl <= $this->config['interval']) {
Redis::db()->del($this->key);
}
Redis::db()->set($this->key, 1, $expired_ttl - $this->config['interval']);
}
return true;
}
}
使用方式 (根据自身业务)
获取当前用户的体力
/**
* 获取用户体力
* @param $user_id
* @return array
*/
public function getPlayer($user_id)
{
// 获取体力信息
$live = new Live($user_id);
$lives = $live->get();
return [
'currentLives' => $lives['lives'],
'nextLiveTime' => $lives['next_live_time'],
'watchAdCount' => $this->getWatchAdCount($user_id)
];
}
观看激励视频后增加用户体力
/**
* 激励视频广告上限
* @var int
*/
protected $watchAdCount = 2;
/**
* 剩余广告次数
* @param $user_id
* @return int
*/
protected function getWatchAdCount($user_id)
{
$todayWatchCount = (int)Redis::db()->get("game:watch:$user_id");
return $this->watchAdCount - $todayWatchCount;
}
/**
* 激励视
* 频增加体力
* @param $user_id
* @return bool
*/
public function watchAd($user_id)
{
$watchAdCount = $this->getWatchAdCount($user_id);
if ($watchAdCount == 0) {
$this->code = Code::FAILURE;
$this->error = '次数不足';
return false;
}
// 增加用户体力
$live = new Live($user_id);
$live->increase();
Redis::db()->incrBy("game:watch:$user_id", 1);
Redis::db()->expire("game:watch:$user_id", Tools::nowTillTomorrowZero());
return true;
}
扣除体力,当然扣除后是会自动恢复的
// 扣除体力 每次-1
$live = new Live($GLOBALS['user_id']);
$result = $live->reduce();
更多使用方式
如果有多个体力的场景,那么可以修改Live类中的key 这样就可以复用到多个体力场景
/**
* redis的key
* @var string
*/
protected $key;
/**
* 设置用户
* Energy constructor.
* @param $user_id
*/
public function __construct($key, $user_id)
{
$this->key = "$key:$user_id";
}