Canvas: лунный шаттл. Начало.

Ну что, готовы немного покодить? Ну если да, то поехали!
(Если нет, то можете продолжать читать  )

Шаблон:


Давайте начнем с шаблона (вы должны по крайней мере знать базовые знание JS):


<!DOCTYPE html>
<html>
    <head>
        <title>Canvas: лунный шаттл.</title>
    </head>
    <body>
        <canvas id="canvas">Канвас не работает, обновите браузер пожалуйста.</canvas>
        <script type="text/javascript">
            // Наша свещенная основа
            var canvas = document.getElementById("canvas");
            var ctx = canvas.getContext("2d");

            // Размер Canvas'a
            var width = canvas.width = window.innerWidth;
            var height = canvas.height = window.innerHeight;
            // Линк на главный цикл
            var timer = null;

            function update() {
                // Очищаем экран и заливаем #000 цветом
                ctx.fillStyle = "#000";
                ctx.strokeStyle = "#fff";
                ctx.clearRect(0,0,width,height);
                ctx.fillRect(0,0,width,height);
            }
            
            // Вещаем цикл на update() 
            timer = setInterval(update, 1000/30);
        </script>
    </body>
</html>

Это кусок кода вы также можете найти также в прошлом топике.

Структура «Игры»:


Я думаю что с этого стоит начать перед тем чем писать игру.
Структура приблизительно такая:
Game:
    - Lunar:
        - Fuel;
    - Sky;
    - World;

О.К., а теперь по подробней:
— Объект Game главный объект в игре, он отвечает за от рисовку всех объектов кроме Fuel.
— Объект Lunar отвечает за от рисовку и за управления нашего корабля.
— Объект Fuel отвечает за от рисовку и за управления газового бака нашего корабля.
— Объект Sky рисует звезды.
— И наконец объект World генерирует случайный ландшафт и рисует его.

Теперь начать писать игру!

Главный объект Game:


Ну что же давайте напишем главный объект:

var Game = {
    objects: [],
    update: function () {
        for (var i = 0; i < this.objects.length; i++) {
            this.objects[i].update();
        }
    },
    render: function() {
        for (var i = 0; i < this.objects.length; i++) {
            this.objects[i].render();
        }
    },
    add: function (obj) {
        this.objects[this.objects.length] = obj;
    },
    init: function () {
        // Инициализация
    }
}

Game.init();

Грубо говоря код наверху, создает определенный объект по имени Game, и делает «инициализацию» этого объекта.
А также надо добавить вот это в конце функции update() (что бы все начало работать):

Game.update();
Game.render();

Тест на работоспособность (необязательный шаг):


Если сомневаетесь или хотите убедится что все работает, можно сделать тест, чтоб проверить что у нас все работает (также рекомендую использовать FireBug, авось ошибки в JS коде):
// После: var width = canvas.width = window.innerWidth;
//var height = canvas.height = window.innerHeight;

var Rect = {
    update: function () { 
        
    },
    render: function () {
        ctx.fillStyle = "#fff";
        ctx.fillRect(0,0,50,50);
    }
}

// ...

var Game = {
    // ...
    init: function () {
        this.add(Rect);
    },
    // ...
};

Код создает объект который будет отображать белый квадрат а также добавляет этот объект в Game.
После вставки этого кода, у вас должен появится белый квадрат в левом верхнем угле размером в 50 пикс. (50х50).

Лунный Шаттл:


Тест завершен (если его делали), поэтому можете удалить объект (если делали) Rect и содержание функции Game.init().
А теперь мы напишем лунный шаттл. Начнем с того что нам понадобится пару функций:

// После: var width = canvas.width = window.innerWidth;
// var height = canvas.height = window.innerHeight;
function rectangle(x, y, w, h) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    this.min = {
        x: this.x,
        y: this.y
    };
    this.max = {
        x: this.x + this.w,
        y: this.y + this.h
    };
    this.setPosition = function (x, y) {
        this.min = {
            x: x,
            y: y
        };
        this.max = {
            x: x + this.w,
            y: y + this.h
        };
    }
}

