Сегодня я расскажу вам о том как написать кубик для игр, при этом понадобиться только браузер и текстовый редактор. О том что можно написать такой кубик, я увидел на одном из примеров и интернете, но мне совсем не понравилась реализация и я решил изменить код на свой вкус поэтому можно считать, что это моя разработка. Итак начнем:
Первое с чем необходимо познакомиться это с объектной моделью языка JavaScript (сами сами). Все это дело я поместил в отдельный файл, — отдельный объект, который в итоге можно очень удобно прицепить к canvas -у.
Сам объект разделил на несколько частей(методов):
- Отрисовка кубика.
- Отрисовка точки.
- Вывод точек на кубике.
- Эффектный бросок.
Творческий процесс начинается еще до момента программирования определяю для себя как будут располагаться точки на кубике их номер и координаты. Кубик статический 100х100px.
this.point={ // Координаты точек.
1:{'x':20, 'y':20}, // 1 2
2:{'x':80, 'y':20}, // 3 4 5
3:{'x':20, 'y':50}, // 6 7
4:{'x':50, 'y':50},
5:{'x':80, 'y':50},
6:{'x':20, 'y':80},
7:{'x':80, 'y':80},
};
Теперь, надо сопоставить какие точки показывать при каком количестве выпавших очков:
this.dependence={ // Колличество очков, какие точки.
1:[4],
2:[1,7],
3:[1,4,7],
4:[1,2,6,7],
5:[1,2,4,6,7],
6:[1,2,3,5,6,7]
};
Теперь можно приступить к прорисовке кубика, ну раз 2d то квадрата. И сразу надо оговориться, что когда я его нарисовал прямоугольным он мне не понравился, порывшись в интернете нашел готовую функцию нарисовать квадрат с закругленными углами.
function fillRoundedRect(x, y, w, h, r){
this.beginPath();
this.moveTo(x+r, y);
this.lineTo(x+w-r, y);
this.quadraticCurveTo(x+w, y, x+w, y+r);
this.lineTo(x+w, y+h-r);
this.quadraticCurveTo(x+w, y+h, x+w-r, y+h);
this.lineTo(x+r, y+h);
this.quadraticCurveTo(x, y+h, x, y+h-r);
this.lineTo(x, y+r);
this.quadraticCurveTo(x, y, x+r, y);
this.fill();
}
Чтоб, ей можно было воспользоваться «красиво» прикручиваю ее через прототип к 2d контенту:
CanvasRenderingContext2D.prototype.fillRoundedRect = fillRoundedRect;
После того как квадрат нарисован, я сохраняю его в переменную с помощью функции getImageData() для дальнейшего использования и сокращение времени выполнения (не надо заново перерисовывать — он уже есть готовый).
/*
* Рисуем квадрат.
*/
this.DrawSquare=function(){
this.ctx.strokeStyle="black";
this.ctx.fillStyle="rgba(100,100,100,0.4)";
this.ctx.fillRoundedRect(0, 00, 100, 100, 15);
this.ctx.stroke();
this.pic_square=this.ctx.getImageData(0, 0, 100, 100);
};
Теперь по тому же принципу рисую точку на квадрате и сохраняю ее в переменную:
/*
* Рисуем точку.
*/
this.DrawPoint=function(){
this.ctx.strokeStyle="black";
this.ctx.fillStyle="rgba(150,150,250,0.9)";
this.ctx.beginPath();
this.ctx.arc(this.point[this.dependence[1][0]].x, this.point[this.dependence[1][0]].y, 10, 0, 360);
this.ctx.stroke();
this.ctx.fill();
this.pic_point=this.ctx.getImageData(this.point[this.dependence[1][0]].x-11, this.point[this.dependence[1][0]].y-11, 22,22);
}
Тут стоит пояснить что происходит при сохранении картинки. Дело в том что координаты точек указанны для центра окружности, поэтому при сохранении надо учитывать сдвиг по осям плюс увеличении площади сохранения. Как показала практика в разы быстрее работает использование именно переменной, а не перерисовки каждой точки заново, при броске.
Теперь интересное… кинем кубик, для этого нужен рандом для случайного появления очков при броске. В интернете есть все:
function getRandomInt(min, max){
return Math.floor(Math.random() * (max - min + 1)) + min;
}
С начало рисую чистый кубик, потом узнаю сколько очков должно выпасть и рисую на нем соответствующие точки:
/*
* Кидаем кубик.
*/
this.ThrowDice=function(){
this.ctx.putImageData(this.pic_square, 0, 0);
var namber=getRandomInt(1,6);
for (var draw in this.dependence[namber])
this.ctx.putImageData(this.pic_point, this.point[this.dependence[namber][draw]].x-11, this.point[this.dependence[namber][draw]].y-11);
};
Стоит отметить опять же про координаты точек, делаем поправку на центр окружности. Можно на этом остановиться и при клике запускать эту функцию.. но мне показалось это скучно… И я решил добавить еще самый волнующий момент — эффект хаотичного вращения кубика:
/*
* Эффкет бросания.
*/
this.RandThrow=function(){
if(this.timer) clearInterval(this.timer);
this.count=0;
this.count_end=getRandomInt(5,20);
var link=this;
this.timer=setInterval(function(){
if(link.count>link.count_end) clearInterval(link.timer);
link.ThrowDice();
link.count++;
},160);
}
Остановлюсь поподробней на этой функции. Первая строчка появилась благодаря моему соседу на котором я тестировал. Дело в том, что она вешается на клик по изображению, и когда происходит несколько кликов даже боюсь представить что твориться. Когда сосед сказал, что кубик не останавливается, у меня вызвало удивление, оказалось он тыркал на него не дождавшись остановки, т.е таймер терял свой идентификатор, и цикл шел бесконечно. Эта строчка избавляет от этого недоразумения.
3 строчка — Случайным образом выбираем «силу» броска.
4 строчка — Для правильного понимания объекта в таймере необходимо использовать постоянную ссылку на объект, т.к. «this» внутри таймера принимает другое значение.
И потом бросаем кубик…
Теперь осталась инициализация, передаем id элемента <canvas> и цепляемся к нему, проверяем поддержку браузером, рисуем квадратик, точку, бросаем кубик, и вешаю событие опять через линк.
var canvas=document.getElementById(canvas);
if(canvas && canvas.getContext) {
this.ctx=canvas.getContext("2d");
this.DrawSquare();
this.DrawPoint();
this.RandThrow();
var link=this;
canvas.onclick=function(){link.RandThrow()};
}
}
Для подключения необходимо добавить на страницу:
<script type="text/javascript" src="/wp-includes/js/dice.js"></script>
И после того как страница загрузиться прицепить к нему объект:
$(document).ready(function(){
var dice1 = new Dice('myCanvas');
});
Описанный выше костяк легко дополняется например достаточно передать дополнительный объект, чтоб бросать по два кубика.. Например:
var bla = new Dice("canvas1",new Dice("canvas2"));
а в инициализации добавить obj.RandThrow();
var canvas=document.getElementById(canvas);
if(canvas && canvas.getContext) {
this.ctx=canvas.getContext("2d");
this.DrawSquare();
this.DrawPoint();
var link=this;
link.RandThrow();
canvas.onclick=function(){link.RandThrow()};
if(obj) obj.RandThrow();
}
}
Полный код ищите в шапке страницы «Dice.js».
Ну и пример работы:
P.s:
Это моя первая статья… =)
Сам писал или готовое решение в инете нашел?
Я в начале написал, идею в интернете нашел, а реализацию сам сочинял.
Красавчик=)