Skip to content

Commit

Permalink
Initial snake renderer with sprites
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasnlm committed Apr 11, 2020
1 parent acb2df4 commit d5926bd
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 115 deletions.
2 changes: 1 addition & 1 deletion lib/game/blocs/game_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class GameBloc extends Bloc<GameEvent, GameState> {
GameBloc({
@required this.random,
this.flameManager,
this.snakeInitialLength = 4,
this.snakeInitialLength = 9,
}) : assert(random != null),
assert(snakeInitialLength >= 4) {
add(LoadAssetsEvent());
Expand Down
4 changes: 4 additions & 0 deletions lib/game/flame/flame_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ class FlameManager with IFlameManager {
await Flame.images.loadAll(<String>[
'food/food.png',
'food/red_food.png',
'snake/head.png',
'snake/body.png',
'snake/body_curve.png',
'snake/tail.png',
]);
}
}
23 changes: 23 additions & 0 deletions lib/game/renderer/board_component.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import 'package:flame/anchor.dart';
import 'package:flame/components/component.dart';
import 'package:flame/sprite.dart';
import 'package:flutter/widgets.dart';

/// Renders a single sprite based on [SpriteComponent] on the game board.
class BoardComponent extends SpriteComponent {
/// Convenient constructor.
BoardComponent(String fileName, double tileSize) {
width = tileSize;
height = tileSize;
sprite = Sprite(fileName);
anchor = Anchor.center;
}

@override
void render(Canvas canvas) {
canvas.save();
canvas.translate(width * 0.5, height * 0.5);
super.render(canvas);
canvas.restore();
}
}
40 changes: 0 additions & 40 deletions lib/game/renderer/food_renderer.dart

This file was deleted.

24 changes: 14 additions & 10 deletions lib/game/renderer/game_renderer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import '../../ui/colors.dart';
import '../models/board.dart';
import '../models/food.dart';
import '../models/snake.dart';
import '../renderer/snake_renderer.dart';
import 'food_renderer.dart';
import 'board_component.dart';
import 'snake_component.dart';

/// Main game render. Used to render the game screeen.
class GameRenderer extends Game {
Expand All @@ -20,8 +20,8 @@ class GameRenderer extends Game {
@required this.board,
@required this.tileSize,
}) {
_foodSprite = FoodRenderer(tileSize);
_snakeRenderer = SnakeRenderer(tileSize);
_food = BoardComponent('food/food.png', tileSize);
_snake = SnakeComponent(tileSize);
}

/// The real screen size.
Expand All @@ -33,17 +33,21 @@ class GameRenderer extends Game {
/// The tile size used to render the food and snake.
final double tileSize;

FoodRenderer _foodSprite;
SnakeRenderer _snakeRenderer;
BoardComponent _food;
SnakeComponent _snake;

/// Called to update the [Food] position.
void updateFood(Food food) {
_foodSprite.updateFood(food);
if (food != null) {
_food
..x = food.x * tileSize
..y = food.y * tileSize;
}
}

/// Called to update the [Snake] positions.
void updateSnake(Snake snake) {
_snakeRenderer.updateSnake(snake);
_snake.updateSnake(snake);
}

@override
Expand All @@ -53,8 +57,8 @@ class GameRenderer extends Game {

_drawBackground(canvas);

_foodSprite.render(canvas);
_snakeRenderer.render(canvas);
_food.render(canvas);
_snake.render(canvas);

canvas.restore();
}
Expand Down
112 changes: 112 additions & 0 deletions lib/game/renderer/snake_component.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import 'dart:math';
import 'dart:ui';

import 'package:flame/components/component.dart';

import '../models/snake.dart';
import '../models/vec2d.dart';
import '../renderer/board_component.dart';

/// Renders the snake using [SpriteComponent] for each part of it.
class SnakeComponent extends Component {
/// Convenient constructor.
SnakeComponent(this.tileSize);

/// The tile size. Use to draw the snake.
final double tileSize;

List<BoardComponent> _snakeBody;

BoardComponent _buildHead(Vec2d current, Vec2d prev) {
final imageRotation = pi / 2;
final sameAxis = prev.x == current.x;

final mustInvest = atan2(current.x - prev.x, current.y - prev.y) <= 0.0;

return BoardComponent('snake/head.png', tileSize)
..x = tileSize * current.x
..y = tileSize * current.y
..angle = (sameAxis ? 0.0 : imageRotation) + (mustInvest ? pi : 0.0);
}

BoardComponent _buildTail(Vec2d current, Vec2d prev) {
final imageRotation = pi / 2;
final sameAxis = prev.x == current.x;

final mustInvest = atan2(current.x - prev.x, current.y - prev.y) > 0.0;

return BoardComponent('snake/tail.png', tileSize)
..x = tileSize * current.x
..y = tileSize * current.y
..angle = (sameAxis ? 0.0 : imageRotation) + (mustInvest ? pi : 0.0);
}

BoardComponent _buildBody(Vec2d current, Vec2d prev, Vec2d next) {
final imageRotation = pi / 2;

BoardComponent component;

final diffAngle = atan2(next.x - current.x, next.y - current.y) -
atan2(prev.x - current.x, prev.y - current.y);

if (asin(sin(diffAngle)).abs() == (pi / 2)) {
final isClockwise = asin(sin(diffAngle)) < 0.0;
final rotate = isClockwise ? 0.0 : imageRotation;

component = BoardComponent('snake/body_curve.png', tileSize);
component.angle = -atan2(next.x - current.x, next.y - current.y) + rotate;
} else {
component = BoardComponent('snake/body.png', tileSize);
final sameAxis = prev.x == current.x;
component.angle = sameAxis ? 0.0 : imageRotation;
}

return component
..x = tileSize * current.x
..y = tileSize * current.y;
}

/// Update the snake position using [snake] info.
void updateSnake(Snake snake) {
if (snake != null) {
final snakeBody = snake.body.toList();
_snakeBody = <BoardComponent>[
_buildHead(
snakeBody[0],
snakeBody[1],
),
];

for (var i = 1; i <= snakeBody.length - 2; i++) {
_snakeBody.add(
_buildBody(
snakeBody[i],
snakeBody[i - 1],
snakeBody[i + 1],
),
);
}

_snakeBody.add(_buildTail(
snakeBody.last,
snakeBody[snakeBody.length - 2],
));
} else {
_snakeBody = null;
}
}

@override
void render(Canvas canvas) {
if (_snakeBody != null) {
for (var part in _snakeBody.skip(1)) {
part.render(canvas);
}

_snakeBody.first.render(canvas);
}
}

@override
void update(double dt) {}
}
56 changes: 0 additions & 56 deletions lib/game/renderer/snake_renderer.dart

This file was deleted.

7 changes: 0 additions & 7 deletions lib/game/renderer/sprite_renderer.dart

This file was deleted.

2 changes: 1 addition & 1 deletion lib/ui/colors.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class GameColors {
static const primaryDark = Color(0xFF1A2629);

/// The color of the unplayable area.
static const voidBackground = Color(0xFF000000);
static const voidBackground = Color(0xFFFF0000);

/// The background of the playable area.
static const background = Color(0xFFB0B0B0);
Expand Down

0 comments on commit d5926bd

Please sign in to comment.