Thành phần trò chơi, người quản lý trò chơi và thuộc tính đối tượng


15

Tôi đang cố gắng để có được đầu của tôi xung quanh thiết kế thực thể dựa trên thành phần.

Bước đầu tiên của tôi là tạo ra các thành phần khác nhau có thể được thêm vào một đối tượng. Đối với mọi loại thành phần tôi có một trình quản lý, sẽ gọi hàm cập nhật của mọi thành phần, chuyển qua các thứ như trạng thái bàn phím, v.v. theo yêu cầu.

Điều tiếp theo tôi đã làm là loại bỏ đối tượng và chỉ cần mỗi thành phần có Id. Vì vậy, một đối tượng được xác định bởi các thành phần có cùng Id.

Bây giờ, tôi nghĩ rằng tôi không cần một người quản lý cho tất cả các thành phần của mình, ví dụ tôi có một tài sản SizeComponent, chỉ có một Sizetài sản). Kết quả là SizeComponentkhông có phương thức cập nhật và phương thức cập nhật của người quản lý không làm gì cả.

Suy nghĩ đầu tiên của tôi là có một ObjectPropertylớp mà các thành phần có thể truy vấn, thay vì có chúng như các thuộc tính của các thành phần. Vì vậy, một đối tượng sẽ có một số ObjectPropertyObjectComponent. Các thành phần sẽ có logic cập nhật truy vấn đối tượng cho các thuộc tính. Người quản lý sẽ quản lý việc gọi phương thức cập nhật của thành phần.

Điều này có vẻ như quá kỹ thuật đối với tôi, nhưng tôi không nghĩ rằng tôi có thể loại bỏ các thành phần, bởi vì tôi cần một cách để các nhà quản lý biết đối tượng nào cần logic thành phần nào để chạy (nếu không tôi sẽ loại bỏ thành phần hoàn toàn và đẩy logic cập nhật của nó vào trình quản lý).

  1. Đây có phải (có ObjectProperty, ObjectComponentComponentManagercác lớp) quá kỹ thuật?
  2. Điều gì sẽ là một thay thế tốt?

1
Bạn có ý tưởng đúng bằng cách cố gắng học mô hình thành phần, nhưng bạn cần hiểu rõ hơn về những gì nó cần làm - và cách duy nhất để làm điều đó là [chủ yếu] hoàn thành một trò chơi mà không cần sử dụng nó. Tôi nghĩ rằng việc tạo ra một SizeComponentquá mức cần thiết - bạn có thể cho rằng hầu hết các đối tượng đều có kích thước - đó là những thứ như kết xuất, AI và vật lý nơi mô hình thành phần được sử dụng; Kích thước sẽ luôn hoạt động giống nhau - vì vậy bạn có thể chia sẻ mã đó.
Jonathan Dickinson


@JonathanDickinson, @Den: Tôi nghĩ, vấn đề của tôi là nơi tôi lưu trữ các thuộc tính chung. Ví dụ, một đối tượng như một tư thế, được sử dụng bởi a RenderingComponentvà a PhysicsComponent. Tôi có suy nghĩ quá mức về quyết định đặt tài sản ở đâu không? Tôi có nên gắn nó vào một trong hai, sau đó có một truy vấn khác một đối tượng cho thành phần có thuộc tính cần thiết không?
George Duckett

Nhận xét trước đây của tôi và quá trình suy nghĩ đằng sau đó là điều thúc đẩy tôi có một lớp riêng cho một thuộc tính (hoặc nhóm các thuộc tính có liên quan) mà các thành phần có thể truy vấn.
George Duckett

1
Tôi thực sự thích ý tưởng đó - nó có thể đáng để thử; nhưng có một đối tượng để mô tả từng tài sản riêng lẻ thực sự tốn kém. Bạn có thể thử PhysicalStateInstance(một cho mỗi đối tượng) cùng với GravityPhysicsShared(một cho mỗi trò chơi); tuy nhiên tôi rất muốn nói rằng điều này đang mạo hiểm vào vương quốc của các kiến ​​trúc sư hưng phấn, đừng tự kiến ​​trúc sư vào một lỗ hổng (chính xác những gì tôi đã làm với hệ thống thành phần đầu tiên của mình). HÔN.
Jonathan Dickinson

Câu trả lời:


6

