用数据驱动思想来设计游戏

2024-09-04 10:08

本文主要是介绍用数据驱动思想来设计游戏,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  游戏由两部分组成,逻辑和数据。这是一种对游戏的有效划分。逻辑部分定义游戏引擎的核心原则和算法,数据部分则提供其内容 和行为的具体细节。在最初的游戏开发的过程中,大家总是喜欢将逻辑和数据都写入代码中,这样的代码可移植性很差,重用性也差,还容易出现数据的不统一。如果要进行些许修改,甚至有可能牵一发而动全身。 

    数据驱动设计可以比喻成是一种面向对象的游戏设计,它将游戏数据存储在文件中,而不再是嵌入在代码里面。游戏过程中,通过动态的载入并读取和写入数据文件来对数据进行操作。在游戏开发过程中使用数据驱动思想需要注意以下几点。

   现在稍稍比较专业点的游戏都是采用数据驱动的思想来进行开发,但程度各异。而如何最大限制度的利用数据驱动的思想开发游戏,使游戏更具有自定义特性,提高玩家的游戏参与感,是未来游戏的主流。 

 

数据驱动编程的核心

数据驱动编程的核心出发点是相对于程序逻辑,人类更擅长于处理数据。数据比程序逻辑更容易驾驭,所以我们应该尽可能的将设计的复杂度从程序代码转移至数据。

数据比程序逻辑更易驾驭。尽可能把设计的复杂度从代码转移至数据是个好实践)

数据驱动编程的核心

数据驱动编程的核心出发点是相对于程序逻辑,人类更擅长于处理数据。数据比程序逻辑更容易驾驭,所以我们应该尽可能的将设计的复杂度从程序代码转移至数据。

真的是这样吗?让我们来看一个示例。

假设有一个程序,需要处理其他程序发送的消息,消息类型是字符串,每个消息都需要一个函数进行处理。第一印象,我们可能会这样处理:

[cpp]  view plain copy
  1. void msg_proc(const char *msg_type, const char *msg_buf)  
  2. {  
  3.     if (0 == strcmp(msg_type, "inivite"))  
  4.     {  
  5.         inivite_fun(msg_buf);  
  6.     }  
  7.     else if (0 == strcmp(msg_type, "tring_100"))  
  8.     {  
  9.         tring_fun(msg_buf);  
  10.     }  
  11.     else if (0 == strcmp(msg_type, "ring_180"))  
  12.     {  
  13.         ring_180_fun(msg_buf);  
  14.     }  
  15.     else if (0 == strcmp(msg_type, "ring_181"))  
  16.     {  
  17.         ring_181_fun(msg_buf);  
  18.     }  
  19.     else if (0 == strcmp(msg_type, "ring_182"))  
  20.     {  
  21.         ring_182_fun(msg_buf);  
  22.     }  
  23.     else if (0 == strcmp(msg_type, "ring_183"))  
  24.     {  
  25.         ring_183_fun(msg_buf);  
  26.     }  
  27.     else if (0 == strcmp(msg_type, "ok_200"))  
  28.     {  
  29.         ok_200_fun(msg_buf);  
  30.     }  
  31.   
  32.     。。。。。。  
  33.     else if (0 == strcmp(msg_type, "fail_486"))  
  34.     {  
  35.         fail_486_fun(msg_buf);  
  36.     }  
  37.     else  
  38.     {  
  39.         log("未识别的消息类型%s\n", msg_type);  
  40.     }  
  41. }  

上面的消息类型取自sip协议(不完全相同,sip协议借鉴了http协议),消息类型可能还会增加。看着常常的流程可能有点累,检测一下中间某个消息有没有处理也比较费劲,而且,没增加一个消息,就要增加一个流程分支。

按照数据驱动编程的思路,可能会这样设计:

[cpp]  view plain copy
  1. typedef void (*SIP_MSG_FUN)(const char *);  
  2.   
  3. typedef struct __msg_fun_st  
  4. {  
  5.     const char *msg_type;//消息类型  
  6.     SIP_MSG_FUN fun_ptr;//函数指针  
  7. }msg_fun_st;  
  8.   
  9. msg_fun_st msg_flow[] =  
  10. {  
  11.         {"inivite", inivite_fun},  
  12.         {"tring_100", tring_fun},  
  13.         {"ring_180", ring_180_fun},  
  14.         {"ring_181", ring_181_fun},  
  15.         {"ring_182", ring_182_fun},  
  16.         {"ring_183", ring_183_fun},  
  17.         {"ok_200", ok_200_fun},  
  18.   
  19.         。。。。。。  
  20.         {"fail_486", fail_486_fun}  
  21. };  
  22.   
  23. void msg_proc(const char *msg_type, const char *msg_buf)  
  24. {  
  25.     int type_num = sizeof(msg_flow) / sizeof(msg_fun_st);  
  26.     int i = 0;  
  27.   
  28.     for (i = 0; i < type_num; i++)  
  29.     {  
  30.         if (0 == strcmp(msg_flow[i].msg_type, msg_type))  
  31.         {  
  32.             msg_flow[i].fun_ptr(msg_buf);  
  33.             return ;  
  34.         }  
  35.     }  
  36.     log("未识别的消息类型%s\n", msg_type);  
  37. }  

下面这种思路的优势:

1、可读性更强,消息处理流程一目了然。

2、更容易修改,要增加新的消息,只要修改数据即可,不需要修改流程。

3、重用,第一种方案的很多的else if其实只是消息类型和处理函数不同,但是逻辑是一样的。下面的这种方案就是将这种相同的逻辑提取出来,而把容易发生变化的部分提到外面。


隐含在背后的思想

很多设计思路背后的原理其实都是相通的,隐含在数据驱动编程背后的实现思想包括:

1、控制复杂度。通过把程序逻辑的复杂度转移到人类更容易处理的数据中来,从而达到控制复杂度的目标。

2、隔离变化。像上面的例子,每个消息处理的逻辑是不变的,但是消息可能是变化的,那就把容易变化的消息和不容易变化的逻辑分离。

3、机制和策略的分离。和第二点很像,本书中很多地方提到了机制和策略。上例中,我的理解,机制就是消息的处理逻辑,策略就是不同的消息处理(后面想专门写一篇文章介绍下机制和策略)。


数据驱动编程可以用来做什么:

如上例所示,它可以应用在函数级的设计中。

同时,它也可以应用在程序级的设计中,典型的比如用表驱动法实现一个状态机(后面写篇文章专门介绍)。

也可以用在系统级的设计中,比如DSL(这方面我经验有些欠缺,目前不是非常确定)。


它不是什么:

1、 它不是一个全新的编程模型:它只是一种设计思路,而且历史悠久,在unix/linux社区应用很多;

2、它不同于面向对象设计中的数据:“数据驱动编程中,数据不但表示了某个对象的状态,实际上还定义了程序的流程;OO看重的是封装,而数据驱动编程看重的是编写尽可能少的代码。”


书中的值得思考的话:

数据压倒一切。如果选择了正确的数据结构并把一切组织的井井有条,正确的算法就不言自明。编程的核心是数据结构,而不是算法。——Rob Pike

程序员束手无策。。。。。只有跳脱代码,直起腰,仔细思考数据才是最好的行动。表达式编程的精髓。——Fred Brooks

数据比程序逻辑更易驾驭。尽可能把设计的复杂度从代码转移至数据是个好实践。——《unix编程艺术》作者。



    现在稍稍比较专业点的游戏都是采用数据驱动的思想来进行开发,但程度各异。而如何最大限制度的利用数据驱动的思想开发游戏,使游戏更具有自定义特性,提高玩家的游戏参与感,是未来游戏的主流。 

这篇关于用数据驱动思想来设计游戏的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1135722

相关文章

MyBatis-plus处理存储json数据过程

《MyBatis-plus处理存储json数据过程》文章介绍MyBatis-Plus3.4.21处理对象与集合的差异:对象可用内置Handler配合autoResultMap,集合需自定义处理器继承F... 目录1、如果是对象2、如果需要转换的是List集合总结对象和集合分两种情况处理,目前我用的MP的版本

GSON框架下将百度天气JSON数据转JavaBean

《GSON框架下将百度天气JSON数据转JavaBean》这篇文章主要为大家详细介绍了如何在GSON框架下实现将百度天气JSON数据转JavaBean,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录前言一、百度天气jsON1、请求参数2、返回参数3、属性映射二、GSON属性映射实战1、类对象映

C# LiteDB处理时间序列数据的高性能解决方案

《C#LiteDB处理时间序列数据的高性能解决方案》LiteDB作为.NET生态下的轻量级嵌入式NoSQL数据库,一直是时间序列处理的优选方案,本文将为大家大家简单介绍一下LiteDB处理时间序列数... 目录为什么选择LiteDB处理时间序列数据第一章:LiteDB时间序列数据模型设计1.1 核心设计原则

Java+AI驱动实现PDF文件数据提取与解析

《Java+AI驱动实现PDF文件数据提取与解析》本文将和大家分享一套基于AI的体检报告智能评估方案,详细介绍从PDF上传、内容提取到AI分析、数据存储的全流程自动化实现方法,感兴趣的可以了解下... 目录一、核心流程:从上传到评估的完整链路二、第一步:解析 PDF,提取体检报告内容1. 引入依赖2. 封装

MySQL中查询和展示LONGBLOB类型数据的技巧总结

《MySQL中查询和展示LONGBLOB类型数据的技巧总结》在MySQL中LONGBLOB是一种二进制大对象(BLOB)数据类型,用于存储大量的二进制数据,:本文主要介绍MySQL中查询和展示LO... 目录前言1. 查询 LONGBLOB 数据的大小2. 查询并展示 LONGBLOB 数据2.1 转换为十

使用SpringBoot+InfluxDB实现高效数据存储与查询

《使用SpringBoot+InfluxDB实现高效数据存储与查询》InfluxDB是一个开源的时间序列数据库,特别适合处理带有时间戳的监控数据、指标数据等,下面详细介绍如何在SpringBoot项目... 目录1、项目介绍2、 InfluxDB 介绍3、Spring Boot 配置 InfluxDB4、I

Java整合Protocol Buffers实现高效数据序列化实践

《Java整合ProtocolBuffers实现高效数据序列化实践》ProtocolBuffers是Google开发的一种语言中立、平台中立、可扩展的结构化数据序列化机制,类似于XML但更小、更快... 目录一、Protocol Buffers简介1.1 什么是Protocol Buffers1.2 Pro

Python实现数据可视化图表生成(适合新手入门)

《Python实现数据可视化图表生成(适合新手入门)》在数据科学和数据分析的新时代,高效、直观的数据可视化工具显得尤为重要,下面:本文主要介绍Python实现数据可视化图表生成的相关资料,文中通过... 目录前言为什么需要数据可视化准备工作基本图表绘制折线图柱状图散点图使用Seaborn创建高级图表箱线图热

MySQL数据脱敏的实现方法

《MySQL数据脱敏的实现方法》本文主要介绍了MySQL数据脱敏的实现方法,包括字符替换、加密等方法,通过工具类和数据库服务整合,确保敏感信息在查询结果中被掩码处理,感兴趣的可以了解一下... 目录一. 数据脱敏的方法二. 字符替换脱敏1. 创建数据脱敏工具类三. 整合到数据库操作1. 创建服务类进行数据库

MySQL中处理数据的并发一致性的实现示例

《MySQL中处理数据的并发一致性的实现示例》在MySQL中处理数据的并发一致性是确保多个用户或应用程序同时访问和修改数据库时,不会导致数据冲突、数据丢失或数据不一致,MySQL通过事务和锁机制来管理... 目录一、事务(Transactions)1. 事务控制语句二、锁(Locks)1. 锁类型2. 锁粒