=====Hra PINP-PONG=====
====Zadání====
Realizovat hru 2D ping-pong s použitím LED matice LPD6803. Hra bude ovládána tlačítky a bude obsahovat minimálně režim pro dva hráče. Využijte vývojovou desku FRDM-KL25Z.
====Hra PING-PONG====
2D - pong hra je trochu jako staré hry na staré konzole " Pong ". V normální hře musite umístit své " bat " ve správné svislé poloze zasáhnout míč , ale protože to je jednorozměrný prostor není moc pohybovat se ve druhém rozměru. Místo toho, " koule " (v tomto případě linie) se pohybuje podél LED pás z jednoho hráče na druhý (druhé každé ze 2 hráče umístěných na jedné straně ledstrip). Budete muset trefit ho včas, aby ji vrátí. Pokud stisknete příliš brzy nebo příliš pozdě , ztrácíte jeden "soubor". Pokud jste hit " míč " ve správném časovém budete vrátit míč na druhou stranu. Aby hra byla ještě zajímavější : později narazí na " kouli ", tím rychleji budete vrátit míč do svého soupeře.
{{:2015:12.png?400|}}
Obr.1 Prvky hry PING-PONG
====Hardware====
2D -pong hra je založena na Freescale FRDM - KL25Z desky, a na základě LPD6803 pixelstrip LED ovladač. Pixelstrip to byl napsán pro měl 50 bodů (150 LED, každý pixel je 3 LED diody v řadě ). Chcete-li řídit pixelů se používá rozhraní SPI na KL25Z. 2 tlačítka slouží k ovládání pádlo, oni jsou oddělené dvěma 100nF kondenzátory ( jak pro zákmitu a být imunní vůči interferující EM polí).
* Nečíslovaný seznamJedním tlačítkem mezi PTD0 a GND (by měla být normálně zavřený, takže pokud jej zasáhl by měla být nulová ohm, pokud by to mělo být propuštěn nekonečno ohm).
* ostatní tlačítka mezi PTD5 a GND (stejného příběhu jak je uvedeno výše).
* Použijte PTD2 pro data a PTD1 pro hodiny na LED pásu. Použime jednoduchý HCT vyrovnávací paměť v mezi ledstrip a mbed pro přepnutí mezi 3v3 a 5V . LDP6803 má pullups na jeho vstupy , takže CLK a DATA jsou vždy vytáhl na 5V. Mbed pracuje na 3V , takže použít něco do vyrovnávací paměti mezi těmito dvěma.
====Software====
Pro realizaci hry v tomto projektu je použit compiler na webu https://developer.mbed.org/, ve kterém je možné vytvořit libovolný projekt na C++.
====Realizace programu hry pomocí jazyka C++====
#include "mbed.h"
#include "MODSERIAL.h"
#include "paddle.h"
#include "GameButton.h"
#define NUMBER_OF_PIXELS 50
#define PADDLE_LENGTH 5
#define START_SPEED 19
#define SPEED_FACTOR 7
#define LEFT false
#define RIGHT true
void Randomblinks(float seconds, bool colored = false);
void PaddleDemo(float seconds, uint8_t red, uint8_t green, uint8_t blue);
void WinLoose(float seconds, bool side);
void UpdateDemoPaddle(void);
void Score(uint8_t left, uint8_t right);
void DrawGamePaddle(void);
void HandleScore(uint8_t *, uint8_t *, bool, Timer *);
void DrawLine(uint8_t start, uint8_t end, uint8_t red, uint8_t green, uint8_t blue);
uint16_t totalstrip[NUMBER_OF_PIXELS];
volatile int8_t paddlestart= 0;
bool strip_drawable = true;
SPI ledstrip(PTD2,NC,PTD1);
MODSERIAL pc(PTA2,PTA1);
Paddle paddle;
class PongGameButton : public GameButton
{
public:
PongGameButton(PinName name, float time);
void pushhandlercallback(void);
int16_t paddlepos;
};
PongGameButton::PongGameButton(PinName name, float time) : GameButton(name, time)
{
paddlepos = 0;
}
void PongGameButton::pushhandlercallback(void)
{
paddlepos = paddle.position;
}
void UpdateLEDstrip(void)
{
uint8_t pixelcounter;
if(strip_drawable)
{
/*start by writing 32 zeroes */
ledstrip.write(0);
ledstrip.write(0);
ledstrip.write(0);
ledstrip.write(0);
for(pixelcounter = 0 ; pixelcounter < NUMBER_OF_PIXELS; pixelcounter++) {
ledstrip.write( uint8_t(totalstrip[pixelcounter]>>8));//uint8_t(temp16));//(totalstrip.ledcounter[pixelcounter].red << 2) | (totalstrip.ledcounter[pixelcounter].high << 7) |(totalstrip.ledcounter[pixelcounter].green & 0x << 2) );
ledstrip.write( uint8_t(totalstrip[pixelcounter]));//(*(uint16_t *)(&totalstrip[pixelcounter]))>>8);
}
}
}
void write_led(uint16_t * led, uint8_t red, uint8_t green, uint8_t blue)
{
*led = (1<<15) | ((green >> 3)<<10) | ((red >>3)<< 5) | (blue >>3);
}
bool rightpushed = false;
int16_t rightpushpos = 0;
void right_pushed(void)
{
rightpushpos = paddle.position;
if(paddle.direction == 1)
rightpushed = true;
}
int main()
{
Ticker updater;
//Ticker demopaddlepos;
Timer gametimer;
PongGameButton buttonleft(PTD0,1);
PongGameButton buttonright(PTD5, 1);
uint8_t ledcounter;
uint8_t left_score = 0, right_score = 0;
pc.baud(115200);
updater.attach(UpdateLEDstrip, .03);
ledstrip.format(8,0); //15 bits, mode '0'
ledstrip.frequency(1000000); //1MHz clock
for(ledcounter = 0; ledcounter < NUMBER_OF_PIXELS; ledcounter++) {//turn off leds
write_led(&totalstrip[ledcounter], 0,0,0);
}
paddle.setSize(6);
paddle.setSpeed(START_SPEED);
paddle.setColor(255,0,255);
paddle.position = NUMBER_OF_PIXELS/2;
gametimer.start();
while(1) {
static uint8_t naglevel1 = 0, naglevel2 = 0;
//paddle.position = 48;
//while(1);
strip_drawable = false;
DrawGamePaddle();
if(buttonleft.getTimeoutActive())
DrawLine(0,1,0,0,255);
if(buttonright.getTimeoutActive())
DrawLine(NUMBER_OF_PIXELS-1, NUMBER_OF_PIXELS,0,0,255);
strip_drawable = true;
if(buttonleft.pushflag || buttonright.pushflag)
{
if(paddle.direction == 1)
{
if(buttonright.pushflag)
{
//printf("\n\rright pushed");
pc.printf("R\n\r");
buttonright.pushflag = false;
if(buttonright.paddlepos >= NUMBER_OF_PIXELS-1 ) //also count when hit at last pixel = NUMBER_OF_PIXELS-1
{
paddle.direction = 0;
paddle.setSpeed(START_SPEED+(buttonright.paddlepos-(NUMBER_OF_PIXELS-1))*SPEED_FACTOR);
paddle.position = NUMBER_OF_PIXELS-2;
}
//pc.printf("\n\rright pushed. Paddle position: %d, registered: %d, speed: %", paddle.position, buttonright.paddlepos,paddle.getSpeed());
}
buttonleft.pushflag = false;
}
else
{
if(buttonleft.pushflag)
{
//printf("\n\rleft pushed");
pc.printf("L\n\r");
buttonleft.pushflag = false;
if(buttonleft.paddlepos <= 1 )
{
paddle.direction = 1;
paddle.setSpeed(START_SPEED+(-buttonleft.paddlepos)*SPEED_FACTOR);
paddle.position = 0;
}
//pc.printf("\n\rleft pushed. Paddle position: %d, registered: %d, speed %d", paddle.position, buttonleft.paddlepos, paddle.getSpeed());
}
buttonright.pushflag = false;
}
}
else
{
if(paddle.position > ( NUMBER_OF_PIXELS + paddle.getSize() ) && (paddle.direction == 1))
{
//pc.printf("\n\rleft player score. Paddle position: %d", paddle.position);
//left player scores!
left_score++;
pc.printf("S:%2d:%2d\n\r",left_score,right_score);
naglevel1=naglevel2 = 0;
HandleScore(&left_score,&right_score,false, &gametimer);
buttonright.pushflag = false;
buttonleft.pushflag = false;
}
if(paddle.position < -paddle.getSize() && (paddle.direction == 0))
{
//pc.printf("\n\rright player score. Paddle position: %d", paddle.position);
//right player scores!
right_score++;
pc.printf("S:%2d:%2d\n\r",left_score,right_score);
naglevel1=naglevel2 = 0;
HandleScore(&left_score,&right_score,true, &gametimer);
buttonright.pushflag = false;
buttonleft.pushflag = false;
}
}
if(gametimer.read()>10 && !naglevel1)
{
naglevel1 = 1;
paddle.setSize(8);
//paddle.setSpeed(40);
}
if(gametimer.read()>15 && !naglevel2)
{
naglevel2 = 1;
paddle.setSize(10);
//paddle.setSpeed(70);
}
wait(0.01);
}
}
void HandleScore(uint8_t *leftscore, uint8_t *rightscore, bool last_won, Timer *gametimer)
{
WinLoose(1.5, last_won);
Score(*leftscore, *rightscore);
if(*leftscore + *rightscore == 11)
{
pc.printf("EOG\n\r");
*leftscore = 0;
*rightscore = 0;
Randomblinks(5,false);
pc.printf("S: 0: 0\n\r");
}
(*(mbed::Timer *)gametimer).reset();
paddle.setSize(6);
(rand()%20)>10?paddle.direction = 1:paddle.direction = 0;
paddle.setSpeed(START_SPEED);
paddle.position = NUMBER_OF_PIXELS/2;
}
void DrawGamePaddle(void)
{
uint8_t ledcounter;
uint8_t colorpos;
for(ledcounter = 0; ledcounter< NUMBER_OF_PIXELS; ledcounter++)
{
if(paddle.direction == 1)
{
if(ledcounter > paddle.position-paddle.getSize() && ledcounter <= paddle.position)
{
colorpos = paddle.getSize()-(paddle.position - ledcounter);//paddle.getSize()-(ledcounter-paddle.position);
write_led(&totalstrip[ledcounter],paddle.getColor(colorpos,0),paddle.getColor(colorpos,1),paddle.getColor(colorpos,2));
}
else
write_led(&totalstrip[ledcounter], 0,0,0);
}
else
{
if(ledcounter >= paddle.position && ledcounter <= paddle.position+paddle.getSize())
{
colorpos = paddle.getSize()-(ledcounter-paddle.position);
write_led(&totalstrip[ledcounter],paddle.getColor(colorpos,0),paddle.getColor(colorpos,1),paddle.getColor(colorpos,2));
}
else
write_led(&totalstrip[ledcounter], 0,0,0);
}
}
}
void Score(uint8_t left, uint8_t right)
{
uint8_t maxscore;
int8_t ledcounter;
uint8_t scorecounter;
typedef struct ledcolor
{
uint8_t red;
uint8_t green;
uint8_t blue;
}ledcolor_t;
ledcolor_t rightled={0,0,0}, leftled={0,0,0};
left>=right?maxscore = left: maxscore = right;
for(scorecounter = 0 ; scorecounter <= maxscore ; scorecounter++)
{
uint8_t templeft,tempright;
uint8_t sidecounter;
templeft = left>scorecounter?scorecounter:left;
tempright = right>scorecounter?scorecounter:right;
//pc.printf("scorecounter: %d, maxscore: %d\n\r",scorecounter, maxscore);
/*Change color on last score update: green is winning, red is losing, yellow is draw*/
if(scorecounter == maxscore)
{
leftled.green = leftled.red = leftled.blue = rightled.green = rightled.red = rightled.blue = 0;
if(left >= right)
{
leftled.green = 255;
rightled.red = 255;
}
if(right >= left)
{
leftled.red = 255;
rightled.green = 255;
}
}
else
{
leftled.green = leftled.red = leftled.blue = rightled.green = rightled.red = rightled.blue = 255;
}
for(ledcounter = 0; ledcounter < NUMBER_OF_PIXELS; ledcounter++)//blank memory
{
write_led(&totalstrip[ledcounter], 0, 0 ,0);
}
for(sidecounter = 0 ; sidecounter < templeft; sidecounter++)
{
write_led(&totalstrip[sidecounter*2],leftled.red,leftled.green,leftled.blue);
}
for(sidecounter = 0 ; sidecounter < tempright ; sidecounter++)
{
write_led(&totalstrip[(NUMBER_OF_PIXELS-1)-(sidecounter*2)],rightled.red,rightled.green,rightled.blue);
}
wait(0.2);
}
wait(0.7);
}
//Only writes pixels that are in 'line'. Does not clear, only overwrites.
void DrawLine(uint8_t start, uint8_t end, uint8_t red, uint8_t green, uint8_t blue)
{
uint8_t ledcounter;
if(end >= NUMBER_OF_PIXELS)
end = NUMBER_OF_PIXELS;
if(start < end)
{
for(ledcounter = start; ledcounter < end ; ledcounter++)
{
write_led(&totalstrip[ledcounter], red, green, blue);
}
}
}
void WinLoose(float seconds, bool side)
{
uint8_t ledcounter;
Timer timer;
timer.start();
while( timer.read() < seconds)
{
uint8_t redvalue = 255-(255.0*(timer.read()/(seconds/2)));
for(ledcounter = 0; ledcounter < NUMBER_OF_PIXELS; ledcounter++)
{
if(ledcounter < NUMBER_OF_PIXELS / 2)
{
if(side)
write_led(&totalstrip[ledcounter], redvalue,0,0);
else
write_led(&totalstrip[ledcounter], 0,255,0);
}
else
{
if(side)
write_led(&totalstrip[ledcounter], 0,255,0);
else
write_led(&totalstrip[ledcounter], redvalue ,0,0);
}
}
}
}
void PaddleDemo(float seconds, uint8_t red, uint8_t green, uint8_t blue)
{
uint8_t ledcounter;
Timer timer;
timer.start();
while( timer.read() < seconds)
{
for(ledcounter = 0; ledcounter < NUMBER_OF_PIXELS; ledcounter++)
{
if((ledcounter >= paddlestart) && ( ledcounter <= paddlestart+PADDLE_LENGTH))
write_led(&totalstrip[ledcounter], red,green,blue);
else
write_led(&totalstrip[ledcounter], 0,0,0);
}
}
}
void Randomblinks(float seconds, bool colored)
{
uint8_t ledcounter;
uint8_t test;
Timer timer;
timer.start();
while( timer.read() < seconds )
{
test = 50.0*rand()/(RAND_MAX*1.0);
for(ledcounter = 0; ledcounter < NUMBER_OF_PIXELS; ledcounter++)
{
if(ledcounter == test)
{
if(colored)
write_led(&totalstrip[ledcounter], test*5,(test%10)*25,(test%15)*15);
else
write_led(&totalstrip[ledcounter], 255,255,255);
}
else
write_led(&totalstrip[ledcounter], 0,0,0);
}
}
}
void UpdateDemoPaddle(void)
{
static uint8_t direction = 1;
if(direction) {
paddlestart++;
} else {
paddlestart--;
}
if(paddlestart > (NUMBER_OF_PIXELS - PADDLE_LENGTH))
direction = 0;
if(paddlestart < 0)
direction = 1;
}
Jeste zapišem knihovny pro tlačítko а pádlo.
#include "GameButton.h"
GameButton::GameButton(PinName pin, float time=0.5)
{
pushflag = false;
intpin = new InterruptIn(pin);
(*intpin).mode(PullUp);
(*intpin).fall(this, &GameButton::PushHandler);
m_time = time;
m_timeoutactive = false;
inpin = pin;
//timeout = new Timeout(m_time);
}
void GameButton::PushHandler(void)
{
DigitalIn input(inpin);
wait_ms(5);
if(!input)
{
pushflag = true;
m_timeoutactive = true;
timeout.attach(this, &GameButton::TimeOutHandler, m_time);
pushhandlercallback();
}
}
void GameButton::TimeOutHandler(void)
{
m_timeoutactive = false;
}
bool GameButton::getTimeoutActive(void)
{
return m_timeoutactive;
}
#include "paddle.h"
Paddle::Paddle()
{
setColor(255,255,255);
setSize(4);
position = -getSize();
direction = 1;
setSpeed(50);
}
void Paddle::setSpeed(float speed)
{
//truncate
float time;
if(speed > 100000)
speed = 100000;
if(speed <= 0)
speed = 0.001;
m_speed = speed;
time = 1/m_speed;
UpdatePosition.detach();
UpdatePosition.attach(this,&Paddle::PositionUpdater, time);
}
void Paddle::PositionUpdater(void)
{
if(direction == 1)
position++;
else
position--;
}
uint8_t Paddle::getColor(uint8_t pixel, uint8_t color)
{
if(pixel MAX_PADDLE_SIZE)
size = MAX_PADDLE_SIZE;
m_size = size;
setColor(m_red, m_green, m_blue);
}
void Paddle::setColor(uint8_t red, uint8_t green, uint8_t blue)
{
uint8_t paddlepixel;
m_red = red;
m_green = green;
m_blue = blue;
for(paddlepixel = 0 ; paddlepixel < MAX_PADDLE_SIZE ; paddlepixel++)
{
float factor;
factor = 1.0*paddlepixel/(m_size*1.0);
if (factor > 1)
factor = 1;
factor = factor*factor*factor;// make the effect more dramatic
m_paddle[paddlepixel][0] = (float)m_red * factor;
m_paddle[paddlepixel][1] = (float)m_green * factor;
m_paddle[paddlepixel][2] = (float)m_blue * factor;
}
}
==== Záver ====
Cílem tohoto individuálního projektu bylo realizovat hru PING-PONG na FRDM-KL25Z s použitím LED matice LPD6803. Při práci na projektu bylo obtížné psát kód pro tlačítka. Býlo obtižne realizovat systému protože stojí to hodně peněz a proste složne pro realizace. Ale celkem je možné říct, že vsechny úkoly tohoto projektu jsou splněny.