2013年1月14日 星期一

Week 19, Final Project

系統名稱: 傳奇戰役 (Rise of The Argonauts)


目的: 

  能夠單純使用一支手控制、遊玩一個2D橫軸的遊戲,能夠控制英雄在眾多怪獸中生存,依據殺敵數量不同而得到不同的分數。並能夠在排行榜中爭取高分!

遊戲畫面:

開始畫面(按下即可開始)


進入遊戲畫面,怪物一定間格時間會產生一隻,並隨機產生其中一種及方向


攻擊畫面,成功擊殺怪物分數增加


可以跳躍迴避攻擊


受到攻擊時,左上角的血量會減少


成績排行(白色代表你當次遊戲的成績)


遊戲設備介紹及控制方法:

  控制手套(正面)


控制手套(背面)


以下同時為控制介紹:

正面(同時為遊戲初始狀態,靜止不動)


控制人物左移


控制人物右移


紅色按鈕為攻擊



附註:手向上甩,人物跳躍

如何製作:

  環境:

    作業系統:Windows 7
    硬體主機板:Arduino
    軟體撰寫:Processing(Java) + Arduino(C)

  製作重點:
      遊戲分為main、Hero Class以及Monster Class,主要撰寫的部分
    是Class。在那二個Class中分別記錄大小、生命值、座標、方向、
    向上跳躍的向量以及攻擊力及攻擊間隔,再各別撰寫函式去控制
    。

      而Serial傳遞數值的部分全部放在serialControl()函數裡面,以便
    於撰寫及控制。排行部分則是使用reader and writer去讀取以及寫檔
    。

程式碼:

Arduino:

#include <string.h>

int btn_left = 2;
int btn_right = 3;
int btn_atk = 4;
int past_leftState =1;
int past_rightState = 1;
int past_atkState = 1;
void setup() {
  Serial.begin(9600);
  pinMode(btn_left, INPUT_PULLUP);
  pinMode(btn_right, INPUT_PULLUP);
  pinMode(btn_atk, INPUT_PULLUP);
}
void loop(){
  int leftState = digitalRead(btn_left);
  int rightState = digitalRead(btn_right);
  int atkState = digitalRead(btn_atk);
  if(past_leftState != leftState) Serial.print(0);
  if(past_rightState != rightState) Serial.print(1);
  if(past_atkState != atkState && atkState == 0) Serial.print(2);
  past_leftState = leftState ;
  past_rightState = rightState ;
  past_atkState = atkState ;
  delay(10);        // delay in between reads for stability
}
Processing 

main

import processing.serial.*;
import java.io.*;
import java.util.*;
import ddf.minim.*;
import java.util.Arrays;
//define
final static int SIZE = 3;
final static int ATK_TIME = 15;

//file process about (in fact , is about rank )
BufferedReader rank;
PrintWriter rankin;

//Music about
Minim minim;
AudioPlayer groove,p;
//Serial
Serial myPort;
//linked list
List<monsterClass> lt;
//hero class
heroClass hero = new heroClass(50,75);
//control kind of avtivity
boolean[] ctl = new boolean[SIZE] ;
//Number Image
PImage[] num = new PImage[10];
//back ground img
PImage img_bg_game = loadImage(basicPath + "background\\gameBg.png");
PImage img_start = loadImage(basicPath + "background\\start.jpg");
//parameter
int ctlEnd = 0;
int grade=0;
int count;
int monsterNumber=0;
int nowRunFrame=0;
int monsterNewTime=50;
int dtime = 0;
//0:: game , 1:: rank ,  2::start
int type = 2;
void setup()
{
  size(640, 480);
  //load music
  minim = new Minim(this);
  groove = minim.loadFile(basicPath + "sound\\1.mp3", 512);
  p = minim.loadFile(basicPath + "sound\\atk.mp3", 512);
  groove.loop();
  //initialize
  lt = new LinkedList<monsterClass>();
  myPort = new Serial(this,"COM3", 9600);
  for(int i=0;i<SIZE;i++) ctl[i] = false;
  for(int i=0;i<10;i++) num[i] = loadImage(basicPath + "number\\" + i + "b.png");
}

void draw()
{
  if(type == 0){
    ctlEnd = 0 ;
    //contorl monster create time
    if(++nowRunFrame>monsterNewTime){
      monsterNewTime = 50;
      //add to list
      monsterClass m = new monsterClass(50,75,(int)random(0,199.99));
      lt.add(m); 
      monsterNumber++;
      nowRunFrame=0;
    }
    
    image(img_bg_game,0,0);
    fill(255,0,0);
    rect(0,0,hero.getHealth()*2,50);
    //show hero
    hero.showImage();
    //show grade
    showGrade();
    //detector monsters motion and attack
    for(int i=0;i<lt.size();i++){
      lt.get(i).move(hero.getPoint());
      lt.get(i).showImage();
      hero.setHealth(lt.get(i).atkDetector(hero.getPoint()));
      if(hero.getHealth() <=0) type =1;
    }
    //serial control about
    serialControl();
    if(count>0) grade += hero.atk(lt);
    //control attack img show time
    count--;
  }
  else if(type == 2 ){
     serialControl();
     image(img_start,0,0,640,480);
     dtime--;
  }
  else{
    dtime--;
    serialControl();
    background(0);
    rank=createReader(basicPath + "data\\rank.txt");
    int[] rankArr = new int[11];
    for(int i=0;i<10;i++){
      try{
          rankArr[i] = int(rank.readLine());
          println(rankArr[i]);
       } 
       catch (IOException e) {
          e.printStackTrace();
       }  
    }
    rankArr[10] = grade;
    Arrays.sort(rankArr);
    for(int k=1;k<11;k++){
      int temp=rankArr[k]*10;
      fill(255);
      if(grade == rankArr[k]) rect(650-240-k*25-5,100+(9-k)*35,230,35);
      image(num[10-k],650-240-k*30-60,100+(9-k)*35,50,50);
      for(int i=3;i>=0;i--)
         image(num[(temp/=10)%10],650-60*(4-i)-k*25,100+(9-k)*35,40,40);
    }
     if(ctlEnd == 1 && dtime<=0){
       rankin=createWriter(basicPath + "data\\rank.txt");
       for(int i=1;i<11;i++) rankin.println(rankArr[i]);
       rankin.flush();
       rankin.close();
       type = 2;
       for(int i=0;i<lt.size();i++) lt.remove(i--);
       hero.setHealth(100);
       grade = 0;
       dtime = 500;
     }
  }
}
void showGrade()
{
  
   int temp=grade*10;
   for(int i=3;i>=0;i--)
     image(num[(temp/=10)%10],640-60*(4-i),0,60,60);
   
}
void serialControl()
{
  int temp;
  if ( myPort.available() > 0){
    temp =myPort.read();
    //If btn push is attack
    if((temp - 48) == 2){
      if(type == 1 ) ctlEnd = 1;
      else if(type == 2 && dtime <= 0){
        type = 0;
        dtime = 500; 
      }
      else if(type== 0){
        grade += hero.atk(lt);
        count=ATK_TIME; 
        p.play(0);
      }
    }
    else ctl[temp-48] = !ctl[temp-48];
  }
  // if left and right together is jump
  if(ctl[0] == true && ctl[1] == true) hero.setJump(17);
  //left
  else if(ctl[0] == true){
     hero.setPoint(hero.getPoint().x-3,hero.getPoint().y);
     hero.setState(FACE_LEFT);
  }
  //right
  else if(ctl[1] == true){
     hero.setPoint(hero.getPoint().x+3,hero.getPoint().y);
     hero.setState(FACE_RIGHT);
  }
}

//Keyboard use

void keyPressed() {
  if (key == CODED) {
    if (keyCode == RIGHT || ctl[1] == true) {
      hero.setPoint(hero.getPoint().x+3,hero.getPoint().y);
      hero.setState(FACE_RIGHT);
    } 
    else if (keyCode == LEFT ) {
      hero.setPoint(hero.getPoint().x-3,hero.getPoint().y);
      hero.setState(FACE_LEFT);
    }
    else if(keyCode == UP){
      hero.setJump(17);
    }
    else if(keyCode == DOWN){
      grade += hero.atk(lt);
      count=ATK_TIME; 
      p.play(0);
    }
  }
}

class

final static String basicPath = "D:\\修\\學校課業\\大三\\互動技術\\期末作業\\game\\"; 
final static int ATK_DURING = 100;

//This class use to save site and size ( 2D information )
class Index2D
{
  public int x;
  public int y;
  public Index2D(int x,int y)
  {
    this.x = x;
    this.y = y; 
  }
};

//define :: To make me more easy to know
final static int FACE_RIGHT = 1;
final static int FACE_LEFT = 0;

class heroClass
{
  private Index2D point,size;
  private int health ;
  private int state ;  //Face 0 :: left , 1 :: right
  private PImage img,img_mirro ; 
  private PImage atk,atk_mirro ;
  private int w,h;
  private int vector;
  private boolean lock;
  
  public heroClass(int w,int h)
  {
     img = loadImage(basicPath + "role\\hero.png");
     img_mirro= loadImage(basicPath + "role\\hero_mirro.png");
     atk = loadImage(basicPath + "role\\attack.png");
     atk_mirro= loadImage(basicPath + "role\\attack_mirro.png");
     point = new Index2D(320-w,350-h);
     size = new  Index2D(w,h);
     state = vector = 0;
     lock = false;
     health = 100;
  }
  
  public void setHealth(int x)
  {
    health += x ;
  }
  public int getHealth()
  {
    return health; 
  }
  public PImage getImage()
  {
    return img;
  }
  public Index2D getPoint()
  {
    return point; 
  }
  public Index2D getSize()
  {
    return size; 
  }
  public void showImage()
  {
    point.y -= vector;
    
    //Jump parameter
    if(point.y +size.y!=350){
      vector-=1;
      lock = true;
    }else{
      vector = 0;
      lock = false ; 
    }
    //do not over the window board
    point.x = point.x < 0 ? 0 : point.x;
    point.x = point.x > 640-size.x ? 640-size.x : point.x;
    //control face which site
    if(state == FACE_LEFT ) image(img,point.x,point.y,size.x,size.y);
    else image(img_mirro,point.x,point.y,size.x,size.y);
  }
  public void setPoint(int x , int y)
  {
    point.x = x;
    point.y = y;
  }
  public void setState(int type)
  {
    state = type; 
  }
  public void setJump(int h)
  {
    if(lock == false ) vector = h ;
  }
  public int atk(List<monsterClass> lt)
  {
    //return value is kill number and will add to grade
    int tmp;
    int killNum = 0;
    if(state == FACE_LEFT ) image(atk,point.x-size.x,point.y,size.x,size.y);
    else image(atk_mirro,point.x+size.x,point.y,size.x,size.y);
    
    for(int i=0;i<lt.size();i++){
      //get monster's x site
      tmp =lt.get(i).getPoint().x;
      if(state == FACE_LEFT){
        //if is in range      reduce their life point
         if(tmp >= point.x-size.x && tmp <= point.x){
           lt.get(i).setHealth(-5);
           if( lt.get(i).getHealth()<=0){
             lt.remove(i--);
             killNum++; 
           }
         }
      }
      else{
        if(tmp >= point.x && tmp <= point.x+size.x){
           lt.get(i).setHealth(-50);
           if( lt.get(i).getHealth()<=0){
             lt.remove(i--);
             killNum++;
           }
         }
      }
    }
    return killNum;
  }
 
};

class monsterClass
{
  private Index2D point,size;
  private int health ;
  private int state ;  //Face 0 :: left , 1 :: right
  private PImage img,img_mirro ; 
  private PImage atk;
  private int w,h;
  private int type; 
  private int during,atkPower; //during is how long will attack hero
  private boolean lock ;
  private int count;
  
  public monsterClass(int w,int h,int t)
  {
    count = 0;
    lock = false;
    //t use to decide which kind monster will appear and their parameter
     if( t < 10){
       img = loadImage(basicPath + "role\\monster.png");
       img_mirro= loadImage(basicPath + "role\\monster_mirro.png");
       atk = loadImage(basicPath + "role\\matk.png");
       health = (int)random(200,1000.99);
       during = 40;
       atkPower = -25;
       type = 0;
     }
     else{
       img = loadImage(basicPath + "role\\monster2.png");
       img_mirro= loadImage(basicPath + "role\\monster2_mirro.png");
       atk = loadImage(basicPath + "role\\matk.png");
       health = 50;
       during = 30;
       atkPower = -5;
       type = 1;
     }
     //decide which site will appear
     if(t%2 == 1) state = FACE_LEFT;
     else state = FACE_RIGHT;
     if(state == FACE_LEFT) point = new Index2D(640,350-h);
     else point = new Index2D(0-w,350-h);
     //if is a stronge monster will become bigger;
     if(type == 0){
       size = new Index2D(w*2,h*2);
       point.x -= w;
       point.y -= h; 
     }
     else size = new Index2D(w,h);
  }
  public void setHealth(int x)
  {
    health+=x;  
  }
  public int getHealth()
  {
     return health; 
  }
  public Index2D getPoint()
  {
    return point; 
  }
  public void move(Index2D hero)
  {
    if(lock == false){
      if (point.x < hero.x ) state = FACE_RIGHT ;
      else state = FACE_LEFT ;
      
      if(state == FACE_LEFT ){
        if(type == 0) point.x -= 1;
        else point.x-=2; 
      }
      else{
         if(type == 0) point.x += 1;
        else point.x+=2; 
      }
    }
    
  }
  public int atkDetector(Index2D hero)
  {
    //detector is hero in the attack range 
    //if yes !!  will increase the during time
    //when is over the threshold will attack and do not miss!! XD
    if(hero.y >= point.y-5){
      if(state == FACE_LEFT) {
         if(hero.x > point.x - size.x && hero.x < point.x){
            during++;
            lock = true;
         }
         else lock = false;
      } 
      else{
         if(hero.x < point.x+size.x && hero.x > point.x){
            during++;
            lock = true;
         } 
         else lock = false;
      }
      if(during > ATK_DURING){
         during = 0;
         count = 12;
         return atkPower; 
      }
      else return 0;
    }
    else return 0;
  }
  public void showImage()
  {
    if(state == FACE_LEFT ) image(img,point.x,point.y,size.x,size.y);
    else image(img_mirro,point.x,point.y,size.x,size.y);
    if(count-->0) atk();
  }
  public void atk()
  {
    if(state == FACE_LEFT ) image(atk,point.x-size.x,point.y,size.x,size.y);
    else image(atk,point.x+size.x,point.y,size.x,size.y);
  }
};

小組分工:

Processing、Arduino、硬體部分:徐修謙
購買硬體:謝睿峰

心得:

在這次的作業當中,深深的體會到接駁硬體的困難。不過在製作的過程中,雖然困難但是是非常有趣的,將一段段的線路銜接,最後所製作而成的作品出現,這是非常有成就感的。而程式部分,由於沒有直接使用期中作業去更改,在最後的製作過程其實並不輕鬆。每一個怪物以及英雄所寫成的class,其實也耗費的不少的力氣。雖然以後不太會用到它,不過這個class其實寫出來有不錯的後續延伸性,如果未來我有需要用到相關的功能也可以直接套用就好,不過這好像短期內不太可能。總而言之、言而總之,在這次作業讓我對java更加熟悉,而且也了解到硬體以及電腦軟體之間的通訊,是甚麼一回事,有很大的收穫,也很開心。

沒有留言:

張貼留言