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 Messageklasy, 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” Objswojego 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” Physicswybierze wiadomość i zrobi wszystko, czego potrzebuje. Po zakończeniu będzie albo:
- Wyślij do siebie komunikat „SetPosition”, który
Positionwybierze komponent;
- Lub bezpośrednio uzyskaj dostęp do
Positionkomponentu 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 Positionkomponent, ale Positionkomponent 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.
MessageKlasa 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ę Messagei dodaje pożądany wektor siły - ale uważaj, jeśli to zrobisz, koszt czasu wykonania RTTI .