Câu trả lời đơn giản cho câu hỏi đầu tiên của bạn là Có, bạn quá kỹ thuật thiết kế. 'Tôi phá vỡ mọi thứ bao xa?' Câu hỏi rất phổ biến khi bước tiếp theo được thực hiện và đối tượng trung tâm (thường được gọi là Thực thể) bị xóa.

Khi bạn chia nhỏ các đối tượng đến mức chi tiết đến mức có kích thước riêng thì thiết kế đã đi quá xa. Bản thân một giá trị dữ liệu không phải là một thành phần. Nó là một kiểu dữ liệu được xây dựng và thường có thể được gọi chính xác là những gì bạn đã bắt đầu gọi chúng, một thuộc tính. Một thuộc tính không phải là một thành phần, nhưng một thành phần có chứa các thuộc tính.

Vì vậy, đây là một vài dòng hướng dẫn tôi thử và làm theo khi phát triển trong một hệ thống thành phần:

  • Không có muỗng.
    • Đây là bước mà bạn đã thực hiện để loại bỏ đối tượng trung tâm. Điều này loại bỏ toàn bộ cuộc tranh luận về những gì đi vào đối tượng Thực thể và những gì đi vào một thành phần vì bây giờ tất cả những gì bạn có là các thành phần.
  • Các thành phần không phải là cấu trúc
    • Nếu bạn chia một cái gì đó xuống nơi nó chỉ chứa dữ liệu thì nó không còn là một thành phần nữa, nó chỉ là một cấu trúc dữ liệu.
    • Một thành phần nên chứa tất cả các chức năng cần thiết để hoàn thành một nhiệm vụ rất cụ thể theo một cách cụ thể.
    • Giao diện IRenderable cung cấp giải pháp chung để hiển thị trực quan mọi thứ trong trò chơi. CRenderableSprite và CRenderableModel là một triển khai thành phần của giao diện đó cung cấp các chi tiết cụ thể để hiển thị tương ứng trong 2D và 3D.
    • IUsizable là giao diện cho thứ mà người chơi có thể tương tác. CUsizableItem sẽ là thành phần bắn ra khẩu súng đang hoạt động hoặc uống thuốc được chọn trong khi CUsizableTrigger có thể là nơi người chơi nhảy vào tháp pháo hoặc ném cần gạt để thả cây cầu.

Vì vậy, với hướng dẫn của các thành phần không phải là cấu trúc, SizeComponent đã bị phá vỡ quá xa. Nó chỉ chứa dữ liệu và những gì xác định kích thước của một cái gì đó có thể thay đổi. Ví dụ, trong thành phần kết xuất, nó có thể là vô hướng 1d hoặc vectơ 2 / 3d. Trong một thành phần vật lý, nó có thể là thể tích giới hạn của vật thể. Trong một mục kiểm kê, nó có thể chiếm bao nhiêu không gian trên lưới 2D.

Hãy thử và vẽ một ranh giới tốt giữa lý thuyết và thực tiễn.

Hi vọng điêu nay co ich.


Chúng ta đừng quên rằng trên một số nền tảng, việc gọi một chức năng từ Giao diện dài hơn so với việc gọi một chức năng từ Lớp cha mẹ (vì câu trả lời của bạn bao gồm các đề cập đến giao diện và các lớp)
ADB

Điểm tốt để nhớ, nhưng tôi đã cố gắng duy trì ngôn ngữ bất khả tri và chỉ sử dụng chúng trong các thuật ngữ thiết kế chung.
James

"Nếu bạn chia nhỏ thứ gì đó xuống nơi nó chỉ chứa dữ liệu thì nó không còn là thành phần nữa, nó chỉ là một cấu trúc dữ liệu." -- Tại sao? "Thành phần" là một từ chung chung đến nỗi nó có thể có nghĩa là cấu trúc dữ liệu.
Paul Manta

@PaulManta Có, đó là một thuật ngữ chung, nhưng toàn bộ điểm của câu hỏi và câu trả lời này là nơi để vẽ đường thẳng. Câu trả lời của tôi, như bạn đã trích dẫn, chỉ là gợi ý của tôi cho một quy tắc của ngón tay cái để làm điều đó. Như mọi khi tôi ủng hộ không bao giờ để lý thuyết hoặc thiết kế cân nhắc là điều thúc đẩy sự phát triển, nó có nghĩa là để hỗ trợ nó.
James

