All projects
Year 2 · Academic Year 2024

Year 2 Projects

DMT221 Graphic Programming for Digital Media / DMT320 Embedded Systems for Media

Academic Year 2024

Processing Game & Arduino Robot Car

DMT221 Graphic Programming for Digital Media / DMT320 Embedded Systems for Media

  • Built Brick Breaker, a three-level arcade game in Processing using object-oriented classes, collision detection and power-up items.
  • Built a line-following robot car on an ESP8266 NodeMCU that reads IR and ultrasonic sensors and uses a servo arm to clear obstacles from its path.
Processing Java OOP Arduino ESP8266 Sensors

Course Projects

Watch each project video and read how it works.

DMT221

Graphic Programming for Digital Media

Processing Java OOP Game Design
Processing Game · Group project (3 members)

Brick Breaker

An arcade game built in Processing. The player moves a paddle at the bottom of the screen to bounce a ball into rows of bricks, and must clear every brick in each level without letting the ball fall. Each level gets harder with a faster ball and stronger bricks, broken bricks can drop power-up items, and collisions between the ball, paddle, bricks and screen edges are calculated with basic physics.

View source code (Final_Brick_Breaker.pde)
Paddle paddle;
ArrayList<Ball> balls;
ArrayList<Brick> bricks;
ArrayList<Item> items = new ArrayList<Item>();
int score = 0;
int itemTimer = 0;
int expandEndTime = 0;
int fireballEndTime = 0;
int slowEndTime = 0;
boolean gameOver = false;
boolean gameWon = false;
int currentLevel = 0;
int totalLevels = 3;
boolean gameStarted = false;
boolean fireBallMode = false;
boolean isSlowed = false;

void setup() {
  size(800, 600);
  textAlign(CENTER, CENTER);
  textFont(createFont("Arial", 24));
  resetGame();
}

void resetGame() {
  paddle = new Paddle();
  balls = new ArrayList<Ball>();
  balls.add(new Ball(currentLevel));
  items.clear();  // เคลียร์ไอเท็มเดิม
  loadLevel(currentLevel);
  gameStarted = false;
}

void draw() {
  background(30, 30, 50);

  if (!gameOver && !gameWon) {
    paddle.update();
    paddle.display();
    
        // ตรวจสอบเวลาไอเท็มหมด
    if (expandEndTime > 0 && millis() > expandEndTime) {
      paddle.w /= 1.5;
      expandEndTime = 0;
    }
    if (fireballEndTime > 0 && millis() > fireballEndTime) {
      fireBallMode = false;
      fireballEndTime = 0;
    }
    if (slowEndTime > 0 && millis() > slowEndTime) {
      for (Ball b : balls) {
        b.speedX /= 0.7;
        b.speedY /= 0.7;
      }
      slowEndTime = 0;
      isSlowed = false;
    }

    
    for (Brick b : bricks) {
      b.display();
    }

    if (!gameStarted) {
      balls.get(0).display();
      fill(255);
      text("Press SPACE to Start", width / 2, height / 2);
      return;
    }
  for (int i = bricks.size() - 1; i >= 0; i--) {
    if (bricks.get(i).isBroken()) {
      bricks.remove(i);
    }
  }
    for (int i = balls.size() - 1; i >= 0; i--) {
    Ball b = balls.get(i);
    b.update();
    b.checkCollision(paddle, bricks);
    b.display();

    if (b.y > height) {
      balls.remove(i);
    }
  }
  for (int i = items.size() - 1; i >= 0; i--) {
  Item item = items.get(i);
  item.update();
  item.display();

  if (item.isCaught(paddle)) {
    item.collected = true;
    activateItem(item.type);
    items.remove(i);
  } else if (item.y > height) {
    items.remove(i); // หล่นทะลุพื้น
  }
}

  if (balls.isEmpty()) {
    gameOver = true;
  }
    fill(255);
    text("Score: " + score, width / 2, 20);
    text("Level: " + (currentLevel + 1), width - 100, 20);
    if (bricks.size() == 0) {
    currentLevel++;
    if (currentLevel < totalLevels) {
      delay(500);
      resetGame();
    } else {
      gameWon = true;
    }
  }

  } else if (gameOver) {
    fill(255, 0, 0);
    text("Game Over", width / 2, height / 2 - 20);
    text("Final Score: " + score, width / 2, height / 2);
    text("Click to Restart", width / 2, height / 2 + 30);
  } else if (gameWon) {
    fill(0, 255, 0);
    text("YOU WIN THE GAME!", width / 2, height / 2 - 20);
    text("Total Score: " + score, width / 2, height / 2);
    text("Click to Play Again", width / 2, height / 2 + 30);
  }
 }

void mousePressed() {
  if (gameOver || gameWon) {
    currentLevel = 0;
    score = 0;
    gameOver = false;
    gameWon = false;
    resetGame();
  }
}

void keyPressed() {
  if (!gameStarted && key == ' ') {
    gameStarted = true;
  }
}

// ------------------ Paddle ------------------
class Paddle {
  float x, y, w, h;

  Paddle() {
    w = 120;
    h = 20;
    y = height - 40;
  }

  void update() {
    x = constrain(mouseX - w / 2, 0, width - w);
  }

  void display() {
    fill(255);
    rect(x, y, w, h, 10);
  }
}

