TankCombat series of articles

If you don’t know about Flame, check it out here:

A look at Flutter’s performance in game development and its cross-platform advantages

Flutter&Flame — TankCombat game development (part 1)

Flutter&Flame — TankCombat game development part 2

Flutter&Flame — TankCombat game development part 3

Flutter&Flame — TankCombat game development (4)

rendering

Pretty good, let me add it to give you an overall impression of what you are doing 🙂

starts

Back to where you are, we started to complete the tank control function this time

Positive and negative schematic diagram of Angle PI

The X-axis is going from right to left, positive negative and the Y-axis is going from top to bottom, positive negative and the range of PI is (-pi, PI).Copy the code

Tank code structure

class Tank extends BaseComponent{ final int tankId = 666; final TankGame game; Sprite bodySprite,turretSprite; Tank(this.game,{this.position}){ turretSprite = Sprite('tank/t_turret_blue.webp'); bodySprite= Sprite('tank/t_body_blue.webp'); } // Offset position; // double bodyAngle = 0; // Double turretAngle = 0; // Double targetBodyAngle; // Double targetTurretAngle; //tank alive bool isDead = false; Final double ration = 0.7; @override void render(Canvas canvas) { if(isDead) return; drawBody(canvas); } @override void update(double t) { rotateTurret(t); moveTank(t); }}Copy the code

Control of the tank

We added several variables to the Tank to control and calculate the Angle of the Tank and turret

// double bodyAngle = 0; // Double turretAngle = 0; // Double targetBodyAngle; // Double targetTurretAngle; //tank alive bool isDead = false;Copy the code

Then we updated the drawBody method to add a rotation drawing for the tank’s body and turret,

Void drawBody(Canvas Canvas) {// Save the state canvas.save(); canvas.translate(position.dx, position.dy); Canvas.rotate (bodyAngle); RenderRect (canvas, rect. fromLTWH(-20*ration, -15*ration, 38*ration, 32*ration)); // Canvas. Rotate (turretAngle); // renderRect(canvas, rect. fromLTWH(-1, -2*ration, 22*ration, 6*ration)); canvas.restore(); }Copy the code

In the meantime, the update method that was previously empty comes in handy, and we add three methods to it:

rotateBody(t); // Rotate the body rotateTurret(t); // moveTank(t); // move the tank note that this t is a double, representing time increment. for example, if it is 60fps, then this t is theoretically equal to 0.01666Copy the code

The source code is as follows:

@override void update(double t) { rotateTurret(t); moveTank(t); }Copy the code

These three methods are long and tedious, so I’ll add them in the notes to make them easier to read

The rotating body

Void rotateBody(double t) {void rotateBody(double t) { The final double rotationRate = PI * t; the final double rotationRate = PI * t; Before doing the rotation, make sure that the target Angle cannot be empty if (targetBodyAngle! If (bodyAngle < targetBodyAngle) {if (bodyAngle < targetBodyAngle) {if (bodyAngle < targetBodyAngle) {if (bodyAngle < targetBodyAngle) ((targetBodyAngle).abs() > PI) {bodyAngle = bodyangle-rotationRate; If (bodyAngle < -pi) {bodyAngle += PI * 2; if (bodyAngle < -pi) {bodyAngle += PI * 2; }} else {// < bodyAngle = bodyAngle + rotationRate; if (bodyAngle > targetBodyAngle) { bodyAngle = targetBodyAngle; }}} // The body Angle is greater than the target Angle // the same, If (bodyAngle > targetBodyAngle) {if ((targetBodyAngle - bodyAngle).abs() > PI) {bodyAngle = bodyAngle + rotationRate; if (bodyAngle > pi) { bodyAngle -= pi * 2; } } else { bodyAngle = bodyAngle - rotationRate; if (bodyAngle < targetBodyAngle) { bodyAngle = targetBodyAngle; } } } } }Copy the code

Rotating turret

void rotateTurret(double t) { final double rotationRate = pi * t; if(targetTurretAngle ! = null){// The principle here is the same as rotating the hull, the only difference is that our turret target Angle, Instead of using the variable targetTurretAngle directly, use targetTurretAngle - bodyAngle to calculate the actual target Angle // this is because, in the drawBody() method, we rotated the body first, So I'm going to subtract double localTargetTurretAngle = targetTurretAngle - bodyAngle; if(turretAngle < localTargetTurretAngle){ if((localTargetTurretAngle -turretAngle).abs() > pi){ turretAngle = turretAngle - rotationRate; If (turretAngle < -pi, PI){turretAngle += PI *2; if(turretAngle += PI *2; } }else{ turretAngle = turretAngle + rotationRate; if(turretAngle > localTargetTurretAngle){ turretAngle = localTargetTurretAngle; } } } if(turretAngle > localTargetTurretAngle){ if((localTargetTurretAngle - turretAngle).abs() > pi){ turretAngle = turretAngle + rotationRate; if(turretAngle > pi){ turretAngle -= pi*2; } }else{ turretAngle = turretAngle - rotationRate; if(turretAngle < localTargetTurretAngle){ turretAngle = localTargetTurretAngle; } } } } }Copy the code

Mobile tanks

100 void moveTank(double t) {if(targetBodyAngle! = null){if(bodyAngle == targetBodyAngle){//tank position = position + Offset. Position = position + offset.fromdirection (bodyAngle,50*t); }}}Copy the code

Now that the tank control system is complete, the next step is to connect the rocker to the tank.

Combination of start

Remember the code in the mian function?

main()

final TankGame tankGame = TankGame(); runApp(Directionality(textDirection: TextDirection.ltr, child: Stack( children: [ tankGame.widget, Column( children: [Spacer(), // Launch button Row(children: [SizedBox(width: 48), FireButton(onTap: tankGame.onFireButtonTap, ), Spacer(), FireButton( onTap: tankGame.onFireButtonTap, ), SizedBox(width: // Children: [SizedBox(width: 48), JoyStick(onChange: (Offset delta)=>tankGame.onLeftJoypadChange(delta), ), Spacer(), JoyStick( onChange: (Offset delta)=>tankGame.onRightJoypadChange(delta), ), SizedBox(width: 48) ], ), SizedBox(height: 24) ], ), ], )));Copy the code

You can see that we passed the offset delta out of the joystick callback through game’s two methods

OnLeftJoypadChange (delta)// Body onRightJoypadChange(delta)// turretCopy the code

Passed to Game to update the game Component

TankGame

We’re going to update the tankgame code a little bit, starting with the addition of a player tank

Tank tank;
Copy the code

After adding two methods to receive the joystick’s delta, we pass the direction of the delta to tank

void onLeftJoypadChange(Offset offset){ if(offset == Offset.zero){ tank.targetBodyAngle = null; }else{// get the targetBodyAngle tank.targetBodyAngle = offset.direction; // Range (PI,-pi)}} void onRightJoypadChange(Offset Offset) {if (Offset == Offset. Zero) {tank.targetTurreTangle = null; } else {// Get turret target Angle tank.targetTurretAngle = offset. Direction; }}Copy the code

Next, let’s instantiate tank in the resize() method, which is normally only called when the screen size changes

If (tank = = null) {tank = tank (/ / birth point in the middle of the screen, this position: Offset (screenSize. Width / 2, screenSize height / 2),); }Copy the code

Ok, so far the joystick controls are connected to the tank, but we can’t see the controls because they are not rendered or updated as the screen refreshes. All we need to do is call the corresponding method of tank in the Render and Update of the game.

@override void render(Canvas canvas) { if(screenSize == null)return; // Draw lawn bg.render(canvas); //tank tank.render(canvas); }Copy the code
  @override
  void update(double t) {
    if(screenSize == null)return;
    tank.update(t);
    }
Copy the code

Now that the entire tank control system is complete, you can run and move the tank, in the next chapter we will design shells and enemy tanks, thank you for reading. 🙂

DEMO

Tanks war