SVG (Scalable Vector Graphics) представляет собой векторный формат, который позволяет создавать графику, масштабируемую без потери качества. В контексте разработки игр это даёт ряд преимуществ:
Библиотека Snap.svg упрощает работу с SVG, предоставляя удобный API для создания, трансформации и анимации элементов.
Создание игрового холста начинается с инициализации SVG-контейнера:
var s = Snap(800, 600);
Этот объект становится основной сценой, на которой размещаются игровые элементы.
Для интеграции в HTML:
<svg id="game"></svg>
<script>
var s = Snap("#game");
</script>
Игровые объекты представляют собой SVG-элементы:
var platform = s.rect(100, 500, 200, 20);
platform.attr({
fill: "#333"
});
var player = s.circle(150, 450, 20);
player.attr({
fill: "blue"
});
var sprite = s.image("player.png", 0, 0, 64, 64);
Для сложных объектов используется группировка:
var character = s.group(
s.circle(0, 0, 20),
s.rect(-10, 20, 20, 30)
);
character.attr({
transform: "translate(200, 200)"
});
Группы позволяют перемещать и анимировать несколько элементов как единое целое.
SVG использует двумерную систему координат:
player.attr({
transform: "translate(50, 0)"
});
player.attr({
transform: "rotate(45, 150, 450)"
});
player.attr({
transform: "scale(1.5)"
});
Комбинирование трансформаций:
player.attr({
transform: "translate(50,0) rotate(30)"
});
Игровая логика невозможна без взаимодействия:
player.click(function() {
console.log("Игрок нажат");
});
s.mousemove(function(e) {
console.log(e.clientX, e.clientY);
});
Snap.svg не обрабатывает клавиатуру напрямую, поэтому используется стандартный Jav * aScript:
document.addEventListener("keydown", function(e) {
if (e.key === "ArrowRight") {
movePlayer(5);
}
});
Snap.svg предоставляет встроенный механизм анимации:
player.animate({
cx: 300
}, 1000);
player.animate({
cy: 300
}, 1000, mina.easeinout);
function bounce() {
player.animate({ cy: 400 }, 500, function() {
player.animate({ cy: 450 }, 500, bounce);
});
}
bounce();
Для полноценной игры необходим цикл обновления:
function gameLoop() {
update();
render();
requestAnimationFrame(gameLoop);
}
gameLoop();
function update() {
playerX += velocityX;
playerY += velocityY;
}
function render() {
player.attr({
cx: playerX,
cy: playerY
});
}
Простейшая проверка пересечения:
function isColliding(a, b) {
var aBox = a.getBBox();
var bBox = b.getBBox();
return !(aBox.x2 < bBox.x ||
aBox.x > bBox.x2 ||
aBox.y2 < bBox.y ||
aBox.y > bBox.y2);
}
Использование:
if (isColliding(player, platform)) {
console.log("Столкновение!");
}
SVG-пути позволяют создавать сложные формы:
var path = s.path("M10 10 L100 100");
path.attr({
stroke: "#000",
fill: "none"
});
var length = Snap.path.getTotalLength(path);
Snap.animate(0, length, function(value) {
var point = Snap.path.getPointAtLength(path, value);
player.attr({
cx: point.x,
cy: point.y
});
}, 2000);
Для анимации кадров:
var frames = ["frame1.png", "frame2.png", "frame3.png"];
var index = 0;
setInterval(function() {
sprite.attr({
href: frames[index]
});
index = (index + 1) % frames.length;
}, 100);
Имитация камеры достигается перемещением сцены:
var world = s.group();
world.add(player, platform);
function moveCamera(x, y) {
world.attr({
transform: "translate(" + (-x) + "," + (-y) + ")"
});
}
Ключевые приёмы:
getBBox()Разделение на компоненты:
function Entity(x, y) {
this.x = x;
this.y = y;
}
function Player(x, y) {
Entity.call(this, x, y);
this.el = s.circle(x, y, 20);
}
Player.prototype.update = function() {
this.x += 1;
this.el.attr({ cx: this.x });
};
Порядок отрисовки задаётся порядком добавления:
var background = s.rect(0, 0, 800, 600).attr({ fill: "#ccc" });
var foreground = s.circle(100, 100, 30);
Изменение порядка:
foreground.toFront();
background.toBack();
Асинхронная загрузка SVG:
Snap.load("level.svg", function(data) {
s.append(data);
});
Пример: движение персонажа по платформе
var playerX = 100;
var velocity = 0;
function update() {
velocity += 0.5; // гравитация
playerX += velocity;
if (playerX > 500) {
velocity = -10;
}
}
function render() {
player.attr({
cx: playerX
});
}
var mask = s.circle(100, 100, 50);
image.attr({
mask: mask
});
var gradient = s.gradient("l(0,0,1,1)#fff-#000");
rect.attr({
fill: gradient
});
var blur = s.filter(Snap.filter.blur(5, 5));
player.attr({
filter: blur
});
Тем не менее, для 2D-игр, головоломок, интерфейсных игр и обучающих проектов Snap.svg остаётся мощным и удобным инструментом.