Forum Replies Created
-
AuthorPosts
-
moeMember
Woah this sounds pretty heavy, I’ll try to implement the things you told me, I hope you can help me for further question!
Thanks really much!
moeMemberUnfortunately I think the game will take longer than you expect because I just have built the title screen and some little things, and I’ve not started developing the game yet… 😛
I might have a big problem because everything I do have now already takes up 10.000 Bytes of 30.000 Bytes available. I don’t have any feeling for storage on the Hackvision platform, do you think my entire game will have enough free storage? Just asking because you already developed 2… And yeah if this would really happen, how can I save storage (without removing too much) and which things take up the most space?
Thanks!
moeMemberWow thanks a lot trodoss for your quick reply and effort!
I’ve already came up with a solution for the “[“, but your answer concerning the bitmaps does help me a lot! I soon will decorate my title screen with a nice picture 😀
So yeah thank you really much, I hope if I’ve got any other question you’ll help me!
Is there any way to contact you via skype or such? Just for talking about Hackvision or little Code related things… I’m also ok if you’d say “No”
moeMember
#define TV_W 136
#define TV_H 98
#define FRAME_X0 8
#define FRAME_X1 128
#define FRAME_Y0 9
#define FRAME_Y1 89
#define LEFT 3
#define RIGHT 2
#define UP 4
#define DOWN 5
#define FIRE 10
struct Segment
{
boolean active;
byte x;
byte y;
};
prog_char s0[] PROGMEM = "ARDUINO WORM";
prog_char s1[] PROGMEM = "GAME";
prog_char s2[] PROGMEM = "OVER";
prog_char s3[] PROGMEM = "SCORE: ";
prog_char s4[] PROGMEM = "BREAKOUT";
PROGMEM const char *strings[] = {
s0,s1,s2,s3,s4};
char s[16]; // general string buffer
char scoreString[11];
byte currentTonePriority = 0;
byte ballX;
byte ballY;
byte movingDirection;
boolean ballCollected = false;
int currentScore;
Segment worm[50];
void (*game)();
TVout TV;
const byte width = 120; //Width of screen
const byte height = 96; //Hight of screen
int dx = -1; //Initial movement of ball
int dy = -1; //Initial movement of ball
int xb; //Balls starting possition
int yb; //Balls starting possition
boolean released; //If the ball has been released by the player
boolean paused = false; //If the game has been paused
byte xPaddle; //X position of paddle
boolean isHit[5][12]; //Array of if bricks are hit or not
boolean bounced=false; //Used to fix double bounce glitch
byte lives = 3; //Amount of lives
byte level = 1; //Current level
unsigned int score=0; //Score for the game
unsigned int brickCount; //Amount of bricks hit
byte pad,pad2,pad3; //Button press buffer used to stop pause repeating
byte oldpad,oldpad2,oldpad3;
char text[16]; //General string buffer
boolean start=false; //If in menu or in game
boolean initialDraw=false;//If the inital draw has happened
char initials[3]; //Initials used in high score
//Ball Bounds used in collision detection
byte leftBall;
byte rightBall;
byte topBall;
byte bottomBall;
//Brick Bounds used in collision detection
byte leftBrick;
byte rightBrick;
byte topBrick;
byte bottomBrick;
float speedAdjust = 1.0;
void setup()
{
// If pin 12 is pulled LOW, then the PAL jumper is shorted.
pinMode(12, INPUT);
digitalWrite(12, HIGH);
//Set video mode
if (digitalRead(12) == LOW) {
TV.begin(PAL,width,height);
}
else {
TV.begin(NTSC,width,height);
}
TV.select_font(font6x8);
randomSeed(analogRead(0));
playTone(1046, 20);
TV.delay(1);
playTone(1318, 20);
TV.delay(1);
playTone(1568, 20);
TV.delay(1);
playTone(2093, 20);
byte m[2] = {0, 4};
byte choice = menu(2, m);
if (choice == 0) {
game = &worms;
TV.delay(10);
initWorms();
}
if (choice == 1) {
game = &breakout;
TV.delay(10);
}
}
byte menu(byte nChoices, byte *choices) {
char choice = 0;
TV.fill(0);
byte x = 24;
byte y;
while (true) {
for(byte i=0;istrcpy_P(s, (char *)pgm_read_word(&(strings[choices])));
TV.print(32, 30+(i*8), s);
}
for(byte i=0;iy = 30+(i*8);
if (i == choice) {
// draw arrow next to selected game
TV.set_pixel(x+4, y, 1);
TV.set_pixel(x+5, y+1, 1);
TV.draw_line(x, y+2, x+6, y+2, 1);
TV.set_pixel(x+5, y+3, 1);
TV.set_pixel(x+4, y+4, 1);
}
else {
for(byte j=0;j<8;j++) {
TV.draw_line(x, y+j, x+7, y+j, 0);
}
}
}
// get input
if (pollFireButtonWorm(10)) {
playTone(1046, 20);
return choice;
}
if (Controller.upPressed()) {
choice--;
if (choice == -1) {
choice = 0;
}
else {
playTone(1046, 20);
}
}
if (Controller.downPressed()) {
choice++;
if (choice == nChoices) {
choice = nChoices-1;
}
else {
playTone(1046, 20);
}
}
}
}
void initWorms() {
for(int x = 0; x < 5; x++) {
worm[x].active = true;
worm[x].x = 30-x;
worm[x].y = 30;
}
for(int x = 5; x < 50; x++) {
worm[x].active = false;
worm[x].x = 0;
worm[x].y = 0;
}
movingDirection = RIGHT;
ballCollected = true;
currentScore = 0;
updateScore();
}
void loop() {
game();
}
void worms() {
TV.fill(0);
drawFrame();
drawScore();
drawBallWorm();
for(int x = 0; x < 50; x++) {
if(worm[x].active)
TV.set_pixel(worm[x].x,worm[x].y,1);
}
move();
TV.delay_frame(4);
}
void drawBallWorm() {
if(ballCollected) {
ballX = random(FRAME_X0+1, FRAME_X1-1);
ballY = random(FRAME_Y0+1, FRAME_Y1-1);
ballCollected = false;
}
TV.set_pixel(ballX,ballY,1);
}
void drawFrame() {
TV.draw_line(FRAME_X0, FRAME_Y0, FRAME_X1, FRAME_Y0, 1);
TV.draw_line(FRAME_X0, FRAME_Y1, FRAME_X1, FRAME_Y1, 1);
TV.draw_line(FRAME_X0, FRAME_Y0, FRAME_X0, FRAME_Y1, 1);
TV.draw_line(FRAME_X1, FRAME_Y0, FRAME_X1, FRAME_Y1, 1);
}
void move() {
if (Controller.upPressed()) {
if(movingDirection != DOWN) {
movingDirection = UP;
}
}
else if(Controller.downPressed()) {
if(movingDirection != UP) {
movingDirection = DOWN;
}
}
else if(Controller.leftPressed()) {
if(movingDirection != RIGHT) {
movingDirection = LEFT;
}
}
else if(Controller.rightPressed()) {
if(movingDirection != LEFT) {
movingDirection = RIGHT;
}
}
switch (movingDirection) {
case UP:
if(detectBallHit(worm[0].x, worm[0].y-1)) {
assignBallCoordinatesToFirstSegment();
}
else {
shiftSegments();
worm[0].y = worm[0].y-1 ;
}
break;
case DOWN:
if(detectBallHit(worm[0].x, worm[0].y+1)){
assignBallCoordinatesToFirstSegment();
}
else {
shiftSegments();
worm[0].y = worm[0].y+1 ;
}
break;
case LEFT:
if(detectBallHit(worm[0].x-1, worm[0].y)){
assignBallCoordinatesToFirstSegment();
}
else {
shiftSegments();
worm[0].x = worm[0].x-1 ;
}
break;
case RIGHT:
if(detectBallHit(worm[0].x+1, worm[0].y)){
assignBallCoordinatesToFirstSegment();
}
else {
shiftSegments();
worm[0].x = worm[0].x+1 ;
}
break;
}
if(worm[0].x > FRAME_X1-1 || worm[0].x < FRAME_X0+1 || worm[0].y > FRAME_Y1-1 || worm[0].y < FRAME_Y0+1 || checkForTailBite()) {
gameOver();
initWorms();
return;
}
}
boolean checkForTailBite() {
for(int x = 1; x < 50; x++) {
if(worm[0].x == worm[x].x && worm[0].y == worm[x].y) {
return true;
}
}
return false;
}
void shiftSegments() {
for(int x = 49; x > 0; x--) {
if(worm[x].active) {
worm[x].x = worm[x-1].x;
worm[x].y = worm[x-1].y;
}
}
}
void assignBallCoordinatesToFirstSegment() {
worm[0].x = ballX;
worm[0].y = ballY;
}
boolean detectBallHit(byte sx, byte sy) {
if(sx == ballX && sy == ballY) {
for(int x = 49; x > -1; x--) {
if(worm[x].active) {
worm[x+1].active = true;
worm[x+1].x = worm[x].x;
worm[x+1].y = worm[x].y;
}
}
ballCollected = true;
currentScore += 1;
updateScore();
playTone(1046, 10);
TV.delay(1);
playTone(1318, 10);
TV.delay(1);
playTone(1568, 10);
TV.delay(1);
playTone(2093, 10);
return true;
}
else {
return false;
}
}
void drawScore() {
TV.print(FRAME_X0, 0, scoreString);
}
void updateScore() {
strcpy_P(scoreString, (char *)pgm_read_word(&(strings[3])));
sprintf(s, "%d", currentScore);
strcat(scoreString, s);
}
void gameOver() {
TV.delay(1000);
TV.fill(0);
strcpy_P(s, (char *)pgm_read_word(&(strings[1])));
TV.print(44, 40, s);
strcpy_P(s, (char *)pgm_read_word(&(strings[2])));
TV.print(72, 40, s);
TV.delay(3000);
}
boolean pollFireButtonWorm(int n) {
for(int i=0;iTV.delay_frame(1);
if (Controller.firePressed()) {
return true;
}
}
return false;
}
void playTone(unsigned int frequency, unsigned long duration_ms) {
// Default is to play tone with highest priority.
playTone(frequency, duration_ms, 9);
}
void playTone(unsigned int frequency, unsigned long duration_ms, byte priority) {
// priority is value 0-9, 9 being highest priority
if (TCCR2B > 0) {
// If a tone is currently playing, check priority
if (priority < currentTonePriority) {
return;
}
}
currentTonePriority = priority;
TV.tone(frequency, duration_ms);
}
void movePaddle()
{
//Move right
if(xPaddle < width-12)
{
if(Controller.rightPressed())
{
xPaddle++;
}
}
//Move left
if(xPaddle > 0)
{
if(Controller.leftPressed())
{
xPaddle--;
}
}
}
void moveBall()
{
if(released)
{
//Move ball
xb=xb + dx;
yb=yb + dy;
//Set bounds
leftBall = xb;
rightBall = xb+2;
topBall = yb;
bottomBall = yb+2;
//Bounce off top edge
if (yb<=0)
{
yb=2;
dy=-dy;
TV.tone(523, 20);
}
//Lose a life if bottom edge hit
if (yb>=89)
{
TV.draw_rect(xPaddle,87,11,1,0);
xPaddle = 54;
yb=84;
released = false;
lives--;
drawLives();
TV.tone(175, 20);
if (random(0, 2) == 0) {
dx = 1;
}
else {
dx = -1;
}
}
//Bounce off left side
if (xb<=0)
{
xb=2;
dx=-dx;
TV.tone(523, 20);
}
//Bounce off right side
if (xb>=width-2)
{
xb=width-4;
dx=-dx;
TV.tone(523, 20);
}
//Bounce off paddle
if (xb+1>=xPaddle && xb<=xPaddle+12 && yb+2>=87 && yb<=92){
dy=-dy;
dx=((xb-(xPaddle+6))/3); //Applies spin on the ball
TV.tone(200, 20);
}
//Bounce off Bricks
for (byte row = 0; row < 5; row++) {
for (byte column = 0; column < 12; column++) {
if (!isHit[row][column]){
//Sets Brick bounds
leftBrick = 10*column;
rightBrick = 10*column+10;
topBrick = 6*row+1;
bottomBrick = 6*row+7;
//If A collison has occured
if (topBall <= bottomBrick && bottomBall>=topBrick && leftBall<=rightBrick && rightBall >=leftBrick){
Score();
brickCount++;
isHit[row][column] = true;
TV.draw_rect(10*column ,2+6*row,8,4,0);
//Vertical collision
if (bottomBall>bottomBrick || topBall//Only bounce once each ball move
if(!bounced){
dy=-dy;
yb+=dy;
bounced=true;
TV.tone(261, 20);
}
}
//Hoizontal collision
if (leftBallrightBrick){
//Only bounce once brick each ball move
if(!bounced){
dx=-dx;
xb+=dx;
bounced=true;
TV.tone(261, 20);
}
}
}
}
}
}
//Reset Bounce
bounced=false;
}
else{
//Ball follows paddle
xb=xPaddle+5;
//Release ball if FIRE pressed
pad3 = Controller.firePressed();
if(pad3 ==1 && oldpad3==0){
released=true;
//Apply random direction to ball on release
if (random(0, 2) == 0) {
dx = 1;
}
else {
dx = -1;
}
//Makes sure the ball heads upwards
dy = -1;
}
oldpad3=pad3;
}
}
void drawBall()
{
TV.set_pixel(xb,yb,0);
TV.set_pixel(xb+1,yb,0);
TV.set_pixel(xb,yb+1,0);
TV.set_pixel(xb+1,yb+1,0);
moveBall();
TV.set_pixel(xb,yb,1);
TV.set_pixel(xb+1,yb,1);
TV.set_pixel(xb,yb+1,1);
TV.set_pixel(xb+1,yb+1,1);
}
void drawPaddle()
{
TV.draw_rect(xPaddle,87,11,1,0);
movePaddle();
TV.draw_rect(xPaddle,87,11,1,1);
}
void drawLives()
{
sprintf(text, "LIVES:%u", lives);
TV.print(0, 90, text);
}
void drawGameOver()
{
TV.set_pixel(xb,yb,0);
TV.set_pixel(xb+1,yb,0);
TV.set_pixel(xb,yb+1,0);
TV.set_pixel(xb+1,yb+1,0);
TV.print(52, 42, "Game");
TV.print(52, 54, "Over");
delay(4000);
}
void pause()
{
paused = true;
//Draw pause to the screen
TV.print(52, 45, "PAUSE");
while(paused){
delay(15);
//Unpause if FIRE is pressed
pad2 = Controller.firePressed();
if(pad2 ==1 && oldpad2==0 && released){
TV.print(52, 45, " ");
paused=false;
}
oldpad2=pad2;
}
}
void Score()
{
score += (level*10);
sprintf(text, "SCORE:%u", score);
TV.print(80, 90, text);
}
void newLevel(){
//Undraw paddle
TV.draw_rect(xPaddle,87,11,1,0);
//Undraw ball
TV.set_pixel(xb,yb,0);
TV.set_pixel(xb+1,yb,0);
TV.set_pixel(xb,yb+1,0);
TV.set_pixel(xb+1,yb+1,0);
//Alter various variables to reset the game
xPaddle = 54;
yb=84;
brickCount=0;
released=false;
//Draws new bricks and resets their values
for (byte row = 0; row < 5; row++) {
for (byte column = 0; column < 12; column++) {
isHit[row][column] = false;
TV.draw_rect(10*column ,2+6*row,8,4,1);
}
}
//Draws the initial lives
drawLives();
//Draws the initial score
sprintf(text, "SCORE:%u", score);
TV.print(80, 90, text);
}
//Used to delay images while reading button input
boolean pollFireButton(int n) {
for(int i=0;iTV.delay(15);
pad = Controller.firePressed();
if(pad ==1 && oldpad==0){
oldpad3=1; //Forces pad loop 3 to run once
return true;
}
oldpad=pad;
}
return false;
}
//Function by nootropic design to display highscores
boolean displayHighScores(byte file) {
byte y = 10;
byte x = 24;
// Each block of EEPROM has 10 high scores, and each high score entry
// is 5 bytes long: 3 bytes for initials and two bytes for score.
int address = file*10*5;
byte hi, lo;
TV.fill(0);
TV.select_font(font6x8);
TV.print(32, 0, "HIGH SCORES");
for(int i=0;i<10;i++) {
sprintf(text, "%2d", i+1);
TV.print(x, y+(i*8), text);
hi = EEPROM.read(address + (5*i));
lo = EEPROM.read(address + (5*i) + 1);
if ((hi == 0xFF) && (lo == 0xFF)) {
score = 0;
}
else {
score = (hi << 8) | lo;
}
initials[0] = (char)EEPROM.read(address + (5*i) + 2);
initials[1] = (char)EEPROM.read(address + (5*i) + 3);
initials[2] = (char)EEPROM.read(address + (5*i) + 4);
if (score > 0) {
sprintf(text, "%c%c%c %u", initials[0], initials[1], initials[2], score);
TV.print(x+24, y+(i*8), text);
}
}
if (pollFireButton(300)) {
return true;
}
return false;
}
boolean titleScreen() {
//Clears the screen
TV.fill(0);
//Draws the main title
TV.bitmap(0, 0, title);
if (pollFireButton(25)) {
return true;
}
//Flash "Press FIRE" 5 times
for(byte i=0;i<5;i++){
//Draws "Press FIRE"
TV.bitmap(31, 53, fire);
if (pollFireButton(50)) {
return true;
}
//Removes "Press FIRE"
TV.draw_rect(31,53,57,8,0,0);
if (pollFireButton(25)) {
return true;
}
}
return false;
}
//Function by nootropic design to add high scores
void enterInitials() {
char index = 0;
TV.fill(0);
TV.print(16, 0, "HIGH SCORE");
sprintf(text, "%u", score);
TV.print(88, 0, text);
initials[0] = ' ';
initials[1] = ' ';
initials[2] = ' ';
while (true) {
TV.print_char(56, 20, initials[0]);
TV.print_char(64, 20, initials[1]);
TV.print_char(72, 20, initials[2]);
for(byte i=0;i<3;i++) {
TV.draw_line(56 + (i*8), 27, 56 + (i*8) + 6, 27, 1);
}
TV.draw_line(56, 28, 88, 28, 0);
TV.draw_line(56 + (index*8), 28, 56 + (index*8) + 6, 28, 1);
TV.delay(150);
if (Controller.leftPressed()) {
index--;
if (index < 0) {
index = 0;
} else {
TV.tone(1046, 20);
}
}
if (Controller.rightPressed()) {
index++;
if (index > 2) {
index = 2;
} else {
TV.tone(1046, 20);
}
}
if (Controller.upPressed()) {
initials[index]++;
TV.tone(523, 20);
// A-Z 0-9 :-? !-/ ' '
if (initials[index] == '0') {
initials[index] = ' ';
}
if (initials[index] == '!') {
initials[index] = 'A';
}
if (initials[index] == '[') {
initials[index] = '0';
}
if (initials[index] == '@') {
initials[index] = '!';
}
}
if (Controller.downPressed()) {
initials[index]--;
TV.tone(523, 20);
if (initials[index] == ' ') {
initials[index] = '?';
}
if (initials[index] == '/') {
initials[index] = 'Z';
}
if (initials[index] == 31) {
initials[index] = '/';
}
if (initials[index] == '@') {
initials[index] = ' ';
}
}
if (Controller.firePressed()) {
if (index < 2) {
index++;
TV.tone(1046, 20);
} else {
TV.tone(1046, 20);
return;
}
}
}
}
void enterHighScore(byte file) {
// Each block of EEPROM has 10 high scores, and each high score entry
// is 5 bytes long: 3 bytes for initials and two bytes for score.
int address = file*10*5;
byte hi, lo;
char tmpInitials[3];
unsigned int tmpScore = 0;
// High score processing
for(byte i=0;i<10;i++) {
hi = EEPROM.read(address + (5*i));
lo = EEPROM.read(address + (5*i) + 1);
if ((hi == 0xFF) && (lo == 0xFF)) {
// The values are uninitialized, so treat this entry
// as a score of 0.
tmpScore = 0;
} else {
tmpScore = (hi << 8) | lo;
}
if (score > tmpScore) {
enterInitials();
for(byte j=i;j<10;j++) {
hi = EEPROM.read(address + (5*j));
lo = EEPROM.read(address + (5*j) + 1);
if ((hi == 0xFF) && (lo == 0xFF)) {
tmpScore = 0;
} else {
tmpScore = (hi << 8) | lo;
}
tmpInitials[0] = (char)EEPROM.read(address + (5*j) + 2);
tmpInitials[1] = (char)EEPROM.read(address + (5*j) + 3);
tmpInitials[2] = (char)EEPROM.read(address + (5*j) + 4);
// tmpScore and tmpInitials now hold what we want to write in the next slot.
// write score and initials to current slot
EEPROM.write(address + (5*j), ((score >> 8) & 0xFF));
EEPROM.write(address + (5*j) + 1, (score & 0xFF));
EEPROM.write(address + (5*j) + 2, initials[0]);
EEPROM.write(address + (5*j) + 3, initials[1]);
EEPROM.write(address + (5*j) + 4, initials[2]);
score = tmpScore;
initials[0] = tmpInitials[0];
initials[1] = tmpInitials[1];
initials[2] = tmpInitials[2];
}
score = 0;
initials[0] = ' ';
initials[1] = ' ';
initials[2] = ' ';
return;
}
}
}
void breakout()
{
//Title screen loop switches from title screen and high scores utill FIRE is pressed
while (!start) {
start = titleScreen();
if (!start) {
start = displayHighScores(2);
}
}
//Inital level draw
if (!initialDraw){
//Clears the screen
TV.fill(0);
//Selects Font
TV.select_font(font4x6);
//Draws the new level
newLevel();
initialDraw=true;
}
if (lives>0){
drawPaddle();
//Pause game if FIRE pressed
pad = Controller.firePressed();
if(pad ==1 && oldpad==0 && released){
oldpad2=1; //Forces pad loop 2 to run once
pause();
}
oldpad=pad;
drawBall();
if(brickCount==60){
level++;
newLevel();
}
}
else{
drawGameOver();
if (score > 0) {
TV.select_font(font6x8);
enterHighScore(2);
}
TV.select_font(font4x6);
drawGameOver();
initialDraw=false;
start=false;
lives=3;
score=0;
newLevel();
}
TV.delay_frame(1);
}moeMemberSooooo yes I finished my game fusion! I followed what Michael said and yes it works.. I moved the code out of loop and integrated the function and the menu. And yes the Breakout-Game works, the Worms also, but the problem is the frame doesn’t show up correctly. Maybe you can try this code on your on Hackvision to see it (or I can upload a pic). But at least it’s playable and my “creation” works. I also tried the other “project” and now I understand the “syntax” of Arduino (at least a little bit). I want to thank you Michael and Trodoss again you guys did great work and yeah thanks helping me. Oh and yes and Michael if you plan to make a Hackvision v2: If it’s possible, could you please add a on/off switch? But thats just an idea I wanted to share..
Code in next post…
moeMemberWow this makes totally sense to me. Yes you are completely right. Ok I’ll try to fix that! Thanks for the good explanation and yeah this is all really new to me… So can you say that loop is kinda something like a “main” function ? And yes I wasn’t sure with these == and = because this is different in some other p. Languages… And the
game = &breakout;
is calling the function breakout ? What’s the difference between void and byte ? Thank you!
moeMemberSorry, more than 20000 chars…
Removed #include too much chars
#define TV_W 136
#define TV_H 98
#define FRAME_X0 8
#define FRAME_X1 128
#define FRAME_Y0 9
#define FRAME_Y1 89
#define LEFT 3
#define RIGHT 2
#define UP 4
#define DOWN 5
#define FIRE 10
const byte width = 120; //Width of screen
const byte height = 96; //Hight of screen
int dx = -1; //Initial movement of ball
int dy = -1; //Initial movement of ball
int xb; //Balls starting possition
int yb; //Balls starting possition
boolean released; //If the ball has been released by the player
boolean paused = false; //If the game has been paused
byte xPaddle; //X position of paddle
boolean isHit[5][12]; //Array of if bricks are hit or not
boolean bounced=false; //Used to fix double bounce glitch
byte lives = 3; //Amount of lives
byte level = 1; //Current level
unsigned int score=0; //Score for the game
unsigned int brickCount; //Amount of bricks hit
byte pad,pad2,pad3; //Button press buffer used to stop pause repeating
byte oldpad,oldpad2,oldpad3;
char text[16]; //General string buffer
boolean start=false; //If in menu or in game
boolean initialDraw=false;//If the inital draw has happened
char initials[3]; //Initials used in high score
//Ball Bounds used in collision detection
byte leftBall;
byte rightBall;
byte topBall;
byte bottomBall;
//Brick Bounds used in collision detection
byte leftBrick;
byte rightBrick;
byte topBrick;
byte bottomBrick;
struct Segment
{
boolean active;
byte x;
byte y;
};
prog_char s0[] PROGMEM = "ARDUINO WORM";
prog_char s1[] PROGMEM = "GAME";
prog_char s2[] PROGMEM = "OVER";
prog_char s3[] PROGMEM = "SCORE: ";
prog_char s4[] PROGMEM = "BREAKOUT";
PROGMEM const char *strings[] = {
s0,s1,s2,s3,s4};
char s[16]; // general string buffer
char scoreString[11];
byte currentTonePriority = 0;
byte ballX;
byte ballY;
byte movingDirection;
boolean ballCollected = false;
int currentScore;
int choice = true;
Segment worm[50];
void (*game)();
TVout tv;
// Allow the overall speed of the game to be adjusted.
// Higher number (like 1.5) slow the game down. Lower numbers (like 0.6) speed it up.
float speedAdjust = 1.0;
void setup() {
// If pin 12 is pulled LOW, then the PAL jumper is shorted.
pinMode(12, INPUT);
digitalWrite(12, HIGH);
if (digitalRead(12) == LOW) {
tv.begin(_PAL, TV_W, TV_H);
// Since PAL processing is faster, we need to slow the game play down.
speedAdjust = 1.4;
}
else {
tv.begin(_NTSC, TV_W, TV_H);
}
tv.select_font(font6x8);
randomSeed(analogRead(0));
playTone(1046, 20);
tv.delay(1);
playTone(1318, 20);
tv.delay(1);
playTone(1568, 20);
tv.delay(1);
playTone(2093, 20);
byte m[2] = {0,4};
byte choice = menu(2, m);
if (choice == 0) {
game = &worms;
tv.delay(10);
initWorms();
}
if (choice == 1) {
choice = false;
tv.delay(10);
initBreakout();
}
}
byte menu(byte nChoices, byte *choices) {
char choice = 0;
tv.fill(0);
byte x = 24;
byte y;
while (true) {
for(byte i=0;istrcpy_P(s, (char *)pgm_read_word(&(strings[choices])));
tv.print(32, 30+(i*8), s);
}
for(byte i=0;iy = 30+(i*8);
if (i == choice) {
// draw arrow next to selected game
tv.set_pixel(x+4, y, 1);
tv.set_pixel(x+5, y+1, 1);
tv.draw_line(x, y+2, x+6, y+2, 1);
tv.set_pixel(x+5, y+3, 1);
tv.set_pixel(x+4, y+4, 1);
}
else {
for(byte j=0;j<8;j++) {
tv.draw_line(x, y+j, x+7, y+j, 0);
}
}
}
// get input
if (pollFireButton(10)) {
playTone(1046, 20);
return choice;
}
if (Controller.upPressed()) {
choice--;
if (choice == -1) {
choice = 0;
}
else {
playTone(1046, 20);
}
}
if (Controller.downPressed()) {
choice++;
if (choice == nChoices) {
choice = nChoices-1;
}
else {
playTone(1046, 20);
}
}
}
}
void initWorms() {
for(int x = 0; x < 5; x++) {
worm[x].active = true;
worm[x].x = 30-x;
worm[x].y = 30;
}
for(int x = 5; x < 50; x++) {
worm[x].active = false;
worm[x].x = 0;
worm[x].y = 0;
}
movingDirection = RIGHT;
ballCollected = true;
currentScore = 0;
updateScore();
}
void initBreakout() {
loop();
}
void loop() {
if (choice = true){
game();
} else {
//Title screen loop switches from title screen and high scores utill FIRE is pressed
while (!start) {
start = titleScreen();
if (!start) {
start = displayHighScores(2);
}
}
//Inital level draw
if (!initialDraw){
//Clears the screen
tv.fill(0);
//Selects Font
tv.select_font(font4x6);
//Draws the new level
newLevel();
initialDraw=true;
}
if (lives>0){
drawPaddle();
//Pause game if FIRE pressed
pad = Controller.firePressed();
if(pad ==1 && oldpad==0 && released){
oldpad2=1; //Forces pad loop 2 to run once
pause();
}
oldpad=pad;
drawBall1();
if(brickCount==60){
level++;
newLevel();
}
}
else{
drawGameOver();
if (score > 0) {
tv.select_font(font6x8);
enterHighScore(2);
}
tv.select_font(font4x6);
drawGameOver();
initialDraw=false;
start=false;
lives=3;
score=0;
newLevel();
}
tv.delay_frame(1);
}
}
void worms() {
tv.fill(0);
drawFrame();
drawScore();
drawBall();
for(int x = 0; x < 50; x++) {
if(worm[x].active)
tv.set_pixel(worm[x].x,worm[x].y,1);
}
move();
tv.delay_frame(4);
}
void drawBall() {
if(ballCollected) {
ballX = random(FRAME_X0+1, FRAME_X1-1);
ballY = random(FRAME_Y0+1, FRAME_Y1-1);
ballCollected = false;
}
tv.set_pixel(ballX,ballY,1);
}
void drawFrame() {
tv.draw_line(FRAME_X0, FRAME_Y0, FRAME_X1, FRAME_Y0, 1);
tv.draw_line(FRAME_X0, FRAME_Y1, FRAME_X1, FRAME_Y1, 1);
tv.draw_line(FRAME_X0, FRAME_Y0, FRAME_X0, FRAME_Y1, 1);
tv.draw_line(FRAME_X1, FRAME_Y0, FRAME_X1, FRAME_Y1, 1);
}
void move() {
if (Controller.upPressed()) {
if(movingDirection != DOWN) {
movingDirection = UP;
}
}
else if(Controller.downPressed()) {
if(movingDirection != UP) {
movingDirection = DOWN;
}
}
else if(Controller.leftPressed()) {
if(movingDirection != RIGHT) {
movingDirection = LEFT;
}
}
else if(Controller.rightPressed()) {
if(movingDirection != LEFT) {
movingDirection = RIGHT;
}
}
switch (movingDirection) {
case UP:
if(detectBallHit(worm[0].x, worm[0].y-1)) {
assignBallCoordinatesToFirstSegment();
}
else {
shiftSegments();
worm[0].y = worm[0].y-1 ;
}
break;
case DOWN:
if(detectBallHit(worm[0].x, worm[0].y+1)){
assignBallCoordinatesToFirstSegment();
}
else {
shiftSegments();
worm[0].y = worm[0].y+1 ;
}
break;
case LEFT:
if(detectBallHit(worm[0].x-1, worm[0].y)){
assignBallCoordinatesToFirstSegment();
}
else {
shiftSegments();
worm[0].x = worm[0].x-1 ;
}
break;
case RIGHT:
if(detectBallHit(worm[0].x+1, worm[0].y)){
assignBallCoordinatesToFirstSegment();
}
else {
shiftSegments();
worm[0].x = worm[0].x+1 ;
}
break;
}
if(worm[0].x > FRAME_X1-1 || worm[0].x < FRAME_X0+1 || worm[0].y > FRAME_Y1-1 || worm[0].y < FRAME_Y0+1 || checkForTailBite()) {
gameOver();
initWorms();
return;
}
}
boolean checkForTailBite() {
for(int x = 1; x < 50; x++) {
if(worm[0].x == worm[x].x && worm[0].y == worm[x].y) {
return true;
}
}
return false;
}
void shiftSegments() {
for(int x = 49; x > 0; x--) {
if(worm[x].active) {
worm[x].x = worm[x-1].x;
worm[x].y = worm[x-1].y;
}
}
}
void assignBallCoordinatesToFirstSegment() {
worm[0].x = ballX;
worm[0].y = ballY;
}
boolean detectBallHit(byte sx, byte sy) {
if(sx == ballX && sy == ballY) {
for(int x = 49; x > -1; x--) {
if(worm[x].active) {
worm[x+1].active = true;
worm[x+1].x = worm[x].x;
worm[x+1].y = worm[x].y;
}
}
ballCollected = true;
currentScore += 1;
updateScore();
playTone(1046, 10);
tv.delay(1);
playTone(1318, 10);
tv.delay(1);
playTone(1568, 10);
tv.delay(1);
playTone(2093, 10);
return true;
}
else {
return false;
}
}
void drawScore() {
tv.print(FRAME_X0, 0, scoreString);
}
void updateScore() {
strcpy_P(scoreString, (char *)pgm_read_word(&(strings[3])));
sprintf(s, "%d", currentScore);
strcat(scoreString, s);
}
void gameOver() {
tv.delay(1000);
tv.fill(0);
strcpy_P(s, (char *)pgm_read_word(&(strings[1])));
tv.print(44, 40, s);
strcpy_P(s, (char *)pgm_read_word(&(strings[2])));
tv.print(72, 40, s);
tv.delay(3000);
}
boolean pollFireButton(int n) {
for(int i=0;itv.delay_frame(1);
if (Controller.firePressed()) {
return true;
}
}
return false;
}
void playTone(unsigned int frequency, unsigned long duration_ms) {
// Default is to play tone with highest priority.
playTone(frequency, duration_ms, 9);
}
void playTone(unsigned int frequency, unsigned long duration_ms, byte priority) {
// priority is value 0-9, 9 being highest priority
if (TCCR2B > 0) {
// If a tone is currently playing, check priority
if (priority < currentTonePriority) {
return;
}
}
currentTonePriority = priority;
tv.tone(frequency, duration_ms);
}
void movePaddle()
{
//Move right
if(xPaddle < width-12)
{
if(Controller.rightPressed())
{
xPaddle++;
}
}
//Move left
if(xPaddle > 0)
{
if(Controller.leftPressed())
{
xPaddle--;
}
}
}
void moveBall()
{
if(released)
{
//Move ball
xb=xb + dx;
yb=yb + dy;
//Set bounds
leftBall = xb;
rightBall = xb+2;
topBall = yb;
bottomBall = yb+2;
//Bounce off top edge
if (yb<=0)
{
yb=2;
dy=-dy;
tv.tone(523, 20);
}
//Lose a life if bottom edge hit
if (yb>=89)
{
tv.draw_rect(xPaddle,87,11,1,0);
xPaddle = 54;
yb=84;
released = false;
lives--;
drawLives();
tv.tone(175, 20);
if (random(0, 2) == 0) {
dx = 1;
}
else {
dx = -1;
}
}
//Bounce off left side
if (xb<=0)
{
xb=2;
dx=-dx;
tv.tone(523, 20);
}
//Bounce off right side
if (xb>=width-2)
{
xb=width-4;
dx=-dx;
tv.tone(523, 20);
}
//Bounce off paddle
if (xb+1>=xPaddle && xb<=xPaddle+12 && yb+2>=87 && yb<=92){
dy=-dy;
dx=((xb-(xPaddle+6))/3); //Applies spin on the ball
tv.tone(200, 20);
}
//Bounce off Bricks
for (byte row = 0; row < 5; row++) {
for (byte column = 0; column < 12; column++) {
if (!isHit[row][column]){
//Sets Brick bounds
leftBrick = 10*column;
rightBrick = 10*column+10;
topBrick = 6*row+1;
bottomBrick = 6*row+7;
//If A collison has occured
if (topBall <= bottomBrick && bottomBall>=topBrick && leftBall<=rightBrick && rightBall >=leftBrick){
Score();
brickCount++;
isHit[row][column] = true;
tv.draw_rect(10*column ,2+6*row,8,4,0);
//Vertical collision
if (bottomBall>bottomBrick || topBall//Only bounce once each ball move
if(!bounced){
dy=-dy;
yb+=dy;
bounced=true;
tv.tone(261, 20);
}
}
//Hoizontal collision
if (leftBallrightBrick){
//Only bounce once brick each ball move
if(!bounced){
dx=-dx;
xb+=dx;
bounced=true;
tv.tone(261, 20);
}
}
}
}
}
}
//Reset Bounce
bounced=false;
}
else{
//Ball follows paddle
xb=xPaddle+5;
//Release ball if FIRE pressed
pad3 = Controller.firePressed();
if(pad3 ==1 && oldpad3==0){
released=true;
//Apply random direction to ball on release
if (random(0, 2) == 0) {
dx = 1;
}
else {
dx = -1;
}
//Makes sure the ball heads upwards
dy = -1;
}
oldpad3=pad3;
}
}
void drawBall1()
{
tv.set_pixel(xb,yb,0);
tv.set_pixel(xb+1,yb,0);
tv.set_pixel(xb,yb+1,0);
tv.set_pixel(xb+1,yb+1,0);
moveBall();
tv.set_pixel(xb,yb,1);
tv.set_pixel(xb+1,yb,1);
tv.set_pixel(xb,yb+1,1);
tv.set_pixel(xb+1,yb+1,1);
}
void drawPaddle()
{
tv.draw_rect(xPaddle,87,11,1,0);
movePaddle();
tv.draw_rect(xPaddle,87,11,1,1);
}
void drawLives()
{
sprintf(text, "LIVES:%u", lives);
tv.print(0, 90, text);
}
void drawGameOver()
{
tv.set_pixel(xb,yb,0);
tv.set_pixel(xb+1,yb,0);
tv.set_pixel(xb,yb+1,0);
tv.set_pixel(xb+1,yb+1,0);
tv.print(52, 42, "Game");
tv.print(52, 54, "Over");
delay(4000);
}
void pause()
{
paused = true;
//Draw pause to the screen
tv.print(52, 45, "PAUSE");
while(paused){
delay(15);
//Unpause if FIRE is pressed
pad2 = Controller.firePressed();
if(pad2 ==1 && oldpad2==0 && released){
tv.print(52, 45, " ");
paused=false;
}
oldpad2=pad2;
}
}
void Score()
{
score += (level*10);
sprintf(text, "SCORE:%u", score);
tv.print(80, 90, text);
}
void newLevel(){
//Undraw paddle
tv.draw_rect(xPaddle,87,11,1,0);
//Undraw ball
tv.set_pixel(xb,yb,0);
tv.set_pixel(xb+1,yb,0);
tv.set_pixel(xb,yb+1,0);
tv.set_pixel(xb+1,yb+1,0);
//Alter various variables to reset the game
xPaddle = 54;
yb=84;
brickCount=0;
released=false;
//Draws new bricks and resets their values
for (byte row = 0; row < 5; row++) {
for (byte column = 0; column < 12; column++) {
isHit[row][column] = false;
tv.draw_rect(10*column ,2+6*row,8,4,1);
}
}
//Draws the initial lives
drawLives();
//Draws the initial score
sprintf(text, "SCORE:%u", score);
tv.print(80, 90, text);
}
//Function by nootropic design to display highscores
boolean displayHighScores(byte file) {
byte y = 10;
byte x = 24;
// Each block of EEPROM has 10 high scores, and each high score entry
// is 5 bytes long: 3 bytes for initials and two bytes for score.
int address = file*10*5;
byte hi, lo;
tv.fill(0);
tv.select_font(font6x8);
tv.print(32, 0, "HIGH SCORES");
for(int i=0;i<10;i++) {
sprintf(text, "%2d", i+1);
tv.print(x, y+(i*8), text);
hi = EEPROM.read(address + (5*i));
lo = EEPROM.read(address + (5*i) + 1);
if ((hi == 0xFF) && (lo == 0xFF)) {
score = 0;
}
else {
score = (hi << 8) | lo;
}
initials[0] = (char)EEPROM.read(address + (5*i) + 2);
initials[1] = (char)EEPROM.read(address + (5*i) + 3);
initials[2] = (char)EEPROM.read(address + (5*i) + 4);
if (score > 0) {
sprintf(text, "%c%c%c %u", initials[0], initials[1], initials[2], score);
tv.print(x+24, y+(i*8), text);
}
}
if (pollFireButton(300)) {
return true;
}
return false;
}
boolean titleScreen() {
//Clears the screen
tv.fill(0);
//Draws the main title
tv.bitmap(0, 0, title);
if (pollFireButton(25)) {
return true;
}
//Flash "Press FIRE" 5 times
for(byte i=0;i<5;i++){
//Draws "Press FIRE"
tv.bitmap(31, 53, fire);
if (pollFireButton(50)) {
return true;
}
//Removes "Press FIRE"
tv.draw_rect(31,53,57,8,0,0);
if (pollFireButton(25)) {
return true;
}
}
return false;
}
//Function by nootropic design to add high scores
void enterInitials() {
char index = 0;
tv.fill(0);
tv.print(16, 0, "HIGH SCORE");
sprintf(text, "%u", score);
tv.print(88, 0, text);
initials[0] = ' ';
initials[1] = ' ';
initials[2] = ' ';
while (true) {
tv.print_char(56, 20, initials[0]);
tv.print_char(64, 20, initials[1]);
tv.print_char(72, 20, initials[2]);
for(byte i=0;i<3;i++) {
tv.draw_line(56 + (i*8), 27, 56 + (i*8) + 6, 27, 1);
}
tv.draw_line(56, 28, 88, 28, 0);
tv.draw_line(56 + (index*8), 28, 56 + (index*8) + 6, 28, 1);
tv.delay(150);
if (Controller.leftPressed()) {
index--;
if (index < 0) {
index = 0;
} else {
tv.tone(1046, 20);
}
}
if (Controller.rightPressed()) {
index++;
if (index > 2) {
index = 2;
} else {
tv.tone(1046, 20);
}
}
if (Controller.upPressed()) {
initials[index]++;
tv.tone(523, 20);
// A-Z 0-9 :-? !-/ ' '
if (initials[index] == '0') {
initials[index] = ' ';
}
if (initials[index] == '!') {
initials[index] = 'A';
}
if (initials[index] == '[') {
initials[index] = '0';
}
if (initials[index] == '@') {
initials[index] = '!';
}
}
if (Controller.downPressed()) {
initials[index]--;
tv.tone(523, 20);
if (initials[index] == ' ') {
initials[index] = '?';
}
if (initials[index] == '/') {
initials[index] = 'Z';
}
if (initials[index] == 31) {
initials[index] = '/';
}
if (initials[index] == '@') {
initials[index] = ' ';
}
}
if (Controller.firePressed()) {
if (index < 2) {
index++;
tv.tone(1046, 20);
} else {
tv.tone(1046, 20);
return;
}
}
}
}
void enterHighScore(byte file) {
// Each block of EEPROM has 10 high scores, and each high score entry
// is 5 bytes long: 3 bytes for initials and two bytes for score.
int address = file*10*5;
byte hi, lo;
char tmpInitials[3];
unsigned int tmpScore = 0;
// High score processing
for(byte i=0;i<10;i++) {
hi = EEPROM.read(address + (5*i));
lo = EEPROM.read(address + (5*i) + 1);
if ((hi == 0xFF) && (lo == 0xFF)) {
// The values are uninitialized, so treat this entry
// as a score of 0.
tmpScore = 0;
} else {
tmpScore = (hi << 8) | lo;
}
if (score > tmpScore) {
enterInitials();
for(byte j=i;j<10;j++) {
hi = EEPROM.read(address + (5*j));
lo = EEPROM.read(address + (5*j) + 1);
if ((hi == 0xFF) && (lo == 0xFF)) {
tmpScore = 0;
} else {
tmpScore = (hi << 8) | lo;
}
tmpInitials[0] = (char)EEPROM.read(address + (5*j) + 2);
tmpInitials[1] = (char)EEPROM.read(address + (5*j) + 3);
tmpInitials[2] = (char)EEPROM.read(address + (5*j) + 4);
// tmpScore and tmpInitials now hold what we want to write in the next slot.
// write score and initials to current slot
EEPROM.write(address + (5*j), ((score >> 8) & 0xFF));
EEPROM.write(address + (5*j) + 1, (score & 0xFF));
EEPROM.write(address + (5*j) + 2, initials[0]);
EEPROM.write(address + (5*j) + 3, initials[1]);
EEPROM.write(address + (5*j) + 4, initials[2]);
score = tmpScore;
initials[0] = tmpInitials[0];
initials[1] = tmpInitials[1];
initials[2] = tmpInitials[2];
}
score = 0;
initials[0] = ' ';
initials[1] = ' ';
initials[2] = ' ';
return;
}
}
}moeMemberFirst of all thanks for the quick replies!
Thanks for the example File, this is nearly exactly what I looked for but could you please add this “choice-menu” like the hackvision firmware? Thanks.
This is my first problem (strange menu):
#include
#include
#include
#include
#include
#define TV_W 136
#define TV_H 98
#define FRAME_X0 8
#define FRAME_X1 128
#define FRAME_Y0 9
#define FRAME_Y1 89
#define LEFT 3
#define RIGHT 2
#define UP 4
#define DOWN 5
#define FIRE 10
struct Segment
{
boolean active;
byte x;
byte y;
};
prog_char s0[] PROGMEM = "DRAW";
PROGMEM const char *strings[] = {s0};
char s[16]; // general string buffer
char scoreString[11];
byte currentTonePriority = 0;
void (*game)();
TVout tv;
int count = 3;
void setup() {
// If pin 12 is pulled LOW, then the PAL jumper is shorted.
pinMode(12, INPUT);
digitalWrite(12, HIGH);
if (digitalRead(12) == LOW) {
tv.begin(_PAL, TV_W, TV_H);
}
else {
tv.begin(_NTSC, TV_W, TV_H);
}
tv.select_font(font6x8);
randomSeed(analogRead(0));
byte m[1] = {0};
byte choice = menu(1, m);
if (choice == 0) {
tv.delay(10);
draw();
}
}
byte menu(byte nChoices, byte *choices) {
char choice = 0;
tv.fill(0);
byte x = 24;
byte y;
while (true) {
for(byte i=0;istrcpy_P(s, (char *)pgm_read_word(&(strings[choices])));
tv.print(32, 30+(i*8), s);
}
for(byte i=0;iy = 30+(i*8);
if (i == choice) {
// draw arrow next to selected game
tv.set_pixel(x+4, y, 1);
tv.set_pixel(x+5, y+1, 1);
tv.draw_line(x, y+2, x+6, y+2, 1);
tv.set_pixel(x+5, y+3, 1);
tv.set_pixel(x+4, y+4, 1);
}
else {
for(byte j=0;j<8;j++) {
tv.draw_line(x, y+j, x+7, y+j, 0);
}
}
}
// get input
if (pollFireButton(10)) {
playTone(1046, 20);
return choice;
}
if (Controller.upPressed()) {
choice--;
if (choice == -1) {
choice = 0;
}
else {
playTone(1046, 20);
}
}
if (Controller.downPressed()) {
choice++;
if (choice == nChoices) {
choice = nChoices-1;
}
else {
playTone(1046, 20);
}
}
}
}
void draw() {
while (Controller.firePressed()) {
tv.clear_screen();
tv.draw_line(0,0,99,99,1);
tv.delay(3600);
tv.clear_screen();
tv.delay(200);
tv.print(10,10,"TEST");
tv.delay(3600);
tv.draw_circle(tv.hres()/2,tv.vres()/2,tv.vres()/3,WHITE);
tv.delay(3600);
tv.clear_screen();
tv.print(count);
count = count-1;
}
setup();
}
boolean pollFireButton(int n) {
for(int i=0;itv.delay_frame(1);
if (Controller.firePressed()) {
return true;
}
}
return false;
}
void playTone(unsigned int frequency, unsigned long duration_ms) {
// Default is to play tone with highest priority.
playTone(frequency, duration_ms, 9);
}
void playTone(unsigned int frequency, unsigned long duration_ms, byte priority) {
// priority is value 0-9, 9 being highest priority
if (TCCR2B > 0) {
// If a tone is currently playing, check priority
if (priority < currentTonePriority) {
return;
}
}
currentTonePriority = priority;
tv.tone(frequency, duration_ms);
}
void loop() {}
Please don’t think what a noob and such things… I know programming languages and I understand much but C or C++ are a bit difficult to learn and really voluminous… So you don’t have to explain it like you would to a total newbie.
And here comes my second (I tried to combine Worms & Breakout) -> Copy&Paste doesn’t work great… I tried it on my hackvision but it only gives an annoying beep all the time… No menu, no games…. So if anyone could help me fixing this? I know maybe the sketch size is too big but if you delete all the same parts and put these two together they should probably work… Am I right? I would really like this to have on my hackvision… It should look like the original firmware of HV. Thanks and here’s the code:
-
AuthorPosts