A 几天前 我们讨论了如何使用VisualStudio3D初学者工具包创建一个简单的掷骰子应用程序。 现在我们将通过添加一些动画,使应用程序更进一步。 如果你需要跟上, 这里有一个链接 上一篇博文。
让它滚吧,宝贝,滚吧
为了使图形应用程序中的任何对象移动,步骤始终相同:
- 设置一些状态(例如布尔标志)以指示动画应该正在运行。如果需要,这也是保存动画的开始位置和时间的好时机。
- 在Update方法(在渲染前的每一帧上调用)中,计算对象的位置/旋转/缩放(在3D语言中, 变换 )使用动画开始和当前帧时间之间的时间。还必须确保在动画结束时正确更新状态以停止动画。
- 在“渲染方法”(Render method)中,确保将变换正确应用于每个对象。
在本例中,我们将添加一个名为RollDie()的新方法,该方法将设置状态并节省启动时间。我们将使用此方法来计算每个模具滚动结果,但现在让我们只执行一个动画,将模具从1旋转到6。
要创建这个动画,我们需要一组变量来跟踪立方体的变换以及动画时间。我们将创建一个布尔值来表示动画正在运行,一个浮点值来存储动画时间,三个向量来存储立方体的初始、最终和当前旋转。这些向量应为XMFLOAT3类型,以存储偏航、俯仰和滚转旋转(参见下图)。
因此,让我们创建这些字段和RollDie()方法。在Game.h中添加以下代码:
ref class Game sealed : public GameBase{ // (snip) other class declarations... Platform::String^ OnHitObject(int x, int y); void RollDie();
private: std::vector<VSD3DStarter::Mesh*> m_meshModels; bool m_isAnimationRunning; float m_animationTime; DirectX::XMFLOAT3 m_initialRotation; DirectX::XMFLOAT3 m_currentRotation; DirectX::XMFLOAT3 m_targetRotation;};
现在,让我们从RollDie()实现开始创建动画。将以下代码添加到Game.cpp:
void Game::RollDie(){ m_initialRotation = m_currentRotation; m_targetRotation = XMFLOAT3(0.0f, XM_PI, 0.0f); // always rotate to 6 m_animationTime = 0.0f; m_isAnimationRunning = true;}
RollDie()将通过将mu isAnimationRunning变量设置为true来启动动画。我们需要在Update()方法中添加一些代码来在每一帧旋转立方体。代码如下所示:
void Game::Update(float timeTotal, float timeDelta){ if (m_isAnimationRunning) { m_animationTime += timeDelta; static const float animationDuration = 0.5f; float animationProgress = std::min<float>(m_animationTime / animationDuration, 1.0f); XMVECTOR initial = XMLoadFloat3(&m_initialRotation); XMVECTOR target = XMLoadFloat3(&m_targetRotation); XMVECTOR current = initial + animationProgress * (target - initial); XMStoreFloat3(&m_currentRotation, current); if (animationProgress >= 1.0f) m_isAnimationRunning = false; }}
分解此代码:
- 仅当设置了mu isAnimationRunning标志时,才应执行动画。
- 首先计算动画开始后的时间(从0到0.5秒)和动画进度(从0到1),即已完成动画的百分比。请注意,进度限制为1.0。
- 将初始和目标旋转矢量加载到XMVECTORs中,通过使用 CPU内部函数 .
- 使用线性公式执行计算:
当前=初始+进度*(目标-初始)
- 将最终计算值存储回m泳currentRotation。
- 检查动画是否完成,必要时停止。
动画中的下一步是使用渲染方法中计算的旋转来旋转立方体。为此,请更改Render()方法中的以下代码行:
void Game::Render(){ GameBase::Render(); // (snip) clear... XMMATRIX transform = XMMatrixRotationRollPitchYawFromVector(XMLoadFloat3(&m_currentRotation)); for (UINT i = 0; i < m_meshModels.size(); i++) { m_meshModels[i]->Render(m_graphics, transform); } // (snip) MSAA...}
我们调用的方法获取包含横摇、俯仰和偏航的XMVECTOR,并返回与组合变换相对应的旋转矩阵。如果愿意,还可以使用xmmatrixrotationrollpoitchyaw方法,该方法分别获取横摇、俯仰和偏航分量。您甚至可以使用方法来计算每个组件,然后自己将它们相乘–要获得生成变换矩阵的方法的完整列表,请选中此项 MSDN文档页 .
我们现在需要做的就是调用RollDie()方法。让我们在用户点击屏幕时调用它。在DirectXPage::OnTapped方法中将此代码添加到DirectXPage.xaml.cpp文件中:
void DirectXPage::OnTapped(Platform::Object^ sender, TappedRoutedEventArgs^ e){ m_renderer->RollDie();}
如果你现在运行应用程序并点击/点击任何地方,骰子将从1掷到6!
应用程序逻辑:随机数生成器
如果你知道会得到哪个数字,掷骰子就没什么意思了。我们必须使用C运行时库函数添加一个随机数生成器 兰德() ,所以骰子每掷一次都会显示一个不同的数字。
要使用rand(),第一步是使用一些种子值初始化它。如果我们不使用任何值初始化它或使用固定值初始化它,那么每次运行应用程序时生成的数字序列都是相同的,这不是我们希望发生的。在本教程中,我们将使用当前的CPU时间为随机数生成器播种。将以下代码添加到游戏::Initialize()代码:
void Game::Initialize(){ Mesh::LoadFromFile(m_graphics, L"die.cmo", L"", L"", m_meshModels); srand ((unsigned int) time(NULL));}
您还需要在Game.cpp的开头包含
#include "pch.h"#include "Game.h"#include <DirectXMath.h>#include <DirectXColors.h>#include <algorithm>#include <time.h>
初始化之后,我们可以使用RollDie()方法中的rand()函数来计算滚动。我们还需要为每个辊正确设置目标模具旋转。为此,替换设置mu targetRotation的行,以便RollDie()方法 看起来像这样:
void Game::RollDie(){ m_initialRotation = m_currentRotation; int currentRoll = rand() % 6 + 1; switch (currentRoll) { case 1: m_targetRotation = XMFLOAT3(0.0f, 0.0f, 0.0f); break; case 2: m_targetRotation = XMFLOAT3(0.0f, XM_PIDIV2, 0.0f); break; case 3: m_targetRotation = XMFLOAT3(XM_PIDIV2, 0.0f, 0.0f); break; case 4: m_targetRotation = XMFLOAT3(-1.0f * XM_PIDIV2, 0.0f, 0.0f); break; case 5: m_targetRotation = XMFLOAT3(0.0f, -1.0f * XM_PIDIV2, 0.0f); break; case 6: m_targetRotation = XMFLOAT3(0.0f, XM_PI, 0.0f); break; } m_animationTime = 0.0f; m_isAnimationRunning = true;}
现在,如果你运行应用程序,我们有我们的动画死亡!你可以在屏幕上的任何地方轻敲来滚动模具。您还可以检查它在捕捉视图上的外观。
让它跳起来!
这个小程序已经很有用了,但是现在我们可以让它变得更有趣。本节将演示如何添加一些天赋,使模具跳跃时,滚动,并添加一些额外的随机旋转看起来更像一个真正的模具。
要添加“跳跃”,我们将向动画中添加另一个变量–垂直(Y)坐标。我们将在骰子滚动时(在Update()方法中)使该值随时间而改变,并将Render()方法更改为将转换应用于立方体。第一步是将转换变量添加到Game.h:
ref class Game sealed : public GameBase{ // (snip) other class declarations...private: std::vector<VSD3DStarter::Mesh*> m_meshModels; bool m_isAnimationRunning; float m_animationTime; DirectX::XMFLOAT3 m_initialRotation; DirectX::XMFLOAT3 m_currentRotation; DirectX::XMFLOAT3 m_targetRotation; float m_currentTranslationY;};
然后,在Update()方法上使值随时间而改变:
void Game::Update(float timeTotal, float timeDelta){ if (m_isAnimationRunning) { // (snip) XMVECTOR current = initial + animationProgress * (target - initial); XMStoreFloat3(&m_currentRotation, current); const float maxHeight = 2.0f; m_currentTranslationY = 4.0f * maxHeight * animationProgress * (1 - animationProgress); if (animationProgress >= 1.0f) m_isAnimationRunning = false; }}
对于那些记得高中数学课的人来说,这个函数是一个抛物线方程,极点在0和1,顶点在(0.5,maxHeight)。这将使我们的立方体上下与现实的效果。 您可以在下面的位置看到此函数的图形 新型网络搜索引擎 .
最后一步是在渲染阶段使用计算出的平移来更改立方体的变换。为此,请向Render方法添加以下代码:
void Game::Render(){ // (snip) clear... m_d3dContext->ClearDepthStencilView( /* (snip) */ ); XMMATRIX transform = XMMatrixRotationRollPitchYawFromVector(XMLoadFloat3(&m_currentRotation)); transform *= XMMatrixTranslation(0.0f, m_currentTranslationY, 0.0f); for (UINT i = 0; i < m_meshModels.size(); i++) { m_meshModels[i]->Render(m_graphics, transform); } // (snip) MSAA...}
现在运行应用程序,点击屏幕将产生一个很好的动画与跳跃模具。最后一步,使它看起来更好是添加一些随机旋转每轴每当模具是滚动。为此,我们只需要更改RollDie()方法,以向目标旋转添加一些随机旋转:
void Game::RollDie(){ // (snip) initialization, roll and switch(currentRoll) XMVECTOR target = XMLoadFloat3(&m_targetRotation); XMVECTOR current = XMLoadFloat3(&m_currentRotation); // account for current rotation target += XMVectorFloor(current / XM_2PI) * XM_2PI; // add -1, 0 or 1 extra spins XMVECTOR randomVector = XMLoadFloat3(&XMFLOAT3(rand() % 3 - 1.0f, rand() % 3 - 1.0f, rand() % 3 - 1.0f)); target += randomVector * XM_2PI; XMStoreFloat3(&m_targetRotation, target); m_animationTime = 0.0f; m_isAnimationRunning = true;}
此代码对目标旋转执行两个更改:
- 将迄今为止的完整旋转总数添加到目标旋转,使每个旋转相对于上一个旋转,而不是相对于模具的初始状态。
- 向目标相对旋转添加-1、0或1个额外的完整旋转。为了做到这一点,我们创建了一个向量,其中三个随机值选择在-1,0和1之间,我们将其乘以2*PI(以弧度为单位的完整旋转)。
还要注意的是,我们再次使用XMVECTORs来加快计算速度,并且能够只编写一个公式,而不是分别为x、y和z分量编写一个公式。
这就完成了我们的应用程序功能。它绝对是经典随机数生成器的一个更有用、更有趣的版本。您可以在上下载此版本 码丛 (下面的互动程序上有直接下载链接)。
我们只有几个步骤来完成,以使这个应用程序运行在WindowsRT和WindowsPhone以及! 我们将在第三篇也是最后一篇博文中介绍这些步骤。 敬请期待!