过程

非支配解


简单讲:

  • 最小化问题,任意目标函数我都比你小;任意目标函数我都不比你大,某个目标函数我还比你更小
  • 最大化问题,任意目标函数我都比你小;任意目标函数我都不比你小,某个目标函数我还比你更大

类比:

  • 比烂,所有方面我都比你更烂;所有方面我都不比你更好,某些方面还比你更烂
  • 比优秀,所有方面我都比你优秀;所有方面我都不比你差,某些方面还比你更优秀

非支配排序


f 1 , f 2 f_1, f_2 f1​,f2​是两个目标函数
根据解被其他解支配的次数从低到高排序,最低是0
被0个解支配的解组成第1前沿,被1个解支配的解组成第2前沿,被2个解支配的解组成第3前沿
第1前沿不被任何解支配,是帕累托最优解集PS在目标空间( f 1 , f 2 f_1, f_2 f1​,f2​)中的映射,帕累托最优前沿(PF)

目标空间拥挤度距离

  1. 传入某个前沿(前沿上有多个个体)
  2. for 所有目标函数:目标函数数量=排序后前沿数量
    根据目标函数值,升序排序前沿上的所有个体,得到排序后的前沿
  3. for 所有排序后前沿:
    排序后前沿第i个个体的拥挤度距离=(第i+1个体的目标函数值-第i-1个体的目标函数值)/(最大值-最小值)
  4. 得到传入前沿上的每个个体的拥挤度距离

交叉

概率交叉a, b个体:(a+b)/2 或 (a-b)/2

变异

对交叉的结果进行变异
概率变异交叉的结果:重新随机初始化 或 返回原值(不变异)

选择

精英选择

  • 首先,保留第1前沿中的不拥挤个体
  • 不够,保留第2前沿中的不拥挤个体

代码/实数个体

