組員:05160035林欣儀、05160160黃偉愷
Demo影片 Youtube網址:https://www.youtube.com/watch?v=1C28ph7BvZ0 介紹:首先,開啟遊戲時,因為載入音樂會需要花點時間,所以製作了loading的畫面。接下來玩家可依照個人的喜好選擇小夥伴,確定小夥伴後就可以進入到遊戲畫面。遊戲主要是讓玩家透過按鍵模組來操控小夥伴的方向,讓小夥伴跳在每朵雲上。每一層的雲朵長度不一致,並且在第五層時雲朵會隨機移動。另外,左右兩側會隨機出現小麻雀飛過,玩家必需閃躲,碰到小麻雀或從雲朵上掉落下來,遊戲就結束了。玩家必需要讓小夥伴安全的跳在每朵雲上和閃躲小麻雀,考驗玩家的技術和反應。
操作:利用arduino按鍵模組上的"上鍵"、"左鍵"、"右鍵",來控制小夥伴的方向。
特色:遊戲的設計上,在越高樓層會提高難度與速度,以及增加隨機飛躍的小麻雀,增加遊戲的困難度,讓玩家可以不斷挑戰。介面的設計上,主要以可愛的角落小夥伴當作遊戲主角,並且搭配輕鬆愉快的音樂,增加遊戲整體的豐富度。另外,因為剛開始載入音樂時,會需要花一點時間等待,為了填補這等待的時間,所以增加loading的畫面。
Code:Sumikkogurashi_shaft
import processing.sound.*; // 匯入音樂播放的套件
import processing.serial.*; // 與Arduino連接的套件
GameView gv; // 遊戲畫面
WelcomeView wv; // 歡迎畫面
LoadingView lv; // loading畫面
boolean isGameStart = false; // 紀錄遊戲開始了沒
boolean isLoadingFinsh = false; // 紀錄Loading完了沒
SoundFile player; // 宣告音樂播放器
Serial serial; // 與Arduino連接的東西
String[] characters = {"cat", "dinosaur", "bear", "lizard"};
int nowCharacter = 0; // 紀錄現在選的是哪個角色
void setup() {
size(500, 800);
lv = new LoadingView();
// 在子線程載入進行遊戲初始化,未初始化前會先顯示載入動畫
thread("initGame");
}
void initGame() {
// 初始化與Arduino連接的東西
serial = new Serial(this, "/dev/cu.wchusbserial1420", 9600);
// 初始化音樂播放器,並傳入音檔檔名
player = new SoundFile(this, "Asset/角落.mp3");
gv = new GameView();
wv = new WelcomeView();
initAllButton();
isLoadingFinsh = true;
}
void initAllButton() {
// 初始化貓咪按鈕
wv.cat = new Button(5, height/2+200, 120, 120) {
@Override
public void onClick() {
// 按下按鈕開始遊戲
nowCharacter = 0;
startGame();
}
};
wv.cat.img = loadImage("Asset/cat.png");
wv.cat.imgHover = loadImage("Asset/cat_left.png");
// 初始化恐龍按鈕
wv.dinosaur = new Button(125, height/2+200, 120, 120) {
@Override
public void onClick() {
// 按下按鈕開始遊戲
nowCharacter = 1;
startGame();
}
};
wv.dinosaur.img = loadImage("Asset/dinosaur.png");
wv.dinosaur.imgHover = loadImage("Asset/dinosaur_left.png");
// 初始化白熊按鈕
wv.bear = new Button(245, height/2+200, 120, 120) {
@Override
public void onClick() {
// 按下按鈕開始遊戲
nowCharacter = 2;
startGame();
}
};
wv.bear.img = loadImage("Asset/bear.png");
wv.bear.imgHover = loadImage("Asset//bear_left.png");
// 初始化蜥蜴按鈕
wv.lizard = new Button(365, height/2+200, 120, 120) {
@Override
public void onClick() {
// 按下按鈕開始遊戲
nowCharacter = 3;
startGame();
}
};
wv.lizard.img = loadImage("Asset/lizard.png");
wv.lizard.imgHover = loadImage("Asset/lizard_left.png");
}
void startGame() {
// 按下按鈕開始遊戲
isGameStart = true;
gv.initView(
"Asset/"+characters[nowCharacter]+"_left.png",
"Asset/"+characters[nowCharacter]+"_right.png");
player.loop(); // 播放音檔
frameRate(75);
}
void draw() {
if (!isLoadingFinsh) {
lv.draw();
return;
}
// 如果還沒開始遊戲就顯示主畫面
if (!isGameStart) {
wv.draw();
handleChoseCharacters();
return;
}
if (!gv.isGameOver()) {
// 如果玩家還沒掛掉就繼續更新遊戲畫面
gv.draw();
//joyStickContorl(); // 搖桿操作
handleContorl(); // 把手操作
} else {
// 如果GameOver就不要繼續顯示遊戲畫面和播音樂了
isGameStart = false;
player.stop();
}
}
void keyPressed() {
switch(keyCode) {
case LEFT:
gv.dinosaur.facing = 'L';
gv.dinosaur.velocity.x = -5f;
break;
case RIGHT:
gv.dinosaur.facing = 'R';
gv.dinosaur.velocity.x = 5f;
break;
case UP:
gv.dinosaur.jump();
break;
}
}
void keyReleased() {
// 放開鍵盤就煞車
if (keyCode == RIGHT) {
gv.dinosaur.velocity.x = 0f;
}
if (keyCode == LEFT) {
gv.dinosaur.velocity.x = 0f;
}
if (keyCode == UP) {
// 放開上鍵後,要記得把isjump設定為false,這樣下次按上鍵時,小恐龍才可以跳耀
gv.dinosaur.isjump = false;
}
}
// 搖桿操作
void joyStickContorl() {
float x=0, y=0;
if(serial.available()>0){
String now= serial.readString();
String[] xy= splitTokens(now);
x = float(xy[0]);
y = float(xy[1]);
println("==================");
println("x: " + x + " y: " + y);
if (x > 550) {
gv.dinosaur.facing = 'R';
gv.dinosaur.velocity.x = 5f;
} else if (x < 500) {
gv.dinosaur.facing = 'L';
gv.dinosaur.velocity.x = -5f;
} else {
gv.dinosaur.velocity.x = 0f;
}
if (y < 500) {
gv.dinosaur.jump();
} else {
// 放開上鍵後,要記得把isjump設定為false,這樣下次按上鍵時,小恐龍才可以跳耀
gv.dinosaur.isjump = false;
}
}
}
// 把手操作
void handleContorl() {
if(serial.available()>0){
float now = float(serial.readString());
//println(now);
if (now >= 160 && now <= 170) {
gv.dinosaur.facing = 'R';
gv.dinosaur.velocity.x = 5f;
} else if (now >= 0 && now <= 10) {
gv.dinosaur.facing = 'L';
gv.dinosaur.velocity.x = -5f;
} else if (now >= 30 && now <= 40) {
gv.dinosaur.jump();
} else {
gv.dinosaur.velocity.x = 0f;
// 放開上鍵後,要記得把isjump設定為false,這樣下次按上鍵時,小恐龍才可以跳耀
gv.dinosaur.isjump = false;
}
//if (now >= 30 && now <= 40) {
// gv.dinosaur.jump();
//} else {
// // 放開上鍵後,要記得把isjump設定為false,這樣下次按上鍵時,小恐龍才可以跳耀
// gv.dinosaur.isjump = false;
//}
}
}
// 用把手選角色
void handleChoseCharacters() {
if(serial.available()>0){
float now = float(serial.readString());
//println("-----------");
//println(now);
if (now >= 160 && now <= 170) {
nowCharacter ++;
if (nowCharacter >= characters.length) nowCharacter=0;
} else if (now >= 0 && now <= 10) {
nowCharacter --;
if (nowCharacter < 0) nowCharacter=characters.length-1;
}
//println(nowCharacter);
clearButtonChose();
// 如果按鈕isChose==true就會顯示面向右邊的圖樣
switch(nowCharacter) {
case 0:
wv.cat.isChose = true;
break;
case 1:
wv.dinosaur.isChose = true;
break;
case 2:
wv.bear.isChose = true;
break;
case 3:
wv.lizard.isChose = true;
break;
}
// 按下最右邊的按鈕開始遊戲
if (now >= 350 && now <= 360) {
startGame();
}
}
}
// 把每個按鈕的isChose清除掉,這樣一次才會顯示一隻被選擇
void clearButtonChose() {
wv.cat.isChose = false;
wv.dinosaur.isChose = false;
wv.bear.isChose = false;
wv.lizard.isChose = false;
}
LoadingView
import java.util.Arrays;
import java.util.List;
class LoadingView {
PImage[] images; // 存開場動畫的陣列
LoadingView() {
// 讀入角落小夥伴動圖的路徑
File temp = new File(sketchPath() + "/Asset/loading_animate/");
File[] fs = temp.listFiles();
ArrayList<File> fsList = new ArrayList<File>(Arrays.asList(fs));
println("\n\n==============Loading動畫的所有檔案==============");
for (File f : fsList) {
println(f);
}
// 移除 .DS_Store 檔案
for (File f : fsList) {
if (f.getName().equals(".DS_Store")) {
fsList.remove(f);
break;
}
}
images = new PImage[fsList.size()];
// 讀入角落小夥伴的動圖
for (int i=0; i<fsList.size(); i++) {
// 因為會有圖片順序的問題,因此暫時先寫死
images[i]=loadImage("Asset/loading_animate/img" + (i+1) + ".png");
}
}
void draw() {
frameRate(7);
background(#555555);
showText("Loading...");
image(images[frameCount%images.length], 65, 250);
}
void showText(String text) {
// 顯示文字
fill(#EEFFEE); // 文字顏色
textSize(50); // 文字大小
textAlign(CENTER, CENTER); // 文字置中對齊
text(text, width/2, height/2 + 150); // 寫文字
}
}
WelcomeView
import java.util.Arrays;
import java.util.List;
class WelcomeView {
PImage bg, title; // 背景圖和標題
PImage[] images; // 存開場動畫的陣列
Button bt; // 按鈕
Button cat, bear, dinosaur, lizard;
int frameIndex = 0; // 動畫的偵號
WelcomeView() {
// 讀入背景和標題圖
bg = loadImage("Asset/welcomeView/sky.jpg");
title = loadImage("Asset/welcomeView/Sky_challenge.png");
// 讀入角落小夥伴動圖的路徑
File temp = new File(sketchPath() + "/Asset/animate/");
File[] fs = temp.listFiles();
ArrayList<File> fsList = new ArrayList<File>(Arrays.asList(fs));
println("\n\n==============開場動畫的所有檔案==============");
for (File f : fsList) {
println(f);
}
// 移除 .DS_Store 檔案
for (File f : fsList) {
if (f.getName().equals(".DS_Store")) {
fsList.remove(f);
break;
}
}
images = new PImage[fsList.size()];
// 讀入角落小夥伴的動圖
for (int i=0; i<fsList.size(); i++) {
// 因為會有圖片順序的問題,因此暫時先寫死
images[i]=loadImage("Asset/animate/img" + (i+1) + ".png");
}
}
void draw() {
frameRate(24);
//background(#D2EDF7);
image(bg, 0, 0);
image(title, width/2-350, height/2-440, 640, 360);
image(images[frameIndex], 65, 250);
// 原本一秒四偵,現在一秒24偵,變六倍了,每六偵更新一次動畫
if (frameCount % 6 == 0) {
frameIndex ++;
if (frameIndex >= images.length) frameIndex = 0;
}
// 畫全部的按鈕
cat.draw();
lizard.draw();
bear.draw();
dinosaur.draw();
}
}
Background
class Background {
ArrayList<Cloud> cloudList;
float y = -150;
PImage bg; // 背景圖
Background() {
cloudList = new ArrayList<Cloud>();
bg = loadImage("Asset/sky.jpg"); // 讀入背景圖
// 初始化所有的雲朵
for (int i=0; i<6; i++) {
cloudList.add(new Cloud((int)random(0, width-200), y+=150));
}
}
void drawCloud() {
// 滾動畫面上的雲朵
for (int i=0; i<6; i++) {
// 如果雲朵滾超出畫面就再畫一個新的雲朵
if (cloudList.get(i).locate.y >= height) {
cloudList.remove(i);
cloudList.add(new Cloud((int)random(0, width-200), -100));
score ++; // 分數加1
moveRate += 0.01; // 速度加0.01
}
cloudList.get(i).locate.y += 1+moveRate;
cloudList.get(i).draw();
}
}
void draw() {
//background(128, 170, 217);
image(bg, 0, 0); // 畫背景圖
showScore();
drawCloud();
}
void showScore() {
// 顯示文字
fill(#EEFFEE); // 文字顏色
textSize(175); // 文字大小
textAlign(CENTER, CENTER); // 文字置中對齊
text((score/6)+"F", width/2, height/2-50); // 寫文字
}
}
GameView
import java.util.Collections;
float moveRate = 0; // 畫面滾動的速度
int score = 6; // 玩家的分數,每六朵雲上升一層樓
class GameView {
Dinosaur dinosaur;
Background bg;
Bird bird;
//GameView() {
// initView();
//}
void initView(String playerImgDir_L, String playerImgDir_R) {
bg = new Background();
dinosaur = new Dinosaur(bg.cloudList.get(1).locate.x, 0, 'R', playerImgDir_L, playerImgDir_R);
dinosaur.locate.y = bg.cloudList.get(1).locate.y + 10 - dinosaur.dinosaurH;
moveRate = 0;
score = 6;
bird = new Bird('L');
bird.locate.x = -100;
}
void draw() {
if (isGameOver()) return; // 如果GameOver了就不要繼續更新畫面了
bg.draw();
dinosaur.draw();
if (dinosaur.touchBird) {
dinosaur.onCloud = false;
} else {
dinosaurOnCloud();
}
drawBird();
dinosaurTouchBird();
}
void drawBird() {
if ((score%6 == 0 && score>6) &&
(bird.locate.x < -500 || bird.locate.x >= width+500)) {
float temp = random(0, 2);
if (temp < 1) {
bird = new Bird('R');
} else {
bird = new Bird('L');
}
}
bird.draw();
}
void dinosaurOnCloud() {
for (Cloud cloud : bg.cloudList) {
if (dinosaur.locate.x >= (cloud.locate.x - dinosaur.dinosaurW + 20) &&
dinosaur.locate.x <= (cloud.locate.x + cloud.cloudW - 20) &&
dinosaur.locate.y + dinosaur.dinosaurH >= (cloud.locate.y) &&
dinosaur.locate.y + dinosaur.dinosaurH <= (cloud.locate.y + cloud.cloudH) &&
dinosaur.velocity.y >= 0) {
dinosaur.onCloud = true;
dinosaur.locate.y = (cloud.locate.y + 10) - dinosaur.dinosaurH;
break;
} else {
dinosaur.onCloud = false;
}
}
}
// 看恐龍有沒有碰到鳥,碰到鳥就掛掉往下掉
void dinosaurTouchBird() {
if (((bird.locate.x+bird.birdW/2) >= dinosaur.locate.x &&
(bird.locate.x+bird.birdW/2) <= (dinosaur.locate.x+dinosaur.dinosaurW)) &&
((bird.locate.y+bird.birdH/2) >= dinosaur.locate.y &&
(bird.locate.y+bird.birdH/2) <= (dinosaur.locate.y+dinosaur.dinosaurH))) {
dinosaur.touchBird = true;
}
}
boolean isGameOver() {
if (dinosaur.locate.y > height + 50) return true;
else return false;
}
}
Dinosaur
class Dinosaur {
PImage dinosaurLeft, dinosaurRight;
float dinosaurW=70, dinosaurH=70; // 恐龍的大小
PVector locate; // 恐龍的位置
PVector velocity; // 恐龍移動的速度
char facing; // 恐龍的面向
boolean onCloud = true; // 紀錄恐龍有沒有在雲上
boolean isjump = false; // 紀錄恐龍有沒有正在跳耀
boolean touchBird = false; // 紀錄恐龍有沒有碰到鳥
Dinosaur(float x, float y, char facingROL, String playerImgDir_L, String playerImgDir_R) {
dinosaurLeft = loadImage(playerImgDir_L);
dinosaurRight = loadImage(playerImgDir_R);
locate = new PVector(x, y);
velocity = new PVector(0, 0);
facing = facingROL;
}
void draw() {
// 讓小恐龍在橫向的方向移動時不會超出畫面
if (locate.x > width-dinosaurW) locate.x = width-dinosaurW;
if (locate.x < 0) locate.x = 0;
switch (facing) {
case 'R':
image(dinosaurRight, locate.x, locate.y, dinosaurW, dinosaurH);
break;
case 'L':
image(dinosaurLeft, locate.x, locate.y, dinosaurW, dinosaurH);
break;
}
// 把小恐龍的位置加上在XY軸方向的加速度
locate.add(velocity);
if (!onCloud) {
// 如果恐龍沒有在雲上就往下掉,給恐龍向下的加速度
if (velocity.y <= 30) velocity.y += 0.98;
} else {
locate.y += 1+moveRate; // 如果恐龍在雲上就要隨著雲向下移動
if (velocity.y >= 0) velocity.y = 0; // 如果恐龍在雲上就可以不用給他向下的加速度
}
}
void jump() {
// 如果小恐龍在雲上且沒有正在跳耀才可以做跳的動作(避免連環跳的情況發生)
if (onCloud && !isjump) {
isjump = true;
velocity.y = -20+moveRate;
}
}
}
Cloud
class Cloud {
PImage cloud;
PVector locate; // 雲朵的位置
int cloudW=200, cloudH=50; // 雲朵的寬度跟高度
int moveDirection; // 雲的移動方向
Cloud(float x, float y) {
cloud = loadImage("Asset/cloud.png");
locate = new PVector(x, y);
moveDirection = round(random(-2, 2)); // 隨機移動雲的方向
cloudW = round(random(150, 200)); // 隨機初始化雲的大小
}
void draw() {
if (score/6 >= 5) {
// 當雲撞到邊界就反彈
if (locate.x > width-cloudW) moveDirection = round(random(-2, 0));
if (locate.x < 0) moveDirection = round(random(0, 2));
locate.x += moveDirection; // 移動雲
}
image(cloud, locate.x, locate.y, cloudW, cloudH);
}
}
Bird
class Bird {
PVector locate; // 鳥的位置
PVector velocity; // 鳥移動的速度
float birdW = 60, birdH = 60; // 鳥的長寬
PImage birdRight;
PImage birdLeft;
char facing; // 鳥的面向
Bird(char facingROL) {
birdRight = loadImage("Asset/bird_right.png");
birdLeft = loadImage("Asset/bird_left.png");
facing = facingROL;
switch (facing) {
case 'R':
velocity = new PVector(round(random(3, 6)), 0);
locate = new PVector(0, round(random(0, height/2)));
break;
case 'L':
velocity = new PVector(-round(random(3, 6)), 0);
locate = new PVector(width, round(random(0, height/2)));
break;
}
}
void draw() {
switch (facing) {
case 'R':
image(birdRight, locate.x, locate.y, birdW, birdH);
break;
case 'L':
image(birdLeft, locate.x, locate.y, birdW, birdH);
break;
}
// 把小鳥的位置加上在XY軸方向的加速度
locate.add(velocity);
}
}
Button
abstract class Button {
PVector pos;
float w, h;
PImage imgHover, img;
int value = 0;
int state; // 紀錄畫Hover還是沒有Hover的按鈕
boolean isChose = false; //紀錄把手有沒有選到
//PImage pointer; // 顯示現在選到哪個角色用的
Button(float x, float y, float btnW, float btnH) {
pos = new PVector(x, y);
w = btnW;
h = btnH;
//pointer = loadImage("Asset/fried_shrimp.png"); // 用炸蝦的圖片當指標
}
void draw() {
checkClick();
mouseHover();
if (state == 0) {
image (img, pos.x, pos.y, w, h);
} else if (state == 1) {
image (imgHover, pos.x, pos.y, w, h);
//image (pointer, pos.x+(w/2)-35, pos.y+h, 70, 70);
}
}
void mouseHover() {
if (isChose) {
state = 1;
} else if (mouseX > pos.x && mouseX < pos.x+w &&
mouseY > pos.y && mouseY < pos.y+h) {
state = 1;
nowCharacter = -1;
} else {
state = 0;
}
}
void checkClick() {
if (mousePressed &&
mouseX > pos.x && mouseX < pos.x+w &&
mouseY > pos.y && mouseY < pos.y+h) {
onClick();
}
}
public abstract void onClick();
}



沒有留言:
張貼留言