3D游戏开发---大系列了---第二篇
2026-02-27 16:28:42
发布于:湖南
V1.2问世了
更新了:反作弊系统
更新了:柏林噪声(地图生成)
修复了:鼠标晃动卡顿
话不多说,直接上代码:
variable.hpp:
#ifndef VARIABLE_HPP
#define VARIABLE_HPP 1
#include <windows.h>
const int WIDTH = 1280;
const int HEIGHT = 720;
const float PI = 3.1415926535f;
const float RAD = PI / 180.0f;
const float NEAR_CLIP = 0.1f;
const float FAR_CLIP = 1000.0f;
const float FOV = 60.0f;
const float TERRAIN_HEIGHT_SCALE = 50.0f;
const float TERRAIN_NOISE_SCALE = 0.01f;
const int TERRAIN_GRID_HALF = 30;
const float TERRAIN_CELL_SIZE = 1.5f;
HWND g_hWnd = nullptr;
HDC g_hMemDC = nullptr;
HBITMAP g_hMemBitmap = nullptr;
HBITMAP g_hOldBitmap = nullptr;
bool g_bRunning = true;
bool g_bWindowActive = false;
HPEN g_hGridPen = nullptr;
HPEN g_hXAxisPen = nullptr;
HPEN g_hZAxisPen = nullptr;
HBRUSH g_hTerrainBrushLow = nullptr;
HBRUSH g_hTerrainBrushHigh = nullptr;
bool g_firstMouse = true;
float g_lastMouseX = WIDTH / 2.0f;
float g_lastMouseY = HEIGHT / 2.0f;
bool g_ignoreMouse = false;
#endif
defense.hpp
#ifndef DEFENSE_HPP
#define DEFENSE_HPP 1
#include <windows.h>
template<typename T>
class EncryptedVar {
private:
T data;
DWORD key;
void encrypt() {
key = GetCurrentThreadId() ^ (DWORD)GetTickCount64();
BYTE* p = reinterpret_cast<BYTE*>(&data);
for (size_t i = 0; i < sizeof(T); i++) {
p[i] ^= (key >> (i % 4 * 8)) & 0xFF;
}
}
void decrypt(T& out) const {
out = data;
BYTE* p = reinterpret_cast<BYTE*>(&out);
for (size_t i = 0; i < sizeof(T); i++) {
p[i] ^= (key >> (i % 4 * 8)) & 0xFF;
}
}
public:
EncryptedVar() : data(T()), key(0) {}
EncryptedVar(T val) { set(val); }
void set(T val) { data = val; encrypt(); }
T get() const { T val; decrypt(val); return val; }
EncryptedVar& operator=(T val) { set(val); return *this; }
operator T() const { return get(); }
};
bool IsDebuggerPresentCheck() {
if (IsDebuggerPresent()) return true;
BOOL isRemoteDebuggerPresent = FALSE;
CheckRemoteDebuggerPresent(GetCurrentProcess(), &isRemoteDebuggerPresent);
if (isRemoteDebuggerPresent) return true;
# ifdef _WIN64
PVOID peb = reinterpret_cast<PVOID>(__readgsqword(0x60));
# else
PVOID peb = reinterpret_cast<PVOID>(__readfsdword(0x30));
# endif
BYTE* pebBase = reinterpret_cast<BYTE*>(peb);
BYTE beingDebugged = *(pebBase + 0x02);
if (beingDebugged) return true;
return false;
}
#endif
Vec3.hpp
#ifndef VE3_HPP
#define VE3_HPP 1
#include <cmath>
struct Vec3 {
float x, y, z;
Vec3() : x(0), y(0), z(0) {}
Vec3(float x, float y, float z) : x(x), y(y), z(z) {}
};
inline Vec3 add(Vec3 a, Vec3 b) {
return Vec3(a.x + b.x, a.y + b.y, a.z + b.z);
}
inline Vec3 sub(Vec3 a, Vec3 b) {
return Vec3(a.x - b.x, a.y - b.y, a.z - b.z);
}
inline Vec3 mul(Vec3 a, float s) {
return Vec3(a.x * s, a.y * s, a.z * s);
}
inline float dot(Vec3 a, Vec3 b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
inline float len(Vec3 a) {
return sqrt(dot(a, a));
}
inline Vec3 norm(Vec3 a) {
float l = len(a);
return l > 0.001f ? mul(a, 1.0f / l) : a;
}
inline Vec3 cross(Vec3 a, Vec3 b) {
return Vec3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
}
#endif
map.hpp
#ifndef MAP_HPP
#define MAP_HPP 1
#include <algorithm>
#include <cmath>
#include <time.h>
#include "variable.hpp"
class PerlinNoise {
public:
PerlinNoise(unsigned int seed = 0) {
for (int i = 0; i < 256; ++i) p[i] = i;
std::srand(seed);
for (int i = 255; i > 0; --i) {
int j = std::rand() % (i + 1);
std::swap(p[i], p[j]);
}
for (int i = 0; i < 256; ++i) p[256 + i] = p[i];
}
float noise(float x, float y) const {
int X = (int)std::floor(x) & 255;
int Y = (int)std::floor(y) & 255;
x -= std::floor(x);
y -= std::floor(y);
float u = fade(x);
float v = fade(y);
int A = p[X] + Y, AA = p[A], AB = p[A + 1];
int B = p[X + 1] + Y, BA = p[B], BB = p[B + 1];
return lerp(v, lerp(u, grad(p[AA], x, y), grad(p[BA], x - 1, y)),
lerp(u, grad(p[AB], x, y - 1), grad(p[BB], x - 1, y - 1)));
}
float octaveNoise(float x, float y, int octaves, float persistence) const {
float total = 0.0f;
float frequency = 1.0f;
float amplitude = 1.0f;
float maxValue = 0.0f;
for (int i = 0; i < octaves; ++i) {
total += noise(x * frequency, y * frequency) * amplitude;
maxValue += amplitude;
amplitude *= persistence;
frequency *= 2.0f;
}
return total / maxValue;
}
private:
int p[512];
static float fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); }
static float lerp(float t, float a, float b) { return a + t * (b - a); }
static float grad(int hash, float x, float y) {
int h = hash & 3;
float u = h < 2 ? x : y;
float v = h < 2 ? y : x;
return ((h & 1) ? -u : u) + ((h & 2) ? -v : v);
}
};
PerlinNoise g_perlinNoise(time(0));
float getTerrainHeight(float worldX, float worldZ) {
float noiseVal = g_perlinNoise.octaveNoise(worldX * TERRAIN_NOISE_SCALE, worldZ * TERRAIN_NOISE_SCALE, 6, 0.5f);
return (noiseVal + 1.0f) * 0.5f * TERRAIN_HEIGHT_SCALE;
}
#endif
camera.hpp
#ifndef CAMERA_HPP
#define CAMERA_HPP 1
#include "Vec3.hpp"
#include "defense.hpp"
#include "variable.hpp"
#include "map.hpp"
struct Camera {
EncryptedVar<Vec3> pos;
float baseYaw;
float basePitch;
float moveSpeed;
float sprintMult;
float mouseSens;
EncryptedVar<float> velY;
float gravity;
float jumpForce;
bool isGrounded;
float bobTimer;
float bobWalkFrequency;
float bobSprintFrequency;
float bobVerticalPosAmp;
float bobPitchViewAmp;
float bobSprintMultiplier;
float bobAirFrequency;
float bobAirAmpMultiplier;
bool isMoving;
Vec3 velXZ;
float acceleration;
float friction;
float airControl;
float bobHorizontalPosAmp;
float bobYawViewAmp;
float landImpact;
bool wasGrounded;
float footstepTimer;
float footstepInterval;
Camera() {
pos = Vec3(0, 30.0f, -40.0f);
baseYaw = 0.0f;
basePitch = -20.0f;
moveSpeed = 6.0f;
sprintMult = 2.0f;
mouseSens = 0.15f;
velY = 0.0f;
gravity = -18.0f;
jumpForce = 8.0f;
isGrounded = true;
bobTimer = 0.0f;
bobWalkFrequency = 10.0f;
bobSprintFrequency = 16.0f;
bobVerticalPosAmp = 0.12f;
bobPitchViewAmp = 0.18f;
bobSprintMultiplier = 2.0f;
bobAirFrequency = 6.0f;
bobAirAmpMultiplier = 0.3f;
isMoving = false;
velXZ = Vec3(0, 0, 0);
acceleration = 25.0f;
friction = 20.0f;
airControl = 0.3f;
bobHorizontalPosAmp = 0.08f;
bobYawViewAmp = 0.12f;
landImpact = 0.0f;
wasGrounded = true;
footstepTimer = 0.0f;
footstepInterval = 0.3f;
}
Vec3 getBaseForward() {
float yawRad = baseYaw * RAD;
float pitchRad = basePitch * RAD;
return norm(Vec3(sin(yawRad) * cos(pitchRad), sin(pitchRad), cos(yawRad) * cos(pitchRad)));
}
Vec3 getForward() {
float pitchBob = sin(bobTimer) * bobPitchViewAmp;
float yawBob = sin(bobTimer * 0.5f) * bobYawViewAmp;
if (isMoving && (GetAsyncKeyState('R') & 0x8000)) {
pitchBob *= bobSprintMultiplier;
yawBob *= bobSprintMultiplier * 0.8f;
}
if (!isGrounded) {
pitchBob *= bobAirAmpMultiplier;
yawBob *= bobAirAmpMultiplier;
}
pitchBob += landImpact * 0.5f;
float finalYaw = (baseYaw + yawBob) * RAD;
float finalPitch = (basePitch + pitchBob) * RAD;
return norm(Vec3(sin(finalYaw) * cos(finalPitch), sin(finalPitch), cos(finalYaw) * cos(finalPitch)));
}
Vec3 getRight() {
Vec3 f = getForward();
return norm(cross(Vec3(0, 1, 0), f));
}
Vec3 getUp() {
Vec3 f = getForward();
Vec3 r = getRight();
return norm(cross(f, r));
}
void processInput(float deltaTime) {
if (!g_bWindowActive || !IsWindow(g_hWnd)) return;
wasGrounded = isGrounded;
const float MAX_SPEED = moveSpeed * sprintMult * 1.5f;
float targetSpeed = moveSpeed;
if (GetAsyncKeyState('R') & 0x8000) targetSpeed *= sprintMult;
Vec3 baseForward = getBaseForward();
Vec3 baseRight = getRight();
Vec3 moveForward = norm(Vec3(baseForward.x, 0, baseForward.z));
Vec3 moveRight = norm(Vec3(baseRight.x, 0, baseRight.z));
bool w = (GetAsyncKeyState('W') & 0x8000) != 0;
bool s = (GetAsyncKeyState('S') & 0x8000) != 0;
bool a = (GetAsyncKeyState('A') & 0x8000) != 0;
bool d = (GetAsyncKeyState('D') & 0x8000) != 0;
isMoving = (w || s || a || d);
Vec3 inputDir = Vec3(0, 0, 0);
if (w) inputDir = add(inputDir, moveForward);
if (s) inputDir = sub(inputDir, moveForward);
if (a) inputDir = sub(inputDir, moveRight);
if (d) inputDir = add(inputDir, moveRight);
if (len(inputDir) > 0.001f) {
inputDir = norm(inputDir);
float accel = isGrounded ? acceleration : acceleration * airControl;
Vec3 targetVel = mul(inputDir, targetSpeed);
Vec3 velDiff = sub(targetVel, velXZ);
float accelAmt = accel * deltaTime;
velXZ = len(velDiff) < accelAmt ? targetVel : add(velXZ, mul(norm(velDiff), accelAmt));
} else {
if (isGrounded) {
float frictionAmt = friction * deltaTime;
float currentSpeed = len(velXZ);
velXZ = currentSpeed > 0.001f ? mul(norm(velXZ), std::max(0.0f, currentSpeed - frictionAmt)) : Vec3(0, 0, 0);
}
}
if (len(velXZ) > MAX_SPEED) velXZ = mul(norm(velXZ), MAX_SPEED);
Vec3 currentPos = pos.get();
currentPos = add(currentPos, mul(velXZ, deltaTime));
pos.set(currentPos);
if ((GetAsyncKeyState(VK_SPACE) & 0x8000) && isGrounded) {
velY = jumpForce;
isGrounded = false;
}
float currentVelY = velY.get();
currentVelY += gravity * deltaTime;
velY.set(currentVelY);
currentPos = pos.get();
currentPos.y += currentVelY * deltaTime;
float terrainH = getTerrainHeight(currentPos.x, currentPos.z);
float playerHeight = 2.0f;
if (currentPos.y <= terrainH + playerHeight) {
currentPos.y = terrainH + playerHeight;
velY = 0.0f;
if (!wasGrounded) {
landImpact = -0.3f;
bobTimer = PI;
}
isGrounded = true;
} else {
isGrounded = false;
}
pos.set(currentPos);
landImpact *= 0.85f;
if (fabs(landImpact) < 0.001f) landImpact = 0.0f;
float currentFreq = 0.0f;
float currentSpeed = len(velXZ);
if (isMoving && currentSpeed > 0.5f) {
currentFreq = isGrounded ? (bobWalkFrequency + (bobSprintFrequency - bobWalkFrequency) * (currentSpeed / (moveSpeed * sprintMult))) : bobAirFrequency;
bobTimer += deltaTime * currentFreq;
if (bobTimer > 2 * PI) bobTimer -= 2 * PI;
} else {
if (bobTimer > 0.0f) {
float decay = deltaTime * bobWalkFrequency * 2.0f;
float target = round(bobTimer / PI) * PI;
bobTimer = fabs(bobTimer - target) < decay ? target : bobTimer + (target > bobTimer ? decay : -decay);
}
}
}
void processMouse(float xOffset, float yOffset) {
if (!g_bWindowActive || !IsWindow(g_hWnd)) return;
xOffset = std::max(-300.0f, std::min(300.0f, xOffset));
yOffset = std::max(-300.0f, std::min(300.0f, yOffset));
baseYaw += xOffset * mouseSens;
basePitch += yOffset * mouseSens;
basePitch = std::max(-89.0f, std::min(89.0f, basePitch));
}
};
struct CameraState {
Vec3 pos;
Vec3 forward;
Vec3 right;
Vec3 up;
float tanHalfFov;
float aspect;
};
CameraState g_camState;
Camera g_cam;
void updateCameraState() {
g_camState.forward = g_cam.getForward();
g_camState.right = g_cam.getRight();
g_camState.up = g_cam.getUp();
g_camState.pos = g_cam.pos.get();
float currentVerticalAmp = 0.0f;
float currentHorizontalAmp = 0.0f;
float currentSpeed = len(g_cam.velXZ);
if (g_cam.isMoving && currentSpeed > 0.5f) {
currentVerticalAmp = g_cam.bobVerticalPosAmp;
currentHorizontalAmp = g_cam.bobHorizontalPosAmp;
if (GetAsyncKeyState('R') & 0x8000) {
currentVerticalAmp *= g_cam.bobSprintMultiplier;
currentHorizontalAmp *= g_cam.bobSprintMultiplier * 0.8f;
}
if (!g_cam.isGrounded) {
currentVerticalAmp *= g_cam.bobAirAmpMultiplier;
currentHorizontalAmp *= g_cam.bobAirAmpMultiplier;
}
}
float verticalBob = sin(g_cam.bobTimer) * currentVerticalAmp;
float horizontalBob = cos(g_cam.bobTimer) * currentHorizontalAmp;
g_camState.pos = add(g_camState.pos, mul(g_camState.up, verticalBob));
g_camState.pos = add(g_camState.pos, mul(g_camState.right, horizontalBob));
g_camState.tanHalfFov = tan(FOV * RAD / 2.0f);
g_camState.aspect = (float)WIDTH / HEIGHT;
}
#endif
paint.hpp
#ifndef PAINT_HPP
#define PAINT_HPP 1
#include <vector>
#include <cstdio>
#include "camera.hpp"
struct Triangle {
Vec3 p0, p1, p2;
COLORREF color;
float depth;
};
bool projectPoint(Vec3 point, POINT* out) {
Vec3 delta = sub(point, g_camState.pos);
float z = dot(delta, g_camState.forward);
if (z < NEAR_CLIP - 0.001f) return false;
float x = dot(delta, g_camState.right);
float y = dot(delta, g_camState.up);
float invZ = 1.0f / (z + 1e-6f);
float ndcX = x * invZ / (g_camState.tanHalfFov * g_camState.aspect);
float ndcY = y * invZ / g_camState.tanHalfFov;
out->x = (int)((ndcX + 1.0f) * 0.5f * WIDTH);
out->y = (int)((1.0f - ndcY) * 0.5f * HEIGHT);
return true;
}
Vec3 nearClipIntersect(Vec3 p1, Vec3 p2) {
Vec3 delta = sub(p2, p1);
float d1 = dot(sub(p1, g_camState.pos), g_camState.forward) - NEAR_CLIP;
float d2 = dot(sub(p2, g_camState.pos), g_camState.forward) - NEAR_CLIP;
float t = d1 / (d1 - d2 + 1e-8f);
return add(p1, mul(delta, t));
}
int clipTriangleToNearPlane(Vec3 inPts[3], Vec3 outPts[4]) {
int outCount = 0;
Vec3 s = inPts[2];
for (int i = 0; i < 3; i++) {
Vec3 e = inPts[i];
float d_e = dot(sub(e, g_camState.pos), g_camState.forward) - NEAR_CLIP;
float d_s = dot(sub(s, g_camState.pos), g_camState.forward) - NEAR_CLIP;
if (d_e >= -0.001f) {
if (d_s < -0.001f) {
outPts[outCount++] = nearClipIntersect(s, e);
}
outPts[outCount++] = e;
} else if (d_s >= -0.001f) {
outPts[outCount++] = nearClipIntersect(s, e);
}
s = e;
}
return outCount;
}
void drawFilledTriangle(Vec3 p0, Vec3 p1, Vec3 p2, COLORREF color) {
float z0 = dot(sub(p0, g_camState.pos), g_camState.forward);
float z1 = dot(sub(p1, g_camState.pos), g_camState.forward);
float z2 = dot(sub(p2, g_camState.pos), g_camState.forward);
if (z0 < NEAR_CLIP && z1 < NEAR_CLIP && z2 < NEAR_CLIP) return;
Vec3 v1 = sub(p1, p0);
Vec3 v2 = sub(p2, p0);
Vec3 normal = cross(v1, v2);
Vec3 viewDir = sub(g_camState.pos, p0);
if (dot(normal, viewDir) < 0.001f) return;
Vec3 inPts[3] = {p0, p1, p2};
Vec3 clippedPts[4];
int clippedCount = clipTriangleToNearPlane(inPts, clippedPts);
if (clippedCount < 3) return;
POINT screenPts[4];
int screenCount = 0;
for (int i = 0; i < clippedCount; i++) {
if (projectPoint(clippedPts[i], &screenPts[screenCount])) {
screenCount++;
}
}
if (screenCount < 3) return;
HPEN oldPen = (HPEN)SelectObject(g_hMemDC, g_hGridPen);
HBRUSH brush = CreateSolidBrush(color);
HBRUSH oldBrush = (HBRUSH)SelectObject(g_hMemDC, brush);
Polygon(g_hMemDC, screenPts, screenCount);
SelectObject(g_hMemDC, oldBrush);
SelectObject(g_hMemDC, oldPen);
DeleteObject(brush);
}
void drawLine(Vec3 p1, Vec3 p2, HPEN pen) {
POINT sp1, sp2;
if (!projectPoint(p1, &sp1) || !projectPoint(p2, &sp2)) return;
HPEN oldPen = (HPEN)SelectObject(g_hMemDC, pen);
MoveToEx(g_hMemDC, sp1.x, sp1.y, nullptr);
LineTo(g_hMemDC, sp2.x, sp2.y);
SelectObject(g_hMemDC, oldPen);
}
float getTriangleDepth(Vec3 p0, Vec3 p1, Vec3 p2) {
Vec3 center = add(add(p0, p1), p2);
center = mul(center, 1.0f / 3.0f);
return dot(sub(center, g_camState.pos), g_camState.forward);
}
void render() {
if (!g_hMemDC || !IsWindow(g_hWnd)) return;
RECT rect = {0, 0, WIDTH, HEIGHT};
FillRect(g_hMemDC, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
updateCameraState();
std::vector<Triangle> triangles;
for (int j = -TERRAIN_GRID_HALF; j <= TERRAIN_GRID_HALF; j++) {
for (int i = -TERRAIN_GRID_HALF; i <= TERRAIN_GRID_HALF; i++) {
float x1 = i * TERRAIN_CELL_SIZE;
float z1 = j * TERRAIN_CELL_SIZE;
float x2 = (i + 1) * TERRAIN_CELL_SIZE;
float z2 = (j + 1) * TERRAIN_CELL_SIZE;
float y1 = getTerrainHeight(x1, z1);
float y2 = getTerrainHeight(x2, z1);
float y3 = getTerrainHeight(x2, z2);
float y4 = getTerrainHeight(x1, z2);
Vec3 p1(x1, y1, z1);
Vec3 p2(x2, y2, z1);
Vec3 p3(x2, y3, z2);
Vec3 p4(x1, y4, z2);
float avgH1 = (y1 + y2 + y4) / 3.0f;
float avgH2 = (y2 + y3 + y4) / 3.0f;
int r1 = (int)(60 + avgH1 * 8);
int g1 = (int)(160 - avgH1 * 4);
int b1 = (int)(60 + avgH1 * 6);
int r2 = (int)(60 + avgH2 * 8);
int g2 = (int)(160 - avgH2 * 4);
int b2 = (int)(60 + avgH2 * 6);
float triDepth1 = getTriangleDepth(p1, p4, p2);
float triDepth2 = getTriangleDepth(p2, p4, p3);
triangles.push_back({p1, p4, p2, RGB(r1, g1, b1), triDepth1});
triangles.push_back({p2, p4, p3, RGB(r2, g2, b2), triDepth2});
}
}
std::sort(triangles.begin(), triangles.end(), [](const Triangle & a, const Triangle & b) {
return a.depth > b.depth;
});
for (const auto& tri : triangles) {
drawFilledTriangle(tri.p0, tri.p1, tri.p2, tri.color);
}
char title[256];
sprintf(title, "3D地形 | WASD移动 R疾跑 空格跳跃 ESC退出");
SetWindowText(g_hWnd, title);
}
#endif
procedure.hpp
#ifndef PROCEDURE_HPP
#define PROCEDURE_HPP 1
#include <windows.h>
#include "variable.hpp"
#include "paint.hpp"
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CREATE: {
HDC hdc = GetDC(hWnd);
g_hMemDC = CreateCompatibleDC(hdc);
g_hMemBitmap = CreateCompatibleBitmap(hdc, WIDTH, HEIGHT);
if (g_hMemDC && g_hMemBitmap) {
g_hOldBitmap = (HBITMAP)SelectObject(g_hMemDC, g_hMemBitmap);
}
g_hGridPen = CreatePen(PS_SOLID, 1, RGB(60, 120, 60));
g_hXAxisPen = CreatePen(PS_SOLID, 3, RGB(255, 0, 0));
g_hZAxisPen = CreatePen(PS_SOLID, 3, RGB(0, 0, 255));
g_hTerrainBrushLow = CreateSolidBrush(RGB(60, 140, 60));
g_hTerrainBrushHigh = CreateSolidBrush(RGB(160, 160, 160));
ReleaseDC(hWnd, hdc);
HMODULE hImmDll = LoadLibrary(TEXT("imm32.dll"));
if (hImmDll) {
typedef HIMC (WINAPI * ImmAssociateContextProc)(HWND, HIMC);
ImmAssociateContextProc pImmAssociateContext = (ImmAssociateContextProc)(void*)GetProcAddress(hImmDll, "ImmAssociateContext");
if (pImmAssociateContext) pImmAssociateContext(hWnd, NULL);
}
break;
}
case WM_ERASEBKGND:
return 1;
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
render();
if (g_hMemDC) BitBlt(hdc, 0, 0, WIDTH, HEIGHT, g_hMemDC, 0, 0, SRCCOPY);
EndPaint(hWnd, &ps);
break;
}
case WM_MOUSEMOVE: {
if (g_ignoreMouse || !g_bWindowActive) {
g_ignoreMouse = false;
return 0;
}
int x = LOWORD(lParam);
int y = HIWORD(lParam);
if (g_firstMouse) {
g_lastMouseX = (float)x;
g_lastMouseY = (float)y;
g_firstMouse = false;
}
float xOffset = (float)x - g_lastMouseX;
float yOffset = g_lastMouseY - (float)y;
g_lastMouseX = (float)x;
g_lastMouseY = (float)y;
g_cam.processMouse(xOffset, yOffset);
RECT clientRect;
GetClientRect(hWnd, &clientRect);
int centerX = (clientRect.left + clientRect.right) / 2;
int centerY = (clientRect.top + clientRect.bottom) / 2;
if (abs(x - centerX) > 2 || abs(y - centerY) > 2) {
ClientToScreen(hWnd, (LPPOINT)&clientRect);
g_ignoreMouse = true;
SetCursorPos(clientRect.left + centerX, clientRect.top + centerY);
g_lastMouseX = (float)centerX;
g_lastMouseY = (float)centerY;
}
break;
}
case WM_ACTIVATE: {
if (wParam == WA_INACTIVE || IsIconic(hWnd)) {
g_bWindowActive = false;
ShowCursor(TRUE);
ClipCursor(nullptr);
} else {
g_bWindowActive = true;
ShowCursor(FALSE);
g_firstMouse = true;
RECT rect;
GetClientRect(hWnd, &rect);
ClientToScreen(hWnd, (LPPOINT)&rect);
ClientToScreen(hWnd, (LPPOINT)&rect + 1);
ClipCursor(&rect);
int centerX = (rect.left + rect.right) / 2;
int centerY = (rect.top + rect.bottom) / 2;
SetCursorPos(centerX, centerY);
}
break;
}
case WM_KEYDOWN:
if (wParam == VK_ESCAPE) DestroyWindow(hWnd);
break;
case WM_DESTROY:
g_bRunning = false;
g_bWindowActive = false;
ClipCursor(nullptr);
ShowCursor(TRUE);
if (g_hGridPen) DeleteObject(g_hGridPen);
if (g_hXAxisPen) DeleteObject(g_hXAxisPen);
if (g_hZAxisPen) DeleteObject(g_hZAxisPen);
if (g_hTerrainBrushLow) DeleteObject(g_hTerrainBrushLow);
if (g_hTerrainBrushHigh) DeleteObject(g_hTerrainBrushHigh);
if (g_hOldBitmap && g_hMemDC) SelectObject(g_hMemDC, g_hOldBitmap);
if (g_hMemBitmap) DeleteObject(g_hMemBitmap);
if (g_hMemDC) DeleteDC(g_hMemDC);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
#endif
main.cpp
#include <windows.h>
#include <cmath>
#include "camera.hpp"
#include "procedure.hpp"
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR cmd, int show) {
(void)hPrev;
(void)cmd;
if (IsDebuggerPresentCheck()) {
MessageBox(NULL, "检测到调试器,程序即将退出", "安全警告", MB_ICONERROR | MB_OK);
return 0;
}
WNDCLASSEX wc = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.hInstance = hInst;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = "3DTerrainFixClass";
RegisterClassEx(&wc);
RECT winRect = {0, 0, WIDTH, HEIGHT};
AdjustWindowRect(&winRect, WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX & ~WS_THICKFRAME, FALSE);
g_hWnd = CreateWindowEx(0, "3DTerrainFixClass", "3D地形",
WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX & ~WS_THICKFRAME,
CW_USEDEFAULT, CW_USEDEFAULT,
winRect.right - winRect.left, winRect.bottom - winRect.top,
NULL, NULL, hInst, NULL
);
if (!g_hWnd) return 0;
ShowWindow(g_hWnd, show);
UpdateWindow(g_hWnd);
SetForegroundWindow(g_hWnd);
LARGE_INTEGER freq, lastTime, currTime;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&lastTime);
DWORD lastTick = GetTickCount64();
const float frameTime = 1.0f / 60.0f;
float accumulator = 0.0f;
MSG msg;
ZeroMemory(&msg, sizeof(msg));
while (g_bRunning) {
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
g_bRunning = false;
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (!IsWindow(g_hWnd)) break;
QueryPerformanceCounter(&currTime);
float deltaTime = (float)(currTime.QuadPart - lastTime.QuadPart) / (float)freq.QuadPart;
DWORD currTick = GetTickCount64();
DWORD tickDelta = currTick - lastTick;
if (fabs(deltaTime - (tickDelta / 1000.0f)) > 0.1f) deltaTime = tickDelta / 1000.0f;
deltaTime = std::max(0.001f, std::min(0.1f, deltaTime));
lastTime = currTime;
lastTick = currTick;
accumulator += deltaTime;
while (accumulator >= frameTime) {
g_cam.processInput(frameTime);
accumulator -= frameTime;
}
InvalidateRect(g_hWnd, NULL, FALSE);
Sleep(1);
}
UnregisterClass("3DTerrainFixClass", hInst);
return (int)msg.wParam;
}
这里空空如也


















有帮助,赞一个