TankCombat系列文章
如果你还不了解Flame可以看这里:
Flutter&Flame——TankCombat游戏开发(一)
Flutter&Flame——TankCombat游戏开发(二)
Flutter&Flame——TankCombat游戏开发(三)
Flutter&Flame——TankCombat游戏开发(四)
效果图
蛮好看的,我再加一下,让大家整体有个印象自己在做什么 :)
image思考
在开工之前,我们先要思考一下,游戏中的电脑(坦克/炮弹),它们并不知道该往哪里走、炮塔怎么转以及何时该开火,这一切都是我们通过代码告诉它们该如何‘表现’的,那么当两台电脑需要交互的时候,就产生了谁开的炮,谁原地爆炸,谁又重生在何时何地的问题...
这时我们就需要一个观察者,并将处理逻辑通过代码告诉它,让它替我们安排诸事。
开工
首先我们给它起名叫 GameObserver。
GameObserver
我们创建这个类的同时,再创建一个枚举,用来区分敌方两种颜色的坦克。
于此同时,我们把要被观察的对象给观察者,即tankGame。
enum TankCate{
GreenTank,SandTank
}
class GameObserver{
final TankGame game;
GameObserver(this.game);
...
}
我们观察者肯定得有一个观察的方法,我们创建一个。
void watching(double t)
看到之后需要做哪些事情呢? 目前我们需要:
制造新的坦克
查看谁被击中并判定谁嗝屁
//这个名字我起得不好,改成 building就容易理解了,你可以把它理解为车间正在制造坦克
bool isGenerating = false;
void watching(double t){
if(!isGenerating){
//重生坦克
if(game.gTanks.length<2){
isGenerating = true;
coolDown(spawnTank(TankCate.GreenTank));
return;
}
if(game.sTanks.length<2){
isGenerating = true;
coolDown(spawnTank(TankCate.SandTank));
return;
}
}
///检查谁被击中
checkHit();
}
制造坦克
我们先看制造坦克部分,首先是一个
coolDown(spawnTank(TankCate.GreenTank))
void coolDown(Function task) {
Future.delayed(Duration(milliseconds: 1500)).then((value) {
if(task != null){
task();
}
});
}
这是一个工具方法在延迟1500毫秒后执行传进来的任务,这里的任务就是制造坦克,我们以此来模拟制造坦克花费的时间。
继续向下看制造坦克的方法:
spawnTank(TankCate tankCate) {
switch(tankCate){
case TankCate.GreenTank:
var turretSprite = Sprite('tank/t_turret_green.webp');
var bodySprite= Sprite('tank/t_body_green.webp');
double r = Random().nextDouble();
game.gTanks.add( GreenTank(game,bodySprite,turretSprite,
r < 0.5 ? Offset(100,100)
:Offset(100,game.screenSize.height*0.8)));
break;
case TankCate.SandTank:
var turretSprite = Sprite('tank/t_turret_sand.webp');
var bodySprite= Sprite('tank/t_body_sand.webp');
double r = Random().nextDouble();
game.sTanks.add( SandTank(game,bodySprite,turretSprite,
r < 0.5 ? Offset(game.screenSize.width-100,100)
:Offset(game.screenSize.width-100,game.screenSize.height*0.8)));
break;
}
isGenerating = false;
}
这个就比较简单了,看过前面文章的你肯定一眼就明白了,简单地生成新的坦克,并添加到game中的坦克list里。
检查命中
这样制造功能就完成了,现在我们看命中检测。(说明添加在注释里)
checkHit();
double hitDistance = 10;
///检查是否有tank被击中
void checkHit() {
//我们循环遍历游戏上存在的子弹
game.bullets.forEach((bullet) {
//根据子弹颜色(谁发射的)我们做出不同的处理
//这里以玩家炮弹为例
switch(bullet.bulletColor){
//玩家是否击中敌军
case BulletColor.BLUE:
//我们遍历游戏上存在的敌军
game.gTanks.forEach((gt) {
//取出他的位置,并与玩家炮弹计算出距离
Offset zone =gt.position - bullet.position;
//如果距离小于hitDistance 那就说明击中了
if(zone.distance < hitDistance){
//我们将对应坦克死亡状态置为 真
gt.isDead = true;
//同时将这颗子弹是否击中置为 真
//这两个值都将决定它们是否还会被绘制
bullet.isHit = true;
//然后我们在死亡坦克位置添加爆炸效果
addExplosion(gt.position);
}
});
game.sTanks.forEach((st) {
Offset zone =st.position - bullet.position;
if(zone.distance < hitDistance){
st.isDead = true;
bullet.isHit = true;
addExplosion(st.position);
}
});
break;
///敌军对玩家
///暂时不写了,打不过 ...
case BulletColor.GREEN:
break;
case BulletColor.SAND:
// TODO: Handle this case.
break;
}
});
}
敌方对玩家的处理方法大同小异,我就不做处理(真打不过),留给有兴趣的自己做吧。
接下来我们看一下爆炸效果:
void addExplosion(Offset position){
game.explosions.add(OrangeExplosion(game, position));
}
我们像添加子弹一样,在game的爆炸list里添加了一个爆炸,我们来具体看一下爆炸效果。
爆炸效果
首先我们创建一个component
class OrangeExplosion extends BaseComponent
然后我们梳理一下需要那些东西,爆炸的位置,爆炸范围,另外爆炸动画由5张图片组成,也就需要5个sprite,还要一个flag判断是否爆炸完毕,ok 代码如下:
final TankGame game;
final Offset position;
final List<Sprite> sprites = [];
Rect exRect;
//绘制那个sprite
int playIndex =0;
//爆炸是否完毕
bool playDone = false;
OrangeExplosion(this.game,this.position){
//爆炸范围
exRect = Rect.fromCenter(center: position,width: 30,height: 30);
//5个爆炸sprite
sprites.add(Sprite('explosion/explosion1.webp'));
sprites.add(Sprite('explosion/explosion2.webp'));
sprites.add(Sprite('explosion/explosion3.webp'));
sprites.add(Sprite('explosion/explosion4.webp'));
sprites.add(Sprite('explosion/explosion5.webp'));
}
接下来我们只需要在update调整播放index和在render中渲染对应sprite即可
@override
void render(Canvas canvas) {
if(playDone)return;
if(playIndex<5){
//渲染对应的sprite
sprites[playIndex].renderRect(canvas, exRect);
}
}
double passedTime = 0;
@override
void update(double t) {
if(playDone)return;
if(playIndex<5){
//根据我的摸索1秒 5张图片 效果不错
passedTime +=t;
playIndex = passedTime ~/ 0.2;
}else{
playDone = true;
}
}
组件创建完毕,现在我们回到game中,向前面添加坦克一样,也添加上类似的代码
tankGame中的代码
//爆炸动画
List<OrangeExplosion> explosions = [];
game.render中增加
//爆炸
explosions.forEach((element) {element.render(canvas);});
game.update中增加
//移除爆炸
explosions.removeWhere((element) => element.playDone);
//爆炸
explosions.forEach((element) {element.update(t);});
这样整个功能就完成了,运行一下看看效果吧。
收尾
web运行
如果你想在web上运行,需要切换flutter分支到dev同时开启web支持:
flutter channel dev
flutter config --enable-web
flutter run -d chrome
在main函数中添加:
bool isWeb = false;
try{
if(Platform.isAndroid || Platform.isIOS){
isWeb = false;
}else{
isWeb = true;
}
}catch(e){
isWeb = true;
}
if(! isWeb){
///设置横屏
await SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeRight,
DeviceOrientation.landscapeLeft
]);
///全面屏
await SystemChrome.setEnabledSystemUIOverlays([]);
}
这样你就可以随时在浏览器或者手机上运行这个游戏了~~~
此游戏只是练习时所写,代码结构和设计、书写等有些随意,还请见谅。
如果觉得对你有帮助的话,点个赞吧! :)
DEMO
TankCombat系列文章
Flutter&Flame——TankCombat游戏开发(一)
Flutter&Flame——TankCombat游戏开发(二)
Flutter&Flame——TankCombat游戏开发(三)
Flutter&Flame——TankCombat游戏开发(四)
网友评论