This game shows a field of moving cars of different sizes and colors. The player controls the red car (initially at the top-left corner) through the mouse cursor. The objective is to move the red car within the time limit with as few collisions as possible. The lower the speed limit, the slower the cars. The game runs until the time limit is over (in minutes). You will lose if the number of collisions during that time is greater than the time elapsed in seconds. You win if you survive the time limit. If the red car is hit, it will shrink to a small black car. In order to revive it, move the cursor to one of the borders of the board. After a certain number of collisions, clone cars will appear. Here is a typical game screen:
This is our first example of a web app, where all the code is assembled in a collision_clones
library that resides in its own lib
folder. Because it is also stored in the packages
folder, it can be imported (line (1)
) in the start up webcollision_clones.dart
script, where a new object of the Board
class is created:
import 'package:collision_clones/collision_clones.dart'; (1) main() { new Board(); }
Now, look at pubspec.yaml
:
dependencies: browser: any
In the libcollision_clones.dart
library file, all the constituent packages and source files are imported:
library collision_clones; import 'dart:html'; import 'dart:async'; import "dart:convert"; import 'dart:math'; part 'game_board.dart'; part 'cars.dart'; part 'score.dart'; part 'util/color.dart'; part 'util/git_commands.dart'; part 'util/random.dart';
Again, the view
class is called Board
(in game_board.dart
) and its instantiation starts and animates the game. The viewable objects in this game are the cars and, specifically, the red car. They have corresponding classes in libcars.dart
and the Board
class contains List<Car>
and a redCar
variable. Because they share many properties, we let them both inherit from a Vehicle
abstract class:
abstract class Vehicle class Car extends Vehicle class RedCar extends Vehicle
The abstract
class contains all that is needed to draw a car in its constructor and the draw
method: getting the canvas context, calculating a random position (x, y) and the color code, and the methods on the canvas context we saw in Chapter 5, Handling DOM in a New Way, are put to use. The draw
method is given as follows:
draw() { context ..beginPath() ..fillStyle = colorCode ..strokeStyle = 'black' ..lineWidth = 2; roundedCorners(context, x, y, x + width, y + height, 10); context ..fill() ..stroke() ..closePath(); // wheels context ..beginPath() ..fillStyle = '#000000' ..rect(x + 12, y - 3, 14, 6) ..rect(x + width - 26, y - 3, 14, 6) ..rect(x + 12, y + height - 3, 14, 6) ..rect(x + width - 26, y + height - 3, 14, 6) ..fill() ..closePath(); }
Every Car
object has a different speed specified in its constructor: a random number that is lesser than the speed limit:
Car(canvas, speedLimit) : super(canvas) { var speedNumber = int.parse(speedLimit); dx = randomNum(speedNumber); dy = randomNum(speedNumber); }
The Car
class contains a move
method, which is called from displayCars()
in Board
, that changes the position (line (1)
), taking into account that cars must bounce from the borders (line(4)
). In line (2)
, we will test whether the car referred to by it has had a collision with the red car and, in line (3)
, we will test whether this car has had a collision with the red car:
move(RedCar redCar, List<Car> cars) { x += dx; (1) y += dy; if (redCar.big) { redCar.collision(this); (2) } for (Car car in cars) { (3) if (car != this) { car.collision(this); } } if (x > canvas.width || x < 0) dx = -dx; (4) if (y > canvas.height || y < 0) dy = -dy; }
The red car contains some Booleans to determine whether it is in its big or small state and when it is movable. The onMouseDown
and onMouseMove
events are defined on document
property of canvas, and are also there in the red car constructor:
RedCar(canvas) : super(canvas) { colorCode = bigColorCode; width = bigWidth; height = bigHeight; canvas.document.onMouseDown.listen((MouseEvent e) { (1) movable = !movable; if (small) { bigger(); } }); canvas.document.onMouseMove.listen((MouseEvent e) { (2) if (movable) { x = e.offset.x - 35; y = e.offset.y - 35; if (x > canvas.width) { bigger(); x = canvas.width - 20; } // some code left out for brevity
The first event handler (line (1)
) makes the red car movable after a click on the Play button at the beginning of the game or after a click on the Stop button. The second handler (line (2)
) makes sure the red car is contained within the board's dimensions and that it is revived at the limits of the board after a collision. When the red car collides with another car, the collision
method in RedCar
invokes smaller()
:
smaller() { if (big) { small = true; colorCode = smallColorCode; width = smallWidth; height = smallHeight; collisionCount++; } }
The Board
class constructs a Score
object, a Red Car
object, and the other cars. In the code following line (1)
, the score will be updated, displayed, and stored in the local storage every 1,000 milliseconds (see save()
and load()
in the Score
class):
Board() { var bestScore = new Score(); // code left out redCar = new RedCar(canvas); cars = new List(); for (var i = 0; i < carCount; i++) { var car = new Car(canvas, score.currentSpeedLimit); cars.add(car); } // code left out // active play time: new Timer.periodic(const Duration(milliseconds: 1000), (t) { if (!stopped && redCar.big) { (1) // code left out }
Here is a portion of the displayCars()
code:
displayCars() { // code left out clear(); // nested function ! for (var i = 0; i <cars.length; i++) { cars[i].move(redCar, cars); cars[i].draw(); } redCar.draw(); // code left out
The Score
class also contains a supporting code to keep track of the counters. This is it, you can find some suggestions for change or improvement in doc odo.txt
.
An important improvement can still be made. The Car
class contains both the state information (position, color, and so on) as well as the way to draw itself. The model should not concern itself with the user interface. This separation is achieved in the car_collisions
variant, where everything related to drawing is moved to the view
class Board
. Compare both the versions and see how the separation makes the project much easier to understand and maintain.
3.145.70.170