我正在尝试实现一个行为树,并且在其组成功能方面苦苦挣扎。 基本上,我需要在下面实现的
这是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | type IBehavior interface { Tick() Status Update() Status } type Behavior struct { Status Status } func (n *Behavior) Tick() Status { fmt.Println("ticking!") if n.Status != RUNNING { n.Initialize() } status := n.Update() if n.Status != RUNNING { n.Terminate(status) } return status } func (n *Behavior) Update() Status { fmt.Println("This update is being called") return n.Status } |
这是嵌入的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | type IBehaviorTree interface { IBehavior } type BehaviorTree struct { Behavior Root IBehavior } func (n *BehaviorTree) Update() Status { fmt.Printf("Tree tick! %#v\ ", n.Root) return n.Root.Tick() } |
使该示例更有意义的一些文件:
1 2 3 4 5 6 7 | type ILeaf interface { IBehavior } type Leaf struct { Behavior } |
和这个:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | type Test struct { Leaf Status Status } func NewTest() *Test { return &Test{} } func (n Test) Update() Status { fmt.Println("Testing!") return SUCCESS } |
这是其用法示例:
1 2 3 4 5 | tree := ai.NewBehaviorTree() test := ai.NewTest() tree.Root = test tree.Tick() |
我期望通过打印以下内容使树正常打勾:
1 2 | ticking! Tree tick! |
但是相反,我得到了:
1 2 | ticking! This update is being called |
有人可以帮我解决这个问题吗?
-
在您的用法示例中,将
test 分配给tree.Root ,但未定义test 。它是什么,它是从哪里来的? - Go绝对没有继承的概念(嵌入不是继承),并且您根本无法在Go中做父母/孩子的事情。重新设计。
- @Adrian添加了该信息。
- @Volker我明白了,但是我之所以在这里是因为我不愿意尝试这种设计代码的方式,所以我一直试图找到那种设计。如果您能提供更多帮助,而不仅仅是说"重新设计",那将是很有启发的,这根本没有帮助。
-
没有继承,但是组合确实允许父/子关系;不知道@Volker在这里。无论如何,在您的用法示例中,它仍然没有显示如何创建分配给
tree.Root 的变量test 。应该是tree.Root = n1 吗? - @FelipeRocha这是一个很好的问题,不要介意downvotes。由于某种原因,该网站上的golang社区会大胆地拒绝问题,无论其有效性如何。也许尝试将您的问题简化为单个函数中的独立示例(例如,不需要多个文件)。
-
@Adrian哦!抱歉!错别字!
n1 应该读取test 。不好解决! - 另请参阅stackoverflow.com/questions/29390736/和stackoverflow.com/questions/45974464/
- @maerics是的,这不是我第一次遇到选票,这就是为什么我问。抱歉,但是我已经尝试将其提炼为基本内容!没有比这更多的了,这个问题就没有多大意义了。
- 不幸的是,这已经没有多大意义了。该结构非常复杂,难以快速跟踪执行。这是将其精简为MCVE的好处-通常,当您删除一些不必要的复杂性时,问题便会暴露出来。
- @FelipeRocha我想您仍然可以简化您的问题,有几行与此问题相关的代码(类型Status,代码清单,未使用的接口方法等)。问题域在这里无关紧要,仅接口,组合和动态调度的细节。
- 抱歉。我在上班,正在等午餐回来。我删除了所有无关的信息以帮助澄清问题
您的问题是
如果希望获得预期的行为,则需要在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | type BehaviorTree struct { Behavior Root IBehavior } func (n *BehaviorTree) Tick() Status { n.Update() // TODO: do you want this status or the Root.Tick() status? return n.Root.Tick() } func (n *BehaviorTree) Update() Status { fmt.Printf("Tree tick! %#v\ ", n.Root) return nil } |
- 有趣。 我试图避免重新定义该行为...真的没有办法解决吗?
- 您可以嵌入定义方法的接口并调用它。 但是,没有,除非您有循环引用(即,子代有一个句柄),否则无法调用"子代"对象的方法,然后让该"子代"对象调用"父代"对象的方法。 父级)。 从嵌入的角度来看,一般的解决方案是具有一个定义外部方法的结构,并且嵌入(或更常见的情况)只是具有一个接口字段,用于保存要动态定义的方法。
正如沃尔克所说
Go has absolutely no notion of inheritance (embedding is not inheritance) and you simply cannot do parent/child stuff in Go. Redesign
据我所知,您想要的是一个使用接口多次执行相同任务的函数。
1 2 3 4 5 6 7 | func Tick(n IBehavior) Status { fmt.Println("ticking!") if n.Status != RUNNING { n.Initialize() } status := n.Update() if n.Status != RUNNING { n.Terminate(status) } return status } |
当然,初始化必须在接口中。