Cây hành vi ưu tiên


25

Tôi đang cố gắng để có được đầu của mình xung quanh cây hành vi, vì vậy tôi đang bỏ qua một số mã kiểm tra. Một điều tôi đang vật lộn là làm thế nào để ưu tiên một nút hiện đang chạy khi có thứ gì đó có mức độ ưu tiên cao hơn xuất hiện.

Hãy xem xét cây hành vi đơn giản, hư cấu sau đây cho một người lính:

nhập mô tả hình ảnh ở đây

Giả sử rằng một số lượng bọ ve đã đi qua và không có kẻ thù nào ở gần, người lính đang đứng trên bãi cỏ, vì vậy nút Ngồi xuống được chọn để thực hiện:

nhập mô tả hình ảnh ở đây

Bây giờ hành động Ngồi xuống cần có thời gian để thực thi vì có một hình ảnh động để phát, do đó, nó trở lại Runningnhư trạng thái của nó. Một hoặc hai tích tắc trôi qua, hình ảnh động vẫn chạy, nhưng Kẻ thù gần? nút kích hoạt điều kiện. Bây giờ chúng ta cần ưu tiên nút Sit down ASAP để có thể thực thi nút Attack . Lý tưởng nhất là người lính thậm chí sẽ không ngồi xuống - thay vào đó anh ta có thể đảo ngược hướng hoạt hình của mình nếu anh ta chỉ bắt đầu ngồi. Để thêm phần chân thực, nếu anh ta vượt qua một số điểm bùng phát trong hoạt hình, thay vào đó chúng ta có thể chọn để anh ta ngồi xuống và sau đó đứng lại, hoặc có thể khiến anh ta vấp ngã để phản ứng với mối đe dọa.

Cố gắng hết mức có thể, tôi không thể tìm thấy hướng dẫn về cách xử lý tình huống này. Tất cả các tài liệu và video tôi đã tiêu thụ trong vài ngày qua (và nó đã được rất nhiều) dường như xoay quanh vấn đề này. Điều gần nhất mà tôi có thể tìm thấy là khái niệm đặt lại các nút đang chạy, nhưng điều đó không cho các nút như Ngồi xuống một cơ hội để nói "này, tôi chưa kết thúc!"

Tôi nghĩ có lẽ định nghĩa một Preempt()hoặc Interrupt()phương thức trên Nodelớp cơ sở của tôi . Các nút khác nhau có thể xử lý nó như thế nào họ thấy phù hợp, nhưng trong trường hợp này, chúng tôi sẽ cố gắng đưa người lính trở lại trên đôi chân của mình càng sớm càng tốt Success. Tôi nghĩ rằng cách tiếp cận này cũng sẽ yêu cầu cơ sở của tôi Nodecó khái niệm về các điều kiện riêng biệt với các hành động khác. Bằng cách đó, động cơ chỉ có thể kiểm tra các điều kiện và, nếu chúng vượt qua, hãy ưu tiên mọi nút hiện đang thực thi trước khi bắt đầu thực hiện các hành động. Nếu sự khác biệt này không được thiết lập, động cơ sẽ cần phải thực thi các nút một cách bừa bãi và do đó có thể kích hoạt một hành động mới trước khi ưu tiên chạy.

Để tham khảo, dưới đây là các lớp cơ sở hiện tại của tôi. Một lần nữa, đây là một đột biến, vì vậy tôi đã cố gắng giữ mọi thứ đơn giản nhất có thể và chỉ thêm sự phức tạp khi tôi cần, và khi tôi hiểu nó, đó là điều tôi đang đấu tranh ngay bây giờ.

public enum ExecuteResult
{
    // node needs more time to run on next tick
    Running,

    // node completed successfully
    Succeeded,

    // node failed to complete
    Failed
}

public abstract class Node<TAgent>
{
    public abstract ExecuteResult Execute(TimeSpan elapsed, TAgent agent, Blackboard blackboard);
}

public abstract class DecoratorNode<TAgent> : Node<TAgent>
{
    private readonly Node<TAgent> child;

    protected DecoratorNode(Node<TAgent> child)
    {
        this.child = child;
    }

