You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
         }
     }
   }

问题根源

  1. 角落碰撞逻辑冲突:先检测碰撞再更新位置,导致角落处两个方向的碰撞无法同时触发,且位置更新后直接超出边界。
  2. 未修正越界位置:碰撞后仅反转速度,未将小球拉回屏幕内,后续循环中位置持续越界,无法触发正确检测。
  3. 速度逻辑冗余错误:用方向标志重置速度再递增的逻辑,会导致速度方向与实际运动方向不一致,尤其在角落碰撞时。

修复方案

直接替换为以下代码,核心修改点如下:

  • 移除冗余的方向标志,直接通过速度符号判断运动方向
  • 先更新位置再检测碰撞,确保基于最新位置判断边界
  • 碰撞时立即将小球拉回屏幕边缘,避免持续越界
  • 简化速度反转逻辑,直接反转符号并基于当前速度递增
  • 支持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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 07:54:56