Commit 9b1985c2 authored by Viktor Lidholt's avatar Viktor Lidholt

Adds basic sprite sheet support to sprites

Adds drawImageRect to Canvas bindings

R=eseidel@chromium.org

Review URL: https://codereview.chromium.org/1204783003.
parent edda0bb8
......@@ -22,13 +22,10 @@ const int _numStarsInStarField = 150;
class GameDemoWorld extends NodeWithSize {
// Images
Image _imgBg;
Image _imgAsteroid;
Image _imgShip;
Image _imgLaser;
Image _imgStar;
Image _imgNebula;
SpriteSheet _spriteSheet;
// Inputs
double _joystickX = 0.0;
double _joystickY = 0.0;
......@@ -42,15 +39,10 @@ class GameDemoWorld extends NodeWithSize {
StarField _starField;
Nebula _nebula;
GameDemoWorld(ImageMap images) : super(new Size(_gameSizeWidth, _gameSizeHeight)) {
GameDemoWorld(ImageMap images, this._spriteSheet) : super(new Size(_gameSizeWidth, _gameSizeHeight)) {
// Fetch images
_imgBg = images["https://raw.githubusercontent.com/slembcke/GalacticGuardian.spritebuilder/GDC/Packages/SpriteBuilder%20Resources.sbpack/resources-auto/BurnTexture.png"];
_imgAsteroid = images["https://raw.githubusercontent.com/slembcke/GalacticGuardian.spritebuilder/GDC/Packages/SpriteBuilder%20Resources.sbpack/Sprites/resources-auto/asteroid_big_002.png"];
_imgShip = images["https://raw.githubusercontent.com/slembcke/GalacticGuardian.spritebuilder/GDC/Packages/SpriteBuilder%20Resources.sbpack/Sprites/resources-auto/GG_blueship_Lv3.png"];
_imgLaser = images["https://raw.githubusercontent.com/slembcke/GalacticGuardian.spritebuilder/GDC/Packages/SpriteBuilder%20Resources.sbpack/Sprites/resources-auto/laserBlue.png"];
_imgStar = images["https://raw.githubusercontent.com/slembcke/GalacticGuardian.spritebuilder/GDC/Packages/SpriteBuilder%20Resources.sbpack/Sprites/resources-auto/laserFlashPurple.png"];
_imgNebula = images["https://raw.githubusercontent.com/slembcke/GalacticGuardian.spritebuilder/GDC/Source/Resources/NebulaClouds.png"];
_imgNebula = images["res/nebula.png"];
_gameLayer = new Node();
this.addChild(_gameLayer);
......@@ -67,7 +59,7 @@ class GameDemoWorld extends NodeWithSize {
addShip();
// Add starfield
_starField = new StarField.withImage(_imgStar, _numStarsInStarField);
_starField = new StarField(_spriteSheet["star.png"], _numStarsInStarField);
_starField.zPosition = -2.0;
addChild(_starField);
......@@ -79,16 +71,9 @@ class GameDemoWorld extends NodeWithSize {
}
// Methods for adding game objects
void addBackground() {
Sprite sprtBg = new Sprite(_imgBg);
sprtBg.size = new Size(_gameSizeWidth, _gameSizeHeight);
sprtBg.pivot = Point.origin;
_gameLayer.addChild(sprtBg);
}
void addAsteroid(AsteroidSize size, [Point pos]) {
Asteroid asteroid = new Asteroid.withImage(_imgAsteroid, size);
Asteroid asteroid = new Asteroid(_spriteSheet["asteroid_big_1.png"], size);
asteroid.zPosition = 1.0;
if (pos != null) asteroid.position = pos;
_gameLayer.addChild(asteroid);
......@@ -96,14 +81,14 @@ class GameDemoWorld extends NodeWithSize {
}
void addShip() {
Ship ship = new Ship.withImage(_imgShip);
Ship ship = new Ship(_spriteSheet["ship.png"]);
ship.zPosition = 10.0;
_gameLayer.addChild(ship);
_ship = ship;
}
void addLaser() {
Laser laser = new Laser.withImage(_imgLaser, _ship);
Laser laser = new Laser(_spriteSheet["laser.png"], _ship);
laser.zPosition = 8.0;
_lasers.add(laser);
_gameLayer.addChild(laser);
......@@ -296,7 +281,7 @@ class Asteroid extends Sprite {
return _radius;
}
Asteroid.withImage(Image img, AsteroidSize this._asteroidSize) : super(img) {
Asteroid(Texture img, AsteroidSize this._asteroidSize) : super(img) {
size = new Size(radius * 2.0, radius * 2.0);
position = new Point(_gameSizeWidth * _rand.nextDouble(), _gameSizeHeight * _rand.nextDouble());
rotation = 360.0 * _rand.nextDouble();
......@@ -322,7 +307,7 @@ class Ship extends Sprite {
Vector2 _movementVector;
double _rotationTarget;
Ship.withImage(Image img) : super(img) {
Ship(Texture img) : super(img) {
_movementVector = new Vector2.zero();
rotation = _rotationTarget = 270.0;
......@@ -350,7 +335,7 @@ class Laser extends Sprite {
Point _movementVector;
double radius = 10.0;
Laser.withImage(Image img, Ship ship) : super(img) {
Laser(Texture img, Ship ship) : super(img) {
size = new Size(20.0, 20.0);
position = ship.position;
rotation = ship.rotation + 90.0;
......@@ -369,13 +354,13 @@ class Laser extends Sprite {
// Background starfield
class StarField extends Node {
Image _img;
Texture _img;
int _numStars;
List<Point> _starPositions;
List<double> _starScales;
List<double> _opacity;
StarField.withImage(Image this._img, int this._numStars) {
StarField(this._img, this._numStars) {
_starPositions = [];
_starScales = [];
_opacity = [];
......@@ -392,8 +377,8 @@ class StarField extends Node {
Paint paint = new Paint();
paint.setTransferMode(TransferMode.plus);
double baseScaleX = 32.0/_img.width;
double baseScaleY = 32.0/_img.height;
double baseScaleX = 32.0 / _img.size.width;
double baseScaleY = 32.0 / _img.size.height;
// Draw each star
for (int i = 0; i < _numStars; i++) {
......@@ -404,9 +389,9 @@ class StarField extends Node {
canvas.save();
canvas.translate(pos.x, pos.y);
canvas.scale(baseScaleX*scale, baseScaleY*scale);
canvas.scale(baseScaleX * scale, baseScaleY * scale);
canvas.drawImage(_img, 0.0, 0.0, paint);
canvas.drawImageRect(_img.image, _img.frame, _img.spriteSourceSize, paint);
canvas.restore();
}
......@@ -436,7 +421,7 @@ class Nebula extends Node {
Nebula.withImage(Image img) {
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
Sprite sprt = new Sprite(img);
Sprite sprt = new Sprite.fromImage(img);
sprt.pivot = Point.origin;
sprt.position = new Point(i * _gameSizeWidth - _gameSizeWidth, j * _gameSizeHeight - _gameSizeHeight);
addChild(sprt);
......
......@@ -3,13 +3,13 @@ part of sprites;
/// A Sprite is a [Node] that renders a bitmap image to the screen.
class Sprite extends NodeWithSize {
/// The image that the sprite will render to screen.
/// The texture that the sprite will render to screen.
///
/// If the image is null, the sprite will be rendered as a red square
/// If the texture is null, the sprite will be rendered as a red square
/// marking the bounds of the sprite.
///
/// mySprite.image = myImage;
Image image;
/// mySprite.texture = myTexture;
Texture texture;
/// If true, constrains the proportions of the image by scaling it down, if its proportions doesn't match the [size].
///
......@@ -29,14 +29,28 @@ class Sprite extends NodeWithSize {
/// mySprite.transferMode = TransferMode.plusMode;
TransferMode transferMode;
/// Creates a new sprite from the provided [texture].
///
/// var mySprite = new Sprite(myTexture)
Sprite([this.texture]) {
if (texture != null) {
size = texture.size;
pivot = texture.pivot;
} else {
pivot = new Point(0.5, 0.5);
}
}
/// Creates a new sprite from the provided [image].
///
/// var mySprite = new Sprite(myImage);
Sprite([Image this.image]) {
/// var mySprite = new Sprite.fromImage(myImage);
Sprite.fromImage(Image image) {
assert(image != null);
texture = new Texture(image);
size = texture.size;
pivot = new Point(0.5, 0.5);
if (image != null) {
size = new Size(image.width.toDouble(), image.height.toDouble());
}
}
/// The opacity of the sprite in the range 0.0 to 1.0.
......@@ -56,19 +70,22 @@ class Sprite extends NodeWithSize {
// Account for pivot point
applyTransformForPivot(canvas);
if (image != null && image.width > 0 && image.height > 0) {
if (texture != null) {
double w = texture.size.width;
double h = texture.size.height;
if (w <= 0 || h <= 0) return;
double scaleX = size.width/image.width;
double scaleY = size.height/image.height;
double scaleX = size.width / w;
double scaleY = size.height / h;
if (constrainProportions) {
// Constrain proportions, using the smallest scale and by centering the image
if (scaleX < scaleY) {
canvas.translate(0.0, (size.height - scaleX * image.height)/2.0);
canvas.translate(0.0, (size.height - scaleX * h) / 2.0);
scaleY = scaleX;
}
else {
canvas.translate((size.width - scaleY * image.width)/2.0, 0.0);
} else {
canvas.translate((size.width - scaleY * w) / 2.0, 0.0);
scaleX = scaleY;
}
}
......@@ -85,9 +102,9 @@ class Sprite extends NodeWithSize {
paint.setTransferMode(transferMode);
}
canvas.drawImage(image, 0.0, 0.0, paint);
}
else {
// Do actual drawing of the sprite
canvas.drawImageRect(texture.image, texture.frame, texture.spriteSourceSize, paint);
} else {
// Paint a red square for missing texture
canvas.drawRect(new Rect.fromLTRB(0.0, 0.0, size.width, size.height),
new Paint()..color = const Color.fromARGB(255, 255, 0, 0));
......
......@@ -205,8 +205,7 @@ class SpriteBox extends RenderBox {
if (scaleX > scaleY) {
scaleY = scaleX;
offsetY = (size.height - scaleY * systemHeight)/2.0;
}
else {
} else {
scaleX = scaleY;
offsetX = (size.width - scaleX * systemWidth)/2.0;
}
......@@ -217,8 +216,7 @@ class SpriteBox extends RenderBox {
if (scaleX < scaleY) {
scaleY = scaleX;
offsetY = (size.height - scaleY * systemHeight)/2.0;
}
else {
} else {
scaleX = scaleY;
offsetX = (size.width - scaleX * systemWidth)/2.0;
}
......
......@@ -3,6 +3,7 @@ library sprites;
import 'dart:math' as Math;
import 'dart:sky';
import 'dart:typed_data';
import 'dart:convert';
import 'package:sky/base/scheduler.dart' as scheduler;
import 'package:sky/mojo/net/image_cache.dart' as image_cache;
......@@ -10,6 +11,7 @@ import 'package:sky/rendering/box.dart';
import 'package:sky/rendering/object.dart';
import 'package:sky/widgets/widget.dart';
import 'package:vector_math/vector_math.dart';
import 'package:sky/framework/net/fetch.dart';
part 'sprite_box.dart';
part 'sprite_widget.dart';
......@@ -17,3 +19,5 @@ part 'node.dart';
part 'node_with_size.dart';
part 'sprite.dart';
part 'image_map.dart';
part 'texture.dart';
part 'spritesheet.dart';
part of sprites;
class SpriteSheet {
Image _image;
Map<String, Texture> _textures = new Map();
SpriteSheet(this._image, String jsonDefinition) {
assert(_image != null);
assert(jsonDefinition != null);
JsonDecoder decoder = new JsonDecoder();
Map file = decoder.convert(jsonDefinition);
assert(file != null);
List frames = file["frames"];
for (Map frameInfo in frames) {
String fileName = frameInfo["filename"];
Rect frame = _readJsonRect(frameInfo["frame"]);
bool rotated = frameInfo["rotated"];
bool trimmed = frameInfo["trimmed"];
Rect spriteSourceSize = _readJsonRect(frameInfo["spriteSourceSize"]);
Size sourceSize = _readJsonSize(frameInfo["sourceSize"]);
Point pivot = _readJsonPoint(frameInfo["pivot"]);
var texture = new Texture._fromSpriteFrame(_image, fileName, sourceSize, rotated, trimmed, frame,
spriteSourceSize, pivot);
_textures[fileName] = texture;
}
}
Rect _readJsonRect(Map data) {
num x = data["x"];
num y = data["y"];
num w = data["w"];
num h = data["h"];
return new Rect.fromLTRB(x.toDouble(), y.toDouble(), (x + w).toDouble(), (y + h).toDouble());
}
Size _readJsonSize(Map data) {
num w = data["w"];
num h = data["h"];
return new Size(w.toDouble(), h.toDouble());
}
Point _readJsonPoint(Map data) {
num x = data["x"];
num y = data["y"];
return new Point(x.toDouble(), y.toDouble());
}
Image get image => _image;
Texture operator [](String fileName) => _textures[fileName];
}
part of sprites;
class Texture {
final Image image;
final Size size;
String name;
final bool rotated;
final bool trimmed;
Rect frame;
Rect spriteSourceSize;
Point pivot;
Texture(Image image) :
size = new Size(image.width.toDouble(), image.height.toDouble()),
image = image,
trimmed = false,
rotated = false,
frame = new Rect.fromLTRB(0.0, 0.0, image.width.toDouble(), image.height.toDouble()),
spriteSourceSize = new Rect.fromLTRB(0.0, 0.0, image.width.toDouble(), image.height.toDouble()),
pivot = new Point(0.5, 0.5);
Texture._fromSpriteFrame(this.image, this.name, this.size, this.rotated, this.trimmed, this.frame,
this.spriteSourceSize, this.pivot) {
}
Texture textureFromRect(Rect rect, Point offset, bool rotated) {
// TODO: Implement this
return null;
}
}
......@@ -3,6 +3,7 @@ import 'dart:sky';
import 'package:sky/widgets/basic.dart';
import 'package:sky/widgets/raised_button.dart';
import 'package:sky/widgets/widget.dart';
import 'package:sky/framework/net/fetch.dart';
import 'lib/game_demo.dart';
import 'lib/sprites.dart';
......@@ -10,18 +11,23 @@ import 'lib/sprites.dart';
void main() {
// Load images
new ImageMap([
"https://raw.githubusercontent.com/slembcke/GalacticGuardian.spritebuilder/GDC/Packages/SpriteBuilder%20Resources.sbpack/resources-auto/BurnTexture.png",
"https://raw.githubusercontent.com/slembcke/GalacticGuardian.spritebuilder/GDC/Packages/SpriteBuilder%20Resources.sbpack/Sprites/resources-auto/asteroid_big_002.png",
"https://raw.githubusercontent.com/slembcke/GalacticGuardian.spritebuilder/GDC/Packages/SpriteBuilder%20Resources.sbpack/Sprites/resources-auto/GG_blueship_Lv3.png",
"https://raw.githubusercontent.com/slembcke/GalacticGuardian.spritebuilder/GDC/Packages/SpriteBuilder%20Resources.sbpack/Sprites/resources-auto/laserBlue.png",
"https://raw.githubusercontent.com/slembcke/GalacticGuardian.spritebuilder/GDC/Packages/SpriteBuilder%20Resources.sbpack/Sprites/resources-auto/laserFlashPurple.png",
"https://raw.githubusercontent.com/slembcke/GalacticGuardian.spritebuilder/GDC/Source/Resources/NebulaClouds.png",
"res/nebula.png",
"res/sprites.png",
],
allLoaded);
allImagesLoaded);
}
void allLoaded(ImageMap loader) {
void allImagesLoaded(ImageMap loader) {
_loader = loader;
fetchBody("res/sprites.json").then((Response response) {
String json = response.bodyAsString();
_spriteSheet = new SpriteSheet(_loader["res/sprites.png"], json);
allResourcesLoaded();
});
}
void allResourcesLoaded() {
runApp(new GameDemoApp());
}
......@@ -29,7 +35,7 @@ class GameDemoApp extends App {
Widget build() {
return new Stack([
new SpriteWidget(new GameDemoWorld(_loader)),
new SpriteWidget(new GameDemoWorld(_loader, _spriteSheet)),
// new StackPositionedChild(
// new Flex([
// new FlexExpandingChild(
......@@ -49,3 +55,4 @@ class GameDemoApp extends App {
}
ImageMap _loader;
SpriteSheet _spriteSheet;
This diff was suppressed by a .gitattributes entry.
{"frames": [
{
"filename": "arrow.png",
"frame": {"x":2,"y":2,"w":446,"h":283},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":30,"y":49,"w":446,"h":283},
"sourceSize": {"w":512,"h":512},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "asteroid_big_0.nrm.png",
"frame": {"x":2,"y":287,"w":200,"h":188},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":200,"h":188},
"sourceSize": {"w":200,"h":188},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "asteroid_big_0.png",
"frame": {"x":204,"y":287,"w":200,"h":188},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":200,"h":188},
"sourceSize": {"w":200,"h":188},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "asteroid_big_1.nrm.png",
"frame": {"x":545,"y":275,"w":204,"h":166},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":204,"h":166},
"sourceSize": {"w":204,"h":166},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "asteroid_big_1.png",
"frame": {"x":589,"y":2,"w":204,"h":166},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":204,"h":166},
"sourceSize": {"w":204,"h":166},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "asteroid_big_2.nrm.png",
"frame": {"x":795,"y":2,"w":194,"h":165},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":194,"h":165},
"sourceSize": {"w":194,"h":165},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "asteroid_big_2.png",
"frame": {"x":795,"y":169,"w":194,"h":165},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":0,"y":2,"w":194,"h":165},
"sourceSize": {"w":194,"h":167},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "asteroid_small_0.nrm.png",
"frame": {"x":646,"y":170,"w":102,"h":84},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":102,"h":84},
"sourceSize": {"w":102,"h":84},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "asteroid_small_0.png",
"frame": {"x":862,"y":336,"w":102,"h":84},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":102,"h":84},
"sourceSize": {"w":102,"h":84},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "asteroid_small_1.nrm.png",
"frame": {"x":450,"y":171,"w":96,"h":102},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":96,"h":102},
"sourceSize": {"w":96,"h":102},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "asteroid_small_1.png",
"frame": {"x":548,"y":171,"w":96,"h":102},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":0,"y":0,"w":96,"h":102},
"sourceSize": {"w":96,"h":106},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "asteroid_small_2.nrm.png",
"frame": {"x":751,"y":336,"w":109,"h":84},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":109,"h":84},
"sourceSize": {"w":109,"h":84},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "asteroid_small_2.png",
"frame": {"x":751,"y":422,"w":109,"h":84},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":109,"h":84},
"sourceSize": {"w":109,"h":84},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "laser.png",
"frame": {"x":751,"y":170,"w":37,"h":76},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":37,"h":76},
"sourceSize": {"w":37,"h":76},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "ship.nrm.png",
"frame": {"x":406,"y":287,"w":137,"h":167},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":137,"h":167},
"sourceSize": {"w":137,"h":167},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "ship.png",
"frame": {"x":450,"y":2,"w":137,"h":167},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":25,"y":10,"w":137,"h":167},
"sourceSize": {"w":188,"h":188},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "star.png",
"frame": {"x":862,"y":422,"w":62,"h":68},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":11,"y":5,"w":62,"h":68},
"sourceSize": {"w":82,"h":78},
"pivot": {"x":0.5,"y":0.5}
}],
"meta": {
"app": "http://www.codeandweb.com/texturepacker",
"version": "1.0",
"image": "sprites.png",
"format": "RGBA8888",
"size": {"w":991,"h":508},
"scale": "1",
"smartupdate": "$TexturePacker:SmartUpdate:b79d98a34caa23746c4e2af6dd5b8506:bfdb7027c351003110a2082bbb53a657:1eabdf11f75e3a4fe3147baf7b5be24b$"
}
}
This diff was suppressed by a .gitattributes entry.
......@@ -20,8 +20,22 @@ void beginFrame(double timeStamp) {
canvas.rotate(math.PI * delta / 1800);
canvas.scale(0.2, 0.2);
Paint paint = new Paint()..color = const Color.fromARGB(255, 0, 255, 0);
// Draw image
if (image != null)
canvas.drawImage(image, -image.width / 2.0, -image.height / 2.0, paint);
// Draw cut out of image
canvas.rotate(math.PI * delta / 1800);
if (image != null) {
var w = image.width.toDouble();
var h = image.width.toDouble();
canvas.drawImageRect(image,
new Rect.fromLTRB(w * 0.25, h * 0.25, w * 0.75, h * 0.75),
new Rect.fromLTRB(-w / 4.0, -h / 4.0, w / 4.0, h / 4.0),
paint);
}
view.picture = canvas.endRecording();
view.scheduleFrame();
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment