基于Android Flutter編寫(xiě)貪吃蛇游戲
前言
放假期間,小T打算回顧一下經(jīng)典,想用Flutter做一下小游戲,做什么好呢,從打飛機(jī)到坦克大戰(zhàn),最后還是想做一款貪吃蛇,依稀還記得,小時(shí)候第一次玩游戲是在父母的小靈通上玩貪吃蛇哈哈,但是光光一個(gè)貪吃蛇太單調(diào)了,我們就加一個(gè)陀螺儀吧~
話(huà)不多說(shuō),先上效果圖,有圖有真相?。。ㄍ勇輧x好難操控)!
開(kāi)發(fā)步驟:
非常簡(jiǎn)單,就是玩起來(lái)有點(diǎn)費(fèi)手~
github倉(cāng)庫(kù)還沒(méi)有搭建,大家可以先看一下文末通知!
1.定義蛇和豆子
2.讓蛇動(dòng)起來(lái)
3.使用陀螺儀來(lái)控制蛇
4.讓蛇吃掉豆子
5.吃掉豆子隨機(jī)生成一個(gè)豆子
1.定義蛇和豆子
///蛇的每一塊的大小 const double size = 10; Offset ball = Offset.zero;//豆子 List<Offset> snakeList = [Offset(50, 0), Offset(60, 0)];//蛇的長(zhǎng)度
顯示豆子和蛇:
使用Stack是因?yàn)槎棺釉诒簧叱缘臅r(shí)候會(huì)重疊
使用Positioned來(lái)進(jìn)行定位
body: Stack( children: snakeList .map((snake) => Positioned.fromRect( rect: Rect.fromCenter( center: adjust(snake), width: size, height: size), child: Container(margin:const EdgeInsets.all(1),color: Colors.black)))//加個(gè)外邊距使每一塊更清晰 .toList() ..add(Positioned.fromRect( rect: Rect.fromCenter( center: adjust(ball), width: size, height: size), child: Container(color: Colors.orange))), )
Offset adjust(Offset offset) { return Offset(offset.dx + size / 2, offset.dy + size / 2); //使每一塊更好的展示出來(lái) }
2.讓蛇動(dòng)起來(lái)
在這里我們要先給蛇來(lái)一個(gè)狀態(tài)定義:
///控制蛇的狀態(tài) enum Direction { Up, Down, Left, Right }
因?yàn)樯咭恢眲?dòng),所以給一個(gè)周期函數(shù):
1.定義200毫秒動(dòng)一次
2.處理更新蛇的長(zhǎng)度
3.求余的好處在于優(yōu)化吃豆子,因?yàn)殡S機(jī)生成豆子時(shí),可能是個(gè)不會(huì)被整除的。
Direction direction = Direction.Left; //給蛇設(shè)置一個(gè)狀態(tài),默認(rèn)向左移動(dòng) ///周期函數(shù) void didChangeDependencies() { ///兩百毫秒的周期函數(shù) var period = Duration(milliseconds: 200); ///對(duì)蛇的長(zhǎng)度進(jìn)行優(yōu)化 double maxWidth = MediaQuery.of(context).size.width; double widthPad = maxWidth % size; maxWidth -= widthPad; double maxHeight = MediaQuery.of(context).size.height; double heigthPad = maxHeight % size; //這里除于是為了更好的游戲體驗(yàn) maxHeight -= heigthPad; ///周期調(diào)用 ///用于貪吃蛇的自己移動(dòng),以及碰撞檢測(cè) Timer.periodic(period, (timer) { final snakeHead = snakeList[0]; List<Offset> newSnakeList = List.generate(snakeList.length, (index) { if (index > 0) { return snakeList[index - 1]; } else { ///移動(dòng)處理 switch (direction) { case Direction.Up: return Offset(snakeHead.dx, (snakeHead.dy - size + maxHeight) % maxHeight); //求余是保證不會(huì)超標(biāo) case Direction.Down: return Offset( snakeHead.dx, (snakeHead.dy + size + maxHeight) % maxHeight); case Direction.Left: return Offset( (snakeHead.dx - size + maxWidth) % maxWidth, snakeHead.dy); case Direction.Right: return Offset( (snakeHead.dx + size + maxWidth) % maxWidth, snakeHead.dy); } } }); setState(() { snakeList = newSnakeList; //更新蛇的狀態(tài) }); }); super.didChangeDependencies(); }
3.使用陀螺儀來(lái)控制蛇
Flutter使用陀螺儀需要借助一個(gè)庫(kù):sensors 或者sensors_plus,兩者沒(méi)有太大的區(qū)別
這個(gè)demo使用:
sensors: any
官方給的例子:
import 'package:sensors/sensors.dart'; ? accelerometerEvents.listen((AccelerometerEvent event) { print(event); }); // [AccelerometerEvent (x: 0.0, y: 9.8, z: 0.0)] 加速度 ? userAccelerometerEvents.listen((UserAccelerometerEvent event) { print(event); }); // [UserAccelerometerEvent (x: 0.0, y: 0.0, z: 0.0)] ? gyroscopeEvents.listen((GyroscopeEvent event) { print(event); }); // [GyroscopeEvent (x: 0.0, y: 0.0, z: 0.0)] 陀螺儀
我們?cè)趇nitState對(duì)陀螺儀進(jìn)行監(jiān)聽(tīng):
這里有x,y,z的三個(gè)參數(shù),也可以自己優(yōu)化調(diào)試,5.0是當(dāng)手機(jī)傾斜>=45°
@override void initState() { super.initState(); accelerometerEvents.listen((AccelerometerEvent event) { setState(() { _accelerometerValues = <double>[event.x, event.y, event.z]; if(_accelerometerValues[0] >= 5.0){ direction = Direction.Left; }else if(_accelerometerValues[1] >= 5.0){ direction = Direction.Down; }else if(_accelerometerValues[0] <= -5.0){ direction = Direction.Right; }else if(_accelerometerValues[1] <= -5.0){ direction = Direction.Up; } }); }); }
4.讓蛇吃掉豆子
還是在剛剛的周期函數(shù)里添加:
當(dāng)蛇頭碰到豆子時(shí),給蛇加一格
if(newSnakeList[0] == ball){ newSnakeList..add(snakeList[snakeList.length - 1]); setState(() { ball = randoowPositon(maxWidth, maxHeight); //隨機(jī)生成一個(gè)豆子,randoowPositon方法在后面 }); }
5.吃掉豆子隨機(jī)生成一個(gè)豆子
對(duì)豆子的生成也需要優(yōu)化一下 (之前生成有點(diǎn)問(wèn)題,現(xiàn)在優(yōu)化一下)
Offset randoowPositon(double widthRange, double heightRange) { ///隨機(jī)生成豆子 var rng = Random(); int intWidthRange = widthRange.toInt(); int intHeightRange = heightRange.toInt(); int finalWdith = rng.nextInt(intWidthRange); int finalHeight = rng.nextInt(intHeightRange); double widthPad = finalWdith % size; double heightPad = finalHeight % size; double actualWidth = finalWdith - widthPad; double actualHeight = finalHeight - heightPad; return Offset(rng.nextInt(widthRange.toInt()).toDouble(), rng.nextInt(heightRange.toInt()).toDouble()); }
到此這篇關(guān)于基于Android Flutter編寫(xiě)貪吃蛇游戲的文章就介紹到這了,更多相關(guān)Flutter貪吃蛇內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android實(shí)現(xiàn)帶磁性的懸浮窗體效果
這篇文章主要介紹了Android實(shí)現(xiàn)帶磁性的懸浮窗體效果,涉及Android針對(duì)窗體的動(dòng)態(tài)操作相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2016-07-07Android RecyclerView實(shí)現(xiàn)水平、垂直方向分割線(xiàn)
這篇文章主要為大家詳細(xì)介紹了Android RecyclerView實(shí)現(xiàn)水平、垂直方向分割線(xiàn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07Android開(kāi)發(fā)筆記 最好使用eclipse
值得注意一點(diǎn)的是,雖然Myeclipse比eclipse功能更強(qiáng)大,但是在具體的安卓開(kāi)發(fā)過(guò)程當(dāng)中,最好還是選用eclipse,sdk跟eclipse的兼容性更好2012-11-11Android短信驗(yàn)證碼(用的Mob短信驗(yàn)證)
這篇文章主要為大家詳細(xì)介紹了Android短信驗(yàn)證碼,使用Mob短信驗(yàn)證,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05Android中LeakCanary檢測(cè)內(nèi)存泄漏的方法
本篇文章主要介紹了Android中LeakCanary檢測(cè)內(nèi)存泄漏的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09Android實(shí)現(xiàn)自定義View控件的流程詳解
這篇文章主要為大家詳細(xì)介紹了Android中實(shí)現(xiàn)自定義View控件的流程,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解一下2023-06-06Android實(shí)現(xiàn)歡迎滑動(dòng)頁(yè)面
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)歡迎滑動(dòng)頁(yè)面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04