You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Studio-Java+Firestore游戏用户分数存储与查询技术咨询

问题背景

我用Android Studio(Java)、Firestore和Firebase Auth开发一款包含3款游戏的应用,需求如下:

  • 为每位用户存储每款游戏的最近5条分数+日期(格式DD/MM/YY)
  • 存储每款游戏的最高分

当前采用单个users集合,User类定义如下:

private String name;
private String email;
private HashMap<Integer, Date> game1_Scores;
private HashMap<Integer, Date> game2_Scores;
private HashMap<Integer, Date> game3_Scores;
private int game1_HighScore;
private int game2_HighScore;
private int game3_HighScore;

问题1:如何最优地用新分数覆盖最早的分数?

首先你当前用HashMap<Integer, Date>的方式有问题——HashMap是无序集合,没法维护分数的时间顺序,根本没法判断哪条是最早的。建议改成有序列表结构,比如自定义一个ScoreEntry类:

public class ScoreEntry {
    private int score;
    private Date date;

    // 空构造函数(Firestore序列化需要)
    public ScoreEntry() {}

    public ScoreEntry(int score, Date date) {
        this.score = score;
        this.date = date;
    }

    // Getter和Setter方法
}

然后把User类里的分数字段替换为:

private List<ScoreEntry> game1_Scores;
private List<ScoreEntry> game2_Scores;
private List<ScoreEntry> game3_Scores;

处理逻辑如下:

  1. 用户提交新分数时,先获取当前用户对应游戏的分数列表
  2. 如果列表长度已达5,删除其中日期最早的元素(遍历比较date字段即可)
  3. 将新的ScoreEntry添加到列表末尾
  4. 同时检查新分数是否超过当前最高分,是则更新对应的gameX_HighScore字段
  5. 用事务把更新后的User对象写回Firestore(避免并发更新冲突)

核心代码示例:

// 获取当前用户的文档引用
DocumentReference userDoc = FirebaseFirestore.getInstance()
    .collection("users")
    .document(FirebaseAuth.getInstance().getCurrentUser().getUid());

// 事务处理
userDoc.runTransaction(transaction -> {
    User user = transaction.get(userDoc).toObject(User.class);
    if (user == null) throw new Exception("用户不存在");

    List<ScoreEntry> scores = user.getGame1_Scores();
    if (scores == null) scores = new ArrayList<>();

    ScoreEntry newEntry = new ScoreEntry(newScore, new Date());
    // 已满5条则删除最早的
    if (scores.size() >= 5) {
        scores.remove(findOldestEntryIndex(scores));
    }
    scores.add(newEntry);

    // 更新最高分
    if (newScore > user.getGame1_HighScore()) {
        user.setGame1_HighScore(newScore);
    }

    user.setGame1_Scores(scores);
    transaction.set(userDoc, user);
    return null;
});

// 辅助方法:找到最早分数的索引
private int findOldestEntryIndex(List<ScoreEntry> entries) {
    int oldestIndex = 0;
    Date oldestDate = entries.get(0).getDate();
    for (int i = 1; i < entries.size(); i++) {
        if (entries.get(i).getDate().before(oldestDate)) {
            oldestDate = entries.get(i).getDate();
            oldestIndex = i;
        }
    }
    return oldestIndex;
}

问题2:如何实现展示所有用户的高分排行榜(从高到低排序)?

方案1:直接查询users集合(适合小用户量)

如果用户数量不多,直接对users集合按对应游戏的最高分字段排序即可:

// 查询game1的全用户排行榜(取前10名)
FirebaseFirestore.getInstance()
    .collection("users")
    .orderBy("game1_HighScore", Query.Direction.DESCENDING)
    .limit(10)
    .get()
    .addOnSuccessListener(querySnapshots -> {
        List<User> leaderboard = querySnapshots.toObjects(User.class);
        // 渲染排行榜UI
    });

注意:如果触发索引错误,需要到Firebase控制台为game1_HighScore创建单字段降序索引。

方案2:预生成排行榜(适合大用户量)

如果用户量很大,直接查询全集合会有性能问题,建议用Cloud Functions定期生成快照:

  1. 创建leaderboards集合,每个游戏对应一个文档(比如game1_leaderboard)
  2. 用定时触发的Cloud Functions查询所有用户的最高分,排序后写入对应文档
  3. 客户端直接读取预生成的排行榜数据,速度更快

问题3:是否应新建“scores”集合?如何关联分数与用户?

两种方案对比

当前单users集合方案新建scores集合方案
查询用户自身分数/最高分速度快支持全局分数统计(比如全用户分数分布、单游戏全分数排名)
数据结构紧凑,适合小数据量扩展性强,后续可添加关卡、成就等维度
无法高效做全局分数分析需要额外维护用户的最近5条分数和最高分缓存

建议

如果仅需存储用户自身历史分数和全用户最高分,当前方案足够;如果未来需要复杂数据分析(比如分数趋势、全游戏分数排名),建议新建scores集合:

  • 每个scores文档包含字段:userId(关联用户ID)、gameId(1/2/3)、score、date
  • 保留users集合中的gameX_Scores和gameX_HighScore作为缓存,避免频繁查询scores集合
  • 关联方式:通过userId字段过滤,查询scores集合时指定userId == 当前用户ID即可获取该用户的所有分数

附加问题:添加新User文档时如何控制字段顺序?

Firestore默认按字段名的ASCII码顺序在控制台显示字段,客户端读取时字段顺序不影响(因为是键值对)。如果一定要在控制台显示自定义顺序,有两种方法:

  1. 给字段名加前缀:比如把name改成0_name,email改成1_email,game1_HighScore改成2_game1_HighScore,控制台会按前缀数字排序,但代码中需要同步修改字段名
  2. 使用@PropertyName注解:只能让代码字段名和Firestore字段名不一致,无法改变控制台显示顺序

注意:客户端读取数据时,字段顺序完全不影响业务逻辑,仅控制台展示有区别。


内容的提问来源于stack exchange,提问作者Ohad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 20:40:18