1
@James Đó là một cuộc thảo luận thú vị. :) chat.stackexchange.com/rooms/2175 lớn nhất của tôi nếu việc triển khai của bạn là các thành phần biết quá nhiều về những gì các thành phần khác quan tâm. Tôi muốn tiếp tục thảo luận vào thời điểm trong tương lai.
Paul Manta

14

Bạn đã chấp nhận một câu trả lời, nhưng đây là cú đâm của tôi tại CBS. Tôi thấy rằng một Componentlớp chung có một số hạn chế, vì vậy tôi đã đi với một thiết kế được mô tả bởi Radical Entertainment tại GDC 2009, người đã đề xuất tách các thành phần thành AttributesBehaviors. (" Lý thuyết và thực hành của kiến ​​trúc thành phần đối tượng trò chơi ", Marcin Chady)

Tôi giải thích các quyết định thiết kế của tôi trong một tài liệu hai trang. Tôi sẽ chỉ đăng liên kết vì quá lâu để dán tất cả vào đây. Hiện tại nó chỉ bao gồm các thành phần logic (không phải các thành phần kết xuất và vật lý), nhưng nó sẽ cho bạn ý tưởng về những gì tôi đã cố gắng thực hiện:

http://www.pdf-archive.com/2012/01/08/entity-component-system/preview/page/1

Đây là một đoạn trích từ tài liệu:

Các thuộc tính và hành vi ngắn

Attributesquản lý một loại dữ liệu và bất kỳ logic nào họ có bị giới hạn phạm vi. Ví dụ: Healthcó thể đảm bảo rằng giá trị hiện tại của nó không bao giờ lớn hơn giá trị tối đa của nó và thậm chí nó có thể thông báo cho các thành phần khác khi giá trị hiện tại giảm xuống dưới một mức tới hạn nhất định, nhưng nó không chứa bất kỳ logic phức tạp nào. Attributesphụ thuộc vào không khác Attributeshoặc Behaviors.

Behaviorskiểm soát cách thức thực thể phản ứng với các sự kiện trò chơi, đưa ra quyết định và thay đổi các giá trị Attributeskhi cần thiết. Behaviorsphụ thuộc vào một số của Attributes, nhưng họ không thể trực tiếp tương tác với nhau - họ chỉ phản ứng với cách Attributes’giá trị được thay đổi bằng cách khác Behaviorsvà các sự kiện chúng được gửi.


Chỉnh sửa: Và đây là sơ đồ mối quan hệ cho thấy cách các thành phần giao tiếp với nhau:

Sơ đồ giao tiếp giữa các thuộc tính và hành vi

Một chi tiết thực hiện: cấp thực thể EventManager chỉ được tạo nếu nó được sử dụng. Các Entitylớp học chỉ lưu trữ một con trỏ đến một EventManagermà được khởi tạo chỉ khi một số yêu cầu phần nó.


Chỉnh sửa: Trên một câu hỏi khác, tôi đã đưa ra một câu trả lời tương tự cho câu hỏi này. Bạn có thể tìm thấy nó ở đây để giải thích rõ hơn về hệ thống:
/gamedev//a/23759/6188


2

