unity3d 收费模式_unity3d中的访客模式

unity3d 收费模式

抽象 (Abstract)

Game development as a discipline is challenging on its own compared to traditional software development. The ability to solve performance-related, architectural and other challenges is often the key to success in the field. Because of these and many other factors, it is often a good practice to follow certain software principles and common practices to enforce the maintainability and extensibility of a product. Software design patterns come in handy in such scenarios. They can make the code base more maintainable, extensible, and contribute significantly to the overall lifetime of a product. One of the use cases to be examined is the application of the visitor pattern (Gamma, et al. 1995) in the context of game development. Possible challenges and benefits of the pattern implementation are outlined respectively in this writing.

与传统软件开发相比,将游戏开发作为一门学科本身具有挑战性。 解决与性能相关,体系结构和其他挑战的能力通常是该领域成功的关键。 由于这些因素和许多其他因素,遵循某些软件原理和通用惯例来强制执行产品的可维护性和可扩展性通常是一个好习惯。 在这种情况下,软件设计模式会派上用场。 它们可以使代码库更具可维护性,可扩展性,并为产品的整个生命周期做出重大贡献。 要研究的用例之一是访客模式在游戏开发中的应用(Gamma等,1995)。 本文分别概述了模式实现的可能挑战和好处。

前言 (Preface)

The scenario used is completely fictional and resembles an isolated use case. The degree of design completeness in the classes is not meant to be a subject for evaluation of any kind and is simplified to emphasize the point of the paper and not to clutter the code with proper, secure programming construction. Edge case checks are omitted along with many other optimization-related techniques to illustrate the point of the topic.

使用的场景完全是虚构的,类似于孤立的用例。 这些类中的设计完整性程度并不意味着要进行任何形式的评估,而应简化以强调本文的要点,并且不要以正确,安全的编程结构使代码混乱。 省略了边缘案例检查以及许多其他与优化相关的技术,以说明该主题的重点。

This writing is a purely personal interpretation and vision of the matter based on the challenges I have encountered while coding and trying to accept Unity3d component-based architecture.

基于我在编码和尝试接受基于Unity3d组件的体系结构时遇到的挑战,本文只是对此问题的纯个人解释和构想。

Full source code can be found in my GitHub repo: https://github.com/george-vasilchenko/unity-visitor

完整的源代码可以在我的GitHub存储库中找到: https//github.com/george-vasilchenko/unity-visitor

问题 (The Problem)

To examine the usefulness and applicability of the visitor pattern within game logic, we sketch the following scenario. Consider the following diagram, to begin with:

为了检查游戏逻辑中访客模式的有用性和适用性,我们绘制了以下场景。 首先考虑下图:

Image for post

The setup is quite straightforward. We have an abstract base class with a few common members for a character. See the code for the CharacterBase class:

设置非常简单。 我们有一个抽象基类,该基类有几个常见的角色成员。 请参见CharacterBase类的代码:

Each of the child classes implements the required members accordingly. The characters have a default set of stats assigned during the initialization. The starting level for each character is 1. To perform an attack, according to my fictional scenario, it is important to calculate damage for the attack. There are hundreds of ways this can be implemented of course but, in my case, it will be a calculation based on the weighted distribution of the stats of a particular character. Here is the implementation of the Attack and CalculateDamage methods from the Archer class:

每个子类都相应地实现所需的成员。 这些字符在初始化期间分配了一组默认的统计信息。 每个角色的起始级别为1。根据我的虚拟场景,要进行攻击,计算攻击的伤害很重要。 当然,有数百种方法可以实现,但就我而言,这将基于特定角色的统计信息的加权分布进行计算。 这是Archer类中AttackCalculateDamage方法的实现:

The idea is that each character, based on its type (Archer, Paladin, etc) has its leading trait. Archer, for instance, has agility as his main trait. In contrast, a magician uses intelligence as the main characteristics. This trend is implemented in the CalculateDamage methods for each of the characters.

这个想法是,每个角色根据其类型(Archer,Paladin等)都有其主导特征。 例如,弓箭手以敏捷为主要特征。 相反,魔术师则以智力为主要特征。 此趋势在CalculateDamage方法中为每个字符实现。

The problem here, however, is that we keep this distribution information in each character class. At the first sight, this seems reasonable, but if we will implement a more advanced way of resolving these numbers, we will need to introduce dependencies in each character class and the code will become less flexible.

但是,这里的问题是,我们在每个字符类中保留了此分发信息。 乍一看,这似乎是合理的,但是如果我们将采用更高级的方式来解析这些数字,则需要在每个字符类中引入依赖关系,并且代码的灵活性将降低。

Let’s consider another scenario for the characters. The ability to level-up in the game can be a desired feature. Usually, this kind of functionality is essential. The ability to increase the level, in my scenario, impacts the damage amount that a character can produce. With each level, normally, a character would deal more and more damage, this is also the case in my fictional scenario. Consider the following implementation of the IncreaseLevel method:

