From ba3cbd401deb060e1b5fac1ea0356bd5fe871d56 Mon Sep 17 00:00:00 2001 From: sigonasr2 Date: Thu, 28 Nov 2024 05:11:31 -0600 Subject: [PATCH] Fix potential bug with sending too many updates too quickly. Add in input handling. --- sample/PGEX_SMX.h | 96 ++++++++++++++++------ sample/PGEX_SMX_Example.cpp | 158 +++++++++++++++++++++++++++++++++--- 2 files changed, 215 insertions(+), 39 deletions(-) diff --git a/sample/PGEX_SMX.h b/sample/PGEX_SMX.h index f302457..5751cc0 100644 --- a/sample/PGEX_SMX.h +++ b/sample/PGEX_SMX.h @@ -1,6 +1,7 @@ #pragma once #include "olcPixelGameEngine.h" #include "SMX.h" +#include using namespace olc; @@ -15,15 +16,17 @@ public: //LEFT is 0x08 //Add these bytes together to get the combination (all 4 held down is 0xAA HWButton GetPanel(Key k,int pad=0)const{ - return panelState[k-UP]; + return panelState[k-UP][pad]; }; void EnableLogMessages(bool enabled){ logMessages=enabled; } private: static bool logMessages; - HWButton internal_panelState[4]; - HWButton panelState[4]; //We store the 4 main panel states, similar to how PGE does. + HWButton internal_panelState[4][2]; + HWButton panelState[4][2]; //We store the 4 main panel states, similar to how PGE does. + double totalElapsedTime{0.}; + double nextLightUpdate{0.}; //SMX "screen" is 12x21 //Each panel is 4x7 static void SMXStateChangedCallback(int pad, SMXUpdateCallbackReason reason, void *pUser) @@ -44,31 +47,57 @@ private: { if(logMessages){ printf("Device %i state changed: %04x\n", pad, SMX_GetInputState(pad)); + printf("Device %i state changed: %04x\n", 1, SMX_GetInputState(1)); } - int16_t state(SMX_GetInputState(pad)); - if(state&0x2&&!internal_panelState[UP-UP].bPressed){ - internal_panelState[UP-UP]={true,false,true}; + int16_t state(SMX_GetInputState(0)); + if(state&0x2&&!internal_panelState[UP-UP][0].bPressed){ + internal_panelState[UP-UP][0]={true,false,true}; }else - if(!(state&0x2)&&!internal_panelState[UP-UP].bReleased){ - internal_panelState[UP-UP]={false,true,false}; + if(!(state&0x2)&&!internal_panelState[UP-UP][0].bReleased){ + internal_panelState[UP-UP][0]={false,true,false}; } - if(state&0x20&&!internal_panelState[RIGHT-UP].bPressed){ - internal_panelState[RIGHT-UP]={true,false,true}; + if(state&0x20&&!internal_panelState[RIGHT-UP][0].bPressed){ + internal_panelState[RIGHT-UP][0]={true,false,true}; }else - if(!(state&0x20)&&!internal_panelState[RIGHT-UP].bReleased){ - internal_panelState[RIGHT-UP]={false,true,false}; + if(!(state&0x20)&&!internal_panelState[RIGHT-UP][0].bReleased){ + internal_panelState[RIGHT-UP][0]={false,true,false}; } - if(state&0x80&&!internal_panelState[DOWN-UP].bPressed){ - internal_panelState[DOWN-UP]={true,false,true}; + if(state&0x80&&!internal_panelState[DOWN-UP][0].bPressed){ + internal_panelState[DOWN-UP][0]={true,false,true}; }else - if(!(state&0x80)&&!internal_panelState[DOWN-UP].bReleased){ - internal_panelState[DOWN-UP]={false,true,false}; + if(!(state&0x80)&&!internal_panelState[DOWN-UP][0].bReleased){ + internal_panelState[DOWN-UP][0]={false,true,false}; } - if(state&0x8&&!internal_panelState[LEFT-UP].bPressed){ - internal_panelState[LEFT-UP]={true,false,true}; + if(state&0x8&&!internal_panelState[LEFT-UP][0].bPressed){ + internal_panelState[LEFT-UP][0]={true,false,true}; }else - if(!(state&0x8)&&!internal_panelState[LEFT-UP].bReleased){ - internal_panelState[LEFT-UP]={false,true,false}; + if(!(state&0x8)&&!internal_panelState[LEFT-UP][0].bReleased){ + internal_panelState[LEFT-UP][0]={false,true,false}; + } + state=SMX_GetInputState(1); + if(state&0x2&&!internal_panelState[UP-UP][1].bPressed){ + internal_panelState[UP-UP][1]={true,false,true}; + }else + if(!(state&0x2)&&!internal_panelState[UP-UP][1].bReleased){ + internal_panelState[UP-UP][1]={false,true,false}; + } + if(state&0x20&&!internal_panelState[RIGHT-UP][1].bPressed){ + internal_panelState[RIGHT-UP][1]={true,false,true}; + }else + if(!(state&0x20)&&!internal_panelState[RIGHT-UP][1].bReleased){ + internal_panelState[RIGHT-UP][1]={false,true,false}; + } + if(state&0x80&&!internal_panelState[DOWN-UP][1].bPressed){ + internal_panelState[DOWN-UP][1]={true,false,true}; + }else + if(!(state&0x80)&&!internal_panelState[DOWN-UP][1].bReleased){ + internal_panelState[DOWN-UP][1]={false,true,false}; + } + if(state&0x8&&!internal_panelState[LEFT-UP][1].bPressed){ + internal_panelState[LEFT-UP][1]={true,false,true}; + }else + if(!(state&0x8)&&!internal_panelState[LEFT-UP][1].bReleased){ + internal_panelState[LEFT-UP][1]={false,true,false}; } } @@ -79,18 +108,29 @@ private: virtual bool OnBeforeUserUpdate(float& fElapsedTime){ //We don't want the panel states changing in the middle of a frame, since the input polling is on a separate thread. //Copy over the internal button states to the current panel state. - for(int i=0;i<4;i++){ - panelState[i]=internal_panelState[i]; - internal_panelState[i].bPressed=false; - internal_panelState[i].bReleased=false; + for(int pad=0;pad<2;pad++){ + for(int i=0;i<4;i++){ + panelState[i][pad]=internal_panelState[i][pad]; + internal_panelState[i][pad].bPressed=false; + internal_panelState[i][pad].bReleased=false; + } } return false; } virtual void OnAfterUserUpdate(float fElapsedTime)override{ - for(int i=0;i<4;i++){ - panelState[i].bPressed=false; - panelState[i].bReleased=false; + double previousTotalElapsedTime{totalElapsedTime}; + totalElapsedTime=fmod(totalElapsedTime+fElapsedTime,10000.); + if(previousTotalElapsedTime>totalElapsedTime){ + //We are in the past, so reset lastLightUpdate to keep lights running. + nextLightUpdate=totalElapsedTime; + } + if(nextLightUpdate>totalElapsedTime)return; //Ignore light updates if not enough time has passed. We can only send light updates at 30 FPS. + for(int pad=0;pad<2;pad++){ + for(int i=0;i<4;i++){ + panelState[i][pad].bPressed=false; + panelState[i][pad].bReleased=false; + } } std::string lightData; //The light data for an SMX dance pad is outlined in the docs but the code used to transform the PGE's pixels to SMX pad's lights will be annotated here. @@ -131,6 +171,8 @@ private: } } } + if(lightData.size()!=1350)throw; + nextLightUpdate=totalElapsedTime+1/30.f; SMX_SetLights2( lightData.data(), lightData.size() ); } }; diff --git a/sample/PGEX_SMX_Example.cpp b/sample/PGEX_SMX_Example.cpp index bace91a..1cc92b6 100644 --- a/sample/PGEX_SMX_Example.cpp +++ b/sample/PGEX_SMX_Example.cpp @@ -20,13 +20,27 @@ public: bool OnUserCreate() override { // Called once at the start, so create things here + smx.EnableLogMessages(true); return true; } - std::vectorsnow; - std::vectorbackgroundSnow; + struct Snow{ + vf2d pos; + int id; + }; - float backgroundSnowIntervalTimer{0.03f}; + struct ExpandCircle{ + vi2d pos; + float radius; + Pixel col; + }; + + std::vectorsnow; + std::vectorbackgroundSnow; + std::vectorexpandCircles; + + const float backgroundSnowInterval{0.1f}; + float backgroundSnowIntervalTimer{backgroundSnowInterval}; float backgroundSnowDirectionInterval{60.f}; #define RIGHT true @@ -35,37 +49,157 @@ public: float snowTimer{snowInterval}; const float snowIntervalChangeInterval{60.f}; float snowIntervalTimer{snowIntervalChangeInterval}; - double totalElapsedTime{}; + double totalElapsedTime{0.}; float fallSpd{6.f}; float snowAmplitude{2.f}; + std::map,float>recreateCircleTimer{}; + const float recreateCircleInterval{0.25f}; + + uint64_t SNOW_ID{0U}; + bool OnUserUpdate(float fElapsedTime) override { Clear({0,0,32}); backgroundSnowIntervalTimer-=fElapsedTime; snowTimer-=fElapsedTime; snowIntervalTimer-=fElapsedTime; + for(auto&[KEY,time]:recreateCircleTimer){ + recreateCircleTimer[KEY]-=fElapsedTime; + } if(snowTimer<=0.f){ - snow.emplace_back(util::random(ScreenWidth()),-2.f); + snow.emplace_back(vf2d{util::random(ScreenWidth()),-2.f},SNOW_ID++); snowTimer=snowInterval; } if(backgroundSnowIntervalTimer<=0.f){ - backgroundSnowIntervalTimer=snowIntervalTimer*26.66666667f; + int xOffset{int(util::random(ScreenWidth()))}; + if(backgroundSnowDirection==RIGHT){ + xOffset-=ScreenWidth()/2; + }else{ + xOffset+=ScreenWidth()/2; + } + backgroundSnow.emplace_back(vf2d{float(xOffset),-2.f},SNOW_ID++); + backgroundSnowIntervalTimer=backgroundSnowInterval; } if(snowIntervalTimer<=0.f){ snowInterval=util::random_range(0.1f,2.f); + backgroundSnowDirection=!RIGHT; + if(rand()%2==0)backgroundSnowDirection=RIGHT; snowIntervalTimer=snowIntervalChangeInterval; } - for(int ind{0};vf2d&snow:snow){ - snow.y+=fallSpd*fElapsedTime; - srand(ind); - FillCircle(vi2d{int(snow.x+sin(totalElapsedTime+ind*0.5f*fallSpd)*snowAmplitude),int(snow.y)},rand()%2+1,{uint8_t(200+rand()%55),uint8_t(235+rand()%20),255}); - ind++; + for(Snow&backSnow:backgroundSnow){ + srand(backSnow.id); + if(backgroundSnowDirection==RIGHT){ + backSnow.pos.x+=fElapsedTime*snowAmplitude; + }else{ + backSnow.pos.x-=fElapsedTime*snowAmplitude; + } + backSnow.pos.y+=fallSpd*fElapsedTime/2.f; + uint8_t randCol{uint8_t(10+rand()%50)}; + Draw(backSnow.pos,Pixel{randCol,randCol,randCol}); + } + for(Snow&snow:snow){ + srand(snow.id); + float fallSpdMult{(rand()%25)/100.f}; + snow.pos.y+=(fallSpd*(1.f-fallSpdMult))*fElapsedTime; + FillCircle(vi2d{int(snow.pos.x+sin(totalElapsedTime+snow.id*0.5f*fallSpd)*snowAmplitude),int(snow.pos.y)},rand()%2+1,{uint8_t(150+rand()%105),uint8_t(190+rand()%65),uint8_t(190+rand()%65)}); } + SetPixelMode(Pixel::ALPHA); + for(ExpandCircle&circle:expandCircles){ + circle.radius+=fallSpd*fElapsedTime*6; + DrawCircle(circle.pos,circle.radius,circle.col); + } + + std::erase_if(snow,[this](const Snow&snow){return snow.pos.y>ScreenHeight()+2;}); + std::erase_if(backgroundSnow,[this](const Snow&backSnow){return backSnow.pos.y>ScreenHeight()+2;}); + std::erase_if(expandCircles,[this](const ExpandCircle&circle){return circle.radius>24+2;}); + double previousTotalElapsedTime{totalElapsedTime}; totalElapsedTime=fmod(totalElapsedTime+fElapsedTime,10000.); + #undef RIGHT + if(smx.GetPanel(RIGHT,0).bHeld){ + FillRect({8,7},{3,6},{VERY_DARK_MAGENTA.r,VERY_DARK_MAGENTA.g,VERY_DARK_MAGENTA.b,64}); + DrawRect({8,7},{3,6},VERY_DARK_MAGENTA); + if(recreateCircleTimer[{RIGHT,0}]<=0.f){ + expandCircles.emplace_back(vi2d{8+1,7+3},1,Pixel{DARK_MAGENTA.r,DARK_MAGENTA.g,DARK_MAGENTA.b,64}); + expandCircles.emplace_back(vi2d{8+1,7+3},0.5f,Pixel{DARK_MAGENTA.r,DARK_MAGENTA.g,DARK_MAGENTA.b,64}); + expandCircles.emplace_back(vi2d{8+1,7+3},0.f,Pixel{DARK_MAGENTA.r,DARK_MAGENTA.g,DARK_MAGENTA.b,64}); + recreateCircleTimer[{RIGHT,0}]=recreateCircleInterval; + } + } + if(smx.GetPanel(UP,0).bHeld){ + FillRect({4,0},{3,6},{VERY_DARK_MAGENTA.r,VERY_DARK_MAGENTA.g,VERY_DARK_MAGENTA.b,64}); + DrawRect({4,0},{3,6},VERY_DARK_MAGENTA); + if(recreateCircleTimer[{UP,0}]<=0.f){ + expandCircles.emplace_back(vi2d{4+1,0+3},1,Pixel{DARK_MAGENTA.r,DARK_MAGENTA.g,DARK_MAGENTA.b,64}); + expandCircles.emplace_back(vi2d{4+1,0+3},0.5f,Pixel{DARK_MAGENTA.r,DARK_MAGENTA.g,DARK_MAGENTA.b,64}); + expandCircles.emplace_back(vi2d{4+1,0+3},0.f,Pixel{DARK_MAGENTA.r,DARK_MAGENTA.g,DARK_MAGENTA.b,64}); + recreateCircleTimer[{UP,0}]=recreateCircleInterval; + } + } + if(smx.GetPanel(DOWN,0).bHeld){ + FillRect({4,14},{3,6},{VERY_DARK_MAGENTA.r,VERY_DARK_MAGENTA.g,VERY_DARK_MAGENTA.b,64}); + DrawRect({4,14},{3,6},VERY_DARK_MAGENTA); + if(recreateCircleTimer[{DOWN,0}]<=0.f){ + expandCircles.emplace_back(vi2d{4+1,14+3},1,Pixel{DARK_MAGENTA.r,DARK_MAGENTA.g,DARK_MAGENTA.b,64}); + expandCircles.emplace_back(vi2d{4+1,14+3},0.5f,Pixel{DARK_MAGENTA.r,DARK_MAGENTA.g,DARK_MAGENTA.b,64}); + expandCircles.emplace_back(vi2d{4+1,14+3},0.f,Pixel{DARK_MAGENTA.r,DARK_MAGENTA.g,DARK_MAGENTA.b,64}); + recreateCircleTimer[{DOWN,0}]=recreateCircleInterval; + } + } + if(smx.GetPanel(LEFT,0).bHeld){ + FillRect({0,7},{3,6},{VERY_DARK_MAGENTA.r,VERY_DARK_MAGENTA.g,VERY_DARK_MAGENTA.b,64}); + DrawRect({0,7},{3,6},VERY_DARK_MAGENTA); + if(recreateCircleTimer[{LEFT,0}]<=0.f){ + expandCircles.emplace_back(vi2d{0+1,7+3},1,Pixel{DARK_MAGENTA.r,DARK_MAGENTA.g,DARK_MAGENTA.b,64}); + expandCircles.emplace_back(vi2d{0+1,7+3},0.5f,Pixel{DARK_MAGENTA.r,DARK_MAGENTA.g,DARK_MAGENTA.b,64}); + expandCircles.emplace_back(vi2d{0+1,7+3},0.f,Pixel{DARK_MAGENTA.r,DARK_MAGENTA.g,DARK_MAGENTA.b,64}); + recreateCircleTimer[{LEFT,0}]=recreateCircleInterval; + } + } + if(smx.GetPanel(RIGHT,1).bHeld){ + FillRect({20,7},{3,6},{VERY_DARK_CYAN.r,VERY_DARK_CYAN.g,VERY_DARK_CYAN.b,64}); + DrawRect({20,7},{3,6},VERY_DARK_CYAN); + if(recreateCircleTimer[{RIGHT,1}]<=0.f){ + expandCircles.emplace_back(vi2d{20+1,7+3},1,Pixel{DARK_CYAN.r,DARK_CYAN.g,DARK_CYAN.b,64}); + expandCircles.emplace_back(vi2d{20+1,7+3},0.5f,Pixel{DARK_CYAN.r,DARK_CYAN.g,DARK_CYAN.b,64}); + expandCircles.emplace_back(vi2d{20+1,7+3},0.f,Pixel{DARK_CYAN.r,DARK_CYAN.g,DARK_CYAN.b,64}); + recreateCircleTimer[{RIGHT,1}]=recreateCircleInterval; + } + } + if(smx.GetPanel(UP,1).bHeld){ + FillRect({16,0},{3,6},{VERY_DARK_CYAN.r,VERY_DARK_CYAN.g,VERY_DARK_CYAN.b,64}); + DrawRect({16,0},{3,6},VERY_DARK_CYAN); + if(recreateCircleTimer[{UP,1}]<=0.f){ + expandCircles.emplace_back(vi2d{16+1,3+3},1,Pixel{DARK_CYAN.r,DARK_CYAN.g,DARK_CYAN.b,64}); + expandCircles.emplace_back(vi2d{16+1,3+3},0.5f,Pixel{DARK_CYAN.r,DARK_CYAN.g,DARK_CYAN.b,64}); + expandCircles.emplace_back(vi2d{16+1,3+3},0.f,Pixel{DARK_CYAN.r,DARK_CYAN.g,DARK_CYAN.b,64}); + recreateCircleTimer[{UP,1}]=recreateCircleInterval; + } + } + if(smx.GetPanel(DOWN,1).bHeld){ + FillRect({16,14},{3,6},{VERY_DARK_CYAN.r,VERY_DARK_CYAN.g,VERY_DARK_CYAN.b,64}); + DrawRect({16,14},{3,6},VERY_DARK_CYAN); + if(recreateCircleTimer[{DOWN,1}]<=0.f){ + expandCircles.emplace_back(vi2d{16+1,14+3},1,Pixel{DARK_CYAN.r,DARK_CYAN.g,DARK_CYAN.b,64}); + expandCircles.emplace_back(vi2d{16+1,14+3},0.5f,Pixel{DARK_CYAN.r,DARK_CYAN.g,DARK_CYAN.b,64}); + expandCircles.emplace_back(vi2d{16+1,14+3},0.f,Pixel{DARK_CYAN.r,DARK_CYAN.g,DARK_CYAN.b,64}); + recreateCircleTimer[{DOWN,1}]=recreateCircleInterval; + } + } + if(smx.GetPanel(LEFT,1).bHeld){ + FillRect({12,7},{3,6},{VERY_DARK_CYAN.r,VERY_DARK_CYAN.g,VERY_DARK_CYAN.b,64}); + DrawRect({12,7},{3,6},VERY_DARK_CYAN); + if(recreateCircleTimer[{LEFT,1}]<=0.f){ + expandCircles.emplace_back(vi2d{12+1,3+3},1,Pixel{DARK_CYAN.r,DARK_CYAN.g,DARK_CYAN.b,64}); + expandCircles.emplace_back(vi2d{12+1,3+3},0.5f,Pixel{DARK_CYAN.r,DARK_CYAN.g,DARK_CYAN.b,64}); + expandCircles.emplace_back(vi2d{12+1,3+3},0.f,Pixel{DARK_CYAN.r,DARK_CYAN.g,DARK_CYAN.b,64}); + recreateCircleTimer[{LEFT,1}]=recreateCircleInterval; + } + } + SetPixelMode(Pixel::MASK); return true; } @@ -75,7 +209,7 @@ public: int main() { SMX_PGE demo; - if (demo.Construct(24, 21, 50, 50)) + if (demo.Construct(24, 21, 50, 50,false,true)) demo.Start(); return 0;