zoukankan      html  css  js  c++  java
  • 【译】什么是游戏开发实体系统框架(一)

    什么是游戏开发实体系统框架(一)

    原文链接:http://www.richardlord.net/blog/what-is-an-entity-framework

    原文很长,译文将分开几部分,此为第一部分。


    上周我发布了Ash(一个Actionscript 游戏开发的实体系统框架),后来很多朋友问我说:“什么是实体系统框架(entity system framework)?”。下面是我对此做出的非常长的解答(译者注:确实有些啰嗦。。。):

    实体系统目前已经越来越流行,比如众所周知的Unity、较少人知道的Ember2Xember和我自己的Ash。它们流行的原因很简单:简单的游戏框架、简明的代码模块以及简易的使用。

    这篇文章我将向大家展示,传统的游戏逻辑(game loop)是如何进化为一个实体框架的。由于目前我正在使用Actionscript做开发,所以文章中的实例代码都是用Actionscript的,但是此框架对所有语言都适用。

    关于示例

    贯穿全文,我将会使用一个简单的Asteroids游戏作为示例,Asteroids就像一个简化版的大型游戏,麻雀虽小五脏俱全,包含一套典型的系统:刷新系统、物理系统、AI系统、输入系统、NPC系统。

    游戏主循环(The game loop)

    要了解为什么要用实体系统,首先我们需要明白传统的游戏循环是如何工作的。Asteroids游戏的主循环可能是这样工作的:

    function update( time:Number ):void
    {
      game.update( time );
      spaceship.updateInputs( time );
      for each( var flyingSaucer:FlyingSaucer in flyingSaucers )
      {
        flyingSaucer.updateAI( time );
      }
      spaceship.update( time );
      for each( var flyingSaucer:FlyingSaucer in flyingSaucers )
      {
        flyingSaucer.update( time );
      }
      for each( var asteroid:Asteroid in asteroids )
      {
        asteroid.update( time );
      }
      for each( var bullet:Bullet in bullets )
      {
        bullet.update( time );
      }
      collisionManager.update( time );
      spaceship.render();
      for each( var flyingSaucer:FlyingSaucer in flyingSaucers )
      {
        flyingSaucer.render();
      }
      for each( var asteroid:Asteroid in asteroids )
      {
        asteroid.render();
      }
      for each( var bullet:Bullet in bullets )
      {
        bullet.render();
      }
    }

    这个主循环会以一个特定的周期被循环调用来更新游戏状态,通常是60次/秒或者30次/秒。一般在主循环里我们会处理各种重要的游戏逻辑,比如更新游戏中的各种物件、检测他们之间的碰撞、绘制等等。

    上面的代码是一个非常简单的主循环,因为:

    1. 游戏本身很简单

    2. 游戏只有一种状态

    在过去,我曾经在一个电视游戏的主循环一个函数中,塞进了超过3000行的代码。这让它看起来一点都不优美,一点都不简明。这就是过去我们编写游戏的方式,并且不得不伴其一生。

    实体系统框架起源于一次对游戏主循环重构的尝试。它假设游戏主循环就是一个游戏的核心,并且在现代游戏框架中简化游戏主循环比其他任何事情都重要,比如比将视图和控制分离更重要。

    进化过程(Processes)

    进化第一步,要回想一个模块被调用的过程(think about objects called processes)。这件模块可以被初始化、被更新、被销毁。这样的过程接口看起来可能是这个样子的:

    interface IProcess
    {
      function start():Boolean;
      function update( time:Number ):void;
      function end():void;
    }

    我们可以通过分解不同的过程来简化主循环,比如渲染过程、运动过程、碰撞处理等。然后我们可以创建一个过程管理器(process manager)来管理这些过程。

    class ProcessManager
    {
      private var processes:PrioritisedList;
    
      public function addProcess( process:IProcess, priority:int ):Boolean
      {
        if( process.start() )
        {
          processes.add( process, priority );
          return true;
        }
        return false;
      }
    
      public function update( time:Number ):void
      {
        for each( var process:IProcess in processes )
        {
          process.update( time );
        }
      }
    
      public function removeProcess( process:IProcess ):void
      {
        process.end();
        processes.remove( process );
      }
    }

    这是一个略显简单的过程管理器。这里要重点强调的是,我们必须保证各个过程的调用是按正确的顺序进行的(由add方法中的priority参数决定),并且我们必须处理一个过程(process)被从update循环中移除的情况。这样的话,你可能会想到,如果我们的游戏主循环被分解为多个子过程,那么过程管理器的update方法也就等价于过去的游戏主循环,子过程的集合也就变成了游戏的核心(the core of the game)。

    绘制过程(The render process)

    让我们以绘制过程为例。我们可以把过去游戏主循环中有关绘制的代码拉取出来,放进一个单独的过程,看起来像这样:

    class RenderProcess implements IProcess
    {
      public function start() : Boolean
      {
        // initialise render system
        return true;
      }
    
      public function update( time:Number ):void
      {
        spaceship.render();
        for each( var flyingSaucer:FlyingSaucer in flyingSaucers )
        {
          flyingSaucer.render();
        }
        for each( var asteroid:Asteroid in asteroids )
        {
          asteroid.render();
        }
        for each( var bullet:Bullet in bullets )
        {
          bullet.render();
        }
      }
      
      public function end() : void
      {
        // clean-up render system
      }
    }

    使用接口

    但是这并不是特别有效。我们仍然需要手动处理各种不同类型的类的刷新。如果我们有个所有可刷新类通用的接口,代码将会更一步的简化。

    interface IRenderable
    {
      function render();
    }
    class RenderProcess implements IProcess
    {
      private var targets:Vector.<IRenderable>;
    
      public function start() : Boolean
      {
        // initialise render system
        return true;
      }
    
      public function update( time:Number ):void
      {
        for each( var target:IRenderable in targets )
        {
          target.render();
        }
      }
      
      public function end() : void
      {
        // clean-up render system
      }
    }

    然后我们的spaceship类的部分代码将会变成这样:

    class Spaceship implements IRenderable
    {
      public var view:DisplayObject;
      public var position:Point;
      public var rotation:Number;
    
      public function render():void
      {
        view.x = position.x;
        view.y = position.y;
        view.rotation = rotation;
      }
    }

    这里的代码是以2D游戏为例,但是3D游戏的原理是一样的。我们需要绘制图片,进而需要绘制图片所需要的位置和旋转信息,然后,render方法处理刷新的实现。

    To be continued...


    以上是此篇译文的第一部分,时间关系先写到这里,下篇继续,敬请期待。

  • 相关阅读:
    系统生命周期
    系统分析师教程——目录
    企业信息系统——SCM
    企业信息系统——CRM
    .NET 解决方案 核心库整理
    .NET 人工智能相关资料整理
    Task
    正则表达式摘录
    记一次IOS对 JS的支持问题
    JavaScript回顾一下js的基础知识,以及学习一下在项目中了解到的新知识
  • 原文地址:https://www.cnblogs.com/gaven/p/3865173.html
Copyright © 2011-2022 走看看