    protected Node<TAgent> Child
    {
        get { return this.child; }
    }
}

public abstract class CompositeNode<TAgent> : Node<TAgent>
{
    private readonly Node<TAgent>[] children;

    protected CompositeNode(IEnumerable<Node<TAgent>> children)
    {
        this.children = children.ToArray();
    }

    protected Node<TAgent>[] Children
    {
        get { return this.children; }
    }
}

public abstract class ConditionNode<TAgent> : Node<TAgent>
{
    private readonly bool invert;

    protected ConditionNode()
        : this(false)
    {
    }

    protected ConditionNode(bool invert)
    {
        this.invert = invert;
    }

    public sealed override ExecuteResult Execute(TimeSpan elapsed, TAgent agent, Blackboard blackboard)
    {
        var result = this.CheckCondition(agent, blackboard);

        if (this.invert)
        {
            result = !result;
        }

        return result ? ExecuteResult.Succeeded : ExecuteResult.Failed;
    }

    protected abstract bool CheckCondition(TAgent agent, Blackboard blackboard);
}

public abstract class ActionNode<TAgent> : Node<TAgent>
{
}

Có ai có cái nhìn sâu sắc nào có thể giúp tôi đi đúng hướng không? Là suy nghĩ của tôi theo đúng dòng, hoặc nó ngây thơ như tôi sợ?


Bạn cần xem tài liệu này: chrishecker.com/My_liner_notes_for_spore/ Khăn ở đây anh ta giải thích cách cây được đi, không giống như một máy trạng thái, nhưng từ ROOT ở mỗi tích tắc, đó là mẹo thực sự để phản ứng. BT không cần ngoại lệ hoặc sự kiện. Họ đang tổng hợp các hệ thống nội tại và phản ứng với mọi tình huống nhờ luôn chảy xuống từ gốc. Đó là cách thức phòng ngừa hoạt động, nếu một điều kiện bên ngoài của kiểm tra ưu tiên cao hơn, nó chảy ở đó. (gọi một số Stop()cuộc gọi lại trước khi thoát khỏi các nút hoạt động)
v.oddou 16/2/2015

này aigamedev.com/open/article/popular-behavior-tree-design cũng rất độc đáo chi tiết
v.oddou

Câu trả lời:


6

Tôi thấy mình hỏi cùng một câu hỏi như bạn và tôi đã có cuộc trò chuyện ngắn tuyệt vời trong phần bình luận của trang blog này , nơi tôi được cung cấp một giải pháp khác cho vấn đề.

Điều đầu tiên là sử dụng nút đồng thời. Nút đồng thời là một loại nút tổng hợp đặc biệt. Nó bao gồm chuỗi các kiểm tra tiền điều kiện theo sau là một nút hành động. Nó cập nhật tất cả các nút con ngay cả khi nút hành động của nó ở trạng thái 'đang chạy'. (Không giống như nút chuỗi phải bắt đầu cập nhật từ nút con đang chạy hiện tại.)

Ý tưởng chính là tạo thêm hai trạng thái trả về cho các nút hành động: "hủy" và "hủy".

Thất bại của kiểm tra điều kiện tiên quyết trong nút đồng thời là một cơ chế kích hoạt hủy bỏ nút hành động đang chạy của nó. Nếu nút hành động không yêu cầu logic hủy bỏ chạy dài thì nó sẽ trả về 'đã hủy' ngay lập tức. Mặt khác, nó chuyển sang trạng thái 'hủy' trong đó bạn có thể đặt tất cả logic cần thiết để gián đoạn chính xác hành động.


Xin chào và chào mừng đến với GDSE. Sẽ thật tuyệt, nếu bạn có thể mở ra câu trả lời từ blog đó đến đây và trong liên kết cuối cùng đến blog đó. Liên kết có xu hướng chết, có câu trả lời đầy đủ ở đây, làm cho nó dai dẳng hơn. Câu hỏi hiện có 8 phiếu, vì vậy câu trả lời tốt sẽ rất tuyệt vời.
Katu

Tôi không nghĩ bất cứ điều gì đưa cây hành vi trở lại máy trạng thái hữu hạn là một giải pháp tốt. Cách tiếp cận của bạn đối với tôi giống như bạn cần hình dung tất cả các điều kiện thoát của mỗi tiểu bang. Khi đây thực sự là nhược điểm của FSM! BT có lợi thế là bắt đầu lại từ gốc, điều này tạo ra một FSM được kết nối hoàn toàn, tránh chúng tôi viết các điều kiện thoát một cách rõ ràng.
v.oddou

5

Tôi nghĩ rằng người lính của bạn có thể bị phân rã vào tâm trí và cơ thể (và bất cứ điều gì khác). Sau đó, cơ thể có thể bị phân hủy thành chân và tay. Sau đó, mỗi phần cần cây hành vi riêng và giao diện chung - cho các yêu cầu từ các phần cao hơn hoặc thấp hơn.

Vì vậy, thay vì quản lý vi mô từng hành động, bạn chỉ cần gửi các tin nhắn bắn nhanh như "cơ thể, ngồi xuống một lúc" hoặc "cơ thể, chạy ở đó" và cơ thể sẽ quản lý hoạt hình, chuyển trạng thái, trì hoãn và các nội dung khác cho bạn.

Ngoài ra, cơ thể có thể tự quản lý các hành vi như thế này. Nếu nó không có đơn đặt hàng, nó có thể hỏi "chúng ta có thể ngồi đây không?". Thú vị hơn, vì đóng gói, bạn có thể dễ dàng mô hình hóa các tính năng như mệt mỏi hoặc choáng.

Bạn thậm chí có thể trao đổi các bộ phận - tạo ra con voi bằng trí tuệ của zombie, thêm đôi cánh cho con người (anh ta thậm chí không chú ý), hoặc bất cứ điều gì khác.

Không có sự phân rã như thế này, tôi cá rằng bạn có nguy cơ gặp phải vụ nổ tổ hợp, sớm hay muộn.

Ngoài ra: http://www.valvesoftware.com/publications/2009/ai_systems_of_l4d_mike_booth.pdf


Cảm ơn. Đọc xong câu trả lời của bạn 3 lần, tôi nghĩ tôi hiểu. Tôi sẽ đọc bản PDF đó vào cuối tuần này.
tôi--

1
Đã suy nghĩ về điều này trong một giờ qua, tôi không chắc mình hiểu được sự khác biệt giữa việc có các BT hoàn toàn tách biệt với tâm trí và cơ thể so với một BT bị phân hủy thành các cây con (được tham chiếu qua một trình trang trí đặc biệt, với các kịch bản thời gian xây dựng buộc tất cả mọi thứ lại với nhau thành một BT lớn). Dường như với tôi rằng điều này sẽ cung cấp các lợi ích trừu tượng tương tự và thực sự có thể giúp dễ hiểu cách thức một thực thể nhất định hành xử vì bạn không phải xem qua nhiều BT riêng biệt. Tuy nhiên, tôi có lẽ là ngây thơ.
tôi--

@ user13414 Sự khác biệt là bạn sẽ cần các tập lệnh đặc biệt để xây dựng cây, khi chỉ cần sử dụng truy cập gián tiếp (tức là khi nút cơ thể phải hỏi cây của nó, đối tượng nào đại diện cho chân) có thể là đủ và cũng sẽ không yêu cầu bất kỳ bộ não bổ sung nào. Ít mã hơn, ít lỗi hơn. Ngoài ra, bạn sẽ mất khả năng (dễ dàng) chuyển đổi cây con trong thời gian chạy. Ngay cả khi bạn không cần sự linh hoạt như vậy, bạn sẽ không mất gì cả (kể cả tốc độ thực hiện).
Bóng tối trong mưa

3

Nằm trên giường tối qua, tôi có một cái gì đó hiển linh về cách tôi có thể đi về vấn đề này mà không giới thiệu sự phức tạp mà tôi đang nghiêng về câu hỏi của tôi. Nó liên quan đến việc sử dụng hỗn hợp "song song" (tên kém, IMHO). Đây là những gì tôi nghĩ:

nhập mô tả hình ảnh ở đây

