Google App Engine 成就的数据方案可能性

1 投票
2 回答
572 浏览
提问于 2025-04-15 15:09

我正在用谷歌应用引擎搭建一个Flash游戏网站,打算在上面添加成就系统。现在我在想,怎么才能把这些成就的数据存储起来。我有一个(非谷歌的)用户模型。我需要知道怎么存储每种成就的信息,比如名称、描述和图片,同时还要把这些成就和获得它们的用户关联起来。此外,我还需要记录用户获得成就的时间,以及那些还没有获得成就的用户的当前进度。

如果有人想给出关于成就检测或其他相关任务的建议,欢迎随时分享。

编辑:

非谷歌用户模型:

class BeardUser(db.Model):
    email = db.StringProperty()
    username = db.StringProperty()
    password = db.StringProperty()
    settings = db.ReferenceProperty(UserSettings)
    is_admin = db.BooleanProperty()
    user_role = db.StringProperty(default="user")
    datetime = db.DateTimeProperty(auto_now_add=True)

2 个回答

3

在Brandon的回答基础上继续讲。

如果可以的话,把所有成就定义在Python文件里会快很多,因为这样它们会在内存中,不需要从数据库中取数据。这种做法也更简单。

class StaticAchievement(object):
    """
    An achievement defined in a Python file.
    """
    by_name = {}
    by_index = []
    def __init__(self, name, description="", picture=None):        
        if picture is None: picture = "static/default_achievement.png"
        StaticAchievement.by_name[name] = self
        StaticAchievement.by_index.append(self)
        self.index = len(StaticAchievement.by_index)

# This automatically adds an entry to the StaticAchievement.by_name dict.
# It also adds an entry to to the StaticAchievement.by_index list.
StaticAchievement(
  name="tied your shoe", 
  description="You successfully tied your shoes!", 
  picture="static/shoes.png"
)

然后你只需要在数据库中用一个db.StringListProperty来保存每个玩家的成就ID。当你加载了玩家对象后,渲染成就就不需要额外查数据库了——你已经有了ID,只需在StaticAchievement.all中查找即可。这种方法简单易行,还能让你轻松查询哪些用户获得了某个成就等等。其他的就不需要了。

如果你想要与用户获得成就相关的额外数据(比如获得成就的日期),那么你有几种选择:

1: 在另一个长度相同的ListProperty中存储。

这样可以保持实现的简单性和属性的可索引性。不过,这种解决方案并不适合所有人。如果你想让数据的使用更整洁,可以在玩家对象上写一个方法,像这样:

    def achievement_tuples(self):
        r = []
        for i in range(0, len(self.achievements)):
            r.append( (self.achievements[i], self.achievement_dates[i]) )
        return r

你可以通过维护一个并行的整数ListProperty来处理进度,当用户取得进展时就增加这些整数。

只要你能理解数据是如何表示的,就可以通过你想要的方法来隐藏这种表示方式——这样你就能同时拥有想要的界面和性能、索引特性。但如果你不太需要索引,也不喜欢列表,可以看看第二种选择。

2: 将额外数据存储在BlobProperty中。

这样你需要对数据进行序列化和反序列化,虽然失去了列表属性的良好查询功能,但如果你讨厌并行列表的概念,可能会更开心。这是人们在真正想用Python的方式做事情时常用的方法,而不是App Engine的方式。

3: 将额外数据存储在数据库中。

(例如,一个PlayersAchievement对象,包含成就ID的StringProperty、日期的DateProperty和用户的UserProperty;而在玩家对象上有一个成就的listproperty,里面全是指向PlayersAchievement对象的引用)

由于我们预计玩家可能会获得大量成就,这样的开销会很快变得很糟糕。解决这个问题会变得非常复杂:需要使用内存缓存、存储中间数据,比如预渲染的HTML或序列化的元组列表、设置任务等等。如果你想让成就定义本身可修改或存储在数据库中,也是需要做这些事情的。

3

除非你的用户会动态添加成就(比如在Kongregate那样,用户可以上传自己的游戏等),否则你的成就列表将是静态的。这意味着你可以把成就的(名称、描述、图片)列表存储在你的主Python文件里,或者作为一个成就模块。你可以通过这个列表中指定的名称或ID来引用这些成就。

为了表示某个用户是否获得了成就,一个简单的方法是给你的玩家模型添加一个ListProperty,用来存储玩家获得的成就。默认情况下,这个属性会被索引,这样你就可以查询哪些玩家获得了哪些成就等信息。

如果你还想记录用户获得成就的日期和时间,这就涉及到一些内置属性不太理想的地方。你可以添加另一个ListProperty,里面存储与每个成就对应的日期时间属性,但这样会显得有些麻烦:我们真正想要的是一个元组或字典,这样可以把成就的ID/名称和获得时间一起存储,并且将来方便添加与每个成就相关的其他属性。

我通常的做法是把我理想的数据结构放到一个BlobProperty里,但这样有一些缺点,你可能会更喜欢其他的方法。

另一种方法是把成就模型和用户模型分开,给用户模型添加一个ListProperty,里面存储所有用户获得的成就的引用属性。这种方法的好处是可以很好地索引所有内容,但在运行时会增加额外的API CPU开销,特别是当你需要查找很多成就时,像删除某个用户的所有成就这样的操作会比前面的方法要昂贵得多。

撰写回答