16

概述

在我的(Android)Java 游戏中,我有一个名为resources的类。顾名思义,这个类拥有游戏的资源。我所有的 OpenGL 对象(精灵)都是在这里创建的

它看起来像下面这样(显然,与实际项目中出现的版本相比,这是一个简化版本):

public class Resources {

    Hero hero;
    Enemy enemy;
    MenuButtons mainMenuButtons;
    Background background;
    Scene mainMenu;

    public void createObjects(){

        hero = new Hero();
        enemy = new Enemy();
        mainMenuButtons = new MenuButtons();
        background = new Background();
        mainMenu = new Scene(this);

    }
}

所以,在我的mainMenu场景中,我需要访问我的对象,所以我们可能会看到如下内容:

public class mainMenu implements Scene {

    Resources resources;

    public mainMenu(Resources resources){

        this.resources = resources;

    }

    @Override
    public void render(){

        resources.background.draw();
        resources.hero.draw();
        resources.enemy.draw();
        mainMenuButtons.draw();           

    }

    @Override
    public void updateLogic(){

        resources.hero.move();
        resources.enemy.move();
        resources.mainMenubuttons.animate();
    }

}

现在,上述方法只是访问资源中的对象 及其方法的一种方法。但这真的违反了得墨忒耳法则吗? 如果不是,为什么不呢?如果是这样,以不违反 LOD 的方式访问这些对象的最佳方法是什么?

访问者?

一种选择(我已经排除了 TBH - 见下文)是将访问器方法放入我的资源类中。这样我就可以做类似的事情:

resources.drawBackround();

我有很多对象,并且每个对象的每个方法/变量都需要一个访问器。不太实用,似乎我正在编写大量额外的代码,最重要的是,只要资源类充满了这些访问器,它就会变得荒谬。因此,我不会走这条路。

将对象传入场景的构造函数

当然,我也可以这样做:

    hero = new Hero();
    enemy = new Enemy();
    mainMenuButtons = new MenuButtons();
    background = new Background();
    mainMenu = new Scene(hero, enemy, mainMenuButtons, background);

所以我可以简单地这样做:

    background.draw(); //etc....

这对于简单场景(例如不需要大量对象的菜单系统)是可行的,但对于主游戏,它可能很快就会变得一团糟,因为我必须将对大约 30 多个对象的引用传递给构造函数确实听起来不太对......

因此,如果有人能指出最好的方法以及原因,我将不胜感激。

4

7 回答 7

4

因此,如果有人能指出最好的方法以及原因,我将不胜感激。

在我看来,最好的方法是保留 Resources 类,将所有对象设为私有以不违反法律并写入访问器(但不是像你已经排除的每个对象)。

我有很多对象,并且每个对象的每个方法/变量都需要一个访问器。不太实用,似乎我正在编写大量额外的代码,最重要的是,只要资源类充满了这些访问器,它就会变得荒谬。因此,我不会走这条路。

我假设许多对象属于同一类。因此,您不必为每个对象都创建一个访问器,这会真正破坏类。在一个游戏中,你通常有一个英雄、一个或多个敌人和许多精灵。

public class Resources {

    private Hero hero;
    private Enemy enemy;
    private MenuButtons mainMenuButtons;
    private Background background;
    private Scene mainMenu;

    public void createObjects(){

        hero = new Hero();
        enemy = new Enemy();
        mainMenuButtons = new MenuButtons();
        background = new Background();
        mainMenu = new Scene(this);

    }

    public Hero getBackground() {

        return background;
    }

    public Hero getHero() {

        return hero;
    }

    public List<Enemy> getEnemies() {

        ArrayList<Enemy> list = new ArrayList<Enemy>();
        list.add(enemy);
        list.add(next_enemy);
        return list;
    }

    public List<Sprite> getSprites() {

        ArrayList<Sprite> list = new ArrayList<Sprite>();
        list.addAll(enemy.getActiveSprites());
        return list;
    }

}

如果 Hero 和 Enemy 派生自同一个类,您也可以创建一个 getActor() 方法,而不是 getHero() 和 getEnemy()。getSprites() 方法只是它的外观示例。

