Using an audio library – Collision clones

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 shrinks 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 appear. Here is a typical game screen:

Using an audio library – Collision clones

Game screen collision clones

Note

For 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 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 startup script webcollision_clones.dart, where a new object of the class Board is created:

import 'package:collision_clones/collision_clones.dart';      (1)

main() {
  new Board();
}

Now, look at pubspec.yaml:

dependencies:
  browser: any
  simple_audio:
    git: https://github.com/johnmccutchan/simpleaudio.git

We see that the simple_audio library, which was briefly discussed at the end of the Adding audio to a web page section, is also imported in the libcollision_clones.dart library file along with all the other constituent packages and source files:

library collision_clones;

import 'dart:html';
import 'dart:async';
import "dart:json";
import 'dart:math';
import 'package:simple_audio/simple_audio.dart';

part 'view/game_board.dart';
part 'model/cars.dart';
part 'model/score.dart';
part 'sound/audio.dart';
part 'util/color.dart';
part 'util/random.dart';

Again, the view class is called Board (in viewgame_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 libmodelcars.dart and the Board class contains List<Car> and a variable redCar. Because they share many properties, we let them both inherit from an abstract class Vehicle:

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 color code, and the methods on the canvas context we saw in Chapter 5, Handling the 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 lower 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 test if the car referred to by this has had a collision with the red car, and in line (3), we test if 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 creates an AudioManager object (line (1) in the following code snippet) in its constructor to make a sound when it collides with another car. The red car also 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) {
    audioManager = new Audio().audioManager;            (1)
    colorCode = bigColorCode;
    width = bigWidth;
    height = bigHeight;
    canvas.document.onMouseDown.listen((MouseEvent e) {  (2)
      movable = !movable;
      if (small) {
        bigger();
      }
    });
    canvas.document.onMouseMove.listen((MouseEvent e) {  (3)
      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 (2)) 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 (3)) 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;
      audioManager.playClipFromSourceIn(0.0, 'game', 'collision'),
      colorCode = smallColorCode;
      width = smallWidth;
      height = smallHeight;
      collisionCount++;
    }
}

This plays a typical collision sound from websoundcollision.ogg. We have added an Audio class (in libsoundaudio.dart) that wraps functionality from the simple_audio library:

class Audio {
  AudioManager _audioManager;

  Audio() {
    _audioManager = new AudioManager('${demoBaseUrl()}/sound'),
    AudioSource audioSource = _audioManager.makeSource('game'),
    audioSource.positional = false;
    AudioClip collisionSound =
        _audioManager.makeClip('collision', 'collision.ogg'),
    collisionSound.load();
  }
  // code left out
  AudioManager get audioManager => _audioManager;
}

The Board class constructs a Score object, a Red Car object, and the other cars. Then displays them in displayCars() every INTERVAL milliseconds in line (1). In line (2), the score is updated, displayed, and stored in local storage every ACTIVE millisecond (see save() and load() in the class Score):

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) {                            (2)
    // 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 supporting code to keep track of the counters. That's 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 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. Also, in this variant the built-in AudioElement class is used. Compare both 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
18.217.206.112