#define UNICODE
#define _UNICODE
#include <windows.h>
#include <math.h>
#include <wchar.h> // 宽字符支持
// 窗口及常量定义
#define WIN_WIDTH 600
#define WIN_HEIGHT 500
#define BALL_RADIUS 10
#define GRAVITY 0.0008
#define REBOUND_FACTOR 0.5
#define GROUND_Y (WIN_HEIGHT - 150) // 地面高度
// 控件 ID
#define IDC_HEIGHT_INPUT 201 // 高度输入框
#define IDC_REBOUNDS_INPUT 202 // 最大反弹次数输入框
#define IDC_SUBMIT_BUTTON 203 // 提交按钮
#define TIMER_ID 101 // 定时器 ID
// 小球结构体定义
typedef struct {
double x, y; // 小球位置 (像素)
double vy; // 垂直速度
double currentHeight; // 当前高度 (像素)
int rebounds; // 当前反弹次数
int maxRebounds; // 最大反弹次数
BOOL isActive; // 是否处于运动状态
BOOL isFalling; // 是否处于下落状态
double holdTime; // 静止时间(毫秒)
double cumulativeDistance; // 累计运动总距离
} Ball;
// 全局变量定义
Ball ball = {0}; // 小球数据
double reboundHeights[100] = {0}; // 每次反弹高度记录
double cumulativeDistances[100] = {0}; // 每次落地累计总路程
HWND heightInput, reboundsInput, submitButton; // 窗口句柄
HWND mainHwnd, outputHwnd = NULL; // 主窗口和结果窗口句柄
DWORD lastTime = 0; // 上次定时器触发的时间戳
SCROLLINFO scrollInfo; // 滚动条信息
// 函数声明
void RenderGround(HDC hdc); // 绘制地面
void RenderBall(HDC hdc); // 绘制小球
void RenderScene(HDC hdc); // 渲染整个场景
void InitializeBall(double initialHeightMeters, int maxRebounds);
void UpdateBall(DWORD deltaTime);
void ShowResultWindow(HINSTANCE hInstance);
LRESULT CALLBACK OutputWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK MainWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
// 初始化小球
void InitializeBall(double initialHeightMeters, int maxRebounds) {
if (initialHeightMeters <= 0 || maxRebounds <= 0) return;
double initialHeightPixels = initialHeightMeters * 2.0; // 假设 1米 = 2像素 ball.x = WIN_WIDTH / 2.0; // 水平居中 ball.y = GROUND_Y - initialHeightPixels; // 初始高度 ball.vy = 0; // 初始速度为0 ball.currentHeight = initialHeightPixels; // 初始高度 ball.rebounds = 0; // 初始反弹次数 ball.maxRebounds = maxRebounds; // 最大反弹次数 ball.isActive = TRUE; // 小球处于运动状态 ball.isFalling = FALSE; // 初始状态为静止 ball.holdTime = 2000.0; // 设置静止时间为2秒 ball.cumulativeDistance = 0.0; // 初始化总路程 memset(reboundHeights, 0, sizeof(reboundHeights)); // 清空反弹高度记录 memset(cumulativeDistances, 0, sizeof(cumulativeDistances)); // 清空累计路径记录
}
// 更新小球物理状态
void UpdateBall(DWORD deltaTime) {
if (!ball.isActive) return;
// 检查是否在静止时间内 if (ball.holdTime > 0) { ball.holdTime -= deltaTime; // 静止倒计时 if (ball.holdTime <= 0) { // 倒计时结束 ball.holdTime = 0; ball.isFalling = TRUE; // 开始进入下落状态 } return; // 在静止时间内不进行运动 } // 小球运动逻辑 if (ball.isFalling) { ball.vy += GRAVITY * deltaTime; // 更新速度 ball.y += ball.vy * deltaTime; // 更新位置 if (ball.y >= GROUND_Y - BALL_RADIUS) { // 到达地面 ball.y = GROUND_Y - BALL_RADIUS; // 保持在地面 ball.isFalling = FALSE; // 切换为上升状态 // 每次落地时记录路径和反弹高度 cumulativeDistances[ball.rebounds] = ball.cumulativeDistance + ball.currentHeight; // 路径记录 reboundHeights[ball.rebounds] = ball.currentHeight * REBOUND_FACTOR; // 记录反弹高度 ball.cumulativeDistance += ball.currentHeight; // 累加经过的距离 ball.rebounds++; // 增加反弹次数 // 检查反弹次数是否超过限制 if (ball.rebounds < ball.maxRebounds) { ball.currentHeight *= REBOUND_FACTOR; // 更新下次反弹高度 ball.vy = -sqrt(2 * GRAVITY * ball.currentHeight); // 更新初始向上速度 } else { ball.isActive = FALSE; // 达到最大反弹次数停止运动 } } } else { ball.y += ball.vy * deltaTime; // 更新上升位置 ball.vy += GRAVITY * deltaTime; // 重力作用减速 if (ball.vy >= 0) ball.isFalling = TRUE; // 达到最高点后切换为下落 }
}
// 绘制地面
void RenderGround(HDC hdc) {
HPEN groundPen = CreatePen(PS_SOLID, 2, RGB(0, 128, 0)); // 绿色地面
HPEN oldPen = SelectObject(hdc, groundPen);
MoveToEx(hdc, 0, GROUND_Y, NULL);
LineTo(hdc, WIN_WIDTH, GROUND_Y); // 绘制地面线条
SelectObject(hdc, oldPen);
DeleteObject(groundPen);
}
// 绘制小球
void RenderBall(HDC hdc) {
HBRUSH ballBrush = CreateSolidBrush(RGB(139, 69, 19)); // 棕色小球
HBRUSH oldBrush = SelectObject(hdc, ballBrush);
Ellipse(hdc,
(int)(ball.x - BALL_RADIUS),
(int)(ball.y - BALL_RADIUS),
(int)(ball.x + BALL_RADIUS),
(int)(ball.y + BALL_RADIUS)); // 绘制圆形
SelectObject(hdc, oldBrush);
DeleteObject(ballBrush);
}
// 渲染场景
void RenderScene(HDC hdc) {
RenderGround(hdc); // 绘制地面
RenderBall(hdc); // 绘制小球
}
// 显示结果窗口
void ShowResultWindow(HINSTANCE hInstance) {
if (outputHwnd != NULL) {
InvalidateRect(outputHwnd, NULL, TRUE); // 刷新已有窗口
return;
}
const wchar_t OUTPUT_CLASS_NAME[] = L"ResultWindow"; WNDCLASS wc = {0}; wc.lpfnWndProc = OutputWindowProc; wc.hInstance = hInstance; wc.lpszClassName = OUTPUT_CLASS_NAME; wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); RegisterClass(&wc); outputHwnd = CreateWindow( OUTPUT_CLASS_NAME, L"运动结果", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, // 增加结果窗口大小 NULL, NULL, hInstance, NULL); ShowWindow(outputHwnd, SW_SHOW);
}
// 结果窗口处理函数
LRESULT CALLBACK OutputWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
wchar_t buffer[256]; int offset = 10; // 起始绘制偏移 HFONT hFont = CreateFontW( 18, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, L"SimSun"); // 宋体字体 HFONT oldFont = SelectObject(hdc, hFont); // 显示总路程 swprintf(buffer, 256, L"小球总运动路程:%.2f 米", ball.cumulativeDistance / 2.0); TextOutW(hdc, 10, offset, buffer, wcslen(buffer)); offset += 20; // 显示每次反弹高度和累计总路程 for (int i = 1; i <= ball.rebounds; i++) { swprintf(buffer, 256, L"第 %d 次反弹高度:%.2f 米", i, reboundHeights[i - 1] / 2.0); TextOutW(hdc, 10, offset, buffer, wcslen(buffer)); offset += 20; swprintf(buffer, 256, L"第 %d 次落地时累计总路程:%.2f 米", i, cumulativeDistances[i - 1] / 2.0); TextOutW(hdc, 10, offset, buffer, wcslen(buffer)); offset += 20; } SelectObject(hdc, oldFont); DeleteObject(hFont); EndPaint(hwnd, &ps); break; } case WM_DESTROY: outputHwnd = NULL; return 0; default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0;
}
// 主窗口逻辑
LRESULT CALLBACK MainWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_CREATE:
CreateWindow(L"STATIC", L"初始高度 (米):“, WS_VISIBLE | WS_CHILD,
20, WIN_HEIGHT - 140, 110, 30, hwnd, NULL, NULL, NULL);
heightInput = CreateWindow(L"EDIT”, L"“, WS_VISIBLE | WS_CHILD | WS_BORDER | ES_NUMBER,
130, WIN_HEIGHT - 140, 100, 30, hwnd, (HMENU)IDC_HEIGHT_INPUT, NULL, NULL);
CreateWindow(L"STATIC”, L"最大反弹次数:“, WS_VISIBLE | WS_CHILD,
20, WIN_HEIGHT - 100, 110, 30, hwnd, NULL, NULL, NULL);
reboundsInput = CreateWindow(L"EDIT”, L"“, WS_VISIBLE | WS_CHILD | WS_BORDER | ES_NUMBER,
130, WIN_HEIGHT - 100, 100, 30, hwnd, (HMENU)IDC_REBOUNDS_INPUT, NULL, NULL);
submitButton = CreateWindow(L"BUTTON”, L"提交", WS_VISIBLE | WS_CHILD,
250, WIN_HEIGHT - 120, 100, 40, hwnd, (HMENU)IDC_SUBMIT_BUTTON, NULL, NULL);
SetTimer(hwnd, TIMER_ID, 10, NULL); // 定时器,10ms刷新
return 0;
case WM_COMMAND: if (LOWORD(wParam) == IDC_SUBMIT_BUTTON) { wchar_t heightBuffer[256], reboundsBuffer[256]; GetWindowText(heightInput, heightBuffer, 256); GetWindowText(reboundsInput, reboundsBuffer, 256); double height = _wtof(heightBuffer); int maxRebounds = _wtoi(reboundsBuffer); // 检查输入是否合法 if (height <= 0 || height > 150) { MessageBox(hwnd, L"高度超出范围!请输入 0 到 150 之间的数值。“, L"输入无效”, MB_ICONERROR); return 0; } if (maxRebounds <= 0) { MessageBox(hwnd, L"反弹次数无效!请输入大于0的整数。“, L"输入无效”, MB_ICONERROR); return 0; } KillTimer(hwnd, TIMER_ID); // 停止旧定时器 InitializeBall(height, maxRebounds); // 初始化小球状态 SetTimer(hwnd, TIMER_ID, 10, NULL); // 设置新的定时器 InvalidateRect(hwnd, NULL, TRUE); // 刷新窗口 } break; case WM_TIMER: if (wParam == TIMER_ID) { DWORD currentTime = GetTickCount(); DWORD deltaTime = currentTime - lastTime; lastTime = currentTime; if (ball.isActive) { UpdateBall(deltaTime); RECT updateRect = {0, 0, WIN_WIDTH, GROUND_Y}; InvalidateRect(hwnd, &updateRect, TRUE); } else { KillTimer(hwnd, TIMER_ID); // 停止定时器 ShowResultWindow((HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE)); // 显示结果窗口 } } break; case WM_PAINT: PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); RenderScene(hdc); // 渲染场景 EndPaint(hwnd, &ps); break; case WM_DESTROY: KillTimer(hwnd, TIMER_ID); PostQuitMessage(0); return 0; default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0;
}
// WinMain 程序入口
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
const wchar_t CLASS_NAME[] = L"BouncingBallWindow";
WNDCLASS wc = {0}; wc.lpfnWndProc = MainWindowProc; wc.hInstance = hInstance; wc.lpszClassName = CLASS_NAME; wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); if (!RegisterClass(&wc)) return 0; mainHwnd = CreateWindow( CLASS_NAME, L"小球反弹模拟", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, WIN_WIDTH, WIN_HEIGHT, NULL, NULL, hInstance, NULL); MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0;
}
分析一下为什么用户点击一次提交时小球不会停留,但点击两次会停留2s
我用的C语言,给出完整的修改后的C语言代码
最新发布