如果该解决方案对您不起作用,我还有另一个建议。

让 Resources 类做一些工作。

public class ResourceManager {

    private Hero hero;
    private Enemy enemy;
    private MenuButtons mainMenuButtons;
    private Background background;
    private Scene mainMenu;

    public void createObjects(){

        hero = new Hero();
        enemy = new Enemy();
        mainMenuButtons = new MenuButtons();
        background = new Background();
        mainMenu = new Scene(this);

    }

    public void render(Scene scene) {

        this.background.draw();
        if (scene != mainMenu) {

            this.hero.draw();
            this.enemy.draw();
        }
        this.mainMenuButtons.draw();           

    }

    public void updateLogic(Scene scene){

        this.hero.move();
        this.enemy.move();
        this.mainMenubuttons.animate();
    }

}

mainMenu 然后直接调用 ResourceManager 类中的逻辑方法。

public class mainMenu implements Scene {

    ResourceManager resourceManager;

    public mainMenu(ResourceManager resourceManager){

        this.resourceManager = resourceManager;
    }

    @Override
    public void render(){

        resourceManager.render(this);
    }

    @Override
    public void updateLogic(){

        resourceManager.updateLogic(this);
    }

}

我希望我的建议能帮助你弄清楚如何继续你的项目。

于 2015-06-16T16:01:26.013 回答
1

您可以使用依赖注入并消除您的 Resources 类。然后你可以在你的领域调用函数,并且不会违反得墨忒耳法则。

下面是一个使用构造函数注入的例子:

public class MainMenu implements Scene {

   Background background;
   Hero hero;  
   Enemy enemy; 
   MenuButtons buttons

    public mainMenu(Background background, Hero hero,  Enemy enemy, MenuButtons buttons){

       this.background = background;
       this.hero = hero;
       this.enemy = enemy;
       this.buttons = buttons;   
    }

    @Override
    public void render(){

        this.background.draw();
        this.hero.draw();
        this.enemy.draw();
        this.mainMenuButtons.draw();           

    }

    @Override
    public void updateLogic(){

        this.hero.move();
        this.enemy.move();
        this.mainMenubuttons.animate();
    }

}

使用依赖注入,您可以将实例传递给构造函数和函数,而不是在类中“更新”它们。不过,您需要在某个地方管理您的实例,并且有很多库可以为您做到这一点。Dagger 在 Android 上很受欢迎:http: //square.github.io/dagger/

于 2015-06-12T17:55:08.530 回答
1

传递列表的想法并不是一个糟糕的第一步,但这还不够。游戏开发者有一个(有些争议的)结构概念,称为“场景图”,可以帮助他们跟踪他们的资源(除其他外)。https://en.wikipedia.org/?title=Scene_graph

这是一个相当复杂的概念,但你迟早需要了解它。gamedev.stackexchange.com 上有很多很好的建议,所以我建议你看看那里。

这是关于这个主题的一个不错的 YouTube 视频教程。https://www.youtube.com/watch?v=ktz9AlMSEoA

于 2015-06-17T02:11:12.437 回答
1

您可以创建一个Drawer类来处理所有对象的绘制。您的场景对象只需要提供Drawer我假设的对象Drawable

public class Drawer {
   public void drawObjects(Drawable... objects) {
      for(Drawable drawable : objects) {
         drawable.draw();
      }
   }
}

然后使用它Scene来绘制这些对象。

public class mainMenu implements Scene {
   Resources resources;
   Drawer drawer;

   ...

   public void render() {
      drawer.drawObjects(resources.background, 
                         resources.hero, 
                         resources.enemy, 
                         resources.mainMenuButtons);
   }

   ...
}

使用 的类似策略Updater可以应用于其他方法。如果您的updateLogic()方法像看起来那样简单地调用,那么您绝对可以做同样的事情,让所有这些对象都从Updateable接口继承。

public interface Updateable {
   void update();
}

Hero's 和Enemy'update()方法可以简单地调用它们的move()方法,而MenuButtons'supdate()可以委托给animate()等。

