Dice — Игральный кубик.

Сегодня я расскажу вам о том как написать кубик для игр, при этом понадобиться только браузер и текстовый редактор. О том что можно написать такой кубик, я увидел на одном из примеров и интернете, но мне совсем не понравилась реализация и я решил изменить код на свой вкус поэтому можно считать, что это моя разработка. Итак начнем:

Первое с чем необходимо познакомиться это с объектной моделью языка 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:
Это моя первая статья… =)

3 Responses

Добавить комментарий