function ngon(cont, x, y, sides, angle, size) {
    var ang = (Math.PI * 2) / sides;

    cont.beginPath();

    for (i = 0; i <= Math.PI * 2; i += ang) {
        cont.lineTo(x + Math.cos(i + angle) * (size.w / 2), y + Math.sin(i + angle) * (size.h / 2));
    }

    cont.closePath();
    cont.fill();
    cont.stroke();

}

Эти две функции будут очень полезны. Первая из них создает прямоугольник (в памяти, так сказать модель). А вторая с помощью «ссылки» на Canvas контекст будет рисовать н-угольник с параметрами: контекст, x, y, н-сторон, угол и размер:{w: ширина, h: высота}

Ну что же, напишем шаттл:

// До: var Game = {
function lander(x,y,w,h) {
    this.render = function () {
        ctx.fillStyle = "#000";
        ctx.strokeStyle = "#fff";
        ctx.lineWidth = 1;
        
        ctx.save();
        ctx.translate(this.x+this.w/2,this.y+this.h/2);
        
        ngon(ctx, 0, 0, 3, 3*Math.PI/2, {
            w: this.w,
            h: this.h
        });
        
        ctx.restore();
    };
    this.update = function () {
        
    };
    this.__proto__ = new rectangle(x,y,w,h);
}
var Lunar = new lander(50,50,20,30);

// ...

var Game = {
    // ...
    init: function () {
        this.add(Lunar);
    },
    // ...
};

На экране появится треугольник, это будет нашим шаттлом. А теперь немного поговорим о гравитации в игре, добавим ее и сделаем платформу, на которую наш шаттл будет садится. Код для управления будем писать в следующей статьи.

Платформа и Гравитация:


Гравитация у нас в игру будет 0.19 пикс./фрейм, это где-то скорость гравитации (9.81 м/с) приблизительно поделенная на 30 и потом на 2.

Ну что же напишем немного кода для того что бы наш корабль падал вниз (чтоб тянула его гравитация):


// До: var width = canvas.width = window.innerWidth;
var grav = 0.19;

    // Внутри:function lander(x,y,w,h) {
    this.velocity = {x:0,y:0};
    // ...
    this.update = function () {
        this.velocity.y += grav;
        this.setPosition(this.x+this.velocity.x,this.y+this.velocity.y);
    };
    // ...
}

Теперь наш корабль будет падать стремительно вниз. Нам понадобится дополнительная функция для определения столкновения:

function aabb(rect1,rect2) {
    if (rect1.min.x < rect2.max.x &&
        rect1.min.y < rect2.max.y &&
        rect1.max.x > rect2.min.x &&
        rect1.max.y > rect2.min.y) {
        return true;
    }
    return false;
}

А теперь, давайте напишем код для платформы на которую будет падать корабль:

function platform(x,y,w,h) {
    this.render = function () {
        ctx.fillStyle = "#fff";
        ctx.fillRect(this.x,this.y,this.w,this.h);
    };
    this.update = function () {
    
    };
    
    this.__proto__ = new rectangle(x,y,w,h);
}
var Plat = new platform(20,height-50,100,10);

Для того что бы платформа отображалась, нужно добавить ее в Game:
var Game = {
    // ...
    init: function () {
        // ...
        this.add(Plat);
    }
    // ...
}

Последнее что осталось нам сделать, это добавить логику для изменения скорости корабля:
function lander(x,y,w,h) {
    // ...
    this.update = function () {
        // ...
        if (aabb(this,Plat)) {
            this.velocity = {x:0,y:0};
        }
        // ...
    };
    // ...
}

Ну все, мы закончили с этой статьей!
Можете побаловаться с кодом, попробовать разный уровень гравитации, может поменять форму корабля ну и т.д.

А теперь ссылка на результат: jsfiddle.net/Volter9/756qX/

Ждите следующей статьи
P.S.: Если найдете ошибки: орфографически, грамматические или же в коде, пожалуйста прокомментируйте эту ошибку, очень буду благодарен.

3 комментария

avatar
Гравитация это сила которая притягивает вниз, для игры возьмем цифру 0.19, это где-то скорость гравитации (9.81 м/с) приблизительно поделенная на 2.

Вот тут я сломал себе моск…
avatar
Мда, щас поправлю.
avatar
Так лучше?