commit
acccd438e5
57
examples/game/lib/action_spline.dart
Normal file
57
examples/game/lib/action_spline.dart
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
part of sprites;
|
||||||
|
|
||||||
|
Point _cardinalSplineAt(Point p0, Point p1, Point p2, Point p3, double tension, double t) {
|
||||||
|
double t2 = t * t;
|
||||||
|
double t3 = t2 * t;
|
||||||
|
|
||||||
|
double s = (1.0 - tension) / 2.0;
|
||||||
|
|
||||||
|
double b1 = s * ((-t3 + (2.0 * t2)) - t);
|
||||||
|
double b2 = s * (-t3 + t2) + (2.0 * t3 - 3.0 * t2 + 1.0);
|
||||||
|
double b3 = s * (t3 - 2.0 * t2 + t) + (-2.0 * t3 + 3.0 * t2);
|
||||||
|
double b4 = s * (t3 - t2);
|
||||||
|
|
||||||
|
double x = p0.x * b1 + p1.x * b2 + p2.x * b3 + p3.x * b4;
|
||||||
|
double y = p0.y * b1 + p1.y * b2 + p2.y * b3 + p3.y * b4;
|
||||||
|
|
||||||
|
return new Point(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ActionSpline extends ActionInterval {
|
||||||
|
ActionSpline(this.setter, this.points, double duration, [Curve curve]) : super(duration, curve) {
|
||||||
|
_dt = 1.0 / (points.length - 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Function setter;
|
||||||
|
final List<Point> points;
|
||||||
|
|
||||||
|
double _dt;
|
||||||
|
|
||||||
|
void update(double t) {
|
||||||
|
double tension = 0.5;
|
||||||
|
|
||||||
|
int p;
|
||||||
|
double lt;
|
||||||
|
|
||||||
|
if (t < 0.0) t = 0.0;
|
||||||
|
|
||||||
|
if (t >= 1.0) {
|
||||||
|
p = points.length - 1;
|
||||||
|
lt = 1.0;
|
||||||
|
} else {
|
||||||
|
p = (t / _dt).floor();
|
||||||
|
lt = (t - _dt * p) / _dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
Point p0 = points[(p - 1).clamp(0, points.length - 1)];
|
||||||
|
Point p1 = points[(p + 0).clamp(0, points.length - 1)];
|
||||||
|
Point p2 = points[(p + 1).clamp(0, points.length - 1)];
|
||||||
|
Point p3 = points[(p + 2).clamp(0, points.length - 1)];
|
||||||
|
|
||||||
|
Point newPos = _cardinalSplineAt(p0, p1, p2, p3, tension, lt);
|
||||||
|
|
||||||
|
//print("newPos: $newPos");
|
||||||
|
|
||||||
|
setter(newPos);
|
||||||
|
}
|
||||||
|
}
|
42
examples/game/lib/constraint.dart
Normal file
42
examples/game/lib/constraint.dart
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
part of sprites;
|
||||||
|
|
||||||
|
abstract class Constraint {
|
||||||
|
void preUpdate(Node node, double dt) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void constrain(Node node, double dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
double _dampenRotation(double src, double dst, double dampening) {
|
||||||
|
double delta = dst - src;
|
||||||
|
while (delta > 180.0) delta -= 360;
|
||||||
|
while (delta < -180) delta += 360;
|
||||||
|
delta *= dampening;
|
||||||
|
|
||||||
|
return src + delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConstraintRotationToMovement {
|
||||||
|
ConstraintRotationToMovement([this.dampening]);
|
||||||
|
final double dampening;
|
||||||
|
|
||||||
|
Point _lastPosition;
|
||||||
|
|
||||||
|
void preUpdate(Node node, double dt) {
|
||||||
|
_lastPosition = node.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
void constrain(Node node, double dt) {
|
||||||
|
assert(_lastPosition != null);
|
||||||
|
if (_lastPosition == node.position) return;
|
||||||
|
|
||||||
|
// Get the target angle
|
||||||
|
Offset offset = node.position - _lastPosition;
|
||||||
|
double target = degrees(GameMath.atan2(offset.dy, offset.dx));
|
||||||
|
|
||||||
|
if (dampening == null)
|
||||||
|
node.rotation = target;
|
||||||
|
else
|
||||||
|
node.rotation = _dampenRotation(node.rotation, target, dampening);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
library game;
|
library game;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
import 'dart:sky' as sky;
|
import 'dart:sky' as sky;
|
||||||
import 'dart:math' as Math;
|
import 'dart:math' as Math;
|
||||||
import 'sprites.dart';
|
import 'sprites.dart';
|
||||||
@ -10,4 +11,4 @@ import 'package:sky/widgets/navigator.dart';
|
|||||||
import 'package:sky/animation/curves.dart';
|
import 'package:sky/animation/curves.dart';
|
||||||
import 'package:vector_math/vector_math_64.dart';
|
import 'package:vector_math/vector_math_64.dart';
|
||||||
|
|
||||||
part 'game_demo_world.dart';
|
part 'game_demo_node.dart';
|
||||||
|
784
examples/game/lib/game_demo_node.dart
Normal file
784
examples/game/lib/game_demo_node.dart
Normal file
@ -0,0 +1,784 @@
|
|||||||
|
part of game;
|
||||||
|
|
||||||
|
final double _gameSizeWidth = 320.0;
|
||||||
|
double _gameSizeHeight = 320.0;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
_objectFactory = new GameObjectFactory(_spritesGame, _sounds, _level);
|
||||||
|
|
||||||
|
_level.ship = new Ship(_objectFactory);
|
||||||
|
_level.addChild(_level.ship);
|
||||||
|
|
||||||
|
// Add the joystick
|
||||||
|
_joystick = new VirtualJoystick();
|
||||||
|
_gameScreen.addChild(_joystick);
|
||||||
|
|
||||||
|
// Add HUD
|
||||||
|
_hud = new Hud(_spritesUI);
|
||||||
|
addChild(_hud);
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
Hud _hud;
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
_framesToFire = _framesBetweenShots;
|
||||||
|
}
|
||||||
|
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
|
||||||
|
_hud.score += damageable.addDamage(laser.impact);
|
||||||
|
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)) {
|
||||||
|
// The ship was hit :(
|
||||||
|
killShip();
|
||||||
|
_level.ship.visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int _chunk = 0;
|
||||||
|
double _chunkSpacing = 640.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;
|
||||||
|
} else if (chunk == 1) {
|
||||||
|
addLevelAsteroids(10, yPos, 0.0);
|
||||||
|
} else {
|
||||||
|
addLevelAsteroids(9 + chunk, yPos, 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addLevelAsteroids(int numAsteroids, double yPos, double distribution) {
|
||||||
|
for (int i = 0; i < numAsteroids; i++) {
|
||||||
|
GameObjectType type = (randomDouble() < distribution) ? GameObjectType.asteroidBig : GameObjectType.asteroidSmall;
|
||||||
|
Point pos = new Point(randomSignedDouble() * 160.0,
|
||||||
|
yPos + _chunkSpacing * randomDouble());
|
||||||
|
_objectFactory.addGameObject(type, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fire() {
|
||||||
|
Laser shot0 = new Laser(_objectFactory);
|
||||||
|
shot0.position = _level.ship.position + new Offset(17.0, -10.0);
|
||||||
|
_level.addChild(shot0);
|
||||||
|
|
||||||
|
Laser shot1 = new Laser(_objectFactory);
|
||||||
|
shot1.position = _level.ship.position + new Offset(-17.0, -10.0);
|
||||||
|
_level.addChild(shot1);
|
||||||
|
|
||||||
|
_effectPlayer.play(_sounds["laser"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void killShip() {
|
||||||
|
// Hide ship
|
||||||
|
_level.ship.visible = false;
|
||||||
|
|
||||||
|
_effectPlayer.play(_sounds["explosion"]);
|
||||||
|
|
||||||
|
// Add explosion
|
||||||
|
Explosion explo = new Explosion(_spritesGame);
|
||||||
|
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
|
||||||
|
new Timer(new Duration(seconds: 2), () { _gameOverCallback(_hud.score); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class VirtualJoystick extends NodeWithSize {
|
||||||
|
VirtualJoystick() : super(new Size(160.0, 160.0)) {
|
||||||
|
userInteractionEnabled = true;
|
||||||
|
handleMultiplePointers = false;
|
||||||
|
position = new Point(160.0, -20.0);
|
||||||
|
pivot = new Point(0.5, 1.0);
|
||||||
|
_center = new Point(size.width / 2.0, size.height / 2.0);
|
||||||
|
_handlePos = _center;
|
||||||
|
|
||||||
|
_paintHandle = new Paint()
|
||||||
|
..color=new Color(0xffffffff);
|
||||||
|
_paintControl = new Paint()
|
||||||
|
..color=new Color(0xffffffff)
|
||||||
|
..strokeWidth = 1.0
|
||||||
|
..setStyle(sky.PaintingStyle.stroke);
|
||||||
|
}
|
||||||
|
|
||||||
|
Point value = Point.origin;
|
||||||
|
|
||||||
|
bool _isDown = false;
|
||||||
|
bool get isDown => _isDown;
|
||||||
|
|
||||||
|
Point _pointerDownAt;
|
||||||
|
Point _center;
|
||||||
|
Point _handlePos;
|
||||||
|
|
||||||
|
Paint _paintHandle;
|
||||||
|
Paint _paintControl;
|
||||||
|
|
||||||
|
bool handleEvent(SpriteBoxEvent event) {
|
||||||
|
if (event.type == "pointerdown") {
|
||||||
|
_pointerDownAt = event.boxPosition;
|
||||||
|
actions.stopAll();
|
||||||
|
_isDown = true;
|
||||||
|
}
|
||||||
|
else if (event.type == "pointerup" || event.type == "pointercancel") {
|
||||||
|
_pointerDownAt = null;
|
||||||
|
value = Point.origin;
|
||||||
|
ActionTween moveToCenter = new ActionTween((a) => _handlePos = a, _handlePos, _center, 0.4, elasticOut);
|
||||||
|
actions.run(moveToCenter);
|
||||||
|
_isDown = false;
|
||||||
|
} else if (event.type == "pointermove") {
|
||||||
|
Offset movedDist = event.boxPosition - _pointerDownAt;
|
||||||
|
|
||||||
|
value = new Point(
|
||||||
|
(movedDist.dx / 80.0).clamp(-1.0, 1.0),
|
||||||
|
(movedDist.dy / 80.0).clamp(-1.0, 1.0));
|
||||||
|
|
||||||
|
_handlePos = _center + new Offset(value.x * 40.0, value.y * 40.0);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void paint(PaintingCanvas canvas) {
|
||||||
|
applyTransformForPivot(canvas);
|
||||||
|
canvas.drawCircle(_handlePos, 25.0, _paintHandle);
|
||||||
|
canvas.drawCircle(_center, 40.0, _paintControl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class GameObject extends Node {
|
||||||
|
double radius = 0.0;
|
||||||
|
double removeLimit = 1280.0;
|
||||||
|
bool canDamageShip = true;
|
||||||
|
bool canBeDamaged = true;
|
||||||
|
double maxDamage = 3.0;
|
||||||
|
double damage = 0.0;
|
||||||
|
|
||||||
|
Paint _paintDebug = new Paint()
|
||||||
|
..color=new Color(0xffff0000)
|
||||||
|
..strokeWidth = 1.0
|
||||||
|
..setStyle(sky.PaintingStyle.stroke);
|
||||||
|
|
||||||
|
bool collidingWith(GameObject obj) {
|
||||||
|
return (GameMath.pointQuickDist(position, obj.position)
|
||||||
|
< radius + obj.radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
void move() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeIfOffscreen(double scroll) {
|
||||||
|
;
|
||||||
|
if (-position.y > scroll + removeLimit ||
|
||||||
|
-position.y < scroll - 50.0) {
|
||||||
|
removeFromParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy() {
|
||||||
|
if (parent != null) {
|
||||||
|
Explosion explo = createExplosion();
|
||||||
|
if (explo != null) {
|
||||||
|
explo.position = position;
|
||||||
|
parent.addChild(explo);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeFromParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int addDamage(double d) {
|
||||||
|
if (!canBeDamaged) return 0;
|
||||||
|
|
||||||
|
damage += d;
|
||||||
|
if (damage >= maxDamage) {
|
||||||
|
destroy();
|
||||||
|
return (maxDamage * 10).ceil();
|
||||||
|
}
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
Explosion createExplosion() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void paint(PaintingCanvas canvas) {
|
||||||
|
if (_drawDebug) {
|
||||||
|
canvas.drawCircle(Point.origin, radius, _paintDebug);
|
||||||
|
}
|
||||||
|
super.paint(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupActions() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Ship extends GameObject {
|
||||||
|
Ship(GameObjectFactory f) {
|
||||||
|
// Add main ship sprite
|
||||||
|
_sprt = new Sprite(f.sheet["ship.png"]);
|
||||||
|
_sprt.scale = 0.3;
|
||||||
|
_sprt.rotation = -90.0;
|
||||||
|
addChild(_sprt);
|
||||||
|
radius = 20.0;
|
||||||
|
|
||||||
|
canBeDamaged = false;
|
||||||
|
canDamageShip = false;
|
||||||
|
|
||||||
|
// Set start position
|
||||||
|
position = new Point(0.0, 50.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sprite _sprt;
|
||||||
|
|
||||||
|
void applyThrust(Point joystickValue, double scroll) {
|
||||||
|
Point oldPos = position;
|
||||||
|
Point target = new Point(joystickValue.x * 160.0, joystickValue.y * 220.0 - 250.0 - scroll);
|
||||||
|
double filterFactor = 0.2;
|
||||||
|
|
||||||
|
position = new Point(
|
||||||
|
GameMath.filter(oldPos.x, target.x, filterFactor),
|
||||||
|
GameMath.filter(oldPos.y, target.y, filterFactor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Laser extends GameObject {
|
||||||
|
double impact = 1.0;
|
||||||
|
|
||||||
|
Laser(GameObjectFactory f) {
|
||||||
|
// Add sprite
|
||||||
|
_sprt = new Sprite(f.sheet["laser.png"]);
|
||||||
|
_sprt.scale = 0.3;
|
||||||
|
_sprt.transferMode = sky.TransferMode.plus;
|
||||||
|
addChild(_sprt);
|
||||||
|
radius = 10.0;
|
||||||
|
removeLimit = 640.0;
|
||||||
|
|
||||||
|
canDamageShip = false;
|
||||||
|
canBeDamaged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sprite _sprt;
|
||||||
|
|
||||||
|
void move() {
|
||||||
|
position += new Offset(0.0, -10.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Obstacle extends GameObject {
|
||||||
|
|
||||||
|
Obstacle(this._f);
|
||||||
|
|
||||||
|
double explosionScale = 1.0;
|
||||||
|
GameObjectFactory _f;
|
||||||
|
|
||||||
|
Explosion createExplosion() {
|
||||||
|
SoundEffectPlayer.sharedInstance().play(_f.sounds["explosion"]);
|
||||||
|
Explosion explo = new Explosion(_f.sheet);
|
||||||
|
explo.scale = explosionScale;
|
||||||
|
return explo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Asteroid extends Obstacle {
|
||||||
|
Asteroid(GameObjectFactory f) : super(f);
|
||||||
|
|
||||||
|
Sprite _sprt;
|
||||||
|
|
||||||
|
void setupActions() {
|
||||||
|
// Rotate obstacle
|
||||||
|
int direction = 1;
|
||||||
|
if (randomDouble() < 0.5) direction = -1;
|
||||||
|
ActionTween rotate = new ActionTween(
|
||||||
|
(a) => _sprt.rotation = a,
|
||||||
|
0.0, 360.0 * direction, 5.0 + 5.0 * randomDouble());
|
||||||
|
_sprt.actions.run(new ActionRepeatForever(rotate));
|
||||||
|
}
|
||||||
|
|
||||||
|
set damage(double d) {
|
||||||
|
super.damage = d;
|
||||||
|
int alpha = ((200.0 * d) ~/ maxDamage).clamp(0, 200);
|
||||||
|
_sprt.colorOverlay = new Color.fromARGB(alpha, 255, 3, 86);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AsteroidBig extends Asteroid {
|
||||||
|
AsteroidBig(GameObjectFactory f) : super(f) {
|
||||||
|
_sprt = new Sprite(f.sheet["asteroid_big_${randomInt(3)}.png"]);
|
||||||
|
_sprt.scale = 0.3;
|
||||||
|
radius = 25.0;
|
||||||
|
maxDamage = 5.0;
|
||||||
|
addChild(_sprt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AsteroidSmall extends Asteroid {
|
||||||
|
AsteroidSmall(GameObjectFactory f) : super(f) {
|
||||||
|
_sprt = new Sprite(f.sheet["asteroid_small_${randomInt(3)}.png"]);
|
||||||
|
_sprt.scale = 0.3;
|
||||||
|
radius = 12.0;
|
||||||
|
maxDamage = 3.0;
|
||||||
|
addChild(_sprt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum GameObjectType {
|
||||||
|
asteroidBig,
|
||||||
|
asteroidSmall,
|
||||||
|
}
|
||||||
|
|
||||||
|
class GameObjectFactory {
|
||||||
|
GameObjectFactory(this.sheet, this.sounds, this.level);
|
||||||
|
|
||||||
|
SpriteSheet sheet;
|
||||||
|
Map<String,SoundEffect> sounds;
|
||||||
|
Level level;
|
||||||
|
|
||||||
|
void addGameObject(GameObjectType type, Point pos) {
|
||||||
|
GameObject obj;
|
||||||
|
if (type == GameObjectType.asteroidBig)
|
||||||
|
obj = new AsteroidBig(this);
|
||||||
|
else if (type == GameObjectType.asteroidSmall)
|
||||||
|
obj = new AsteroidSmall(this);
|
||||||
|
|
||||||
|
obj.position = pos;
|
||||||
|
obj.setupActions();
|
||||||
|
|
||||||
|
level.addChild(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// class MovingObstacle extends Obstacle {
|
||||||
|
// MovingObstacle(SpriteSheet sheet, Map<String,SoundEffect> effects, ObstacleType type) : super (sheet, effects, type);
|
||||||
|
//
|
||||||
|
// void setupAction() {
|
||||||
|
// actions.stopAll();
|
||||||
|
//
|
||||||
|
// List<Offset> offsets = [
|
||||||
|
// new Offset(-160.0, 160.0),
|
||||||
|
// new Offset(-80.0, -160.0),
|
||||||
|
// new Offset(0.0, 160.0),
|
||||||
|
// new Offset(80.0, -160.0),
|
||||||
|
// new Offset(160.0, 160.0)];
|
||||||
|
//
|
||||||
|
// List<Point> points = [];
|
||||||
|
// for (Offset offset in offsets) {
|
||||||
|
// points.add(position + offset);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// ActionSpline spline = new ActionSpline((a) => position = a, points, 4.0);
|
||||||
|
// actions.run(new ActionRepeatForever(spline));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
class StarField extends NodeWithSize {
|
||||||
|
sky.Image _image;
|
||||||
|
SpriteSheet _spriteSheet;
|
||||||
|
int _numStars;
|
||||||
|
bool _autoScroll;
|
||||||
|
|
||||||
|
List<Point> _starPositions;
|
||||||
|
List<double> _starScales;
|
||||||
|
List<Rect> _rects;
|
||||||
|
List<Color> _colors;
|
||||||
|
|
||||||
|
final double _padding = 50.0;
|
||||||
|
Size _paddedSize = Size.zero;
|
||||||
|
|
||||||
|
Paint _paint = new Paint()
|
||||||
|
..setFilterQuality(sky.FilterQuality.low)
|
||||||
|
..isAntiAlias = false
|
||||||
|
..setTransferMode(sky.TransferMode.plus);
|
||||||
|
|
||||||
|
StarField(this._spriteSheet, this._numStars, [this._autoScroll = false]) : super(Size.zero) {
|
||||||
|
_image = _spriteSheet.image;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addStars() {
|
||||||
|
_starPositions = [];
|
||||||
|
_starScales = [];
|
||||||
|
_colors = [];
|
||||||
|
_rects = [];
|
||||||
|
|
||||||
|
size = spriteBox.visibleArea.size;
|
||||||
|
_paddedSize = new Size(size.width + _padding * 2.0,
|
||||||
|
size.height + _padding * 2.0);
|
||||||
|
|
||||||
|
for (int i = 0; i < _numStars; i++) {
|
||||||
|
_starPositions.add(new Point(randomDouble() * _paddedSize.width,
|
||||||
|
randomDouble() * _paddedSize.height));
|
||||||
|
_starScales.add(randomDouble() * 0.4);
|
||||||
|
_colors.add(new Color.fromARGB((255.0 * (randomDouble() * 0.5 + 0.5)).toInt(), 255, 255, 255));
|
||||||
|
_rects.add(_spriteSheet["star_${randomInt(2)}.png"].frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void spriteBoxPerformedLayout() {
|
||||||
|
addStars();
|
||||||
|
}
|
||||||
|
|
||||||
|
void paint(PaintingCanvas canvas) {
|
||||||
|
// Create a transform for each star
|
||||||
|
List<sky.RSTransform> transforms = [];
|
||||||
|
for (int i = 0; i < _numStars; i++) {
|
||||||
|
sky.RSTransform transform = new sky.RSTransform(
|
||||||
|
_starScales[i],
|
||||||
|
0.0,
|
||||||
|
_starPositions[i].x - _padding,
|
||||||
|
_starPositions[i].y - _padding);
|
||||||
|
|
||||||
|
transforms.add(transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the stars
|
||||||
|
canvas.drawAtlas(_image, transforms, _rects, _colors, sky.TransferMode.modulate, null, _paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
void move(double dx, double dy) {
|
||||||
|
for (int i = 0; i < _numStars; i++) {
|
||||||
|
double xPos = _starPositions[i].x;
|
||||||
|
double yPos = _starPositions[i].y;
|
||||||
|
double scale = _starScales[i];
|
||||||
|
|
||||||
|
xPos += dx * scale;
|
||||||
|
yPos += dy * scale;
|
||||||
|
|
||||||
|
if (xPos >= _paddedSize.width) xPos -= _paddedSize.width;
|
||||||
|
if (xPos < 0) xPos += _paddedSize.width;
|
||||||
|
if (yPos >= _paddedSize.height) yPos -= _paddedSize.height;
|
||||||
|
if (yPos < 0) yPos += _paddedSize.height;
|
||||||
|
|
||||||
|
_starPositions[i] = new Point(xPos, yPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(double dt) {
|
||||||
|
if (_autoScroll) {
|
||||||
|
move(0.0, dt * 100.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RepeatedImage extends Node {
|
||||||
|
Sprite _sprt0;
|
||||||
|
Sprite _sprt1;
|
||||||
|
|
||||||
|
RepeatedImage(sky.Image image, [sky.TransferMode mode = null]) {
|
||||||
|
_sprt0 = new Sprite.fromImage(image);
|
||||||
|
_sprt0.size = new Size(1024.0, 1024.0);
|
||||||
|
_sprt0.pivot = Point.origin;
|
||||||
|
_sprt1 = new Sprite.fromImage(image);
|
||||||
|
_sprt1.size = new Size(1024.0, 1024.0);
|
||||||
|
_sprt1.pivot = Point.origin;
|
||||||
|
_sprt1.position = new Point(0.0, -1024.0);
|
||||||
|
|
||||||
|
if (mode != null) {
|
||||||
|
_sprt0.transferMode = mode;
|
||||||
|
_sprt1.transferMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
addChild(_sprt0);
|
||||||
|
addChild(_sprt1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void move(double dy) {
|
||||||
|
double yPos = (position.y + dy) % 1024.0;
|
||||||
|
position = new Point(0.0, yPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Explosion extends Node {
|
||||||
|
Explosion(SpriteSheet sheet) {
|
||||||
|
// Add particles
|
||||||
|
ParticleSystem particlesDebris = new ParticleSystem(
|
||||||
|
sheet["explosion_particle.png"],
|
||||||
|
rotateToMovement: true,
|
||||||
|
startRotation:90.0,
|
||||||
|
startRotationVar: 0.0,
|
||||||
|
endRotation: 90.0,
|
||||||
|
startSize: 0.3,
|
||||||
|
startSizeVar: 0.1,
|
||||||
|
endSize: 0.3,
|
||||||
|
endSizeVar: 0.1,
|
||||||
|
numParticlesToEmit: 25,
|
||||||
|
emissionRate:1000.0,
|
||||||
|
greenVar: 127,
|
||||||
|
redVar: 127
|
||||||
|
);
|
||||||
|
particlesDebris.zPosition = 1010.0;
|
||||||
|
addChild(particlesDebris);
|
||||||
|
|
||||||
|
ParticleSystem particlesFire = new ParticleSystem(
|
||||||
|
sheet["fire_particle.png"],
|
||||||
|
colorSequence: new ColorSequence([new Color(0xffffff33), new Color(0xffff3333), new Color(0x00ff3333)], [0.0, 0.5, 1.0]),
|
||||||
|
numParticlesToEmit: 25,
|
||||||
|
emissionRate: 1000.0,
|
||||||
|
startSize: 0.5,
|
||||||
|
startSizeVar: 0.1,
|
||||||
|
endSize: 0.5,
|
||||||
|
endSizeVar: 0.1,
|
||||||
|
posVar: new Point(10.0, 10.0),
|
||||||
|
speed: 10.0,
|
||||||
|
speedVar: 5.0
|
||||||
|
);
|
||||||
|
particlesFire.zPosition = 1011.0;
|
||||||
|
addChild(particlesFire);
|
||||||
|
|
||||||
|
// Add ring
|
||||||
|
Sprite sprtRing = new Sprite(sheet["explosion_ring.png"]);
|
||||||
|
sprtRing.transferMode = sky.TransferMode.plus;
|
||||||
|
addChild(sprtRing);
|
||||||
|
|
||||||
|
Action scale = new ActionTween( (a) => sprtRing.scale = a, 0.2, 1.0, 1.5);
|
||||||
|
Action scaleAndRemove = new ActionSequence([scale, new ActionRemoveNode(sprtRing)]);
|
||||||
|
Action fade = new ActionTween( (a) => sprtRing.opacity = a, 1.0, 0.0, 1.5);
|
||||||
|
actions.run(scaleAndRemove);
|
||||||
|
actions.run(fade);
|
||||||
|
|
||||||
|
// Add streaks
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
Sprite sprtFlare = new Sprite(sheet["explosion_flare.png"]);
|
||||||
|
sprtFlare.pivot = new Point(0.3, 1.0);
|
||||||
|
sprtFlare.scaleX = 0.3;
|
||||||
|
sprtFlare.transferMode = sky.TransferMode.plus;
|
||||||
|
sprtFlare.rotation = randomDouble() * 360.0;
|
||||||
|
addChild(sprtFlare);
|
||||||
|
|
||||||
|
double multiplier = randomDouble() * 0.3 + 1.0;
|
||||||
|
|
||||||
|
Action scale = new ActionTween( (a) => sprtFlare.scaleY = a, 0.3 * multiplier, 0.8, 1.5 * multiplier);
|
||||||
|
Action scaleAndRemove = new ActionSequence([scale, new ActionRemoveNode(sprtFlare)]);
|
||||||
|
Action fadeIn = new ActionTween( (a) => sprtFlare.opacity = a, 0.0, 1.0, 0.5 * multiplier);
|
||||||
|
Action fadeOut = new ActionTween( (a) => sprtFlare.opacity = a, 1.0, 0.0, 1.0 * multiplier);
|
||||||
|
Action fadeInOut = new ActionSequence([fadeIn, fadeOut]);
|
||||||
|
actions.run(scaleAndRemove);
|
||||||
|
actions.run(fadeInOut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Hud extends Node {
|
||||||
|
SpriteSheet sheet;
|
||||||
|
Sprite sprtBgScore;
|
||||||
|
|
||||||
|
bool _dirtyScore = true;
|
||||||
|
int _score = 0;
|
||||||
|
|
||||||
|
int get score => _score;
|
||||||
|
|
||||||
|
set score(int score) {
|
||||||
|
_score = score;
|
||||||
|
_dirtyScore = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Hud(this.sheet) {
|
||||||
|
position = new Point(310.0, 10.0);
|
||||||
|
scale = 0.6;
|
||||||
|
|
||||||
|
sprtBgScore = new Sprite(sheet["scoreboard.png"]);
|
||||||
|
sprtBgScore.pivot = new Point(1.0, 0.0);
|
||||||
|
sprtBgScore.scale = 0.6;
|
||||||
|
addChild(sprtBgScore);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(double dt) {
|
||||||
|
// Update score
|
||||||
|
if (_dirtyScore) {
|
||||||
|
|
||||||
|
sprtBgScore.removeAllChildren();
|
||||||
|
|
||||||
|
String scoreStr = _score.toString();
|
||||||
|
double xPos = -50.0;
|
||||||
|
for (int i = scoreStr.length - 1; i >= 0; i--) {
|
||||||
|
String numStr = scoreStr.substring(i, i + 1);
|
||||||
|
Sprite numSprt = new Sprite(sheet["number_$numStr.png"]);
|
||||||
|
numSprt.position = new Point(xPos, 49.0);
|
||||||
|
sprtBgScore.addChild(numSprt);
|
||||||
|
xPos -= 37.0;
|
||||||
|
}
|
||||||
|
_dirtyScore = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Flash extends NodeWithSize {
|
||||||
|
Flash(Size size, this.duration) : super(size) {
|
||||||
|
ActionTween fade = new ActionTween((a) => _opacity = a, 1.0, 0.0, duration);
|
||||||
|
ActionSequence seq = new ActionSequence([fade, new ActionRemoveNode(this)]);
|
||||||
|
actions.run(seq);
|
||||||
|
}
|
||||||
|
|
||||||
|
double duration;
|
||||||
|
double _opacity = 1.0;
|
||||||
|
Paint _cachedPaint = new Paint();
|
||||||
|
|
||||||
|
void paint(PaintingCanvas canvas) {
|
||||||
|
// Update the color
|
||||||
|
_cachedPaint.color = new Color.fromARGB((255.0 * _opacity).toInt(),
|
||||||
|
255, 255, 255);
|
||||||
|
// Fill the area
|
||||||
|
applyTransformForPivot(canvas);
|
||||||
|
canvas.drawRect(new Rect.fromLTRB(0.0, 0.0, size.width, size.height),
|
||||||
|
_cachedPaint);
|
||||||
|
}
|
||||||
|
}
|
@ -1,752 +0,0 @@
|
|||||||
part of game;
|
|
||||||
|
|
||||||
const double _steeringThreshold = 0.0;
|
|
||||||
const double _steeringMax = 150.0;
|
|
||||||
|
|
||||||
// Random generator
|
|
||||||
Math.Random _rand = new Math.Random();
|
|
||||||
|
|
||||||
const double _gameSizeWidth = 1024.0;
|
|
||||||
const double _gameSizeHeight = 1024.0;
|
|
||||||
|
|
||||||
const double _shipRadius = 30.0;
|
|
||||||
const double _lrgAsteroidRadius = 40.0;
|
|
||||||
const double _medAsteroidRadius = 20.0;
|
|
||||||
const double _smlAsteroidRadius = 10.0;
|
|
||||||
const double _maxAsteroidSpeed = 1.0;
|
|
||||||
|
|
||||||
const int _lifeTimeLaser = 50;
|
|
||||||
|
|
||||||
const int _numStarsInStarField = 150;
|
|
||||||
|
|
||||||
const int _numFramesShieldActive = 60 * 5;
|
|
||||||
const int _numFramesShieldFlickers = 60;
|
|
||||||
|
|
||||||
class GameDemoWorld extends NodeWithSize {
|
|
||||||
// Images
|
|
||||||
sky.Image _imgNebula;
|
|
||||||
|
|
||||||
SpriteSheet _spriteSheet;
|
|
||||||
SpriteSheet _spriteSheetUI;
|
|
||||||
|
|
||||||
Map<String,SoundEffect> _sounds;
|
|
||||||
SoundEffectPlayer _soundPool = SoundEffectPlayer.sharedInstance();
|
|
||||||
|
|
||||||
Navigator _navigator;
|
|
||||||
|
|
||||||
// Inputs
|
|
||||||
double _joystickX = 0.0;
|
|
||||||
double _joystickY = 0.0;
|
|
||||||
|
|
||||||
Node _gameLayer;
|
|
||||||
|
|
||||||
Ship _ship;
|
|
||||||
Sprite _shield;
|
|
||||||
List<Asteroid> _asteroids = [];
|
|
||||||
List<Laser> _lasers = [];
|
|
||||||
StarField _starField;
|
|
||||||
Nebula _nebula;
|
|
||||||
|
|
||||||
// Game state
|
|
||||||
int _numFrames = 0;
|
|
||||||
bool _isGameOver = false;
|
|
||||||
int _gameOverFrame;
|
|
||||||
int _currentLevel = 0;
|
|
||||||
|
|
||||||
// Heads up display
|
|
||||||
Hud _hud;
|
|
||||||
|
|
||||||
Function _gameOverCallback;
|
|
||||||
|
|
||||||
GameDemoWorld(App app, this._navigator, ImageMap images, this._spriteSheet, this._spriteSheetUI, this._sounds, this._gameOverCallback) : super(new Size(_gameSizeWidth, _gameSizeHeight)) {
|
|
||||||
// Fetch images
|
|
||||||
_imgNebula = images["assets/nebula.png"];
|
|
||||||
|
|
||||||
_gameLayer = new Node();
|
|
||||||
this.addChild(_gameLayer);
|
|
||||||
// Add ship
|
|
||||||
addShip();
|
|
||||||
|
|
||||||
// Add background
|
|
||||||
Sprite sprtBackground = new Sprite.fromImage(images["assets/starfield.png"]);
|
|
||||||
sprtBackground.position = new Point(512.0, 512.0);
|
|
||||||
sprtBackground.zPosition = -3.0;
|
|
||||||
addChild(sprtBackground);
|
|
||||||
|
|
||||||
// Add starfield
|
|
||||||
_starField = new StarField(_spriteSheet, _numStarsInStarField);
|
|
||||||
_starField.zPosition = -2.0;
|
|
||||||
addChild(_starField);
|
|
||||||
|
|
||||||
// Add nebula
|
|
||||||
addNebula();
|
|
||||||
|
|
||||||
userInteractionEnabled = true;
|
|
||||||
handleMultiplePointers = true;
|
|
||||||
|
|
||||||
_hud = new Hud(_spriteSheetUI);
|
|
||||||
_hud.zPosition = 1000.0;
|
|
||||||
addChild(_hud);
|
|
||||||
|
|
||||||
// Setup level
|
|
||||||
setupLevel(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setupLevel(int level) {
|
|
||||||
int numLargeAsteroids = 5 + level * 2;
|
|
||||||
int numMediumAsteroids = 5 + level * 2;
|
|
||||||
|
|
||||||
// Add some asteroids to the game world
|
|
||||||
for (int i = 0; i < numLargeAsteroids; i++) {
|
|
||||||
addAsteroid(AsteroidSize.large);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < numMediumAsteroids; i++) {
|
|
||||||
addAsteroid(AsteroidSize.medium);
|
|
||||||
}
|
|
||||||
|
|
||||||
_numFrames = 0;
|
|
||||||
_shield.visible = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Methods for adding game objects
|
|
||||||
|
|
||||||
void addAsteroid(AsteroidSize size, [Point pos]) {
|
|
||||||
Asteroid asteroid = new Asteroid(_spriteSheet, size);
|
|
||||||
asteroid.zPosition = 1.0;
|
|
||||||
if (pos != null) asteroid.position = pos;
|
|
||||||
_gameLayer.addChild(asteroid);
|
|
||||||
_asteroids.add(asteroid);
|
|
||||||
|
|
||||||
// Animate asteroid into the scene
|
|
||||||
Action action = new ActionTween((a) => asteroid.scale = a, 0.0, 1.0, 1.0, bounceOut);
|
|
||||||
_gameLayer.actions.run(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
void addShip() {
|
|
||||||
Ship ship = new Ship(_spriteSheet["ship.png"]);
|
|
||||||
ship.zPosition = 10.0;
|
|
||||||
_gameLayer.addChild(ship);
|
|
||||||
_ship = ship;
|
|
||||||
|
|
||||||
_shield = new Sprite(_spriteSheet["shield.png"]);
|
|
||||||
_shield.zPosition = 11.0;
|
|
||||||
_shield.scale = 0.5;
|
|
||||||
_shield.transferMode = sky.TransferMode.plus;
|
|
||||||
_gameLayer.addChild(_shield);
|
|
||||||
|
|
||||||
Action rotate = new ActionRepeatForever(new ActionTween((a) => _shield.rotation = a, 0.0, 360.0, 1.0));
|
|
||||||
actions.run(rotate);
|
|
||||||
}
|
|
||||||
|
|
||||||
void addLaser() {
|
|
||||||
Laser laser = new Laser(_spriteSheet["laser.png"], _ship);
|
|
||||||
laser.zPosition = 8.0;
|
|
||||||
laser.constrainProportions = true;
|
|
||||||
_lasers.add(laser);
|
|
||||||
_gameLayer.addChild(laser);
|
|
||||||
|
|
||||||
_soundPool.play(_sounds["laser"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void addNebula() {
|
|
||||||
_nebula = new Nebula.withImage(_imgNebula);
|
|
||||||
_gameLayer.addChild(_nebula);
|
|
||||||
}
|
|
||||||
|
|
||||||
void addExplosion(AsteroidSize asteroidSize, Point position) {
|
|
||||||
Node explosionNode = new Node();
|
|
||||||
|
|
||||||
// Add particles
|
|
||||||
ParticleSystem particlesDebris = new ParticleSystem(
|
|
||||||
_spriteSheet["explosion_particle.png"],
|
|
||||||
rotateToMovement: true,
|
|
||||||
startRotation:90.0,
|
|
||||||
startRotationVar: 0.0,
|
|
||||||
endRotation: 90.0,
|
|
||||||
startSize: 0.3,
|
|
||||||
startSizeVar: 0.1,
|
|
||||||
endSize: 0.3,
|
|
||||||
endSizeVar: 0.1,
|
|
||||||
numParticlesToEmit: 25,
|
|
||||||
emissionRate:1000.0,
|
|
||||||
greenVar: 127,
|
|
||||||
redVar: 127
|
|
||||||
);
|
|
||||||
particlesDebris.zPosition = 1010.0;
|
|
||||||
explosionNode.addChild(particlesDebris);
|
|
||||||
|
|
||||||
ParticleSystem particlesFire = new ParticleSystem(
|
|
||||||
_spriteSheet["fire_particle.png"],
|
|
||||||
colorSequence: new ColorSequence([new Color(0xffffff33), new Color(0xffff3333), new Color(0x00ff3333)], [0.0, 0.5, 1.0]),
|
|
||||||
numParticlesToEmit: 25,
|
|
||||||
emissionRate: 1000.0,
|
|
||||||
startSize: 0.5,
|
|
||||||
startSizeVar: 0.1,
|
|
||||||
endSize: 0.5,
|
|
||||||
endSizeVar: 0.1,
|
|
||||||
posVar: new Point(10.0, 10.0),
|
|
||||||
speed: 10.0,
|
|
||||||
speedVar: 5.0
|
|
||||||
);
|
|
||||||
particlesFire.zPosition = 1011.0;
|
|
||||||
explosionNode.addChild(particlesFire);
|
|
||||||
|
|
||||||
|
|
||||||
// Add ring
|
|
||||||
Sprite sprtRing = new Sprite(_spriteSheet["explosion_ring.png"]);
|
|
||||||
sprtRing.transferMode = sky.TransferMode.plus;
|
|
||||||
explosionNode.addChild(sprtRing);
|
|
||||||
|
|
||||||
Action scale = new ActionTween( (a) => sprtRing.scale = a, 0.2, 1.0, 1.5);
|
|
||||||
Action scaleAndRemove = new ActionSequence([scale, new ActionRemoveNode(sprtRing)]);
|
|
||||||
Action fade = new ActionTween( (a) => sprtRing.opacity = a, 1.0, 0.0, 1.5);
|
|
||||||
actions.run(scaleAndRemove);
|
|
||||||
actions.run(fade);
|
|
||||||
|
|
||||||
// Add streaks
|
|
||||||
for (int i = 0; i < 5; i++) {
|
|
||||||
Sprite sprtFlare = new Sprite(_spriteSheet["explosion_flare.png"]);
|
|
||||||
sprtFlare.pivot = new Point(0.3, 1.0);
|
|
||||||
sprtFlare.scaleX = 0.3;
|
|
||||||
sprtFlare.transferMode = sky.TransferMode.plus;
|
|
||||||
sprtFlare.rotation = _rand.nextDouble() * 360.0;
|
|
||||||
explosionNode.addChild(sprtFlare);
|
|
||||||
|
|
||||||
double multiplier = _rand.nextDouble() * 0.3 + 1.0;
|
|
||||||
|
|
||||||
Action scale = new ActionTween( (a) => sprtFlare.scaleY = a, 0.3 * multiplier, 0.8, 1.5 * multiplier);
|
|
||||||
Action scaleAndRemove = new ActionSequence([scale, new ActionRemoveNode(sprtFlare)]);
|
|
||||||
Action fadeIn = new ActionTween( (a) => sprtFlare.opacity = a, 0.0, 1.0, 0.5 * multiplier);
|
|
||||||
Action fadeOut = new ActionTween( (a) => sprtFlare.opacity = a, 1.0, 0.0, 1.0 * multiplier);
|
|
||||||
Action fadeInOut = new ActionSequence([fadeIn, fadeOut]);
|
|
||||||
actions.run(scaleAndRemove);
|
|
||||||
actions.run(fadeInOut);
|
|
||||||
}
|
|
||||||
|
|
||||||
explosionNode.position = position;
|
|
||||||
explosionNode.zPosition = 1010.0;
|
|
||||||
|
|
||||||
if (asteroidSize == AsteroidSize.large) {
|
|
||||||
explosionNode.scale = 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
_gameLayer.addChild(explosionNode);
|
|
||||||
|
|
||||||
_soundPool.play(_sounds["explosion"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void update(double dt) {
|
|
||||||
// Move asteroids
|
|
||||||
for (Asteroid asteroid in _asteroids) {
|
|
||||||
asteroid.position = pointAdd(asteroid.position, asteroid._movementVector);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move lasers and remove expired lasers
|
|
||||||
for (int i = _lasers.length - 1; i >= 0; i--) {
|
|
||||||
Laser laser = _lasers[i];
|
|
||||||
laser.move();
|
|
||||||
if (laser._frameCount > _lifeTimeLaser) {
|
|
||||||
laser.removeFromParent();
|
|
||||||
_lasers.removeAt(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply thrust to ship
|
|
||||||
if (_joystickX != 0.0 || _joystickY != 0.0) {
|
|
||||||
_ship.thrust(_joystickX, _joystickY);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move ship
|
|
||||||
_ship.move();
|
|
||||||
_shield.position = _ship.position;
|
|
||||||
|
|
||||||
// Check collisions between asteroids and lasers
|
|
||||||
for (int i = _lasers.length -1; i >= 0; i--) {
|
|
||||||
// Iterate over all the lasers
|
|
||||||
Laser laser = _lasers[i];
|
|
||||||
|
|
||||||
for (int j = _asteroids.length - 1; j >= 0; j--) {
|
|
||||||
// Iterate over all the asteroids
|
|
||||||
Asteroid asteroid = _asteroids[j];
|
|
||||||
|
|
||||||
// Check for collision
|
|
||||||
if (pointQuickDist(laser.position, asteroid.position) < laser.radius + asteroid.radius) {
|
|
||||||
// Remove laser
|
|
||||||
laser.removeFromParent();
|
|
||||||
_lasers.removeAt(i);
|
|
||||||
|
|
||||||
// Add asteroids and explosions
|
|
||||||
if (asteroid._asteroidSize == AsteroidSize.large) {
|
|
||||||
for (int a = 0; a < 3; a++) addAsteroid(AsteroidSize.medium, asteroid.position);
|
|
||||||
}
|
|
||||||
else if (asteroid._asteroidSize == AsteroidSize.medium) {
|
|
||||||
for (int a = 0; a < 5; a++) addAsteroid(AsteroidSize.small, asteroid.position);
|
|
||||||
}
|
|
||||||
|
|
||||||
addExplosion(asteroid._asteroidSize, asteroid.position);
|
|
||||||
|
|
||||||
// Remove asteroid
|
|
||||||
asteroid.removeFromParent();
|
|
||||||
_asteroids.removeAt(j);
|
|
||||||
|
|
||||||
// Scoring
|
|
||||||
if (asteroid._asteroidSize == AsteroidSize.large)
|
|
||||||
addScore(100);
|
|
||||||
else if (asteroid._asteroidSize == AsteroidSize.medium)
|
|
||||||
addScore(50);
|
|
||||||
else
|
|
||||||
addScore(10);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check collisions between asteroids and ship
|
|
||||||
if (_numFrames > _numFramesShieldActive) {
|
|
||||||
// Shield is no longer active
|
|
||||||
|
|
||||||
for (int i = _asteroids.length - 1; i >= 0; i--) {
|
|
||||||
// Iterate over all the asteroids
|
|
||||||
Asteroid asteroid = _asteroids[i];
|
|
||||||
|
|
||||||
if (pointQuickDist(asteroid.position, _ship.position) < asteroid.radius + _ship.radius) {
|
|
||||||
killShip();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move objects to center camera and warp objects around the edges
|
|
||||||
centerCamera();
|
|
||||||
warpObjects();
|
|
||||||
|
|
||||||
// Check for level up
|
|
||||||
if (_asteroids.length == 0) {
|
|
||||||
_currentLevel++;
|
|
||||||
setupLevel(_currentLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update shield
|
|
||||||
if (_numFrames > _numFramesShieldActive) _shield.visible = false;
|
|
||||||
else if (_numFrames > _numFramesShieldActive - _numFramesShieldFlickers) _shield.visible = !_shield.visible;
|
|
||||||
|
|
||||||
// Check for exit back to main screen
|
|
||||||
if (_isGameOver && _numFrames - _gameOverFrame == 60) {
|
|
||||||
_navigator.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
_numFrames++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void centerCamera() {
|
|
||||||
const cameraDampening = 0.1;
|
|
||||||
Point delta = new Point(_gameSizeWidth/2 - _ship.position.x, _gameSizeHeight/2 - _ship.position.y);
|
|
||||||
delta = pointMult(delta, cameraDampening);
|
|
||||||
|
|
||||||
for (Node child in _gameLayer.children) {
|
|
||||||
child.position = pointAdd(child.position, delta);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update starfield
|
|
||||||
_starField.move(delta.x, delta.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
void warpObjects() {
|
|
||||||
for (Node child in _gameLayer.children) {
|
|
||||||
if (child.position.x < 0) child.position = pointAdd(child.position, new Point(_gameSizeWidth, 0.0));
|
|
||||||
if (child.position.x >= _gameSizeWidth) child.position = pointAdd(child.position, new Point(-_gameSizeWidth, 0.0));
|
|
||||||
if (child.position.y < 0) child.position = pointAdd(child.position, new Point(0.0, _gameSizeHeight));
|
|
||||||
if (child.position.y >= _gameSizeHeight) child.position = pointAdd(child.position, new Point(0.0, -_gameSizeHeight));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void killShip() {
|
|
||||||
if (_isGameOver) return;
|
|
||||||
|
|
||||||
// Set game over
|
|
||||||
_isGameOver = true;
|
|
||||||
_gameOverFrame = _numFrames;
|
|
||||||
_gameOverCallback(_hud.score);
|
|
||||||
|
|
||||||
// Remove the ship
|
|
||||||
_ship.visible = false;
|
|
||||||
|
|
||||||
// Add an explosion
|
|
||||||
addExplosion(AsteroidSize.large, _ship.position);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handling controls
|
|
||||||
|
|
||||||
void controlSteering(double x, double y) {
|
|
||||||
// Reset controls if it's game over
|
|
||||||
if (_isGameOver) {
|
|
||||||
x = y = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
_joystickX = x;
|
|
||||||
_joystickY = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
void controlFire() {
|
|
||||||
// Don't shoot if it's game over
|
|
||||||
if (_isGameOver) return;
|
|
||||||
|
|
||||||
addLaser();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle pointer events
|
|
||||||
|
|
||||||
int _firstPointer = -1;
|
|
||||||
int _secondPointer = -1;
|
|
||||||
Point _firstPointerDownPos;
|
|
||||||
|
|
||||||
bool handleEvent(SpriteBoxEvent event) {
|
|
||||||
|
|
||||||
Point pointerPos = convertPointToNodeSpace(event.boxPosition);
|
|
||||||
int pointer = event.pointer;
|
|
||||||
|
|
||||||
switch (event.type) {
|
|
||||||
case 'pointerdown':
|
|
||||||
if (_firstPointer == -1) {
|
|
||||||
// Assign the first pointer
|
|
||||||
_firstPointer = pointer;
|
|
||||||
_firstPointerDownPos = pointerPos;
|
|
||||||
}
|
|
||||||
else if (_secondPointer == -1) {
|
|
||||||
// Assign second pointer
|
|
||||||
_secondPointer = pointer;
|
|
||||||
controlFire();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// There is a pointer used for steering, let's fire instead
|
|
||||||
controlFire();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'pointermove':
|
|
||||||
if (pointer == _firstPointer) {
|
|
||||||
// Handle turning control
|
|
||||||
double joystickX = 0.0;
|
|
||||||
double deltaX = pointerPos.x - _firstPointerDownPos.x;
|
|
||||||
if (deltaX > _steeringThreshold || deltaX < -_steeringThreshold) {
|
|
||||||
joystickX = (deltaX - _steeringThreshold)/(_steeringMax - _steeringThreshold);
|
|
||||||
if (joystickX > 1.0) joystickX = 1.0;
|
|
||||||
if (joystickX < -1.0) joystickX = -1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
double joystickY = 0.0;
|
|
||||||
double deltaY = pointerPos.y - _firstPointerDownPos.y;
|
|
||||||
if (deltaY > _steeringThreshold || deltaY < -_steeringThreshold) {
|
|
||||||
joystickY = (deltaY - _steeringThreshold)/(_steeringMax - _steeringThreshold);
|
|
||||||
if (joystickY > 1.0) joystickY = 1.0;
|
|
||||||
if (joystickY < -1.0) joystickY = -1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
controlSteering(joystickX, joystickY);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'pointerup':
|
|
||||||
case 'pointercancel':
|
|
||||||
if (pointer == _firstPointer) {
|
|
||||||
// Un-assign the first pointer
|
|
||||||
_firstPointer = -1;
|
|
||||||
_firstPointerDownPos = null;
|
|
||||||
controlSteering(0.0, 0.0);
|
|
||||||
}
|
|
||||||
else if (pointer == _secondPointer) {
|
|
||||||
_secondPointer = -1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scoring and HUD
|
|
||||||
void addScore(int score) {
|
|
||||||
_hud.score += score;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Game objects
|
|
||||||
|
|
||||||
enum AsteroidSize {
|
|
||||||
small,
|
|
||||||
medium,
|
|
||||||
large,
|
|
||||||
}
|
|
||||||
|
|
||||||
class Asteroid extends Sprite {
|
|
||||||
Point _movementVector;
|
|
||||||
AsteroidSize _asteroidSize;
|
|
||||||
double _radius;
|
|
||||||
|
|
||||||
double get radius {
|
|
||||||
if (_radius != null) return _radius;
|
|
||||||
if (_asteroidSize == AsteroidSize.small) _radius = _smlAsteroidRadius;
|
|
||||||
else if (_asteroidSize == AsteroidSize.medium) _radius = _medAsteroidRadius;
|
|
||||||
else if (_asteroidSize == AsteroidSize.large) _radius = _lrgAsteroidRadius;
|
|
||||||
return _radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
Asteroid(SpriteSheet spriteSheet, AsteroidSize this._asteroidSize) {
|
|
||||||
size = new Size(radius * 2.0, radius * 2.0);
|
|
||||||
position = new Point(_gameSizeWidth * _rand.nextDouble(), _gameSizeHeight * _rand.nextDouble());
|
|
||||||
rotation = 360.0 * _rand.nextDouble();
|
|
||||||
|
|
||||||
if (_asteroidSize == AsteroidSize.small) {
|
|
||||||
texture = spriteSheet["asteroid_small_${_rand.nextInt(2)}.png"];
|
|
||||||
} else {
|
|
||||||
texture = spriteSheet["asteroid_big_${_rand.nextInt(2)}.png"];
|
|
||||||
}
|
|
||||||
|
|
||||||
_movementVector = new Point(_rand.nextDouble() * _maxAsteroidSpeed * 2 - _maxAsteroidSpeed,
|
|
||||||
_rand.nextDouble() * _maxAsteroidSpeed * 2 - _maxAsteroidSpeed);
|
|
||||||
|
|
||||||
userInteractionEnabled = true;
|
|
||||||
|
|
||||||
// Rotate forever
|
|
||||||
double direction = (_rand.nextBool()) ? 360.0 : -360.0;
|
|
||||||
ActionTween rot = new ActionTween( (a) => rotation = a, 0.0, direction, 2.0 * _rand.nextDouble() + 2.0);
|
|
||||||
ActionRepeatForever repeat = new ActionRepeatForever(rot);
|
|
||||||
actions.run(repeat);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool handleEvent(SpriteBoxEvent event) {
|
|
||||||
if (event.type == "pointerdown") {
|
|
||||||
actions.stopWithTag("fade");
|
|
||||||
colorOverlay = new Color(0x99ffffff);
|
|
||||||
}
|
|
||||||
else if (event.type == "pointerup") {
|
|
||||||
// Fade out the color overlay
|
|
||||||
Action fadeOut = new ActionTween((a) => this.colorOverlay = a, new Color(0x99ffffff), new Color(0x00ffffff), 1.0);
|
|
||||||
Action fadeOutAndRemove = new ActionSequence([fadeOut, new ActionCallFunction(() => this.colorOverlay = null)]);
|
|
||||||
actions.run(fadeOutAndRemove, "fade");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Ship extends Sprite {
|
|
||||||
Vector2 _movementVector;
|
|
||||||
double _rotationTarget;
|
|
||||||
double radius = _shipRadius;
|
|
||||||
|
|
||||||
Ship(Texture img) : super(img) {
|
|
||||||
_movementVector = new Vector2.zero();
|
|
||||||
rotation = _rotationTarget = 270.0;
|
|
||||||
|
|
||||||
// Create sprite
|
|
||||||
size = new Size(_shipRadius * 2.0, _shipRadius * 2.0);
|
|
||||||
position = new Point(_gameSizeWidth/2.0, _gameSizeHeight/2.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void thrust(double x, double y) {
|
|
||||||
_rotationTarget = convertRadians2Degrees(Math.atan2(y, x));
|
|
||||||
Vector2 directionVector = new Vector2(x, y).normalize();
|
|
||||||
_movementVector.addScaled(directionVector, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void move() {
|
|
||||||
position = new Point(position.x + _movementVector[0], position.y + _movementVector[1]);
|
|
||||||
_movementVector.scale(0.9);
|
|
||||||
|
|
||||||
rotation = dampenRotation(rotation, _rotationTarget, 0.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Laser extends Sprite {
|
|
||||||
int _frameCount = 0;
|
|
||||||
Point _movementVector;
|
|
||||||
double radius = 20.0;
|
|
||||||
|
|
||||||
Laser(Texture img, Ship ship) : super(img) {
|
|
||||||
size = new Size(30.0, 30.0);
|
|
||||||
position = ship.position;
|
|
||||||
rotation = ship.rotation + 90.0;
|
|
||||||
transferMode = sky.TransferMode.plus;
|
|
||||||
double rotRadians = convertDegrees2Radians(rotation);
|
|
||||||
_movementVector = pointMult(new Point(Math.sin(rotRadians), -Math.cos(rotRadians)), 10.0);
|
|
||||||
_movementVector = new Point(_movementVector.x + ship._movementVector[0], _movementVector.y + ship._movementVector[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void move() {
|
|
||||||
position = pointAdd(position, _movementVector);
|
|
||||||
_frameCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Background starfield
|
|
||||||
|
|
||||||
class StarField extends NodeWithSize {
|
|
||||||
sky.Image _image;
|
|
||||||
int _numStars;
|
|
||||||
bool _autoScroll;
|
|
||||||
List<Point> _starPositions;
|
|
||||||
List<double> _starScales;
|
|
||||||
List<Rect> _rects;
|
|
||||||
List<Color> _colors;
|
|
||||||
Paint _paint = new Paint()
|
|
||||||
..setFilterQuality(sky.FilterQuality.low)
|
|
||||||
..isAntiAlias = false
|
|
||||||
..setTransferMode(sky.TransferMode.plus);
|
|
||||||
|
|
||||||
StarField(SpriteSheet spriteSheet, this._numStars, [this._autoScroll = false]) : super(new Size(1024.0, 1024.0)) {
|
|
||||||
_starPositions = [];
|
|
||||||
_starScales = [];
|
|
||||||
_colors = [];
|
|
||||||
_rects = [];
|
|
||||||
|
|
||||||
for (int i = 0; i < _numStars; i++) {
|
|
||||||
_starPositions.add(new Point(_rand.nextDouble() * _gameSizeWidth, _rand.nextDouble() * _gameSizeHeight));
|
|
||||||
_starScales.add(_rand.nextDouble());
|
|
||||||
_colors.add(new Color.fromARGB((255.0 * (_rand.nextDouble() * 0.5 + 0.5)).toInt(), 255, 255, 255));
|
|
||||||
_rects.add(spriteSheet["star_${_rand.nextInt(2)}.png"].frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
_image = spriteSheet.image;
|
|
||||||
}
|
|
||||||
|
|
||||||
void paint(PaintingCanvas canvas) {
|
|
||||||
// Create a transform for each star
|
|
||||||
List<sky.RSTransform> transforms = [];
|
|
||||||
for (int i = 0; i < _numStars; i++) {
|
|
||||||
sky.RSTransform transform = new sky.RSTransform(_starScales[i], 0.0, _starPositions[i].x, _starPositions[i].y);
|
|
||||||
transforms.add(transform);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw the stars
|
|
||||||
canvas.drawAtlas(_image, transforms, _rects, _colors, sky.TransferMode.modulate, null, _paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
void move(double dx, double dy) {
|
|
||||||
for (int i = 0; i < _numStars; i++) {
|
|
||||||
double xPos = _starPositions[i].x;
|
|
||||||
double yPos = _starPositions[i].y;
|
|
||||||
double scale = _starScales[i];
|
|
||||||
|
|
||||||
xPos += dx * scale;
|
|
||||||
yPos += dy * scale;
|
|
||||||
|
|
||||||
if (xPos >= _gameSizeWidth) xPos -= _gameSizeWidth;
|
|
||||||
if (xPos < 0) xPos += _gameSizeWidth;
|
|
||||||
if (yPos >= _gameSizeHeight) yPos -= _gameSizeHeight;
|
|
||||||
if (yPos < 0) yPos += _gameSizeHeight;
|
|
||||||
|
|
||||||
_starPositions[i] = new Point(xPos, yPos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void update(double dt) {
|
|
||||||
if (_autoScroll) {
|
|
||||||
move(dt * 100.0, 0.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Hud extends NodeWithSize {
|
|
||||||
SpriteSheet spriteSheetUI;
|
|
||||||
Sprite sprtBgScore;
|
|
||||||
Sprite sprtBgShield;
|
|
||||||
bool _dirtyScore = true;
|
|
||||||
|
|
||||||
int _score = 0;
|
|
||||||
|
|
||||||
int get score => _score;
|
|
||||||
|
|
||||||
set score(int score) {
|
|
||||||
_score = score;
|
|
||||||
_dirtyScore = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Hud(this.spriteSheetUI) : super(Size.zero) {
|
|
||||||
pivot = Point.origin;
|
|
||||||
|
|
||||||
sprtBgScore = new Sprite(spriteSheetUI["scoreboard.png"]);
|
|
||||||
sprtBgScore.pivot = new Point(1.0, 0.0);
|
|
||||||
sprtBgScore.scale = 0.6;
|
|
||||||
addChild(sprtBgScore);
|
|
||||||
|
|
||||||
sprtBgShield = new Sprite(spriteSheetUI["bar_shield.png"]);
|
|
||||||
sprtBgShield.pivot = Point.origin;
|
|
||||||
sprtBgShield.scale = 0.6;
|
|
||||||
// TODO: Add shield
|
|
||||||
//addChild(sprtBgShield);
|
|
||||||
}
|
|
||||||
|
|
||||||
void spriteBoxPerformedLayout() {
|
|
||||||
// Set the size and position of HUD display
|
|
||||||
position = spriteBox.visibleArea.topLeft;
|
|
||||||
size = spriteBox.visibleArea.size;
|
|
||||||
|
|
||||||
// Position hud objects
|
|
||||||
sprtBgShield.position = new Point(20.0, 20.0);
|
|
||||||
sprtBgScore.position = new Point(size.width - 20.0, 20.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void update(double dt) {
|
|
||||||
// Update score
|
|
||||||
if (_dirtyScore) {
|
|
||||||
|
|
||||||
sprtBgScore.removeAllChildren();
|
|
||||||
|
|
||||||
String scoreStr = _score.toString();
|
|
||||||
double xPos = -50.0;
|
|
||||||
for (int i = scoreStr.length - 1; i >= 0; i--) {
|
|
||||||
String numStr = scoreStr.substring(i, i + 1);
|
|
||||||
Sprite numSprt = new Sprite(spriteSheetUI["number_$numStr.png"]);
|
|
||||||
numSprt.position = new Point(xPos, 49.0);
|
|
||||||
sprtBgScore.addChild(numSprt);
|
|
||||||
xPos -= 37.0;
|
|
||||||
}
|
|
||||||
_dirtyScore = false;
|
|
||||||
}
|
|
||||||
// Update power bar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Nebula extends Node {
|
|
||||||
|
|
||||||
Nebula.withImage(sky.Image img) {
|
|
||||||
for (int i = 0; i < 2; i++) {
|
|
||||||
for (int j = 0; j < 2; j++) {
|
|
||||||
Sprite sprt = new Sprite.fromImage(img);
|
|
||||||
sprt.transferMode = sky.TransferMode.plus;
|
|
||||||
sprt.pivot = Point.origin;
|
|
||||||
sprt.position = new Point(i * _gameSizeWidth - _gameSizeWidth, j * _gameSizeHeight - _gameSizeHeight);
|
|
||||||
addChild(sprt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convenience methods
|
|
||||||
|
|
||||||
Point pointAdd(Point a, Point b) {
|
|
||||||
return new Point(a.x+ b.x, a.y + b.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
Point pointMult(Point a, double multiplier) {
|
|
||||||
return new Point(a.x * multiplier, a.y * multiplier);
|
|
||||||
}
|
|
||||||
|
|
||||||
double dampenRotation(double src, double dst, double dampening) {
|
|
||||||
double delta = dst - src;
|
|
||||||
while (delta > 180.0) delta -= 360;
|
|
||||||
while (delta < -180) delta += 360;
|
|
||||||
delta *= dampening;
|
|
||||||
|
|
||||||
return src + delta;
|
|
||||||
}
|
|
||||||
|
|
||||||
double pointQuickDist(Point a, Point b) {
|
|
||||||
double dx = a.x - b.x;
|
|
||||||
double dy = a.y - b.y;
|
|
||||||
if (dx < 0.0) dx = -dx;
|
|
||||||
if (dy < 0.0) dy = -dy;
|
|
||||||
if (dx > dy) {
|
|
||||||
return dx + dy/2.0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return dy + dx/2.0;
|
|
||||||
}
|
|
||||||
}
|
|
@ -77,10 +77,19 @@ main() async {
|
|||||||
class GameDemoApp extends App {
|
class GameDemoApp extends App {
|
||||||
|
|
||||||
NavigationState _navigationState;
|
NavigationState _navigationState;
|
||||||
GameDemoWorld _game;
|
NodeWithSize _game;
|
||||||
int _lastScore = 0;
|
int _lastScore = 0;
|
||||||
|
|
||||||
void initState() {
|
void initState() {
|
||||||
|
// _game = new GameDemoNode(
|
||||||
|
// _imageMap,
|
||||||
|
// _spriteSheet,
|
||||||
|
// _spriteSheetUI,
|
||||||
|
// _sounds,
|
||||||
|
// (lastScore) {
|
||||||
|
// setState(() {_lastScore = lastScore;});
|
||||||
|
// });
|
||||||
|
|
||||||
_navigationState = new NavigationState([
|
_navigationState = new NavigationState([
|
||||||
new Route(
|
new Route(
|
||||||
name: '/',
|
name: '/',
|
||||||
@ -112,24 +121,23 @@ class GameDemoApp extends App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildGameScene(navigator, route) {
|
Widget _buildGameScene(navigator, route) {
|
||||||
return new SpriteWidget(_game);
|
return new SpriteWidget(_game, SpriteBoxTransformMode.fixedWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildMainScene(navigator, route) {
|
Widget _buildMainScene(navigator, route) {
|
||||||
return new Stack([
|
return new Stack([
|
||||||
new SpriteWidget(new MainScreenBackground()),
|
new SpriteWidget(new MainScreenBackground(), SpriteBoxTransformMode.fixedWidth),
|
||||||
new Flex([
|
new Flex([
|
||||||
new TextureButton(
|
new TextureButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_game = new GameDemoWorld(
|
_game = new GameDemoNode(
|
||||||
_app,
|
|
||||||
navigator,
|
|
||||||
_imageMap,
|
_imageMap,
|
||||||
_spriteSheet,
|
_spriteSheet,
|
||||||
_spriteSheetUI,
|
_spriteSheetUI,
|
||||||
_sounds,
|
_sounds,
|
||||||
(lastScore) {
|
(lastScore) {
|
||||||
setState(() {_lastScore = lastScore;});
|
setState(() {_lastScore = lastScore;});
|
||||||
|
navigator.pop();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
navigator.pushNamed('/game');
|
navigator.pushNamed('/game');
|
||||||
@ -243,14 +251,19 @@ class _TextureButtonToken {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MainScreenBackground extends NodeWithSize {
|
class MainScreenBackground extends NodeWithSize {
|
||||||
MainScreenBackground() : super(new Size(1024.0, 1024.0)) {
|
MainScreenBackground() : super(new Size(320.0, 320.0)) {
|
||||||
Sprite sprtBackground = new Sprite.fromImage(_imageMap['assets/starfield.png']);
|
// Sprite sprtBackground = new Sprite.fromImage(_imageMap['assets/starfield.png']);
|
||||||
sprtBackground.position = new Point(512.0, 512.0);
|
// sprtBackground.position = new Point(160.0, 160.0);
|
||||||
addChild(sprtBackground);
|
// addChild(sprtBackground);
|
||||||
|
|
||||||
assert(_spriteSheet.image != null);
|
assert(_spriteSheet.image != null);
|
||||||
|
|
||||||
StarField starField = new StarField(_spriteSheet, 200, true);
|
StarField starField = new StarField(_spriteSheet, 200, true);
|
||||||
addChild(starField);
|
addChild(starField);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void paint(PaintingCanvas canvas) {
|
||||||
|
canvas.drawRect(new Rect.fromLTWH(0.0, 0.0, 320.0, 320.0), new Paint()..color=new Color(0xff000000));
|
||||||
|
super.paint(canvas);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,25 @@ class Node {
|
|||||||
return _actions;
|
return _actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Constraint> _constraints;
|
||||||
|
|
||||||
|
List<Constraint> get constraints {
|
||||||
|
return _constraints;
|
||||||
|
}
|
||||||
|
|
||||||
|
set constraints(List<Constraint> constraints) {
|
||||||
|
_constraints = constraints;
|
||||||
|
if (_spriteBox != null) _spriteBox._constrainedNodes = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyConstraints(double dt) {
|
||||||
|
if (_constraints == null) return;
|
||||||
|
|
||||||
|
for (Constraint constraint in _constraints) {
|
||||||
|
constraint.constrain(this, dt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
/// Creates a new [Node] without any transformation.
|
/// Creates a new [Node] without any transformation.
|
||||||
|
@ -74,6 +74,8 @@ class SpriteBox extends RenderBox {
|
|||||||
|
|
||||||
List<ActionController> _actionControllers;
|
List<ActionController> _actionControllers;
|
||||||
|
|
||||||
|
List<Node> _constrainedNodes;
|
||||||
|
|
||||||
Rect _visibleArea;
|
Rect _visibleArea;
|
||||||
|
|
||||||
Rect get visibleArea {
|
Rect get visibleArea {
|
||||||
@ -139,11 +141,13 @@ class SpriteBox extends RenderBox {
|
|||||||
_registerNode(Node node) {
|
_registerNode(Node node) {
|
||||||
_actionControllers = null;
|
_actionControllers = null;
|
||||||
_eventTargets = null;
|
_eventTargets = null;
|
||||||
|
if (node == null || node.constraints != null) _constrainedNodes = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_deregisterNode(Node node) {
|
_deregisterNode(Node node) {
|
||||||
_actionControllers = null;
|
_actionControllers = null;
|
||||||
_eventTargets = null;
|
_eventTargets = null;
|
||||||
|
if (node == null || node.constraints != null) _constrainedNodes = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event handling
|
// Event handling
|
||||||
@ -353,8 +357,10 @@ class SpriteBox extends RenderBox {
|
|||||||
|
|
||||||
_frameRate = 1.0/delta;
|
_frameRate = 1.0/delta;
|
||||||
|
|
||||||
|
_callConstraintsPreUpdate(delta);
|
||||||
_runActions(delta);
|
_runActions(delta);
|
||||||
_callUpdate(_rootNode, delta);
|
_callUpdate(_rootNode, delta);
|
||||||
|
_callConstraintsConstrain(delta);
|
||||||
|
|
||||||
// Schedule next update
|
// Schedule next update
|
||||||
_scheduleTick();
|
_scheduleTick();
|
||||||
@ -392,6 +398,42 @@ class SpriteBox extends RenderBox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _callConstraintsPreUpdate(double dt) {
|
||||||
|
if (_constrainedNodes == null) {
|
||||||
|
_constrainedNodes = [];
|
||||||
|
_addConstrainedNodes(_rootNode, _constrainedNodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Node node in _constrainedNodes) {
|
||||||
|
for (Constraint constraint in node.constraints) {
|
||||||
|
constraint.preUpdate(node, dt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _callConstraintsConstrain(double dt) {
|
||||||
|
if (_constrainedNodes == null) {
|
||||||
|
_constrainedNodes = [];
|
||||||
|
_addConstrainedNodes(_rootNode, _constrainedNodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Node node in _constrainedNodes) {
|
||||||
|
for (Constraint constraint in node.constraints) {
|
||||||
|
constraint.constrain(node, dt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _addConstrainedNodes(Node node, List<Node> nodes) {
|
||||||
|
if (node._constraints != null && node._constraints.length > 0) {
|
||||||
|
nodes.add(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Node child in node.children) {
|
||||||
|
_addConstrainedNodes(child, nodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _callSpriteBoxPerformedLayout(Node node) {
|
void _callSpriteBoxPerformedLayout(Node node) {
|
||||||
node.spriteBoxPerformedLayout();
|
node.spriteBoxPerformedLayout();
|
||||||
for (Node child in node.children) {
|
for (Node child in node.children) {
|
||||||
|
@ -22,6 +22,8 @@ import 'package:sky_services/media/media.mojom.dart';
|
|||||||
import 'package:vector_math/vector_math.dart';
|
import 'package:vector_math/vector_math.dart';
|
||||||
|
|
||||||
part 'action.dart';
|
part 'action.dart';
|
||||||
|
part 'constraint.dart';
|
||||||
|
part 'action_spline.dart';
|
||||||
part 'color_secuence.dart';
|
part 'color_secuence.dart';
|
||||||
part 'image_map.dart';
|
part 'image_map.dart';
|
||||||
part 'layer.dart';
|
part 'layer.dart';
|
||||||
|
@ -80,4 +80,21 @@ class GameMath {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static double pointQuickDist(Point a, Point b) {
|
||||||
|
double dx = a.x - b.x;
|
||||||
|
double dy = a.y - b.y;
|
||||||
|
if (dx < 0.0) dx = -dx;
|
||||||
|
if (dy < 0.0) dy = -dy;
|
||||||
|
if (dx > dy) {
|
||||||
|
return dx + dy/2.0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return dy + dx/2.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static double filter (double a, double b, double filterFactor) {
|
||||||
|
return (a * (1-filterFactor)) + b * filterFactor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user