让我们考虑字符的另一种情况。 在游戏中升级的能力可能是理想的功能。 通常,这种功能是必不可少的。 在我的情况下,提高等级的能力会影响角色可能产生的伤害量。 通常,在每个级别上,角色都会造成越来越多的伤害,在我的虚拟场景中也是如此。 考虑下面的GrowthLevel方法的实现:

We increment the level member variable each time the method is called. For us to come up with a proper stat increase for a given level, we should increment each stats property each time the level is increased. Using the CreateFromOtherWithDeltas method of structure CharacterStats, gives us the updated stats instance that is built on top of the existing one and with the delta for each property. The implementation of the CharacterStats structure:

每次调用该方法时,我们都会增加级别成员变量。 为了让我们针对给定的级别提出适当的统计信息增加,每次级别增加时,我们都应该增加每个统计信息属性。 使用CharacterStats结构的CreateFromOtherWithDeltas方法,可以为我们提供更新的stats实例,该实例建立在现有实例的基础上,并具有每个属性的增量。 CharacterStats结构的实现:

To sum it up, we calculate stats deltas based on the level of the character and we update the stats reference with a new object. This, in turn, will be used in the CalculateDamage method to come up with the final damage amount.

综上所述,我们根据字符的级别计算统计数据增量,并使用新对象更新统计数据引用。 反过来,它将在CalculateDamage方法中使用,以得出最终的损坏量。

The direction where this implementation is heading is obvious. We keep extending the class with the implementation of every new level, besides, in a more advanced scenario, the resolution of the stats values would be delegated to an external class which in turn would become a dependency for the character class. And this repeats for each new character we desire to add in the game. This will bring maintenance problems, high complexity of the character classes, and tight coupling between possible external modules that provide numeric data for this logic.

该实现的方向很明显。 我们将通过每个新级别的实现来扩展类,此外,在更高级的情况下,统计值的解析将委派给外部类,而外部类又将成为字符类的依赖项。 对于我们希望添加到游戏中的每个新角色,此操作都会重复。 这将带来维护问题,字符类的高度复杂性以及可能为此逻辑提供数字数据的外部模块之间的紧密耦合。

有希望的解决方案 (The Promising Solution)

The Visitor pattern, according to Gamma, et al. (1995), is intended to

访客模式,根据Gamma等人的说法。 (1995),旨在

“represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.”

“表示要在对象结构的元素上执行的操作。 访问者可让您定义新操作,而无需更改其所操作元素的类。”

This definition strikes as a possible solution to achieve the needed maintainability in the classes from the fictional scenario. Let’s try to implement such a pattern and analyze whether it will enhance the quality and design of the program.

该定义是从虚拟场景中实现类中所需的可维护性的可能解决方案。 让我们尝试实现这种模式,并分析它是否会提高程序的质量和设计。

I have gone ahead and refactored the code to fit the pattern. A few more classes have emerged. Consider the new diagram:

我已经继续并重构了代码以适应模式。 出现了更多的类。 考虑新图:

Image for post

Types like IDamageable and Enemy are just supplementary components to make the scenario more or less complete, they are used in the Attack method of each character. I have introduced a few more types, specifically ICharacterVisitor<T> and two implementations of this interface: StatsDistributionVisitor and StatsIncreasePerLevelVisitor. The first one was designed to take responsibility for the distribution logic that is used by each character in a specific way, and the latter is meant to tackle the level-based stats problem. I moved all related code from the character classes into the visitor classes respectively. StatsDistribution structure was introduced to encapsulate the concept of stats deltas. The code for the distribution visitor:

诸如IDamageableEnemy之类的类型只是使场景或多或少完整的补充组件,它们在每个角色的Attack方法中使用。 我介绍了更多类型,特别是ICharacterVisitor <T>和此接口的两个实现: StatsDistributionVisitorStatsIncreasePerLevelVisitor 。 第一个设计用于负责每个角色以特定方式使用的分配逻辑,而后者则旨在解决基于级别的统计问题。 我将所有相关代码分别从字符类移到了访客类。 引入了StatsDistribution结构来封装stats deltas概念。 分发访问者的代码:

Here we can see that values that were provided in the CalculateDamage method are moved into this class and it is way easier to maintain this structure. The stats distribution logic is now in one place and can be easily extended by any external source. For instance, we can use a ScriptableObject (Unity, 2018) instance to manipulate these values from the Editor, which would be a good solution for level designers and testers.

在这里,我们可以看到CalculateDamage方法中提供的值已移入此类,并且可以更轻松地维护此结构。 统计信息分配逻辑现在处于一个位置,可以通过任何外部源轻松扩展。 例如,我们可以使用ScriptableObject (Unity,2018)实例从编辑器中操纵这些值,这对于关卡设计人员和测试人员而言将是一个很好的解决方案。