Hy vọng rằng vẫn còn khá dễ đọc. Những điểm quan trọng là:

  • trình tự Ngồi xuống / Trì hoãn / Đứng lên là một chuỗi trong một chuỗi song song ( A ). Trên mỗi đánh dấu, chuỗi song song cũng kiểm tra điều kiện gần của Kẻ thù (đảo ngược). Nếu một kẻ thù gần, điều kiện không và như vậy quá không toàn bộ chuỗi song song (ngay lập tức, ngay cả khi chuỗi con được nữa đường thông qua Sit xuống , chậm trễ , hoặc đứng lên )
  • khi thất bại, bộ chọn B phía trên chuỗi song song sẽ nhảy xuống bộ chọn C để xử lý gián đoạn. Điều quan trọng, bộ chọn C sẽ không chạy nếu chuỗi song song A hoàn thành thành công
  • Bộ chọn C sau đó cố gắng đứng lên bình thường, nhưng cũng có thể kích hoạt hoạt hình vấp ngã nếu người lính hiện đang ở vị trí quá khó xử để chỉ đơn giản là đứng lên

Tôi nghĩ rằng điều này sẽ làm việc (sẽ sớm thử nó trong đột biến của tôi), mặc dù có một chút lộn xộn hơn tôi đã dự tính. Điều tốt là cuối cùng tôi sẽ có thể gói gọn các cây con dưới dạng các mảnh logic có thể tái sử dụng và tham chiếu chúng từ nhiều điểm. Điều đó sẽ làm giảm bớt hầu hết mối quan tâm của tôi ở đó, vì vậy tôi nghĩ rằng đây là một giải pháp khả thi.

Tất nhiên, tôi vẫn muốn nghe nếu có ai có bất kỳ suy nghĩ nào về việc này.

CẬP NHẬT : mặc dù phương pháp này có hiệu quả về mặt kỹ thuật, tôi đã quyết định nó sux. Đó là bởi vì các cây con không liên quan cần phải "biết" về các điều kiện được xác định trong các phần khác của cây để chúng có thể kích hoạt sự tàn lụi của chính chúng. Trong khi chia sẻ các tham chiếu của cây con sẽ đi theo cách nào đó để giảm bớt nỗi đau này, nó vẫn trái với những gì người ta mong đợi khi nhìn vào cây hành vi. Thật vậy, tôi đã phạm sai lầm tương tự hai lần trên một cành rất đơn giản.

Do đó, tôi sẽ đi theo con đường khác: hỗ trợ rõ ràng cho việc ưu tiên trong mô hình đối tượng và một tổ hợp đặc biệt cho phép thực hiện một tập hợp hành động khác khi thực hiện quyền ưu tiên. Tôi sẽ đăng một câu trả lời riêng khi tôi có việc gì đó.


1
Nếu bạn thực sự muốn sử dụng lại cây con thì logic khi nào cần ngắt ("kẻ địch ở gần" ở đây) có lẽ không phải là một phần của cây con. Thay vào đó, có lẽ hệ thống có thể yêu cầu bất kỳ cây con nào (ví dụ B ở đây) tự ngắt do kích thích có mức độ ưu tiên cao hơn và sau đó nó sẽ chuyển sang nút gián đoạn được đánh dấu đặc biệt (C ở đây) để xử lý đưa nhân vật trở lại trạng thái tiêu chuẩn. , ví dụ như đứng. Một chút giống như cây hành vi tương đương xử lý ngoại lệ.
Nathan Reed

1
Bạn thậm chí có thể kết hợp nhiều trình xử lý gián đoạn tùy thuộc vào kích thích nào bị gián đoạn. Ví dụ, nếu NPC đang ngồi và bắt đầu khai hỏa, bạn có thể không muốn anh ta đứng lên (và đưa ra một mục tiêu lớn hơn) mà chỉ ở mức thấp và tranh giành vỏ bọc.
Nathan Reed

