review

Yesterday afternoon the author has completed the background animation loop play. In the evening, I asked stackOverflow questions about issues I found during development. General content of the question:

How do I stretch and tile a problem link from a smaller image in the Canvas

Two valid answers were received to this question

  • Canvas.drawImageRect()
  • paintImage()

I have been tested by the author



The visuals are similar, but paintImage’s performance is a serious drain on GPU resources. Looking at paintImage’s source code, it turns out that the way this function is implemented is by calling drawImageRect, which is a problem. Those of you who are interested can take a closer look. Discussion together can also be of great help to the optimization of Flutter performance.

void paintImage(
  ...
  if (centerSlice == null) {
    for (Rect tileRect in _generateImageTileRects(rect, destinationRect, repeat))
      canvas.drawImageRect(image, sourceRect, tileRect, paint);
  } else {
    for (Rect tileRect in _generateImageTileRects(rect, destinationRect, repeat))
      canvas.drawImageNine(image, centerSlice, tileRect, paint);
  }
  if (needSave)
    canvas.restore();
}
Copy the code

start

The main task of this article is to add the aircraft we control on the drawing board, which can operate the aircraft to move.

Draw a plane

Considering we’re going to be drawing players’ planes in the future. We first abstracted a Plan class to facilitate our future development. Dart is a new file called plan. Dart. Define his method.

abstract class Plan {
  void init() {}
  void moveTo(double x, double y) {}
  void destroy() {}
  void paint(Canvas canvas, Size size) async {}
}
Copy the code

Next we can define the MainHero of our main character. Dart, reference and inherit the Plan, and implement the methods defined above. The basic methods and attributes are as follows:

Enum PlanStatus {stay, move, die} class MainHero extends Plan {// double x = 100.0; // Double y = 100.0; // Double width = 132.0; // Double height = 160.0; ui.Image image; @override void init() async { // TODO: implement init image = await Utils.getImage('assets/images/hero.png');
  }
  @override
  void moveTo(double x, double y) {
    // TODO: implement moveTo
  }
  @override
  void destroy() { // TODO: implement destroy super.destroy(); } @override void paint(Canvas canvas, Size size) { Rect paintArea = Offset(100, 100) & Size(width, height); Rect planArea = Offset(0, 0) & Size(image.width, image.height) canvas.save(); Canvas. Translate (-width / 2, -height / 2); // Move the canvas to the top left and move the drawing point to the center of the plane. canvas.drawImageRect(image, planArea, paintArea, new Paint()); frameIndex++; canvas.restore(); }}Copy the code

In this time, we use drawImageRect for our drawing interface, and use the method reference document. We create an instance of the protagonist in the entry file of the game to complete the initialization, which is similar to the logic of the drawing. The specific details are similar to the background image, so we won’t go into details.

Without further ado, go straight to the renderings

Dynamic effects of aircraft

In the airplane game we played. The plane we control usually has a dynamic effect, which will enhance the visual experience of players. The author found a copy of the dynamic effect of the game plane from the Internet as follows:

gif
flutter
gif
The plane moved

Next, we need to take this image and modify our MainHero to display it dynamically on our screen. We add two properties and a method to it, and with every screen refresh we increment the frameIndex by one. When we reach the last frame, we reset the frameIndex to zero so that our plane can move

Int frameNumber = 2; Int frameIndex = 0; Rect getPlanAreaSize(int _frameIndex) {double perFrameWidth = image.width/frameNumber; double offsetX = perFrameWidth * _frameIndex; double offsetY = 0;if (offsetX >= image.width) {
  frameIndex = 0;
  return this.getPlanAreaSize(0);
}
return Offset(offsetX, offsetY) & Size(66.0, 80.0);
}
Copy the code

The renderings are as follows:

Control of aircraft

The idea of controlling the flight of the airplane is that we dynamically update the coordinate points of (x,y) drawn by the airplane by monitoring the movement of the screen and fingers, so as to achieve the desired effect.

In the Documentation of Flutter, we find the GestureDetector interface. In the Enter entry, we surround our CustomPaint palette control with the GestureDetector control. Our next step is to use the GestureDetector control to capture the user’s drag events. And update our MainHero coordinate points.

The implementation is as follows:

 Widget build(BuildContext context) build() {...returnGestureDetector( child: CustomPaint( painter: MainPainter(background: background, hero: hero) ), onPanStart: (DragDownDetails) { hero.moveTo(DragDownDetails.globalPosition.dx, DragDownDetails.globalPosition.dy); }, onPanUpdate: (DragDownDetails) { hero.moveTo(DragDownDetails.globalPosition.dx, DragDownDetails.globalPosition.dy); })}Copy the code

Next we will modify our MainHero class to improve its moveTo method. During the game, we drag the finger, the plane can not flash in the way of flash, it needs to move a little bit to the position we want. We define several properties and methods in MainHero

// double _x; double _y; double speed = 20; // Dynamically calculate the new coordinate point voidcalculatePosition() {}
Copy the code

Here we use a graph to show the relationship between the old and new coordinate points:

calculatePosition

 void moveTo(double x, double y) {
    // TODO: implement moveTo
    this._x = x;
    this._y = y;
 }
void calculatePosition() { Point p1 = Point(x, y); Point p2 = Point(_x, _y); double distance = p1.distanceTo(p2); double flyRadian = acos(((y - _y) / distance).abs()); // Determine the displacement directionif (_x < x) {
      x -= speed * sin(flyRadian);
    } else {
      x += speed * sin(flyRadian);
    }
    if (_y < y) {
      y -= speed * cos(flyRadian);
    } else{ y += speed * cos(flyRadian); }}Copy the code

Through the above reform, we test found that the movement to the end, the aircraft will be shaking in the end, the problems found, is our calculatePosition method, the calculation of x value, will be the last time calculation, produce a | x – _x | > 0 results, So the plane will bounce back and forth between the coordinates. To avoid this, let’s rework the calculatePosition method again

MainHero

// Stay unmanned, free flight // MOVE controlled, flight motion status // DIE dead enum PlanStatus {stay, move, die} voidcalculatePosition() {... // Avoid jitter and make a judgment. distanceif (distance < 10) {
      x = _x;
      y = _y;
      status = PlanStatus.stay;
      returnnull; Void paint(Canvas Canvas, Size Size) {Canvas Canvas, Size Size) {Canvas Canvas, Size Size;if(status == PlanStatus.move) { calculatePosition(); }}Copy the code

Through the above transformation, let’s take a look at the final effect.

conclusion

The second part, the completion of the work, the content may have errors, please point out that I will correct, the rest of the logic. I will fill in the details bit by bit. If you find this article helpful, I hope you like it