The second visitor class is responsible for determining a boost for the stats for each character based on the level. Here is the implementation in code:

第二个访问者类负责根据级别确定每个角色的属性提升。 这是代码中的实现:

The level stats logic was moved from the character classes into the visitor class. Similarly, this class can also take advantage of additional dependencies. This approach takes the burden concerning level-based upgrades away from the character classes.

级别统计逻辑已从角色类移至访客类。 同样,此类也可以利用其他依赖项。 这种方法减轻了基于角色升级的负担。

Let’s take a look at the actual use of the visitor classes by, say, the Mage class:

让我们看一下Mage类对访客类的实际使用:

The size of the class is reduced. The amount of level related logic will no longer affect this class because the logic is placed in the visitor class. The distribution logic is also substituted with the call to the Visit method of the distribution visitor.

班级人数减少。 与级别相关的逻辑的数量将不再影响此类,因为该逻辑已放置在visiter类中。 分发逻辑也用对分发访问者的Visit方法的调用代替。

测试中 (Testing)

To try out both implementations, I have set up a few simple editor-based unit tests. The tests take care of creating instances of the characters. Each character gets its enemy to attack. To not overengineer the scenario and to not sway from the topic, the assessment logic simply examines the change in the health of each enemy. Additionally, the information is logged into the console to illustrate the difference. The tests are identical concerning the implementation before the visitor pattern and after, the only difference is the character class implementations. Here is the sample code for one of the tests:

为了尝试这两种实现,我建立了一些简单的基于编辑器的单元测试。 测试负责创建字符实例。 每个角色都会受到敌人的攻击。 为了不对场景进行过度设计并且不偏离主题,评估逻辑仅检查每个敌人的健康状况变化。 此外,该信息将记录到控制台中以说明差异。 关于访问者模式之前和之后的实现,测试是相同的,唯一的区别是字符类实现。 以下是其中一项测试的示例代码:

The results of the tests are identical. Here is the result for the implementation without the visitor pattern:

测试结果是相同的。 这是没有访问者模式的实现结果:

Image for post

Here is the result after the visitor pattern was implemented:

这是实现访客模式后的结果:

Image for post

思想 (Thoughts)

Considering the amount of effort it takes to implement game logic in game development, it is essential to find an efficient way to do that. Using software design patterns is considered good practice but the patterns have to be applied with a reason and caution. It is often tempting to over-complicate design and implementation, which, in result, puts the success of a product at risk. Unity3d coding approach follows a component-based design (Component-based software engineering, 2020). It is quite challenging to follow object-oriented principles in such an environment when architecture works against you. However, in certain isolated components, it is possible to tackle problems by following common best practices. The visitor pattern appears to be useful in scenarios when we have to deal with multiple objects of the same structure that have to implement certain operations. Visitor lets us keep related operations together by defining them in one class (Gamma, et al. 1995). This opens additional opportunities to make use of the reach feature set of the engine when we want to influence the data of the visitor classes.

考虑到在游戏开发中实现游戏逻辑所需的工作量,找到一种有效的方法至关重要。 使用软件设计模式被认为是一种很好的做法,但是必须谨慎使用这些模式。 通常,设计和实现过于复杂是很诱人的,这最终使产品的成功受到威胁。 Unity3d编码方法遵循基于组件的设计(基于组件的软件工程,2020)。 当架构不利于您时,在这样的环境中遵循面向对象的原则是非常具有挑战性的。 但是,在某些隔离的组件中,可以通过遵循常见的最佳实践来解决问题。 当我们必须处理必须执行某些操作的具有相同结构的多个对象时,访问者模式在场景中很有用。 访客可以通过将它们定义在一类中来使相关操作保持在一起(Gamma等,1995)。 当我们想影响访问者类的数据时,这为利用引擎的覆盖范围功能集提供了更多机会。

后记 (Afterword)

The implementation of the visitor pattern lacks the so-called common Apply method. This is done intentionally and considered unnecessary by me because the way the instances of the visitor classes are resolved satisfies the overall idea of the pattern. Of course, it is possible to add another level of abstraction in the character classes such as ArcherDamageSystem, ArcherStatsSystem that would in turn use the visitor classes and expose the Apply method, but I believe this is not the point here and ignoring Apply method is acceptable due to the circumstances.

访问者模式的实现缺少所谓的通用Apply方法。 这是有意完成的,我认为这是不必要的,因为解析访问者类的实例的方式满足了该模式的整体思想。 当然,也可以添加在字符类如ArcherDamageSystem,ArcherStatsSystem这反过来将使用访问者类和揭露应用方法另一层抽象的意义,但我相信在这里这并不是问题的关键,而忽略应用方法是可以接受的视情况而定。

翻译自: https://medium.com/the-innovation/visitor-pattern-in-unity3d-6f70f60db75d

unity3d 收费模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值