@Nathan: buồn cười bạn đề cập đến "xử lý ngoại lệ". Cách tiếp cận khả thi đầu tiên tôi nghĩ đến tối qua là ý tưởng về một tổ hợp Preeem, trong đó sẽ có hai đứa con: một cho thực thi bình thường và một cho thực thi được ưu tiên. Nếu đứa trẻ bình thường vượt qua hoặc thất bại, kết quả đó lan truyền lên. Đứa trẻ được ưu tiên sẽ chỉ chạy nếu sự ưu tiên xảy ra. Tất cả các nút sẽ có một Preempt()phương thức, sẽ đi qua cây. Tuy nhiên, điều duy nhất để thực sự "xử lý" điều này sẽ là hỗn hợp phủ đầu, nó sẽ ngay lập tức chuyển sang nút con ưu tiên của nó.
tôi--

Sau đó, tôi nghĩ đến cách tiếp cận song song mà tôi phác thảo ở trên, và điều đó có vẻ thanh lịch hơn bởi vì nó không yêu cầu thêm hành trình trong suốt API. Theo quan điểm của bạn về việc đóng gói các cây con, tôi nghĩ rằng bất cứ nơi nào phức tạp phát sinh, đó sẽ là một điểm thay thế có thể. Đó thậm chí có thể là nơi bạn có một số điều kiện thường xuyên được kiểm tra cùng nhau. Trong trường hợp đó, gốc của sự thay thế sẽ là một chuỗi tổng hợp, với nhiều điều kiện như các phần tử con của nó.
tôi--

Tôi nghĩ Subtrees biết các điều kiện họ cần "đánh" trước khi thực hiện là hoàn toàn phù hợp vì nó khiến chúng khép kín và rất rõ ràng so với ẩn. Nếu đó là một mối quan tâm lớn hơn, thì đừng giữ các điều kiện trong cây con, mà tại "trang web gọi" của nó.
Seivan

2

Đây là giải pháp tôi đã giải quyết bây giờ ...

  • NodeLớp cơ sở của tôi có một Interruptphương thức, theo mặc định, không có gì
  • Các điều kiện là các cấu trúc "hạng nhất", theo đó chúng được yêu cầu trả về bool(do đó ngụ ý rằng chúng nhanh để thực thi và không bao giờ cần nhiều hơn một bản cập nhật)
  • Node trưng bày một tập hợp các điều kiện riêng biệt cho tập hợp các nút con của nó
  • Node.Executethực hiện tất cả các điều kiện đầu tiên và thất bại ngay lập tức nếu bất kỳ điều kiện thất bại. Nếu điều kiện thành công (hoặc không có), nó gọi ExecuteCoređể lớp con có thể thực hiện công việc thực tế của nó. Có một tham số cho phép bỏ qua các điều kiện, vì những lý do bạn sẽ thấy bên dưới
  • Nodecũng cho phép các điều kiện được thực hiện trong sự cô lập thông qua một CheckConditionsphương thức. Tất nhiên, Node.Executethực sự chỉ gọi CheckConditionskhi cần xác thực các điều kiện
  • Tổng Selectorhợp của tôi bây giờ gọi CheckConditionscho mỗi đứa trẻ mà nó xem xét để thực hiện. Nếu điều kiện thất bại, nó di chuyển thẳng đến đứa trẻ tiếp theo. Nếu họ vượt qua, nó sẽ kiểm tra xem đã có con thực thi chưa. Nếu vậy, nó gọi Interruptvà sau đó thất bại. Đó là tất cả những gì nó có thể làm vào thời điểm này, với hy vọng rằng nút hiện đang chạy sẽ đáp ứng yêu cầu ngắt, điều mà nó có thể làm bằng cách ...
  • Tôi đã thêm một Interruptiblenút, đó là một loại trang trí đặc biệt bởi vì nó có luồng logic thường xuyên như con trang trí của nó, và sau đó là một nút riêng biệt cho các gián đoạn. Nó thực hiện đứa con bình thường của mình để hoàn thành hoặc thất bại miễn là nó không bị gián đoạn. Nếu nó bị gián đoạn, nó ngay lập tức chuyển sang thực hiện nút con xử lý gián đoạn của nó, có thể là một cây con phức tạp theo yêu cầu

Kết quả cuối cùng là một cái gì đó như thế này, được lấy từ cành của tôi:

nhập mô tả hình ảnh ở đây

Trên đây là cây hành vi cho một con ong, thu thập mật hoa và trả nó về tổ của nó. Khi nó không có mật hoa và không ở gần một bông hoa có một số, nó đi lang thang:

nhập mô tả hình ảnh ở đây

Nếu nút này không bị gián đoạn thì nó sẽ không bao giờ thất bại, vì vậy con ong sẽ đi lang thang không ngừng. Tuy nhiên, vì nút cha là một bộ chọn và nó có con ưu tiên cao hơn, nên tính đủ điều kiện để thực hiện của chúng liên tục được kiểm tra. Nếu điều kiện của chúng vượt qua, bộ chọn sẽ làm gián đoạn và cây con bên trên ngay lập tức chuyển sang đường dẫn "Bị gián đoạn", đơn giản là bảo vệ ASAP bằng cách thất bại. Tất nhiên, nó có thể thực hiện một số hành động khác trước, nhưng đột biến của tôi không thực sự có gì để làm ngoài việc bảo lãnh.

Tuy nhiên, để giải quyết vấn đề này với câu hỏi của tôi, bạn có thể tưởng tượng rằng con đường "Bị gián đoạn" có thể cố gắng đảo ngược hoạt hình ngồi xuống và, thất bại, khiến người lính vấp ngã. Tất cả điều này sẽ giữ sự chuyển đổi sang trạng thái ưu tiên cao hơn, và đó chính xác là mục tiêu.

Tôi nghĩ rằng tôi hài lòng với cách tiếp cận này - đặc biệt là các phần cốt lõi mà tôi phác thảo ở trên - nhưng thành thật mà nói, nó đặt ra nhiều câu hỏi hơn về sự phổ biến của việc thực hiện các điều kiện và hành động cụ thể, và buộc cây hành vi vào hệ thống hoạt hình. Tôi thậm chí không chắc mình có thể nói rõ những câu hỏi này chưa, vì vậy tôi sẽ tiếp tục suy nghĩ / đạp xe.


1

Tôi đã sửa vấn đề tương tự bằng cách phát minh ra trang trí "Khi". Nó có một điều kiện và hai hành vi trẻ em ("sau đó" và "nếu không"). Khi "Khi" được thực thi, nó sẽ kiểm tra điều kiện và tùy thuộc vào kết quả của nó, chạy rồi / nếu không thì con. Nếu kết quả điều kiện thay đổi, chạy con được đặt lại và con tương ứng với nhánh khác được bắt đầu. Nếu con kết thúc thực hiện, toàn bộ "Khi" kết thúc thực hiện.

Điểm mấu chốt là không giống như BT ban đầu trong câu hỏi này khi điều kiện chỉ được kiểm tra khi bắt đầu chuỗi, "Khi" tôi tiếp tục kiểm tra điều kiện trong khi nó đang chạy. Vì vậy, đỉnh của cây hành vi được thay thế bằng:

When[EnemyNear]
  Then
    AttackSequence
  Otherwise
    When[StandingOnGrass]
      Then
        IdleSequence
      Otherwise
        Hum a tune

Để sử dụng "Khi" nâng cao hơn, người ta cũng muốn đưa ra hành động "Chờ" mà đơn giản là không làm gì trong một khoảng thời gian xác định hoặc vô thời hạn (cho đến khi được thiết lập lại bởi hành vi cha mẹ). Ngoài ra, nếu bạn chỉ cần một nhánh "Khi", một nhánh khác có thể chứa các hành động "Thành công" hoặc "Thất bại", thì kính ngắm đó sẽ thành công và thất bại ngay lập tức.


Tôi nghĩ rằng cách tiếp cận này gần hơn với những gì các nhà phát minh ban đầu của BT đã nghĩ đến. Nó sử dụng một luồng động hơn, đó là lý do tại sao trạng thái "chạy" trong BT là trạng thái rất nguy hiểm, rất hiếm khi được sử dụng. Chúng ta nên thiết kế BT luôn trong tâm trí khả năng quay trở lại từ gốc bất cứ lúc nào.
v.oddou

0

Trong khi tôi đến muộn, nhưng hy vọng điều này có thể giúp đỡ. Chủ yếu là vì tôi muốn chắc chắn rằng bản thân tôi đã không bỏ lỡ điều gì khi tôi cũng đang cố gắng tìm ra điều này. Tôi hầu như đã mượn ý tưởng này từ Unreal, nhưng không biến nó thành một Decoratortài sản trên cơ sở Nodehoặc gắn chặt với Blackboard, nó chung chung hơn.