Nó thực sự phụ thuộc vào các thuộc tính bạn cần và nơi bạn cần chúng. Dung lượng bộ nhớ bạn sẽ có và sức mạnh / loại xử lý bạn sẽ sử dụng. Tôi đã thấy và cố gắng làm như sau:

  • Các thuộc tính được sử dụng bởi nhiều thành phần nhưng chỉ được sửa đổi bởi một thành phần được lưu trữ trong thành phần đó. Hình dạng là một ví dụ điển hình trong một trò chơi trong đó hệ thống AI, hệ thống vật lý cũng như hệ thống kết xuất cần truy cập vào hình dạng cơ sở, đó là một tài sản nặng và nó chỉ nên duy trì ở một nơi nếu có thể.
  • Các thuộc tính như vị trí đôi khi cần phải được nhân đôi. Ví dụ: nếu bạn chạy song song nhiều hệ thống, bạn muốn tránh nhìn trộm qua các hệ thống và thay vào đó sẽ đồng bộ hóa vị trí (sao chép từ thành phần chính hoặc đồng bộ hóa qua deltas hoặc với thẻ vượt qua nếu cần).
  • Các thuộc tính có nguồn gốc từ các điều khiển hoặc "ý định" AI có thể được lưu trữ trong một hệ thống chuyên dụng vì chúng có thể được áp dụng cho các hệ thống khác mà không thể nhìn thấy từ bên ngoài.
  • Thuộc tính đơn giản có thể trở nên phức tạp. Đôi khi, vị trí của bạn sẽ yêu cầu một hệ thống chuyên dụng nếu bạn cần chia sẻ nhiều dữ liệu (vị trí, hướng, khung delta, tổng chuyển động của dòng delta, chuyển động delta cho khung hiện tại và cho khung trước đó, xoay ...). Trong trường hợp đó, bạn sẽ phải đi cùng với hệ thống và truy cập dữ liệu mới nhất từ ​​thành phần chuyên dụng và bạn có thể phải thay đổi thông qua bộ tích lũy (deltas).
  • Đôi khi các thuộc tính của bạn có thể được lưu trữ trong một mảng thô (double *) và các thành phần của bạn sẽ chỉ có các con trỏ tới các mảng chứa các thuộc tính khác nhau. Ví dụ rõ ràng nhất là khi bạn cần các phép tính song song lớn (CUDA, OpenCL). Vì vậy, có một hệ thống để quản lý đúng các con trỏ có thể có trong tay.

Những nguyên tắc này có những hạn chế của họ. Tất nhiên bạn sẽ phải đẩy hình học vào trình kết xuất nhưng có lẽ bạn sẽ không muốn lấy nó từ đó. Hình dạng chính sẽ được lưu trữ trong động cơ vật lý trong trường hợp biến dạng xảy ra ở đó và được đồng bộ hóa với trình kết xuất (theo thời gian tùy thuộc vào khoảng cách của các đối tượng). Vì vậy, theo một cách nào đó bạn sẽ nhân đôi nó.

Không có hệ thống hoàn hảo. Và một số trò chơi sẽ tốt hơn với một hệ thống đơn giản hơn trong khi những trò chơi khác sẽ yêu cầu đồng bộ hóa phức tạp hơn giữa các hệ thống.

Đầu tiên, đảm bảo tất cả các thuộc tính có thể được truy cập một cách đơn giản từ các thành phần của bạn để bạn có thể thay đổi cách bạn lưu trữ các thuộc tính một cách trong suốt khi bạn bắt đầu tinh chỉnh hệ thống của mình.

Không có sự xấu hổ trong việc sao chép một số tài sản. Nếu một vài thành phần cần giữ một bản sao cục bộ, đôi khi hiệu quả hơn để sao chép và đồng bộ hóa thay vì truy cập vào một "giá trị" bên ngoài

Đồng bộ hóa không phải xảy ra mọi khung hình. Một số thành phần có thể được đồng bộ hóa ít thường xuyên hơn những thành phần khác. Thành phần kết xuất thường là một ví dụ tốt. Những người không tương tác với người chơi có thể được đồng bộ hóa ít thường xuyên hơn, giống như những người ở xa. Những người ở xa và bên ngoài trường camera có thể được đồng bộ hóa thậm chí ít thường xuyên hơn.


Khi nói đến thành phần kích thước của bạn, nó có thể được gói trong thành phần vị trí của bạn:

  • không phải tất cả các thực thể có kích thước đều có thành phần vật lý, ví dụ như các khu vực, vì vậy việc kết hợp nó với vật lý không phải là lợi ích tốt nhất của bạn.
  • kích thước có thể sẽ không quan trọng nếu không có vị trí
  • tất cả các đối tượng có vị trí có thể sẽ có kích thước (có thể được sử dụng cho các tập lệnh, vật lý, AI, kết xuất ...).
  • kích thước có thể không được cập nhật mỗi chu kỳ nhưng vị trí có thể.

Thay vì kích thước, bạn thậm chí có thể sử dụng một công cụ sửa đổi kích thước, nó có thể trở nên dễ xử lý hơn.

Để lưu trữ tất cả các thuộc tính trong một hệ thống lưu trữ thuộc tính chung ... Tôi không chắc bạn đang đi đúng hướng ... Tập trung vào các thuộc tính cốt lõi cho trò chơi của bạn và tạo các thành phần đóng gói càng nhiều thuộc tính càng tốt. Miễn là bạn trừu tượng hóa quyền truy cập vào các thuộc tính này một cách chính xác (thông qua getters trong các thành phần cần chúng chẳng hạn), bạn sẽ có thể di chuyển, sao chép và đồng bộ hóa chúng sau này, mà không phá vỡ quá nhiều logic.