import math
import random
import matplotlib.pyplot as pltdef fitness_func1(x):"""目标函数1:param x::return:"""value = -x ** 2return valuedef fitness_func2(x):"""目标函数2:param x::return:"""value = -(x - 2) ** 2return valueclass NSGAII(object):"""nondominated sorting genetic algorithm II"""def __init__(self, max_gen, lb, ub, NP, dim):"""初始化化:param max_gen: 最大迭代次数:param lb: 下限:param ub: 上限:param NP: 种群大小,解个数:param dim: 解维度"""# 终止条件self.max_gen = max_gen# 解空间self.lb = lbself.ub = ub# 求解者self.NP = NPself.dim = dim# 参数def initialize(self):"""种群初始化:return:"""# 在解空间内随机初始化种群solution = [self.lb + (self.ub - self.lb) * random.random() for i in range(0, self.NP)]return solutiondef evolve(self, solution):"""进化:交叉,变异,选择:param solution::return:"""for gen_no in range(1, self.max_gen + 1):# 求种群中每个个体的目标函数值function1_values = [fitness_func1(solution[i]) for i in range(self.NP)]function2_values = [fitness_func2(solution[i]) for i in range(self.NP)]# 非支配排序,求多级前沿,front[[],[]] 第1级前沿上个体的索引列表,第2级前沿上个体的索引列表non_dominated_sorted_solution = self.fast_non_dominated_sort(function1_values[:], function2_values[:])# 输出当前迭代的最佳前沿,第1级前沿print("第", gen_no, "迭代的最佳前沿:")for idx in non_dominated_sorted_solution[0]:# round 四舍五入 3保留3位小数print(round(solution[idx], 3), end=" ")print("\n")# 求每一级前沿上的每个个体的拥挤度crowding_distance_values = []for i in range(0, len(non_dominated_sorted_solution)):crowding_distance_values.append(self.crowding_distance(function1_values[:], function2_values[:],non_dominated_sorted_solution[i][:]))# 创建新种群,后代,在原种群中随机找两个个体交叉后变异产生后代。新种群扩大至原来的2倍solution2 = solution[:]while (len(solution2) != 2 * self.NP):a1 = random.randint(0, self.NP - 1)b1 = random.randint(0, self.NP - 1)solution2.append(self.crossover(solution[a1], solution[b1]))# 求新种群中每个个体的目标函数值function1_values2 = [fitness_func1(solution2[i]) for i in range(0, 2 * self.NP)]function2_values2 = [fitness_func2(solution2[i]) for i in range(0, 2 * self.NP)]# 非支配排序,求多级前沿,front[[],[]] 第1级前沿上个体的索引列表,第2级前沿上个体的索引列表non_dominated_sorted_solution2 = self.fast_non_dominated_sort(function1_values2[:], function2_values2[:])# 求每一级前沿上的每个个体的拥挤度crowding_distance_values2 = []for i in range(0, len(non_dominated_sorted_solution2)):crowding_distance_values2.append(self.crowding_distance(function1_values2[:], function2_values2[:],non_dominated_sorted_solution2[i][:]))# 精英选择,首先,保留第1前沿中的不拥挤个体,不够NP,保留第2前沿中的不拥挤个体,依次类推solution = self.select(solution2, non_dominated_sorted_solution2, crowding_distance_values2)return function1_values, function2_valuesdef crossover(self, a, b):"""交叉,种群中的个体a, b进行交配:param a: 种群中的个体a:param b: 种群中的个体b:return: 交配后的子个体"""r = random.random()if r > 0.5:return self.mutation((a + b) / 2)else:return self.mutation((a - b) / 2)def mutation(self, c):"""变异,对交叉后的个体进行变异交叉后的个体一定概率发生变异变异方式:随机初始化:param c: 交叉后的个体,交配后的子个体:return: 变异后的子个体"""mutation_prob = random.random()if mutation_prob < 1:c = self.lb + (self.ub - self.lb) * random.random()return cdef select(self, solution2, non_dominated_sorted_solution2, crowding_distance_values2):"""依次保留第i级前沿中的不拥挤个体选择,精英选择机制首先,保留第1前沿中的不拥挤的个体不够,再保留第2前沿中的不拥挤的个体不够,往后面的前沿寻找:param solution2: 交叉变异后的种群:param non_dominated_sorted_solution2: 非支配排序后的多级前沿,前沿上有多个个体:param crowding_distance_values2: 每级牵引上的每个个体的拥挤度:return:"""new_solution_idx = []# 循环每个前沿for i in range(0, len(non_dominated_sorted_solution2)):# 第i级前沿front_i = non_dominated_sorted_solution2[i]# 第i级前沿上个体的拥挤度front_i_crow = crowding_distance_values2[i]# 我在什么位置# front_i[j],每个个体在前沿上的位置front_i_idxs = [self.index_of(front_i[j], front_i) for j in range(len(front_i))]# 我的位置挤不挤# 根据拥挤度升序排序第i级前沿个体索引sorted_front_i_idxs = self.sort_by_values(front_i_idxs[:], front_i_crow[:])# 得到拥挤度升序排序后的前沿front = [front_i[sorted_front_i_idxs[j]] for j in range(0, len(front_i))]# reverse;反转列表中的元素,拥挤度越大越好front.reverse()# 依次保留第i级前沿中的不拥挤个体for value in front:new_solution_idx.append(value)if (len(new_solution_idx) == self.NP):breakif (len(new_solution_idx) == self.NP):break# 获取新解solution = [solution2[idx] for idx in new_solution_idx]return solutiondef fast_non_dominated_sort(self, values1, values2):"""获得非支配排序的多级前沿快速非支配排序被0个解支配的解组成第1前沿,被1个解支配的解组成第2前沿,被2个解支配的解组成第3前沿:param values1: 种群个体代入适应度函数1的值:param values2: 种群个体代入适应度函数2的值:return: 多级前沿,front[[],[],...]"""# 算法:使用索引# 保留每个解支配的解,我支配了谁S = [[] for i in range(0, len(values1))]# 依次保留第1级前沿个体,第2级前沿个体front = [[]]# 保留每个解被n个解支配n = [0 for i in range(0, len(values1))]# 保留每个解的等级rank = [0 for i in range(0, len(values1))]# 算法:在循环过程中使用了存储器和计数器# 目的:保留每个解支配的解,保留每个解被其他解支配的次数for p in range(0, len(values1)):# p支配了谁S[p]# p被n个解支配n[p] = 0# 要么就是p支配q,要么就是q支配pfor q in range(0, len(values1)):# 两个目标的非支配解定义:简单讲,对于任何目标我都比你大if (values1[p] > values1[q] and values2[p] > values2[q]) \or (values1[p] >= values1[q] and values2[p] > values2[q]) \or (values1[p] > values1[q] and values2[p] >= values2[q]):if q not in S[p]:S[p].append(q)elif (values1[q] > values1[p] and values2[q] > values2[p]) \or (values1[q] >= values1[p] and values2[q] > values2[p]) \or (values1[q] > values1[p] and values2[q] >= values2[p]):n[p] = n[p] + 1if n[p] == 0:rank[p] = 0if p not in front[0]:front[0].append(p)i = 0# 多前沿排序,第1级前沿不被任何解支配,第2级前沿被1个解支配,依次类推while (front[i] != []):# 存储被p支配的解Q = []for p in front[i]:# 被p支配的解for q in S[p]:# 第3级前沿解被第1级,第2级前沿解支配,第3级前沿解在第1级和第2级的循环中都会-1n[q] = n[q] - 1if (n[q] == 0):rank[q] = i + 1if q not in Q:Q.append(q)i = i + 1front.append(Q)# 删除了不支配任何解的前沿,被前面所有前沿支配的前沿del front[len(front) - 1]return frontdef crowding_distance(self, values1, values2, front):"""前沿中第i个个体的拥挤距离=排序后左右个体目标函数之差/(最大值-最小值)目标空间拥挤度距离:param values1: 种群个体代入适应度函数1的值:param values2: 种群个体代入适应度函数2的值:param front: 前沿:return:"""distance = [0 for i in range(0, len(front))]# 根据目标函数值升序排序前沿sorted1_front = self.sort_by_values(front, values1[:])sorted2_front = self.sort_by_values(front, values2[:])# 第一个和最后一个个体的拥挤距离设为无穷大distance[0] = 4444444444444444distance[len(front) - 1] = 4444444444444444# 前沿中第i个个体的拥挤距离=排序后左右个体目标函数之差/(最大值-最小值)for k in range(1, len(front) - 1):distance[k] = distance[k] + (values1[sorted1_front[k + 1]] - values2[sorted1_front[k - 1]]) / (max(values1) - min(values1))for k in range(1, len(front) - 1):distance[k] = distance[k] + (values1[sorted2_front[k + 1]] - values2[sorted2_front[k - 1]]) / (max(values2) - min(values2))return distancedef index_of(self, a, list):"""返回值a在list中的位置(索引):param a::param list::return:"""for i in range(len(list)):if list[i] == a:return ireturn -1def sort_by_values(self, list0, values):"""根据values升序排序listvalues的索引部分在list中:param list0: 待排序列表:param values: 值列表:return:"""sorted_list = []while (len(sorted_list) != len(list0)):# 如果目标函数列表中的最小值的索引位于当前前沿中,则将当前索引存入sorted_list中if self.index_of(min(values), values) in list0:sorted_list.append(self.index_of(min(values), values))# 为了找下一个目标函数列表中的最小值,将当前最小值设置为 浮点正无穷values[self.index_of(min(values), values)] = math.infreturn sorted_listdef show(self, function1_values, function2_values):"""绘制帕累托前沿PF:param function1_values: 解代入目标函数1的值:param function2_values: 解代入目标函数2的值:return:"""# *-1:适应度函数值越大越好,将目标函数改造成适应度函数时*-1。画图目标函数的PF时,再变回来func1_vs = [v1 * -1 for v1 in function1_values]func2_vs = [v2 * -1 for v2 in function2_values]plt.title('NSGAII')plt.xlabel('Function 1', fontsize=15)plt.ylabel('Function 2', fontsize=15)plt.scatter(func1_vs, func2_vs)plt.show()if __name__ == '__main__':# max_gen, lb, ub, NP, dim# 认为1个数是一个解nsgaii = NSGAII(1000, -55, 55, 20, 1)solution = nsgaii.initialize()function1_values, function2_values = nsgaii.evolve(solution)nsgaii.show(function1_values, function2_values)

