Flutter&Flame——TankCombat游戏开发(四)

2,302 阅读3分钟

TankCombat系列文章

如果你还不了解Flame可以看这里:

见微知著,Flutter在游戏开发的表现及跨平台带来的优势

Flutter&Flame——TankCombat游戏开发(一)

Flutter&Flame——TankCombat游戏开发(二)

Flutter&Flame——TankCombat游戏开发(三)

Flutter&Flame——TankCombat游戏开发(四)

效果图

蛮好看的,我再加一下,让大家整体有个印象自己在做什么 :)

思考

在开工之前,我们先要思考一下,游戏中的电脑(坦克/炮弹),它们并不知道该往哪里走、炮塔怎么转以及何时该开火,这一切都是我们通过代码告诉它们该如何‘表现’的,那么当两台电脑需要交互的时候,就产生了谁开的炮,谁原地爆炸,谁又重生在何时何地的问题...

这时我们就需要一个观察者,并将处理逻辑通过代码告诉它,让它替我们安排诸事。

开工

首先我们给它起名叫 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在游戏开发的表现及跨平台带来的优势

Flutter&Flame——TankCombat游戏开发(一)

Flutter&Flame——TankCombat游戏开发(二)

Flutter&Flame——TankCombat游戏开发(三)

Flutter&Flame——TankCombat游戏开发(四)