如何制作没有OOP的游戏?[关闭]


10

我目前正在研究游戏开发和练习制作游戏。

我在游戏中经常使用OOP。例如,发射的每枚导弹都是一个Missile对象的实例,并添加到Missile对象列表中。游戏中的每个坦克都是一个Tank物体。等等。

该程序的整个设计都基于此。例如,拥有一个Missile物体列表可以使我每一帧移动导弹,牵引导弹等。而Tank每个坦克都有一个对象实例可以让我检查每个坦克是否与某些物体发生碰撞等。

我很难想象如何用非OO语言编写游戏(比《吃豆人》更复杂)。(当然也要尊重非OO程序员)。不仅要花费多长时间,而且主要要以这种方式设计游戏。

我无法想象不使用面向对象的程序设计游戏,因为我对如何设计游戏程序的全部理解是基于OOP的。

我想问一问:今天,有没有像我上面所描述的那样没有使用OOP编程的游戏?是否有没有将OOP用作开发过程中主要因素的“专业”游戏?

如果是这样,您能否给我一个想法,例如,在没有OOP的情况下,如何实现坦克与N枚导弹之间的碰撞检测?


6
这是一个哲学问题吗?即使您不将战车称为“对象”,您也可能会想要“实体”,“角色”,“代理”,“结构”或同一概念的其他名称,这些名称是由旋转的长方体组成的属性和行为,其炮塔可以发射东西,称为坦克。编程语言将有不同的方式来形式化这个想法,但最终,它将成为一个坦克。
Anko 2014年

许多游戏使用此组件描述的基于组件的系统:gamedev.stackexchange.com/a/31491/9366
John McDonald

这既非常广泛(您可以通过缩小范围来解决),也并非真正针对游戏开发(因为没有OO技术的软件构建并不是游戏开发人员比其他任何软件开发人员都能提供的更好的答案),恐怕这使这里成为话题。

它可能适用于StackOverflow,或者您可以查看帮助中心,以找到特定于游戏开发特定站点的选择(例如GDNet),这些站点将允许进行此类广泛的,面向讨论的主题。祝好运!

Answers:


16

我无法想象不使用面向对象的程序设计游戏,因为我对如何设计游戏程序的全部理解是基于OOP的。

那么尝试以非OO风格编写一些程序对您可能会有所帮助。即使您发现这对您而言并不实用,您也可能会在以后的学习中学到很多东西。

OO风格非常适合游戏,因为游戏几乎总是与有状态对象有关。激光束撞击机器人,并且机器人的状态发生变化,同时其身份保持不变。

但是,可以按照功能样式对游戏进行编程。在功能样式中,状态本身不会改变。对象是不可变的。您提出的问题不是改变对象,而是我改变了它,宇宙将如何变化?然后产生具有更改属性的整个新Universe。当然,您可以重用许多先前存在的Universe,因为它是不可变的

在函数式编程中,每个函数必须仅根据传入的信息来计算其返回值。没有从“全局状态”中读取任何信息。

如果这样做,则必须解决的基本问题是每次更新都是非破坏性的。当激光照射机器人时,您不会更改机器人的状态。最终,您将计算出一个与旧Universe完全相同的全新Universe,不同之处在于机器人的状态有所不同。如果您需要那个旧的宇宙,它仍然存在,没有变化。

这一系列博客文章对以功能风格编写游戏有更多的想法:

http://prog21.dadgum.com/23.html

本文专门解决您的“炮弹击中坦克”的问题:

http://prog21.dadgum.com/189.html

实际上,只需阅读整个博客即可。那里好东西,文章短。


12

通过将所有类替换为结构并将所有成员函数转换为独立函数(将对象this作为参数),可以将任何面向对象的程序重构为过程程序。

所以

 missile.setVelocity(100);

变成

 setMissileVelocity(missile, 100);

或当该功能不重要时,您只需

 missile.velocity = 100;

面向对象编程和过程编程之间的主要区别是如何处理数据。在OOP中,数据是智能的。它自我管理和操纵。但是在过程编程中,数据是愚蠢的。它本身不会做任何事情,需要从外部进行操作。

