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

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

相关文章

C++如何通过Qt反射机制实现数据类序列化

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊C++如何通过Qt反射机制实现数据类序列化吧... 目录设计预期设计思路代码实现使用方法在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类

usb接口驱动异常问题常用解决方案

《usb接口驱动异常问题常用解决方案》当遇到USB接口驱动异常时,可以通过多种方法来解决,其中主要就包括重装USB控制器、禁用USB选择性暂停设置、更新或安装新的主板驱动等... usb接口驱动异常怎么办,USB接口驱动异常是常见问题,通常由驱动损坏、系统更新冲突、硬件故障或电源管理设置导致。以下是常用解决

SpringBoot使用GZIP压缩反回数据问题

《SpringBoot使用GZIP压缩反回数据问题》:本文主要介绍SpringBoot使用GZIP压缩反回数据问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录SpringBoot使用GZIP压缩反回数据1、初识gzip2、gzip是什么,可以干什么?3、Spr

SpringBoot集成Milvus实现数据增删改查功能

《SpringBoot集成Milvus实现数据增删改查功能》milvus支持的语言比较多,支持python,Java,Go,node等开发语言,本文主要介绍如何使用Java语言,采用springboo... 目录1、Milvus基本概念2、添加maven依赖3、配置yml文件4、创建MilvusClient

SpringValidation数据校验之约束注解与分组校验方式

《SpringValidation数据校验之约束注解与分组校验方式》本文将深入探讨SpringValidation的核心功能,帮助开发者掌握约束注解的使用技巧和分组校验的高级应用,从而构建更加健壮和可... 目录引言一、Spring Validation基础架构1.1 jsR-380标准与Spring整合1

MySQL 中查询 VARCHAR 类型 JSON 数据的问题记录

《MySQL中查询VARCHAR类型JSON数据的问题记录》在数据库设计中,有时我们会将JSON数据存储在VARCHAR或TEXT类型字段中,本文将详细介绍如何在MySQL中有效查询存储为V... 目录一、问题背景二、mysql jsON 函数2.1 常用 JSON 函数三、查询示例3.1 基本查询3.2

SpringBatch数据写入实现

《SpringBatch数据写入实现》SpringBatch通过ItemWriter接口及其丰富的实现,提供了强大的数据写入能力,本文主要介绍了SpringBatch数据写入实现,具有一定的参考价值,... 目录python引言一、ItemWriter核心概念二、数据库写入实现三、文件写入实现四、多目标写入

使用Python将JSON,XML和YAML数据写入Excel文件

《使用Python将JSON,XML和YAML数据写入Excel文件》JSON、XML和YAML作为主流结构化数据格式,因其层次化表达能力和跨平台兼容性,已成为系统间数据交换的通用载体,本文将介绍如何... 目录如何使用python写入数据到Excel工作表用Python导入jsON数据到Excel工作表用

Mysql如何将数据按照年月分组的统计

《Mysql如何将数据按照年月分组的统计》:本文主要介绍Mysql如何将数据按照年月分组的统计方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录mysql将数据按照年月分组的统计要的效果方案总结Mysql将数据按照年月分组的统计要的效果方案① 使用 DA

鸿蒙中Axios数据请求的封装和配置方法

《鸿蒙中Axios数据请求的封装和配置方法》:本文主要介绍鸿蒙中Axios数据请求的封装和配置方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1.配置权限 应用级权限和系统级权限2.配置网络请求的代码3.下载在Entry中 下载AxIOS4.封装Htt