Điều này sẽ giới thiệu một loại nút mới gọi Guardđó là giống như một sự kết hợp của một Decorator, và Compositevà có condition() -> Resultchữ ký cùng với mộtupdate() -> Result

Nó có ba chế độ để cho biết cách hủy sẽ xảy ra khi Guardtrả về Successhoặc Failed, việc hủy thực tế phụ thuộc vào người gọi. Vì vậy, để Selectorgọi một Guard:

  1. Hủy bỏ .self -> Chỉ hủy bỏ Guard(và con đang chạy) nếu nó đang chạy và điều kiện làFailed
  2. Hủy bỏ .lower-> Chỉ hủy các nút ưu tiên thấp hơn nếu chúng đang chạy và điều kiện là SuccesshoặcRunning
  3. Hủy bỏ .both -> Cả hai .self.lowertùy thuộc vào các điều kiện và các nút đang chạy. Bạn muốn tự hủy nếu nó đang chạy và sẽ điều kiện falsehoặc hủy nút đang chạy nếu chúng được coi là ưu tiên thấp hơn dựa trên Compositequy tắc ( Selectortrong trường hợp của chúng tôi) nếu điều kiện là Success. Nói cách khác, về cơ bản, cả hai khái niệm này được kết hợp.

Thích Decoratorvà không giống như Compositenó chỉ mất một đứa trẻ.

Mặc dù Guardchỉ có một con duy nhất, bạn có thể làm tổ càng nhiều Sequences, Selectorshoặc khác loại Nodesnhư bạn muốn, kể cả khác Guardshoặc Decorators.

Selector1 Guard.both[Sequence[EnemyNear?]] Sequence1 MoveToEnemy Attack Selector2 Sequence2 StandingOnGrass? Idle HumATune

Trong kịch bản trên, bất cứ khi nào Selector1cập nhật, nó sẽ luôn chạy kiểm tra điều kiện trên các vệ sĩ liên quan đến con của nó. Trong trường hợp trên, Sequence1được bảo vệ và cần được kiểm tra trước khi Selector1tiếp tục với các runningnhiệm vụ.

Bất cứ khi nào Selector2hoặc Sequence1đang chạy ngay khi EnemyNear?trả về successtrong khi Guards condition()kiểm tra thì Selector1sẽ đưa ra một ngắt / hủy cho running nodevà sau đó tiếp tục như bình thường.

Nói cách khác, chúng ta có thể phản ứng với nhánh "nhàn rỗi" hoặc "tấn công" dựa trên một số điều kiện làm cho hành vi phản ứng mạnh hơn nhiều so với khi chúng ta giải quyết Parallel

Điều này cũng cho phép bạn bảo vệ người độc thân Nodecó mức độ ưu tiên cao hơn so với việc chạy Nodestrong cùngComposite

Selector1 Guard.both[Sequence[EnemyNear?]] Sequence1 MoveToEnemy Attack Selector2 Guard.both[StandingOnGrass?] Idle HumATune

Nếu hoạt HumATuneđộng lâu Node, Selector2sẽ luôn kiểm tra cái đó trước nếu nó không dành cho Guard. Vì vậy, nếu npc được dịch chuyển lên một bản vá cỏ, lần sau Selector2chạy, nó sẽ kiểm tra Guardvà hủy HumATuneđể chạyIdle

Nếu nó bị dịch chuyển ra khỏi bản vá cỏ, nó sẽ hủy nút đang chạy ( Idle) và di chuyển đếnHumATune

Như bạn thấy ở đây, việc ra quyết định phụ thuộc vào người gọi Guardchứ không phải Guardchính nó. Các quy tắc của những người được coi là lower priorityvẫn còn với người gọi. Trong cả hai ví dụ, đó là Selectorngười định nghĩa cái gì tạo thành a lower priority.

Nếu bạn đã có một cuộc Compositegọi Random Selector, thì bạn sẽ có thể xác định các quy tắc trong quá trình thực hiện cụ thể đó Composite.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.