代码/向量个体

import numpy
import matplotlib.pyplot as pltdef fitness_func1(x):"""目标函数1:param x::return:"""value = numpy.sum(-x ** 2)return valuedef fitness_func2(x):"""目标函数2:param x::return:"""value = numpy.sum(-(x - 2) ** 2)return valueclass NSGAII(object):"""nondominated sorting genetic algorithm II"""def __init__(self, max_gen, lb, ub, NP, dim):"""初始化化:param max_gen: 最大迭代次数:param lb: 下限:param ub: 上限:param NP: 种群大小,解个数:param dim: 解维度"""# 终止条件self.max_gen = max_gen# 解空间self.lb = lbself.ub = ub# 求解者self.NP = NPself.dim = dim# 参数def initialize(self):"""种群初始化:return:"""# 在解空间内随机初始化种群solution = numpy.random.random((self.NP, self.dim)) * (self.ub - self.lb) + self.lb# solution = numpy.random.uniform(self.lb, self.ub, (self.NP, self.dim));return solutiondef evolve(self, solution):"""进化:交叉,变异,选择:param solution::return:"""for gen_no in range(1, self.max_gen + 1):# 求种群中每个个体的目标函数值function1_values = [fitness_func1(solution[i]) for i in range(self.NP)]function2_values = [fitness_func2(solution[i]) for i in range(self.NP)]# 非支配排序,求多级前沿,front[[],[]] 第1级前沿上个体列表,第2级前沿上个体列表non_dominated_sorted_solution = self.fast_non_dominated_sort(function1_values[:], function2_values[:])# 输出当前迭代的最佳前沿,第1级前沿print("第", gen_no, "迭代的最佳前沿:")for idx in non_dominated_sorted_solution[0]:# round 四舍五入 3保留3位小数print(numpy.round(solution[idx], 3), end=" ")print("\n")# 求每一级前沿上的每个个体的拥挤度crowding_distance_values = []for i in range(0, len(non_dominated_sorted_solution)):crowding_distance_values.append(self.crowding_distance(function1_values[:], function2_values[:],non_dominated_sorted_solution[i][:]))# 创建新种群,后代,在原种群中随机找两个个体交叉后变异产生后代。新种群扩大至原来的2倍solution2 = numpy.copy(solution)while (len(solution2) != 2 * self.NP):a1 = numpy.random.randint(0, self.NP - 1)b1 = numpy.random.randint(0, self.NP - 1)cm = self.crossover(solution[a1], solution[b1])# 防止越界numpy.clip(cm, self.lb, self.ub)solution2 = numpy.append(solution2, cm, axis=0)# 求新种群中每个个体的目标函数值function1_values2 = [fitness_func1(solution2[i]) for i in range(0, 2 * self.NP)]function2_values2 = [fitness_func2(solution2[i]) for i in range(0, 2 * self.NP)]# 非支配排序,求多级前沿,front[[],[]] 第1级前沿上个体的索引列表,第2级前沿上个体的索引列表non_dominated_sorted_solution2 = self.fast_non_dominated_sort(function1_values2[:], function2_values2[:])# 求每一级前沿上的每个个体的拥挤度crowding_distance_values2 = []for i in range(0, len(non_dominated_sorted_solution2)):crowding_distance_values2.append(self.crowding_distance(function1_values2[:], function2_values2[:],non_dominated_sorted_solution2[i][:]))# 精英选择,首先,保留第1前沿中的不拥挤个体,不够NP,保留第2前沿中的不拥挤个体,依次类推solution = self.select(solution2, non_dominated_sorted_solution2, crowding_distance_values2)return function1_values, function2_valuesdef crossover(self, a, b):"""交叉,种群中的个体a, b进行交配:param a: 种群中的个体a:param b: 种群中的个体b:return: 交配后的子个体"""r = numpy.random.random()if r > 0.5:return self.mutation((a + b) / 2)else:return self.mutation((a - b) / 2)def mutation(self, c):"""变异,对交叉后的个体进行变异交叉后的个体一定概率发生变异变异方式:随机初始化:param c: 交叉后的个体,交配后的子个体:return: 变异后的子个体"""mutation_prob = numpy.random.random()if mutation_prob < 1:c = numpy.random.random((1, self.dim)) * (self.ub - self.lb) + self.lb# c = numpy.random.uniform(self.lb, self.ub, (1, self.dim))return cdef select(self, solution2, non_dominated_sorted_solution2, crowding_distance_values2):"""依次保留第i级前沿中的不拥挤个体选择,精英选择机制首先,保留第1前沿中的不拥挤的个体不够,再保留第2前沿中的不拥挤的个体不够,往后面的前沿寻找:param solution2: 交叉变异后的种群:param non_dominated_sorted_solution2: 非支配排序后的多级前沿,前沿上有多个个体:param crowding_distance_values2: 每级牵引上的每个个体的拥挤度:return:"""new_solution_idx = []# 循环每个前沿for i in range(0, len(non_dominated_sorted_solution2)):# 第i级前沿front_i = non_dominated_sorted_solution2[i]# 第i级前沿上个体的拥挤度front_i_crow = crowding_distance_values2[i]# 我在什么位置# front_i[j],每个个体在前沿上的位置front_i_idxs = [self.index_of(front_i[j], front_i) for j in range(len(front_i))]# 我的位置挤不挤# 根据拥挤度升序排序第i级前沿个体索引sorted_front_i_idxs = self.sort_by_values(front_i_idxs[:], front_i_crow[:])# 得到拥挤度升序排序后的前沿front = [front_i[sorted_front_i_idxs[j]] for j in range(0, len(front_i))]# reverse;反转列表中的元素,拥挤度越大越好front.reverse()# 依次保留第i级前沿中的不拥挤个体for value in front:new_solution_idx.append(value)if (len(new_solution_idx) == self.NP):breakif (len(new_solution_idx) == self.NP):break# 获取新解solution = solution2[new_solution_idx, :]return solutiondef fast_non_dominated_sort(self, values1, values2):"""获得非支配排序的多级前沿快速非支配排序被0个解支配的解组成第1前沿,被1个解支配的解组成第2前沿,被2个解支配的解组成第3前沿:param values1: 种群个体代入适应度函数1的值:param values2: 种群个体代入适应度函数2的值:return: 多级前沿,front[[],[],...]"""# 算法:使用索引# 保留每个解支配的解,我支配了谁S = [[] for i in range(0, len(values1))]# 依次保留第1级前沿个体,第2级前沿个体front = [[]]# 保留每个解被n个解支配n = [0 for i in range(0, len(values1))]# 保留每个解的等级rank = [0 for i in range(0, len(values1))]# 算法:在循环过程中使用了存储器和计数器# 目的:保留每个解支配的解,保留每个解被其他解支配的次数for p in range(0, len(values1)):# p支配了谁S[p]# p被n个解支配n[p] = 0# 要么就是p支配q,要么就是q支配pfor q in range(0, len(values1)):# 两个目标的非支配解定义:简单讲,对于任何目标我都比你大if (values1[p] > values1[q] and values2[p] > values2[q]) \or (values1[p] >= values1[q] and values2[p] > values2[q]) \or (values1[p] > values1[q] and values2[p] >= values2[q]):if q not in S[p]:S[p].append(q)elif (values1[q] > values1[p] and values2[q] > values2[p]) \or (values1[q] >= values1[p] and values2[q] > values2[p]) \or (values1[q] > values1[p] and values2[q] >= values2[p]):n[p] = n[p] + 1if n[p] == 0:rank[p] = 0if p not in front[0]:front[0].append(p)i = 0# 多前沿排序,第1级前沿不被任何解支配,第2级前沿被1个解支配,依次类推while (front[i] != []):# 存储被p支配的解Q = []for p in front[i]:# 被p支配的解for q in S[p]:# 第3级前沿解被第1级,第2级前沿解支配,第3级前沿解在第1级和第2级的循环中都会-1n[q] = n[q] - 1if (n[q] == 0):rank[q] = i + 1if q not in Q:Q.append(q)i = i + 1front.append(Q)# 删除了不支配任何解的前沿,被前面所有前沿支配的前沿del front[len(front) - 1]return frontdef crowding_distance(self, values1, values2, front):"""前沿中第i个个体的拥挤距离=排序后左右个体目标函数值之差/(最大值-最小值)目标空间拥挤度距离:param values1: 种群个体代入适应度函数1的值:param values2: 种群个体代入适应度函数2的值:param front: 前沿:return:"""distance = [0 for i in range(0, len(front))]# 根据目标函数值升序排序前沿sorted1_front = self.sort_by_values(front, values1[:])sorted2_front = self.sort_by_values(front, values2[:])# 第一个和最后一个个体的拥挤距离设为无穷大distance[0] = 4444444444444444distance[len(front) - 1] = 4444444444444444# 前沿中第i个个体的拥挤距离=排序后左右个体目标函数之差/(最大值-最小值)for k in range(1, len(front) - 1):distance[k] = distance[k] + (values1[sorted1_front[k + 1]] - values2[sorted1_front[k - 1]]) / (max(values1) - min(values1))for k in range(1, len(front) - 1):distance[k] = distance[k] + (values1[sorted2_front[k + 1]] - values2[sorted2_front[k - 1]]) / (max(values2) - min(values2))return distancedef index_of(self, a, list):"""返回值a在list中的位置(索引):param a::param list::return:"""for i in range(len(list)):if list[i] == a:return ireturn -1def sort_by_values(self, list0, values):"""根据values升序排序listvalues的索引部分在list中:param list0: 待排序列表:param values: 值列表:return:"""sorted_list = []while (len(sorted_list) != len(list0)):# 如果目标函数列表中的最小值的索引位于当前前沿中,则将当前索引存入sorted_list中if self.index_of(min(values), values) in list0:sorted_list.append(self.index_of(min(values), values))# 为了找下一个目标函数列表中的最小值,将当前最小值设置为 浮点正无穷values[self.index_of(min(values), values)] = numpy.math.infreturn sorted_listdef show(self, function1_values, function2_values):"""绘制帕累托前沿PF:param function1_values: 解代入目标函数1的值:param function2_values: 解代入目标函数2的值:return:"""# *-1:适应度函数值越大越好,将目标函数改造成适应度函数时*-1。画图目标函数的PF时,再变回来func1_vs = [v1 * -1 for v1 in function1_values]func2_vs = [v2 * -1 for v2 in function2_values]plt.title('NSGAII')plt.xlabel('Function 1', fontsize=15)plt.ylabel('Function 2', fontsize=15)plt.scatter(func1_vs, func2_vs)plt.show()if __name__ == '__main__':# max_gen, lb, ub, NP, dimnsgaii = NSGAII(5000, -55, 55, 20, 2)solution = nsgaii.initialize()function1_values, function2_values = nsgaii.evolve(solution)nsgaii.show(function1_values, function2_values)

