MSP-EXP432P4111屏幕小球弹跳问题:撞角落无法反弹如何修复?
修复MSP432小球角落弹跳丢失问题
我正在MSP-EXP432P4111微控制器及配套BoosterPack上运行C代码,实现128×128像素屏幕的小球碰壁(含角落)弹跳效果。目前小球撞入角落时无法反弹,直接丢失。我尝试修改三元运算符的条件,但没有效果,请问该如何修复以实现持续弹跳?
#include <ti/devices/msp432p4xx/driverlib/driverlib.h> #include <ti/grlib/grlib.h> #include "LcdDriver/Crystalfontz128x128_ST7735.h" #include <stdio.h> #include <stdbool.h> //***************************************************************************** Graphics_Context g_sContext; volatile uint32_t flag; //global flag volatile uint32_t window_height; volatile uint32_t window_width; volatile int32_t x; // x position volatile int32_t y; // y position volatile int32_t vx; // x velocity volatile int32_t vy; // y velocity const int32_t radius = 3; volatile uint32_t hits; // # of wall hits volatile uint32_t hitCounter; //used to reset velocity int32_t oldx; // Variable to store the previous x position int32_t oldy; // Variable to store the previous y position volatile bool xDirectionPositive; // Flag indicating the direction of x movement volatile bool yDirectionPositive; // Flag indicating the direction of y movement volatile uint32_t flag; // Flag variable void SysTick_Handler(void){ flag = 1; } //***************************************************************************** void LCD_init(){ //add LCD related codes in here Crystalfontz128x128_Init(); //initializes display Crystalfontz128x128_SetOrientation(LCD_ORIENTATION_UP); //set default screen orientation Graphics_initContext(&g_sContext, &g_sCrystalfontz128x128, &g_sCrystalfontz128x128_funcs); Graphics_setForegroundColor(&g_sContext, GRAPHICS_COLOR_RED); Graphics_setBackgroundColor(&g_sContext, GRAPHICS_COLOR_WHITE); GrContextFontSet(&g_sContext, &g_sFontFixed6x8); Graphics_clearDisplay(&g_sContext); } //***************************************************************************** void erase(int32_t x, int32_t y, int32_t radius){ // draw over old with background color to erase it Graphics_setForegroundColor(&g_sContext, GRAPHICS_COLOR_WHITE); Graphics_drawCircle(&g_sContext, x, y , radius); //restore foreground color setting Graphics_setForegroundColor(&g_sContext, GRAPHICS_COLOR_RED); } //***************************************************************************** void draw(int32_t x, int32_t y, int32_t radius){ // draw circle with default foreground color Graphics_drawCircle(&g_sContext , x, y , radius); } //***************************************************************************** int main(void) { SysTick_enableModule(); x = 64; y = 64; vx = 1; vy = -1; hits = 0; hitCounter = 0; window_height = 127; window_width = 127; oldx = x; oldy = y; xDirectionPositive = true; yDirectionPositive = true; WDT_A_holdTimer(); LCD_init(); SysTick_setPeriod(30000); SysTick_enableInterrupt(); // ... (existing code) while (1) { if (flag == 1) { erase(oldx, oldy, radius); // Erase the old circle at its previous position // Update ball position oldx = x; // Save the current x as the old x oldy = y; // Save the current y as the old y // Check if the ball is hitting the left or right wall if ((x + radius >= window_width && xDirectionPositive) || (x - radius <= 0 && !xDirectionPositive)) { xDirectionPositive = !xDirectionPositive; // Toggle the x-direction flag vx = (xDirectionPositive) ? 1 : -1; // Reset velocity based on x direction flag // Increase the magnitude of the x-velocity up to a maximum of 15 if (vx < 0) { vx = (-vx) < 15 ? (-vx) + 1 : 15; } else { vx = vx < 15 ? vx + 1 : 15; } hits++; // Increment hits counter when the ball hits the wall } // Check if the ball is hitting the top or bottom wall if ((y + radius >= window_height && yDirectionPositive) || (y - radius <= 0 && !yDirectionPositive)) { yDirectionPositive = !yDirectionPositive; // Toggle the y-direction flag vy = (yDirectionPositive) ? 1 : -1; // Reset velocity based on y direction flag // Increase the magnitude of the y-velocity up to a maximum of 15 if (vy < 0) { vy = (-vy) < 15 ? (-vy) + 1 : 15; } else { vy = vy < 15 ? vy + 1 : 15; } hits++; // Increment hits counter when the ball hits the wall } // Check if 50 hits are reached to reset velocities if (hits >= 50) { hits = 0; // Reset hits counter hitCounter++; // Reset velocities to have magnitude 1 while maintaining the current direction if (hitCounter % 2 == 0) { vx = (xDirectionPositive) ? 1 : -1; // Reset based on x direction flag vy = (yDirectionPositive) ? 1 : -1; // Reset based on y direction flag } } x += vx; // Update x based on velocity y += vy; // Update y based on velocity draw(x, y, radius); // Draw the new circle at its updated position flag = 0; } } }
问题根源
- 角落碰撞逻辑冲突:先检测碰撞再更新位置,导致角落处两个方向的碰撞无法同时触发,且位置更新后直接超出边界。
- 未修正越界位置:碰撞后仅反转速度,未将小球拉回屏幕内,后续循环中位置持续越界,无法触发正确检测。
- 速度逻辑冗余错误:用方向标志重置速度再递增的逻辑,会导致速度方向与实际运动方向不一致,尤其在角落碰撞时。
修复方案
直接替换为以下代码,核心修改点如下:
- 移除冗余的方向标志,直接通过速度符号判断运动方向
- 先更新位置再检测碰撞,确保基于最新位置判断边界
- 碰撞时立即将小球拉回屏幕边缘,避免持续越界
- 简化速度反转逻辑,直接反转符号并基于当前速度递增
- 支持X/Y方向同时触发碰撞,处理角落弹跳
#include <ti/devices/msp432p4xx/driverlib/driverlib.h> #include <ti/grlib/grlib.h> #include "LcdDriver/Crystalfontz128x128_ST7735.h" #include <stdio.h> #include <stdbool.h> //***************************************************************************** Graphics_Context g_sContext; volatile uint32_t flag; //全局标志位 volatile uint32_t window_height; volatile uint32_t window_width; volatile int32_t x; // x坐标 volatile int32_t y; // y坐标 volatile int32_t vx; // x方向速度 volatile int32_t vy; // y方向速度 const int32_t radius = 3; volatile uint32_t hits; //碰壁次数 volatile uint32_t hitCounter; //用于重置速度的计数器 int32_t oldx; //存储上一次x坐标 int32_t oldy; //存储上一次y坐标 void SysTick_Handler(void){ flag = 1; } //***************************************************************************** void LCD_init(){ Crystalfontz128x128_Init(); //初始化显示屏 Crystalfontz128x128_SetOrientation(LCD_ORIENTATION_UP); //设置屏幕正向 Graphics_initContext(&g_sContext, &g_sCrystalfontz128x128, &g_sCrystalfontz128x128_funcs); Graphics_setForegroundColor(&g_sContext, GRAPHICS_COLOR_RED); Graphics_setBackgroundColor(&g_sContext, GRAPHICS_COLOR_WHITE); GrContextFontSet(&g_sContext, &g_sFontFixed6x8); Graphics_clearDisplay(&g_sContext); } //***************************************************************************** void erase(int32_t x, int32_t y, int32_t radius){ //用背景色覆盖旧位置实现擦除 Graphics_setForegroundColor(&g_sContext, GRAPHICS_COLOR_WHITE); Graphics_drawCircle(&g_sContext, x, y , radius); //恢复前景色 Graphics_setForegroundColor(&g_sContext, GRAPHICS_COLOR_RED); } //***************************************************************************** void draw(int32_t x, int32_t y, int32_t radius){ //绘制红色小球 Graphics_drawCircle(&g_sContext , x, y , radius); } //***************************************************************************** int main(void) { SysTick_enableModule(); x = 64; y = 64; vx = 1; vy = -1; hits = 0; hitCounter = 0; window_height = 127; window_width = 127; oldx = x; oldy = y; WDT_A_holdTimer(); LCD_init(); SysTick_setPeriod(30000); SysTick_enableInterrupt(); while (1) { if (flag == 1) { erase(oldx, oldy, radius); //擦除上一位置的小球 //先更新位置 oldx = x; oldy = y; x += vx; y += vy; //检测X方向边界碰撞 if (x + radius >= window_width || x - radius <= 0) { //将小球拉回屏幕内 x = (x + radius >= window_width) ? (window_width - radius) : radius; vx = -vx; //反转X方向速度 //速度递增,最大15 if ((vx < 0 && -vx < 15) || (vx > 0 && vx < 15)) vx += (vx > 0) ? 1 : -1; hits++; } //检测Y方向边界碰撞 if (y + radius >= window_height || y - radius <= 0) { //将小球拉回屏幕内 y = (y + radius >= window_height) ? (window_height - radius) : radius; vy = -vy; //反转Y方向速度 //速度递增,最大15 if ((vy < 0 && -vy < 15) || (vy > 0 && vy < 15)) vy += (vy > 0) ? 1 : -1; hits++; } //每50次碰壁重置速度为±1 if (hits >= 50) { hits = 0; hitCounter++; vx = (vx > 0) ? 1 : -1; vy = (vy > 0) ? 1 : -1; } draw(x, y, radius); //绘制新位置的小球 flag = 0; } } }
内容的提问来源于stack exchange,提问作者Srijon Datta
相关产品推荐
相关产品推荐