当您甚至认为结构过于面向对象时,您可以将一个结构阵列替换为多个阵列,对于所有可能成为导弹变量的阵列,都可以替换一个。所以

struct Missile {
     int x;
     int y;
     int velocity;
}

Missile missiles[256];

变成

int missileX[256];
int missileY[256];
int missileVelocities[256];

在这种设计中,在同一枚导弹上执行涉及多个属性的操作的函数现在将采用数组索引,而不是对结构的引用。它的实现如下所示:

function updateMissilePosition(int index) {
     missileX[index] += missileVelocity[index];
}

1
而是missile对象的实例。在非OOP中,没有实例,对吗?如果是这样,如何设置setMissileVelocity(missile,100)?
user3150201 2014年

1
@ user3150201您并不完全正确。大多数非OOP语言(我会争论任何适合严肃游戏开发的语言)都支持结构。结构有点像一个类,只是它除了公共变量之外什么都不包含。因此,这将允许您创建一个类型Missile,这是一种结构与几个领域,如xyanglevelocity
Philipp 2014年

@ user3150201更新了答案,其中包含有关如何在没有结构的情况下执行此操作的部分。
菲利普2014年

很好的答案Philipp,但是,我不明白为什么不愿意编写面向对象的代码。很难阅读非OOP语言,并且可能会麻烦很多。该代码可以立即变成一团糟。
Zhafur 2014年

2
@Zhafur您知道您的声明是一个巨大的诱饵,对吗?
菲利普2014年

6

我这样做如下:

  • 所有OOP类/方法都可以访问this。为了this在非OO方法中使用,只需将this应该作为第一个参数的任何实例(请参阅下一点)传递。
  • 现在,作为实例,您可以将structs 传递给函数this,但是我发现为多产对象(例如实体或粒子)实现良好的缓存性能的最佳方法是简单地将单个索引传递到多个基元数组中或小号struct。因此,此索引用于原始类的每个单独的数据成员。例如,如果您有

...

class Entity //let's say you had 100 instances of this
{
   int a;
   char b;
   function foo() 
   {
      .../*can access 'this' herein*/
   }
}

您可以将其替换为

int a[100];
char b[100];
function foo(int index);

这样您现在就可以将索引传递到函数中,以获取通常的状态this

请记住,您可能希望使用上述原始数组struct s 数组,这取决于如何以最佳方式对数据进行交织以获得良好的缓存局部性(参考局部性)。当然,良好的缓存性能还不仅仅依赖于此(尤其是您要在哪种语言/平台上编写代码),但是即使在基于VM的动态分配语言(如Java)中,大型线性原语数组也往往会显示出比对象实例更好的性能特征。这样做的主要原因是对象是通过引用访问的,这意味着您要在内存中跳转来访问数据,与从大型数组中连续访问基元相比,效率低下。

有关将实体等作为结构或基元数组构建的更多信息,请参见Mick West的Evolve your Hierarchy


0

除了现有的答案外,您可能还想知道如何使用过程语言进行多态。

有两种方法:

存储类型

在这种情况下,该结构具有类型标识符的字段(可能是枚举),switch当需要执行特定于类型的操作时,将使用语句检查该字段。

另一种方法是:

存储函数指针

您没有提到您使用哪种编程语言,但是在各种语言中,它们也称为回调,委托,事件或高阶函数或简称为函数对象。

在这种情况下,您将不会存储类型,而是会存储指向执行特定操作的函数的指针/引用。当需要执行特定于类型的操作时,只需调用此函数即可。这个看起来很像普通的虚拟方法。

通过允许独立设置每个功能,您可以获得自由的策略模式


关于最后一段与碰撞检测。我认为您可能拥有多种坦克和多种导弹,每种组合在碰撞时可能会有不同的结果。如果这是您要寻找的东西,那么您将遇到甚至OOP语言都无法解决的问题:可以具有多个this不同类型参数的多重调度和多重方法。对于此问题,再次有两种选择:

  • 坦克和导弹的每种组合的嵌套开关。
  • 二维调度数组,其中包含指向坦克和导弹每种组合的功能的指针。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.