一個 uml 課程的大作業,專案要求設計並開發一款 2048 與某種遊戲型別相結合的創新遊戲。可以選擇只建模或者既建模又實現,既然要做當然是選擇實現啦(雖然沒有接觸過遊戲...期末周的莽衝hhh,小組內我負責程式碼實現,用的是基於JavaFX的遊戲開發框架FXGL
遊戲介紹
遊戲背景
因為這次大作業趕上期末複習周,我們遊戲的名字就叫FinalWeek2048,融入了我們學校期末周的背景。以2048遊戲元素為主,加入雙人競技,碰撞,放置的創新元素。
2048中從2-2048共11種方塊,我們用C, C++, java, Python,資料庫,資料結構,計算機組成原理,計算機網路,Web開發,HTML,UML共11中課程方塊替代,分值上和2048中相對應。(計算機宇宙的盡頭是UML建模設計哈哈哈
遊戲玩法
玩家一和玩家二通過按鍵控制角色,隨機釋放不同的方塊,所放置的方塊具有重力屬性,放置後自由下墜,觸碰到其他不同的方塊或遊戲邊界則停留,若與其他相同的方塊相撞則合成一個更加高階的方塊,並加上等級相應的分數。
遊戲結束條件:堆積的方塊觸到玩家所站的平臺或一方勝利
勝利條件:a. 率先得到2048分;b. 率先合成UML方塊;c. 一方觸頂,另一方勝利
遊戲開發框架FXGL
以下是FXGL在Github上對自己的介紹和定位(直接谷歌生翻的
我選擇使用FXGL,首先是考慮如果在這麼短的時間去重新學習目前比較熱門的遊戲框架難度更大(自身也沒打算往遊戲方向發展,易上癮不敢碰),然後之前已經學習了JavaFX正好FXGL是基於它的,學習成本會小很多,還能鍛鍊一下。
事實證明,我想多了,阻力比我想象的大,FXGL在國內外都比較冷門,在國內資料幾乎沒有,官方給的wiki不能滿足我的需求,再配上官方的遊戲demo,勉強做了下來。(感覺如果之前用過Unity或者Cocos這樣的框架會好學很多,因為官方也提到了FXGL是參照熱門的一些遊戲框架開發的)
然後就是我過菜的英文水平!基本的就是應付官方文件和wiki,非常關鍵在於在社群找bug的解決方案的時候...翻譯不通
還有一點,FXGL的API文件版本很舊,目前沒有釋出最新的但框架好像已經更到了17...
成果展示
1. 主選單介面
主選單是繼承了MainMenu自己重新寫了個,藍色是糊掉了學校資訊,還加上了與期末周格格不入的歡快背景音樂[doge]
還有個遊戲結束時的子選單,直接用了內建的SimpleGameMenu。
2. 遊戲主介面
雖然有素材,但沒有給兩個玩家加上移動的動態動作,當時時間趕懶得分開寫。背景是我們學校的一個塔,放這裡還挺合適的。方塊碰撞消除會有音效和粒子碰撞效果~
實現細節
以下是在實現過程中我覺得難度較大的部分,可以先跳過看原始碼瞭解整體的框架
- 在工廠中對方塊實體的設定
要使得實體間產生碰撞或者具有重力,則需要把它們加入物理世界並且設定引數。
關於相同的方塊碰撞加分,就是賦予了方塊實體HealthIntComponent部件(一般在遊戲中是血條),讓它具有一個整型引數屬性,應該還有更好的處理方法,然後為了方便佈置關卡,我是在工廠寫出了這11中方塊實體從a-k。
@Spawns("a")
public Entity newA(SpawnData data) {
// 這裡就是設定方塊的物理屬性,設定密度,能在物理世界自由下墜
PhysicsComponent physics = new PhysicsComponent();
physics.setBodyType(BodyType.DYNAMIC);
physics.setFixtureDef(new FixtureDef().density(0.03f));
return entityBuilder(data)
.type(FinalWeekType.BOOKBLOCK)
.viewWithBBox(texture("bookblock_2.png", 80, 80))
.with(new HealthIntComponent(2))
.with(physics)
.collidable()
.build();
}
- 方塊之間和方塊與平臺的碰撞處理
不同方塊直接堆疊,相同方塊碰撞後移除遊戲世界產生特效實體以及新的方塊實體
處理碰撞的計分歸屬,是在隨機引數的方塊加了IDComponent來識別是玩家一還是玩家二放置的。
@Override
protected void initPhysics() {
PhysicsWorld physicsWorld = getPhysicsWorld();
// 當兩個相同的方塊碰撞在一起後消失,併產生一個加倍的方塊
physicsWorld.addCollisionHandler(new CollisionHandler(FinalWeekType.BOOKBLOCK, FinalWeekType.BOOKBLOCK) {
@Override
protected void onCollision(Entity playerBlock, Entity block) {
int num1 = playerBlock.getComponent(HealthIntComponent.class).getMaxValue();
int num2 = block.getComponent(HealthIntComponent.class).getMaxValue();
String curBlock = "";
if(playerBlock.isType(block.getType())) {
if(playerBlock.hasComponent(IDComponent.class)) {
curBlock = playerBlock.getComponent(IDComponent.class).getName();
}
if(num1 == num2) {
Point2D explosionSpawnPoint = playerBlock.getCenter().subtract(64, 64);
spawn("explosion", explosionSpawnPoint);
runOnce(()->play("combine.wav"), Duration.seconds(0.5));
double x = block.getCenter().getX() - 40, y = block.getCenter().getY() - 40;
playerBlock.removeFromWorld();
block.removeFromWorld();
int score = (int)(Math.log(num1) / Math.log(2));
char ch = (char) (score + 'a');
if(ch >= 'k') {
if(curBlock == "firstPlayerScore") showGameOver("玩家一");
else showGameOver("玩家二");
}
String str = "" + ch;
spawn(str, x, y);
if(curBlock != "") inc(curBlock, +num1);
}
}
}
});
// 處理方塊與玩家所站的平臺底部的碰撞
physicsWorld.addCollisionHandler(new CollisionHandler(FinalWeekType.BOOKBLOCK, FinalWeekType.PLATFORM) {
@Override
protected void onCollision(Entity block, Entity platform) {
if(block.getComponent(IDComponent.class).getName() == "firstPlayerScore") showGameOver("玩家二");
else showGameOver("玩家一");
}
});
}
原始碼分享
FinalWeek2048 原始碼
程式碼其實還沒整理好,然後碰撞產生的連鎖碰撞消除的計分還沒有處理好,等之後空閒下來再來整理完善~
一個簡單的小遊戲,歡迎大佬們指教!