game_demo_node.dart 7.03 KB
Newer Older
1 2 3 4 5
part of game;

final double _gameSizeWidth = 320.0;
double _gameSizeHeight = 320.0;

6 7 8
final double _chunkSpacing = 640.0;
final int _chunksPerLevel = 5;

9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
final bool _drawDebug = false;

class GameDemoNode extends NodeWithSize {

  GameDemoNode(
    this._images,
    this._spritesGame,
    this._spritesUI,
    this._sounds,
    this._gameOverCallback
  ): super(new Size(320.0, 320.0)) {
    // Add background
    _background = new RepeatedImage(_images["assets/starfield.png"]);
    addChild(_background);

    // Create starfield
    _starField = new StarField(_spritesGame, 200);
    addChild(_starField);

    // Add nebula
    _nebula = new RepeatedImage(_images["assets/nebula.png"], sky.TransferMode.plus);
    addChild(_nebula);

    // Setup game screen, it will always be anchored to the bottom of the screen
    _gameScreen = new Node();
    addChild(_gameScreen);

    // Setup the level and add it to the screen, the level is the node where
    // all our game objects live. It is moved to scroll the game
    _level = new Level();
    _gameScreen.addChild(_level);

41
    // Add heads up display
42
    _playerState = new PlayerState(_spritesUI, _spritesGame);
43 44 45
    addChild(_playerState);

    _objectFactory = new GameObjectFactory(_spritesGame, _sounds, _level, _playerState);
46 47

    _level.ship = new Ship(_objectFactory);
48
    _level.ship.setupActions();
49 50 51 52 53 54
    _level.addChild(_level.ship);

    // Add the joystick
    _joystick = new VirtualJoystick();
    _gameScreen.addChild(_joystick);

55
    // Add initial game objects
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
    addObjects();
  }

  // Resources
  ImageMap _images;
  Map<String, SoundEffect> _sounds;
  SpriteSheet _spritesGame;
  SpriteSheet _spritesUI;

  // Sounds
  SoundEffectPlayer _effectPlayer = SoundEffectPlayer.sharedInstance();

  // Callback
  Function _gameOverCallback;

  // Game screen nodes
  Node _gameScreen;
  VirtualJoystick _joystick;

  GameObjectFactory _objectFactory;
  Level _level;
  StarField _starField;
  RepeatedImage _background;
  RepeatedImage _nebula;
80
  PlayerState _playerState;
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114

  // Game properties
  double _scrollSpeed = 2.0;
  double _scroll = 0.0;

  int _framesToFire = 0;
  int _framesBetweenShots = 20;

  bool _gameOver = false;

  void spriteBoxPerformedLayout() {
    _gameSizeHeight = spriteBox.visibleArea.height;
    _gameScreen.position = new Point(0.0, _gameSizeHeight);
  }

  void update(double dt) {
    // Scroll the level
    _scroll = _level.scroll(_scrollSpeed);
    _starField.move(0.0, _scrollSpeed);

    _background.move(_scrollSpeed * 0.1);
    _nebula.move(_scrollSpeed);

    // Add objects
    addObjects();

    // Move the ship
    if (!_gameOver) {
      _level.ship.applyThrust(_joystick.value, _scroll);
    }

    // Add shots
    if (_framesToFire == 0 && _joystick.isDown && !_gameOver) {
      fire();
115
      _framesToFire = (_playerState.speedLaserActive) ? _framesBetweenShots ~/ 2 : _framesBetweenShots;
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
    }
    if (_framesToFire > 0) _framesToFire--;

    // Move game objects
    for (Node node in _level.children) {
      if (node is GameObject) {
        node.move();
      }
    }

    // Remove offscreen game objects
    for (int i = _level.children.length - 1; i >= 0; i--) {
      Node node = _level.children[i];
      if (node is GameObject) {
        node.removeIfOffscreen(_scroll);
      }
    }

    if (_gameOver) return;

    // Check for collisions between lasers and objects that can take damage
    List<Laser> lasers = [];
    for (Node node in _level.children) {
      if (node is Laser) lasers.add(node);
    }

    List<GameObject> damageables = [];
    for (Node node in _level.children) {
      if (node is GameObject && node.canBeDamaged) damageables.add(node);
    }

    for (Laser laser in lasers) {
      for (GameObject damageable in damageables) {
        if (laser.collidingWith(damageable)) {
          // Hit something that can take damage
151
          damageable.addDamage(laser.impact);
152 153 154 155 156 157 158 159 160 161
          laser.destroy();
        }
      }
    }

    // Check for collsions between ship and objects that can damage the ship
    List<Node> nodes = new List.from(_level.children);
    for (Node node in nodes) {
      if (node is GameObject && node.canDamageShip) {
        if (node.collidingWith(_level.ship)) {
162 163 164 165 166 167 168
          if (_playerState.shieldActive) {
            // Hit, but saved by the shield!
            node.destroy();
          } else {
            // The ship was hit :(
            killShip();
          }
169
        }
170 171 172
      } else if (node is GameObject && node.canBeCollected) {
        if (node.collidingWith(_level.ship)) {
          // The ship ran over something collectable
173
          node.collect();
174
        }
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
      }
    }
  }

