από το scratch στην processing

Από το Scratch…

Τα παιδιά της Ελληνογαλλικής σχολής δημιούργησαν πολύ ωραία παιχνίδια κατά τη διάρκεια της ενασχόλησής τους με το Scratch. Ένα από αυτά είναι οι Κινούμενες μπάρες. Δείτε το εδώ και παίξτε αν θέλετε επίσης! Αν δούμε τον κώδικα του Σεναρίου, για κάθε μία από τις μπάρες χρησιμοποιείται ένας βρόχος τύπου “για πάντα”, και μέσα σε αυτόν δύο εντολές κίνησης που οδηγούν την μπάρα από πάνω προς τα κάτω και το ανάποδο.

  Για τον αγαπητό ήρωα του Σεναρίου, ο κώδικας είναι ελαφρά πιο πολύπλοκος: ακούει στο πάτημα των πλήκτρων {αριστερό, δεξί, πάνω, κάτω} βέλος, ενώ αν αγγίξει τα χρώματα μπλε, μαύρο και κόκκινο τότε επιστρέφει στην αρχική του θέση.Τέλος αν αγγίξει το κίτρινο, που είναι το χρώμα του επάθλου αλλάζει την ενδυμασία του και αναφωνεί χαρούμενος “Νίκησα!!”.

…στην Processing

Για τη μεταφορά του Σεναρίου από το Scratch στην Processing, σε πρώτο στάδιο ακολουθήσαμε διαδικαστική σχεδίαση, ενώ η αντικειμενοστραφής θα ακολουθήσει σύντομα. Αντί για γατούλη και μπανάνες, εδώ έχουμε μια μπάλα και ένα σπίτι αντίστοιχα.

setup()

Μέσα στη setup() υπολογίζουμε κατά βάση τις αρχικές θέσεις για τις τρεις μπάρες και για την μπάλα.

draw()

Μέσα στη draw() σχεδιάζουμε το φόντο, το “σπίτι”, τις 3 μπάρες, καλούμε τη διαδικασία ελέγχου σύγκρουσης και τέλος έχουμε μια εντολή ελέγχου:

αν έχουμε σύγκρουση   στέλνουμε τη μπάλα στην αρχική της θέση, αλλιώς σχεδιάζουμε τη μπάλα στο σημείο που βρίσκεται.

Η draw() δηλαδή, χωρίς όλες τις λεπτομέρειες, δείχνει κάπως έτσι:
draw() {
  background( παράμετρος );
  rect( παράμετροι ); // σπίτι
  rect( παράμετροι ); // μπάρα 1
  rect( παράμετροι ); // μπάρα 2
  rect( παράμετροι ); // μπάρα 3
  check_collision();
  if ( collision ) {
    ellipse( αρχική_θέση );
  } else {
    ellipse( τρέχουσα_θέση );
  }
}
Τη draw() ακολουθούν δύο άλλες δηλώσεις διαδικασιών, η keyPressed() την οποία μας προσφέρει η Processing και η check_collision().

keyPressed()

Η keyPressed() ελέγχει αν έχει πατηθεί το πλήκτρο “αριστερά” ή το πλήκτρο “δεξιά” και ανάλογα αλλάζει τη διεύθυνση της κίνησης της μπάλας. Στην ουσία υπολογίζει τη σχέση:
χ2 = χ1 + δ x Τ, όπου

χ2 είναι η νέα θέση,

χ2 είναι η τρέχουσα θέση,

δ είναι -1 ή 1, υποδεικνύοντας Αριστερά ή Δεξιά, αντίστοιχα, και

Τ είναι η Ταχύτητα, η οποία είναι σταθερή.

check_collision()

Η check_collision() ελέγχει για κάθε μια από τις μπάρες αν βρίσκεται μέσα στην περιοχή που ορίζεται από την μπάλα, και σε αυτή την περίπτωση επιστρέφει τιμή true (αληθής) αλλιώς επιστρέφει τιμή false (ψευδής). Για να γίνει ο έλεγχος της επικάλυψης χρησιμοποιείται η συνάρτηση check_overlap().

check_overlap()

Η συνάρτηση αυτή ορίζει τα σημεία πάνω-αριστερά και κάτω-δεξιά ενός ορθογωνίου και με βάση αυτά ορίζει τα 4 όρια του ορθογωνίου: πάνω, κάτω, αριστερό, δεξί. Για να μην υπάρχει επικάλυψη μεταξύ 2 ορθογωνίων πρέπει να ισχύει η συνθήκη που ελέγχεται με την μοναδική εντολή if/else της συνάρτησης. Ανάλογα με το αποτέλεσμα επιστρέφεται η τιμή αληθής ή ψευδής.

