提交 109271ea 编写于 作者: V Viktor Lidholt

Initial version of working game with Box2D, images, nodes and sprites.

R=abarth@chromium.org

Review URL: https://codereview.chromium.org/1161023006
上级 898273c7
library game;
import 'dart:sky';
import 'dart:math' as Math;
import 'package:vector_math/vector_math_64.dart';
import 'sprites.dart';
import 'package:box2d/box2d.dart';
part 'game_world.dart';
\ No newline at end of file
part of game;
class GameWorld extends TransformNode {
World world;
List<Body> bodies = [];
Image _image;
GameWorld(double width, double height) {
this.width = width;
this.height = height;
world = new World.withGravity(new Vector2(0.0, 0.0));
// Load and add background
Image imgBg = new Image()..src="https://raw.githubusercontent.com/slembcke/GalacticGuardian.spritebuilder/GDC/Packages/SpriteBuilder%20Resources.sbpack/resources-auto/BurnTexture.png";
SpriteNode sprtBg = new SpriteNode.withImage(imgBg);
sprtBg.width = width;
sprtBg.height = height;
sprtBg.pivot = new Vector2(0.0, 0.0);
this.children.add(sprtBg);
SpriteNode sprtCenter = new SpriteNode.withImage(imgBg);
sprtCenter.width = 32.0;
sprtCenter.height = 32.0;
sprtCenter.position = new Vector2(512.0, 512.0);
this.children.add(sprtCenter);
// Load asteroid image
_image = new Image()..src="https://raw.githubusercontent.com/slembcke/GalacticGuardian.spritebuilder/GDC/Packages/SpriteBuilder%20Resources.sbpack/Sprites/resources-auto/asteroid_big_002.png";
// Add some asteroids to the game world
for (int i = 0; i < 50; i++) {
addAsteroid(10.0);
}
for (int i = 0; i < 50; i++) {
addAsteroid(20.0);
}
}
void addAsteroid([double radius=20.0]) {
// Create shape
final CircleShape shape = new CircleShape();
shape.radius = radius;
// Define fixture (links body and shape)
final FixtureDef activeFixtureDef = new FixtureDef();
activeFixtureDef.restitution = 1.0;
activeFixtureDef.density = 0.05;
activeFixtureDef.shape = shape;
// Define body
final BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyType.DYNAMIC;
bodyDef.position = new Vector2(0.0, 30.0);
bodyDef.linearDamping = 0.0;
bodyDef.angularDamping = 0.0;
// Create body and fixture from definitions
final Body body = world.createBody(bodyDef);
body.createFixtureFromFixtureDef(activeFixtureDef);
// Set position of object
Math.Random rand = new Math.Random();
body.setTransform(new Vector2(rand.nextDouble() * this.width, rand.nextDouble() * this.height), 0.0);
body.applyLinearImpulse(new Vector2(rand.nextDouble()*10000.0-5000.0, rand.nextDouble()*10000.0-5000.0), new Vector2(0.0, 0.0), true);
// Add to list
bodies.add(body);
SpriteNode sprt = new SpriteNode.withImage(_image);
sprt.width = radius*2;
sprt.height = radius*2;
body.userData = sprt;
this.children.add(sprt);
}
void update(double dt) {
world.stepDt(1.0/60.0, 10, 10); // Pass in dt
bodies.forEach(updateBody);
}
void updateBody(Body body) {
SpriteNode sprt = body.userData;
double rot = 0.0; //body.getRotation();
// Check bounds and warp objects
if (body.position[0] < -sprt.width/2) {
body.setTransform(new Vector2(body.position[0] + this.width + sprt.width, body.position[1]), rot);
}
if (body.position[0] > this.width + sprt.width/2) {
body.setTransform(new Vector2(body.position[0] - (this.width + sprt.width), body.position[1]), rot);
}
if (body.position[1] < -sprt.height/2) {
body.setTransform(new Vector2(body.position[0], body.position[1] + this.height + sprt.height), rot);
}
if (body.position[1] > this.height + sprt.height/2) {
body.setTransform(new Vector2(body.position[0], body.position[1] - (this.height + sprt.height)), rot);
}
// Update sprite
sprt.position = body.position;
sprt.rotation = body.getAngle();
}
}
\ No newline at end of file
part of sprites;
enum SpriteBoxTransformMode {
nativePoints,
letterbox,
stretch,
scaleToFit,
fixedWidth,
fixedHeight,
}
class SpriteBox extends RenderBox {
// Root node for drawing
TransformNode _rootNode;
// Tracking of frame rate and updates
double _lastTimeStamp;
int _numFrames = 0;
SpriteBoxTransformMode transformMode;
double systemWidth;
double systemHeight;
SpriteBox(TransformNode rootNode, [SpriteBoxTransformMode mode = SpriteBoxTransformMode.nativePoints, double width=1024.0, double height=1024.0]) {
// Setup root node
_rootNode = rootNode;
// Setup transform mode
transformMode = mode;
systemWidth = width;
systemHeight = height;
_scheduleTick();
}
void performLayout() {
size = constraints.constrain(new Size.infinite());
}
void handlePointer(PointerEvent event) {
switch (event.type) {
case 'pointerdown':
print("pointerdown");
break;
}
}
void paint(RenderNodeDisplayList canvas) {
// Move to correct coordinate space before drawing
double scaleX = 1.0;
double scaleY = 1.0;
double offsetX = 0.0;
double offsetY = 0.0;
switch(transformMode) {
case SpriteBoxTransformMode.stretch:
scaleX = size.width/systemWidth;
scaleY = size.height/systemHeight;
break;
case SpriteBoxTransformMode.letterbox:
scaleX = size.width/systemWidth;
scaleY = size.height/systemHeight;
if (scaleX > scaleY) {
scaleY = scaleX;
offsetY = (size.height - scaleY * systemHeight)/2.0;
}
else {
scaleX = scaleY;
offsetX = (size.width - scaleX * systemWidth)/2.0;
}
break;
case SpriteBoxTransformMode.nativePoints:
break;
default:
assert(false);
break;
}
canvas.save();
canvas.translate(offsetX, offsetY);
canvas.scale(scaleX, scaleY);
// Draw the sprite tree
_rootNode.visit(canvas);
canvas.restore();
}
int _animationId = 0;
void _scheduleTick() {
_animationId = scheduler.requestAnimationFrame(_tick);
}
void _tick(double timeStamp) {
// Calculate the time between frames in seconds
if (_lastTimeStamp == null) _lastTimeStamp = timeStamp;
double delta = (timeStamp - _lastTimeStamp) / 1000;
_lastTimeStamp = timeStamp;
// Count the number of frames we've been running
_numFrames += 1;
// Print frame rate
if (_numFrames % 60 == 0) print("delta: ${delta} fps: ${1.0/delta}");
_rootNode.update(delta);
_scheduleTick();
}
}
\ No newline at end of file
part of sprites;
// TODO: Actually draw images
class SpriteNode extends TransformNode {
Image _image;
bool constrainProportions = false;
SpriteNode() {
this.pivot = new Vector2(0.5, 0.5);
}
SpriteNode.withImage(Image image) : super() {
this.pivot = new Vector2(0.5, 0.5);
_image = image;
}
void paint(PictureRecorder canvas) {
if (_image != null && _image.width > 0 && _image.height > 0) {
canvas.save();
double scaleX = _width/_image.width;
double scaleY = _height/_image.height;
if (constrainProportions) {
// Constrain proportions, using the smallest scale and by centering the image
if (scaleX < scaleY) {
canvas.translate(0.0, (_height - scaleX * _image.height)/2.0);
scaleY = scaleX;
}
else {
canvas.translate((_width - scaleY * _image.width)/2.0, 0.0);
scaleX = scaleY;
}
}
canvas.scale(scaleX, scaleY);
canvas.drawImage(_image, 0.0, 0.0, new Paint()..setARGB(255, 255, 255, 255));
canvas.restore();
}
else {
// Paint a red square for missing texture
canvas.drawRect(new Rect.fromLTRB(0.0, 0.0, this.width, this.height),
new Paint()..setARGB(255, 255, 0, 0));
}
}
}
\ No newline at end of file
library sprites;
import 'dart:sky';
import 'dart:math' as Math;
import 'package:vector_math/vector_math_64.dart';
import 'package:sky/framework/app.dart';
import 'package:sky/framework/rendering/render_box.dart';
import 'package:sky/framework/rendering/render_node.dart';
import 'package:sky/framework/scheduler.dart' as scheduler;
part 'sprite_box.dart';
part 'transform_node.dart';
part 'sprite_node.dart';
\ No newline at end of file
part of sprites;
double degrees2radians(double degrees) => degrees * Math.PI/180.8;
double radians2degrees(double radians) => radians * 180.0/Math.PI;
class TransformNode {
Vector2 _position;
double _rotation;
bool _isMatrixDirty;
Matrix3 _transform;
Matrix3 _pivotTransform;
double _width;
double _height;
Vector2 _pivot;
List<TransformNode>children;
TransformNode() {
_width = 0.0;
_height = 0.0;
_rotation = 0.0;
_pivot = new Vector2(0.0, 0.0);
_position = new Vector2(0.0, 0.0);
_isMatrixDirty = false;
_transform = new Matrix3.identity();
_pivotTransform = new Matrix3.identity();
children = [];
}
double get rotation => _rotation;
void set rotation(double rotation) {
_rotation = rotation;
_isMatrixDirty = true;
}
Vector2 get position => _position;
void set position(Vector2 position) {
_position = position;
_isMatrixDirty = true;
}
double get width => _width;
void set width(double width) {
_width = width;
_isMatrixDirty = true;
}
double get height => _height;
void set height(double height) {
_height = height;
_isMatrixDirty = true;
}
Vector2 get pivot => _pivot;
void set pivot(Vector2 pivot) {
_pivot = pivot;
_isMatrixDirty = true;
}
Matrix3 get transformMatrix {
if (!_isMatrixDirty) {
return _transform;
}
Vector2 pivotInPoints = new Vector2(_width * _pivot[0], _height * _pivot[1]);
double cx, sx, cy, sy;
if (_rotation == 0) {
cx = 1.0;
sx = 0.0;
cy = 1.0;
sy = 0.0;
}
else {
double radiansX = degrees2radians(_rotation);
double radiansY = degrees2radians(_rotation);
cx = Math.cos(radiansX);
sx = Math.sin(radiansX);
cy = Math.cos(radiansY);
sy = Math.sin(radiansY);
}
// TODO: Add support for scale
double scaleX = 1.0;
double scaleY = 1.0;
// Create transformation matrix for scale, position and rotation
_transform.setValues(cy * scaleX, sy * scaleX, 0.0,
-sx * scaleY, cx * scaleY, 0.0,
_position[0], _position[1], 1.0);
if (_pivot.x != 0 || _pivot.y != 0) {
_pivotTransform.setValues(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, pivotInPoints[0], pivotInPoints[1], 1.0);
_transform.multiply(_pivotTransform);
}
return _transform;
}
void visit(PictureRecorder canvas) {
prePaint(canvas);
paint(canvas);
visitChildren(canvas);
postPaint(canvas);
}
void prePaint(PictureRecorder canvas) {
canvas.save();
canvas.translate(_position[0], _position[1]);
canvas.rotateDegrees(_rotation);
canvas.translate(-_width*_pivot[0], -_height*_pivot[1]);
// TODO: Use transformation matrix instead of individual calls
// List<double> matrix = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
// this.transformMatrix.copyIntoArray(matrix);
// canvas.concat(matrix);
}
void paint(PictureRecorder canvas) {
}
void visitChildren(PictureRecorder canvas) {
children.forEach((child) => child.visit(canvas));
}
void postPaint(PictureRecorder canvas) {
canvas.restore();
}
void update(double dt) {
}
}
\ No newline at end of file
import 'dart:sky';
import 'lib/game.dart';
import 'lib/sprites.dart';
import 'package:sky/framework/app.dart';
AppView app;
void main() {
// Create a new app with the sprite box that contains our game world
app = new AppView(new SpriteBox(new GameWorld(1024.0, 1024.0),SpriteBoxTransformMode.letterbox));
}
name: game
dependencies:
box2d: any
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册