  int _chunk = 0;

  void addObjects() {

    while (_scroll + _chunkSpacing >= _chunk * _chunkSpacing) {
      addLevelChunk(
        _chunk,
        -_chunk * _chunkSpacing - _chunkSpacing);

      _chunk += 1;
    }
  }

  void addLevelChunk(int chunk, double yPos) {
    if (chunk == 0) {
      // Leave the first chunk empty
      return;
    }

198 199 200 201 202 203 204 205
    chunk -= 1;

    int level = chunk ~/ _chunksPerLevel;
    int part = chunk % _chunksPerLevel;

    if (part == 0) {
      _objectFactory.addAsteroids(10 + level * 4, yPos, 0.0 + (level * 0.2).clamp(0.0, 0.7));
    } else if  (part == 1) {
206
      _objectFactory.addEnemyScoutSwarm(4 + level * 2, yPos);
207 208 209
    } else if (part == 2) {
      _objectFactory.addAsteroids(10 + level * 4, yPos, 0.0 + (level * 0.2).clamp(0.0, 0.7));
    } else if (part == 3) {
210
      _objectFactory.addEnemyDestroyerSwarm(2 + level, yPos);
211 212
    } else if (part == 4) {
      _objectFactory.addAsteroids(10 + level * 4, yPos, 0.0 + (level * 0.2).clamp(0.0, 0.7));
213 214 215 216
    }
  }

  void fire() {
217
    Laser shot0 = new Laser(_objectFactory, -90.0);
218 219 220
    shot0.position = _level.ship.position + new Offset(17.0, -10.0);
    _level.addChild(shot0);

221
    Laser shot1 = new Laser(_objectFactory, -90.0);
222 223 224
    shot1.position = _level.ship.position + new Offset(-17.0, -10.0);
    _level.addChild(shot1);

225 226 227 228 229 230 231 232 233 234
    if (_playerState.sideLaserActive) {
      Laser shot2 = new Laser(_objectFactory, 0.0);
      shot2.position = _level.ship.position + new Offset(17.0, -10.0);
      _level.addChild(shot2);

      Laser shot3 = new Laser(_objectFactory, 180.0);
      shot3.position = _level.ship.position + new Offset(-17.0, -10.0);
      _level.addChild(shot3);
    }

235 236 237 238 239 240 241 242 243 244
    _effectPlayer.play(_sounds["laser"]);
  }

  void killShip() {
    // Hide ship
    _level.ship.visible = false;

    _effectPlayer.play(_sounds["explosion"]);

    // Add explosion
245
    ExplosionBig explo = new ExplosionBig(_spritesGame);
246 247 248 249 250 251 252 253 254 255 256 257
    explo.scale = 1.5;
    explo.position = _level.ship.position;
    _level.addChild(explo);

    // Add flash
    Flash flash = new Flash(size, 1.0);
    addChild(flash);

    // Set the state to game over
    _gameOver = true;

    // Return to main scene and report the score back in 2 seconds
258
    new Timer(new Duration(seconds: 2), () { _gameOverCallback(_playerState.score); });
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
  }
}

class Level extends Node {
  Level() {
    position = new Point(160.0, 0.0);
  }

  Ship ship;

  double scroll(double scrollSpeed) {
    position += new Offset(0.0, scrollSpeed);
    return position.y;
  }
}