2
BTW +1 hoặc -1 tôi vì đại diện hiện tại của tôi là 666 kể từ ngày 9 tháng 11 ... Thật đáng sợ.
Coyote

1

Nếu các thành phần có thể được thêm tùy ý vào các thực thể, thì bạn cần một cách để truy vấn nếu một thành phần nhất định tồn tại trong một thực thể và để có được một tham chiếu đến nó. Vì vậy, bạn có thể lặp lại danh sách các đối tượng được lấy từ ObjectComponent cho đến khi bạn tìm thấy đối tượng bạn muốn và trả lại nó. Nhưng bạn sẽ trả về một đối tượng đúng loại.

Trong C ++ hoặc C #, điều này thường có nghĩa là bạn có một phương thức mẫu trên thực thể như T GetComponent<T>() . Và một khi bạn có tài liệu tham khảo đó, bạn sẽ biết chính xác dữ liệu thành viên của nó, vì vậy chỉ cần truy cập trực tiếp.

Trong một cái gì đó như Lua hoặc Python, bạn không nhất thiết phải có một loại rõ ràng của đối tượng đó và có lẽ cũng không quan tâm. Nhưng một lần nữa, bạn chỉ có thể truy cập vào biến thành viên và xử lý bất kỳ ngoại lệ nào phát sinh từ việc cố gắng truy cập vào thứ gì đó không có ở đó.

Truy vấn các thuộc tính đối tượng nghe có vẻ rõ ràng như sao chép công việc mà ngôn ngữ có thể làm cho bạn, hoặc tại thời gian biên dịch cho các ngôn ngữ được nhập tĩnh hoặc tại thời gian chạy cho các ngôn ngữ được nhập động.


Tôi hiểu về việc nhận các thành phần được gõ mạnh từ một thực thể (sử dụng tổng quát hoặc tương tự), câu hỏi của tôi là về các thuộc tính đó nên đi đâu, đặc biệt là nơi một thuộc tính được sử dụng bởi nhiều thành phần và không thể nói thành phần nào sở hữu nó. Xem bình luận thứ 3 và thứ 4 của tôi về câu hỏi.
George Duckett

Chỉ cần chọn một cái hiện có nếu nó phù hợp hoặc đưa yếu tố vào một thành phần mới nếu nó không phù hợp. Ví dụ, Unity có thành phần 'Biến đổi' chỉ là vị trí và nếu có bất cứ điều gì khác cần thay đổi vị trí của đối tượng, họ sẽ thực hiện thông qua thành phần đó.
Kylotan

1

"Tôi nghĩ, vấn đề của tôi là tôi lưu trữ các thuộc tính chung ở đâu. Ví dụ, một đối tượng là một tư thế, được sử dụng bởi RenderingComponent và PhysComponent. Tôi có nghĩ quá nhiều về quyết định đặt tài sản ở đâu không? trong một trong hai, sau đó có một truy vấn khác một đối tượng cho thành phần có thuộc tính cần thiết không? "

Có điều là RenderingComponent sử dụng vị trí, nhưng ChemistryComponent cung cấp nó. Bạn chỉ cần một cách để nói với từng thành phần người dùng sẽ sử dụng nhà cung cấp nào. Lý tưởng theo một cách bất khả tri, nếu không sẽ có một sự phụ thuộc.

"... câu hỏi của tôi là về những tài sản đó nên đi đâu, đặc biệt là nơi một tài sản được sử dụng bởi nhiều thành phần và không có thành phần nào có thể nói là sở hữu nó. Xem bình luận thứ 3 và thứ 4 của tôi về câu hỏi."

Không có quy tắc chung. Phụ thuộc vào tài sản cụ thể (xem ở trên).

Tạo một trò chơi với kiến ​​trúc xấu nhưng dựa trên thành phần và sau đó cấu trúc lại nó.