// ------------------ Ball ------------------
class Ball {
  float x, y;
  float r = 15;
  float speedX;
  float speedY;

  Ball(int level) {
    x = width / 2;
    y = height / 2;
    float speedBase = 4 + level * 1.5;
    speedX = random(1) < 0.5 ? speedBase : -speedBase;
    speedY = -speedBase;
  }

  void update() {
    x += speedX;
    y += speedY;

    if (x < 0 || x > width) speedX *= -1;
    if (y < 0) speedY *= -1;
  }

  void display() {
    fill(0, 255, 255);
    ellipse(x, y, r * 2, r * 2);
  }

  void checkCollision(Paddle p, ArrayList<Brick> bricks) {
    if (y + r >= p.y && y + r <= p.y + p.h && x >= p.x && x <= p.x + p.w) {
      speedY *= -1;
      y = p.y - r;
    }

    for (Brick b : bricks) {
  if (!b.isBroken() && x > b.x && x < b.x + b.w && y - r < b.y + b.h && y + r > b.y) {
    speedY *= -1;
    b.hit();
    score += 10;  // เพิ่มคะแนนเมื่อโดนอิฐ

    if (b.isBroken()) {
      // สุ่มปล่อยไอเท็ม (โอกาส 30%)
      if (random(1) < 0.3) {
        String[] possibleItems = {"expand", "fireball", "multiball", "slow", "score"};
        String itemType = possibleItems[(int)random(possibleItems.length)];
        items.add(new Item(b.x + b.w/2 - 15, b.y + b.h/2 - 15, itemType));
          }
        }
        break;
      }
    }
  }
}
class Item {
  float x, y, w = 30, h = 30;
  String type;
  boolean collected = false;

  Item(float x, float y, String type) {
    this.x = x;
    this.y = y;
    this.type = type;
  }

  void update() {
    y += 3;
  }

  void display() {
    if (!collected) {
      if (type.equals("expand")) fill(0, 200, 255);
      else if (type.equals("fireball")) fill(255, 100, 0);
      else if (type.equals("multiball")) fill(0, 255, 100);
      else if (type.equals("slow")) fill(255, 255, 0);
      else if (type.equals("score")) fill(255);
      rect(x, y, w, h, 10);
    }
  }

  boolean isCaught(Paddle p) {
    return !collected && y + h > p.y && y < p.y + p.h && x + w > p.x && x < p.x + p.w;
  }
}
// ------------------ Brick ------------------
class Brick {
  float x, y, w, h;
  int hitsLeft;

  Brick(float x, float y, float w, float h, int strength) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    this.hitsLeft = strength;
  }
  void hit() {
    hitsLeft--;
  }

  boolean isBroken() {
    return hitsLeft <= 0;
  }

  void display() {
    if (!isBroken()) {
      if (hitsLeft == 1) {
        fill(255, 100, 100); // อ่อน
      } else {
        fill(100, 100, 255); // แข็ง
      }
      rect(x, y, w, h, 5);
    }
  }
}

// ------------------ Level Loader ------------------
void loadLevel(int levelNum) {
  bricks = new ArrayList<Brick>();
  int cols = 10;
  int rows = 3 + levelNum * 2;  // เพิ่มจำนวนแถวตามเลเวล
  float brickW = width / cols;
  float brickH = 30;

  for (int r = 0; r < rows; r++) {
    for (int c = 0; c < cols; c++) {
      if (random(1) < 0.85) {
        int strength;
        if (levelNum >= 1 && random(1) < 0.25) {
          strength = 2; // บางก้อนแข็งขึ้นในเลเวลสูง
        } else {
          strength = 1;
        }
        bricks.add(new Brick(c * brickW, r * brickH + 50, brickW - 5, brickH - 5, strength));
      }
    }
  }
}

void activateItem(String type) {
  if (type.equals("expand")) {
    paddle.w *= 1.5;
    expandEndTime = millis() + 8000;  // อยู่ได้ 8 วินาที
  } else if (type.equals("fireball")) {
    fireBallMode = true;
    fireballEndTime = millis() + 8000;
  } else if (type.equals("multiball")) {
    ArrayList<Ball> newBalls = new ArrayList<Ball>();
    for (Ball b : balls) {
      Ball extra = new Ball(currentLevel);
      extra.x = b.x;
      extra.y = b.y;
      extra.speedX = -b.speedX;
      extra.speedY = b.speedY;
      newBalls.add(extra);
    }
    balls.addAll(newBalls);
  } else if (type.equals("slow")) {
    if (!isSlowed) {
      for (Ball b : balls) {
        b.speedX *= 0.7;
        b.speedY *= 0.7;
      }
      isSlowed = true;
    }
    slowEndTime = millis() + 8000;
  } else if (type.equals("score")) {
    score += 100;
  }
}
DMT320

Embedded Systems for Media

Arduino ESP8266 NodeMCU IR Sensor Ultrasonic Sensor Servo Motor
Arduino Robot Car · Group project (2 members)

Line-Following Robot Car

A robot car built on an ESP8266 NodeMCU that follows a black line, even in the dark. Two IR sensors read the left and right edges of the track so the car can steer and keep moving forward. An ultrasonic sensor watches the path ahead: when an obstacle comes closer than 5 cm, the car stops, swings its servo arm left and right to sweep the obstacle off the track, and then carries on along the line.