Γρήγορη σύγκριση

Ειδοποιοί διαφορές μεταξύ της υλοποίησης στο Scratch και αυτής στην Processing είναι οι χρήσεις:
  • μεταβλητών για τους υπολογισμούς των θέσεων κυρίως αλλά και για αποφάσεις,
  • διαδικασιών ώστε να είναι πιο δομημένη η προσέγγιση,
  • μαθηματικών τύπων για τον υπολογισμό κίνησης και επικάλυψης, και
  • των μαθηματικών συναρτήσεων random(), abs(), min(), max()
στην περίπτωση της Processing. Όσον αφορά τα γεγονότα του πληκτρολογίου, χρησιμοποιούνται εργαλεία που μας παρέχει η ίδια η Processing κατ’αντιστοιχία εκείνων του Scratch. Στη συγκεκριμένη υλοποίηση προκειμένου να μην αυξηθεί η πολυπλοκότητα των προγραμματιστικών εννοιών δεν έχει χρησιμοποιηθεί μεταβλητή τύπου πίνακα για τις μπάρες.  

show code

//  ///////////////////////////
//  The falling bars:
//  From Scratch to Processing
//  Declarative implementation
//  by agi, 11/06/'14
//  eschoolplus.gr

//  BARS: CONSTANTS
final int XBOUND = 80;          // X boundary
final int WI = 5;               // Bar dimensions: Width
final int HI = 50;              // Bar dimensions: Height
final float YSPEED = 7.6;       // Speed of the bars - uniform

//  BARS: VARIABLES
int xpos1, xpos2, xpos3;        // Starting position of bar x
int ypos1, ypos2, ypos3;        // Starting position of bar y
int ydirection1, ydirection2, ydirection3;
                                // ydirectionX, values: -1, 1
float sign;

//  ELLIPSE: CONSTANTS
final int EWI = 60;               // Ellipse dimensions
final int ESTARTX = 30;           // Start center positions
final int ESTARTY = 30;
final float EXSPEED = 11.4;       // Speed of the ellipse - uniform

//  ELLIPSE: VARIABLES
int e_xpos, e_ypos;
int e_xdir;                       // -1: LEFT or 1: RIGHT

//  OTHER VARIABLES
PFont myFont;


void setup() {

  size(480, 320);
  noStroke();
  frameRate(30);
  rectMode( CENTER );
  ellipseMode( CENTER );
  
  myFont = createFont( "Georgia", 100 );
  textFont( myFont );
  textAlign( CENTER, CENTER );
  
  // Set the starting position of the 3 bars
  // and select a random direction, upwards or downwards
  xpos1 = (int) random( XBOUND, width-XBOUND );
  xpos2 = (int) random( XBOUND, width-XBOUND );
  xpos3 = (int) random( XBOUND, width-XBOUND );

  ypos1 = (int) random( 25, height - HI/2 );
  ypos2 = (int) random( 25, height - HI/2 );
  ypos3 = (int) random( 25, height - HI/2 );

  ydirection1 = ( ( sign = random(-1, 1) ) == abs(sign) ) ? 1 : -1;
  ydirection2 = ( ( sign = random(-1, 1) ) == abs(sign) ) ? 1 : -1;
  ydirection3 = ( ( sign = random(-1, 1) ) == abs(sign) ) ? 1 : -1;

  // Set the starting position of the ball:
  e_xpos = ESTARTX;  
  e_ypos = height - ESTARTY;
    
}