参考文献

  1. 岳彩通, et al., 多模态多目标优化综述. 控制与决策, 2021.
  2. Deb, K., et al., A fast and elitist multiobjective genetic algorithm: NSGA-II. IEEE TRANSACTIONS ON EVOLUTIONARY COMPUTATION, 2002. 6(2): p. 182-197.

MMO/NSGAII相关推荐

  1. [转] MMO即时战斗:地图角色同步管理和防作弊实现

    一.前言 无论是端游.页游.手游如果是采用了MMO即时战斗游戏模式,基本都会遇到同屏多角色实时移动.释放技能.战斗等场景,于是自然也需要实现如何管理同屏内各种角色的信息同步:例如角色的位置.以及角色身 ...

  2. NSGA-II入门C1

    NSGA-II入门C1 觉得有用的话,欢迎一起讨论相互学习~Follow Me 参考文献1 参考文献2 白话多目标 多目标中的目标是个瓦特? 多目标即是优化问题中的优化目标在3个及以上,一般这些优化的 ...

  3. 【NSGAII】基于NSGAII的多目标优化算法的MATLAB仿真

    1.软件版本 matlab2021a 2.本算法理论知识 NSGA-II适合应用于复杂的.多目标优化问题.是K-Deb教授于2002在论文:A Fast and Elitist Multiobject ...

  4. 基于NSGAII的多目标优化算法的MATLAB仿真

    1.算法简介 NSGA-II在引入算术交叉算子的同时,提出并引入累积排序适应度赋值策略.实验表明,INSGA具有更高的收敛速度和更好的种群多样性. 2.部分核心代码 clc; clear; close ...

  5. MATLAB应用实战系列NSGA-II多目标优化算法原理及应用实例(附MATLAB代码)

    前言 NSGA-Ⅱ是最流行的多目标遗传算法之一,它降低了非劣排序遗传算法的复杂性,具有运行速度快,解集的收敛性好的优点,成为其他多目标优化算法性能的基准. NSGA-Ⅱ算法是 Srinivas 和 D ...

  6. workerman mmo_2020了,我们为什么还在做MMO端游

    众所周知,近几年端游市场全然不景气,有实力的大厂基本放弃了对端游的研发. 以MMORPG为例,许多年前,这类游戏盛行的时候,尤其是征途开创免费游戏先河后,各式各样的MMO遍地开花,几乎所有游戏厂商都在 ...

  7. 《天涯明月刀》动作开发负责人:让美术hold住全场,推翻MMO的动作套路化设计

    10月16日,在腾讯游戏学院举办的第三届游戏开发者大会(TGDC)艺术分论坛上,<天涯明月刀>动作开发负责人窦德斌,从美术从业者的角度对游戏动作设计的种种维度做了分享. 针对<天涯明 ...

  8. 字节跳动又一款中重度游戏曝光,它要进军“漫改MMO”领域!

    近期,由巴别时代研发.朝夕光年(字节跳动旗下游戏品牌)发行的<镖人>手游开启了新一轮的测试.游戏由同名漫画改编而成. 提及<镖人>这个IP,不关注漫画的人可能不太了解. < ...

  9. 【逆水寒三周年】大型MMO项目UI管理的价值与责任

    <逆水寒>作为一款大型MMO端游,UI工作的复杂和压力相当巨大,虽然最艰难紧张的时间是17年到19年,但是如今想来还是心有余悸.大型MMO端游流程复杂.资源繁多.开发过程中,参与人员多.开 ...

最新文章

  1. Java IO流之字符缓冲流
  2. 配置mysql使其允许外部ip进行登录
  3. spi四种工作模式时序图_还没学会SPI总线协议?干货都在这里
  4. 我们都有冲动了的飞鸽传书2011
  5. 无法启动调试--未安装 Silverlight Developer 运行时。请安装一个匹配版本
  6. javascript ajax 脚本跨域调用全解析
  7. [CC-CHEFINV]Chef and Swaps
  8. oracle 脚本怎么写,wincc与 oracle 数据通讯脚本如何写-工业支持中心-西门子中国...
  9. 软件测试数据中心,数据中心行业测试方案.PDF
  10. led数字字体_led电视质量排行榜
  11. 2021年新版python学习课程网盘分享
  12. 特殊符号(包括数字和字母)
  13. 《三体2:黑暗森林》读后感
  14. 360极速浏览器打不开国内网站的一种解决方法
  15. 深度学习进行人体的姿态估计
  16. Unity NavMesh寻路 A*(A star)分析及实例应用(一)
  17. 元学习 - Learning How to Learn - 第一课:集中与发散思维
  18. 2020年11月软考网络规划设计师上午真题及答案解析
  19. Lumiprobe/艾美捷——LumiMAG基因组DNA血液和口腔试剂盒
  20. php读音量大小,Android_Android中实时获取音量分贝值详解,基础知识 度量声音强度,大 - phpStudy...

热门文章

  1. 计算机组成原理一(考研)
  2. ie型lfsr_crc校验的实现(移位寄存器)
  3. BLE4.0教程一 蓝牙协议连接过程与广播分析
  4. 就业前夕——Java查缺补漏(从头学)
  5. 泛微E8设置表单明细增删按钮
  6. Go使用Gin+Redis实现增删改查
  7. Matplotlib任务实现:绘制国民生产总值构成分布饼图
  8. 【以太网硬件十六】双绞线有哪些种类?
  9. CSS写表格样式的两种基本方式
  10. 智能工单售后管理系统是什么?