Zwykle odbywa się to za pomocą wiadomości. Możesz znaleźć wiele szczegółów w innych pytaniach na tej stronie, takich jak tutaj lub tam .
Aby odpowiedzieć na twój konkretny przykład, sposobem jest zdefiniowanie małej Message
klasy, którą Twoje obiekty mogą przetwarzać, np .:
struct Message
{
Message(const Objt& sender, const std::string& msg)
: m_sender(&sender)
, m_msg(msg) {}
const Obj* m_sender;
std::string m_msg;
};
void Obj::Process(const Message& msg)
{
for (int i=0; i<m_components.size(); ++i)
{
// let components do some stuff with msg
m_components[i].Process(msg);
}
}
W ten sposób nie „zanieczyszczasz” Obj
swojego interfejsu klasami metodami związanymi z komponentami. Niektóre komponenty mogą przetworzyć wiadomość, niektóre mogą ją po prostu zignorować.
Możesz zacząć od wywołania tej metody bezpośrednio z innego obiektu:
Message msg(obj1, "EmitForce(5.0,0.0,0.0)");
obj2.ProcessMessage(msg);
W takim przypadku obj2
„s” Physics
wybierze wiadomość i zrobi wszystko, czego potrzebuje. Po zakończeniu będzie albo:
- Wyślij do siebie komunikat „SetPosition”, który
Position
wybierze komponent;
- Lub bezpośrednio uzyskaj dostęp do
Position
komponentu w celu modyfikacji (całkiem źle w przypadku projektu opartego wyłącznie na komponentach, ponieważ nie możesz założyć, że każdy obiekt ma Position
komponent, ale Position
komponent może być wymagany Physics
).
Zasadniczo dobrym pomysłem jest opóźnienie faktycznego przetworzenia wiadomości na aktualizację następnego komponentu. Natychmiastowe przetworzenie może oznaczać wysyłanie wiadomości do innych elementów innych obiektów, więc wysłanie tylko jednej wiadomości może szybko oznaczać nierozerwalny stos spaghetti.
Prawdopodobnie będziesz musiał później wybrać bardziej zaawansowany system: asynchroniczne kolejki komunikatów, wysyłanie wiadomości do grupy obiektów, rejestrowanie / wyrejestrowywanie poszczególnych komponentów z wiadomości itp.
Message
Klasa może być ogólna pojemnik z prostego łańcucha, jak pokazano powyżej, ale przetwarzania ciągi przy starcie nie jest naprawdę skuteczny. Możesz wybrać kontener wartości ogólnych: ciągi, liczby całkowite, liczby zmiennoprzecinkowe ... Z nazwą lub jeszcze lepszą, z identyfikatorem, aby rozróżnić różne typy wiadomości. Możesz też uzyskać klasę podstawową, która będzie pasować do konkretnych potrzeb. W twoim przypadku możesz sobie wyobrazić, EmitForceMessage
że wywodzi się Message
i dodaje pożądany wektor siły - ale uważaj, jeśli to zrobisz, koszt czasu wykonania RTTI .