Este sub-módulo é responsável pela verificação das jogadas. Descreve o comportamento dos objetos virtuais por meio de fórmulas matemáticas e envia atualizações ao distribuidor de eventos que atualiza os clientes conectados ao servidor. Tais operações são implementadas a partir do TorqueScript.
As peças se movimentam de acordo com uma função de projeção realizada pela API
OpenGL, usando a função gluUnProject que converte um ponto da tela para o cenário, a partir
de algum ponto que seria o observador, no caso a câmera. Exemplo da definição da função
gluUnProject, com os parâmetros a ser passados para execução.
GLint gluUnProject( GLdouble winX, GLdouble winY, GLdouble winZ, const GLdouble
*model, const GLdouble *proj, const GLint *view, GLdouble* objX, GLdouble* objY,
Na Figura 5-7, mostrando o código fonte da torque enfocando a função para executar o movimento de uma peça.
Figura 5-7: Implementação na biblioteca da Torque em C++.
O funcionamento das jogadas do Quarto começa com o jogador entregando ao adversário uma peça. Para fazer esse movimento é necessário que primeiramente ele selecione uma jogada.
Os arquivos que compõem esses algoritmos são quarto.contents/client/scripts/
playGui.cs e quarto.contents.server/scripts/commands.cs. Esses arquivos contêm toda a parte
de logística de movimentação do mouse.
O objeto PlayGui (Figura 5-8) é o responsável por capturar as ações do mouse (Mouse Down, Mouse Up, Mouse Move (Figura 5-9) e Mouse Dragged). Cada um desses eventos possui uma aplicação no jogo.
Figura 5-8: Codificação de eventos do mouse em C++. // Em engine/game/GameTSCtrl.h /*…*/ private: Point3F mMouse3DVec; Point3F mMouse3DPos; Point3F mObjReferencePos; public:
void onMouseMove(const GuiEvent &evt); void onMouseDown(const GuiEvent &evt); void onMouseUp(const GuiEvent &evt); void onMouseDragged(const GuiEvent &evt); Point3F getMouse3DVec() {return mMouse3DVec;}; Point3F getMouse3DPos() {return mMouse3DPos;}; /*…*/
// Em engine/gui/core/GuiTSCtrl.cc
// Utilização da gluUnProject dentro da Engine TGE /*…*/
bool GuiTSCtrl::unproject(const Point3F &pt, Point3F *dest) {
GLdouble objx, objy, objz;
GLint result = gluUnProject(pt.x, pt.y, pt.z,
mSaveModelview, mSaveProjection, mSaveViewport, &objx, &objy, &objz);
if(result == GL_FALSE) return false;
dest->set(objx, objy, objz); return true;
} /*…*/
A PlayGui é uma instância do objeto GameTSCtrl (Figura 5-9). Esta classe inicialmente não possui os eventos de mouse implementados. Por essa razão foi necessário fazer a implementação destas funções para que pudéssemos utilizar o Mouse 3D.
Figura 5-9: Implementação da função onMouseMove().
Na Figura 5-10, apresenta a função PlayGui::onMouseDown(%this) armazena a posição 3D do mouse e a sua direção a partir da câmera. Ao fazer isso, ele envia um comando ao Servidor, contendo essas informações.
commandToServer('selectObject', %mouseVec, %cameraPoint);
Esse comando tem como função dizer ao servidor que o botão do mouse foi clicado, e se for durante sua própria jogada, ele poderá evoluir o onMouseDown para onMouseDragged e arrastar uma peça para que seja jogada no tabuleiro por meio da função onMouseUp.
Entretanto, na Figura 5-10 descreve a implementação do código que refere-se ao processo de seleção e movimentação das peças.
// Em engine/game/GameTSCtrl.cc // Demonstração do onMouseMove.
void GameTSCtrl::onMouseMove(const GuiEvent &evt) { if(gSnapLine) return; MatrixF mat; Point3F vel; if ( GameGetCameraTransform(&mat, &vel) ) {
Point3F pos;// = mObjReferencePos; mat.getColumn(3,&pos);
const Point3F screenPoint(evt.mousePoint.x, evt.mousePoint.y, 1); Point3F worldPoint;
if (unproject(screenPoint, &worldPoint)) {
Point3F vec = worldPoint - pos; lineTestStart = pos;
vec.normalizeSafe(); mMouse3DPos = pos; mMouse3DVec = vec;
// Função que leva para a PlayGui::onMouseMove Con::executef(this, 2, "onMouseMove");
} }
Figura 5-10: Exemplificando a execução da função pela Torque.
Ao ativar o evento onMouseDown da PlayGui, o mouse envia por meio do comando
commandToServer, e o jogador consegue “empacotar” os dados com relação à posição do
mouse do cliente, e sua câmera. A partir disso, em serverCmdSelectObject, é definido o range, ou seja, uma distância de até onde será traçado o ContainerRayCast, uma função que recupera o primeiro objeto com as características enviadas, no nosso caso
$TypeMasks::StaticShapeObjectType.
A seguir, ele verifica qual o estado em que o turno está e dependendo de cada um desses estados ele processará de uma maneira diferente. Por exemplo, se for o estado
$ESTADO_ESCOLHER_PECA ele irá criar uma área com um objeto que utiliza uma IFL
para fazer animações, informando para onde deve ser levada a peça.
A peça seguirá o mouse por meio do evento onMouseDragged da PlayGui e depois será colocada no lugar onde foi deixada ao realizar o onMouseUp, após uma prévia certificação.
// Em PlayGui.cs
function PlayGui::onMouseDown(%this) {
// Recupera o vetor direção em relação a câmera e a posição 3D do mouse. %mouseVec = %this.getMouse3DVec();
// Posição 3D da câmera.
%cameraPoint = %this.getMouse3DPos();
// Mensagem enviada ao servidor dizer para selecionar um Objeto. commandToServer('selectObject', %mouseVec, %cameraPoint);
}
// Em Commands.cs
function serverCmdSelectObject(%client, %mouseVec, %cameraPoint) {
/*…*/
%selectRange = 200;
%mouseScaled = VectorScale(%mouseVec, %selectRange); %rangeEnd = VectorAdd(%cameraPoint, %mouseScaled); %searchMasks = $TypeMasks::StaticShapeObjectType;
%scanTarg = ContainerRayCast (%cameraPoint, %rangeEnd, %searchMasks, false);
O botão Quarto (Q) foi implementado para que qualquer um dos dois jogadores possa apertá-lo em qualquer momento do jogo.22 Uma vez apertado, este jogador irá para o estado
$ESTADO_SELECIONAR_QUARTO, enquanto o outro vai para o $ESTADO_AGUARDAR_QUARTO.
Cada peça selecionada é armazenada em uma variável do tipo Array. Após o Jogador terminar de selecionar as peças que ele julga formar Quarto, imediatamente o servidor executa a função (Figura 5-11) verificaQuarto(). Essa função tem como objetivo, conferir primeiramente se as peças selecionadas estão em posição de formar um Quarto.
Figura 5-11: Algoritmo de execução das regras do jogo.
Caso ele não esteja em posição de formar Quarto, então cancela-se o estado, caso contrário ele faz a verificação das peças e se estiver correto, declara final de jogo conforme detalhado na Figura 5-12. // Em Quarto.cs function verificaQuarto() { /*…*/
// Verifica se a linha é valida.
if(!((%ind(0) == 1 && %ind(1) == 2 && %ind(2) == 3 && %ind(3) == 4) || (%ind(0) == 5 && %ind(1) == 6 && %ind(2) == 7 && %ind(3) == 8) || (%ind(0) == 9 && %ind(1) == 10 && %ind(2) == 11 && %ind(3) == 12) || (%ind(0) == 13 && %ind(1) == 14 && %ind(2) == 15 && %ind(3) == 16) || (%ind(0) == 1 && %ind(1) == 5 && %ind(2) == 9 && %ind(3) == 13) || (%ind(0) == 2 && %ind(1) == 6 && %ind(2) == 10 && %ind(3) == 14) || (%ind(0) == 3 && %ind(1) == 7 && %ind(2) == 11 && %ind(3) == 15) || (%ind(0) == 4 && %ind(1) == 8 && %ind(2) == 12 && %ind(3) == 16) || (%ind(0) == 1 && %ind(1) == 6 && %ind(2) == 11 && %ind(3) == 16) || (%ind(0) == 4 && %ind(1) == 7 && %ind(2) == 10 && %ind(3) == 13))) { cancelaQuarto(); return; } if(verificaPecas($Game::pecasQuarto(0), $Game::pecasQuarto(1), $Game::pecasQuarto(2), $Game::pecasQuarto(3))) {
iluminaCilindros(%ind(0), %ind(1), %ind(2), %ind(3)); // Fim de jogo fimDeJogo(); return true; } else { return false; } }
Figura 5-12: Código de verificação do alinhamento do quarto.