├── .gitattributes ├── .gitignore ├── LEDSpriteDataGenerator.exe ├── LEDSprites.cpp ├── LEDSprites.h ├── README.md ├── examples ├── SpriteExample1 │ └── SpriteExample1.ino ├── SpriteExample2 │ └── SpriteExample2.ino ├── SpriteExample3 │ └── SpriteExample3.ino ├── SpriteExample4 │ └── SpriteExample4.ino └── Tetris │ └── Tetris.ino └── keywords.txt /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | -------------------------------------------------------------------------------- /LEDSpriteDataGenerator.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AaronLiddiment/LEDSprites/d817538263c5d263aaed64a51be8c89574dfe8bb/LEDSpriteDataGenerator.exe -------------------------------------------------------------------------------- /LEDSprites.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | LEDSprites V5 class by Aaron Liddiment (c) 2015 3 | 4 | Inspiration came from my old C64 days :) 5 | 6 | FastLED v3.1 library by Daniel Garcia and Mark Kriegsmann. 7 | Written & tested on a Teensy 3.1 using Arduino V1.0.5r2 & teensyduino V1.20 8 | */ 9 | 10 | #include "FastLED.h" 11 | #include "LEDMatrix.h" 12 | #include "LEDSprites.h" 13 | 14 | cSprite::cSprite(uint16_t Width, uint16_t Height, const uint8_t *Data, uint8_t NumFrames, SpriteNumBits_t BitsPixel, const struct CRGB *ColTable, const uint8_t *Mask) 15 | { 16 | if (Data) 17 | Setup(Width, Height, Data, NumFrames, BitsPixel, ColTable, Mask); 18 | } 19 | 20 | void cSprite::Setup(uint16_t Width, uint16_t Height, const uint8_t *Data, uint8_t NumFrames, SpriteNumBits_t BitsPixel, const struct CRGB *ColTable, const uint8_t *Mask) 21 | { 22 | m_Width = Width; 23 | m_Height = Height; 24 | m_Data = Data; 25 | m_NumFrames = NumFrames; 26 | m_BitsPixel = (uint8_t)BitsPixel; 27 | m_ColTable = ColTable; 28 | m_Mask = Mask; 29 | m_PrevSprite = m_NextSprite = NULL; 30 | m_X = m_Y = 0; 31 | m_Frame = m_FrameRate = m_XRate = m_YRate = m_CounterFrame = m_CounterX = m_CounterY = m_Options = m_Flags = 0; 32 | m_XChange = m_YChange = 0; 33 | m_NumCols = (1 << (uint16_t)m_BitsPixel) - 1; 34 | m_MaskSize = ((m_Width + 7) / 8) * m_Height; 35 | m_FrameSize = m_MaskSize * m_BitsPixel; 36 | } 37 | 38 | void cSprite::Update() 39 | { 40 | if (m_CounterFrame > 0) 41 | { 42 | if ((--m_CounterFrame) == 0) 43 | { 44 | m_CounterFrame = m_FrameRate; 45 | m_Frame++; 46 | if (m_Frame >= m_NumFrames) 47 | m_Frame = 0; 48 | } 49 | } 50 | if (m_CounterX > 0) 51 | { 52 | if ((--m_CounterX) == 0) 53 | { 54 | m_CounterX = m_XRate; 55 | if (m_Options & SPRITE_X_KEEPIN) 56 | { 57 | if ( ((m_Flags & SPRITE_EDGE_X_MIN) && (m_XChange < 0)) || ((m_Flags & SPRITE_EDGE_X_MAX) && (m_XChange > 0)) ) 58 | m_XChange = 0 - m_XChange; 59 | } 60 | m_X += m_XChange; 61 | } 62 | } 63 | if (m_CounterY > 0) 64 | { 65 | if ((--m_CounterY) == 0) 66 | { 67 | m_CounterY = m_YRate; 68 | if (m_Options & SPRITE_Y_KEEPIN) 69 | { 70 | if ( ((m_Flags & SPRITE_EDGE_Y_MIN) && (m_YChange < 0)) || ((m_Flags & SPRITE_EDGE_Y_MAX) && (m_YChange > 0)) ) 71 | m_YChange = 0 - m_YChange; 72 | } 73 | m_Y += m_YChange; 74 | } 75 | } 76 | } 77 | 78 | boolean cSprite::Combine(int16_t dx, int16_t dy, cSprite *Src) 79 | { 80 | // Purely added to make Tetris example! Note that the colour tables have to be the same! 81 | if ( (m_BitsPixel != Src->m_BitsPixel) || (Src->m_Mask == NULL) || (m_Mask == NULL) ) 82 | return(false); 83 | else 84 | { 85 | uint8_t *SrcData = (uint8_t *)&(Src->m_Data[Src->m_Frame * Src->m_FrameSize]); 86 | uint8_t *DstData = (uint8_t *)&(m_Data[(m_Frame * m_FrameSize) + ((dx / 8) * m_BitsPixel) + (((m_Height - Src->m_Height) - dy) * ((m_Width + 7) / 8) * m_BitsPixel)]); 87 | uint8_t *SrcMask = (uint8_t *)&(Src->m_Mask[Src->m_Frame * Src->m_MaskSize]); 88 | uint8_t *DstMask = (uint8_t *)&(m_Mask[(m_Frame * m_MaskSize) + (dx / 8) + (((m_Height - Src->m_Height) - dy) * ((m_Width + 7) / 8))]); 89 | int8_t DSh = 16 - m_BitsPixel; 90 | uint8_t DMb = 0x80; 91 | for (int8_t spx=dx%8; spx>0; --spx,DSh-=m_BitsPixel,DMb>>=1) 92 | { 93 | if (DSh <= (8 - m_BitsPixel)) 94 | { 95 | DstData++; 96 | DSh += 8; 97 | } 98 | } 99 | for (int16_t k=Src->m_Height; k>0; --k) 100 | { 101 | uint8_t *dstData = DstData; 102 | int8_t dSh = DSh; 103 | uint8_t *dstMask = DstMask; 104 | uint8_t dMb = DMb; 105 | uint16_t sfd = ((*SrcData) << 8) | *(SrcData+1); 106 | SrcData++; 107 | int8_t sSh = 16 - m_BitsPixel; 108 | uint8_t sMb = 0x80; 109 | int16_t j; 110 | for (j=0; jm_Width; ++j,sSh-=m_BitsPixel,dSh-=m_BitsPixel) 111 | { 112 | if (sSh <= (8 - m_BitsPixel)) 113 | { 114 | sfd = (sfd << 8) | *(++SrcData); 115 | sSh += 8; 116 | } 117 | if (dSh <= (8 - m_BitsPixel)) 118 | { 119 | dstData++; 120 | dSh += 8; 121 | } 122 | if (*SrcMask & sMb) 123 | { 124 | uint16_t dfd = ((*dstData) << 8) | *(dstData+1); 125 | dfd = (dfd & ((m_NumCols << dSh) ^ 0xffff)) | (((sfd >> sSh) & m_NumCols) << dSh); 126 | *dstData = dfd >> 8; 127 | *(dstData + 1) = dfd & 0xff; 128 | *dstMask |= dMb; 129 | } 130 | if ((sMb>>=1) == 0x00) 131 | { 132 | sMb = 0x80; 133 | SrcMask++; 134 | } 135 | if ((dMb>>=1) == 0x00) 136 | { 137 | dMb = 0x80; 138 | dstMask++; 139 | } 140 | } 141 | if (m_BitsPixel < 8) 142 | DstData = &DstData[((m_Width + 7) / 8) * m_BitsPixel]; 143 | else 144 | DstData = &DstData[m_Width * m_BitsPixel]; 145 | DstMask = &DstMask[(m_Width + 7) / 8]; 146 | if (m_BitsPixel < 8) 147 | { 148 | while ((j % 8) != 0) 149 | { 150 | if (sSh <= (8 - m_BitsPixel)) 151 | { 152 | ++SrcData; 153 | sSh += 8; 154 | } 155 | sSh-=m_BitsPixel; 156 | ++j; 157 | } 158 | } 159 | if (sMb != 0x80) 160 | SrcMask++; 161 | } 162 | return(true); 163 | } 164 | } 165 | 166 | void cSprite::Render(cLEDMatrixBase *Matrix) 167 | { 168 | int16_t y = m_Y + m_Height - 1; 169 | uint8_t *sprframedata = (uint8_t *)&(m_Data[m_Frame * m_FrameSize]); 170 | 171 | for (int16_t i=m_Height; i>0; --i,--y) 172 | { 173 | int16_t x = m_X; 174 | uint16_t sfd = ((*sprframedata) << 8) | *(sprframedata+1); 175 | sprframedata++; 176 | int8_t sSh = 16 - m_BitsPixel; 177 | int16_t j; 178 | for (j=0; j> sSh) & m_NumCols; 186 | if (col > 0) 187 | (*Matrix)(x, y) = m_ColTable[col - 1]; 188 | } 189 | if (m_BitsPixel < 8) 190 | { 191 | while ((j % 8) != 0) 192 | { 193 | if (sSh <= (8 - m_BitsPixel)) 194 | { 195 | ++sprframedata; 196 | sSh += 8; 197 | } 198 | sSh-=m_BitsPixel; 199 | ++j; 200 | } 201 | } 202 | } 203 | } 204 | 205 | void cSprite::EdgeDetect(cLEDMatrixBase *Matrix) 206 | { 207 | bool onXMatrix = false, onYMatrix = false, onEdgeXMin = false, onEdgeXMax = false, onEdgeYMin = false, onEdgeYMax = false; 208 | 209 | if (m_Mask == NULL) 210 | return; 211 | if ((m_X + m_Width) <= 0) 212 | onEdgeXMin = true; 213 | else if (m_X >= Matrix->Width()) 214 | onEdgeXMax = true; 215 | else 216 | { 217 | uint8_t *Mask = (uint8_t *)&m_Mask[m_Frame * m_MaskSize]; 218 | uint16_t XCnt = (m_Width + 7) / 8; 219 | int16_t xoff; 220 | if (m_X <= 0) 221 | xoff = max(0, 0 - m_X); 222 | else 223 | xoff = max(0, (Matrix->Width() - 1) - m_X); 224 | uint8_t XBit = xoff % 8; 225 | uint8_t XMid = xoff / 8; 226 | for (int16_t y=(m_Y+m_Height)-1; y>=m_Y; --y) 227 | { 228 | if ((y >= 0) && (y < Matrix->Height())) 229 | { 230 | for (uint8_t x=0; x> XBit))) || ((x > XMid) && (Mask[x])) ) 243 | { 244 | if (m_X <= 0) 245 | { 246 | onXMatrix = true; 247 | onYMatrix = true; 248 | } 249 | else 250 | onEdgeXMax = true; 251 | } 252 | } 253 | } 254 | Mask = &Mask[XCnt]; 255 | } 256 | } 257 | if ((m_Y + m_Height) <= 0) 258 | onEdgeYMin = true; 259 | else if (m_Y >= Matrix->Height()) 260 | onEdgeYMax = true; 261 | else 262 | { 263 | uint8_t *Mask = (uint8_t *)&m_Mask[m_Frame * m_MaskSize]; 264 | uint16_t XCnt = (m_Width + 7) / 8; 265 | for (int16_t y=(m_Y+m_Height)-1; y>=m_Y; --y) 266 | { 267 | for (uint8_t x=0; x= (Matrix->Height() - 1)) 274 | onEdgeYMax = true; 275 | if ( (y >= 0) && (y < Matrix->Height()) ) 276 | onYMatrix = true; 277 | } 278 | } 279 | Mask = &Mask[XCnt]; 280 | } 281 | } 282 | m_Flags &= (~ (SPRITE_EDGE_MASK | SPRITE_OFF_MASK)); 283 | if (onXMatrix) 284 | { 285 | if (onEdgeXMin) 286 | m_Flags |= SPRITE_EDGE_X_MIN; 287 | if (onEdgeXMax) 288 | m_Flags |= SPRITE_EDGE_X_MAX; 289 | } 290 | else if ((onEdgeXMin) || (onEdgeXMax)) 291 | m_Flags |= SPRITE_MATRIX_X_OFF; 292 | if (onYMatrix) 293 | { 294 | if (onEdgeYMin) 295 | m_Flags |= SPRITE_EDGE_Y_MIN; 296 | if (onEdgeYMax) 297 | m_Flags |= SPRITE_EDGE_Y_MAX; 298 | } 299 | else if ((onEdgeYMin) || (onEdgeYMax)) 300 | m_Flags |= SPRITE_MATRIX_Y_OFF; 301 | } 302 | 303 | void cSprite::SetPosition(int16_t X, int16_t Y) 304 | { 305 | m_X = X; 306 | m_Y = Y; 307 | } 308 | 309 | void cSprite::SetFrame(uint8_t Frame, uint8_t FrameRate) 310 | { 311 | m_Frame = min(m_NumFrames - 1, Frame); 312 | m_FrameRate = m_CounterFrame = FrameRate; 313 | } 314 | 315 | void cSprite::SetMotion(int8_t XChange, uint8_t XRate, int8_t YChange, uint8_t YRate) 316 | { 317 | m_XChange = XChange; 318 | m_XRate = m_CounterX = XRate; 319 | m_YChange = YChange; 320 | m_YRate = m_CounterY = YRate; 321 | } 322 | 323 | void cSprite::SetOptions(uint8_t Options) 324 | { 325 | m_Options = Options; 326 | } 327 | 328 | void cSprite::SetPositionFrameMotionOptions(int16_t X, int16_t Y, uint8_t Frame, uint8_t FrameRate, int8_t XChange, uint8_t XRate, int8_t YChange, uint8_t YRate, uint8_t Options) 329 | { 330 | SetPosition(X, Y); 331 | SetFrame(Frame, FrameRate); 332 | SetMotion(XChange, XRate, YChange, YRate); 333 | SetOptions(Options); 334 | } 335 | 336 | 337 | cLEDSprites::cLEDSprites(cLEDMatrixBase *Matrix) 338 | { 339 | m_Matrix = Matrix; 340 | m_SpriteHead = NULL; 341 | } 342 | 343 | void cLEDSprites::AddSprite(cSprite *Spr) 344 | { 345 | if (m_SpriteHead == NULL) 346 | { 347 | m_SpriteHead = Spr; 348 | Spr->m_PrevSprite = Spr->m_NextSprite = NULL; 349 | } 350 | else 351 | { 352 | if (IsSprite(Spr) == false) 353 | { 354 | cSprite *tmpSpr = m_SpriteHead; 355 | while (tmpSpr->m_NextSprite != NULL) 356 | tmpSpr = tmpSpr->m_NextSprite; 357 | tmpSpr->m_NextSprite = Spr; 358 | Spr->m_PrevSprite = tmpSpr; 359 | Spr->m_NextSprite = NULL; 360 | } 361 | } 362 | } 363 | 364 | boolean cLEDSprites::IsSprite(cSprite *Spr) 365 | { 366 | cSprite *tmpSpr = m_SpriteHead; 367 | while ((tmpSpr) && (tmpSpr != Spr)) 368 | tmpSpr = tmpSpr->m_NextSprite; 369 | if (tmpSpr) 370 | return(true); 371 | else 372 | return(false); 373 | } 374 | 375 | void cLEDSprites::RemoveSprite(cSprite *Spr) 376 | { 377 | if (IsSprite(Spr) == true) 378 | { 379 | if (Spr->m_PrevSprite != NULL) 380 | (Spr->m_PrevSprite)->m_NextSprite = Spr->m_NextSprite; 381 | if (Spr->m_NextSprite != NULL) 382 | (Spr->m_NextSprite)->m_PrevSprite = Spr->m_PrevSprite; 383 | if (m_SpriteHead == Spr) 384 | m_SpriteHead = Spr->m_NextSprite; 385 | Spr->m_PrevSprite = Spr->m_NextSprite = NULL; 386 | } 387 | } 388 | 389 | void cLEDSprites::RemoveAllSprites() 390 | { 391 | cSprite *tmpSpr; 392 | 393 | while ((tmpSpr = m_SpriteHead) != NULL) 394 | { 395 | m_SpriteHead = tmpSpr->m_NextSprite; 396 | tmpSpr->m_PrevSprite = tmpSpr->m_NextSprite = NULL; 397 | } 398 | } 399 | 400 | void cLEDSprites::ChangePriority(cSprite *Spr, SpritePriority_t Priority) 401 | { 402 | if (IsSprite(Spr) == true) 403 | { 404 | if (Priority == SPR_FRONT) 405 | { 406 | if (Spr->m_NextSprite != NULL) 407 | { 408 | cSprite *tmpSpr; 409 | if (Spr->m_PrevSprite != NULL) 410 | (Spr->m_PrevSprite)->m_NextSprite = Spr->m_NextSprite; 411 | else 412 | m_SpriteHead = Spr->m_NextSprite; 413 | (Spr->m_NextSprite)->m_PrevSprite = Spr->m_PrevSprite; 414 | tmpSpr = Spr->m_NextSprite; 415 | while (tmpSpr->m_NextSprite) 416 | tmpSpr = tmpSpr->m_NextSprite; 417 | tmpSpr->m_NextSprite = Spr; 418 | Spr->m_PrevSprite = tmpSpr; 419 | Spr->m_NextSprite = NULL; 420 | } 421 | } 422 | else if (Priority == SPR_FORWARD) 423 | { 424 | if (Spr->m_NextSprite != NULL) 425 | { 426 | if (Spr->m_PrevSprite != NULL) 427 | (Spr->m_PrevSprite)->m_NextSprite = Spr->m_NextSprite; 428 | else 429 | m_SpriteHead = Spr->m_NextSprite; 430 | (Spr->m_NextSprite)->m_PrevSprite = Spr->m_PrevSprite; 431 | Spr->m_PrevSprite = Spr->m_NextSprite; 432 | Spr->m_NextSprite = (Spr->m_NextSprite)->m_NextSprite; 433 | (Spr->m_PrevSprite)->m_NextSprite = Spr; 434 | if (Spr->m_NextSprite != NULL) 435 | (Spr->m_NextSprite)->m_PrevSprite = Spr; 436 | } 437 | } 438 | else if (Priority == SPR_BACKWARD) 439 | { 440 | if (Spr->m_PrevSprite != NULL) 441 | { 442 | if (Spr->m_NextSprite != NULL) 443 | (Spr->m_NextSprite)->m_PrevSprite = Spr->m_PrevSprite; 444 | (Spr->m_PrevSprite)->m_NextSprite = Spr->m_NextSprite; 445 | Spr->m_NextSprite = Spr->m_PrevSprite; 446 | Spr->m_PrevSprite = (Spr->m_PrevSprite)->m_PrevSprite; 447 | (Spr->m_NextSprite)->m_PrevSprite = Spr; 448 | if (Spr->m_PrevSprite != NULL) 449 | (Spr->m_PrevSprite)->m_NextSprite = Spr; 450 | else 451 | m_SpriteHead = Spr; 452 | } 453 | } 454 | else if (Priority == SPR_BACK) 455 | { 456 | if (Spr->m_PrevSprite != NULL) 457 | { 458 | if (Spr->m_NextSprite != NULL) 459 | (Spr->m_NextSprite)->m_PrevSprite = Spr->m_PrevSprite; 460 | (Spr->m_PrevSprite)->m_NextSprite = Spr->m_NextSprite; 461 | m_SpriteHead->m_PrevSprite = Spr; 462 | Spr->m_NextSprite = m_SpriteHead; 463 | m_SpriteHead = Spr; 464 | Spr->m_PrevSprite = NULL; 465 | } 466 | } 467 | } 468 | } 469 | 470 | void cLEDSprites::UpdateSprites() 471 | { 472 | cSprite *tmpSpr = m_SpriteHead; 473 | while (tmpSpr) 474 | { 475 | tmpSpr->Update(); 476 | if (tmpSpr->m_Options & SPRITE_DETECT_EDGE) 477 | tmpSpr->EdgeDetect(m_Matrix); 478 | tmpSpr = tmpSpr->m_NextSprite; 479 | } 480 | } 481 | 482 | void cLEDSprites::RenderSprites() 483 | { 484 | cSprite *tmpSpr = m_SpriteHead; 485 | while (tmpSpr) 486 | { 487 | tmpSpr->Render(m_Matrix); 488 | tmpSpr = tmpSpr->m_NextSprite; 489 | } 490 | } 491 | 492 | void cLEDSprites::DetectCollisions(cSprite *srcSpr) 493 | { 494 | cSprite *colSpr[2]; 495 | bool FirstLoop = true; 496 | 497 | if (srcSpr != NULL) 498 | colSpr[0] = srcSpr; 499 | else 500 | colSpr[0] = m_SpriteHead; 501 | while (colSpr[0] != NULL) 502 | { 503 | if (FirstLoop) 504 | colSpr[0]->m_Flags &= (~SPRITE_COLLISION); // Clear collision flag 505 | if (srcSpr != NULL) 506 | colSpr[1] = m_SpriteHead; 507 | else 508 | colSpr[1] = colSpr[0]->m_NextSprite; 509 | while (colSpr[1] != NULL) 510 | { 511 | while ((colSpr[0] == colSpr[1]) && (colSpr[1] != NULL)) 512 | colSpr[1] = colSpr[1]->m_NextSprite; 513 | if (colSpr[1] != NULL) 514 | { 515 | if (FirstLoop) 516 | colSpr[1]->m_Flags &= (~SPRITE_COLLISION); // Clear collision flag 517 | if ((colSpr[0]->m_Options & SPRITE_DETECT_COLLISION) || (colSpr[1]->m_Options & SPRITE_DETECT_COLLISION)) 518 | { 519 | if ( (colSpr[0]->m_Mask != NULL) && (colSpr[1]->m_Mask != NULL) ) 520 | { 521 | bool chkColission = false; 522 | if ( (colSpr[0]->m_X < (colSpr[1]->m_X + colSpr[1]->m_Width)) && (colSpr[1]->m_X < (colSpr[0]->m_X + colSpr[0]->m_Width)) 523 | && (colSpr[0]->m_Y < (colSpr[1]->m_Y + colSpr[1]->m_Height)) && (colSpr[1]->m_Y < (colSpr[0]->m_Y + colSpr[0]->m_Height)) ) 524 | { 525 | uint8_t *colMask[2], partX, partY, colXBit; 526 | int16_t colXCnt, colYCnt; 527 | uint16_t xoff, yoff; 528 | if (colSpr[0]->m_X <= colSpr[1]->m_X) 529 | partX = 0; 530 | else 531 | partX = 1; 532 | if ((colSpr[partX]->m_Y + colSpr[partX]->m_Height) >= (colSpr[1 - partX]->m_Y + colSpr[1 - partX]->m_Height)) 533 | partY = partX; 534 | else 535 | partY = 1 - partX; 536 | xoff = colSpr[1 - partX]->m_X - colSpr[partX]->m_X; 537 | colMask[partX] = (uint8_t *)&colSpr[partX]->m_Mask[(colSpr[partX]->m_Frame * colSpr[partX]->m_MaskSize) + (xoff / 8)]; 538 | colMask[1 - partX] = (uint8_t *)&colSpr[1 - partX]->m_Mask[(colSpr[1 - partX]->m_Frame * colSpr[1 - partX]->m_MaskSize)]; 539 | colXBit = xoff % 8; 540 | colXCnt = min(colSpr[partX]->m_Width - xoff, colSpr[1 - partX]->m_Width); 541 | yoff = (colSpr[partY]->m_Y + colSpr[partY]->m_Height) - (colSpr[1 - partY]->m_Y + colSpr[1 - partY]->m_Height); 542 | colMask[partY] = (uint8_t *)&colMask[partY][yoff * ((colSpr[partY]->m_Width + 7) / 8)]; 543 | colYCnt = min(colSpr[partY]->m_Height - yoff, colSpr[1 - partY]->m_Height); 544 | for (; colYCnt>0; --colYCnt) 545 | { 546 | uint8_t x = 0; 547 | int16_t tmpXCnt = colXCnt; 548 | do 549 | { 550 | uint8_t colAData = colMask[partX][x] << colXBit; 551 | if ((8 - colXBit) < colXCnt) 552 | colAData |= colMask[partX][x + 1] >> (8 - colXBit); 553 | if ((colAData & colMask[1 - partX][x]) != 0x00) 554 | { 555 | chkColission = true; 556 | break; 557 | } 558 | ++x; 559 | colXCnt -= 8; 560 | } 561 | while (colXCnt > 0); 562 | if (chkColission) 563 | break; 564 | colXCnt = tmpXCnt; 565 | colMask[0] = (uint8_t *)&colMask[0][(colSpr[0]->m_Width + 7) / 8]; 566 | colMask[1] = (uint8_t *)&colMask[1][(colSpr[1]->m_Width + 7) / 8]; 567 | } 568 | } 569 | if (chkColission) 570 | { 571 | colSpr[0]->m_Flags |= SPRITE_COLLISION; 572 | colSpr[1]->m_Flags |= SPRITE_COLLISION; 573 | } 574 | } 575 | } 576 | colSpr[1] = colSpr[1]->m_NextSprite; 577 | } 578 | } 579 | if (srcSpr != NULL) 580 | break; 581 | colSpr[0] = colSpr[0]->m_NextSprite; 582 | FirstLoop = false; 583 | } 584 | } 585 | -------------------------------------------------------------------------------- /LEDSprites.h: -------------------------------------------------------------------------------- 1 | #ifndef LEDSprites_h 2 | #define LEDSprites_h 3 | 4 | #define B8_1B__(x) (uint8_t)(((x&0x10000000LU)>>21)+((x&0x01000000LU)>>18)+((x&0x00100000LU)>>15)+((x&0x00010000LU)>>12)+ \ 5 | ((x&0x00001000LU)>>9)+((x&0x00000100LU)>>6)+((x&0x00000010LU)>>3)+((x&0x00000001LU))) 6 | #define B8_2B__(x) (uint8_t)(((x&0x30000000LU)>>22)+((x&0x03000000LU)>>20)+((x&0x00300000LU)>>18)+((x&0x00030000LU)>>16)), \ 7 | (uint8_t)(((x&0x00003000LU)>>6)+((x&0x00000300LU)>>4)+((x&0x00000030LU)>>2)+((x&0x00000003LU))) 8 | #define B8_3B__(x) (uint8_t)(((x&0x70000000LU)>>23)+((x&0x07000000LU)>>22)+((x&0x00700000LU)>>21)), \ 9 | (uint8_t)(((x&0x00700000LU)>>13)+((x&0x00070000LU)>>12)+((x&0x00007000LU)>>11)+((x&0x00000700LU)>>10)), \ 10 | (uint8_t)(((x&0x00000700LU)>>2)+((x&0x00000070LU)>>1)+((x&0x00000007LU))) 11 | #define B8_4B__(x) (uint8_t)(((x&0xf0000000LU)>>24)+((x&0x0f000000LU)>>24)), \ 12 | (uint8_t)(((x&0x00f00000LU)>>16)+((x&0x000f0000LU)>>16)), \ 13 | (uint8_t)(((x&0x0000f000LU)>>8)+((x&0x00000f00LU)>>8)), \ 14 | (uint8_t)((x&0x000000f0LU)+(x&0x0000000fLU)) 15 | #define B8_5B__(x) (uint8_t)(((x[0]>'9'?(x[0]-'A')+10:x[0]-'0')<<3)+(((x[1]>'9'?(x[1]-'A')+10:x[1]-'0')>>2)&0x07)), \ 16 | (uint8_t)(((x[1]>'9'?(x[1]-'A')+10:x[1]-'0')<<6)+((x[2]>'9'?(x[2]-'A')+10:x[2]-'0')<<1)+(((x[3]>'9'?(x[3]-'A')+10:x[3]-'0')>>4)&0x01)), \ 17 | (uint8_t)(((x[3]>'9'?(x[3]-'A')+10:x[3]-'0')<<4)+((x[4]>'9'?(x[4]-'A')+10:x[4]-'0')>>1)), \ 18 | (uint8_t)(((x[4]>'9'?(x[4]-'A')+10:x[4]-'0')<<7)+((x[5]>'9'?(x[5]-'A')+10:x[5]-'0')<<2)+(((x[6]>'9'?(x[6]-'A')+10:x[6]-'0')>>3)&0x03)), \ 19 | (uint8_t)(((x[6]>'9'?(x[6]-'A')+10:x[6]-'0')<<5)+(x[7]>'9'?(x[7]-'A')+10:x[7]-'0')) 20 | 21 | #define B8_1BIT(d) B8_1B__(0x##d##LU) 22 | #define B8_2BIT(d) B8_2B__(0x##d##LU) 23 | #define B8_3BIT(d) B8_3B__(0x##d##LU) 24 | #define B8_4BIT(d) B8_4B__(0x##d##LU) 25 | #define B8_5BIT(d) B8_5B__(#d) 26 | 27 | enum SpriteNumBits_t { _1BIT=1, 28 | _2BIT=2, 29 | _3BIT=3, 30 | _4BIT=4, 31 | _5BIT=5, 32 | _8BIT=8 }; 33 | 34 | enum SpritePriority_t { SPR_FRONT, 35 | SPR_BACK, 36 | SPR_FORWARD, 37 | SPR_BACKWARD }; 38 | 39 | // defines for m_Flags 40 | #define SPRITE_EDGE_X_MIN 0x01 41 | #define SPRITE_EDGE_X_MAX 0x02 42 | #define SPRITE_EDGE_Y_MIN 0x04 43 | #define SPRITE_EDGE_Y_MAX 0x08 44 | #define SPRITE_MATRIX_X_OFF 0x10 45 | #define SPRITE_MATRIX_Y_OFF 0x20 46 | #define SPRITE_COLLISION 0x40 47 | #define SPRITE_EDGE_MASK (SPRITE_EDGE_X_MIN | SPRITE_EDGE_X_MAX | SPRITE_EDGE_Y_MIN | SPRITE_EDGE_Y_MAX) 48 | #define SPRITE_OFF_MASK (SPRITE_MATRIX_X_OFF | SPRITE_MATRIX_Y_OFF) 49 | 50 | // defines for m_Options 51 | #define SPRITE_DETECT_EDGE 0x01 52 | #define SPRITE_DETECT_COLLISION 0x02 53 | #define SPRITE_X_KEEPIN 0x40 54 | #define SPRITE_Y_KEEPIN 0x80 55 | #define SPRITE_KEEPIN_MASK (SPRITE_X_KEEPIN | SPRITE_Y_KEEPIN) 56 | 57 | class cSprite 58 | { 59 | friend class cLEDSprites; 60 | public: 61 | cSprite(uint16_t Width = 0, uint16_t Height = 0, const uint8_t *Data = NULL, uint8_t NumFrames = 0, SpriteNumBits_t BitsPixel = _1BIT, const struct CRGB *ColTable = NULL, const uint8_t *Mask = NULL); 62 | void Setup(uint16_t Width, uint16_t Height, const uint8_t *Data, uint8_t NumFrames, SpriteNumBits_t BitsPixel, const struct CRGB *ColTable, const uint8_t *Mask = NULL); 63 | void Update(); 64 | boolean Combine(int16_t dx, int16_t dy, cSprite *Src); 65 | void Render(cLEDMatrixBase *Matrix); 66 | void EdgeDetect(cLEDMatrixBase *Matrix); 67 | void SetPosition(int16_t X, int16_t Y); 68 | void SetFrame(uint8_t Frame, uint8_t FrameRate = 0); 69 | void SetMotion(int8_t XChange, uint8_t XRate, int8_t YChange, uint8_t YRate); 70 | void SetOptions(uint8_t Options); 71 | void SetPositionFrameMotionOptions(int16_t X, int16_t Y, uint8_t Frame, uint8_t FrameRate, int8_t XChange, uint8_t XRate, int8_t YChange, uint8_t YRate, uint8_t Options = 0); 72 | uint8_t GetFlags() { return(m_Flags); }; 73 | uint8_t GetCurrentFrame() { return(m_Frame); }; 74 | int8_t GetXChange() { return(m_XChange); }; 75 | int8_t GetYChange() { return(m_YChange); }; 76 | uint8_t GetXCounter() { return(m_CounterX); }; 77 | uint8_t GetYCounter() { return(m_CounterY); }; 78 | void SetXChange(int8_t XC) { m_XChange = XC; }; 79 | void SetYChange(int8_t YC) { m_YChange = YC; }; 80 | void SetXCounter(uint8_t Xcnt) { m_CounterX = Xcnt; }; 81 | void SetYCounter(uint8_t Ycnt) { m_CounterY = Ycnt; }; 82 | void IncreaseFrame() { m_Frame++; if (m_Frame >= m_NumFrames) m_Frame = 0; }; 83 | void DecreaseFrame() { if (m_Frame == 0) m_Frame = m_NumFrames; m_Frame--; }; 84 | 85 | private: 86 | uint16_t m_MaskSize, m_FrameSize; 87 | uint8_t m_CounterFrame, m_NumCols, m_Options, m_Flags, m_CounterX, m_CounterY, m_XRate, m_YRate, m_Frame, m_FrameRate; 88 | int8_t m_XChange, m_YChange; 89 | cSprite *m_PrevSprite, *m_NextSprite; 90 | protected: 91 | uint8_t m_NumFrames, m_BitsPixel; 92 | uint16_t m_Width, m_Height; 93 | const uint8_t *m_Data, *m_Mask; 94 | const struct CRGB *m_ColTable; 95 | public: 96 | int16_t m_X, m_Y; 97 | }; 98 | 99 | class cLEDSprites 100 | { 101 | public: 102 | cLEDSprites(cLEDMatrixBase *Matrix); 103 | void AddSprite(cSprite *Spr); 104 | boolean IsSprite(cSprite *Spr); 105 | void RemoveSprite(cSprite *Spr); 106 | void RemoveAllSprites(); 107 | void ChangePriority(cSprite *Spr, SpritePriority_t Priority); 108 | void UpdateSprites(); 109 | void RenderSprites(); 110 | void DetectCollisions(cSprite *srcSpr = NULL); 111 | 112 | private: 113 | cSprite *m_SpriteHead; 114 | cLEDMatrixBase *m_Matrix; 115 | }; 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Once you have downloaded the Zip file, it should be extracted into your Arduino Libraries folder and the folder renamed to "LEDSprites". 2 | 3 | For full instructions see the Wiki icon on the right. 4 | -------------------------------------------------------------------------------- /examples/SpriteExample1/SpriteExample1.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | // Change the next 6 defines to match your matrix type and size 7 | 8 | #define LED_PIN 2 9 | #define COLOR_ORDER GRB 10 | #define CHIPSET WS2812B 11 | 12 | #define MATRIX_WIDTH 80 13 | #define MATRIX_HEIGHT 10 14 | #define MATRIX_TYPE HORIZONTAL_MATRIX 15 | 16 | cLEDMatrix leds; 17 | 18 | 19 | #define SHAPE_WIDTH 5 20 | #define SHAPE_HEIGHT 5 21 | const uint8_t ShapeData[] = 22 | { 23 | B8_1BIT(00100000), 24 | B8_1BIT(01110000), 25 | B8_1BIT(11111000), 26 | B8_1BIT(01110000), 27 | B8_1BIT(00100000), 28 | }; 29 | struct CRGB ColTable[1] = { CRGB(64, 128, 255) }; 30 | cLEDSprites Sprites(&leds); 31 | cSprite Shape(SHAPE_WIDTH, SHAPE_HEIGHT, ShapeData, 1, _1BIT, ColTable, ShapeData); 32 | 33 | 34 | void setup() 35 | { 36 | FastLED.addLeds(leds[0], leds.Size()); 37 | FastLED.setBrightness(64); 38 | FastLED.clear(true); 39 | delay(500); 40 | FastLED.showColor(CRGB::Red); 41 | delay(1000); 42 | FastLED.showColor(CRGB::Lime); 43 | delay(1000); 44 | FastLED.showColor(CRGB::Blue); 45 | delay(1000); 46 | FastLED.showColor(CRGB::White); 47 | delay(1000); 48 | FastLED.show(); 49 | 50 | Shape.SetPositionFrameMotionOptions(0/*X*/, 0/*Y*/, 0/*Frame*/, 0/*FrameRate*/, +1/*XChange*/, 1/*XRate*/, +1/*YChange*/, 1/*YRate*/, SPRITE_DETECT_EDGE | SPRITE_X_KEEPIN | SPRITE_Y_KEEPIN); 51 | Sprites.AddSprite(&Shape); 52 | } 53 | 54 | 55 | void loop() 56 | { 57 | FastLED.clear(); 58 | Sprites.UpdateSprites(); 59 | Sprites.RenderSprites(); 60 | FastLED.show(); 61 | delay(50); 62 | } 63 | -------------------------------------------------------------------------------- /examples/SpriteExample2/SpriteExample2.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | // Change the next 6 defines to match your matrix type and size 7 | 8 | #define LED_PIN 2 9 | #define COLOR_ORDER GRB 10 | #define CHIPSET WS2812B 11 | 12 | #define MATRIX_WIDTH 80 13 | #define MATRIX_HEIGHT 10 14 | #define MATRIX_TYPE HORIZONTAL_MATRIX 15 | 16 | cLEDMatrix leds; 17 | 18 | 19 | #define RAINBOW_WIDTH 16 20 | #define RAINBOW_HEIGHT 10 21 | const uint8_t RainbowData[] = 22 | { 23 | B8_3BIT(01111111),B8_3BIT(11111110), 24 | B8_3BIT(11222222),B8_3BIT(22222211), 25 | B8_3BIT(12233333),B8_3BIT(33333221), 26 | B8_3BIT(12334444),B8_3BIT(44443321), 27 | B8_3BIT(12344555),B8_3BIT(55544321), 28 | B8_3BIT(12345566),B8_3BIT(66554321), 29 | B8_3BIT(12345667),B8_3BIT(76654321), 30 | B8_3BIT(12345677),B8_3BIT(77654321), 31 | B8_3BIT(12345670),B8_3BIT(07654321), 32 | B8_3BIT(12345670),B8_3BIT(07654321), 33 | }; 34 | const uint8_t RainbowMask[] = 35 | { 36 | B8_1BIT(01111111),B8_1BIT(11111110), 37 | B8_1BIT(11111111),B8_1BIT(11111111), 38 | B8_1BIT(11111111),B8_1BIT(11111111), 39 | B8_1BIT(11111111),B8_1BIT(11111111), 40 | B8_1BIT(11111111),B8_1BIT(11111111), 41 | B8_1BIT(11111111),B8_1BIT(11111111), 42 | B8_1BIT(11111111),B8_1BIT(11111111), 43 | B8_1BIT(11111111),B8_1BIT(11111111), 44 | B8_1BIT(11111110),B8_1BIT(01111111), 45 | B8_1BIT(11111110),B8_1BIT(01111111), 46 | }; 47 | struct CRGB RainbowColTable[7] = { CHSV(216, 255, 255), CHSV(180, 255, 255), CHSV(144, 255, 255), CHSV(108, 255, 255), CHSV(72, 255, 255), CHSV(36, 255, 255), CHSV(0, 255, 255) }; 48 | 49 | #define SHAPE_WIDTH 5 50 | #define SHAPE_HEIGHT 5 51 | const uint8_t ShapeData[] = 52 | { 53 | B8_1BIT(00100000), 54 | B8_1BIT(01110000), 55 | B8_1BIT(11111000), 56 | B8_1BIT(01110000), 57 | B8_1BIT(00100000), 58 | }; 59 | struct CRGB ShapeColTable[1] = { CRGB(255, 255, 255) }; 60 | 61 | cLEDSprites Sprites(&leds); 62 | cSprite Rainbow(RAINBOW_WIDTH, RAINBOW_HEIGHT, RainbowData, 1, _3BIT, RainbowColTable, RainbowMask); 63 | cSprite Shape(SHAPE_WIDTH, SHAPE_HEIGHT, ShapeData, 1, _1BIT, ShapeColTable, ShapeData); 64 | 65 | 66 | void setup() 67 | { 68 | FastLED.addLeds(leds[0], leds.Size()); 69 | FastLED.setBrightness(64); 70 | FastLED.clear(true); 71 | delay(500); 72 | FastLED.showColor(CRGB::Red); 73 | delay(1000); 74 | FastLED.showColor(CRGB::Lime); 75 | delay(1000); 76 | FastLED.showColor(CRGB::Blue); 77 | delay(1000); 78 | FastLED.showColor(CRGB::White); 79 | delay(1000); 80 | FastLED.show(); 81 | 82 | Rainbow.SetPositionFrameMotionOptions((MATRIX_WIDTH - RAINBOW_WIDTH) / 2/*X*/, (MATRIX_HEIGHT - RAINBOW_HEIGHT) / 2/*Y*/, 0/*Frame*/, 0/*FrameRate*/, 0/*XChange*/, 0/*XRate*/, 0/*YChange*/, 0/*YRate*/); 83 | Sprites.AddSprite(&Rainbow); 84 | 85 | Shape.SetPositionFrameMotionOptions(0/*X*/, 0/*Y*/, 0/*Frame*/, 0/*FrameRate*/, +1/*XChange*/, 1/*XRate*/, +1/*YChange*/, 2/*YRate*/, SPRITE_DETECT_EDGE | SPRITE_X_KEEPIN | SPRITE_Y_KEEPIN); 86 | Sprites.AddSprite(&Shape); 87 | } 88 | 89 | 90 | void loop() 91 | { 92 | FastLED.clear(); 93 | Sprites.UpdateSprites(); 94 | Sprites.RenderSprites(); 95 | FastLED.show(); 96 | delay(50); 97 | } 98 | -------------------------------------------------------------------------------- /examples/SpriteExample3/SpriteExample3.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | // Change the next 6 defines to match your matrix type and size 7 | 8 | #define LED_PIN 2 9 | #define COLOR_ORDER GRB 10 | #define CHIPSET WS2812B 11 | 12 | #define MATRIX_WIDTH 80 13 | #define MATRIX_HEIGHT 10 14 | #define MATRIX_TYPE HORIZONTAL_MATRIX 15 | 16 | cLEDMatrix leds; 17 | 18 | 19 | #define SQUARE_WIDTH 7 20 | #define SQUARE_HEIGHT 7 21 | const uint8_t SquareData[] = 22 | { 23 | B8_2BIT(11111110), 24 | B8_2BIT(12222210), 25 | B8_2BIT(12333210), 26 | B8_2BIT(12333210), 27 | B8_2BIT(12333210), 28 | B8_2BIT(12222210), 29 | B8_2BIT(11111110) 30 | }; 31 | const uint8_t SquareMask[] = 32 | { 33 | B8_2BIT(11111110), 34 | B8_2BIT(11111110), 35 | B8_2BIT(11111110), 36 | B8_2BIT(11111110), 37 | B8_2BIT(11111110), 38 | B8_2BIT(11111110), 39 | B8_2BIT(11111110) 40 | }; 41 | cLEDSprites Sprites(&leds); 42 | 43 | #define MAX_SQUARES 8 44 | #define NUM_COLS 3 45 | cSprite Shape[MAX_SQUARES]; 46 | struct CRGB ColTabs[MAX_SQUARES][NUM_COLS]; 47 | int NumSquares; 48 | 49 | 50 | void setup() 51 | { 52 | FastLED.addLeds(leds[0], leds.Size()); 53 | FastLED.setBrightness(64); 54 | FastLED.clear(true); 55 | delay(500); 56 | FastLED.showColor(CRGB::Red); 57 | delay(1000); 58 | FastLED.showColor(CRGB::Lime); 59 | delay(1000); 60 | FastLED.showColor(CRGB::Blue); 61 | delay(1000); 62 | FastLED.showColor(CRGB::White); 63 | delay(1000); 64 | FastLED.show(); 65 | 66 | int16_t x = MATRIX_WIDTH - SQUARE_WIDTH - 1, y = 0; 67 | int8_t xc = -1, yc = 1; 68 | NumSquares = 0; 69 | while ((NumSquares < MAX_SQUARES) && (x < (MATRIX_WIDTH - SQUARE_WIDTH))) 70 | { 71 | for (int i=0; i 2 | 3 | #include 4 | #include 5 | 6 | // Change the next 6 defines to match your matrix type and size 7 | 8 | #define LED_PIN 2 9 | #define COLOR_ORDER GRB 10 | #define CHIPSET WS2812B 11 | 12 | #define MATRIX_WIDTH 80 13 | #define MATRIX_HEIGHT 10 14 | #define MATRIX_TYPE HORIZONTAL_MATRIX 15 | 16 | cLEDMatrix leds; 17 | 18 | 19 | cLEDSprites Sprites(&leds); 20 | 21 | #define MY_SPRITE_WIDTH 11 22 | #define MY_SPRITE_HEIGHT 10 23 | #define PACMAN_FRAMES 3 24 | #define PINKY_FRAMES 2 25 | 26 | #define POWER_PILL_SIZE 4 27 | const uint8_t PowerPillData[] = 28 | { 29 | B8_1BIT(01100000), 30 | B8_1BIT(11110000), 31 | B8_1BIT(11110000), 32 | B8_1BIT(01100000) 33 | }; 34 | const struct CRGB PowerPillColTab[] = { CRGB(48, 48, 255) }; 35 | 36 | const uint8_t PacmanRightData[] = 37 | { 38 | // Pacman Open 39 | B8_2BIT(00021112),B8_2BIT(00000000), 40 | B8_2BIT(00111111),B8_2BIT(10000000), 41 | B8_2BIT(01111111),B8_2BIT(00000000), 42 | B8_2BIT(21111110),B8_2BIT(00000000), 43 | B8_2BIT(11111000),B8_2BIT(00000000), 44 | B8_2BIT(11111000),B8_2BIT(00000000), 45 | B8_2BIT(21111110),B8_2BIT(00000000), 46 | B8_2BIT(01111111),B8_2BIT(00000000), 47 | B8_2BIT(00111111),B8_2BIT(10000000), 48 | B8_2BIT(00021112),B8_2BIT(00000000), 49 | // Pacman Half 50 | B8_2BIT(00021112),B8_2BIT(00000000), 51 | B8_2BIT(00111111),B8_2BIT(10000000), 52 | B8_2BIT(01111111),B8_2BIT(11000000), 53 | B8_2BIT(21111111),B8_2BIT(11200000), 54 | B8_2BIT(11111200),B8_2BIT(00000000), 55 | B8_2BIT(11111200),B8_2BIT(00000000), 56 | B8_2BIT(21111111),B8_2BIT(11200000), 57 | B8_2BIT(01111111),B8_2BIT(11000000), 58 | B8_2BIT(00111111),B8_2BIT(10000000), 59 | B8_2BIT(00021112),B8_2BIT(00000000), 60 | // Pacman Closed 61 | B8_2BIT(00021112),B8_2BIT(00000000), 62 | B8_2BIT(00111111),B8_2BIT(10000000), 63 | B8_2BIT(01111111),B8_2BIT(11000000), 64 | B8_2BIT(21111111),B8_2BIT(11200000), 65 | B8_2BIT(11111111),B8_2BIT(11100000), 66 | B8_2BIT(11111111),B8_2BIT(11100000), 67 | B8_2BIT(21111111),B8_2BIT(11200000), 68 | B8_2BIT(01111111),B8_2BIT(11000000), 69 | B8_2BIT(00111111),B8_2BIT(10000000), 70 | B8_2BIT(00021112),B8_2BIT(00000000) 71 | }; 72 | const uint8_t PacmanRightMask[] = 73 | { 74 | // Pacman Open 75 | B8_1BIT(00011111),B8_1BIT(00000000), 76 | B8_1BIT(00111111),B8_1BIT(10000000), 77 | B8_1BIT(01111111),B8_1BIT(00000000), 78 | B8_1BIT(11111110),B8_1BIT(00000000), 79 | B8_1BIT(11111000),B8_1BIT(00000000), 80 | B8_1BIT(11111000),B8_1BIT(00000000), 81 | B8_1BIT(11111110),B8_1BIT(00000000), 82 | B8_1BIT(01111111),B8_1BIT(00000000), 83 | B8_1BIT(00111111),B8_1BIT(10000000), 84 | B8_1BIT(00011111),B8_1BIT(00000000), 85 | // Pacman Half 86 | B8_1BIT(00011111),B8_1BIT(00000000), 87 | B8_1BIT(00111111),B8_1BIT(10000000), 88 | B8_1BIT(01111111),B8_1BIT(11000000), 89 | B8_1BIT(11111111),B8_1BIT(11100000), 90 | B8_1BIT(11111100),B8_1BIT(00000000), 91 | B8_1BIT(11111100),B8_1BIT(00000000), 92 | B8_1BIT(11111111),B8_1BIT(11100000), 93 | B8_1BIT(01111111),B8_1BIT(11000000), 94 | B8_1BIT(00111111),B8_1BIT(10000000), 95 | B8_1BIT(00011111),B8_1BIT(00000000), 96 | // Pacman Closed 97 | B8_1BIT(00011111),B8_1BIT(00000000), 98 | B8_1BIT(00111111),B8_1BIT(10000000), 99 | B8_1BIT(01111111),B8_1BIT(11000000), 100 | B8_1BIT(11111111),B8_1BIT(11100000), 101 | B8_1BIT(11111111),B8_1BIT(11100000), 102 | B8_1BIT(11111111),B8_1BIT(11100000), 103 | B8_1BIT(11111111),B8_1BIT(11100000), 104 | B8_1BIT(01111111),B8_1BIT(11000000), 105 | B8_1BIT(00111111),B8_1BIT(10000000), 106 | B8_1BIT(00011111),B8_1BIT(00000000) 107 | }; 108 | const struct CRGB PacmanRightColTab[] = { CRGB(255, 255, 0), CRGB(100, 100, 0), CRGB(255, 255, 255) }; 109 | 110 | const uint8_t PinkyData[] = 111 | { 112 | // Pinky Frame 1 113 | B8_2BIT(00001110),B8_2BIT(00000000), 114 | B8_2BIT(00111111),B8_2BIT(10000000), 115 | B8_2BIT(01111111),B8_2BIT(11000000), 116 | B8_2BIT(01122212),B8_2BIT(22000000), 117 | B8_2BIT(11122312),B8_2BIT(23100000), 118 | B8_2BIT(11122212),B8_2BIT(22100000), 119 | B8_2BIT(11111111),B8_2BIT(11100000), 120 | B8_2BIT(11111111),B8_2BIT(11100000), 121 | B8_2BIT(11111111),B8_2BIT(11100000), 122 | B8_2BIT(11001110),B8_2BIT(01100000), 123 | // Pinky Frame 2 124 | B8_2BIT(00001110),B8_2BIT(00000000), 125 | B8_2BIT(00111111),B8_2BIT(10000000), 126 | B8_2BIT(01111111),B8_2BIT(11000000), 127 | B8_2BIT(01122212),B8_2BIT(22000000), 128 | B8_2BIT(11122312),B8_2BIT(23100000), 129 | B8_2BIT(11122212),B8_2BIT(22100000), 130 | B8_2BIT(11111111),B8_2BIT(11100000), 131 | B8_2BIT(11111111),B8_2BIT(11100000), 132 | B8_2BIT(11111111),B8_2BIT(11100000), 133 | B8_2BIT(10011011),B8_2BIT(00100000), 134 | }; 135 | const uint8_t PinkyMask[] = 136 | { 137 | // Pinky Frame 1 138 | B8_1BIT(00001110),B8_1BIT(00000000), 139 | B8_1BIT(00111111),B8_1BIT(10000000), 140 | B8_1BIT(01111111),B8_1BIT(11000000), 141 | B8_1BIT(01111111),B8_1BIT(11000000), 142 | B8_1BIT(11111111),B8_1BIT(11100000), 143 | B8_1BIT(11111111),B8_1BIT(11100000), 144 | B8_1BIT(11111111),B8_1BIT(11100000), 145 | B8_1BIT(11111111),B8_1BIT(11100000), 146 | B8_1BIT(11111111),B8_1BIT(11100000), 147 | B8_1BIT(11001110),B8_1BIT(01100000), 148 | // Pinky Frame 2 149 | B8_1BIT(00001110),B8_1BIT(00000000), 150 | B8_1BIT(00111111),B8_1BIT(10000000), 151 | B8_1BIT(01111111),B8_1BIT(11000000), 152 | B8_1BIT(01111111),B8_1BIT(11000000), 153 | B8_1BIT(11111111),B8_1BIT(11100000), 154 | B8_1BIT(11111111),B8_1BIT(11100000), 155 | B8_1BIT(11111111),B8_1BIT(11100000), 156 | B8_1BIT(11111111),B8_1BIT(11100000), 157 | B8_1BIT(11111111),B8_1BIT(11100000), 158 | B8_1BIT(10011011),B8_1BIT(00100000), 159 | }; 160 | const struct CRGB PinkyColTab[] = { CRGB(255, 0, 0), CRGB(255, 255, 255), CRGB(0, 0, 255) }; 161 | 162 | const uint8_t PacmanLeftData[] = 163 | { 164 | // Pacman Open 165 | B8_2BIT(00021112),B8_2BIT(00000000), 166 | B8_2BIT(00111111),B8_2BIT(10000000), 167 | B8_2BIT(00011111),B8_2BIT(11000000), 168 | B8_2BIT(00001111),B8_2BIT(11200000), 169 | B8_2BIT(00000011),B8_2BIT(11100000), 170 | B8_2BIT(00000011),B8_2BIT(11100000), 171 | B8_2BIT(00001111),B8_2BIT(11200000), 172 | B8_2BIT(00011111),B8_2BIT(11000000), 173 | B8_2BIT(00111111),B8_2BIT(10000000), 174 | B8_2BIT(00021112),B8_2BIT(00000000), 175 | // Pacman Half 176 | B8_2BIT(00021112),B8_2BIT(00000000), 177 | B8_2BIT(00111111),B8_2BIT(10000000), 178 | B8_2BIT(01111111),B8_2BIT(11000000), 179 | B8_2BIT(21111111),B8_2BIT(11200000), 180 | B8_2BIT(00000211),B8_2BIT(11100000), 181 | B8_2BIT(00000211),B8_2BIT(11100000), 182 | B8_2BIT(21111111),B8_2BIT(11200000), 183 | B8_2BIT(01111111),B8_2BIT(11000000), 184 | B8_2BIT(00111111),B8_2BIT(10000000), 185 | B8_2BIT(00021112),B8_2BIT(00000000), 186 | // Pacman Closed 187 | B8_2BIT(00021112),B8_2BIT(00000000), 188 | B8_2BIT(00111111),B8_2BIT(10000000), 189 | B8_2BIT(01111111),B8_2BIT(11000000), 190 | B8_2BIT(21111111),B8_2BIT(11200000), 191 | B8_2BIT(11111111),B8_2BIT(11100000), 192 | B8_2BIT(11111111),B8_2BIT(11100000), 193 | B8_2BIT(21111111),B8_2BIT(11200000), 194 | B8_2BIT(01111111),B8_2BIT(11000000), 195 | B8_2BIT(00111111),B8_2BIT(10000000), 196 | B8_2BIT(00021112),B8_2BIT(00000000) 197 | }; 198 | const uint8_t PacmanLeftMask[] = 199 | { 200 | // Pacman Open 201 | B8_1BIT(00011111),B8_1BIT(00000000), 202 | B8_1BIT(00111111),B8_1BIT(10000000), 203 | B8_1BIT(00011111),B8_1BIT(11000000), 204 | B8_1BIT(00001111),B8_1BIT(11100000), 205 | B8_1BIT(00000011),B8_1BIT(11100000), 206 | B8_1BIT(00000011),B8_1BIT(11100000), 207 | B8_1BIT(00001111),B8_1BIT(11100000), 208 | B8_1BIT(00011111),B8_1BIT(11000000), 209 | B8_1BIT(00111111),B8_1BIT(10000000), 210 | B8_1BIT(00011111),B8_1BIT(00000000), 211 | // Pacman Half 212 | B8_1BIT(00011111),B8_1BIT(00000000), 213 | B8_1BIT(00111111),B8_1BIT(10000000), 214 | B8_1BIT(01111111),B8_1BIT(11000000), 215 | B8_1BIT(11111111),B8_1BIT(11100000), 216 | B8_1BIT(00000111),B8_1BIT(11100000), 217 | B8_1BIT(00000111),B8_1BIT(11100000), 218 | B8_1BIT(11111111),B8_1BIT(11100000), 219 | B8_1BIT(01111111),B8_1BIT(11000000), 220 | B8_1BIT(00111111),B8_1BIT(10000000), 221 | B8_1BIT(00011111),B8_1BIT(00000000), 222 | // Pacman Closed 223 | B8_1BIT(00011111),B8_1BIT(00000000), 224 | B8_1BIT(00111111),B8_1BIT(10000000), 225 | B8_1BIT(01111111),B8_1BIT(11000000), 226 | B8_1BIT(11111111),B8_1BIT(11100000), 227 | B8_1BIT(11111111),B8_1BIT(11100000), 228 | B8_1BIT(11111111),B8_1BIT(11100000), 229 | B8_1BIT(11111111),B8_1BIT(11100000), 230 | B8_1BIT(01111111),B8_1BIT(11000000), 231 | B8_1BIT(00111111),B8_1BIT(10000000), 232 | B8_1BIT(00011111),B8_1BIT(00000000) 233 | }; 234 | const struct CRGB PacmanLeftColTab[] = { CRGB(255, 255, 0), CRGB(100, 100, 0), CRGB(255, 255, 255) }; 235 | 236 | const uint8_t GhostData[] = 237 | { 238 | // Ghost Frame 1 239 | B8_2BIT(00001110),B8_2BIT(00000000), 240 | B8_2BIT(00111111),B8_2BIT(10000000), 241 | B8_2BIT(01111111),B8_2BIT(11000000), 242 | B8_2BIT(01122122),B8_2BIT(11000000), 243 | B8_2BIT(11122122),B8_2BIT(11100000), 244 | B8_2BIT(11111111),B8_2BIT(11100000), 245 | B8_2BIT(11131313),B8_2BIT(11100000), 246 | B8_2BIT(11313131),B8_2BIT(31100000), 247 | B8_2BIT(11111111),B8_2BIT(11100000), 248 | B8_2BIT(11001110),B8_2BIT(01100000), 249 | // Ghost Frame 2 250 | B8_2BIT(00001110),B8_2BIT(00000000), 251 | B8_2BIT(00111111),B8_2BIT(10000000), 252 | B8_2BIT(01111111),B8_2BIT(11000000), 253 | B8_2BIT(01122122),B8_2BIT(11000000), 254 | B8_2BIT(11122122),B8_2BIT(11100000), 255 | B8_2BIT(11111111),B8_2BIT(11100000), 256 | B8_2BIT(11131313),B8_2BIT(11100000), 257 | B8_2BIT(11313131),B8_2BIT(31100000), 258 | B8_2BIT(11111111),B8_2BIT(11100000), 259 | B8_2BIT(10011011),B8_2BIT(00100000) 260 | }; 261 | const uint8_t GhostMask[] = 262 | { 263 | // Ghost Frame 1 264 | B8_1BIT(00001110),B8_1BIT(00000000), 265 | B8_1BIT(00111111),B8_1BIT(10000000), 266 | B8_1BIT(01111111),B8_1BIT(11000000), 267 | B8_1BIT(01111111),B8_1BIT(11000000), 268 | B8_1BIT(11111111),B8_1BIT(11100000), 269 | B8_1BIT(11111111),B8_1BIT(11100000), 270 | B8_1BIT(11111111),B8_1BIT(11100000), 271 | B8_1BIT(11111111),B8_1BIT(11100000), 272 | B8_1BIT(11111111),B8_1BIT(11100000), 273 | B8_1BIT(11001110),B8_1BIT(01100000), 274 | // Ghost Frame 2 275 | B8_1BIT(00001110),B8_1BIT(00000000), 276 | B8_1BIT(00111111),B8_1BIT(10000000), 277 | B8_1BIT(01111111),B8_1BIT(11000000), 278 | B8_1BIT(01111111),B8_1BIT(11000000), 279 | B8_1BIT(11111111),B8_1BIT(11100000), 280 | B8_1BIT(11111111),B8_1BIT(11100000), 281 | B8_1BIT(11111111),B8_1BIT(11100000), 282 | B8_1BIT(11111111),B8_1BIT(11100000), 283 | B8_1BIT(11111111),B8_1BIT(11100000), 284 | B8_1BIT(10011011),B8_1BIT(00100000) 285 | }; 286 | const struct CRGB GhostColTab[] = { CRGB(0, 0, 255), CRGB(255, 255, 255), CRGB(255, 90, 120) }; 287 | 288 | const uint8_t S200Data[] = 289 | { 290 | B8_1BIT(00000000),B8_1BIT(00000000), 291 | B8_1BIT(01000100),B8_1BIT(01000000), 292 | B8_1BIT(10101010),B8_1BIT(10100000), 293 | B8_1BIT(10101010),B8_1BIT(10100000), 294 | B8_1BIT(00101010),B8_1BIT(10100000), 295 | B8_1BIT(01001010),B8_1BIT(10100000), 296 | B8_1BIT(11100100),B8_1BIT(01000000), 297 | B8_1BIT(00000000),B8_1BIT(00000000), 298 | B8_1BIT(00000000),B8_1BIT(00000000), 299 | B8_1BIT(00000000),B8_1BIT(00000000) 300 | }; 301 | const struct CRGB S200ColTab[] = { CRGB(0, 192, 255) }; 302 | 303 | const uint8_t EyesData[] = 304 | { 305 | B8_2BIT(00000000),B8_2BIT(00000000), 306 | B8_2BIT(00000000),B8_2BIT(00000000), 307 | B8_2BIT(01110001),B8_2BIT(11000000), 308 | B8_2BIT(11111011),B8_2BIT(11100000), 309 | B8_2BIT(11122011),B8_2BIT(12200000), 310 | B8_2BIT(11122011),B8_2BIT(12200000), 311 | B8_2BIT(01110001),B8_2BIT(11000000), 312 | B8_2BIT(00000000),B8_2BIT(00000000), 313 | B8_2BIT(00000000),B8_2BIT(00000000), 314 | B8_2BIT(00000000),B8_2BIT(00000000) 315 | }; 316 | const uint8_t EyesMask[] = 317 | { 318 | B8_1BIT(00000000),B8_1BIT(00000000), 319 | B8_1BIT(00000000),B8_1BIT(00000000), 320 | B8_1BIT(01110001),B8_1BIT(11000000), 321 | B8_1BIT(11111011),B8_1BIT(11100000), 322 | B8_1BIT(11111011),B8_1BIT(11100000), 323 | B8_1BIT(11111011),B8_1BIT(11100000), 324 | B8_1BIT(01110001),B8_1BIT(11000000), 325 | B8_1BIT(00000000),B8_1BIT(00000000), 326 | B8_1BIT(00000000),B8_1BIT(00000000), 327 | B8_1BIT(00000000),B8_1BIT(00000000) 328 | }; 329 | const struct CRGB EyesColTab[] = { CRGB(255, 255, 255), CRGB(0, 0, 255), CRGB(0, 0, 0) }; 330 | 331 | cSprite SprPacmanRight(MY_SPRITE_WIDTH, MY_SPRITE_HEIGHT, PacmanRightData, PACMAN_FRAMES, _2BIT, PacmanRightColTab, PacmanRightMask); 332 | cSprite SprPinky(MY_SPRITE_WIDTH, MY_SPRITE_HEIGHT, PinkyData, PINKY_FRAMES, _2BIT, PinkyColTab, PinkyMask); 333 | cSprite SprPacmanLeft(MY_SPRITE_WIDTH, MY_SPRITE_HEIGHT, PacmanLeftData, PACMAN_FRAMES, _2BIT, PacmanLeftColTab, PacmanLeftMask); 334 | cSprite SprGhost(MY_SPRITE_WIDTH, MY_SPRITE_HEIGHT, GhostData, PINKY_FRAMES, _2BIT, GhostColTab, GhostMask); 335 | cSprite SprPill(POWER_PILL_SIZE, POWER_PILL_SIZE, PowerPillData, 1, _1BIT, PowerPillColTab, PowerPillData); 336 | cSprite Spr200(MY_SPRITE_WIDTH, MY_SPRITE_HEIGHT, S200Data, 1, _1BIT, S200ColTab, S200Data); 337 | cSprite SprEyes(MY_SPRITE_WIDTH, MY_SPRITE_HEIGHT, EyesData, 1, _2BIT, EyesColTab, EyesMask); 338 | 339 | 340 | void setup() 341 | { 342 | FastLED.addLeds(leds[0], leds.Size()); 343 | FastLED.setBrightness(64); 344 | FastLED.clear(true); 345 | delay(500); 346 | FastLED.showColor(CRGB::Red); 347 | delay(1000); 348 | FastLED.showColor(CRGB::Lime); 349 | delay(1000); 350 | FastLED.showColor(CRGB::Blue); 351 | delay(1000); 352 | FastLED.showColor(CRGB::White); 353 | delay(1000); 354 | FastLED.show(); 355 | 356 | SprPill.SetPositionFrameMotionOptions(MATRIX_WIDTH - POWER_PILL_SIZE - 1/*X*/, (MY_SPRITE_HEIGHT - POWER_PILL_SIZE) / 2/*Y*/, 0/*Frame*/, 0/*FrameRate*/, 0/*XChange*/, 0/*XRate*/, 0/*YChange*/, 0/*YRate*/); 357 | Sprites.AddSprite(&SprPill); 358 | SprPacmanRight.SetPositionFrameMotionOptions(-12/*X*/, 0/*Y*/, 0/*Frame*/, 4/*FrameRate*/, +1/*XChange*/, 2/*XRate*/, 0/*YChange*/, 0/*YRate*/, SPRITE_DETECT_EDGE | SPRITE_DETECT_COLLISION); 359 | Sprites.AddSprite(&SprPacmanRight); 360 | SprPinky.SetPositionFrameMotionOptions(-26/*X*/, 0/*Y*/, 0/*Frame*/, 2/*FrameRate*/, +1/*XChange*/, 2/*XRate*/, 0/*YChange*/, 0/*YRate*/, SPRITE_DETECT_EDGE); 361 | Sprites.AddSprite(&SprPinky); 362 | } 363 | 364 | 365 | void loop() 366 | { 367 | FastLED.clear(); 368 | Sprites.UpdateSprites(); 369 | Sprites.DetectCollisions(); 370 | if (Sprites.IsSprite(&SprPinky)) 371 | { 372 | if (SprPill.GetFlags() & SPRITE_COLLISION) 373 | { 374 | Sprites.RemoveSprite(&SprPinky); 375 | Sprites.RemoveSprite(&SprPacmanRight); 376 | Sprites.RemoveSprite(&SprPill); 377 | SprGhost.SetPositionFrameMotionOptions(SprPinky.m_X, SprPinky.m_Y, 0, 3, -1, 3, 0, 0, SPRITE_DETECT_EDGE | SPRITE_DETECT_COLLISION); 378 | Sprites.AddSprite(&SprGhost); 379 | SprPacmanLeft.SetPositionFrameMotionOptions(SprPacmanRight.m_X, SprPacmanRight.m_Y, 0, 4, -1, 2, 0, 0, SPRITE_DETECT_EDGE); 380 | Sprites.AddSprite(&SprPacmanLeft); 381 | } 382 | } 383 | else if (Sprites.IsSprite(&SprGhost)) 384 | { 385 | if (SprGhost.GetFlags() & SPRITE_COLLISION) 386 | { 387 | Sprites.RemoveSprite(&SprGhost); 388 | Spr200.SetPositionFrameMotionOptions(SprGhost.m_X, SprGhost.m_Y, 0, 0, 0, 0, 0, 0); 389 | Sprites.AddSprite(&Spr200); 390 | Sprites.ChangePriority(&Spr200, SPR_BACK); 391 | } 392 | } 393 | else if (Sprites.IsSprite(&Spr200)) 394 | { 395 | if (SprPacmanLeft.GetFlags() & SPRITE_EDGE_X_MIN) 396 | { 397 | Sprites.RemoveSprite(&Spr200); 398 | SprEyes.SetPositionFrameMotionOptions(Spr200.m_X, Spr200.m_Y, 0, 0, +1, 2, 0, 0, SPRITE_DETECT_EDGE); 399 | Sprites.AddSprite(&SprEyes); 400 | Sprites.ChangePriority(&SprEyes, SPR_BACK); 401 | } 402 | } 403 | else 404 | { 405 | if (SprEyes.GetFlags() & SPRITE_MATRIX_X_OFF) 406 | { 407 | Sprites.RemoveSprite(&SprEyes); 408 | Sprites.RemoveSprite(&SprPacmanLeft); 409 | SprPill.SetPositionFrameMotionOptions(MATRIX_WIDTH - POWER_PILL_SIZE - 1/*X*/, (MY_SPRITE_HEIGHT - POWER_PILL_SIZE) / 2/*Y*/, 0/*Frame*/, 0/*FrameRate*/, 0/*XChange*/, 0/*XRate*/, 0/*YChange*/, 0/*YRate*/); 410 | Sprites.AddSprite(&SprPill); 411 | SprPacmanRight.SetPositionFrameMotionOptions(-12/*X*/, 0/*Y*/, 0/*Frame*/, 4/*FrameRate*/, +1/*XChange*/, 2/*XRate*/, 0/*YChange*/, 0/*YRate*/, SPRITE_DETECT_EDGE | SPRITE_DETECT_COLLISION); 412 | Sprites.AddSprite(&SprPacmanRight); 413 | SprPinky.SetPositionFrameMotionOptions(-26/*X*/, 0/*Y*/, 0/*Frame*/, 2/*FrameRate*/, +1/*XChange*/, 2/*XRate*/, 0/*YChange*/, 0/*YRate*/, SPRITE_DETECT_EDGE); 414 | Sprites.AddSprite(&SprPinky); 415 | } 416 | } 417 | Sprites.RenderSprites(); 418 | FastLED.show(); 419 | delay(10); 420 | } 421 | -------------------------------------------------------------------------------- /examples/Tetris/Tetris.ino: -------------------------------------------------------------------------------- 1 | // TETRIS 2 | // A simple Tetris game to show the use of my cLEDMatrix, cLEDText & cLEDSprite classes using the FastlED library. 3 | // It uses 47.5k rom and 6k ram. 4 | // The joystick/buttons should just ground out the appropriate pins to control the game 5 | // I have my Teensy 3.1 hooked up to a standard 9 way D socket so that I can use a standard Atari style joytick 6 | // Teensy pin 6 to DType pin 2 7 | // Teensy pin 4 to DType pin 3 8 | // Teensy pin 5 to DType pin 4 9 | // Teensy pin 3 to DType pin 6 10 | // Teensy Gnd to DType pin 8 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | // Change the next 6 defines to match your matrix type and size 20 | 21 | #define LED_PIN 2 22 | #define COLOR_ORDER GRB 23 | #define CHIPSET WS2812B 24 | 25 | #define MATRIX_WIDTH 16 26 | #define MATRIX_HEIGHT 32 27 | #define MATRIX_TYPE VERTICAL_MATRIX 28 | 29 | // NOTE the '-' sign before the width, this is due to my leds matrix origin being on the right hand side 30 | cLEDMatrix<-MATRIX_WIDTH, MATRIX_HEIGHT, MATRIX_TYPE> leds; 31 | 32 | 33 | #define TARGET_FRAME_TIME 25 // Desired update rate, though if too many leds it will just run as fast as it can! 34 | #define INITIAL_DROP_FRAMES 20 // Start of game block drop delay in frames 35 | 36 | // Joystick pins used with pullup so active when grounded 37 | #define ROTATE_PIN 3 38 | #define LEFT_PIN 4 39 | #define RIGHT_PIN 5 40 | #define DOWN_PIN 6 41 | 42 | 43 | const uint8_t TetrisIData[] = 44 | { 45 | // Frame 1 46 | B8_3BIT(00000000), 47 | B8_3BIT(00000000), 48 | B8_3BIT(00000000), 49 | B8_3BIT(11110000), 50 | // Frame 2 51 | B8_3BIT(10000000), 52 | B8_3BIT(10000000), 53 | B8_3BIT(10000000), 54 | B8_3BIT(10000000), 55 | // Frame 3 56 | B8_3BIT(00000000), 57 | B8_3BIT(00000000), 58 | B8_3BIT(00000000), 59 | B8_3BIT(11110000), 60 | // Frame 4 61 | B8_3BIT(10000000), 62 | B8_3BIT(10000000), 63 | B8_3BIT(10000000), 64 | B8_3BIT(10000000) 65 | }; 66 | const uint8_t TetrisIMask[] = 67 | { 68 | // Frame 1 69 | B8_1BIT(00000000), 70 | B8_1BIT(00000000), 71 | B8_1BIT(00000000), 72 | B8_1BIT(11110000), 73 | // Frame 2 74 | B8_1BIT(10000000), 75 | B8_1BIT(10000000), 76 | B8_1BIT(10000000), 77 | B8_1BIT(10000000), 78 | // Frame 3 79 | B8_1BIT(00000000), 80 | B8_1BIT(00000000), 81 | B8_1BIT(00000000), 82 | B8_1BIT(11110000), 83 | // Frame 4 84 | B8_1BIT(10000000), 85 | B8_1BIT(10000000), 86 | B8_1BIT(10000000), 87 | B8_1BIT(10000000) 88 | }; 89 | const uint8_t TetrisJData[] = 90 | { 91 | // Frame 1 92 | B8_3BIT(00000000), 93 | B8_3BIT(00000000), 94 | B8_3BIT(22200000), 95 | B8_3BIT(00200000), 96 | // Frame 2 97 | B8_3BIT(00000000), 98 | B8_3BIT(02000000), 99 | B8_3BIT(02000000), 100 | B8_3BIT(22000000), 101 | // Frame 3 102 | B8_3BIT(00000000), 103 | B8_3BIT(00000000), 104 | B8_3BIT(20000000), 105 | B8_3BIT(22200000), 106 | // Frame 4 107 | B8_3BIT(00000000), 108 | B8_3BIT(22000000), 109 | B8_3BIT(20000000), 110 | B8_3BIT(20000000) 111 | }; 112 | const uint8_t TetrisJMask[] = 113 | { 114 | // Frame 1 115 | B8_1BIT(00000000), 116 | B8_1BIT(00000000), 117 | B8_1BIT(11100000), 118 | B8_1BIT(00100000), 119 | // Frame 2 120 | B8_1BIT(00000000), 121 | B8_1BIT(01000000), 122 | B8_1BIT(01000000), 123 | B8_1BIT(11000000), 124 | // Frame 3 125 | B8_1BIT(00000000), 126 | B8_1BIT(00000000), 127 | B8_1BIT(10000000), 128 | B8_1BIT(11100000), 129 | // Frame 4 130 | B8_1BIT(00000000), 131 | B8_1BIT(11000000), 132 | B8_1BIT(10000000), 133 | B8_1BIT(10000000) 134 | }; 135 | const uint8_t TetrisLData[] = 136 | { 137 | // Frame 1 138 | B8_3BIT(00000000), 139 | B8_3BIT(00000000), 140 | B8_3BIT(33300000), 141 | B8_3BIT(30000000), 142 | // Frame 2 143 | B8_3BIT(00000000), 144 | B8_3BIT(33000000), 145 | B8_3BIT(03000000), 146 | B8_3BIT(03000000), 147 | // Frame 3 148 | B8_3BIT(00000000), 149 | B8_3BIT(00000000), 150 | B8_3BIT(00300000), 151 | B8_3BIT(33300000), 152 | // Frame 4 153 | B8_3BIT(00000000), 154 | B8_3BIT(30000000), 155 | B8_3BIT(30000000), 156 | B8_3BIT(33000000) 157 | }; 158 | const uint8_t TetrisLMask[] = 159 | { 160 | // Frame 1 161 | B8_1BIT(00000000), 162 | B8_1BIT(00000000), 163 | B8_1BIT(11100000), 164 | B8_1BIT(10000000), 165 | // Frame 2 166 | B8_1BIT(00000000), 167 | B8_1BIT(11000000), 168 | B8_1BIT(01000000), 169 | B8_1BIT(01000000), 170 | // Frame 3 171 | B8_1BIT(00000000), 172 | B8_1BIT(00000000), 173 | B8_1BIT(00100000), 174 | B8_1BIT(11100000), 175 | // Frame 4 176 | B8_1BIT(00000000), 177 | B8_1BIT(10000000), 178 | B8_1BIT(10000000), 179 | B8_1BIT(11000000) 180 | }; 181 | const uint8_t TetrisOData[] = 182 | { 183 | // Frame 1 184 | B8_3BIT(00000000), 185 | B8_3BIT(00000000), 186 | B8_3BIT(44000000), 187 | B8_3BIT(44000000), 188 | // Frame 2 189 | B8_3BIT(00000000), 190 | B8_3BIT(00000000), 191 | B8_3BIT(44000000), 192 | B8_3BIT(44000000), 193 | // Frame 3 194 | B8_3BIT(00000000), 195 | B8_3BIT(00000000), 196 | B8_3BIT(44000000), 197 | B8_3BIT(44000000), 198 | // Frame 4 199 | B8_3BIT(00000000), 200 | B8_3BIT(00000000), 201 | B8_3BIT(44000000), 202 | B8_3BIT(44000000) 203 | }; 204 | const uint8_t TetrisOMask[] = 205 | { 206 | // Frame 1 207 | B8_1BIT(00000000), 208 | B8_1BIT(00000000), 209 | B8_1BIT(11000000), 210 | B8_1BIT(11000000), 211 | // Frame 2 212 | B8_1BIT(00000000), 213 | B8_1BIT(00000000), 214 | B8_1BIT(11000000), 215 | B8_1BIT(11000000), 216 | // Frame 3 217 | B8_1BIT(00000000), 218 | B8_1BIT(00000000), 219 | B8_1BIT(11000000), 220 | B8_1BIT(11000000), 221 | // Frame 4 222 | B8_1BIT(00000000), 223 | B8_1BIT(00000000), 224 | B8_1BIT(11000000), 225 | B8_1BIT(11000000) 226 | }; 227 | const uint8_t TetrisSData[] = 228 | { 229 | // Frame 1 230 | B8_3BIT(00000000), 231 | B8_3BIT(00000000), 232 | B8_3BIT(05500000), 233 | B8_3BIT(55000000), 234 | // Frame 2 235 | B8_3BIT(00000000), 236 | B8_3BIT(50000000), 237 | B8_3BIT(55000000), 238 | B8_3BIT(05000000), 239 | // Frame 3 240 | B8_3BIT(00000000), 241 | B8_3BIT(00000000), 242 | B8_3BIT(05500000), 243 | B8_3BIT(55000000), 244 | // Frame 4 245 | B8_3BIT(00000000), 246 | B8_3BIT(50000000), 247 | B8_3BIT(55000000), 248 | B8_3BIT(05000000) 249 | }; 250 | const uint8_t TetrisSMask[] = 251 | { 252 | // Frame 1 253 | B8_1BIT(00000000), 254 | B8_1BIT(00000000), 255 | B8_1BIT(01100000), 256 | B8_1BIT(11000000), 257 | // Frame 2 258 | B8_1BIT(00000000), 259 | B8_1BIT(10000000), 260 | B8_1BIT(11000000), 261 | B8_1BIT(01000000), 262 | // Frame 3 263 | B8_1BIT(00000000), 264 | B8_1BIT(00000000), 265 | B8_1BIT(01100000), 266 | B8_1BIT(11000000), 267 | // Frame 4 268 | B8_1BIT(00000000), 269 | B8_1BIT(10000000), 270 | B8_1BIT(11000000), 271 | B8_1BIT(01000000) 272 | }; 273 | const uint8_t TetrisTData[] = 274 | { 275 | // Frame 1 276 | B8_3BIT(00000000), 277 | B8_3BIT(00000000), 278 | B8_3BIT(66600000), 279 | B8_3BIT(06000000), 280 | // Frame 2 281 | B8_3BIT(00000000), 282 | B8_3BIT(06000000), 283 | B8_3BIT(66000000), 284 | B8_3BIT(06000000), 285 | // Frame 3 286 | B8_3BIT(00000000), 287 | B8_3BIT(00000000), 288 | B8_3BIT(06000000), 289 | B8_3BIT(66600000), 290 | // Frame 4 291 | B8_3BIT(00000000), 292 | B8_3BIT(60000000), 293 | B8_3BIT(66000000), 294 | B8_3BIT(60000000) 295 | }; 296 | const uint8_t TetrisTMask[] = 297 | { 298 | // Frame 1 299 | B8_1BIT(00000000), 300 | B8_1BIT(00000000), 301 | B8_1BIT(11100000), 302 | B8_1BIT(01000000), 303 | // Frame 2 304 | B8_1BIT(00000000), 305 | B8_1BIT(01000000), 306 | B8_1BIT(11000000), 307 | B8_1BIT(01000000), 308 | // Frame 3 309 | B8_1BIT(00000000), 310 | B8_1BIT(00000000), 311 | B8_1BIT(01000000), 312 | B8_1BIT(11100000), 313 | // Frame 4 314 | B8_1BIT(00000000), 315 | B8_1BIT(10000000), 316 | B8_1BIT(11000000), 317 | B8_1BIT(10000000) 318 | }; 319 | const uint8_t TetrisZData[] = 320 | { 321 | // Frame 1 322 | B8_3BIT(00000000), 323 | B8_3BIT(00000000), 324 | B8_3BIT(77000000), 325 | B8_3BIT(07700000), 326 | // Frame 2 327 | B8_3BIT(00000000), 328 | B8_3BIT(07000000), 329 | B8_3BIT(77000000), 330 | B8_3BIT(70000000), 331 | // Frame 3 332 | B8_3BIT(00000000), 333 | B8_3BIT(00000000), 334 | B8_3BIT(77000000), 335 | B8_3BIT(07700000), 336 | // Frame 4 337 | B8_3BIT(00000000), 338 | B8_3BIT(07000000), 339 | B8_3BIT(77000000), 340 | B8_3BIT(70000000) 341 | }; 342 | const uint8_t TetrisZMask[] = 343 | { 344 | // Frame 1 345 | B8_1BIT(00000000), 346 | B8_1BIT(00000000), 347 | B8_1BIT(11000000), 348 | B8_1BIT(01100000), 349 | // Frame 2 350 | B8_1BIT(00000000), 351 | B8_1BIT(01000000), 352 | B8_1BIT(11000000), 353 | B8_1BIT(10000000), 354 | // Frame 3 355 | B8_1BIT(00000000), 356 | B8_1BIT(00000000), 357 | B8_1BIT(11000000), 358 | B8_1BIT(01100000), 359 | // Frame 4 360 | B8_1BIT(00000000), 361 | B8_1BIT(01000000), 362 | B8_1BIT(11000000), 363 | B8_1BIT(10000000) 364 | }; 365 | 366 | #define TETRIS_SPR_WIDTH 4 367 | #define TETRIS_SPR_HEIGHT 4 368 | const uint8_t *TetrisSprData[] = { TetrisIData, TetrisJData, TetrisLData, TetrisOData, TetrisSData, TetrisTData, TetrisZData }; 369 | const uint8_t *TetrisSprMask[] = { TetrisIMask, TetrisJMask, TetrisLMask, TetrisOMask, TetrisSMask, TetrisTMask, TetrisZMask}; 370 | const struct CRGB TetrisColours[] = { CRGB(0, 255, 255), CRGB(0, 0, 255), CRGB(255, 165, 0), CRGB(255, 255, 0), CRGB(50, 205, 50), CRGB(255, 0, 255), CRGB(255, 0, 0) }; 371 | 372 | uint8_t PlayfieldData[MATRIX_HEIGHT * ((MATRIX_WIDTH + 7) / 8) * _3BIT]; 373 | uint8_t PlayfieldMask[MATRIX_HEIGHT * ((MATRIX_WIDTH + 7) / 8) * _1BIT]; 374 | uint8_t CompletedLinesData[TETRIS_SPR_HEIGHT * ((MATRIX_WIDTH + 7) / 8) * _1BIT]; 375 | const struct CRGB CompletedLinesColour[] = { CRGB(255, 255, 255) }; 376 | cSprite Playfield, CompletedLines, CurrentBlock; 377 | cLEDSprites Sprites(&leds); 378 | 379 | unsigned char AttractMsg[144], GameOverMsg[88]; 380 | char BlankMsg[32]; 381 | cLEDText TetrisMsg; 382 | 383 | uint8_t DropDelay; 384 | boolean AttractMode, NextBlock; 385 | int16_t TotalLines; 386 | unsigned int HighScore = 0, LastScore; 387 | 388 | uint16_t PlasmaTime, PlasmaShift; 389 | uint32_t LoopDelayMS, LastLoop; 390 | 391 | 392 | // Joystick class to handle input debounce along with variable delays and repeat option 393 | class cJoyStick 394 | { 395 | public: 396 | cJoyStick(uint8_t pin, uint16_t Debouncems, uint16_t Delayms, boolean Repeat) 397 | { 398 | m_pin = pin; 399 | m_DebounceMS = Debouncems; 400 | m_DelayMS = Delayms; 401 | m_Repeat = Repeat; 402 | m_LastMS = millis(); 403 | m_state = digitalRead(m_pin); 404 | } 405 | boolean Read() 406 | { 407 | uint8_t state = digitalRead(m_pin); 408 | uint32_t ms = millis(); 409 | if ((state != m_state) && ((ms - m_LastMS) >= m_DebounceMS)) 410 | { 411 | m_state = state; 412 | m_LastMS = ms; 413 | return(true); 414 | } 415 | if ( (m_Repeat) && ((ms - m_LastMS) >= m_DelayMS) ) 416 | { 417 | m_LastMS = ms; 418 | return(true); 419 | } 420 | return(false); 421 | } 422 | uint8_t GetState() 423 | { 424 | return(m_state); 425 | } 426 | void SetRepeat(boolean Repeat) 427 | { 428 | m_Repeat = Repeat; 429 | } 430 | protected: 431 | uint8_t m_pin, m_state; 432 | uint16_t m_DebounceMS, m_DelayMS; 433 | uint32_t m_LastMS; 434 | boolean m_Repeat; 435 | }; 436 | 437 | 438 | cJoyStick JSRotate(ROTATE_PIN, 10, 250, false); 439 | cJoyStick JSLeft(LEFT_PIN, 10, 250, false); 440 | cJoyStick JSRight(RIGHT_PIN, 10, 250, false); 441 | cJoyStick JSDown(DOWN_PIN, 10, 50, false); 442 | 443 | 444 | void setup() 445 | { 446 | pinMode(ROTATE_PIN, INPUT_PULLUP); 447 | pinMode(LEFT_PIN, INPUT_PULLUP); 448 | pinMode(RIGHT_PIN, INPUT_PULLUP); 449 | pinMode(DOWN_PIN, INPUT_PULLUP); 450 | 451 | FastLED.addLeds(leds[0], leds.Size()); 452 | FastLED.setBrightness(128); 453 | FastLED.clear(true); 454 | delay(500); 455 | FastLED.showColor(CRGB::Red); 456 | delay(1000); 457 | FastLED.showColor(CRGB::Lime); 458 | delay(1000); 459 | FastLED.showColor(CRGB::Blue); 460 | delay(1000); 461 | FastLED.showColor(CRGB::White); 462 | delay(1000); 463 | FastLED.show(); 464 | 465 | memset(PlayfieldData, 0, sizeof(PlayfieldData)); 466 | memset(PlayfieldMask, 0, sizeof(PlayfieldMask)); 467 | Playfield.Setup(leds.Width(), leds.Height(), PlayfieldData, 1, _3BIT, TetrisColours, PlayfieldMask); 468 | Playfield.SetPositionFrameMotionOptions(0, 0, 0, 0, 0, 0, 0, 0, 0); 469 | Sprites.AddSprite(&Playfield); 470 | 471 | memset(CompletedLinesData, 0, sizeof(CompletedLinesData)); 472 | CompletedLines.Setup(leds.Width(), TETRIS_SPR_HEIGHT, CompletedLinesData, 1, _1BIT, CompletedLinesColour, CompletedLinesData); 473 | CompletedLines.SetPositionFrameMotionOptions(0, 0, 0, 0, 0, 0, 0, 0, 0); 474 | 475 | TetrisMsg.SetFont(MatriseFontData); 476 | sprintf((char *)BlankMsg, "%.*s", min(((leds.Height() + TetrisMsg.FontHeight()) / (TetrisMsg.FontHeight() + 1)), (int)sizeof(BlankMsg) - 1), " "); 477 | sprintf((char *)AttractMsg, "%sTETRIS%sSCORE %u%sHIGH %u%sANY BUTTON TO START%s", BlankMsg, BlankMsg, LastScore, BlankMsg, (int)HighScore, BlankMsg, BlankMsg); 478 | TetrisMsg.Init(&leds, TetrisMsg.FontWidth() + 1, leds.Height(), (leds.Width() - TetrisMsg.FontWidth()) / 2, 0); 479 | TetrisMsg.SetBackgroundMode(BACKGND_LEAVE); 480 | TetrisMsg.SetScrollDirection(SCROLL_UP); 481 | TetrisMsg.SetTextDirection(CHAR_UP); 482 | TetrisMsg.SetFrameRate(1); 483 | TetrisMsg.SetOptionsChangeMode(INSTANT_OPTIONS_MODE); 484 | TetrisMsg.SetText(AttractMsg, strlen((const char *)AttractMsg)); 485 | AttractMode = true; 486 | LoopDelayMS = TARGET_FRAME_TIME; 487 | LastLoop = millis() - LoopDelayMS; 488 | PlasmaShift = (random8(0, 5) * 32) + 64; 489 | PlasmaTime = 0; 490 | } 491 | 492 | void loop() 493 | { 494 | if (abs(millis() - LastLoop) >= LoopDelayMS) 495 | { 496 | LastLoop = millis(); 497 | FastLED.clear(); 498 | 499 | // Fill background with dim plasma 500 | #define PLASMA_X_FACTOR 24 501 | #define PLASMA_Y_FACTOR 24 502 | for (int16_t x=0; x PlasmaTime) 514 | PlasmaShift = (random8(0, 5) * 32) + 64; 515 | 516 | if (AttractMode) 517 | { 518 | if ( ((JSRotate.Read()) && (JSRotate.GetState() == LOW)) || ((JSLeft.Read()) && (JSLeft.GetState() == LOW)) 519 | || ((JSRight.Read()) && (JSRight.GetState() == LOW)) || ((JSDown.Read()) && (JSDown.GetState() == LOW)) ) 520 | { 521 | JSRotate.SetRepeat(true); 522 | JSLeft.SetRepeat(true); 523 | JSRight.SetRepeat(true); 524 | JSDown.SetRepeat(true); 525 | AttractMode = false; 526 | memset(PlayfieldData, 0, sizeof(PlayfieldData)); 527 | memset(PlayfieldMask, 0, sizeof(PlayfieldMask)); 528 | Sprites.RemoveSprite(&CurrentBlock); 529 | LastScore = 0; 530 | TotalLines = 0; 531 | DropDelay = INITIAL_DROP_FRAMES; 532 | CurrentBlock.SetXChange(-1); 533 | NextBlock = true; 534 | } 535 | } 536 | else 537 | { 538 | if (Sprites.IsSprite(&CompletedLines)) // We have highlighted complete lines, delay for visual effect 539 | { 540 | if (CompletedLines.GetXCounter() > 0) 541 | CompletedLines.SetXCounter(CompletedLines.GetXCounter() - 1); 542 | else 543 | { 544 | Sprites.RemoveSprite(&CompletedLines); 545 | // Remove completed lines from playfield sprite 546 | uint8_t *Data = PlayfieldData; 547 | uint8_t *Mask = PlayfieldMask; 548 | uint16_t Mbpl = (MATRIX_WIDTH + 7) / 8; 549 | uint16_t Dbpl = Mbpl * _3BIT; 550 | int16_t k; 551 | for (int16_t i=(MATRIX_HEIGHT-1)*Dbpl,j=(MATRIX_HEIGHT-1)*Mbpl; i>=0; i-=Dbpl,j-=Mbpl) 552 | { 553 | for (k=0; k> min(MATRIX_WIDTH - k, 8)) != Mask[j + (k / 8)]) 556 | break; 557 | } 558 | if (k >= MATRIX_WIDTH) 559 | { 560 | memmove(&Data[Dbpl], &Data[0], i); 561 | memset(&Data[0], 0, Dbpl); 562 | memmove(&Mask[Mbpl], &Mask[0], j); 563 | memset(&Mask[0], 0, Mbpl); 564 | i+=Dbpl; 565 | j+=Mbpl; 566 | } 567 | } 568 | } 569 | } 570 | else 571 | { 572 | if (CurrentBlock.GetXChange() >= 0) // We have a current block 573 | { 574 | // Check for user input 575 | if ( (JSRotate.Read()) && (JSRotate.GetState() == LOW) ) 576 | { 577 | if ((CurrentBlock.GetCurrentFrame() % 2) == 1) 578 | { 579 | if (CurrentBlock.GetXChange() == 0) 580 | CurrentBlock.m_X = min(CurrentBlock.m_X, MATRIX_WIDTH - TETRIS_SPR_WIDTH); 581 | else if ((CurrentBlock.GetXChange() != 3) && (CurrentBlock.GetFlags() & SPRITE_EDGE_X_MAX)) 582 | --CurrentBlock.m_X; 583 | } 584 | CurrentBlock.IncreaseFrame(); 585 | Sprites.DetectCollisions(&CurrentBlock); 586 | if (CurrentBlock.GetFlags() & SPRITE_COLLISION) 587 | CurrentBlock.DecreaseFrame(); 588 | } 589 | if ( (JSLeft.Read()) && (JSLeft.GetState() == LOW) && (! (CurrentBlock.GetFlags() & SPRITE_EDGE_X_MIN)) ) 590 | { 591 | CurrentBlock.m_X--; 592 | Sprites.DetectCollisions(&CurrentBlock); 593 | if (CurrentBlock.GetFlags() & SPRITE_COLLISION) 594 | CurrentBlock.m_X++; 595 | } 596 | else if ( (JSRight.Read()) && (JSRight.GetState() == LOW) && (! (CurrentBlock.GetFlags() & SPRITE_EDGE_X_MAX)) ) 597 | { 598 | CurrentBlock.m_X++; 599 | Sprites.DetectCollisions(&CurrentBlock); 600 | if (CurrentBlock.GetFlags() & SPRITE_COLLISION) 601 | CurrentBlock.m_X--; 602 | } 603 | if ( (JSDown.Read()) && (JSDown.GetState() == LOW) ) 604 | CurrentBlock.SetYCounter(1); 605 | // Do block checks for bottom or collision 606 | if (CurrentBlock.GetYCounter() <= 1) 607 | { 608 | if (CurrentBlock.GetFlags() & SPRITE_EDGE_Y_MIN) 609 | NextBlock = true; 610 | else 611 | { 612 | --CurrentBlock.m_Y; 613 | Sprites.DetectCollisions(&CurrentBlock); 614 | ++CurrentBlock.m_Y; 615 | if (CurrentBlock.GetFlags() & SPRITE_COLLISION) 616 | { 617 | // Block has collided check for game over 618 | int16_t MaxY = MATRIX_HEIGHT - 2; 619 | if ((CurrentBlock.GetCurrentFrame() % 2) == 1) 620 | { 621 | if (CurrentBlock.GetXChange() == 0) 622 | MaxY -= 2; 623 | else if (CurrentBlock.GetXChange() != 3) 624 | MaxY -= 1; 625 | } 626 | else if (CurrentBlock.GetXChange() == 0) 627 | ++MaxY; 628 | if (CurrentBlock.m_Y < MaxY) 629 | NextBlock = true; 630 | else 631 | { 632 | // Game over 633 | CurrentBlock.SetYCounter(2); // Stop last block moving down! 634 | AttractMode = true; 635 | JSRotate.SetRepeat(false); 636 | JSLeft.SetRepeat(false); 637 | JSRight.SetRepeat(false); 638 | JSDown.SetRepeat(false); 639 | if (LastScore > HighScore) 640 | { 641 | HighScore = LastScore; 642 | sprintf((char *)GameOverMsg, "%sGAME OVER%sNEW HIGH SCORE %u%s", BlankMsg, BlankMsg, LastScore, BlankMsg); 643 | } 644 | else 645 | sprintf((char *)GameOverMsg, "%sGAME OVER%sSCORE %u%s", BlankMsg, BlankMsg, LastScore, BlankMsg); 646 | sprintf((char *)AttractMsg, "%sTETRIS%sSCORE %u%sHIGH %u%sANY BUTTON TO START%s", BlankMsg, BlankMsg, LastScore, BlankMsg, HighScore, BlankMsg, BlankMsg); 647 | TetrisMsg.SetText(GameOverMsg, strlen((char *)GameOverMsg)); 648 | TetrisMsg.SetBackgroundMode(BACKGND_DIMMING, 0x40); 649 | } 650 | } 651 | } 652 | } 653 | } 654 | if (NextBlock) // Start new block 655 | { 656 | if (CurrentBlock.GetXChange() >= 0) // We have a current block so add to playfield before creating new block 657 | { 658 | Playfield.Combine(CurrentBlock.m_X, CurrentBlock.m_Y, &CurrentBlock); 659 | Sprites.RemoveSprite(&CurrentBlock); 660 | // Make completed lines highlight sprite & score 661 | memset(CompletedLinesData, 0, sizeof(CompletedLinesData)); 662 | CompletedLines.m_Y = -1; 663 | uint8_t *Mask = PlayfieldMask; 664 | uint16_t Mbpl = (MATRIX_WIDTH + 7) / 8; 665 | int16_t j, numlines = 0; 666 | for (int16_t i=(MATRIX_HEIGHT-1)*Mbpl, y=0; i>=0; i-=Mbpl,++y) 667 | { 668 | for (j=0; j> min(MATRIX_WIDTH - j, 8)) != Mask[i + (j / 8)]) 671 | break; 672 | } 673 | if (j >= MATRIX_WIDTH) 674 | { 675 | if (CompletedLines.m_Y == -1) 676 | CompletedLines.m_Y = y; 677 | memset(&CompletedLinesData[((TETRIS_SPR_HEIGHT - 1) - (y - CompletedLines.m_Y)) * Mbpl], 0xff, Mbpl); 678 | numlines++; 679 | } 680 | } 681 | if (numlines > 0) 682 | { 683 | CompletedLines.SetXCounter(15); // Set delay for highlight display to 15 loops 684 | Sprites.AddSprite(&CompletedLines); 685 | } 686 | LastScore += 1; 687 | if (numlines == 1) 688 | LastScore += 4; 689 | else if (numlines == 2) 690 | LastScore += 12; 691 | else if (numlines == 3) 692 | LastScore += 20; 693 | else if (numlines == 4) 694 | LastScore += 40; 695 | TotalLines += numlines; 696 | DropDelay = max(1, INITIAL_DROP_FRAMES - (TotalLines / 5)); 697 | } 698 | // Start new block 699 | uint8_t j = random8(sizeof(TetrisSprData) / sizeof(TetrisSprData[0])); 700 | CurrentBlock.Setup(TETRIS_SPR_WIDTH, TETRIS_SPR_WIDTH, TetrisSprData[j], 4, _3BIT, TetrisColours, TetrisSprMask[j]); 701 | CurrentBlock.SetPositionFrameMotionOptions((MATRIX_WIDTH/2)-1, MATRIX_HEIGHT, 0, 0, 0, 0, -1, DropDelay, SPRITE_DETECT_COLLISION | SPRITE_DETECT_EDGE); 702 | CurrentBlock.SetXChange(j); 703 | Sprites.AddSprite(&CurrentBlock); 704 | NextBlock = false; 705 | } 706 | Sprites.UpdateSprites(); 707 | } 708 | } 709 | Sprites.RenderSprites(); 710 | if (AttractMode) 711 | { 712 | if (TetrisMsg.UpdateText() == -1) 713 | { 714 | TetrisMsg.SetText(AttractMsg, strlen((char *)AttractMsg)); 715 | TetrisMsg.SetBackgroundMode(BACKGND_LEAVE); 716 | Sprites.RemoveSprite(&CurrentBlock); 717 | memset(PlayfieldData, 0, sizeof(PlayfieldData)); 718 | memset(PlayfieldMask, 0, sizeof(PlayfieldMask)); 719 | } 720 | } 721 | FastLED.show(); 722 | } 723 | } 724 | 725 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For LEDSprites 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | cSprite KEYWORD1 9 | cLEDSprites KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | Setup KEYWORD2 15 | Update KEYWORD2 16 | Combine KEYWORD2 17 | Render KEYWORD2 18 | EdgeDetect KEYWORD2 19 | SetPosition KEYWORD2 20 | SetFrame KEYWORD2 21 | SetMotion KEYWORD2 22 | SetOptions KEYWORD2 23 | SetPositionFrameMotionOptions KEYWORD2 24 | GetFlags KEYWORD2 25 | GetCurrentFrame KEYWORD2 26 | GetXChange KEYWORD2 27 | GetYChange KEYWORD2 28 | GetXCounter KEYWORD2 29 | GetYCounter KEYWORD2 30 | SetXChange KEYWORD2 31 | SetYChange KEYWORD2 32 | SetXCounter KEYWORD2 33 | SetYCounter KEYWORD2 34 | IncreaseFrame KEYWORD2 35 | DecreaseFrame KEYWORD2 36 | 37 | AddSprite KEYWORD2 38 | IsSprite KEYWORD2 39 | RemoveSprite KEYWORD2 40 | RemoveAllSprites KEYWORD2 41 | ChangePriority KEYWORD2 42 | UpdateSprites KEYWORD2 43 | RenderSprites KEYWORD2 44 | DetectCollisions KEYWORD2 45 | 46 | ####################################### 47 | # Constants (LITERAL1) 48 | ####################################### 49 | B8_1BIT LITERAL1 50 | B8_2BIT LITERAL1 51 | B8_3BIT LITERAL1 52 | B8_4BIT LITERAL1 53 | B8_5BIT LITERAL1 54 | _1BIT LITERAL1 55 | _2BIT LITERAL1 56 | _3BIT LITERAL1 57 | _4BIT LITERAL1 58 | _5BIT LITERAL1 59 | _8BIT LITERAL1 60 | SPR_FRONT LITERAL1 61 | SPR_BACK LITERAL1 62 | SPR_FORWARD LITERAL1 63 | SPR_BACKWARD LITERAL1 64 | SPRITE_EDGE_X_MIN LITERAL1 65 | SPRITE_EDGE_X_MAX LITERAL1 66 | SPRITE_EDGE_Y_MIN LITERAL1 67 | SPRITE_EDGE_Y_MAX LITERAL1 68 | SPRITE_MATRIX_X_OFF LITERAL1 69 | SPRITE_MATRIX_Y_OFF LITERAL1 70 | SPRITE_COLLISION LITERAL1 71 | SPRITE_DETECT_EDGE LITERAL1 72 | SPRITE_DETECT_COLLISION LITERAL1 73 | SPRITE_X_KEEPIN LITERAL1 74 | SPRITE_Y_KEEPIN LITERAL1 75 | 76 | ####################################### 77 | # Class public variables (LITERAL2) 78 | ####################################### 79 | m_X LITERAL2 80 | m_Y LITERAL2 81 | --------------------------------------------------------------------------------