Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Los juegos para la Plataforma universal de Windows (UWP) se ejecutan en una amplia variedad de dispositivos, como equipos de escritorio, portátiles y tabletas. Un dispositivo puede tener una gran cantidad de mecanismos de entrada y control. En este documento se describen las prácticas clave que se deben tener en cuenta al trabajar con dispositivos de entrada y se muestra cómo Marble Maze aplica estas prácticas.
Nota:
El código de ejemplo que corresponde a este documento se encuentra en el ejemplo de juego DirectX Marble Maze .
Estos son algunos de los puntos clave que describe este documento para cuando trabajes con la entrada en tu juego:
Cuando sea posible, admita varios dispositivos de entrada para permitir que el juego admita una gama más amplia de preferencias y capacidades entre sus clientes. Aunque el uso del controlador de juegos y el sensor es opcional, se recomienda encarecidamente mejorar la experiencia del jugador. Hemos diseñado las API de sensor y controlador de juegos para ayudarle a integrar estos dispositivos de entrada con mayor facilidad.
Para inicializar la entrada táctil, debe suscribirse a eventos de ventana, como cuando el puntero se activa, se libera y se mueve. Para inicializar el acelerómetro, cree un objeto Windows::Devices::Sensors::Accelerometer al inicializar la aplicación. Un controlador de juego no requiere inicialización.
Para los juegos de un solo jugador, considere si combinar la entrada de todos los controladores posibles. De este modo, no es necesario realizar un seguimiento de qué entrada procede del controlador. O bien, simplemente realice un seguimiento de la entrada solo desde el controlador agregado más recientemente, como hacemos en este ejemplo.
Procesar eventos de Windows antes de procesar dispositivos de entrada.
El controlador de juegos y el acelerómetro admiten sondeos. Es decir, puede consultar los datos cuando lo necesite. Para los eventos táctiles, registre los eventos táctiles en estructuras de datos que estén disponibles para el código de procesamiento de entrada.
Considere si se normalizan los valores de entrada en un formato común. Si lo hace, puede simplificar la forma en que otros componentes del juego interpretan la entrada, como la simulación de física, y pueden facilitar la escritura de juegos que funcionan en diferentes resoluciones de pantalla.
Dispositivos de entrada compatibles con Marble Maze
Marble Maze admite el mando del juego, el mouse y la función táctil para seleccionar elementos de menú, y el mando del juego, el mouse, el toque y el acelerómetro para controlar el juego. Marble Maze usa las API de Windows::Gaming::Input para sondear el controlador para la entrada. Touch permite a las aplicaciones realizar un seguimiento y responder a la entrada del dedo. Un acelerómetro es un sensor que mide la fuerza que se aplica a lo largo de los ejes X, Y y Z. Con Windows Runtime, puedes sondear el estado actual del dispositivo acelerómetro, así como recibir eventos táctiles a través del mecanismo de control de eventos de Windows Runtime.
Nota:
Este documento utiliza "táctil" para referirse tanto a la entrada táctil como a la del ratón, y "puntero" para referirse a cualquier dispositivo que use eventos de puntero. Dado que la función táctil y el mouse usan eventos de puntero estándar, puedes usar cualquiera de los dispositivos para seleccionar elementos de menú y controlar el juego.
Nota:
El manifiesto del paquete establece Landscape como la única rotación admitida para el juego para evitar que la orientación cambie al girar el dispositivo para rodar la canica. Para ver el manifiesto del paquete, abra package.appxmanifest en el explorador de soluciones de en Visual Studio.
Inicialización de dispositivos de entrada
El controlador de juego no requiere inicialización. Para inicializar la función táctil, debe registrarse para los eventos de ventana gráfica, como cuando se activa el puntero (por ejemplo, el reproductor presiona el botón del mouse o toca la pantalla), se libera y se mueve. Para inicializar el acelerómetro, debe crear un objeto Windows::Devices::Sensors::Accelerometer al inicializar la aplicación.
En el ejemplo siguiente se muestra cómo el método App::SetWindow se registra para los eventos de puntero Windows::UI::Core::CoreWindow::PointerPressed, Windows::UI::Core::CoreWindow::PointerReleasedy Windows::UI::Core::CoreWindow::PointerMoved. Estos eventos se registran durante la inicialización de la aplicación y antes del bucle del juego.
Estos eventos se controlan en un subproceso independiente que invoca a los controladores de eventos.
Para obtener más información sobre cómo se inicializa la aplicación, consulte estructura de aplicaciones de Marble Maze.
window->PointerPressed += ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(
this,
&App::OnPointerPressed);
window->PointerReleased += ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(
this,
&App::OnPointerReleased);
window->PointerMoved += ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(
this,
&App::OnPointerMoved);
La clase MarbleMazeMain también crea un objeto std::map para contener eventos táctiles. La clave de este objeto de mapa es un valor que identifica de forma única el puntero de entrada. Cada tecla se asigna a la distancia entre cada punto táctil y el centro de la pantalla. Marble Maze usa estos valores más adelante para calcular la cantidad por la que se inclina el laberinto.
typedef std::map<int, XMFLOAT2> TouchMap;
TouchMap m_touches;
La clase MarbleMazeMain también contiene un objeto Acelerómetro.
Windows::Devices::Sensors::Accelerometer^ m_accelerometer;
El objeto acelerómetro
// Returns accelerometer ref if there is one; nullptr otherwise.
m_accelerometer = Windows::Devices::Sensors::Accelerometer::GetDefault();
Navegar por los menús
Puedes usar el mouse, el toque o un controlador de juego para navegar por los menús, como se indica a continuación:
- Use el panel direccional para cambiar el elemento de menú activo.
- Use la función táctil, el botón A o el botón Menú para seleccionar un elemento de menú o cerrar el menú actual, como la tabla de puntuación alta.
- Usa el botón Menú para pausar o reanudar el juego.
- Haga clic en un elemento de menú con el mouse para elegir esa acción.
Seguimiento de la entrada del controlador de juego
Para realizar un seguimiento de los mandos de juego conectados actualmente al dispositivo, MarbleMazeMain define una variable miembro, m_myGamepads, que es una colección de objetos Windows::Gaming::Input::Gamepad. Esto se inicializa en el constructor de la siguiente manera:
m_myGamepads = ref new Vector<Gamepad^>();
for (auto gamepad : Gamepad::Gamepads)
{
m_myGamepads->Append(gamepad);
}
Además, el constructor
Gamepad::GamepadAdded +=
ref new EventHandler<Gamepad^>([=](Platform::Object^, Gamepad^ args)
{
m_myGamepads->Append(args);
m_currentGamepadNeedsRefresh = true;
});
Gamepad::GamepadRemoved +=
ref new EventHandler<Gamepad ^>([=](Platform::Object^, Gamepad^ args)
{
unsigned int indexRemoved;
if (m_myGamepads->IndexOf(args, &indexRemoved))
{
m_myGamepads->RemoveAt(indexRemoved);
m_currentGamepadNeedsRefresh = true;
}
});
Cuando se agrega un controlador para juegos, se agrega a m_myGamepads; cuando se quita un controlador para juegos, comprobamos si el controlador para juegos está en m_myGamepads, y si es así, lo quitamos. En ambos casos, establecemos m_currentGamepadNeedsRefresh en true, lo que indica que necesitamos reasignar m_gamepad.
Por último, asignamos un controlador para juegos a m_gamepad y establecemos m_currentGamepadNeedsRefresh en false:
m_gamepad = GetLastGamepad();
m_currentGamepadNeedsRefresh = false;
En el método Update, verificamos si m_gamepad necesita ser reasignado:
if (m_currentGamepadNeedsRefresh)
{
auto mostRecentGamepad = GetLastGamepad();
if (m_gamepad != mostRecentGamepad)
{
m_gamepad = mostRecentGamepad;
}
m_currentGamepadNeedsRefresh = false;
}
Si m_gamepad necesita reasignarse, se le asigna el controlador para juegos agregado más recientemente, con GetLastGamepad, que se define de la siguiente manera:
Gamepad^ MarbleMaze::MarbleMazeMain::GetLastGamepad()
{
Gamepad^ gamepad = nullptr;
if (m_myGamepads->Size > 0)
{
gamepad = m_myGamepads->GetAt(m_myGamepads->Size - 1);
}
return gamepad;
}
Este método simplemente devuelve el último gamepad en m_myGamepads.
Puedes conectar hasta cuatro controladores de juego a un dispositivo Windows 10. Para evitar tener que averiguar qué controlador es el activo, simplemente solo realizamos un seguimiento del controlador para juegos agregado más recientemente. Si tu juego admite más de un jugador, tienes que realizar un seguimiento de la entrada de cada jugador por separado.
El método MarbleMazeMain::Update sondea el controlador para juegos para la entrada:
if (m_gamepad != nullptr)
{
m_oldReading = m_newReading;
m_newReading = m_gamepad->GetCurrentReading();
}
Realizamos un seguimiento de la lectura de entrada que obtuvimos en el último fotograma con m_oldReadingy la lectura de entrada más reciente con m_newReading, que obtenemos llamando a Gamepad::GetCurrentReading. Esto devuelve un objeto GamepadReading, que contiene información sobre el estado actual del controlador para juegos.
Para comprobar si un botón ha sido presionado o liberado recientemente, definimos MarbleMazeMain::ButtonJustPressed y MarbleMazeMain::ButtonJustReleased, que comparan las lecturas de botón de este fotograma y el último fotograma. De este modo, podemos realizar una acción solo en el momento en que se presiona o suelta inicialmente un botón, y no cuando se mantiene presionado:
bool MarbleMaze::MarbleMazeMain::ButtonJustPressed(GamepadButtons selection)
{
bool newSelectionPressed = (selection == (m_newReading.Buttons & selection));
bool oldSelectionPressed = (selection == (m_oldReading.Buttons & selection));
return newSelectionPressed && !oldSelectionPressed;
}
bool MarbleMaze::MarbleMazeMain::ButtonJustReleased(GamepadButtons selection)
{
bool newSelectionReleased =
(GamepadButtons::None == (m_newReading.Buttons & selection));
bool oldSelectionReleased =
(GamepadButtons::None == (m_oldReading.Buttons & selection));
return newSelectionReleased && !oldSelectionReleased;
}
Las lecturas de GamepadButtons se comparan mediante operaciones bit a bit: comprobamos si se presiona un botón mediante AND bitwise (&). Determinamos si un botón se ha presionado o liberado comparando la lectura antigua y la nueva lectura.
Con los métodos anteriores, se comprueba si se han presionado determinados botones y se realizan las acciones correspondientes que deben producirse. Por ejemplo, cuando se presiona el botón Menú (GamepadButtons::Menu), el estado del juego cambia de activo a pausado o en pausa a activo.
if (ButtonJustPressed(GamepadButtons::Menu) || m_pauseKeyPressed)
{
m_pauseKeyPressed = false;
if (m_gameState == GameState::InGameActive)
{
SetGameState(GameState::InGamePaused);
}
else if (m_gameState == GameState::InGamePaused)
{
SetGameState(GameState::InGameActive);
}
}
También comprobamos si el jugador presiona el botón Ver, en cuyo caso reiniciamos el juego o borramos la tabla de puntuación alta:
if (ButtonJustPressed(GamepadButtons::View) || m_homeKeyPressed)
{
m_homeKeyPressed = false;
if (m_gameState == GameState::InGameActive ||
m_gameState == GameState::InGamePaused ||
m_gameState == GameState::PreGameCountdown)
{
SetGameState(GameState::MainMenu);
m_inGameStopwatchTimer.SetVisible(false);
m_preGameCountdownTimer.SetVisible(false);
}
else if (m_gameState == GameState::HighScoreDisplay)
{
m_highScoreTable.Reset();
}
}
Si el menú principal está activo, el elemento de menú activo cambia cuando se presiona el panel direccional hacia arriba o hacia abajo. Si el usuario elige la selección actual, el elemento de interfaz de usuario adecuado se marca como elegido.
// Handle menu navigation.
bool chooseSelection =
(ButtonJustPressed(GamepadButtons::A)
|| ButtonJustPressed(GamepadButtons::Menu));
bool moveUp = ButtonJustPressed(GamepadButtons::DPadUp);
bool moveDown = ButtonJustPressed(GamepadButtons::DPadDown);
switch (m_gameState)
{
case GameState::MainMenu:
if (chooseSelection)
{
m_audio.PlaySoundEffect(MenuSelectedEvent);
if (m_startGameButton.GetSelected())
{
m_startGameButton.SetPressed(true);
}
if (m_highScoreButton.GetSelected())
{
m_highScoreButton.SetPressed(true);
}
}
if (moveUp || moveDown)
{
m_startGameButton.SetSelected(!m_startGameButton.GetSelected());
m_highScoreButton.SetSelected(!m_startGameButton.GetSelected());
m_audio.PlaySoundEffect(MenuChangeEvent);
}
break;
case GameState::HighScoreDisplay:
if (chooseSelection || anyPoints)
{
SetGameState(GameState::MainMenu);
}
break;
case GameState::PostGameResults:
if (chooseSelection || anyPoints)
{
SetGameState(GameState::HighScoreDisplay);
}
break;
case GameState::InGamePaused:
if (m_pausedText.IsPressed())
{
m_pausedText.SetPressed(false);
SetGameState(GameState::InGameActive);
}
break;
}
Seguimiento de la entrada táctil y del mouse
Para la entrada táctil y del mouse, se elige un elemento de menú cuando el usuario toca o hace clic en él. En el ejemplo siguiente se muestra cómo el método MarbleMazeMain::Update procesa la entrada del puntero para seleccionar elementos de menú. La variable miembro m_pointQueue registra las ubicaciones en las que el usuario tocó o hizo clic en la pantalla. La forma en que Marble Maze recopila la entrada del puntero se describe con más detalle más adelante en este documento en la sección Entrada de puntero de procesamiento.
// Check whether the user chose a button from the UI.
bool anyPoints = !m_pointQueue.empty();
while (!m_pointQueue.empty())
{
UserInterface::GetInstance().HitTest(m_pointQueue.front());
m_pointQueue.pop();
}
El método UserInterface::HitTest determina si el punto proporcionado se encuentra en los límites de cualquier elemento de interfaz de usuario. Los elementos de la interfaz de usuario que superen esta prueba se marcan como táctiles. Este método usa la función auxiliar de PointInRect para determinar si el punto proporcionado se encuentra en los límites de cada elemento de la interfaz de usuario.
void UserInterface::HitTest(D2D1_POINT_2F point)
{
for (auto iter = m_elements.begin(); iter != m_elements.end(); ++iter)
{
if (!(*iter)->IsVisible())
continue;
TextButton* textButton = dynamic_cast<TextButton*>(*iter);
if (textButton != nullptr)
{
D2D1_RECT_F bounds = (*iter)->GetBounds();
textButton->SetPressed(PointInRect(point, bounds));
}
}
}
Actualización del estado del juego
Cuando el método MarbleMazeMain::Update procesa la entrada del controlador y táctil, actualiza el estado del juego si se ha presionado algún botón.
// Update the game state if the user chose a menu option.
if (m_startGameButton.IsPressed())
{
SetGameState(GameState::PreGameCountdown);
m_startGameButton.SetPressed(false);
}
if (m_highScoreButton.IsPressed())
{
SetGameState(GameState::HighScoreDisplay);
m_highScoreButton.SetPressed(false);
}
Controlar el juego
El bucle del juego y el método MarbleMazeMain::Update funcionan juntos para actualizar el estado de los objetos del juego. Si el juego acepta la entrada de varios dispositivos, puedes acumular la entrada de todos los dispositivos en un conjunto de variables para que puedas escribir código que sea más fácil de mantener. El método MarbleMazeMain::Update define un conjunto de variables que acumula el movimiento de todos los dispositivos.
float combinedTiltX = 0.0f;
float combinedTiltY = 0.0f;
El mecanismo de entrada puede variar de un dispositivo de entrada a otro. Por ejemplo, la entrada del puntero se controla mediante el modelo de control de eventos de Windows Runtime. Por el contrario, sondeas los datos de entrada del controlador de juego cuando los necesitas. Se recomienda seguir siempre el mecanismo de entrada que se prescribe para un dispositivo determinado. En esta sección se describe cómo Marble Maze lee la entrada de cada dispositivo, cómo actualiza los valores de entrada combinados y cómo usa los valores de entrada combinados para actualizar el estado del juego.
Procesamiento de la entrada del puntero
Al trabajar con la entrada de punteros, llame al método Windows::UI::Core::CoreDispatcher::ProcessEvents para procesar eventos de ventana. Llama a este método en el bucle del juego antes de actualizar o representar la escena. Marble Maze llama a esto en el método App::Run:
while (!m_windowClosed)
{
if (m_windowVisible)
{
CoreWindow::GetForCurrentThread()->
Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);
m_main->Update();
if (m_main->Render())
{
m_deviceResources->Present();
}
}
else
{
CoreWindow::GetForCurrentThread()->
Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
}
}
Si la ventana está visible, pasamos CoreProcessEventsOption::ProcessAllIfPresent a ProcessEvents para procesar todos los eventos en cola y regresar inmediatamente; de lo contrario, pasamos CoreProcessEventsOption::ProcessOneAndAllPending para procesar todos los eventos en cola y esperar al siguiente evento nuevo. Una vez procesados los eventos, Marble Maze representa y presenta el siguiente fotograma.
Windows Runtime llama al controlador registrado para cada evento que se produjo. El método App::SetWindow se registra para eventos y reenvía información de puntero a la clase MarbleMazeMain.
void App::OnPointerPressed(
Windows::UI::Core::CoreWindow^ sender,
Windows::UI::Core::PointerEventArgs^ args)
{
m_main->AddTouch(args->CurrentPoint->PointerId, args->CurrentPoint->Position);
}
void App::OnPointerReleased(
Windows::UI::Core::CoreWindow^ sender,
Windows::UI::Core::PointerEventArgs^ args)
{
m_main->RemoveTouch(args->CurrentPoint->PointerId);
}
void App::OnPointerMoved(
Windows::UI::Core::CoreWindow^ sender,
Windows::UI::Core::PointerEventArgs^ args)
{
m_main->UpdateTouch(args->CurrentPoint->PointerId, args->CurrentPoint->Position);
}
La MarbleMazeMain clase reacciona a los eventos de puntero actualizando el objeto de mapa que contiene eventos táctiles. Se llama al método MarbleMazeMain::AddTouch cuando se presiona por primera vez el puntero, por ejemplo, cuando el usuario toca inicialmente la pantalla en un dispositivo táctil. Se llama al método MarbleMazeMain::UpdateTouch cuando se mueve la posición del puntero. El método MarbleMazeMain::RemoveTouch se llama cuando se libera el puntero, por ejemplo, cuando el usuario deja de tocar la pantalla.
void MarbleMazeMain::AddTouch(int id, Windows::Foundation::Point point)
{
m_touches[id] = PointToTouch(point, m_deviceResources->GetLogicalSize());
m_pointQueue.push(D2D1::Point2F(point.X, point.Y));
}
void MarbleMazeMain::UpdateTouch(int id, Windows::Foundation::Point point)
{
if (m_touches.find(id) != m_touches.end())
m_touches[id] = PointToTouch(point, m_deviceResources->GetLogicalSize());
}
void MarbleMazeMain::RemoveTouch(int id)
{
m_touches.erase(id);
}
La función PointToTouch traduce la posición actual del puntero para que el origen esté en el centro de la pantalla y, a continuación, escale las coordenadas para que oscilan aproximadamente entre -1.0 y +1.0. Esto facilita el cálculo de la inclinación del laberinto de forma coherente en distintos métodos de entrada.
inline XMFLOAT2 PointToTouch(Windows::Foundation::Point point, Windows::Foundation::Size bounds)
{
float touchRadius = min(bounds.Width, bounds.Height);
float dx = (point.X - (bounds.Width / 2.0f)) / touchRadius;
float dy = ((bounds.Height / 2.0f) - point.Y) / touchRadius;
return XMFLOAT2(dx, dy);
}
El método MarbleMazeMain::Update actualiza los valores de entrada combinados incrementando el factor de inclinación por un valor de escalado constante. Este valor de escalado se determinó experimentando con varios valores diferentes.
// Account for touch input.
for (TouchMap::const_iterator iter = m_touches.cbegin();
iter != m_touches.cend();
++iter)
{
combinedTiltX += iter->second.x * m_touchScaleFactor;
combinedTiltY += iter->second.y * m_touchScaleFactor;
}
Procesamiento de la entrada del acelerómetro
Para procesar la entrada del acelerómetro, el método MarbleMazeMain::Update llama al método Windows::Devices::Sensors::Accelerometer::GetCurrentReading. Este método devuelve un objeto Windows::Devices::Sensors::AccelerometerReading, que representa una lectura del acelerómetro. Las Windows::Devices::Sensors::AccelerometerReading::AccelerationX y Windows::Devices::Sensors::AccelerometerReading::AccelerationY contienen la aceleración de fuerza-G a lo largo de los ejes X e Y, respectivamente.
En el ejemplo siguiente se muestra cómo el método MarbleMazeMain::Update sondea el acelerómetro y actualiza los valores de entrada combinados. Al inclinar el dispositivo, la gravedad hace que la canica se mueva más rápido.
// Account for sensors.
if (m_accelerometer != nullptr)
{
Windows::Devices::Sensors::AccelerometerReading^ reading =
m_accelerometer->GetCurrentReading();
if (reading != nullptr)
{
combinedTiltX +=
static_cast<float>(reading->AccelerationX) * m_accelerometerScaleFactor;
combinedTiltY +=
static_cast<float>(reading->AccelerationY) * m_accelerometerScaleFactor;
}
}
Dado que no puede estar seguro de que un acelerómetro está presente en el equipo del usuario, asegúrese siempre de tener un acelerómetro válido objeto antes de sondear el acelerómetro.
Procesamiento de la entrada del controlador de juego
En el método MarbleMazeMain::Update, usamos m_newReading para procesar la entrada desde el stick analógico izquierdo:
float leftStickX = static_cast<float>(m_newReading.LeftThumbstickX);
float leftStickY = static_cast<float>(m_newReading.LeftThumbstickY);
auto oppositeSquared = leftStickY * leftStickY;
auto adjacentSquared = leftStickX * leftStickX;
if ((oppositeSquared + adjacentSquared) > m_deadzoneSquared)
{
combinedTiltX += leftStickX * m_controllerScaleFactor;
combinedTiltY += leftStickY * m_controllerScaleFactor;
}
Comprobamos si la entrada del stick analógico izquierdo está fuera de la zona muerta y, si es así, la agregamos a combinedTiltX y combinedTiltY (multiplicado por un factor de escala) para inclinar el escenario.
Importante
Cuando trabajas con un controlador de juego, siempre tienes en cuenta la zona muerta. La zona muerta hace referencia a la varianza entre los controladores para juegos en su sensibilidad al movimiento inicial. En algunos controladores, un pequeño movimiento puede no generar lectura, pero en otros puede generar una lectura medible. Para tener en cuenta esto en tu juego, crea una zona de no movimiento para el movimiento inicial del stick digital. Para obtener más información sobre la zona muerta, vea Lectura de las palancas de mando.
Aplicación de la entrada al estado del juego
Los dispositivos notifican valores de entrada de diferentes maneras. Por ejemplo, la entrada del puntero podría estar en coordenadas de pantalla y la entrada del controlador podría estar en un formato completamente diferente. Un desafío con la combinación de la entrada de varios dispositivos en un conjunto de valores de entrada es la normalización o la conversión de valores a un formato común. Marble Maze normaliza los valores al escalarlos al intervalo [-1.0, 1.0]. La función PointToTouch, que se ha descrito anteriormente en esta sección, convierte las coordenadas de pantalla en valores normalizados que oscilan aproximadamente entre -1.0 y +1.0.
Sugerencia
Incluso si la aplicación usa un método de entrada, se recomienda normalizar siempre los valores de entrada. Si lo hace, puede simplificar la interpretación de la entrada por otros componentes del juego, como la simulación física, y facilita la escritura de juegos que funcionan en diferentes resoluciones de pantalla.
Una vez que el método MarbleMazeMain::Update procesa la entrada, crea un vector que representa el efecto de la inclinación del laberinto en la canica. En el ejemplo siguiente se muestra cómo Marble Maze usa la función XMVector3Normalize
const float maxTilt = 1.0f / 8.0f;
XMVECTOR gravity = XMVectorSet(
combinedTiltX * maxTilt,
combinedTiltY * maxTilt,
1.0f,
0.0f);
gravity = XMVector3Normalize(gravity);
Para completar la actualización de los objetos de escena, Marble Maze transfiere el vector de gravedad actualizado a la simulación física, actualiza esta simulación durante el tiempo transcurrido desde el fotograma anterior y ajusta la posición y orientación de la canica. Si la canica ha caído a través del laberinto, el método MarbleMazeMain::Update coloca la canica en el último punto de control que tocó la canica y restablece el estado de la simulación física.
XMFLOAT3A g;
XMStoreFloat3(&g, gravity);
m_physics.SetGravity(g);
if (m_gameState == GameState::InGameActive)
{
// Only update physics when gameplay is active.
m_physics.UpdatePhysicsSimulation(static_cast<float>(m_timer.GetElapsedSeconds()));
// ...Code omitted for simplicity...
}
// ...Code omitted for simplicity...
// Check whether the marble fell off of the maze.
const float fadeOutDepth = 0.0f;
const float resetDepth = 80.0f;
if (marblePosition.z >= fadeOutDepth)
{
m_targetLightStrength = 0.0f;
}
if (marblePosition.z >= resetDepth)
{
// Reset marble.
memcpy(&marblePosition, &m_checkpoints[m_currentCheckpoint], sizeof(XMFLOAT3));
oldMarblePosition = marblePosition;
m_physics.SetPosition((const XMFLOAT3&)marblePosition);
m_physics.SetVelocity(XMFLOAT3(0, 0, 0));
m_lightStrength = 0.0f;
m_targetLightStrength = 1.0f;
m_resetCamera = true;
m_resetMarbleRotation = true;
m_audio.PlaySoundEffect(FallingEvent);
}
En esta sección no se describe cómo funciona la simulación física. Para obtener más información sobre esto, consulte Physics.h y Physics.cpp en los orígenes de Marble Maze.
Pasos siguientes
Lea el artículo Agregando audio al ejemplo de Marble Maze para obtener información sobre algunas de las prácticas clave a tener en cuenta al trabajar con audio. En el documento se describe cómo Marble Maze usa Microsoft Media Foundation y XAudio2 para cargar, mezclar y reproducir recursos de audio.
Temas relacionados
- Agregar audio a la muestra Marble Maze
- Agregar contenido visual al ejemplo Marble Maze
- desarrollar Marble Maze, un juego para UWP en C++ y DirectX