preface

I say that the cross-endpoint consistency of Flutter is not new

If you’ve ever used a game engine like Unity3D, Unreal, etc., you’ll see that these are cross-terminal, right? Don’t you just start with a canvas and finish the rest?

Now compare that to a game engine

So let's really play a game with Flutter

FLAME engine

Here I used a Game engine based on Flutter calledFLAME. Using it to develop games, you don’t really need the ones in FlutterWidgetSo there’s the question in the title

A Flutter application that does not use a FlutterWidgetIs that a Flutter?

Anyway, finish writing this article and redo the game with the widget sometime

Fundamentals of Game Engines

The introduction of Flame library

dependencies:
  flame: ^1.0. 0-rc5
Copy the code

Developing a game is based on these three things

  • Update mechanism
  • Renderer
  • Input events

In Flame, a basic game framework code is shown below

class MyGameSubClass extends Game {
  @override
  void render(Canvas canvas) {
    // TODO: implement render
  }

  @override
  void update(double t) {
    // TODO: implement update
  }
}
    
  
main() {
  runApp(
    GameWidget(
      game: MyGameSubClass(),
    )
  );
}
Copy the code

Refresh, render all have, events, in this game we add drag events

class MyGameSubClass extends BaseGame with PanDetector
Copy the code

Render graphic elements

  • The plane

The graphic material of the plane is a Sprite map, in which the first two frames are the flying state, and the last three are the explosion state

playerSpriteSheet = SpriteSheet(
  image: await images.load('player.png'),
  srcSize: playerSize,
);

// Animation of Sprite map in running state
player = SpriteAnimationComponent(
  size: playerSize,
  animation: playerSpriteSheet.createAnimation(row: 0, stepTime: 1., to: 2));Copy the code

We then use the Add method to display the graphic elements to the screen

add(player);

// Put it in x:100,y:100 to give you a sense of the coordinate system
player.x=player.y=100;
Copy the code

  • The bullet

bulletImage = await images.load('bullet.png');
Copy the code

The logic and life cycle of a bullet goes like this

  1. Generated from off-screen
  2. Move in the direction of the current coordinates of the aircraft
  3. Remove the screen to destroy
// Add a bullet
void addBullet() {
final bullet = SpriteComponent.fromImage(bulletImage, size: bulletSize);

double bulletX;
double bulletY;

// Randomly generate the rectangle around the display area
if (Random().nextBool()) {
  bulletX = Random().nextDouble() * (screenSize.width + bulletSize.x) -
      bulletSize.x;
  bulletY = Random().nextBool() ? -bulletSize.y : screenSize.height;
} else {
  bulletX = Random().nextBool() ? -bulletSize.x : screenSize.width;
  bulletY = Random().nextDouble() * (screenSize.height + bulletSize.y) -
      bulletSize.y;
}

// Give the bullet its initial coordinates
bullet.x = bulletX;
bullet.y = bulletY;

// Add to the scene
add(bullet);

// Add bullet management array to update bullet flight coordinates and collision detection later
bullets.add({
  "component": bullet,// Bullet control instance
  "speed": (1+gameTime/10) + Random().nextDouble()*3.// Flight speed
  "angle": atan2(((bulletY + bulletSize.y/2) - (player.y + playerSize.y / 2)),
      ((bulletX + bulletSize.x) - (player.x + playerSize.x / 2)))});// Vector Angle
}
Copy the code

By calling the addBullet method, we can fire a bullet at the location of the plane on the screen

To fire multiple bullets, let’s create a new function, addGroupBullet

// Add a set of bullets
void addGroupBullet() {
    int groupCount = 10+Random().nextInt(gameTime+1);
    for (int i = 0; i < groupCount; i++) { addBullet(); }}Copy the code

The refresh mechanism

In the Update method provided by Flame we need to update the positions of all bullets because bullets are not static, they need to be constantly moving.

@override
void update(double dt) {
    super.update(dt);
    
    // Iterate through the bullet array to update all bullets
    for (int i = bullets.length - 1; i >= 0; i--) {
          var bulletItem = bullets[i];
          
          // Get the bullet instance
          SpriteComponent bullet = bulletItem["component"] as SpriteComponent;

          double angle = bulletItem["angle"];
          double speed = bulletItem["speed"];

          // Let the bullet move according to the vector Angle at which it is fired
          bullet.x -= cos(angle) * speed;
          bullet.y -= sin(angle) * speed;
          
          // Destroy the bullet when it moves off-screen
          if (isNotInScreen(bullet.x, bullet.y)) {
                print("bullet removed");
                remove(bullet);
                bullets.removeAt(i);
                continue; }}}Copy the code

Are you starting to feel a little hot?

Movement of aircraft

void onPanUpdate(DragUpdateDetails details) {
    super.onPanUpdate(details);

    if(! isGameStart)return;

    // Since the coordinate of the airplane object is in the upper left corner, the movement should be offset
    player.x = details.globalPosition.dx - playerSize.x / 2;
    player.y = details.globalPosition.dy - playerSize.y / 2;
}
Copy the code

Collision detection

Every time you refresh the bullet coordinates, check to see if they coincide with the current plane coordinates, and the game is over

if (isHitPlayer(bullet.x, bullet.y)) {
    gameOver();
}
Copy the code

A simple judgment mechanism is used here, which is not accurate to the outline of the plane. First, the accuracy to the outer contour will bring more computation, but also more complex. The second is that the game experience is not good, after all, when you play on your phone, your fingers have to block most of the airplane graphics

// Aircraft and bullet collision judgment
bool isHitPlayer(double x, double y) {
    double _x = (x + bulletSize.x / 2 - (player.x + playerSize.x / 2)).abs();
    double _y = (y + bulletSize.y / 2 - (player.y + playerSize.y / 2)).abs();

    // Find the distance between the bullet and the plane
    double distance = sqrt(_x * _x + _y * _y);

    // Return true/false if the linear distance is less than the distance judged to be a collision
    if (distance <= hitDistance) return true;
    return false;
}
Copy the code

Game over

void gameOver() {
    isGameStart = false;
    
    // Cancel the timer
    if(timer! =null)timer.cancel();

    // Play plane explosion animation
    player.animation = playerSpriteSheet.createAnimation(
        row: 0, stepTime: 1., loop: false, from: 2, to: 6);

    print("game over");
}
Copy the code

Code warehouse

Github.com/ezshine/flu…

The latter

So written, from the code level, and not too much Flutter, but use of Flutter ability across the end, we can easily develop a across the Mac, Linux, Windows, Web, iOS, Android the little game. The Flutter is also a good choice if you don’t use an IDE for game development, such as Unity3D.

Pay attention to a guard

An old programmer who was a designer and wanted to write code until he was 70


Recent articles (thanks for your encouragement and support 🌹🌹🌹)

  • 🔥 made a night of animation, just to give you a better understanding of Vue3 Composition Api 940 likes
  • 🔥 update 2020, Vue simulates the effects of dragging and dropping cards for 1,114 likes
  • 🔥 super detailed, practical experience of Flutter2.0 building Web applications | technical review 14 upvotes
  • The use of 🔥 Webview | VSCode plug-in development tutorial 19 praise
  • 🔥 launched nuggets statistics dashboard function [I love Nuggets] 0.0.15 | bold New Year essay 7 likes

This article is participating in the “Nuggets 2021 Spring Recruitment Campaign”, click to see the details of the campaign