3

游戏突发随机事件设计

 2 years ago
source link: https://www.xiejingyang.com/2019/08/19/game-random-event-system-design/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
游戏突发随机事件设计 | Xieisabug

最近在学游戏开发,尝试开发一个经营类游戏,其中有个点子就是:在游戏的过程中,会不时的发生一些突发事件,可能是任务,可能是天灾人祸等,当然,要有趣,那么这个事件就得是随机的,不能是一成不变的。

我的想法是设计一个表达式,使用表达式形成事件的多样化,表达式里的词缀够多,用词缀来实现随机化,这样添加足够多的词缀和表达式就能有足够多的随机事件。最后实现的效果应该是这样:

$charactor 很喜欢您的弟子 #disciple ,对他 $act ,获得奖励 #prize
王重阳很喜欢您的弟子王铁锤,对他千锤百炼,获得奖励一件衣服
乔峰很喜欢您的弟子王铁锤,对他千锤百炼,获得奖励领悟一个技能

其中$开头的词语,代表随机词缀;#开头的词语代表编程表达,也就是需要代码实现功能的词缀;获得的奖励,将与词缀进行挂钩。

为了快速实现原型,先采用python来完成demo,其他语言都能够实现相同效果。

第一步,先解析表达式。首先使用空格对整个表达式split,拆分出来所有的token:

token = expression_str.split(" ")
token
# ['$charactor', '很喜欢您的弟子', '#disciple', ',对他', '$act', ',获得奖励', '#prize']
token = expression_str.split(" ")
token
# ['$charactor', '很喜欢您的弟子', '#disciple', ',对他', '$act', ',获得奖励', '#prize']

遍历tokens,如果是$开头则代表词缀,#开头则代表编程表达,其实也可以直接用token来对比词缀表实现匹配,可是这样效率较低,所以加上了前缀来方便判断。

total_text = []
for i in token:
if i.startswith("$"):
elif i.startswith("#"):
else:
total_text.append(i)
total_text = []
for i in token:
    if i.startswith("$"):
      pass
    elif i.startswith("#"):
      pass
    else:
      total_text.append(i)

在处理词缀之前,首先要定义好词缀表:

store_data = {
'charactor': { 'text': ["令狐冲", "林平之", "王重阳", "乔峰", "段誉", "段誉姑姑的弟弟的儿子的同学的妈妈"] },
'act': { 'text': ['一番把玩', '千锤百炼', '打情骂俏', '拳打脚踢'] },
'place': { 'text': ['泰山', '黄山', '茅厕', '草丛', '房顶'] }
store_data = {
    'charactor': { 'text': ["令狐冲", "林平之", "王重阳", "乔峰", "段誉", "段誉姑姑的弟弟的儿子的同学的妈妈"] },
    'act': { 'text': ['一番把玩', '千锤百炼', '打情骂俏', '拳打脚踢'] },
    'place': { 'text': ['泰山', '黄山', '茅厕', '草丛', '房顶'] }
}

定义好词缀表之后,在遇到$开头的词缀的时候,进行词缀随机匹配:

total_text = []
for i in token:
if i.startswith("$"):
name = i[1:] # 去除前缀的$
rand_i = random.randint(0, len(store_data[name]['text']) - 1) # 随机获得词缀中的一个
total_text.append(store_data[name]['text'][rand_i])
elif i.startswith("#"):
else:
total_text.append(i)
total_text = []
for i in token:
    if i.startswith("$"):
      name = i[1:] # 去除前缀的$
      rand_i = random.randint(0, len(store_data[name]['text']) - 1) # 随机获得词缀中的一个
      total_text.append(store_data[name]['text'][rand_i])
    elif i.startswith("#"):
      pass
    else:
      total_text.append(i)

第二步,计算奖励,在这里为了简便,我直接定义为了#prize,并且一定要是表达式的最后一个词缀,至于为什么往下看就知道了。而#disciple我会获取到游戏中我所拥有的弟子中的一个,那首要一步是定义弟子数据。

disciple_data = ["王铁锤", "谢帅逼"] # 简单定义
disciple_data = ["王铁锤", "谢帅逼"] # 简单定义

当碰到#disciple的时候,直接进行随机选取弟子:

total_text = []
for i in token:
if i.startswith("$"):
name = i[1:] # 去除前缀的$
rand_i = random.randint(0, len(store_data[name]['text']) - 1) # 随机获得词缀中的一个
total_text.append(store_data[name]['text'][rand_i])
elif i.startswith("#"):
name = i[1:]
if name == "disciple":
rand_i = random.randint(0, len(disciple_data) - 1)
total_text.append(disciple_data[rand_i])
else:
total_text.append(i)
total_text = []
for i in token:
    if i.startswith("$"):
      name = i[1:] # 去除前缀的$
      rand_i = random.randint(0, len(store_data[name]['text']) - 1) # 随机获得词缀中的一个
      total_text.append(store_data[name]['text'][rand_i])
    elif i.startswith("#"):
      name = i[1:]
      if name == "disciple":
        rand_i = random.randint(0, len(disciple_data) - 1)
        total_text.append(disciple_data[rand_i])
    else:
      total_text.append(i)

这个时候词缀的填充大致完成了,那是时候计算整个事件带来的奖励了,大致思路是每个词缀都有一个价值,当词缀完成整个句子的时候,整个句子的价值决定了获取到的奖励,这个时候就需要给词缀表增加价值字段:

store_data = {
'charactor': { 'text': ["令狐冲", "林平之", "王重阳", "乔峰", "段誉", "段誉姑姑的弟弟的儿子的同学的妈妈"], 'value': [5, 3, 5, 4, 3, -1] },
'act': { 'text': ['一番把玩', '千锤百炼', '打情骂俏', '拳打脚踢'], 'value': [1, 5, 1, -2] },
'place': { 'text': ['泰山', '黄山', '茅厕', '草丛', '房顶'], 'value': [3, 3, -1, 0, 1]}
store_data = {
    'charactor': { 'text': ["令狐冲", "林平之", "王重阳", "乔峰", "段誉", "段誉姑姑的弟弟的儿子的同学的妈妈"], 'value': [5, 3, 5, 4, 3, -1] },
    'act': { 'text': ['一番把玩', '千锤百炼', '打情骂俏', '拳打脚踢'], 'value': [1, 5, 1, -2] },
    'place': { 'text': ['泰山', '黄山', '茅厕', '草丛', '房顶'], 'value': [3, 3, -1, 0, 1]}
}

第三步就是把整个句子的价值算出来:

total_value = 0 # 用来计算整个句子价值
total_text = []
for i in token:
if i.startswith("$"):
name = i[1:] # 去除前缀的$
rand_i = random.randint(0, len(store_data[name]['text']) - 1) # 随机获得词缀中的一个
total_text.append(store_data[name]['text'][rand_i])
total_value += store_data[name]['value'][rand_i] # 加上词缀对应的价值
elif i.startswith("#"):
name = i[1:]
if name == "disciple":
rand_i = random.randint(0, len(disciple_data) - 1)
total_text.append(disciple_data[rand_i])
else:
total_text.append(i)
total_value = 0 # 用来计算整个句子价值
total_text = []
for i in token:
    if i.startswith("$"):
      name = i[1:] # 去除前缀的$
      rand_i = random.randint(0, len(store_data[name]['text']) - 1) # 随机获得词缀中的一个
      total_text.append(store_data[name]['text'][rand_i])
      total_value += store_data[name]['value'][rand_i] # 加上词缀对应的价值
    elif i.startswith("#"):
      name = i[1:]
      if name == "disciple":
        rand_i = random.randint(0, len(disciple_data) - 1)
        total_text.append(disciple_data[rand_i])
    else:
      total_text.append(i)

这样价值就计算出来了,并且“ 令狐冲 ”、“ 王重阳 ”比“ 林平之 ”、“ 段誉 ”词缀的价值要高,“ 千锤百炼 ”比“ 一番把玩 ”价值要高,“ 拳打脚踢 ”甚至会产生负效果。

对应价值,设计一个简单的奖励表:

prize_data = {
-4: "失去10声望",
-3: "失去2000块钱",
-2: "失去1000块钱",
-1: "失去100块钱",
0: "0块钱",
1: "100块钱",
2: "1000块钱",
3: "2000块钱",
4: "随机道具",
5: "10声望",
6: "20声望",
7: "50声望",
8: "一件衣服",
9: "一把武器",
10: "领悟一个技能",
11: "资质提升",
12: "领悟两个技能"
prize_data = {
    -4: "失去10声望",
    -3: "失去2000块钱",
    -2: "失去1000块钱",
    -1: "失去100块钱",
    0: "0块钱",
    1: "100块钱",
    2: "1000块钱",
    3: "2000块钱",
    4: "随机道具",
    5: "10声望",
    6: "20声望",
    7: "50声望",
    8: "一件衣服",
    9: "一把武器",
    10: "领悟一个技能",
    11: "资质提升",
    12: "领悟两个技能"
}

当然,如果算出来的奖励是多少,就获得对应等级的奖励,游戏也太无趣了,让获取奖励也多一些随机性,使用正态分布来处理获取奖励的概率,如果表达式的价值是8,那么应该是6、7、8的等级概率高,同时往两边扩散概率变低。

那么就进行最后一步,处理prize词缀,并且封装成方法,因为处理prize的时候直接使用了total_value的值,并没有检查之后是否还有词缀,所以为了计算奖励更加准确,需要将prize放在所有词缀的后面,以保证词缀都计算完成:

def random_event(expression_str):
token = expression_str.split(" ")
total_value = 0
total_text = []
for i in token:
if i.startswith("$"):
name = i[1:] # 去除前缀的$
rand_i = random.randint(0, len(store_data[name]['text']) - 1) # 随机获得词缀中的一个
total_text.append(store_data[name]['text'][rand_i])
total_value += store_data[name]['value'][rand_i]
elif i.startswith("#"):
name = i[1:]
if name == "disciple":
rand_i = random.randint(0, len(disciple_data) - 1)
total_text.append(disciple_data[rand_i])
elif name == 'prize':
rand_i = round(random.gauss(total_value - 1, (total_value - 1) / 10))
total_text.append(prize_data[rand_i])
else:
total_text.append(i)
return "".join(total_text)
def random_event(expression_str):
  token = expression_str.split(" ")

  total_value = 0
  total_text = []
  for i in token:
    if i.startswith("$"):
      name = i[1:] # 去除前缀的$
      rand_i = random.randint(0, len(store_data[name]['text']) - 1) # 随机获得词缀中的一个
      total_text.append(store_data[name]['text'][rand_i])
      total_value += store_data[name]['value'][rand_i]
    elif i.startswith("#"):
      name = i[1:]
      if name == "disciple":
        rand_i = random.randint(0, len(disciple_data) - 1)
        total_text.append(disciple_data[rand_i])
      elif name == 'prize':
        rand_i = round(random.gauss(total_value - 1, (total_value - 1) / 10))
        total_text.append(prize_data[rand_i])
    else:
      total_text.append(i)
    
  return "".join(total_text)
random_event("$charactor 很喜欢您的弟子 #disciple ,对他 $act ,获得奖励 #prize")
# 段誉姑姑的弟弟的儿子的同学的妈妈很喜欢您的弟子谢帅逼,对他打情骂俏,获得奖励失去100块钱
random_event("$charactor 从 $place 到您的门派求学,是否接纳")
# 王重阳从泰山到您的门派求学,是否接纳

这样就可以通过编写不同的表达式,添加不同的普通词缀,编写更丰富的编程表达,来丰富整个事件系统,事件系统的能力也会成倍数增长。当然,这只是个思路,要真正做出这个事件,还有很多的概率需要计算。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK