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

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

相关文章

一文详解Python如何开发游戏

《一文详解Python如何开发游戏》Python是一种非常流行的编程语言,也可以用来开发游戏模组,:本文主要介绍Python如何开发游戏的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录一、python简介二、Python 开发 2D 游戏的优劣势优势缺点三、Python 开发 3D

Linux下利用select实现串口数据读取过程

《Linux下利用select实现串口数据读取过程》文章介绍Linux中使用select、poll或epoll实现串口数据读取,通过I/O多路复用机制在数据到达时触发读取,避免持续轮询,示例代码展示设... 目录示例代码(使用select实现)代码解释总结在 linux 系统里,我们可以借助 select、

C#使用iText获取PDF的trailer数据的代码示例

《C#使用iText获取PDF的trailer数据的代码示例》开发程序debug的时候,看到了PDF有个trailer数据,挺有意思,于是考虑用代码把它读出来,那么就用到我们常用的iText框架了,所... 目录引言iText 核心概念C# 代码示例步骤 1: 确保已安装 iText步骤 2: C# 代码程

Pandas处理缺失数据的方式汇总

《Pandas处理缺失数据的方式汇总》许多教程中的数据与现实世界中的数据有很大不同,现实世界中的数据很少是干净且同质的,本文我们将讨论处理缺失数据的一些常规注意事项,了解Pandas如何表示缺失数据,... 目录缺失数据约定的权衡Pandas 中的缺失数据None 作为哨兵值NaN:缺失的数值数据Panda

C++中处理文本数据char与string的终极对比指南

《C++中处理文本数据char与string的终极对比指南》在C++编程中char和string是两种用于处理字符数据的类型,但它们在使用方式和功能上有显著的不同,:本文主要介绍C++中处理文本数... 目录1. 基本定义与本质2. 内存管理3. 操作与功能4. 性能特点5. 使用场景6. 相互转换核心区别

python库pydantic数据验证和设置管理库的用途

《python库pydantic数据验证和设置管理库的用途》pydantic是一个用于数据验证和设置管理的Python库,它主要利用Python类型注解来定义数据模型的结构和验证规则,本文给大家介绍p... 目录主要特点和用途:Field数值验证参数总结pydantic 是一个让你能够 confidentl

JAVA实现亿级千万级数据顺序导出的示例代码

《JAVA实现亿级千万级数据顺序导出的示例代码》本文主要介绍了JAVA实现亿级千万级数据顺序导出的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 前提:主要考虑控制内存占用空间,避免出现同时导出,导致主程序OOM问题。实现思路:A.启用线程池

SpringBoot分段处理List集合多线程批量插入数据方式

《SpringBoot分段处理List集合多线程批量插入数据方式》文章介绍如何处理大数据量List批量插入数据库的优化方案:通过拆分List并分配独立线程处理,结合Spring线程池与异步方法提升效率... 目录项目场景解决方案1.实体类2.Mapper3.spring容器注入线程池bejsan对象4.创建

PHP轻松处理千万行数据的方法详解

《PHP轻松处理千万行数据的方法详解》说到处理大数据集,PHP通常不是第一个想到的语言,但如果你曾经需要处理数百万行数据而不让服务器崩溃或内存耗尽,你就会知道PHP用对了工具有多强大,下面小编就... 目录问题的本质php 中的数据流处理:为什么必不可少生成器:内存高效的迭代方式流量控制:避免系统过载一次性

C#实现千万数据秒级导入的代码

《C#实现千万数据秒级导入的代码》在实际开发中excel导入很常见,现代社会中很容易遇到大数据处理业务,所以本文我就给大家分享一下千万数据秒级导入怎么实现,文中有详细的代码示例供大家参考,需要的朋友可... 目录前言一、数据存储二、处理逻辑优化前代码处理逻辑优化后的代码总结前言在实际开发中excel导入很