Windows游戏开发始于最基本的窗口程序创建。这个看似简单的框架实际上包含了Windows应用程序的核心运行机制。让我们深入分析每个关键组件的工作原理:
cpp复制#include <Windows.h>
// 窗口过程函数声明
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
窗口过程函数是Windows程序的心脏,它负责处理所有发送到窗口的消息。在游戏开发中,我们需要特别关注以下几种消息类型:
窗口类的注册是创建窗口前的必要步骤:
cpp复制WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc; // 关键:指定消息处理函数
wc.hInstance = hInstance; // 当前程序实例
wc.lpszClassName = CLASS_NAME; // 窗口类名标识
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // 设置默认光标
实际开发中建议将窗口类名定义为常量字符串,避免多处硬编码导致维护困难。窗口样式(WS_OVERLAPPEDWINDOW)可以根据游戏需求调整,全屏游戏通常会使用WS_POPUP样式。
Windows程序的核心是消息驱动机制,游戏循环需要与之巧妙结合:
cpp复制MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg); // 转换键盘消息
DispatchMessage(&msg); // 分发到窗口过程
}
传统游戏循环与Windows消息循环的融合是个关键问题。现代游戏通常采用以下两种模式:
cpp复制while (true)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
UpdateGameLogic(); // 游戏逻辑更新
RenderFrame(); // 渲染帧
}
}
cpp复制// 消息处理线程
std::thread messageThread([&](){
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
gameRunning = false;
});
// 游戏主循环线程
while (gameRunning)
{
UpdateGameLogic();
RenderFrame();
std::this_thread::sleep_for(std::chrono::milliseconds(16));
}
messageThread.join();
Direct2D是Windows平台高效的2D图形API,其初始化过程需要严格遵循特定步骤:
cpp复制// 1. 创建D2D工厂
D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
&d2dFactory
);
// 2. 创建渲染目标
d2dFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(
windowHandle,
D2D1::SizeU(width, height)
),
&renderTarget
);
// 3. 创建画刷
renderTarget->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::Blue),
&brush
);
实际项目中应该检查每个步骤的HRESULT返回值,失败时需要进行适当的错误处理。工厂对象建议使用单例模式管理,避免重复创建。
Direct2D渲染性能直接影响游戏流畅度,以下是几个关键优化点:
cpp复制renderTarget->BeginDraw();
// 集中所有绘制操作
renderTarget->DrawRectangle(...);
renderTarget->FillEllipse(...);
renderTarget->DrawText(...);
renderTarget->EndDraw(); // 实际提交绘制命令
cpp复制renderTarget->CreateLayer(&layer);
renderTarget->PushLayer(
D2D1::LayerParameters(rect),
layer
);
// 绘制需要应用特效的内容
renderTarget->PopLayer();
cpp复制// 预先创建并缓存文字格式对象
writeFactory->CreateTextFormat(
L"微软雅黑", // 使用系统字体
NULL,
DWRITE_FONT_WEIGHT_NORMAL,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
24.0f,
L"zh-cn",
&textFormat
);
// 设置文字抗锯齿
renderTarget->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);
现代Windows游戏主要基于Direct3D构建3D渲染管线,以下是关键初始化步骤:
cpp复制// 1. 创建设备和交换链
D3D11CreateDeviceAndSwapChain(
nullptr,
D3D_DRIVER_TYPE_HARDWARE,
nullptr,
creationFlags,
featureLevels,
featureLevelCount,
D3D11_SDK_VERSION,
&swapChainDesc,
&swapChain,
&d3dDevice,
nullptr,
&deviceContext
);
// 2. 创建渲染目标视图
swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&backBuffer);
d3dDevice->CreateRenderTargetView(backBuffer, nullptr, &renderTargetView);
// 3. 设置视口
D3D11_VIEWPORT viewport;
viewport.Width = (FLOAT)width;
viewport.Height = (FLOAT)height;
deviceContext->RSSetViewports(1, &viewport);
调试版本建议启用D3D11_CREATE_DEVICE_DEBUG标志,可以获取更详细的错误信息。特性级别(D3D_FEATURE_LEVEL)应该从高到低尝试,优先使用最新特性。
专业的游戏循环需要考虑以下几个关键因素:
cpp复制// 高精度计时器
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
LARGE_INTEGER lastTime;
QueryPerformanceCounter(&lastTime);
while (gameRunning)
{
LARGE_INTEGER currentTime;
QueryPerformanceCounter(¤tTime);
float deltaTime = (currentTime.QuadPart - lastTime.QuadPart) / (float)frequency.QuadPart;
lastTime = currentTime;
UpdateGame(deltaTime);
RenderFrame();
}
cpp复制// 渲染线程
std::thread renderThread([&](){
while (gameRunning)
{
std::unique_lock<std::mutex> lock(renderMutex);
renderCondition.wait(lock, [&]{return renderQueue.size() > 0;});
auto renderData = renderQueue.front();
renderQueue.pop();
lock.unlock();
RenderScene(renderData);
}
});
// 主线程
while (gameRunning)
{
UpdateGame(deltaTime);
{
std::lock_guard<std::mutex> guard(renderMutex);
renderQueue.push(gameState);
}
renderCondition.notify_one();
}
cpp复制// 游戏状态栈
std::vector<std::unique_ptr<GameState>> stateStack;
void PushState(std::unique_ptr<GameState> state)
{
if (!stateStack.empty())
stateStack.back()->Pause();
state->Initialize();
stateStack.push_back(std::move(state));
}
void PopState()
{
stateStack.back()->Shutdown();
stateStack.pop_back();
if (!stateStack.empty())
stateStack.back()->Resume();
}
现代游戏引擎普遍采用组件化设计,以下是一个典型实现:
cpp复制class GameObject
{
private:
std::unordered_map<std::type_index, std::shared_ptr<Component>> components;
public:
template <typename T, typename... Args>
std::shared_ptr<T> AddComponent(Args&&... args)
{
auto component = std::make_shared<T>(this, std::forward<Args>(args)...);
components[typeid(T)] = component;
return component;
}
template <typename T>
std::shared_ptr<T> GetComponent()
{
auto it = components.find(typeid(T));
if (it != components.end())
return std::static_pointer_cast<T>(it->second);
return nullptr;
}
void Update(float deltaTime)
{
for (auto& pair : components)
pair.second->Update(deltaTime);
}
};
大规模游戏场景需要高效的空间管理:
cpp复制class QuadTreeNode
{
private:
AABB bounds;
std::vector<GameObject*> objects;
std::unique_ptr<QuadTreeNode> children[4];
public:
void Insert(GameObject* obj)
{
if (!bounds.Contains(obj->GetBounds()))
return;
if (children[0] == nullptr && objects.size() < capacity)
{
objects.push_back(obj);
return;
}
if (children[0] == nullptr)
Split();
for (int i = 0; i < 4; ++i)
children[i]->Insert(obj);
}
void Query(const AABB& range, std::vector<GameObject*>& results)
{
if (!bounds.Intersects(range))
return;
for (auto obj : objects)
if (range.Intersects(obj->GetBounds()))
results.push_back(obj);
if (children[0] != nullptr)
for (int i = 0; i < 4; ++i)
children[i]->Query(range, results);
}
};
cpp复制class SceneGraph
{
private:
std::unordered_map<std::string, std::shared_ptr<GameObject>> objects;
QuadTreeNode spatialPartition;
public:
void AddObject(const std::shared_ptr<GameObject>& obj)
{
objects[obj->GetId()] = obj;
spatialPartition.Insert(obj.get());
}
std::vector<std::shared_ptr<GameObject>> QueryVisibleObjects(const Camera& camera)
{
std::vector<GameObject*> rawResults;
spatialPartition.Query(camera.GetFrustum(), rawResults);
std::vector<std::shared_ptr<GameObject>> results;
for (auto ptr : rawResults)
results.push_back(objects[ptr->GetId()]);
return results;
}
};
使用DXGI帧统计监测渲染性能:
cpp复制// 获取DXGI帧统计
DXGI_FRAME_STATISTICS stats;
swapChain->GetFrameStatistics(&stats);
// 计算实际帧率
static int frameCount = 0;
static float timeAccum = 0;
timeAccum += deltaTime;
frameCount++;
if (timeAccum >= 1.0f)
{
float fps = frameCount / timeAccum;
frameCount = 0;
timeAccum = 0;
// 输出帧率信息
OutputDebugStringA(("FPS: " + std::to_string(fps) + "\n").c_str());
}
cpp复制class GraphicsDevice
{
public:
virtual void Clear(float r, float g, float b, float a) = 0;
virtual void Present() = 0;
virtual std::shared_ptr<Texture> CreateTexture(const std::string& path) = 0;
};
class D3D11Device : public GraphicsDevice
{
// Direct3D 11实现
};
class VulkanDevice : public GraphicsDevice
{
// Vulkan实现
};
class GameEngine
{
private:
std::unique_ptr<GraphicsDevice> graphics;
public:
void Initialize(GraphicsAPI api)
{
switch (api)
{
case GraphicsAPI::Direct3D11:
graphics = std::make_unique<D3D11Device>();
break;
case GraphicsAPI::Vulkan:
graphics = std::make_unique<VulkanDevice>();
break;
}
}
};
cpp复制class InputSystem
{
public:
enum class KeyCode { W, A, S, D, Space, Count };
virtual bool IsKeyDown(KeyCode key) = 0;
virtual bool IsKeyPressed(KeyCode key) = 0;
virtual void GetMousePosition(int& x, int& y) = 0;
};
class Win32Input : public InputSystem
{
private:
bool currentKeys[(int)KeyCode::Count] = {};
bool previousKeys[(int)KeyCode::Count] = {};
public:
void Update()
{
memcpy(previousKeys, currentKeys, sizeof(currentKeys));
currentKeys[(int)KeyCode::W] = (GetAsyncKeyState('W') & 0x8000) != 0;
// 更新其他按键状态...
}
bool IsKeyDown(KeyCode key) override
{
return currentKeys[(int)key];
}
bool IsKeyPressed(KeyCode key) override
{
return currentKeys[(int)key] && !previousKeys[(int)key];
}
};
cpp复制class ResourceManager
{
private:
std::unordered_map<std::string, std::shared_ptr<Resource>> resources;
std::mutex resourceMutex;
std::condition_variable resourceCV;
std::queue<std::function<void()>> loadQueue;
std::vector<std::thread> workerThreads;
bool shutdownRequested = false;
public:
ResourceManager(int workerCount = 4)
{
for (int i = 0; i < workerCount; ++i)
{
workerThreads.emplace_back([this](){
while (true)
{
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(resourceMutex);
resourceCV.wait(lock, [this](){
return !loadQueue.empty() || shutdownRequested;
});
if (shutdownRequested && loadQueue.empty())
return;
task = std::move(loadQueue.front());
loadQueue.pop();
}
task();
}
});
}
}
~ResourceManager()
{
{
std::lock_guard<std::mutex> lock(resourceMutex);
shutdownRequested = true;
}
resourceCV.notify_all();
for (auto& thread : workerThreads)
thread.join();
}
template <typename T>
std::shared_ptr<T> Load(const std::string& path)
{
std::shared_ptr<T> resource;
{
std::lock_guard<std::mutex> lock(resourceMutex);
auto it = resources.find(path);
if (it != resources.end())
return std::dynamic_pointer_cast<T>(it->second);
resource = std::make_shared<T>();
resources[path] = resource;
}
auto loadTask = [=](){
if (resource->LoadFromFile(path))
{
std::lock_guard<std::mutex> lock(resourceMutex);
resource->SetReady(true);
}
};
{
std::lock_guard<std::mutex> lock(resourceMutex);
loadQueue.push(loadTask);
}
resourceCV.notify_one();
return resource;
}
};
cpp复制class ResourceHotReloader
{
private:
std::unordered_map<std::string, std::filesystem::file_time_type> fileTimestamps;
std::vector<std::string> changedFiles;
std::thread watchThread;
bool running = true;
public:
void StartWatching(const std::string& directory)
{
watchThread = std::thread([this, directory](){
while (running)
{
CheckDirectory(directory);
std::this_thread::sleep_for(std::chrono::seconds(1));
}
});
}
void StopWatching()
{
running = false;
if (watchThread.joinable())
watchThread.join();
}
std::vector<std::string> GetChangedFiles()
{
std::vector<std::string> result;
std::swap(result, changedFiles);
return result;
}
private:
void CheckDirectory(const std::string& path)
{
for (const auto& entry : std::filesystem::recursive_directory_iterator(path))
{
if (!entry.is_regular_file())
continue;
const auto& filePath = entry.path().string();
auto lastWrite = entry.last_write_time();
auto it = fileTimestamps.find(filePath);
if (it == fileTimestamps.end())
{
fileTimestamps[filePath] = lastWrite;
}
else if (it->second != lastWrite)
{
it->second = lastWrite;
changedFiles.push_back(filePath);
}
}
}
};
cpp复制class Serializer
{
public:
virtual void Serialize(const std::string& name, int& value) = 0;
virtual void Serialize(const std::string& name, float& value) = 0;
virtual void Serialize(const std::string& name, std::string& value) = 0;
virtual void BeginObject(const std::string& name) = 0;
virtual void EndObject() = 0;
virtual void BeginArray(const std::string& name, size_t& size) = 0;
virtual void EndArray() = 0;
};
class JsonSerializer : public Serializer
{
private:
nlohmann::json& json;
std::vector<nlohmann::json*> stack;
public:
JsonSerializer(nlohmann::json& root) : json(root)
{
stack.push_back(&json);
}
void Serialize(const std::string& name, int& value) override
{
(*stack.back())[name] = value;
}
void BeginObject(const std::string& name) override
{
auto& current = *stack.back();
current[name] = nlohmann::json::object();
stack.push_back(¤t[name]);
}
void EndObject() override
{
stack.pop_back();
}
};
class BinarySerializer : public Serializer
{
private:
std::ostream& stream;
public:
BinarySerializer(std::ostream& out) : stream(out) {}
void Serialize(const std::string& name, int& value) override
{
stream.write(reinterpret_cast<const char*>(&value), sizeof(value));
}
// 其他基本类型序列化...
};
cpp复制class SaveGameManager
{
private:
struct SaveHeader
{
char magic[4] = {'S','A','V','E'};
uint32_t version = CURRENT_SAVE_VERSION;
uint32_t checksum = 0;
uint64_t timestamp = 0;
};
public:
bool Load(const std::string& path, GameState& state)
{
std::ifstream file(path, std::ios::binary);
if (!file)
return false;
SaveHeader header;
file.read(reinterpret_cast<char*>(&header), sizeof(header));
if (memcmp(header.magic, "SAVE", 4) != 0)
return false;
if (header.version > CURRENT_SAVE_VERSION)
return false; // 未来版本不兼容
std::vector<char> buffer(
std::istreambuf_iterator<char>(file),
std::istreambuf_iterator<char>()
);
if (header.version < 2)
{
// 旧版本存档转换逻辑
LegacySaveLoader loader;
return loader.Load(buffer.data(), buffer.size(), state);
}
else
{
// 新版本直接加载
MemoryStream stream(buffer.data(), buffer.size());
BinarySerializer serializer(stream);
state.Serialize(serializer);
return true;
}
}
};
cpp复制class BehaviorNode
{
public:
enum class Status { Running, Success, Failure };
virtual Status Update(float deltaTime) = 0;
};
class SequenceNode : public BehaviorNode
{
private:
std::vector<std::unique_ptr<BehaviorNode>> children;
size_t currentIndex = 0;
public:
Status Update(float deltaTime) override
{
if (children.empty())
return Status::Success;
while (currentIndex < children.size())
{
auto status = children[currentIndex]->Update(deltaTime);
if (status != Status::Success)
return status;
currentIndex++;
}
currentIndex = 0;
return Status::Success;
}
};
class SelectorNode : public BehaviorNode
{
private:
std::vector<std::unique_ptr<BehaviorNode>> children;
public:
Status Update(float deltaTime) override
{
for (auto& child : children)
{
auto status = child->Update(deltaTime);
if (status != Status::Failure)
return status;
}
return Status::Failure;
}
};
class AIController
{
private:
std::unique_ptr<BehaviorNode> behaviorTree;
public:
void Update(float deltaTime)
{
if (behaviorTree)
behaviorTree->Update(deltaTime);
}
void SetBehaviorTree(std::unique_ptr<BehaviorNode> tree)
{
behaviorTree = std::move(tree);
}
};
cpp复制class NavMesh
{
private:
std::vector<Polygon> polygons;
std::vector<Edge> edges;
std::vector<Connection> connections;
public:
std::vector<Vector3> FindPath(const Vector3& start, const Vector3& end)
{
// 1. 定位起点和终点所在的多边形
auto startPoly = FindContainingPolygon(start);
auto endPoly = FindContainingPolygon(end);
if (!startPoly || !endPoly)
return {};
// 2. A*算法寻路
std::priority_queue<Node> openSet;
std::unordered_map<Polygon*, Node> nodeMap;
auto& startNode = nodeMap[startPoly];
startNode.poly = startPoly;
startNode.gScore = 0;
startNode.fScore = Heuristic(start, end);
openSet.push(startNode);
while (!openSet.empty())
{
auto current = openSet.top();
openSet.pop();
if (current.poly == endPoly)
return ReconstructPath(current);
for (auto& conn : current.poly->connections)
{
auto neighbor = conn.to;
float tentativeG = current.gScore + conn.cost;
auto& neighborNode = nodeMap[neighbor];
if (tentativeG < neighborNode.gScore)
{
neighborNode.cameFrom = current.poly;
neighborNode.gScore = tentativeG;
neighborNode.fScore = tentativeG + Heuristic(conn.midPoint, end);
if (std::find(openSet.begin(), openSet.end(), neighborNode) == openSet.end())
openSet.push(neighborNode);
}
}
}
return {};
}
};
cpp复制class AISensorySystem
{
private:
struct Stimulus
{
enum class Type { Sight, Sound, Damage };
Type type;
Vector3 position;
float intensity;
float timestamp;
};
std::vector<Stimulus> stimuli;
float memoryDuration = 10.0f;
public:
void AddStimulus(const Stimulus& stimulus)
{
stimuli.push_back(stimulus);
}
void Update(float deltaTime)
{
// 移除过期的刺激
float currentTime = GetGameTime();
stimuli.erase(
std::remove_if(stimuli.begin(), stimuli.end(),
[currentTime, this](const Stimulus& s) {
return currentTime - s.timestamp > memoryDuration;
}),
stimuli.end()
);
}
std::vector<Stimulus> GetRelevantStimuli(const Vector3& position, float radius) const
{
std::vector<Stimulus> result;
for (const auto& stim : stimuli)
{
if (Vector3::Distance(position, stim.position) <= radius)
result.push_back(stim);
}
return result;
}
};
cpp复制class AudioSystem
{
private:
IXAudio2* xAudio = nullptr;
IXAudio2MasteringVoice* masterVoice = nullptr;
std::unordered_map<std::string, std::unique_ptr<AudioClip>> clips;
public:
bool Initialize()
{
if (FAILED(XAudio2Create(&xAudio, 0, XAUDIO2_DEFAULT_PROCESSOR)))
return false;
if (FAILED(xAudio->CreateMasteringVoice(&masterVoice)))
return false;
return true;
}
void Shutdown()
{
if (masterVoice) masterVoice->DestroyVoice();
if (xAudio) xAudio->Release();
}
AudioClip* LoadClip(const std::string& path)
{
auto it = clips.find(path);
if (it != clips.end())
return it->second.get();
auto clip = std::make_unique<AudioClip>();
if (!clip->Load(path, xAudio))
return nullptr;
auto result = clip.get();
clips[path] = std::move(clip);
return result;
}
void Play(AudioClip* clip, float volume = 1.0f, bool loop = false)
{
if (!clip || !masterVoice)
return;
IXAudio2SourceVoice* voice = nullptr;
if (FAILED(xAudio->CreateSourceVoice(&voice, clip->GetFormat())))
return;
if (loop)
voice->SetLoopCount(XAUDIO2_LOOP_INFINITE);
voice->SetVolume(volume);
voice->SubmitSourceBuffer(clip->GetBuffer());
voice->Start();
}
};
cpp复制class SpatialAudioSystem
{
private:
struct AudioEmitter
{
Vector3 position;
Vector3 velocity;
float innerRadius = 1.0f;
float outerRadius = 10.0f;
IXAudio2SourceVoice* voice = nullptr;
};
std::vector<AudioEmitter> emitters;
Vector3 listenerPosition;
Vector3 listenerForward;
Vector3 listenerUp;
public:
void Update(float deltaTime)
{
for (auto& emitter : emitters)
{
if (!emitter.voice)
continue;
// 计算距离衰减
float distance = Vector3::Distance(listenerPosition, emitter.position);
float attenuation = 1.0f;
if (distance > emitter.innerRadius)
{
float t = (distance - emitter.innerRadius) /
(emitter.outerRadius - emitter.innerRadius);
attenuation = 1.0f - std::clamp(t, 0.0f, 1.0f);
}
// 计算立体声平衡
Vector3 toEmitter = emitter.position - listenerPosition;
float dotRight = Vector3::Dot(toEmitter.Normalized(),
Vector3::Cross(listenerForward, listenerUp));
float pan = std::clamp(dotRight, -1.0f, 1.0f);
// 应用3D效果
float leftGain = attenuation * (1.0f - pan) * 0.5f;
float rightGain = attenuation * (1.0f + pan) * 0.5f;
float matrix[4] = { leftGain, 0, 0, rightGain };
emitter.voice->SetOutputMatrix(nullptr, 2, 2, matrix);
}
}
};