void draw() {
  
  // Redraw background with every iteration
  /////////////////////////////////////////
  background(102);

  // Draw Home: winning position
  //////////////////////////////
  fill( 255 );
  rect( width-15, height-20, 30, 40 );

  // Draw the 3 bars
  //////////////////

  // Update the position of the bar based on speed and direction
  ypos1 = constrain((int) ( ypos1 + YSPEED * ydirection1 ), HI/2, height - ( HI/2 ));
  
  // Test to see if the bar exceeds the boundaries of the screen:
  // If it does, reverse its direction by multiplying by -1
  if (( ypos1 >= ( height - ( HI/2 ))) || ( ypos1 <= ( HI/2 ))) {
    
    ydirection1 *= -1;
    
  }

  // Draw bar1
  fill( 255, 0, 0 );
  rect(xpos1, ypos1, WI, HI);
        

  // Update the position of the bar based on speed and direction
  ypos2 = constrain((int) ( ypos2 + YSPEED * ydirection2 ), HI/2, height - ( HI/2 ));
  
  // Test to see if the bar exceeds the boundaries of the screen:
  // If it does, reverse its direction by multiplying by -1
  if (( ypos2 >= ( height - ( HI/2 ))) || ( ypos2 <= ( HI/2 ))) {
    
    ydirection2 *= -1;
    
  }

  // Draw bar2
  fill( 0, 255, 0 );
  rect(xpos2, ypos2, WI, HI);


  // Update the position of the bar based on speed and direction
  ypos3 = constrain((int) ( ypos3 + YSPEED * ydirection3 ), HI/2, height - ( HI/2 ));
  
  // Test to see if the bar exceeds the boundaries of the screen:
  // If it does, reverse its direction by multiplying by -1
  if (( ypos3 >= ( height - ( HI/2 ))) || ( ypos3 <= ( HI/2 ))) {
    
    ydirection3 *= -1;
    
  }

  // Draw bar3
  fill( 0, 0, 255 );
  rect(xpos3, ypos3, WI, HI);

  
  
  // Check for collisions
  ///////////////////////
  if ( check_collision() ) {
    
    // Collision:
    // Draw the ellipse at the starting position
    e_xpos = ESTARTX;  
    e_ypos = height - ESTARTY;
    fill( 255, 0, 0 );
    ellipse( e_xpos, e_ypos, EWI, EWI );

  } else {

    // No collision:
    // Draw the ellipse at the current position
    fill( 255, 0, 0 );
    ellipse( e_xpos, e_ypos, EWI, EWI );
    
  }

}

void keyPressed() {

  if( key == CODED ) {

    if ( keyCode == RIGHT ) {
      
      // Direction right = 1
      e_xdir = 1;
      if ( e_xpos < ( width-ESTARTX ) ) {
       
        e_xpos = e_xpos + e_xdir*round( EXSPEED );
        fill( 255, 0, 0 );
        ellipse( e_xpos, e_ypos, EWI, EWI );
        
      } else {
        
        // The ellipse is beyond the right boundary
        fill( 255, 191, 127 );
        text( "You win!", width/2, height/2 );
        noLoop();
        
      }
        
    } else if ( keyCode == LEFT ) {
      
      // Direction left = -1
      e_xdir = -1;
      if ( e_xpos > ESTARTX ) {
        
        e_xpos = e_xpos + e_xdir*round( EXSPEED );
        fill( 255, 0, 0 );
        ellipse( e_xpos, e_ypos, EWI, EWI );

      }
      
      // No else case: 
      // when the ball reaches far left
      // then do nothing
      
    } 

  }

}


//  ///////////////////////////
//  check_overlap:
//    arguments:
//      shape A:
//        (x0,y0) (x1,y1)
//      shape B:
//        (x0,y0) (x1,y1)
//    returns true if overlap || false if no overlap

boolean check_overlap(float ax0, float ay0, float ax1, float ay1, float bx0, float by0, float bx1, float by1) {

  float topA = min(ay0, ay1);
  float botA = max(ay0, ay1);
  float leftA = min(ax0, ax1);
  float rightA = max(ax0, ax1);
  float topB = min(by0, by1);
  float botB = max(by0, by1);
  float leftB = min(bx0, bx1);
  float rightB = max(bx0, bx1);

  if ( botA < topB || botB < topA || rightA < leftB || rightB < leftA ) {

    return false;

  } else {

    return true;

  }

}


//  ///////////////////////////
//  check_collision:
//    arguments:
//      none
//    returns true if overlap between ball and bars || 
//            false if no overlap

boolean check_collision() {
  
  float a,b,c,d;

  float e1 = e_xpos - EWI/2; 
  float e2 = e_ypos - EWI/2;
  float e3 = e_xpos + EWI/2;
  float e4 = e_ypos + EWI/2;

  a = xpos1 - WI/2;
  b = ypos1 - WI/2;
  c = xpos1 + WI/2;
  d = ypos1 + WI/2;
  
  if ( check_overlap(e1,e2,e3,e4,a,b,c,d) ) {
    return true;
  }

  a = xpos2 - WI/2;
  b = ypos2 - WI/2;
  c = xpos2 + WI/2;
  d = ypos2 + WI/2;
  
  if ( check_overlap(e1,e2,e3,e4,a,b,c,d) ) {
    return true;
  }

  a = xpos3 - WI/2;
  b = ypos3 - WI/2;
  c = xpos3 + WI/2;
  d = ypos3 + WI/2;
  
  if ( check_overlap(e1,e2,e3,e4,a,b,c,d) ) {
    return true;
  }
  
  return false;
  
}
Αυτό το περιεχόμενο αναρτήθηκε στο eschool+ και ετικέτα , , , , . Bookmark the permalink. Both comments and trackbacks are currently closed.