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