显然,如果您愿意,您可以使用某种集合而不是可变参数作为Drawer's的参数drawObjects()。我只是喜欢可变参数带来的流畅性,因为您不必创建集合。

有关使代码符合得墨忒耳法则的其他提示,请查看这篇文章:得墨忒耳法则及其使用方法

于 2015-06-17T20:12:48.627 回答
1

改变这个:

 public void render(){

        resources.background.draw();
        resources.hero.draw();
        resources.enemy.draw();
        mainMenuButtons.draw();           

    }
 @Override
    public void updateLogic(){

        resources.hero.move();
        resources.enemy.move();
        resources.mainMenubuttons.animate();
    }

有了这个:

public void render(){
            resources.render();
    } 

@Override
    public void updateLogic(){
        resources.update();
} 

ResourceManager 不必知道 Resources 里面有什么。它可能是一个敌人或十个敌人,它并不关心 ResourceManager。

因此,在“资源”中,您可以执行以下操作:

  public void update(){
        hero.update();// Cause hero may, or may not move, he makes the choice
        enemy.update();//...
        mainMenubuttons.update();//.
    }
  public void render(){
  ...
  }

更有什者!您可以使用接口更改“资源”实现,您将为接口而不是实现编程,这很酷!这样,您可以拥有一个用于游戏内的“资源”和另一个用于菜单的“资源”,它们将以相同的方式使用:仅在运行时更改您将在菜单或游戏中的具体资源!

无论如何,并不总是需要填充得墨忒耳。

于 2015-06-18T13:18:44.447 回答
1

我喜欢 ResourceManager 的概念。
但是 ResourceManager 应该负责加载资源、缓存和释放它们。
渲染绝对是渲染对象的一种方法。

因此 Scence - render 方法可以在实例化 Renderer 后将渲染委托给
它,并使用 Drawables 提供它,因为 Renderer 不
渲染资源,而是渲染可渲染对象。

说:

class MainMenu implements Scene {
    Renderer sceneRenderer = new Renderer();
    AnimatedRenderer animatedRenderer = new AnimatedRenderer();
    ResourceManager resourceManager = ResourceManager.getInstance();
    List<Resource> resources;
    List<Drawable> renderedObjects;
    GameObjectController gameObjectController;


    void initializeScene() {
          resources = resourceManager.getResources();
          renderedObjects = resourcesAsRenderables();
          sceneRenderer.setDrawables(renderedObjects);
    }

    List<Drawable> resourcesAsRenderables() {
      // if resources are not directly renderable, do decoration etc
      // and return a List of Drawable
    }

    @Override
    public void render(){
         sceneRenderer.render();
    }
    @Override
    public void updateLogic(){
       moveGameObjects();
       doAnimations();

    }
    protected void moveGameObjects() {
        gameObjectController.moveAllObjects(this, resources);
    }
    protected void doAnimations() {
        animatedRenderer.render(resources);
    }


    class ResourceManager {
       private static ResourceManager instance = null;
       List<Resource> resources;
       public ResourceManager getInstance() {
          if(instance == null) {
             instance = new ResourceManager();
             instance.loadResources();
          }
          return instance;
       }
       private void loadResources() { 
           resources = new LinkedList<Resource>();
           resources.add(new Hero());
           ....
       }
       public List<Resource> getResources() {
          return resources;
       }
    }

这清楚地将场景生命周期中执行的任务的逻辑和职责分开。资源管理器负责检索资源,因为它们可能需要很长的加载时间,所以在内存不足的情况下缓存或释放等操作会对客户端隐藏详细信息。负责显示对象的渲染器和负责移动对象的控制器。控制器本身可以实现键盘事件的处理程序,但这不是必须对场景透明的东西。渲染器可以交换背景或缩小或缩放或设置照明效果,但场景仅调用其渲染方法。动画渲染器负责启动、渲染和停止动画。

于 2015-06-18T21:30:07.453 回答
0

可以看出,您的资源不需要重新创建,而是使用一些无法重新加载的资源(可能是图像)。

您应该在 Resource 类中共享图像对象,并在 Scene 类中创建对象,在实体的构造函数上,您可以获得预加载的共享资源。

于 2015-06-18T21:33:54.430 回答