Tôi không nghĩ rằng tôi khá hiểu những gì PhysicsComponentnên làm sau đó. Tôi thấy nó là quản lý mô phỏng đối tượng trong môi trường vật lý, điều này dẫn đến sự nhầm lẫn này: Không phải tất cả những thứ cần được kết xuất sẽ cần phải được mô phỏng, vì vậy tôi có thể thêm sai PhysicsComponentkhi thêm RenderingComponentvì nó chứa một vị trí mà RenderingComponentsử dụng. Tôi có thể dễ dàng thấy mình kết thúc với một mạng lưới các thành phần được kết nối với nhau, có nghĩa là tất cả / hầu hết cần phải được thêm vào mỗi thực thể.
George Duckett

Tôi đã có một tình huống tương tự thực sự :). Tôi có một PhysBodyComponent và SimpleSpatialComponent. Cả hai đều cung cấp Vị trí, Góc và Kích thước. Nhưng cái đầu tiên tham gia vào mô phỏng vật lý và có các thuộc tính liên quan bổ sung, và cái thứ hai chỉ chứa dữ liệu không gian đó. Nếu bạn có động cơ vật lý của riêng mình, bạn thậm chí có thể thừa hưởng cái trước từ cái sau.
Den

"Tôi có thể dễ dàng thấy mình kết thúc với một mạng lưới các thành phần được kết nối với nhau, nghĩa là tất cả / hầu hết cần phải được thêm vào mỗi thực thể." Đó là bởi vì bạn không có một nguyên mẫu trò chơi thực tế. Chúng tôi đang nói về một số thành phần cơ bản ở đây. Không có gì ngạc nhiên khi chúng sẽ được sử dụng ở khắp mọi nơi.
Den

1

Ruột của bạn đang nói với bạn rằng có ThingProperty, ThingComponentThingManagercho mỗiThing loại thành phần một chút quá mức cần thiết. Tôi nghĩ nó đúng.

Nhưng, bạn cần một số cách để theo dõi các thành phần liên quan về các hệ thống sử dụng chúng, chúng thuộc về thực thể nào, v.v.

TransformPropertysẽ là một khá phổ biến. Nhưng ai chịu trách nhiệm về nó, hệ thống kết xuất? Hệ thống vật lý? Hệ thống âm thanh? Tại sao mộtTransform thành phần thậm chí cần phải cập nhật chính nó?

Giải pháp là loại bỏ bất kỳ loại mã nào khỏi các thuộc tính của bạn bên ngoài getters, setters và bộ khởi tạo. Các thành phần là dữ liệu, được sử dụng bởi các hệ thống trong trò chơi để thực hiện các tác vụ khác nhau như kết xuất, AI, phát lại âm thanh, chuyển động, v.v.

Đọc về Artemis: http://piemaster.net/2011/07/entity-component-artemis/

Nhìn vào mã của nó và bạn sẽ thấy rằng nó dựa trên các Hệ thống khai báo các phụ thuộc của chúng dưới dạng danh sách ComponentTypes. Bạn viết từng cái của bạnSystem lớp và trong phương thức constructor / init khai báo loại hệ thống đó phụ thuộc vào.

Trong quá trình thiết lập cho các cấp hoặc không có gì, bạn tạo các thực thể của mình và thêm các thành phần cho chúng. Sau đó, bạn nói với thực thể đó báo cáo lại cho Artemis, và sau đó Artemis tìm ra dựa trên thành phần của thực thể đó mà các hệ thống sẽ quan tâm khi biết về thực thể đó.

Sau đó, trong giai đoạn cập nhật vòng lặp của bạn System, giờ đây bạn có một danh sách các thực thể cần cập nhật. Bây giờ bạn có thể có granularity của các thành phần để bạn có thể đưa ra hệ thống điên, xây dựng đơn vị ra khỏi một ModelComponent, TransformComponent, FliesLikeSupermanComponent, vàSocketInfoComponent , và làm điều gì đó kỳ lạ như make một chiếc đĩa bay mà ruồi giữa khách hàng kết nối với một trò chơi nhiều người. Được rồi, có thể không phải vậy, nhưng ý tưởng là nó giữ cho mọi thứ tách rời và linh hoạt.

Artemis không hoàn hảo và các ví dụ trên trang web hơi cơ bản, nhưng sự phân tách mã và dữ liệu rất mạnh mẽ. Nó cũng tốt cho bộ nhớ cache của bạn nếu bạn làm đúng. Artemis có lẽ không làm điều đó ngay trên mặt trận đó, nhưng thật tốt khi học hỏi.

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.