Google App Engine 成就的数据方案可能性
我正在用谷歌应用引擎搭建一个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 个回答
在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或序列化的元组列表、设置任务等等。如果你想让成就定义本身可修改或存储在数据库中,也是需要做这些事情的。
除非你的用户会动态添加成就(比如在Kongregate那样,用户可以上传自己的游戏等),否则你的成就列表将是静态的。这意味着你可以把成就的(名称、描述、图片)列表存储在你的主Python文件里,或者作为一个成就模块。你可以通过这个列表中指定的名称或ID来引用这些成就。
为了表示某个用户是否获得了成就,一个简单的方法是给你的玩家模型添加一个ListProperty,用来存储玩家获得的成就。默认情况下,这个属性会被索引,这样你就可以查询哪些玩家获得了哪些成就等信息。
如果你还想记录用户获得成就的日期和时间,这就涉及到一些内置属性不太理想的地方。你可以添加另一个ListProperty,里面存储与每个成就对应的日期时间属性,但这样会显得有些麻烦:我们真正想要的是一个元组或字典,这样可以把成就的ID/名称和获得时间一起存储,并且将来方便添加与每个成就相关的其他属性。
我通常的做法是把我理想的数据结构放到一个BlobProperty里,但这样有一些缺点,你可能会更喜欢其他的方法。
另一种方法是把成就模型和用户模型分开,给用户模型添加一个ListProperty,里面存储所有用户获得的成就的引用属性。这种方法的好处是可以很好地索引所有内容,但在运行时会增加额外的API CPU开销,特别是当你需要查找很多成就时,像删除某个用户的所有成就这样的操作会比前面的方法要昂贵得多。