The Collision Clones game

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:

The Collision Clones game

Collision Clones game screen

Note

For the code file of this section, refer to chapter 7collision_clones and chapter 7car_collisions in the code bundle. You can also get them from GitHub using the following commands:

git clone git://github.com/dzenanr/collision_clones.git
git clone git://github.com/dzenanr/car_collisions.git

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.70.170